The context you author
Which of Your Words Is Which
The noun it got wrong
The agent got one word wrong. Everything downstream of that word was correct.
The task touched a thing called a workspace. It never asked me what that meant, because it did not know there was anything to ask. It already had a perfectly serviceable meaning: a workspace is the place where I do my work. The place where I do my work is this repository. So this feature is about this repository.
Then it planned. Carefully, at length, in the way you actually want an agent to plan. What came back was a full restructuration of the repo. Directories moved, module boundaries redrawn, an ordered migration with the risky steps flagged and sequenced. Genuinely good work, sitting on the wrong noun.
There was a real Workspace in that codebase. A domain object, with fields and tests and callers. The agent had invented a second one and never noticed the collision, because from the inside there was nothing to notice.
I caught it at the implementation-plan review, the last gate before an agent starts touching files, and I refused the plan. That gate is a backstop, not a fix, and where to put gates is its own discipline.
What stays with me is how late it was. The wrong noun survived the reading, the reasoning, the drafting, and the agent’s own review of its own plan. Nothing tripped, because nothing was inconsistent. Its reasoning was sound the whole way down.
A wrong word does not fail loudly. It compounds quietly until something expensive is built on top of it.
Which of your words is which
Here is what I take from that. Your agent does not need more of your codebase. It needs to know which of your words is which.
That sounds minor next to model choice, or tooling, or how much autonomy you hand over. It is not. The previous post ended on a question it deliberately refused to answer: of everything you could always load into the window, which tokens actually earn their place? This is the answer, and it is a smaller answer than most people expect.
The reflex is to close the gap by feeding the agent more. Point it at the code. Let it explore first. Give it the docs, give it the wiki, give it the ADRs. That reflex fails for two reasons, and they are not the same reason.
The first: the code cannot tell it. A real repository holds several live meanings of your word, so reading more of it makes the ambiguity worse rather than better.
The second: half the time you cannot tell it either, because your team never settled the word.
I’ll take them in order.
ℹ️ Indirection? Post 1 gave you progressive disclosure: load a doc or a skill only when a step actually needs it. Indirection is the primitive underneath it. In computer science, an indirection is a way of referring to something using a name, reference, or container instead of the value itself, and each hop through such a name is a level of indirection. DNS is the picture.
en.wikipedia.orgstands in for a network address like208.80.154.224, so the reference stays short, memorable, and survives the thing moving. Hold onto that last property. It comes back.
The code is not a glossary
“Just let it read the code first” assumes the code is a glossary. It is not. It is a place where words live, and words live there in more than one sense at a time.
That workspace repo had integrated git. Git has a workspace. So the tree contained a real, working, heavily referenced meaning of the word that had nothing to do with the domain object of the same name. An agent grepping for workspace finds both, and both are legitimate. More reading does not resolve that. More reading adds candidates.
The neighbours arrive from three directions, and only one of them is yours:
- Your dependencies. Git’s workspace, your framework’s session, your ORM’s entity.
- Your own tooling vocabulary. The words your build system and your scripts already claim.
- The agent’s own harness vocabulary. This is the ambush.
At Packmind, the product’s domain entities were named standard, command and skill. Those are also words an agent uses about its own scaffolding. So “update our standards” kept being read as update the curated context instead of update the code that handles standards. It went to edit the wrong layer of the system entirely.
That one I caught in seconds, because an agent editing the visibly wrong file is obvious. The workspace case survived to a finished plan. Same class of error, wildly different cost, and the only variable was how fast the mistake became visible.
Giving it a lexicon fixed the Packmind case outright. The reaction I wrote down at the time was “it knows what we are talking about.”
But not for the reason I first assumed. The fix is not that the entry defines your word. It is that it names the ones next to it. Back in the git-integrated repo, the entry that finally settled workspace put the domain object beside the git objects that same tree used, in the space of a few lines. The agent did not get a better definition. It got a boundary.
Define your word and you have told the agent what it is. Name the neighbours and you have told it what it is not. The second one is what it was missing.
Cart, trolley, basket
Now the less comfortable half. Most of the time, the agent’s confusion is inherited. It started with the humans.
The tell is synonyms. Cart, trolley, basket. They probably all mean the same thing. Are you sure? Reading that codebase right now, could you say whether those three words name one concept or two, and which one a new class ought to use?
If you cannot, neither can the agent, and it is worse off than you are. Search is its primary tool. It greps, reads what comes back, greps again. Synonym drift breaks that silently. You cannot search for a word you don’t know is the canonical one, and a miss produces no error. It produces a smaller result set, and a confident answer built on it.
That is mechanical harm, not tidiness. It is not a style preference about naming things nicely. It is the agent’s main instrument quietly returning partial results.
This has a name, and it is older than any of this. Eric Evans named the practice ubiquitous language in Domain-Driven Design, published in 2003. That is more than two decades of people making the same argument to the same room: settle the words, then write the code.
I did this before agents existed, and I did it for the humans. I do it more now, because the bill arrives faster. A senior engineer silently compensates for a fuzzy word all day long. An agent just proceeds. Starting by grounding the semantics is not only good for agents.
What earns an entry
So you write a lexicon. And immediately the wrong instinct shows up: describe the code.
Don’t. The lexicon is not documentation of the code. It is the design surface you argue on.
Here is what convinced me. Another project, another ill-defined workspace, except this one was ill-defined for the humans too. Nobody on the team gave the same answer twice. project was already written down as an entry with its relationships, and sitting in front of those few lines made drafting the real relationships between everything else fast and precise. We ended up changing the definition of workspace so that it fit the mental model we had been carrying around unexamined.
You do not do that to a document that describes code. You do it to a design artifact. That single property decides everything else about what belongs in the file.
It is pure business. The lexicon knows nothing about code or techniques. It holds no rules. Anything with a should in it is not a lexicon entry, it belongs in the artifact that holds your architecture decisions.
Interfaces sometimes earn an entry. Their implementations never. One Git Provider earns a line, because the domain has exactly one idea of a git provider. The concrete providers behind it do not, because the domain does not know they exist. Not all interfaces earn to stay in the lexicon, but when they do, their implementation should never follow.
It holds fewer entries than the code has entities. There is no 1:1 mapping and there should not be. The extra coded entities exist for maintainability or for performance. Those are real reasons, and they are not domain facts.
The exclusions fall out of that. No value objects, no ID types, no commands or events, no infrastructure types, no polymorphic subtypes. Two of those, value objects and events, are Evans’ own categories. Value objects from the 2003 book, events from the 2015 reference that followed it. The rest come from the same test: does the business ever argue about this thing? Nobody argues about an ID type.
And every entry carries an owner. Not a definition standing alone, but a place in a hierarchy. A LineItem is owned by a Cart, a Cart is owned by a Customer, and Customer is a root entity. That column is what makes the artifact something you can argue on. Relationships build the human mental model first. Then, when an agent drafts a plan, they make the scope of a task visible: touch the Cart and the ownership chain tells you what else is in the blast radius.
Who writes it? Both of us. The agent drafts most entries out of a design session. I rewrite parts by hand when that is faster than asking for it, which it often is. The ongoing job is not correction, it is deletion: implementation details that crept in and carry no domain value. That is the ONE document you want to stay sharp.
Ground twice
There is a popular fix for the wrong-noun problem that I want to name and then reject. Make the agent mark its assumptions in-band. Have it tag what it decided, what it is unsure of, what it needs from you, then read the tags and answer them.
I ran that for a while. It treats a sequencing problem as a labelling problem. The question is arising at the wrong moment, and improving the notation does not move the moment.
Order the phases so it cannot arise. Ground, then challenge and design, then update the lexicon, then ground again, then spec, then implement.
Six steps, and the one people ask about is the fourth. Why ground twice?
Because design and spec want different contexts. When I’m challenging a design I want the domain concepts and their relationships and almost nothing else. No implementation details. And that session explores: it argues, it produces mockups, it goes down two paths and abandons one. All of that is valuable while it’s happening and radioactive afterwards.
So each phase gets a fresh handoff note. Not a growing one. The spec session inherits the locked decisions and no trace of the argument that produced them. Deliberate amnesia, on purpose, at every boundary.
Which is also why the lexicon is never preloaded. The first session reads it, or the part of it that matters. What rides forward is the extract, inside the handoff note, never the file. That is the indirection from earlier doing its job: the lexicon sits beside the pipeline and gets reached by name, so it can grow to any size without a single session paying for it.
Since I ordered it this way, those questions stopped surfacing at the wrong time. The one thing that still escalates is a genuine blind spot in the implementation plan, where the agent hits something it cannot resolve and stops to ask. That is the escalation I want. It is the only one left.
Small enough to review
Post 1’s diagnostic was an instrument. A status line that changes colour as the window fills, so you cannot miss the number even when you’ve stopped reading it. There is no instrument here. Nothing to watch.
What there is instead is a size. The grounding phase produces about twenty lines. Sometimes closer to thirty. It looks like this:
## Grounding (Phase 1)
**Lexical terms used**: Cart, LineItem, Checkout, Order, Customer, SavedBasket
(in code only — not yet in lexicon).
**Vocabulary discrepancies found**:
- **SavedBasket** exists in code (`services/checkout`, `SavedBasketRepository`) but is
missing from `docs/LEXICON.md`. Follow-up: run `/update-lexicon` to add it under the
`checkout` section.
- Support calls this a "trolley"; the code calls it `Cart`; the design docs call it
`Bag`. Resolved → **"Cart"** is canonical going forward. "Basket" is reserved for the
saved-for-later list.
**Locked decisions reused**:
- A Cart belongs to exactly one Customer; guest carts are keyed by session.
`docs/design/checkout.md:41` (locked 2026-03-12).
- Cart totals are recomputed at checkout, never trusted from the client. `checkout.md:58`.
**Subagent findings**:
- Q: Does a Cart persist across sessions for guests? A: **No.** Session-scoped only,
dropped at 30 days. Source: `services/checkout/src/cart_repository.rs:112-130`.
- Q: Is there an existing SavedBasket → Cart transfer path? A: **None.** Net-new work.
Source: `services/checkout/` survey.
How long did that take you to check?
That is the mechanism. Not an alarm, granularity. The output of each phase is small enough that the review actually happens, instead of being something I intend to do and then skim because it runs four hundred lines and I have a meeting.
Many small reviewable outputs beat one big late one. Which closes this piece’s own loop. The workspace plan was caught at implementation-plan review, the last gate, by a human reading pages. Twenty-odd lines of grounding would have killed the wrong noun in the first minutes of the first session, and the careful plan built on top of it would never have been written at all.
The map, not the knowledge
So: of everything you could always load, which tokens earn their place?
Not the knowledge. The map.
There is one file your agent reads at the start of every session, whatever your vendor calls it. The cross-tool convention has settled on AGENTS.md, stewarded by the Agentic AI Foundation under the Linux Foundation. That file should hold what any agent, in any session, would otherwise burn tokens rediscovering. What this project is, in one sentence. What it’s built with. The commands to build it and test it. And a very short layout saying where the lexicon lives, where the design docs live, where the code lives.
That is the whole file. Mine never go past a hundred lines, and a hundred is already a lot. That is my own rule of thumb rather than a law, and if you arrange your work differently the number moves.
Notice what is not in it. The lexicon is not in it. The lexicon is pointed at by it, and read when a grounding phase needs it. Everything this piece has argued for rides forward as one line of a file, and the file it points to can be ten times larger without anyone paying for the difference. A pointer costs a line.
The guardrail is an old line, usually attributed to David Wheeler: all problems in computer science can be solved by another level of indirection. The addendum everyone adds is the more useful half. Except the problem of too many levels of indirection. A hundred lines of pure pointers with no substance is a map of a map, and no use to anyone. Keep the sentence that says what this thing actually is.
Which leaves the question I’ve been walking past. A lexicon is a document, and code moves out from under documents. That is what the discrepancy list in the grounding note is for. It compares the words in the code to the words in the file every session, so drift shows up as a line item instead of as a surprise.
Sources
- Indirection, Wikipedia. Retrieved 2026-07-28. https://en.wikipedia.org/wiki/Indirection
- Evans, E., Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley, 2003.
- AGENTS.md, Agentic AI Foundation (Linux Foundation). Retrieved 2026-07-28. https://agents.md/