The Ontology Is the Source of Truth, Not the Code
Published 27 min read
At some point in the past I worked as a systems architect on a system that had started life as a Delphi desktop client talking straight to a database. That history had left a mental model behind in the team: presentation and implementation were the same thing, because for years they actually had been. So whenever the customer wanted to change what they saw on screen, the room assumed automatically that it meant touching everything, all the way down to the schema.
I spent a lot of time reminding people that wasn't true anymore. You could show a user something different without changing anything about how it was computed underneath. It's a little like the instinct behind GraphQL, where a client requests the shape of data it wants over a fixed set of underlying capabilities without touching what computes them. GraphQL isn't a perfect parallel, since it can leak the shape of the backend if you let it. But the instinct is the one that matters here. What you show someone and what actually computes it are two different axes of change, and if you treat them as one axis, every UI tweak turns into a database migration.
That job left me with an itch I never quite scratched. If how it looks and what it does are genuinely different things, why do our tools and processes treat them as one? Everyone says code is the best documentation, the single source of truth. I've come to think that's backwards, or at least incomplete. Code is a fine source of truth for what one particular implementation currently does. It's a bad source of truth for what a system is supposed to do, because it fuses that intent to one specific how, in one specific language, shaped by whatever the UI needed that week. By ontology I just mean a careful description of what the system is for rather than how it happens to be built. And when I say the ontology is the source of truth, I mean something narrower than the title lets on: it's the source of truth for the code, not for the intent. Humans still sit above it. The ontology is a much better photograph of what the person wants, but it isn't the person.
How I got here changes what the claim is worth. I didn't read this in a book and then go looking for examples. I backed into it from that one frustrating job, reasoning from first principles about why a change to a screen kept dragging the schema along behind it. Only later did I find that people had been building formal versions of exactly this split for over twenty years, under names like Model-Driven Architecture and Ontology-Driven Software Engineering. That's the part I've come to trust most. I reasoned from my own scars to a conclusion, in my own words, and then found that a standards body had reasoned its way to the same place two decades earlier in a completely different vocabulary. That convergence is decent evidence that we're both looking at something real, and that I'm not just nursing a personal tic. I wasn't the first person to bump into this, and the fact that so many people bumped into it independently is itself the argument.
It's suddenly relevant again, for a specific reason. The older versions of this idea failed for several compounding reasons: the notation was punishing, the tooling was immature, and the organizations around it never really adopted it. Underneath all of those sat one weak link that mattered more than the rest, and it's the exact thing AI coding agents now repair. So I'll start with the twenty-year-old framework and the specific reason its first attempt largely failed, then the single recent change to how code gets generated that makes it live again, and then the newer and arguably nastier way it can go wrong once the old problem is fixed. Before that last part I'll look at the full pipeline when a team takes this seriously, and build the smallest working version by hand: a tiny system you can clone and run in a few minutes, where one plain-text file decides a real behavior and two completely different interfaces both obey it without knowing about each other. It shows, in miniature, both the payoff and the way it can fail.
Code Is a Photograph
A photograph of someone is accurate. It really does show what they looked like, in that light, from that angle, at that moment. But if you wanted to know what they actually look like in general, you wouldn't hand someone a single snapshot and call it the source of truth. You'd want something that captures the person independent of any one photo: their bone structure, not one shadow it happened to cast. That analogy is what made it click for me.
Code is the photograph. It's a faithful record of what one implementation does, in one language, on one platform, shaped by one team's choices about database engine, framework and API style on the day it was written. It's real and it runs, but it's a projection, and projections lose information going the other direction. You can easily render a three-dimensional object as a flat shadow. You can't always reconstruct the object from the shadow.
An ontology, in this sense, tries to describe the object instead of one of its shadows. Take password reset. The ontology doesn't say how the endpoint is implemented in Java with a Redis-backed token store. It says that a user who forgets their password must be able to regain access without contacting support, that tokens expire after some fixed window, and that a token can only be used once. That description doesn't care whether you build it in Java, Python or a spreadsheet macro, or whether you expose it as a web form, a CLI or a chat bot. Those are all shadows of the same object.
The whole argument turns on this next point. The ontology isn't the person either. The person is the human intent behind the system: the actual decisions someone made about what it should do and why. The ontology is a much better photograph of that intent than code is, a studio portrait instead of a snapshot, higher fidelity and less warped by incidental choices. But it's still a photograph. Code, then, is a photograph of a photograph, a projection of the ontology narrowed to one runnable form. That's why the ontology can sit above the code as its source of truth while still answering to something above it. Keep that ladder in mind: intent at the top, ontology beneath it, code at the bottom. It's the difference between the title of this piece and its ending.
I didn't know this had a history when I started chewing on it. It does, and the most useful thing about it is that it already failed once.
The Idea Is Twenty Years Old, and It Already Failed
The oldest formal version I found is the Object Management Group's Model-Driven Architecture, standardized back in 2003. MDA splits a system into a Platform-Independent Model, the "what," free of any specific technology, and one or more Platform-Specific Models derived from it, the "how" for a given platform. In principle, the same platform-independent model could generate a Java implementation and a .NET implementation without a single change to the model itself. That's the object and its shadows, written down two decades before agentic coding made anyone care again.
Academic work on Ontology-Driven Software Engineering picked up a version of this in the mid-2000s. MDA thinks in terms of a model. This line of work thinks in terms of an ontology proper, a graph of concepts and the relations between them, and treats that graph as the mediator and source of truth for a system, with code generated from it rather than written by hand against it. The formalism is different and the instinct is the same: the description is primary, and the code is downstream of it.
The part most retellings leave out is the part that matters most. As a way of actually building mainstream software, this largely failed. The dream was full generation: you author the model and the running system falls out of the bottom. Outside a few niches like some embedded and enterprise-modeling shops, it never became how most software got made. MDA didn't take over the world, and neither did ontology-driven generation. For a long time that looked like a verdict on the idea. I've come to think it was mostly a verdict on the tooling.
The failure wasn't one clean thing, and it's worth being fair about that. The modeling notations were heavy, and getting a whole organization to author and trust models instead of code was a cultural fight that most teams simply lost. Those causes were real and they compounded. But under all of them sat one structural weakness that made the rest fatal: the generation step, the arrow that runs from model down to code, was too weak to carry the weight the idea placed on it. The only generators anyone had were template engines and CASE tools, and all they could really do was mechanically expand a rigid, fully formal model. Feed them structure and they emit boilerplate. They can't infer, and they can't fill a gap with judgment. They can only expand exactly what you already spelled out.
That one weakness had two consequences, and between them they quietly strangled the approach from both ends. The first was on the way in. Because the generator could only expand what was already fully specified, the model had to be pinned down in exhausting formal detail, so detailed that authoring it was about as much work as writing the code. You weren't saving effort, just moving it into a less familiar notation. Human-level intent is vague and contextual and full of unstated assumptions, and running code is none of those things. That gap was too wide for a template to cross, so you had to close it yourself, by hand, before the generator would run.
The second consequence was on the way out. It usually gets dropped from this story, and it comes back later in a nastier form. The round trip was brittle. The instant a developer edited the generated output, the link between model and code broke, and they always had to edit it, because the generated code was never quite right. Regenerate and you'd stomp the hand edits. Maintain both by hand and you'd watch them drift apart until neither described the system. The tooling threw real engineering at this: marked-off "protected regions" the regenerator promised to leave alone, or a split where the generator produced a base class and you hand-wrote a subclass to hold the parts it couldn't express. Those were ingenious, and they were a losing battle. The interesting work always crept into the very places the generator wasn't allowed to touch, and the model slowly stopped describing the running system and became a stale sketch of one. So people wrote the code by hand and let the models rot, which is exactly what you'd predict. The map was fine. The only thing anyone had to turn the map into territory was a glorified copy-paste macro, and a macro can neither bridge intent on the way down nor absorb a human's edits on the way back up.
None of that argues against the idea. It argues against the only generator anyone had at the time, and the generator is what the last couple of years have quietly changed.
What Changed Is the Generator
The generation step can now be an AI coding agent, and an agent is the first generator I've seen that can work in the register the idea always needed. A template engine could only operate on the form of what you handed it, shuffling text around a skeleton you had already fully specified. An agent operates on the meaning. That move, from manipulating form to manipulating intent, is the whole of what changed, and it cuts in both directions of the round trip. It does very different things to the two consequences, though, so it's worth being honest about which is which.
The first consequence, over-specification, the agent dissolves outright. A template engine could only expand a formal model. An agent can consume informal, human-level intent, the vague, contextual, half-specified kind, and turn it into working code, filling in the unstated assumptions the way a competent developer would. So the model no longer has to be so complete that writing it costs as much as writing the code. It can be closer to a careful description of what you want, and the generator meets it partway. The gap between intent and code, the thing that made authoring the model as expensive as writing the software, is exactly the gap an agent is built to cross. That problem doesn't get mitigated. It goes away.
The second consequence, the brittle round trip, does not go away. I don't want to pretend it does, because that sleight of hand would make this whole revival dishonest. If anything the raw danger is worse now. A human can still open the agent's output and edit it. Agent-written code looks so polished and plausible that the temptation to tweak it in place, instead of going back up and changing the intent, is stronger than it ever was with ugly generated boilerplate. Edit the code directly and you've desynced it from the ontology just as surely as anyone did in 2005.
The risk didn't vanish. It moved from impossible to merely hard. In the template era, keeping model and code in sync through hand edits was structurally impossible, because the tooling couldn't understand a human's change well enough to reconcile it. Now the same generator that consumes intent can re-derive the code from an updated ontology instead of being patched in place. And it has a move the template never had. Because it reads meaning rather than form, it can read code a human already touched, work out what the edit was trying to do, and then either fold that intent back up into the ontology or tell you plainly that the two have diverged. Layer real traceability over the top, a grounding graph that ties each decision in the ontology to the files and tests that implement it, and drift becomes something you can detect and reconcile instead of something that silently accumulates. The round trip stops being a law of physics and becomes a discipline you have to keep. That discipline isn't free, and I'll come back to why when we get to what a serious version looks like. But a discipline you have to keep is a long way from a contradiction you can't resolve, and that difference is the whole reason the idea is alive again. The framework wasn't wrong. It was early. It was waiting for a generator flexible enough to consume intent on the way down and to re-derive rather than desync on the way back up, and agentic coding is the first thing that qualifies.
This is also where the first-principles convergence I mentioned earlier starts to look like evidence rather than a coincidence. The same seam shows up all over the place under different names, which is why it's so easy to bump into it alone and think you've found something new. An orchestrator kicks off a saga while separate services execute each step. A backend-for-frontend shapes a response while the real backend does the work. Domain-Driven Design splits an application layer that coordinates a use case from a domain layer that actually enforces the rules. MDA has its model and its platform-specific derivations, and the ontology-driven graph has its generated code. The vocabularies differ, but the shape recurs: something initiates and presents, something else decides and executes, and the first is never allowed to dictate the shape of the second. When people who weren't talking to each other keep reinventing an idea, and one of them is you, reasoning from a job that annoyed you, the independence is what carries the weight. Fashion needs a shared conversation to spread. A pattern that keeps surfacing in fields that never compared notes is far likelier to be tracking something structural than something merely in vogue.
What It Looks Like When You Take This Seriously
None of the sources in this lineage stop at "keep the model separate from the code." Push the instinct all the way and you get a pipeline with distinct layers, each answering to the one above it:
- Human-authored architecture decisions sit at the top: the actual design tradeoffs and rationale a person wrote down, the highest authority in the system.
- An ontology underneath that: a description of behavior in terms of intent (why a rule exists) and decisions (what the rule actually says), independent of any programming language.
- Multiple views derived from the ontology: a product-facing view, an architecture view, a test-strategy view. Each presents the same underlying thing to a different audience.
- Generated code at the bottom: the actual implementation, meant to be a mechanical output of everything above it, not a place where new decisions get made.
Notice where the humans are: above the ontology, not below it. That's the qualifier from the start of this piece made concrete, the same ladder of intent over ontology over code. The ontology is authoritative over the code beneath it and not over the intent above it. It's the source of truth for the implementation, and it's itself derived from something it doesn't own.
What I find interesting is that the line doesn't run straight from ontology to code. The ontology feeds several different, independently changeable surfaces, and none of them should be allowed to leak backward and redefine it. A test writer shouldn't need to touch the ontology to write a test. Someone reading the product view shouldn't need to understand the implementation language underneath. That's the instinct from my old job again, finally given a shape: presentation, however many presentations you need, sits downstream of a single description of behavior, never upstream of it. This is also where the round-trip discipline from the last section lives. You could build real traceability on top of this, with every decision traced forward to the files and tests that implement it. Then changing a rule tells you everywhere downstream that has to change, changing code tells you which rule it's supposed to be honoring, and drifting code lights up instead of hiding. That part isn't free. It's tooling and discipline layered on top of the idea, and most teams skip it because the payoff shows up months later, not on day one. It's also the exact discipline that turns the old round-trip failure from a dead end into a chore.
You can see a partial form of this right now. A lot of the current spec-driven development tooling, GitHub's Spec Kit, AWS's Kiro, and the broader push behind intent-driven.dev, is built on a version of the same claim: the spec, not the code, should be the thing you trust and keep. (intent-driven.dev is a site organized around treating intent and context, rather than code, as the artifact you structure and maintain, so agents build what you actually meant.) That's a real improvement over writing code first and documenting it never. But it's a narrower move than this lineage points toward.
Birgitta Böckeler's survey of these tools, written for Martin Fowler's Exploring Gen AI series, sorts them along a ladder. At the bottom is spec-first, where the spec is scaffolding you throw away once the code exists. Then spec-anchored, where the spec lives alongside the feature. At the top is spec-as-source, where the spec is the primary file and the human never touches the code. What strikes me is that even the tools reaching for that top rung still keep, in her description, roughly a one-spec-to-one-implementation relationship. One of them she describes as a straight 1:1 mapping, one spec file to one file in the codebase. None of them has yet made the further split this lineage insists on, between the one thing that's true and the several independently changeable ways you're allowed to show it. And the old round trip is right there, showing it hasn't been solved. At one point she watches an agent take her hand-written notes about existing classes as if they were a fresh specification and regenerate the lot as duplicates. That's the MDA round trip breaking again, today, under a far better generator. A spec that quietly hardens into an implementation plan has only moved the coupling problem one document over. Without the traceability discipline to catch the drift, the better generator will happily desync faster than the old one ever could.
Further out, and at real scale, Palantir positions its Foundry Ontology as exactly this kind of source of truth for both data and logic. In the company's own framing, that lets AI agents acting inside a business be governed and inspected at the ontology level instead of hiding their reasoning inside opaque code. That's a vendor describing its own product, so take the specifics as a pitch rather than a neutral finding. But the shape of the pitch is telling. It's more or less the old MDA promise, sold on the strength of exactly the generator MDA lacked.
Building the Smallest Version of This Yourself
None of these sources hand you something small enough to hold in your head, so I built the smallest version I could. It's small enough to clone and run in a few minutes, using nothing but the standard library and PyYAML. The behavior it governs is whether a password reset request should be allowed. One caveat before the code. I'm using the word "ontology" loosely. To the MDA people it's a formal model, and to the ontology-driven crowd it's a graph of concepts and relations. What I'm about to show you is neither. It's a flat little decision table in a YAML file, so the word is doing lighter duty than the literature would like. The instinct is identical, though, and the instinct is the point.
The entire source of truth is one YAML file:
behavior: password_reset
intent:
I1: >
A user who forgets their password must be able to regain access
without contacting support.
decisions:
D1:
rule: "A reset token expires after a fixed number of minutes."
field: token_ttl_minutes
value: 15
D2:
rule: "A reset token can only be used once."
field: single_use
value: true
D3:
rule: "A new password must be at least this many characters."
field: min_password_length
value: 12
The file carries more than values. Each decision has a human-readable rule, and the whole behavior has an intent, I1, saying why it exists at all. The field names, token_ttl_minutes and the rest, are there for a code generator to key its output off. This toy has no generator, so for now they honestly just sit there as documentation. I'd rather flag that than pretend the toy uses every part of the file.
The file has no idea it will ever be called from a terminal or an HTTP request. It just states what's true. One small piece of Python reads it, and that's the only place the decision gets made. Here it is in full, so you can run it exactly as shown. When it denies a request it doesn't only say no. It cites the authored decision that said no, quoting the rule text straight out of the ontology:
"""
The only place the password_reset behavior is actually decided.
Every surface (CLI, API, anything else) calls this and nothing else.
Each answer cites the authored ontology entry behind it, so a decision
traces back to prose a human wrote, not to a bare number in this file.
"""
import yaml
from pathlib import Path
ONTOLOGY_PATH = Path(__file__).parent / "ontology.yaml"
def load_ontology():
with open(ONTOLOGY_PATH) as f:
return yaml.safe_load(f)
def evaluate_reset(token_age_minutes, token_used, new_password):
ontology = load_ontology()
intent = ontology["intent"]
rules = ontology["decisions"]
def deny(decision_id, detail):
return False, f'{detail} (see {decision_id}: "{rules[decision_id]["rule"]}")'
if token_used and rules["D2"]["value"]:
return deny("D2", "token already used")
ttl = rules["D1"]["value"]
if token_age_minutes >= ttl:
return deny("D1", f"token expired ({token_age_minutes}m >= {ttl}m ttl)")
min_len = rules["D3"]["value"]
if len(new_password) < min_len:
return deny("D3", f"password too short (need {min_len} chars)")
return True, f'reset allowed (serving I1: "{intent["I1"].strip()}")'
That citation does real work, small as it is. With a bare return False, "token expired", the ontology's prose would be purely decorative. The engine would only ever read value, and you'd be right to call the whole thing a config file with delusions of grandeur. But the denial quotes rules["D1"]["rule"] and the allow quotes intent["I1"], so the sentences a human wrote are load-bearing. Change the wording of D1 in the YAML and the reason the CLI prints changes with it. It's the smallest possible demonstration that the prose, not just the numbers, is part of the behavior.
Then there are two surfaces, a command-line tool and a tiny HTTP server, and both call that one function and nothing else. Here's the CLI against a few cases:
$ python3 cli.py --token-age 10 --password "correct-horse-battery"
ALLOW: reset allowed (serving I1: "A user who forgets their password must be able to regain access without contacting support.")
$ python3 cli.py --token-age 20 --password "correct-horse-battery"
DENY: token expired (20m >= 15m ttl) (see D1: "A reset token expires after a fixed number of minutes.")
$ python3 cli.py --token-age 5 --used --password "correct-horse-battery"
DENY: token already used (see D2: "A reset token can only be used once.")
$ python3 cli.py --token-age 5 --password "short"
DENY: password too short (need 12 chars) (see D3: "A new password must be at least this many characters.")
Each of those denials traces back to a specific authored decision, by id and by the sentence a person wrote, instead of dying on an anonymous number buried in a function. That's the difference between a rule and a magic constant.
The HTTP surface gives the same rules and the same answers in a completely different shape:
$ curl -s -X POST localhost:8934/reset -d '{"token_age_minutes": 10, "used": false, "password": "correct-horse-battery"}'
{"allowed": true, "reason": "reset allowed (serving I1: \"A user who forgets their password must be able to regain access without contacting support.\")"}
$ curl -s -X POST localhost:8934/reset -d '{"token_age_minutes": 20, "used": false, "password": "correct-horse-battery"}'
{"allowed": false, "reason": "token expired (20m >= 15m ttl) (see D1: \"A reset token expires after a fixed number of minutes.\")"}
It's a toy server, so I'll be honest about its edges. A request that's missing a field or carrying malformed JSON gets a 400 with an error object, not a stack trace, because the handler wraps its body in a try/except. That's the floor you'd want, and nothing like what a real endpoint needs:
$ curl -s -X POST localhost:8934/reset -d '{"token_age_minutes": 10}'
{"error": "bad request: 'used'"}
Now the actual point, which goes in two directions. Start with the one the old tools always demonstrated: change a rule, and every surface follows. If the business decides tokens should live for 30 minutes instead of 15, that's a one-line change to the ontology, not to either surface. Open ontology.yaml and change value: 15 to value: 30 under D1. On Linux you can do that in place as a shortcut:
$ python3 cli.py --token-age 20 --password "correct-horse-battery"
DENY: token expired (20m >= 15m ttl) (see D1: "A reset token expires after a fixed number of minutes.")
$ sed -i 's/value: 15/value: 30/' ontology.yaml
$ python3 cli.py --token-age 20 --password "correct-horse-battery"
ALLOW: reset allowed (serving I1: "A user who forgets their password must be able to regain access without contacting support.")
That sed line is a convenience, not the lesson, and it's fragile. It works only because value: 15 happens to be unique in this file, and on macOS the same command needs an argument right after -i (sed -i '' 's/value: 15/value: 30/' ontology.yaml). The safe instruction is the boring one: open the file and change the number. Either way, neither cli.py nor api.py was touched, and neither one even knows the other exists. If someone adds a third surface tomorrow, a chat command say, it inherits the current rules for free, because the rules live in exactly one place and everything else just asks.
But that was never the direction that itched. The story I opened with was the opposite one, where presentation got falsely chained to implementation and repainting the door somehow meant rewiring the pipes. Say I want the CLI to speak JSON, so another program can consume its output. That's purely a presentation concern, and it should cost me nothing in the ontology or the engine. Here's cli.py with the flag that does it:
"""Surface #1: a command-line tool. Presents the behavior, doesn't define it."""
import argparse
import json
from engine import evaluate_reset
parser = argparse.ArgumentParser()
parser.add_argument("--token-age", type=int, required=True)
parser.add_argument("--used", action="store_true")
parser.add_argument("--password", required=True)
parser.add_argument("--json", action="store_true", help="print the result as JSON")
args = parser.parse_args()
allowed, reason = evaluate_reset(args.token_age, args.used, args.password)
if args.json:
print(json.dumps({"allowed": allowed, "reason": reason}))
else:
print(f"{'ALLOW' if allowed else 'DENY'}: {reason}")
And a run, using a case that's allowed whatever the token lifetime is set to:
$ python3 cli.py --token-age 10 --password "correct-horse-battery" --json
{"allowed": true, "reason": "reset allowed (serving I1: \"A user who forgets their password must be able to regain access without contacting support.\")"}
Look at where this change doesn't reach. Adding the flag touches exactly one file, cli.py. It doesn't go near engine.py or the ontology, and it doesn't disturb api.py, the other surface, which still answers exactly as it did. The flag only reformats the allowed, reason pair that already comes back from the engine. It adds no rule, changes no decision and asks nothing new of the source of truth, because presentation is not where rules live. If you're tracking the folder in git, the only path that moves is cli.py. That's the itch from the Delphi job, scratched at last, in about four lines.
Put the two directions side by side. The first changed what the system does, and both surfaces followed, because there was only ever one place the answer lived and nothing downstream is allowed to become a second one. The second changed how one surface looks, and nothing underneath so much as noticed.
You should be suspicious by now. A reasonable person is squinting and saying this is just a config file, and that reading a value from one place instead of hard-coding it in two is just DRY, the first thing anyone learns. That's fair, and at this size it's true. The rule citation dresses it up, but you could fairly call that fancier logging: the decision still turns on value, and nothing reads the field names in the YAML at all. Without the framing, what I've shown you is a config file plus a shared function, and you don't need the word "ontology" or a twenty-year lineage to justify not copy-pasting a number. The framing only earns its weight when the pieces I've been waving at actually exist. A serious version has five things this toy doesn't: several derived views instead of one, real code generation instead of a hand-written engine, a grounding graph tying each decision to the files and tests that implement it, cycle detection so the graph can't quietly contradict itself, and a review process before anything lands. Those five are the difference between a diagram and a system, and they're also where all the hard problems live. Below that threshold, call it a config file and be happy. Above it, config is the word you'll wish you'd stopped using.
So when is this overkill? "It depends" is a cop-out, so I'll be concrete. Most of the time, for most software, it's overkill. A small app with one implementation and one surface should reach for a config file and move on. The seam I've been describing is pure cost until something starts pulling on it, and three things do.
The first is surface count. Once you have several presentations over one behavior, a web UI and a mobile client and a partner API all enforcing the same reset rules, the cost of keeping them honest with each other by hand starts to climb, and a single source they all defer to starts to pay. The second is longevity and scrutiny. Behavior that's regulated, audited, or simply long-lived enough that someone will ask "why does it do this, and who decided that" years after everyone who knew has left benefits from carrying its own intent and rationale, instead of leaving them to be reverse-engineered out of code. The third is implementation count. The same rules realized more than once, in different languages or services, is the original MDA use case, and it's where a description that outranks all the implementations stops being a luxury.
The practical tell that you've crossed over is boring and specific. You catch yourself hand-copying the same rule into a second and third place and quietly praying they stay in agreement. Or you find yourself answering "why does it work this way" by doing archaeology through code and commit messages, because the reason was never written down anywhere a person could just read it. Before those moments, reaching for the machinery is MDA's original mistake in miniature: paying the full authoring cost up front and never collecting the payoff, because there's only one surface and one implementation to keep honest. When none of the three pulls is real, you're building a cathedral to house a garden shed's worth of decisions. This toy is deliberately in the shed, so the machinery is small enough to see all at once. It's a diagram of the idea, not an argument for using the idea at this scale.
Take every one of those caveats away, though, and one claim survives at every size, from the toy to the cathedral. Behavior and presentation are two different axes of change, and when you build the seam between them on purpose, moving along one stops dragging the other with it. That's what the toy honestly proves, and it's the seam this whole lineage is built around.
Where This Gets Messy
There's a failure mode here that stops being hypothetical the moment an AI agent is the thing maintaining the ontology, and that's the arrangement the revival depends on. Remember the ladder: intent at the top, the ontology as a photograph of it, code as a photograph of that. Putting the agent underneath the ontology as the generator fixes the old problem, the weak generation step. But it opens a new one directly above, at the ontology itself. The original approach failed because the generator couldn't reach up to human intent, which made both the over-specification and the round-trip problems intractable. The new generator can reach up, which is the entire point, but that cuts both ways. An agent that can turn loose intent into working code can also invent loose intent. Having an explicit source of truth doesn't stop an agent from hallucinating. It just moves the hallucination up one level, into the one artifact everything downstream now trusts without question.
An agent can write a perfectly plausible rule, or a whole feature nobody asked for, straight into the source of truth. Every view and every generated file below it will then faithfully render the invention as if it were real and had always been there. The old tooling at least made you write the wrong thing down in code, where a reviewer had a chance to catch it. The new arrangement lets a confident mistake enter at the top and propagate downward, cleanly, everywhere, at once.
What strikes me is how this rhymes with the two old failures and inverts both. The old generator was too rigid to reach intent, and it produced a model too detailed to be worth authoring. The new one reaches intent so fluently that it can author intent that was never there. The old round trip broke loudly, with a regeneration stomping your edits where you'd see it. The new one can break silently: a fabricated rule flows down into code and tests that all agree with each other, because they all came from the same invention. The failure didn't go away. It went up the ladder and got quieter, which is worse, because it now looks exactly like the thing you meant to trust. It's an extra wing on the blueprint, and everything below will dutifully build it in every surface without a warning, because as far as that machinery is concerned the wing was always part of the design.
This is where the title has to be qualified. The ontology is the source of truth for the code. It's authoritative over every shadow it casts. It isn't authoritative over itself, because it's still only a photograph of a human intent that sits above it, and a photograph can be doctored so smoothly that nothing else in the album can tell, especially one an eager agent just took at three in the morning. So if you let agents write into this layer, you need the same discipline you'd want around a database migration: a human check before anything lands, and a standing rule that new content has to trace back to a decision an actual person made, not merely to the model's own confidence. This is the same traceability I called not-free two sections ago, and here's the bill for skipping it. Without a grounding graph tying each rule back to an authoring decision, you have no way to tell an invented rule from a real one, because both render perfectly. The ontology can be authoritative over the code and still answer to the people above it, and holding both of those at once is the whole game.
So the discipline matters more than any particular tool. The idea is twenty years old, and it flopped the first time for want of a good generator. We finally have the generator, which is why an old, half-buried idea is live again, and it's also what makes the discipline non-optional now. Whatever you use to describe what your system should do, resist the pull to let any single presentation of it, whether a UI mock, a spec doc or a first working prototype, become the thing everyone treats as ground truth without checking. Resist just as hard the newer pull to trust the description itself simply because it's written down and the machine sounded sure when it wrote it. The description is a photograph too. It's useful and worth keeping, but it isn't the person.