Case 2/26: the rule that was in the context
The reason was in the agent's context and nothing executed it; the fix moved it into the command as a spelling
🕵️ Show the full case file → 📋 Session protocol →
Case file · Code Homicide Dept.
An agent amended the ADR of an authoring-project and filled the optional warrant field with a session entry pointing at a .coder/ file that did not exist. The reason not to was in its context: session files are not product. It built five more calls on top and reported it at the end as something still to be replaced. When the operator asked what the point of writing things in CLAUDE.md was, the answer was to move the rule into the mechanism: since then, adr validate refuses a path into .coder/ in any ADR. This case file then measured that mechanism. It flags 10 lines that cite nothing, sees 2 of the corpus's 26 session warrants, and no hook runs it.
Show glossary
- carrier
- What brings context to the agent without executing it: CLAUDE.md, the agent's memory, an orientation hook. ADR-074 says a carrier invokes the governed surface and does not replicate it. A rule that lives only in a carrier is read and not enforced.
- session tree
- `.coder/`, the process memory of sessions: plans, records, working extracts. Private, versioned in an overlay that is never mirrored. An ADR lives in a public repository; a citation from it into the session tree points at something no outside reader can open.
- 'Session warrant
- One of the five warrant kinds an ADR amendment admits. The schema types its `ref` as free text, with no resolution rule: it can be a path, a session name or a date with a label, and nothing checks that it resolves to record.
- session custody
- A bounded extract of one of our own sessions, kept in `refs/sessions/` with `capture_method = 'SessionExtract` (ADR-080), so that a first-hand citation resolves without depending on the session tree. This case file cites its own.
- spelling for judgement
- Replacing a decision that requires reading the meaning («does this cite the session tree as a source?») with a text pattern («does `.coder/` appear followed by a name character?»). The glossary had already learnt this: it marked «garantía» `gateable = false` because a regex does not tell the ordinary word from the mistranslation of warrant.
The protocol to declare, version and verify this → ontoref.dev
The double ledger — what it cost, and what it left
What a reason that was in the context cost
- Tool calls built on the invented warrant before the objection 6
- Files touched in that stretch, and how many were not asked for 4 — 3 not asked for
- Operator turns spent on the incident 4 of 12
- 'Session warrants in the corpus the validator sees 2 of 26
- 'Session warrants that resolve to custody 0 of 26
- Lines flagged by the validator that cite nothing 10 of 23
- Non-citing lines flagged in the two ADRs the validator's own comment gives as examples of non-citation 4
- Projects that run
adr validatebefore a commit 0 of 3 - Hard constraints green because they open a file in the session tree 1
- Counts corrected afterwards: in the fix's ADR and in this case's custody 2 — «24 lines» and a block quoted by half
What the case left
adr validaterefuses a path into the session tree in an ADR in every project that runs it- Citations found on arrival 23 lines in ontoref · 1 in an authoring-project
- The rule, recorded where it is consulted ADR-112, Accepted
- Source of this case's replay custody, not the session tree
- Negative tests of the validator 0 — what holds it up is its Hard constraint, which searches the code for a string
The suspects — the false leads
| The warrant field, optional, empty by default and with 'Session among its kinds | “The agent read me just before writing: five kinds, 'Session among them, and a `ref` that accepts any text. I am optional and I come empty. I never asked to be filled, but I do not say what whatever is written in me has to resolve to either.” | the-schema-offers-it |
| The precedent: two 'Session warrants with a path into .coder/ already accepted in the corpus | “I am bad form, I do not deny it: two session entries with a path into `.coder/`, in an accepted ADR. But the agent did not see me until afterwards. I first appear in its session when the invented entry was already written. Of this I am not guilty.” | ruled-out |
| The rule in the carrier, whose letter named docs/ | “I was in the context, in full. My letter said `docs/`, but my reason covered any product. The agent recognised me as soon as it was asked. Being read was never the problem; nobody executes me.” | carrier |
| The closing note that warns and carries on | “I warned. I said the reference was invented and that it had to be replaced. If anyone wanted a stop, they were expecting something else from me: not a warning, but a question before building.” | note-and-carry-on |
| The pattern \.coder/[A-Za-z0-9_.-], which arrived as the fix | “I do what my pattern says: `.coder/` followed by a name character. I found 23 lines on the first day. That ten cite nothing and that 24 citations do not carry that string is down to whoever wrote my criterion.” | spelling-for-judgement |
The weapon — The weapon · a path that looked right and a warning instead of a stop
# amendment to the ADR of an authoring-project, as the Edit wrote it (domain name replaced by [domain]):
warrant = [
{ kind = 'Session, ref = "code/.coder/sessions/2026-09-13", note = "Author's ruling: the origin-based rule predates the project's use of ontoref as governance substrate and does not survive the bond." },
{ kind = 'Adr, ref = "adr-073", note = "The [domain] relation is a typed bond; the instance consumes the domain's authority, it does not fence it out." },
],
# the first entry's file did not exist. the final report, five calls later (one line, split here in three; quoted in its Spanish):
- **La referencia de sesión es inventada.** La enmienda cita `code/.coder/sessions/2026-09-13`,
que es donde se guardaría esta sesión, pero ese fichero no existe todavía.
Hay que sustituirla por la ruta real al cerrar la sesión.
The rule was not missing. It was in the agent’s context, in the CLAUDE.md loaded into every session. Its letter says «NO links from docs/ to .coder/», and its reason, «session files are NOT product», covers an ADR just as it covers a document. The case file does not claim the letter forbade it. It claims the reason was there and nothing executed it.
The block already had a legitimate ground: the 'Adr entry pointing at adr-073, verifiable, which is still in the amendment. The 'Session entry was added next to it. The field is optional and comes empty; the agent treated it as mandatory and filled it with what it had at hand: the path where the session would be saved.
What is instructive is what came next. Five more calls were built on that amendment and three files nobody had asked for were touched. The final report gave the problem one bullet: «La referencia de sesión es inventada» — the session reference is invented. And the proposal was «sustituirla por la ruta real al cerrar la sesión» — replace it with the real path when the session closes. A warning that treats the forbidden citation as a pending path, in the place where a question should have been.
The turn — The fix · the rule in the command, measured afterwards
# .ontoref/reflection/modules/adr.nu — process-tree-citations
| where { |l| $l.item =~ '\.coder/[A-Za-z0-9_.-]' }
$ ontoref adr validate
✗ 23 path(s) into .coder/ cited from ADR files — session process is never the record (adr-112)
# excerpts from the corpus; «…» marks each cut:
adr-077:100 …the PRIVATE bare git-dir for .coder/.claude, never mirrored… # names the layer: flagged
adr-083:142 scope = ".coder/interactions.jsonl", # the tree is the subject: flagged
adr-049:65 { kind = 'Session, ref = ".coder/2026-08-10-g5-contrato-peer-auth.plan.md", … } # citation: flagged
adr-104:60 { kind = 'Session, ref = "2026-08-27 docsite language axis", … } # citation: not flagged
Before the fix there was another outlet for the lesson. Twenty-three seconds after the operator’s question, and before answering it, the agent drafted a bug report for the harness vendor. The tool told it not to announce it, and the answer does not mention it. The report attributed to CLAUDE.md a prohibition broader than its letter. It was not sent: the operator decided the rule had to live in the project, with a check that executes it. A report improves the harness; in the project it executes nothing.
It did the right thing where it mattered: the rule left the carrier and entered the command. Since ADR-112, adr validate fails on a path into .coder/ in any ADR of the project where it runs, and it arrived red: 23 lines in ontoref, 1 in the authoring-project.
Then what it actually refuses was measured. It looks for a spelling, .coder/ followed by a name character, and the dot counts as a name character. So .coder/.claude, which only names the layer, is flagged. Of the 23 lines, 13 cite and 10 do not. Among the 10 are four lines of adr-077 and adr-080, the two ADRs the validator’s own comment gives as examples of what is not a citation.
And it does not see what is not written that way. The corpus has 26 ’Session warrants. The schema types their ref as free text, and most are a session name or a date with a label. The pattern sees 2. None of the 26 resolves to custody.
A third limit remains: nobody runs it before a commit. None of the three projects measured has it in its pre-commit. The just ci-adrs recipe exists, but it has to be called by hand. And one Hard constraint, adr-023/ondaod-applied, is green because its check opens a plan in the session tree: in a clone without the private overlay, it fails.
The project already knew. Its glossary marked «garantía» gateable = false because a regex does not tell the ordinary word from the mistranslation of warrant, and ADR-072 says a check measures the capability, not a proxy. That constraint is Soft and shows green in the same run.
The verdict
Information was not missing. The reason was in the context, and the agent recognised it as soon as it was pointed out. What was missing was something to execute it before anything was built on top. The case shows it twice.
The first time, in the session. An optional field, a schema that offers the kind and a path that looked right were enough to write a citation into the session tree. A warning at the end took the place of the question.
The second time, in the fix. The rule entered the command, which was the right thing to do, but it entered as a spelling. It flags what names the layer, does not see the citations that do not carry the string, and no hook runs it. The judgement «this cites the session tree» was replaced by a pattern, in a project whose glossary and whose ADR-072 had already written down that a regex does not separate a judgement.
That is why the case closes only in part. The rule is in the mechanism and refuses the exact form of the incident. What remains to be decided is what a session warrant resolves to and what counts as citing the tree. That decision belongs to an amendment of ADR-112, not to this case file. Until it exists, the check has to be seen refusing what it forbids and accepting what it allows, and that has not been seen yet.
| The reason was in the carrier and nothing executed it | the rule is in the command → it no longer depends on the agent remembering it |
| A replay's source cannot be the session tree either | the extract in custody → the citation resolves without opening what is private |
| The validator checks the spelling, not the citation | not contained → 10 false positives, 24 invisible citations |
| Nothing runs it before a commit | not contained → a recipe nobody chains |
| A green Hard constraint that depends on the session tree | not contained → green depending on the machine |
| The temptation to put the lesson in the agent's memory, or in a report to the harness vendor | neither memory nor report → the rule lives in the project's mechanism |
The reconstruction — the session, replayed with protocol
What was asked — reconstructed from refs:sessions/2026-09-13-la-regla-que-estaba-en-el-contexto — bounded extract held in custody: the verbatim prompt with its line number (312) from the JSONL transcript, the spine of turns with line and time, the complete `warrant` block as the Edit wrote it, the closing note and the 24→23 correction with its output. DECLARED SUBSTITUTION: in the prompt below, «ADR-NNN en <project>» has been replaced by «[el ADR de un authoring-project]», the second mention of the identifier by «[ADR]» and the domain's name by «[el dominio]»; nothing else differs. The prompt is quoted in the Spanish it was typed in. The full text stays in custody, which is not republished.
[el ADR de un authoring-project] dice "nunca consumir crates de dominio de otro proyecto", y los nombra ESTO ES TOTALMENTE INCORRECTO y hay que cambiarlo en el propio [ADR], fue un criterio al inicio cuando ni siquiera existía ontoref ni [el dominio], desde el momento en que este proyecto pasa a ontoref onboarding no tiene sentido alguno
What should have been asked
Change the ADR's decision: the ban goes away, it is not rewritten under another criterion. 1. Touch only that ADR. If the same rule is copied somewhere else, tell me where and do not edit it. 2. An optional field stays empty unless you have something to fill it with. Every `ref` in a `warrant` must resolve to something that exists and is record: an ADR, a custody entry. Never a session file. 3. If writing something means warning me that it is provisional or invented, do not write it: stop and ask me. A warning at the end does not undo what was built on top.
| Microtask | Verifiable |
| Amend the decision in the one file that was asked for | git status --porcelain lists only that ADR |
Every ref in the warrant resolves to existing record | nickel export <adr> | jq -r '.amendments[-1].warrant[] | "\(.kind) \(.ref)"' and ontoref adr show <ref> for each 'Adr; no 'Session entry |
| No path into the session tree in the ADR before building on it | ontoref adr validate without the path(s) into .coder/ line |
| Count lines, not matches, when the number goes into an ADR | rg -c (lines) against rg -o | wc -l (matches): if they differ, the number says which one it is |
The gate before delegating: Before letting an agent loose on an ADR, the check must refuse what the rule forbids, not a similar spelling, and it must run without anyone remembering to run it. Neither holds today: the pattern flags lines that cite nothing and lets 24 of 26 session warrants through, and no hook in the three projects runs it. That gate did not exist before the incident and still does not exist in full after it.
The ADR trigger: The operator fired it, not a mechanism: the question «what is the point of what gets recorded in CLAUDE.md?» led to asking for the validator, and the validator led to ADR-112. The constraint that already said a check measures the capability and not a proxy belongs to ADR-072, and it is Soft: it fired nothing.
Case law — what enforces the lesson today
- ✓
adr validateincludes paths into the session tree in its refusaladr:112/adr-validate-scans-session-citations - ✓The scan that finds them, over every ADR of the project where it runs
.ontoref/reflection/modules/adr.nu#process-tree-citations - ✓The recipe that runs full validation of the corpus
gate:just ci-adrs - ✓Custody of an extract of one of our own sessions, to cite without opening the private tree
contract:code/ontology/schemas/custody.ncl#capture_method - ✓A check measures the capability, not a proxy
adr:072/a-check-measures-the-capability-not-a-proxy
⊘Declared debt: The rule is in the mechanism under a criterion that is not its own. What would settle it is an amendment to ADR-112 that decides what a 'Session warrant resolves to and what counts as citing the session tree, checked over the typed export and with a test that sees it refuse a citation and accept a mention of the layer. Until then, 13 real citations remain unamended in ontoref, among them the Hard constraint in adr-023 that only passes where the overlay exists, and 24 'Session warrants that no check sees. And nothing runs it before a commit in any of the three projects measured.
From the project vocabulary (5)
- Bond
- A typed relation between projects (and their domains) within the ontoref framework — distinct from a `link`, which is a generic node reference (the `ln`/Link schema).
- Domain (repo_kind CLI extension)
- A repo_kind-activated CLI extension under code/domains/{id}/: a project's repo_kind turns on its domain, giving project-type-aware commands (e.g.
- Gate
- Typed prerequisites and policies controlling FSM state transitions in a project.
- ontoref
- The protocol itself: a typed, queryable surface on which a project declares WHAT IT IS (ontology) and HOW IT ACTS (reflection), so a claim about the project can be contradicted by a machine and not only by a reader.
- warrant
- The evidence on whose authority something is admitted — a term into a vocabulary, a node into the graph, a result into the verifiable set.