Too many ADRs made our AI follow outdated decisions. The fix: keep them all, let an index name the current ones A developer rebuilt the ADR index in ELN workflow, an in-house plugin suite for Claude Code, after a naive parser that read ADR Supersedes fields reported live decisions as superseded — in a 270-ADR repository, all 5 records it flagged were false alarms. The new index applies strict header rules, only marking a record superseded when the header is written in a strict machine-readable form, and raised the count of visible live decisions from 126 to 155 by recognizing bold status lines. Across 38 sessions, the agent never cat-ed every ADR at once, instead listing the folder 36 times and reading files one at a time as the index directed. The obvious fix for "my AI agent keeps relying on superseded architecture decisions" is a small parser: read each ADR's Supersedes field and mark the old records as replaced. We built exactly that. Then we ran it on two other repositories of ours, with 270 and 93 ADRs, and it reported live decisions as dead. In the larger repository, every one of the 5 records it called "superseded" was a false alarm. A parser that just picks up ADR numbers does not just miss things. It creates misreadings of its own, and an AI agent will trust them. This is a write-up of how we rebuilt the ADR index in ELN workflow, our in-house plugin suite for Claude Code, so that "which decisions are current?" is answered by one deterministic set of rules instead of by the model reading ADR bodies and guessing. It follows an earlier article, The real reason AI ignores existing decisions: designing an ADR context auto-injection hook https://dev.to/uehara/the-real-reason-ai-ignores-existing-decisions-designing-an-adr-context-auto-injection-hook-34e2 , which ended with two known gaps: the injected list stopped at 30 entries, and 29 records written in another format were not read at all. The hooks are Claude Code specific. The problem is not: any coding agent, Claude Code or Codex or another, that works in a repository with a few hundred ADRs has to find out which ones still hold. status: label made an accepted record unreadable. Another repository's ADR number pointed at an unrelated local record as the replacement. Now the tool says "superseded" only when the header is written in a strict form. ls docs/adr/ for the rest, and 29 live decisions were invisible because their status line was bold. Only 3 records declared their replacement in a machine-readable header; 76 mentioned it in prose. cat , and it never blocks , a CI check on the header lines a pull request changes, and one command that updates the old ADR, the new ADR and the index together. The first version picked up every ADR number in the Supersedes and Superseded by fields. Checked against the real files and run on the two other repositories, it got four kinds of header wrong. Supersedes: none — amends ADR-xxxx : nothing was replaced, this record amends another one. Picking up the number made the amended record look superseded. In our plugin's own repository, two live decisions would have been reported as replaced. The other repository had 7 records written as "none does not overturn ADR-xxxx ". - status: Accepted was not read, so an accepted record counted as unknown. The rules that came out of this are short, and they are mostly about what the parser refuses to infer: ADR-NNNN numbers. Any prose in the value makes it none or the Japanese "なし" creates One suggestion was to write reading rules into a README. I did not trust a note the agent may or may not read without a mechanical guard, so I measured first. At that point the repository had 167 ADRs, 2,171,690 bytes in total, the largest 68,777 bytes. - Status: Accepted but not - Status : Accepted . 30 records used the bold form and 29 of them were live. The index saw 126 live decisions; the real number was 155. sed , head or tail . The existing large-file warning watched only the Read tool, and only above 1,000 lines. The longest ADR was 648 lines. It never fired. The behavior I was worried about, the agent cat -ing every ADR at once, happened zero times in those 38 sessions. What it did was list the folder 36 times , pick files by name and read them one at a time, which is exactly what the index told it to do. ADRs exist so the reasoning behind today's design can be traced; deleting old ones loses that. So the files stay as they are: nothing deleted, moved or split. The plugin's skills, hooks and scripts refer to ADRs by number and path, and at 167 records only one ADR was not referenced from anywhere. Moving files would break those references. "Is this decision current?" is answered by an index that one parser builds from the header fields status, date, supersedes, superseded by every time. There is no committed index.json and no cache, because a stored index drifts away from the files the same way the hand-written table did. The human-readable table in docs/adr/README.md is still there, but it is generated: eln-adr rewrites a marked block, and CI fails if the block differs from a fresh run. The brief and the read warning never use that table; they use the parse of the files. | Effective status | Meaning | Counted as current | |---|---|---| | live | Accepted or Amended, not replaced | yes | | partial | Partly replaced by a newer record | yes, with the replacement shown | | superseded | Replaced by a newer record | no | | proposed | Not yet accepted | no | | rejected | An option we did not take | no | | unknown | Status not in the fixed vocabulary | no, and shown as a count | An index nobody reads changes nothing. There are four paths. When Claude Code starts, a hook a script Claude Code runs automatically at fixed moments puts this into the agent's context. The order is fixed: counts, how to use the CLI, the superseded table, the 10 newest records by revision date, the proposed count. The budget is 1,200 bytes, and the trimming order is fixed too proposed down to a count, shorter titles, newest 8, then 6, the superseded table down to a count, even shorter titles, and finally no newest list . The counts line, the CLI line and the number of superseded records always stay. The brief never sends the agent to ls docs/adr/ . After a compact or a resume, only a pointer of 300 bytes or less is injected: pushing a large block back in right after the context was shrunk would undo the shrink, and the index can be derived again at any time with the CLI. A second hook runs before the agent reads an ADR, looks up that record's effective status, and speaks up only when it matters. awk , bat , less or more has an ADR path in its arguments. show . Three or more files, or a glob, is called out as a bulk read. Every warning includes the CLI's path. Subagents that Claude Code starts internally never receive the session-start brief, so for them this warning is the only pointer. eln-adr show : the key points first show returns one record's essentials in 2.5 KB or less. Here a 25,216-byte record becomes 878 bytes. Status, replacement, amendments, heading line numbers and the opening of the decision are enough to decide whether to read more, and if so, which line range. show also puts ⚠️ on a partly superseded record and marks the replacement " partial ", telling the agent to read the replacement for that part. The record itself still counts as current; the status line says "effective: partial". A correct index is undone by the next badly written header. CI checks the ADRs a pull request adds or changes, and only the header lines that changed: the status word, a - Date: YYYY-MM-DD line, the format of the supersede fields, a supersede declared on only one side, new duplicate numbers. Each failure has a reason code. Untouched old records are never blocked, so the 167 existing files did not have to be rewritten at once. Accepting, amending and superseding are one command each. eln-adr supersede