<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: 张旭</title>
    <description>The latest articles on DEV Community by 张旭 (@illegal).</description>
    <link>https://dev.to/illegal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2297725%2Fe1dba687-48d6-4692-9d6a-5d56721d9f8b.png</url>
      <title>DEV Community: 张旭</title>
      <link>https://dev.to/illegal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/illegal"/>
    <language>en</language>
    <item>
      <title>Keep Documents from Rotting: The Document Lifecycle in the Agent Era (4)</title>
      <dc:creator>张旭</dc:creator>
      <pubDate>Fri, 16 Oct 2026 02:05:11 +0000</pubDate>
      <link>https://dev.to/illegal/keep-documents-from-rotting-the-document-lifecycle-in-the-agent-era-4-14m8</link>
      <guid>https://dev.to/illegal/keep-documents-from-rotting-the-document-lifecycle-in-the-agent-era-4-14m8</guid>
      <description>&lt;p&gt;&lt;em&gt;English edition of a series I originally wrote in Chinese (&lt;a href="https://github.com/IllegalCreed/agent-doc-architecture/blob/main/blog/04-lifecycle.md" rel="noopener noreferrer"&gt;original&lt;/a&gt;); translated with AI assistance.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the last post in the series "Agent-first Documentation Layers for Large Projects." The first three answered which document to read, where to look, and which copy to trust. This post answers: once a document is written, how does it not rot? The material comes from an enterprise payment-approval system, and from one documentation cleanup done on it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. The rules were all there. The docs rotted anyway.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/illegal/agents-arent-amnesiac-they-cant-find-the-entry-point-documentation-layers-for-large-projects-53pk"&gt;Post 1&lt;/a&gt; closed with a judgment: the agent that writes the docs is amnesiac too. The evidence comes from a documentation health check on October 14. The problems it found have one thing in common: &lt;strong&gt;the rule had been written long before, and the document that violated it was lying next to the rule.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The documentation-model spec defined 8 statuses. The plan index actually had 9 spellings. &lt;code&gt;completed&lt;/code&gt;, &lt;code&gt;done&lt;/code&gt;, and free-form Chinese were all in there.&lt;/li&gt;
&lt;li&gt;The spec says the index "enforces only six columns." It actually had 8. Another 4 rows had the wrong column count, and nobody had noticed.&lt;/li&gt;
&lt;li&gt;The two same-numbered bundles from the opening of post 3 (&lt;code&gt;F-20260909-005&lt;/code&gt;): the later claimant wrote a line in its header, "⚠️ same number, left on record." Someone had noticed, left a note, and did not fix it.&lt;/li&gt;
&lt;li&gt;The repo had a link-checking script, and it was attached to no execution point. Broken links at the entry layer were 0. Deeper broken links numbered in the five hundreds. On an ordinary day, nobody could see that number.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A fair share of these documents came from an agent's hand. Post 1 counted it: nearly four in ten of the commits that touched docs carried an agent attribution, and that is only a lower bound. At the moment of writing, the agent most likely obeyed the rule it had read. It does not remember a convention another session settled, and it does not go back to check whether the whole system is consistent with itself.&lt;/p&gt;

&lt;p&gt;Group the symptoms and there are three ways docs rot. &lt;strong&gt;The work is done and nothing is written back&lt;/strong&gt;: the implementation record is missing, and the next agent can read only the plan. &lt;strong&gt;The convention depends on good intentions&lt;/strong&gt;: vocabulary, column count, and IDs are written only in the spec, and nothing checks them at commit time. &lt;strong&gt;Old facts do not leave the stage&lt;/strong&gt;: date banners, a stale overview, and old conclusions in personal memory stay hanging. The root cause is the same. When a document is written, where it is written, who checks it, and when it leaves the stage were only hoped for. They were never designed.&lt;/p&gt;

&lt;p&gt;The "same number, left on record" line makes the point best: &lt;strong&gt;without machine enforcement, noticing is not fixing.&lt;/strong&gt; The cheapest move is to leave a sentence. Once the sentence is there, the problem becomes a "known situation," and it never gets fixed.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Three principles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Principle 1: write-back is part of the change.&lt;/strong&gt; (Answers "no memory across sessions.") The definition of done includes writing the docs back: the implementation record, the status in the index, the cases, the spec, the manual. If what should have been written was not written, the change is not done. How it was actually done, and where it departed from the plan, exists only in the session that did the work. When the session ends, that is gone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 2: a machine enforces the convention. The human only adjudicates.&lt;/strong&gt; (Answers "is also the main author" and "can grep, glob, and run scripts.") The session that writes the spec and the session that violates it may be the same agent twice. A convention that depends on good intentions will drift. Whenever a convention is parseable — vocabulary, IDs, metadata fields, table headers — hand it to a checker. The human shows up only where a machine cannot judge: is the definition right, is this exception granted, does this problem get fixed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 3: zero tolerance at the entry layer. Deeper layers have a baseline that may only fall, never rise.&lt;/strong&gt; (Answers "no memory across sessions" and "follows the literal text and does not infer intent.") Every session enters through the entry, so one error in the entry is executed literally by every session. It has to be 0. Deeper documents are a large existing stock. Demanding they be cleared to zero at once means stopping all work to sweep. The practice is to record today's number as a baseline. A new problem may not push the number above the baseline. Fix one, and the baseline drops by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Mechanism 1: the write-back loop
&lt;/h2&gt;

&lt;p&gt;Principle 1 lands as a table of "change type → what must be written back before it is done → who checks." The source is the write-back rules scattered across several of our specs. The full version, expanded by document type, is in section 10. The two rows below are the ones that table does not include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change type&lt;/th&gt;
&lt;th&gt;Must be written back before done&lt;/th&gt;
&lt;th&gt;Who checks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Any registered change&lt;/td&gt;
&lt;td&gt;The implementation record; the status in the metadata and in the index&lt;/td&gt;
&lt;td&gt;doc-lint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A new business flow&lt;/td&gt;
&lt;td&gt;A mobile-adaptation assessment&lt;/td&gt;
&lt;td&gt;One row of the routing table + code review&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The "who checks" column cannot be empty.&lt;/strong&gt; The write-back rule existed on March 4: after development, backfill the plan's "actual execution record," and update the roadmap at the same time. Execution was entirely a matter of good intentions. Post 1 counted it: a batch of completed bundles had no implementation record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lower the bar, and write-back actually happens.&lt;/strong&gt; Requiring the four-piece bundle for a small defect usually ends as one requirements file, with write-back skipped entirely. The lightweight shape defined in post 3 needs only a requirements document, but when the status enters "implemented," the document must gain a section called "implementation record."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deliverables are inside the loop, and outside the route.&lt;/strong&gt; The user manual, the changelog, and the handover note are for business users and operations. The agent maintains them and does not learn the job from them. The routing table records only "after which change this must be updated." The user manual in post 1, eight days late, is the cost of that row being absent.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Mechanism 2: from an incident to a check
&lt;/h2&gt;

&lt;p&gt;After every accident, a rule walks a pipeline: &lt;strong&gt;incident → codified rule → check.&lt;/strong&gt; The problem is that it often stops halfway.&lt;/p&gt;

&lt;p&gt;I rank the force of a rule as a ladder, weak to strong:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Narrative.&lt;/strong&gt; Write the accident as a story. Only a session that happens to read it is affected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An L0 one-sentence rule.&lt;/strong&gt; Written into the entry file, with one sentence of reason and a link. Every session reads it. Whether it is obeyed is up to the agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L1 detail.&lt;/strong&gt; Spell out how to do it, and what counts as a violation. The rule goes from "known" to "executable and decidable."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A code-review check.&lt;/strong&gt; A violation is stopped, before merge, by the reviewer against a list. The reviewer is often another agent. The "Code review" items in the entry file sit at this rung.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A shared helper or an automatic check.&lt;/strong&gt; The wrong way cannot be written, or it goes red as soon as it runs. It no longer depends on anyone remembering.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The further up, the less it depends on "being read and being remembered." Two examples. One climbed to the fifth rung. One stopped halfway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Climbed to the fifth rung: the download endpoint's magic header.&lt;/strong&gt; Tests of the download endpoint used to check only the filename suffix. A JSON 500 from the backend was still judged green. A rule was then codified: a download must verify the magic header at the start of the file (the fixed bytes that identify the format), and it was made a shared helper. A new test calls the helper and the check happens. The correct way became the cheapest way. The helper is not the only way out. A hand-written check is allowed too, as long as it compares the magic-header bytes, and that part is still backed by the review list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stopped halfway: taking a migration number.&lt;/strong&gt; After the migration collision in the opening of post 3, the rule "before taking a number, check every parallel branch and worktree" is still a manual rule today. The other half of the same kind of problem did climb to the fifth rung. The plan-bundle collision on &lt;code&gt;f005&lt;/code&gt; was changed, during the cleanup, so that taking a number uses only doc-lint's number command, and a duplicate ID is an error from the check directly.&lt;/p&gt;

&lt;p&gt;The pipeline asks one question of every rule: &lt;strong&gt;can it climb one more rung?&lt;/strong&gt; A rule that stays on a low rung is not necessarily wrong. Some judgments a machine cannot make. But it has to be clear that the rule depends on good intentions. I recommend that the L1 detail note which rung each rule is on.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Mechanism 3: doc-lint, the test suite for docs
&lt;/h2&gt;

&lt;p&gt;doc-lint is the core product of this cleanup: a script that checks documents, which is what post 1 called "the test suite for docs." It has 36 check codes, and it carries 62 unit tests of its own. What it checks falls into a few groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IDs and registration.&lt;/strong&gt; Stable IDs are unique. A bundle's directory name corresponds mechanically to its ID. Every bundle is registered in the index; otherwise, for an agent that enters through the index, it does not exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vocabulary and metadata.&lt;/strong&gt; Status and type may take only values from the vocabulary. A bundle's metadata block must be complete, and &lt;code&gt;Replaced by&lt;/code&gt; is a required key. The problem post 2 left lands here: the field may be filled with &lt;code&gt;none&lt;/code&gt;, but it may not be omitted, or the rule "if you read a superseded document, jump first" spins in place. The tool checks only that the key is present. It does not check that a superseded document really filled in the new ID, and that half still depends on review. (The skill shipped with this series has since added the check: a &lt;code&gt;superseded&lt;/code&gt; document's &lt;code&gt;Replaced by&lt;/code&gt; must be an ID already registered in the index.) The status in the metadata and the status in the index must agree. The metadata is the single source of truth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index shape.&lt;/strong&gt; The header is fixed at six columns, and every row has the same column count. Title and "next step" have length limits. Date banners are forbidden.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write-back.&lt;/strong&gt; An implemented bundle must have an implementation record. In the lightweight shape, that is an "implementation record" section (mechanism 1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cases.&lt;/strong&gt; The main table's header, and the Case ID format. Case narrative may not be mixed in ahead of the main table. The two derived views, by layer and by module, must match what the script generates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Links.&lt;/strong&gt; One broken link at the entry layer is an error. Broken links across the repo may not exceed the baseline (principle 3).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rest.&lt;/strong&gt; A project-layer spec must declare which shared spec it inherits. A current-fact document must carry a review date (mechanism 5). A new filename in a record directory must be glob-matchable. A document must contain the required sections of its template (templates are the subject of post 3).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The output is one problem per line, with the check code, the file, and the reason. An agent can read it and fix it. The tool also does two jobs of "make the correct way the cheapest way":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 scripts/doc-lint.py
python3 scripts/doc-lint.py next-id &lt;span class="nt"&gt;--type&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
python3 scripts/doc-lint.py gen-views
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order: run every check; return the next free ID, so taking a number no longer depends on the agent counting directories itself; regenerate the derived views from the case main table. The views may not be edited by hand, and a disagreement with the main table is an error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Steady-state rules and the onboarding config
&lt;/h3&gt;

&lt;p&gt;The checker code contains not one date constant, and not one historical path. The special cases of an existing project are gathered in one onboarding config:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The exemption list: existing documents that do not match a template or the naming rule, existing files inside a bundle that are outside the allowlist, and old-format Case IDs.&lt;/li&gt;
&lt;li&gt;3 frozen directories; old-format documents are read-only.&lt;/li&gt;
&lt;li&gt;A repo-wide broken-link baseline of 521.&lt;/li&gt;
&lt;li&gt;The rule effective date, and the bundle root.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A new project has no such file: no frozen area, no effective date, and a baseline of zero. That is post 3's "steady-state rules versus onboarding an existing tree," landing on the tool. Not one line of project history may enter the checker code. That boundary was held in review. See section 8.&lt;/p&gt;

&lt;h3&gt;
  
  
  The enforcement point: not a hook, and not CI
&lt;/h3&gt;

&lt;p&gt;Post 1 said that "is also the main author" implies machine enforcement. The general shape is a git hook (a script that runs automatically before a commit, such as a pre-commit hook) or a CI gate (a pipeline that runs the check automatically before merge). Our project has explicitly decided to do neither. There are only two enforcement points.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first is one row of the routing table.&lt;/strong&gt; A row was added to the entry file's "operation → required reading" table (excerpt, redacted; the original table really is two columns like this, which is exactly the shape post 2 criticizes):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Create or edit any document under docs/ | The documentation-model spec. A new document starts by copying the matching template; imitating the previous document of the same kind is forbidden. Before committing, doc-lint must report 0 ERRORs. A new change takes its number only from next-id. After editing the case main table, run gen-views. |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The second is the release-seal gate.&lt;/strong&gt; The release-seal checklist gains a step: doc-lint in release-seal mode reports 0 ERRORs. If it does not pass, do not continue.&lt;/p&gt;

&lt;p&gt;Why does this still count as machine enforcement? I have three reasons.&lt;/p&gt;

&lt;p&gt;First, the routing table is the one place the agent is certain to read. "If you changed docs, run doc-lint" is resident for every session that edits a document. The script makes the judgment. The agent is only responsible for running it.&lt;/p&gt;

&lt;p&gt;Second, the release seal is a point every version passes through. The checklist is executed by the agent and accepted by a human. A violation that was skipped in ordinary work is stopped here at the latest, and what the human sees is one line, "0 ERROR."&lt;/p&gt;

&lt;p&gt;Third, when a hook stops an agent, the cheapest way out is often to step around it, not to fix the document. Put the check inside the agent's own workflow, and the output tells it directly which line is wrong. Fixing is cheaper than stepping around.&lt;/p&gt;

&lt;p&gt;The cost should be stated too. Without a hook, whether doc-lint was run depends on the agent reading that row and following it. Between two release seals, a violation can enter the repo first, and it is stopped at the next release seal at the latest. If a project allows CI, I would hang the same command on it. The check logic does not change. There is only one more execution point.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Mechanism 4: layers of memory
&lt;/h2&gt;

&lt;p&gt;Post 2 gave the test: if the agent changes, or the machine changes, should this still be in force? If yes, it goes into the repo. The cleanup carried that out once, and two new problems showed up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before migrating, check whether it is still true.&lt;/strong&gt; Personal memory held one line: "OCR (optical character recognition of images) has no server, and is deliberately not integrated." During the migration, the code and the roadmap were checked, and the production environment had long since integrated GPU OCR. The memory was long stale, and it had kept loading with every session as a "fact." It was not migrated. It was deleted. The migration procedure gained a step, "first check whether it is still true." Following that step, of the 21 memories moved into the repo, 4 were partly stale, and only the part that was still true was moved. Personal memory has no review date and no checker. Once it is stale, it is hard to notice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A verbatim move moves the error along with the rule.&lt;/strong&gt; The old entry file said "dev = the user's notion of 'production / real data.'" In fact the three environments, dev, test, and prod, each mean something, and "production" does not mean dev. That sentence went from the entry file into personal memory, and during the cleanup it was moved verbatim into a newly written development-environment document. A word-for-word move guaranteed that no rule was lost, and it also guaranteed that no error was lost. Review found it. After the user corrected it, the repo and the personal memory were both fixed.&lt;/p&gt;

&lt;p&gt;So the full rule is: personal memory holds only collaboration preferences; project-level lessons go into L0 or L1 according to their layer; before a migration, verify and then move; and check the facts once before the move and once after.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Mechanism 5: freshness
&lt;/h2&gt;

&lt;p&gt;Current-fact documents — the project overview, the roadmap, the various specs — are believed by the agent as soon as it reads them. What they fear most is having been right once and being wrong now. Post 1 noted that the project overview stopped on September 3, and several versions shipped after that without a change to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A review date.&lt;/strong&gt; The header of a current-fact document must carry &lt;code&gt;Last reviewed: YYYY-MM-DD&lt;/code&gt;. If it is missing, that is an error. A review date earlier than the most recent release means the last time someone confirmed it was still right was before the previous version. The ordinary check only warns on this. It does not block the work, but the warning is visible every time. After the cleanup was merged there were 10 such warnings. In release-seal mode (&lt;code&gt;--release&lt;/code&gt;), an outdated project overview or roadmap is an error outright.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A cap on banners.&lt;/strong&gt; Post 1 noted that the date banners at the top of the roadmap were stacked one on another and had become a second changelog. The rule now: the top of the roadmap keeps at most 5 banners, and older ones move into the archive. The plan index is allowed none. The full record belongs in implementation records and in L3 execution reports. The two release documents are deliverables, and they do not receive banners. A banner says "what happened recently." Capping it is what makes it roll.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Where the human adjudicates
&lt;/h2&gt;

&lt;p&gt;The second half of principle 2, "the human only adjudicates," has a record in this cleanup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Approving the plan.&lt;/strong&gt; The cleanup itself was a documentation change (ID D-20261014-001). By project rule the plan was written first and implemented after approval. The code in the plan had already been rehearsed on a copy of the repo. What the human approved was a plan that had been run on real data (section 9).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drawing a boundary.&lt;/strong&gt; Task review found that the doc-lint code given in the plan had written this project's old status mapping (such as &lt;code&gt;completed&lt;/code&gt; → &lt;code&gt;verified&lt;/code&gt;) into the vocabulary, and that the inheritance relation had this project's paths hardcoded. After the user adjudicated, the first moved into a one-shot migration script, and the second moved into project config. "Project history does not enter the checker code," in section 5, is the boundary that decision held.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correcting a definition.&lt;/strong&gt; The sentence "production = dev" in section 6 was found in review and corrected by the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rejecting a false defect.&lt;/strong&gt; Review found that a comment on a save endpoint said "in flight, the current handler only," while the implementation also let the creator through, and recorded it as "suspected defect, pending a decision." The user pointed out that the detail page calls this save endpoint before "approve" is clicked, so it cannot be tightened. After the call chain and the fix record of August 20 were checked, the judgment was withdrawn, and the document was changed to "the comment is narrower than the implementation; the code is the authority." A comment is not the source of truth. The call chain is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deciding not to do it.&lt;/strong&gt; No hook, and no CI gate, is a project-level decision. doc-lint's two enforcement points were designed from that decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What they share: a machine can find an inconsistency. It cannot judge which side is right.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. The cleanup, as it happened
&lt;/h2&gt;

&lt;p&gt;There were 33 commits related to the cleanup. In the table below, "before" is the start of the cleanup branch, and "after" is the main development branch once the cleanup had been merged. The same script measured each item. Only the rows that bear on lifecycle are kept:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;doc-lint violations (measured with the tool from after the cleanup)&lt;/td&gt;
&lt;td&gt;304 ERRORs&lt;/td&gt;
&lt;td&gt;0 ERRORs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entry file AGENTS.md&lt;/td&gt;
&lt;td&gt;457 lines&lt;/td&gt;
&lt;td&gt;242 lines (the 9 Code review items are still in the entry file)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broken links across the repo (the same new checker)&lt;/td&gt;
&lt;td&gt;521 (0 at the entry layer)&lt;/td&gt;
&lt;td&gt;521 (0 at the entry layer)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The addressing rows — IDs, status, metadata, the index, and the case views — are in section 11 of post 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same ruler.&lt;/strong&gt; 304 is what the post-cleanup doc-lint measured on the pre-cleanup tree. The leading items are in post 3. Different tools, and the numbers are not comparable. The same ruler, and anyone can re-run it against the pre-cleanup commit.&lt;/p&gt;

&lt;p&gt;Broken links are the example in the other direction. Measuring the post-cleanup tree with the old pre-cleanup checker produced 638, more than before the cleanup. That was a false positive: the old checker treated example links inside code blocks as broken links, and the post-cleanup docs contain more examples. The new checker measures 521 both before and after. The count of deep broken links did not change. All of them went into the baseline, which may only fall, never rise.&lt;/p&gt;

&lt;p&gt;The cleanup itself also exposed problems. Two of them bear directly on this post's argument.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rehearse the plan on real data first.&lt;/strong&gt; The code was pulled out of the plan and run on a copy of the repo. Before approval, 5 defects turned up. Truncating the "next step" text cut into a Markdown link and produced a new broken link. The index preamble does not start with &lt;code&gt;&amp;gt; 20&lt;/code&gt;, so the banner check missed it. When a missing row was registered, the title repeated the ID prefix. One test forgot to pass the onboarding config. And a batch of old-format Case IDs did not match the new rule. Before the rehearsal, that version of the tool reported 308 ERRORs. After the rehearsal, 0. A migration script is code, the same as lint, and it has to be run on real data first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A one-shot migration script cannot be re-run.&lt;/strong&gt; The plan said that when merging the main development branch, "take its index and re-run the migration script." Final review simulated this in a temporary worktree and found that the script aborted on the rows with the wrong column count. Even if that were bypassed, it would drop the cleanup change's own index rows and write the status back. In the end it was absorbed by hand. A migration script is written for one starting point. That has to be written into the document, or the next agent will re-run it because the text says to.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Each document: when it is written back, who checks, and what the human does
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Document&lt;/th&gt;
&lt;th&gt;Write-back trigger&lt;/th&gt;
&lt;th&gt;Where it is written&lt;/th&gt;
&lt;th&gt;Who enforces&lt;/th&gt;
&lt;th&gt;Human's role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Requirements / design&lt;/td&gt;
&lt;td&gt;Before starting; when scope or the design changes&lt;/td&gt;
&lt;td&gt;The change's bundle&lt;/td&gt;
&lt;td&gt;doc-lint (registration, metadata)&lt;/td&gt;
&lt;td&gt;Approve the plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation record&lt;/td&gt;
&lt;td&gt;When status enters "implemented"&lt;/td&gt;
&lt;td&gt;The bundle's implementation record; in the lightweight shape, an "implementation record" section inside requirements&lt;/td&gt;
&lt;td&gt;doc-lint&lt;/td&gt;
&lt;td&gt;Read the deviation summary and confirm the departure is acceptable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Case index&lt;/td&gt;
&lt;td&gt;A case is added or changed&lt;/td&gt;
&lt;td&gt;The case main table; views are generated by script&lt;/td&gt;
&lt;td&gt;doc-lint&lt;/td&gt;
&lt;td&gt;At acceptance, confirm a coverage gap is acceptable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development / testing spec&lt;/td&gt;
&lt;td&gt;An incident is codified; a reusable pitfall is hit&lt;/td&gt;
&lt;td&gt;The L1 spec, plus one L0 row when the risk is high&lt;/td&gt;
&lt;td&gt;Code review&lt;/td&gt;
&lt;td&gt;Make the call on the new rule and the definition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB migration&lt;/td&gt;
&lt;td&gt;A schema change or cross-environment data&lt;/td&gt;
&lt;td&gt;The migration script and the migration index&lt;/td&gt;
&lt;td&gt;A review check; taking a number is still a manual rule&lt;/td&gt;
&lt;td&gt;Before deploy, confirm the execution list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment runbook&lt;/td&gt;
&lt;td&gt;The environment or the process changes&lt;/td&gt;
&lt;td&gt;The matching booklet&lt;/td&gt;
&lt;td&gt;Code review&lt;/td&gt;
&lt;td&gt;Approve production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incident report&lt;/td&gt;
&lt;td&gt;After an incident&lt;/td&gt;
&lt;td&gt;L3; the lesson takes the rule-codifying pipeline separately&lt;/td&gt;
&lt;td&gt;A human&lt;/td&gt;
&lt;td&gt;Confirm the root cause, and decide which rung the rule is codified onto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project overview / roadmap&lt;/td&gt;
&lt;td&gt;A release, or a change of scope&lt;/td&gt;
&lt;td&gt;The current-fact document, with banners capped&lt;/td&gt;
&lt;td&gt;doc-lint (review date, banners)&lt;/td&gt;
&lt;td&gt;Make the call when definitions conflict&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User manual&lt;/td&gt;
&lt;td&gt;A user-visible feature change&lt;/td&gt;
&lt;td&gt;The matching section of the manual&lt;/td&gt;
&lt;td&gt;Code review&lt;/td&gt;
&lt;td&gt;Spot-check, then confirm the deliverable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changelog / handover note&lt;/td&gt;
&lt;td&gt;A release&lt;/td&gt;
&lt;td&gt;The deliverables directory, against the template&lt;/td&gt;
&lt;td&gt;The release-seal checklist + doc-lint&lt;/td&gt;
&lt;td&gt;Accept the release seal, and confirm the deliverable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal memory&lt;/td&gt;
&lt;td&gt;A collaboration preference changes&lt;/td&gt;
&lt;td&gt;Memory on this machine&lt;/td&gt;
&lt;td&gt;None; preferences only&lt;/td&gt;
&lt;td&gt;When a definition is corrected, confirm both places change together&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The more formatted something is, the earlier it is handed to a machine. Whether the content is right still depends on review and on a human. No cell of "human's role" says "maintain."&lt;/p&gt;

&lt;h2&gt;
  
  
  11. How this mechanism breaks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Failure 1: the checker itself is wrong
&lt;/h3&gt;

&lt;p&gt;A checker is code, and it can be wrong. The link checker originally did not skip code blocks. Example links in the cleanup plan itself were false positives, 58 of them, and existing documents produced 12 more. The 638 measured by the old checker, earlier in this post, is the same defect. One case where the template check judged its scope by phrases in the body, and skipped a document, is in post 3. Problems of this kind show up only when the check is run on the tool's own documents (dogfooding: checking yourself with your own product).&lt;/p&gt;

&lt;p&gt;Rules go stale too. Vocabulary, column count, and length limits are all set by today's practice. When the practice changes and the rule does not, the agent will twist the document to satisfy an outdated rule. The counter: list the check rules as a table in the documentation-model spec, one-to-one with the check codes. When a rule forces a document out of shape, hand the rule change to a human. The spec, the code, and the tests change together. The agent does not step around it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 2: enforcement that is too heavy forces the agent to step around it
&lt;/h3&gt;

&lt;p&gt;A rule that cannot be satisfied will be satisfied by the agent in the cheapest way: boilerplate in a required section, a formally valid value in a field, or adding the file to the exemption list. So enforcement has to come with an exception path. Existing problems go into the exemption list of the onboarding config. Sections of a template that are not required are marked "optional." Banners are capped rather than cleared to zero. The old-format Case IDs during the cleanup took this path: into the exemption list, and not renamed. The exception path has one requirement: &lt;strong&gt;explicit, gathered in one place, and visible.&lt;/strong&gt; Every exemption is one line in the same file, and the change shows up in the diff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 3: "only fall, never rise" is only written down
&lt;/h3&gt;

&lt;p&gt;The spec says the baseline file may only be lowered. The tool delivers half of that. Repo-wide broken links have a numeric baseline, and exceeding it is an error. The exemption list does not. Template exemptions started at 315 during the cleanup, were trimmed to 314, and gained another 4 when the main development branch was merged, becoming 318. Those 4 were existing documents that had already been filed before the rule took effect and came in with the merge, and the commit message stated the reason. This increase had a reason. Even with no reason, no check would have reported an error. Entries in the list that are no longer in force are not reported either. The second half of principle 3, today, depends on someone reading the diff. The repair is not hard — for example, compare the exemption count with the previous tag (the version mark placed on a commit at release time) and allow only fewer, never more. Until that is added, it is the softest link in the mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 4: the list of forbidden words leaks itself
&lt;/h3&gt;

&lt;p&gt;This happened in the repo where this series was written. While the plan was being written, the whole redaction word list and the real configuration names were committed as test fixtures. Final review found them. The handling: the word list exists only in a directory that is not committed; test fixtures are concatenated at runtime; the checker gained a repo-wide scan, and commit messages are scanned too; the repo history was rebuilt, and the old history was kept only as a local backup. The lesson: &lt;strong&gt;the list of words that must not appear is itself sensitive.&lt;/strong&gt; A checker's configuration is a document too, and it has a lifecycle of its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Docs rot in three places: the work is done and nothing is written back, the convention depends on good intentions, and old facts do not leave the stage. A written rule does not stop it. The agent that writes the docs is amnesiac too.&lt;/li&gt;
&lt;li&gt;Three principles. Write-back is part of the change (no memory across sessions). A machine enforces the convention, and the human only adjudicates (is also the main author, and can run scripts). Zero tolerance at the entry layer, and deeper layers have a baseline that may only fall, never rise (every session enters through the entry and follows the literal text).&lt;/li&gt;
&lt;li&gt;Every rule sits on the enforcement ladder, from narrative up to an automatic check. The question to ask is "can it climb one more rung?"&lt;/li&gt;
&lt;li&gt;doc-lint is the test suite for docs. It does not depend on a hook or on CI. The enforcement points are one row of the routing table plus the release-seal gate. The cost is that a violation can enter the repo first, with the release seal as the backstop.&lt;/li&gt;
&lt;li&gt;Personal memory holds only collaboration preferences, and a migration verifies first. Current fact carries a review date, and banners are capped so they roll.&lt;/li&gt;
&lt;li&gt;The mechanism breaks on its own: the tool false-positives, rules go stale, exemptions can quietly grow, and even the redaction list can leak. The checker has to be checked too.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  End of the series
&lt;/h3&gt;

&lt;p&gt;Read together, the four posts are one sentence: in a project where an agent writes most of the code, documentation is a routing system for the agent. Post 1 gives the four-layer model. Post 2 builds the roads. Post 3 fixes the locations. This post keeps all of that from rotting over time. The human in the middle is no longer the reader and the maintainer. The human is the adjudicator.&lt;/p&gt;

&lt;p&gt;The mechanisms in the four posts will be extracted into a skill, in two modes — initializing a new project, and onboarding an existing tree — and published separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Series
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/agents-arent-amnesiac-they-cant-find-the-entry-point-documentation-layers-for-large-projects-53pk"&gt;Agents Aren't Amnesiac — They Can't Find the Entry Point: Documentation Layers for Large Projects (1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/make-the-agent-read-only-what-it-should-documentation-routing-2-450d"&gt;Make the Agent Read Only What It Should: Documentation Routing (2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/give-every-document-exactly-one-place-documentation-addressing-3-1oih"&gt;Give Every Document Exactly One Place: Documentation Addressing (3)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Keep Documents from Rotting: The Document Lifecycle in the Agent Era (4) (this post)&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;The full series, examples, and an installable agent skill are on GitHub: &lt;a href="https://github.com/IllegalCreed/agent-doc-architecture" rel="noopener noreferrer"&gt;IllegalCreed/agent-doc-architecture&lt;/a&gt; — English skill: &lt;a href="https://github.com/IllegalCreed/agent-doc-architecture/tree/main/skill/agent-first-docs-en" rel="noopener noreferrer"&gt;skill/agent-first-docs-en&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>documentation</category>
      <category>productivity</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Give Every Document Exactly One Place: Documentation Addressing (3)</title>
      <dc:creator>张旭</dc:creator>
      <pubDate>Fri, 16 Oct 2026 02:05:03 +0000</pubDate>
      <link>https://dev.to/illegal/give-every-document-exactly-one-place-documentation-addressing-3-1oih</link>
      <guid>https://dev.to/illegal/give-every-document-exactly-one-place-documentation-addressing-3-1oih</guid>
      <description>&lt;p&gt;&lt;em&gt;English edition of a series I originally wrote in Chinese (&lt;a href="https://github.com/IllegalCreed/agent-doc-architecture/blob/main/blog/03-addressing.md" rel="noopener noreferrer"&gt;original&lt;/a&gt;); translated with AI assistance.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the third post in the series "Agent-first Documentation Layers for Large Projects." &lt;a href="https://dev.to/illegal/agents-arent-amnesiac-they-cant-find-the-entry-point-documentation-layers-for-large-projects-53pk"&gt;Post 1&lt;/a&gt; treated the doc system as a routing system for agents and left three questions. &lt;a href="https://dev.to/illegal/make-the-agent-read-only-what-it-should-documentation-routing-2-450d"&gt;Post 2&lt;/a&gt; answered "which documents should be read," and left placement rules and templates for this post. This post answers the second question: where do I look, and which copy do I trust? The material is still the enterprise payment-approval system. The project has already run one round of documentation cleanup along the lines of this series. The "before / after" numbers in the text were measured by the same script on the two trees.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. After moving 235 files
&lt;/h2&gt;

&lt;p&gt;On September 3 the project reshuffled its documentation architecture and touched 235 files. Before that, test material was scattered in four places, several ways of splitting plans sat side by side under the plan root, and an agent looking for a document was basically guessing the filename. After the reshuffle, test material was gathered into one place. Plans were collected into three kinds: structured change bundles (one change's requirements, design, implementation record, and test cases in a single directory), long-running tracks, and early single-file plans. The top of the docs home said to find a document by its responsibility first, and to stop guessing a location from the filename (see post 2).&lt;/p&gt;

&lt;p&gt;The locations existed. The problem was not over.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IDs collided.&lt;/strong&gt; Two changes were both numbered &lt;code&gt;F-20260909-005&lt;/code&gt;: two directories, one number. At the time, taking a plan number had no rule and no check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrations collided too.&lt;/strong&gt; On September 29, two database migration scripts with the same number had both already run in production. The only option was to move them and rework, and only afterward was the rule written: "before taking a number, check every parallel branch and worktree" (a worktree is several working directories checked out from the same repo at once).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package names could not be told apart.&lt;/strong&gt; On September 30 an old package with the same name was deployed by mistake. After that, package names were required to carry the hour, minute, and second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More than one account of the same thing.&lt;/strong&gt; The README of the change directory declared "we do not maintain a second status table," and then maintained a status list of its own, stopped on September 22.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A directory only answers "which area." For an agent to find a document and trust it, it also has to know which one (the ID), whether it still counts (status and replacement), and which section the information is in (the template). I call the four layers together &lt;strong&gt;addressing&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Symptoms and root cause
&lt;/h2&gt;

&lt;p&gt;Addressing failed in this project in five shapes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Guessing.&lt;/strong&gt; Migrations, bundles, and test reports were named "date + English slug." Early single-file plans and incident reports were long Chinese titles. There was no one pattern that could glob the documents of a kind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The same number.&lt;/strong&gt; Two directories, one ID. An agent starting from the ID does not know which directory to enter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One fact in several places.&lt;/strong&gt; Status was written in the bundle header, the index, and the directory README. Beside the case index there were two views kept in sync by hand. Where they disagreed, the agent could only pick one and believe it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The directory name was read as a status.&lt;/strong&gt; Early plans lived under &lt;code&gt;legacy/&lt;/code&gt;. The literal word is "left over," and it is easily read as "finished and archived."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The section cannot be found inside the document.&lt;/strong&gt; Each version of the same kind of report had different sections. Finding "rollback steps" meant reading from the top to the bottom.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is one root cause: &lt;strong&gt;location, identity, status, and structure — these four pieces of information were either only a convention, or written in more than one place, or expressed by borrowing something else.&lt;/strong&gt; A directory name was used to express status. The previous document was used to express structure. A human infers. An agent that follows the literal text does not: the directory is whatever it is called, and this document looks like the previous one.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Three principles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Principle 1: split directories by responsibility, not by type and not by status. When the work is done, do not move it.&lt;/strong&gt; (Answers "can grep, glob, and run scripts.") A directory answers only "what this document is for." Type and status are both fields. The moment directories are split by status, a finished change has to move house, and every reference to it — other documents, commit messages, comments inside migration scripts — breaks together. Paths stay stable, and the agent can find a document by pattern instead of by memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 2: one fact has one source. Everything else is a derived view.&lt;/strong&gt; (Answers "is also the main author.") Two hand-maintained copies will drift. This session edits the bundle header. The next session may not remember to edit the index. So name one source. Every other place either holds only a link, or is generated by a script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 3: status is written in the document. It is not expressed by the directory.&lt;/strong&gt; (Answers "follows the literal text and does not infer intent.") Status is a field with a fixed vocabulary. Replacement is a field pointing at the new ID. A directory name means only a responsibility.&lt;/p&gt;

&lt;p&gt;One more sentence on naming. The reason for a uniform name is that &lt;strong&gt;a machine can match it&lt;/strong&gt;, not that it looks tidy (the same answer to "can grep / glob"). &lt;code&gt;YYYYMMDD-&amp;lt;slug&amp;gt;&lt;/code&gt; can be described by one regular expression. A long Chinese title cannot.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Mechanism 1: responsibility directories + placement rules
&lt;/h2&gt;

&lt;p&gt;The steady-state directory structure is below (excerpt). "Steady state" means the shape a new project should have on day one. How that differs from onboarding an existing project is mechanism 6.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docs/
├── README.md                    Docs home: split by responsibility + placement rules
├── overview.md                  What the system is, and where its boundary is (current fact)
├── roadmap.md                   The global roadmap
├── handbook/                    Specs that stay in force
│   └── templates/               One template per document kind
├── testing/
│   ├── guides/                  Testing specs
│   └── cases/                   The global case index and its derived views
├── deployment/                  The deployment runbook that can be executed now
├── plans/
│   ├── index.md                 The change index
│   ├── YYYYMMDD-&amp;lt;t&amp;gt;NNN-&amp;lt;slug&amp;gt;/  One bundle per change, flat; stays put when finished
│   └── tracks/&amp;lt;topic&amp;gt;/          Navigation for a long-running track
├── reports/                     Execution evidence: tests, audits, incidents, migrations
└── releases/                    Deliverables: changelogs, handover notes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two choices. &lt;code&gt;reports/&lt;/code&gt; is L3 evidence, for the agent to drill into. &lt;code&gt;releases/&lt;/code&gt; is deliverables, for business users and operations. They are separate for that reason. Under &lt;code&gt;plans/&lt;/code&gt; there is no &lt;code&gt;done/&lt;/code&gt; or &lt;code&gt;bugfix/&lt;/code&gt; subdirectory. Type and status are both fields.&lt;/p&gt;

&lt;p&gt;Each top-level directory's README has two sections: "Navigation" for the agent that reads, "Placement rules" for the agent that writes. Placement rules look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Placement rules&lt;/span&gt;

| Directory | What goes here | Rule for adding |
|---|---|---|
| Bundles under plans/ | One deliverable change | Take a number, then create the directory; type and status go in metadata, not in a physical split |
| tracks/ | A long-running topic that spans many changes | Navigation and links to related bundles only; no plans, no status |
| Not in this directory | Execution evidence, deliverables | Those go to reports/ and releases/ respectively |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Placement rules are the route for writing. Before the agent creates a document, it reads the README of the target directory once, and it knows where the document lands and which template to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Mechanism 2: a stable ID scheme
&lt;/h2&gt;

&lt;p&gt;A Stable ID is a document's fixed number. It is taken when the document is created, and it does not change later with the title, the directory, or the type. The ID scheme is the part of this mechanism most worth copying directly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Object&lt;/th&gt;
&lt;th&gt;ID / filename&lt;/th&gt;
&lt;th&gt;Mechanical relation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Change&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;T&amp;gt;-YYYYMMDD-NNN&lt;/code&gt; ↔ directory &lt;code&gt;YYYYMMDD-&amp;lt;t&amp;gt;NNN-&amp;lt;slug&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Each can be derived from the other; the first letter is fixed by the type at creation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database migration&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NNNN-YYYYMMDD-&amp;lt;slug&amp;gt;.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The global sequence decides execution order&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test case&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TC-&amp;lt;module&amp;gt;-NNN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The module can be read off the ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reports and other records&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YYYYMMDD-&amp;lt;slug&amp;gt;.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sorted by date; a release-seal test report's slug carries the version (&lt;code&gt;test-report-vX.Y.Z&lt;/code&gt;) and can be grepped by version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release documents&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vX.Y.Z.md&lt;/code&gt; / &lt;code&gt;vX.Y.Z-delivery.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Changelog and handover note come in pairs, by version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release package&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vX.Y.Z-YYYYMMDD-HHMMSS-dist.zip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Several packages on the same day can be told apart&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An agent holding &lt;code&gt;D-20261014-001&lt;/code&gt; can glob &lt;code&gt;20261014-d001-*&lt;/code&gt; without opening the index. Holding a directory name, it can derive the ID the other way. This one was already done well before the cleanup: of the 51 bundles, 35 carried a Stable ID, and directory name matched ID 35/35, with zero misalignment.&lt;/p&gt;

&lt;p&gt;The first-letter map has a small trap. The shared layer recommends &lt;code&gt;V&lt;/code&gt; for &lt;code&gt;release&lt;/code&gt; (from version). Our project uses &lt;code&gt;C&lt;/code&gt;, from the early &lt;code&gt;chore/release&lt;/code&gt; type. An ID, once issued, cannot be changed, so the first-letter map is defined by the project layer and then left alone. The first letter of a new ID is decided by the type at creation, and a later change of type does not change the ID. By the same reasoning, the cleanup found 9 case IDs in the old format. The treatment was to exempt them in the onboarding config, not to rename them. The entire value of a Stable ID is that it does not change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rule for taking a number&lt;/strong&gt; is the easiest piece to underestimate. Several agents work in parallel on different branches and different worktrees. Each looks at its own directory and takes "the next number." A collision is almost inevitable. Both collisions in the opening had this shape. The rule is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before taking a number, scan every local branch, every remote branch, and every worktree, and take the first number that is free globally. For bundle IDs, the cleanup turned this step into a number-taking command (see post 4).&lt;/li&gt;
&lt;li&gt;On a collision, &lt;strong&gt;the later claimant moves&lt;/strong&gt;: the directory, the ID, and every reference change together, the content does not, and the move-out record states the original number.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A migration collision costs more, because the sequence is the execution order. Adding the hour, minute, and second to a release package is the same idea. An ID has to make "this one" and "that one" impossible to confuse in the eyes of a machine. Two packages on the same day, and a date is not enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Mechanism 3: the bundle, and the lightweight shape
&lt;/h2&gt;

&lt;p&gt;The bundle's four pieces each answer one question:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Answers&lt;/th&gt;
&lt;th&gt;When it is written&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;requirements&lt;/td&gt;
&lt;td&gt;What is wanted&lt;/td&gt;
&lt;td&gt;At creation; the header carries the metadata block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;design&lt;/td&gt;
&lt;td&gt;How it is intended to be done&lt;/td&gt;
&lt;td&gt;Before approval&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;implementation&lt;/td&gt;
&lt;td&gt;How it was actually done, and what departed&lt;/td&gt;
&lt;td&gt;Backfilled during implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;test-cases&lt;/td&gt;
&lt;td&gt;How it is proved&lt;/td&gt;
&lt;td&gt;In step with the implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Define only the four pieces, and reality grows other files on its own. Before the cleanup, bundles contained 7 hand-written case files, 5 impact analyses, 4 handoff documents, more than ten stage booklets, and even &lt;code&gt;.mjs&lt;/code&gt; scripts. The spec defined none of them. After the cleanup this became an &lt;strong&gt;optional-file allowlist&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Optional file | When it is needed | When it is read |
|---|---|---|
| impact-map.md | The impact crosses layers or modules and is large | After design |
| handoff.md | Handed to another agent to implement or review | At implementation or review |
| manual-test-cases.md | Some cases must be accepted by a human | At acceptance |
| review-&lt;span class="nt"&gt;&amp;lt;agent&amp;gt;&lt;/span&gt;-YYYYMMDD.md | An independent re-review record | At review |
| stages.md + stage booklets | A large change split into stages | When entering the matching stage |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Existing files outside the allowlist stay where they are and are registered as exemptions (14 of them). A new file outside the allowlist is always an error. Scripts go in &lt;code&gt;scripts/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The other end is the small change. Before the cleanup, 20 bundles contained only a requirements file, and 18 completed bundles had no implementation. The spec had never defined what a small change looks like, and the write-back loop broke there. After the cleanup a &lt;strong&gt;lightweight shape&lt;/strong&gt; was defined: a small bugfix, a docs change, or a release-type change needs only requirements, and when the status advances to "implemented," the document must contain a section called "implementation record." The shape is distinguished by a condition that can be judged: whether there is a design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short-term and long-term have to be split too.&lt;/strong&gt; A bundle is short-term: one deliverable change. A large change with several stages is still one bundle plus a stage split. A track is long-term: topic navigation across several independent changes. It writes only why, the order, the boundary, and links to the related bundles. &lt;strong&gt;It does not hold plans, and it does not write status.&lt;/strong&gt; Post 1 noted that our refactoring track had swallowed over a hundred short-term plans and a roadmap with status marks. On top of that, 3 shelved items were not registered in the index, and the IDs in the roadmap did not match the filenames. Writing status inside a track creates a second source of status, which violates principle 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Mechanism 4: a thin index + derived views
&lt;/h2&gt;

&lt;p&gt;The index answers only "where to read." It does not answer "what it is." After the cleanup the header is fixed at six columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Stable ID | Type | Title | Status | Next step | Plan |
|---|---|---|---|---|---|
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few hard rules go with it. The title is at most 50 characters. The next step is at most 80. A terminal state's next step is &lt;code&gt;-&lt;/code&gt;. The Plan column holds only the link to the bundle entry. Date banners (progress announcements that start with a date) are not allowed in the index. Version news goes on the roadmap. Progress goes in the bundle.&lt;/p&gt;

&lt;p&gt;More important is source versus projection: &lt;strong&gt;the metadata block in the bundle header is the single source of truth for status, and the index is its projection.&lt;/strong&gt; A disagreement is an error from the checker. That is where principle 2 lands. The README list from the opening, stopped on September 22, has no place to stand under this rule.&lt;/p&gt;

&lt;p&gt;Unregistered is the same. Before the cleanup, one bundle was not in the index. For an agent that enters through the index, it did not exist. Now an unregistered bundle is an error outright.&lt;/p&gt;

&lt;p&gt;The case index works the same way. The main table is the only source. The two views, by layer and by module, are generated by a script from the main table and may not be edited by hand. Before the cleanup, narrative written batch by batch was piled on top of the main table, and many IDs in the narrative were not in the main table. Each of the two views also had a batch of IDs the main table did not have. An agent coming in through different doors saw a different "all the cases." After the cleanup, IDs in the narrative moved back to the change they belonged to, and the views became generated (the numbers are in section 11). When a human wants progress, the agent summarizes it on the spot, or runs a script to generate a view.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Mechanism 5: the state machine, the replacement chain, and archive
&lt;/h2&gt;

&lt;p&gt;Every value in the status vocabulary has to correspond to a way the agent uses it. Writing the "meaning" is not enough. The agent needs to know what to do after it reads the status:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;How the agent uses it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;draft&lt;/td&gt;
&lt;td&gt;Under discussion&lt;/td&gt;
&lt;td&gt;Background only; not a basis for implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;approved&lt;/td&gt;
&lt;td&gt;Approved, not implemented&lt;/td&gt;
&lt;td&gt;May be a basis for implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in-progress&lt;/td&gt;
&lt;td&gt;Being implemented&lt;/td&gt;
&lt;td&gt;Backfill while implementing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;implemented&lt;/td&gt;
&lt;td&gt;Code has landed; verification is incomplete&lt;/td&gt;
&lt;td&gt;Read the implementation record and the test record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;verified&lt;/td&gt;
&lt;td&gt;Implementation and verification are both confirmed&lt;/td&gt;
&lt;td&gt;May be treated as current fact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;superseded&lt;/td&gt;
&lt;td&gt;Taken over by a new change&lt;/td&gt;
&lt;td&gt;Must jump to &lt;code&gt;Replaced by&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deprecated&lt;/td&gt;
&lt;td&gt;No longer represents current behavior&lt;/td&gt;
&lt;td&gt;For tracing only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;obsolete&lt;/td&gt;
&lt;td&gt;No longer in force&lt;/td&gt;
&lt;td&gt;Not a basis for implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cancelled&lt;/td&gt;
&lt;td&gt;Decided not to do&lt;/td&gt;
&lt;td&gt;Not a basis for implementation; read the reason it was cancelled&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Before the cleanup, the index contained &lt;code&gt;completed&lt;/code&gt;, &lt;code&gt;done&lt;/code&gt;, &lt;code&gt;in_progress&lt;/code&gt;, and free-form Chinese. In the type column, bug and fix were mixed, and there were combinations such as &lt;code&gt;feature/fix&lt;/code&gt;. If the vocabulary allows near-synonyms, the agent is invited to infer whether &lt;code&gt;done&lt;/code&gt; is &lt;code&gt;verified&lt;/code&gt;. During the cleanup the old spellings were mapped onto the vocabulary by a one-shot migration script. The mapping table is recorded only in the project layer. It does not enter the shared layer or the checker.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;replacement chain&lt;/strong&gt; is a pair of fields. The old document writes &lt;code&gt;Replaced by: &amp;lt;new ID&amp;gt;&lt;/code&gt;. The new document writes &lt;code&gt;Replaces: &amp;lt;old ID&amp;gt;&lt;/code&gt;. An agent that reads superseded must jump. It may not keep looking for the answer in the old document. Post 1 gave the reason this rule spun in place: the field was not there. After the cleanup every bundle writes &lt;code&gt;Replaced by&lt;/code&gt;, and if there is none it writes &lt;code&gt;none&lt;/code&gt;. An explicit "none" is information too.&lt;/p&gt;

&lt;p&gt;Last is &lt;strong&gt;archive&lt;/strong&gt;, the word most easily misused. Archive means only &lt;strong&gt;history that has been superseded&lt;/strong&gt;, for example an old manual replaced by a new deployment runbook: move it out of the current directory, and leave a link to the new document in the old place. &lt;strong&gt;A finished change is not archived.&lt;/strong&gt; It stays where it is, and the status is written in the metadata. The reason goes back to principle 1. A finished change is exactly what is referenced the most. Moving it breaks every reference.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Mechanism 6: steady-state rules versus onboarding an existing tree
&lt;/h2&gt;

&lt;p&gt;The easiest mistake, when abstracting a shared spec out of one project, is to treat that project's history as shared structure. In the shared part of the source project's documentation-model spec, the "recommended target structure" drew &lt;code&gt;legacy/YYYYMM/&lt;/code&gt;. Review pointed out, one after another, that both &lt;code&gt;legacy/&lt;/code&gt; and &lt;code&gt;changes/&lt;/code&gt; are products of onboarding. Exceptions that draw a line by date are more hidden. They were scattered through the rule text and through the checker code. Another example inside the checker code is in post 4.&lt;/p&gt;

&lt;p&gt;So a spec has to be written for two situations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steady state.&lt;/strong&gt; A new project files documents by the rules from day one. There is no frozen area, no date exception, and the exemption baseline is zero. Under &lt;code&gt;plans/&lt;/code&gt; there is only the index, bundles laid out flat, and &lt;code&gt;tracks/&lt;/code&gt;. Bundles are distinguished from &lt;code&gt;tracks/&lt;/code&gt; by the name pattern &lt;code&gt;YYYYMMDD-&amp;lt;t&amp;gt;NNN-&lt;/code&gt;. They do not need another wrapping directory called &lt;code&gt;changes/&lt;/code&gt;. In our project that extra layer existed only to separate bundles from the frozen area. And "change," read literally, means "a modification." The first feature of a new project does not modify anything, and an agent that follows the literal text will be misled by the name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Onboarding an existing tree.&lt;/strong&gt; An old project that adopts the rules midway needs four onboarding pieces, and &lt;strong&gt;all of them live in one onboarding config.&lt;/strong&gt; The rule text and the checker code contain no date constant and no historical path:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Onboarding piece&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;When it exits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frozen area&lt;/td&gt;
&lt;td&gt;Old-format documents from before onboarding; read-only, nothing new&lt;/td&gt;
&lt;td&gt;When touched, migrate to a standard bundle and mark the replacement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rule effective date&lt;/td&gt;
&lt;td&gt;Objects filed before this date are exempt from the rule (for example, the ID's first letter)&lt;/td&gt;
&lt;td&gt;Permanent; applies only to what already existed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exemption baseline&lt;/td&gt;
&lt;td&gt;The list and the count of existing violations&lt;/td&gt;
&lt;td&gt;May only fall, never rise; delete it when it reaches zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Historical directory layer&lt;/td&gt;
&lt;td&gt;A directory layer that is heavily referenced, where renaming costs too much&lt;/td&gt;
&lt;td&gt;Migrate with a major version, or keep it permanently&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Our project's &lt;code&gt;changes/&lt;/code&gt; is the fourth piece. It is referenced from 73 files in 564 places, and the comments in 13 migration scripts that have already been executed cannot be changed, so it stays. The onboarding config declares where the bundle root is. The skeleton of that config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bundle_root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docs/plans/changes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rules_effective_since"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"20261014"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"frozen_dirs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;frozen directory&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"template_exceptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;existing path that did not match a template before onboarding&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"naming_exceptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;existing path that did not match the naming rule before onboarding&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new project has no such file, and the checker runs a full check with zero exemptions.&lt;/p&gt;

&lt;p&gt;The name of the frozen area needs extra care. A frozen area means &lt;strong&gt;old format&lt;/strong&gt;. It does not mean &lt;strong&gt;finished&lt;/strong&gt;. Work that still has to move forward must be registered in the index. The word &lt;code&gt;legacy&lt;/code&gt; naturally pulls a person toward "finished and archived." Under our &lt;code&gt;legacy/202504/&lt;/code&gt; there was only one file, and the filename ended in "do this later" — a piece of work that was explicitly unfinished, lying in a directory that looked archived. A human sees the mismatch at a glance. An agent that follows the literal text treats it as done.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Mechanism 7: the template is addressing inside the document
&lt;/h2&gt;

&lt;p&gt;Directories and IDs answer "where the document is." The template answers "which section the information is in." Section names are fixed, so the agent can jump straight to "rollback and acceptance boundary" and does not have to read the whole document.&lt;/p&gt;

&lt;p&gt;What happens with no template is on record, in full, as a counterexample. Post 2 noted that the release-seal spec, at the time the rule was written, said of the handover note "the template is to follow v2.0.2-delivery.md." Naming a historical document as the sample writes imitation into the rules. From v2.0.3 on, the handover notes were missing SHA-256. The drift went much further than that one spot. The rollback section was missing for 8 versions in a row, from v2.0.1 through v2.2.0, and came back only in v2.2.1. From v2.0.3 on, the changelog lost the two sections "tests in this version" and "build traceability." The section counts of the release-seal test reports ran 6 → 3 → 5 → 5 → 5 → 4 → 8 → 6 → 6 → 6. There were 5 incident reports, 13 audit reports, and 10 migration handovers, and almost every one invented its own structure. The only section that stayed stable was "delivery result" in the handover note: 14 of 14 had it. I call this &lt;strong&gt;imitation drift&lt;/strong&gt;. Each generation is only "write it like the previous one," and the deviation is passed on generation after generation.&lt;/p&gt;

&lt;p&gt;The rules of a template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One template per document kind&lt;/strong&gt;, all of them in the same template directory. The directory README registers them in one table: kind | template | applies to | write / read.&lt;/li&gt;
&lt;li&gt;The template's header comment declares the &lt;strong&gt;paths it applies to&lt;/strong&gt; and the &lt;strong&gt;fields it must contain&lt;/strong&gt;. The template's H2 headings are the required sections, in a fixed order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The template is the lint rule.&lt;/strong&gt; The checker reads the template directly. It does not keep a separate rule list.&lt;/li&gt;
&lt;li&gt;A new document is created by copying the template and deleting the header comment, then filling it in. &lt;strong&gt;Copying the previous document of the same kind is forbidden.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The handover-note template (excerpt, redacted):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&amp;lt;!-- template
applies-to: docs/releases/v&lt;span class="err"&gt;*&lt;/span&gt;-delivery.md
must-contain: SHA-256；回滚；tag
--&amp;gt;
&lt;span class="gh"&gt;# vX.Y.Z artifact handover note&lt;/span&gt;

&lt;span class="gu"&gt;## Delivery result&lt;/span&gt;
&lt;span class="gu"&gt;## Artifacts and verification&lt;/span&gt;
&lt;span class="gu"&gt;## Release scope&lt;/span&gt;
&lt;span class="gu"&gt;## Deploy order and smoke&lt;/span&gt;
&lt;span class="gu"&gt;## Rollback and acceptance boundary&lt;/span&gt;
&lt;span class="gu"&gt;## Record&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two tokens inside &lt;code&gt;must-contain&lt;/code&gt; — &lt;code&gt;SHA-256&lt;/code&gt;, and &lt;code&gt;回滚&lt;/code&gt; (rollback) — are exactly the things imitation drift had dropped. The rule lives in the template. Change the template, and the check changes with it. There is no "the spec says one thing and the check looks for another."&lt;/p&gt;

&lt;p&gt;After the cleanup, the documentation-model spec was split out of a 1,123-line mixture (about 460 lines of it were templates) into a 252-line project layer, a separate shared layer, and 31 templates. The templates cover 338 applicable documents in the repo. None is unowned, and none is owned by two templates.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Before and after the cleanup
&lt;/h2&gt;

&lt;p&gt;How it was measured: "before" is the start of the cleanup branch (October 14), and "after" is the state once the cleanup had been merged (October 15). The two trees were measured item by item with the same statistics script, and the measurement can be repeated. The table below keeps only the rows that bear on addressing. Checker violations, the entry file, and broken links are in post 4.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plan-index columns / rows&lt;/td&gt;
&lt;td&gt;8 columns / 56 rows, 4 rows with the wrong column count&lt;/td&gt;
&lt;td&gt;6 columns / 62 rows, 0 misaligned rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Index row width (characters), average / max&lt;/td&gt;
&lt;td&gt;394 / 1128&lt;/td&gt;
&lt;td&gt;157 / 227&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct status spellings&lt;/td&gt;
&lt;td&gt;9 (including &lt;code&gt;done&lt;/code&gt; inside a misaligned row)&lt;/td&gt;
&lt;td&gt;6 actually used (vocabulary of 9)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct type spellings&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate Stable IDs&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bundles not registered in the index&lt;/td&gt;
&lt;td&gt;1 (51 in total)&lt;/td&gt;
&lt;td&gt;0 (53 in total)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bundles with no Stable ID / no &lt;code&gt;Replaced by&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;16 / 35&lt;/td&gt;
&lt;td&gt;0 / 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata formats&lt;/td&gt;
&lt;td&gt;3 coexisting&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Case index&lt;/td&gt;
&lt;td&gt;457 lines; main table has 86 IDs; the narrative has another 83 IDs not in the main table&lt;/td&gt;
&lt;td&gt;169 lines; the main table's 86 IDs unchanged; narrative IDs: 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Derived case views&lt;/td&gt;
&lt;td&gt;By layer, 204 lines (25 IDs not in the main table); by module, 248 lines (18 IDs)&lt;/td&gt;
&lt;td&gt;123 lines / 148 lines, 0 such IDs, generated by script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Document templates&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A thinner index is not deleted information. The row count did not shrink. What got narrower is each row, and the progress narrative went back to the bundle. Of the checker's ERRORs before the cleanup (how they were measured, and how many, is in post 4), the top four were all addressing problems: case narrative mixed in ahead of the main table, 102; type not in the vocabulary, 39; bundle missing metadata, 35; status not in the vocabulary, 28.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Where each document goes, what it is called, and which copy to trust
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Document&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Name / ID&lt;/th&gt;
&lt;th&gt;Source of truth&lt;/th&gt;
&lt;th&gt;Derived view&lt;/th&gt;
&lt;th&gt;Template&lt;/th&gt;
&lt;th&gt;Human's role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Requirements / design&lt;/td&gt;
&lt;td&gt;The bundle under &lt;code&gt;plans/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;T&amp;gt;-YYYYMMDD-NNN&lt;/code&gt; ↔ directory&lt;/td&gt;
&lt;td&gt;The bundle metadata block&lt;/td&gt;
&lt;td&gt;One row of the index&lt;/td&gt;
&lt;td&gt;One each for requirements and design&lt;/td&gt;
&lt;td&gt;Approve the plan, and make the call on scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation record&lt;/td&gt;
&lt;td&gt;The same bundle (in the lightweight shape, an "implementation record" section inside requirements)&lt;/td&gt;
&lt;td&gt;Follows the bundle&lt;/td&gt;
&lt;td&gt;This document&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Implementation record&lt;/td&gt;
&lt;td&gt;Confirm the departure is acceptable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-running track&lt;/td&gt;
&lt;td&gt;&lt;code&gt;plans/tracks/&amp;lt;topic&amp;gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Topic name&lt;/td&gt;
&lt;td&gt;Metadata of the related bundles&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Track README&lt;/td&gt;
&lt;td&gt;Make the call on track priority and on closing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test case&lt;/td&gt;
&lt;td&gt;The global case index + the bundle's test-cases&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TC-&amp;lt;module&amp;gt;-NNN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The index's main table&lt;/td&gt;
&lt;td&gt;By layer / by module (generated)&lt;/td&gt;
&lt;td&gt;Case index, case&lt;/td&gt;
&lt;td&gt;At acceptance, confirm a coverage gap is acceptable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database migration&lt;/td&gt;
&lt;td&gt;The migration directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NNNN-YYYYMMDD-&amp;lt;slug&amp;gt;.sql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The script itself; the sequence sets the order&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;— (not a document)&lt;/td&gt;
&lt;td&gt;Before deploy, confirm the execution list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release-seal test report (L3)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reports/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YYYYMMDD-test-report-vX.Y.Z.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The report itself&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Release-seal test report&lt;/td&gt;
&lt;td&gt;Accept the release seal: release on the evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incident report (L3)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;reports/&lt;/code&gt;, by month&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YYYYMMDD-&amp;lt;slug&amp;gt;.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The report itself&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Incident report&lt;/td&gt;
&lt;td&gt;Confirm the root cause, and decide whether to codify a rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spec (L1)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;handbook/&lt;/code&gt;, &lt;code&gt;testing/guides/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A fixed filename; shared layer and project layer in pairs&lt;/td&gt;
&lt;td&gt;This document&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Spec document&lt;/td&gt;
&lt;td&gt;Make the call when a rule is codified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment runbook&lt;/td&gt;
&lt;td&gt;&lt;code&gt;deployment/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;By environment / topic&lt;/td&gt;
&lt;td&gt;This document&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Deployment runbook&lt;/td&gt;
&lt;td&gt;Approve production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changelog / handover note (deliverables)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;releases/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vX.Y.Z.md&lt;/code&gt; / &lt;code&gt;vX.Y.Z-delivery.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;This document&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;One each&lt;/td&gt;
&lt;td&gt;Confirm the deliverable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User manual (deliverable)&lt;/td&gt;
&lt;td&gt;The docs root&lt;/td&gt;
&lt;td&gt;A fixed filename&lt;/td&gt;
&lt;td&gt;This document&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;User manual&lt;/td&gt;
&lt;td&gt;Spot-check, then confirm the deliverable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most cells of "derived view" are "—". Build one only when there really are several ways to read the same source, and once it exists a script generates it. Deliverables do not enter the routing system, and they still have templates, because they drift under imitation too. Every cell of "human's role" is approve, make the call, accept, or confirm. The human does not need to know where a document lives or what it is called. Addressing is for the agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. How addressing breaks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Failure 1: exemptions make the template govern mostly new documents
&lt;/h3&gt;

&lt;p&gt;Most existing documents were written before onboarding and do not match a template, so they can only go into the exemption list. After the templates go live, what they govern is mostly new documents. Existing ones wait until they are touched, and are migrated then. Naming exemptions work the same way. 40 existing filenames do not match the rule. By principle 1 they are not moved unless someone migrates them, and a pattern glob keeps missing them. The exemption list was supposed to only fall, never rise. What makes "only fall, never rise" real is in post 4.&lt;/p&gt;

&lt;p&gt;The counter: when a frozen area or an exempted file is touched, migrate it to the standard shape in passing, so the list actually moves down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 2: work that was already open before the rules landed hits the new rules only at merge
&lt;/h3&gt;

&lt;p&gt;The changelog and handover note for v2.3.2 were committed to the main development branch on the morning of October 15. About 30 minutes later, the cleanup was merged in. When they were written, the main development branch had no doc-lint and no templates. The only "previous one" available to copy was the old format: registration reused the old eight-column index, and the sections did not follow a template. The check reported it at merge time, and it passed only after the sections were filled in against the template. This is not someone breaking a rule. It is work that was already open before the rules landed, hitting the new rules only at merge.&lt;/p&gt;

&lt;p&gt;The counter: during the cleanup, "a new document starts by copying the template; do not imitate the previous one" was written into that row of the routing table, and into the AI Quick Path at the top of the documentation-model spec. A branch opened before the rule landed cannot read it. Re-running the check at merge is the last gate, and this one was stopped there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 3: a template that is too strict forces the agent to fill in empty phrases
&lt;/h3&gt;

&lt;p&gt;If every section is required, the agent will stuff boilerplate into it to pass the check, and boilerplate looks the same as real content. The adjudicator cannot tell them apart. The counter is to press the required sections down to the minimum. Mark the rest "(optional)," and delete them if they are unused. If there is nothing to say, write "none," which is more honest than a paragraph of boilerplate. Sections outside the template are allowed. A template specifies &lt;strong&gt;where to look&lt;/strong&gt;, not &lt;strong&gt;how much to write&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 4: judging the scope of application by the content
&lt;/h3&gt;

&lt;p&gt;The template check originally decided whether a document was a generated view, and whether to skip it, by looking for phrases in the body. The result was that a design document which mentioned the generate command was skipped entirely. It was found only when the check was run on the cleanup plan's own document. Scope of application is an addressing problem, so it should be judged by path. The applicable path in the template's header comment is more reliable than any feature of the body. How the tool itself is tested is left to post 4.&lt;/p&gt;

&lt;h2&gt;
  
  
  14. Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Addressing has four layers. The directory answers which area. The ID answers which one. Status and the replacement chain answer whether it still counts. The template answers which section the information is in. Drop a layer, and the agent has to guess.&lt;/li&gt;
&lt;li&gt;Three principles. Split directories by responsibility, and do not move finished work (can grep / glob). One fact, one source; the rest are derived views (is also the main author). Status is written in the document, not expressed by the directory (follows the literal text).&lt;/li&gt;
&lt;li&gt;IDs have to be mechanically derivable in both directions. Taking a number scans every parallel branch and worktree. On a collision the later claimant moves, and records the original number. An ID that has already been issued is exempted rather than renamed.&lt;/li&gt;
&lt;li&gt;Steady-state rules and onboarding an existing tree are written apart. A new project has zero exemptions and no frozen area. An old project's frozen area, effective date, exemption baseline, and historical directory layer all go into one onboarding config. Names are read literally. &lt;code&gt;legacy&lt;/code&gt; and &lt;code&gt;changes&lt;/code&gt; both are.&lt;/li&gt;
&lt;li&gt;The template is addressing inside the document, and it is the check rule itself. Without a template, the agent only imitates the previous document. It breaks when exemptions make the template govern mostly new documents, when work opened before the rules landed hits them only at merge, when a template that is too strict forces empty phrases, and when scope is judged by content instead of by path.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Series
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/agents-arent-amnesiac-they-cant-find-the-entry-point-documentation-layers-for-large-projects-53pk"&gt;Agents Aren't Amnesiac — They Can't Find the Entry Point: Documentation Layers for Large Projects (1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/make-the-agent-read-only-what-it-should-documentation-routing-2-450d"&gt;Make the Agent Read Only What It Should: Documentation Routing (2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Give Every Document Exactly One Place: Documentation Addressing (3) (this post)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/keep-documents-from-rotting-the-document-lifecycle-in-the-agent-era-4-14m8"&gt;Keep Documents from Rotting: The Document Lifecycle in the Agent Era (4)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>documentation</category>
      <category>productivity</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Make the Agent Read Only What It Should: Documentation Routing (2)</title>
      <dc:creator>张旭</dc:creator>
      <pubDate>Fri, 16 Oct 2026 02:04:57 +0000</pubDate>
      <link>https://dev.to/illegal/make-the-agent-read-only-what-it-should-documentation-routing-2-450d</link>
      <guid>https://dev.to/illegal/make-the-agent-read-only-what-it-should-documentation-routing-2-450d</guid>
      <description>&lt;p&gt;&lt;em&gt;English edition of a series I originally wrote in Chinese (&lt;a href="https://github.com/IllegalCreed/agent-doc-architecture/blob/main/blog/02-routing.md" rel="noopener noreferrer"&gt;original&lt;/a&gt;); translated with AI assistance.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the second post in the series "Agent-first Documentation Layers for Large Projects." &lt;a href="https://dev.to/illegal/agents-arent-amnesiac-they-cant-find-the-entry-point-documentation-layers-for-large-projects-53pk"&gt;Post 1&lt;/a&gt; gave the four-layer model, L0–L3: L0 is the entry file loaded automatically every session, L1 is the specs split by work type, L2 is module and change documents, and L3 is execution evidence. This post answers one question: the agent is handed a task — which documents should it read? The material is still the enterprise payment-approval system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. The spec was already there. There was just no road.
&lt;/h2&gt;

&lt;p&gt;Post 1 opened with that release seal (freeze the version, run the full test suite, tag, and produce the package). On September 16 two versions were tagged on the same day, and neither changelog nor handover note existed until the rule-making commit two days later. That same commit created the release-seal spec and added an "operation → required reading" table to the entry file. This post digs down from that table.&lt;/p&gt;

&lt;p&gt;First, say "could not find it" precisely. Post 1 already noted that before the rule was written, the definition of the "six-suite full run" was already in the project testing spec, and a search of the entry file of that day for "full suite" and "release seal" came back empty.&lt;/p&gt;

&lt;p&gt;Two failures have to be distinguished. Their fixes are completely different.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buried.&lt;/strong&gt; The road is there. A few hundred lines of narrative cover it. The fix is to shorten, to bold, to move it somewhere visible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unreachable.&lt;/strong&gt; Starting from the task in hand, the entry has no signpost pointing at it. There is only one fix: build a road.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ours was the second, and it was more hidden than "there is no reference." Strictly speaking, the reference graph had a two-hop path. The development-workflow section of the entry file pointed its "detailed spec" at the development-workflow document (the project's development spec). That document's related-docs table had a row linking to the project testing spec, and the cell said "five-layer test pyramid." The full-suite definition was on line 154 of that spec. The path was connected. For an agent that had been handed a release seal, no signpost along the way said "full suite" or "release seal." Another path mentioned the release seal and then ended in the wrong place. The entry mentioned the path of the change index. Three rows in the index, "v2.0.x local release seal and packaging," linked to those seals' own documents. One of the requirements listed the six suites item by item. That was one historical instance, not the current definition, and it did not link to the testing spec. For a reader with no memory across sessions, a document that cannot be reached from the task in hand does not exist, however well it is written.&lt;/p&gt;

&lt;p&gt;How the rule-making commit built the road is worth a close look. It did not copy the six-suite definition into the entry file. It added a table, and the first row pointed "release seal / produce a package / tag / say 'full suite green'" at the new release-seal spec. The entry file went from 410 lines to 424. The new section, title and introduction included, was 14 lines. The 7 rows of the table were 7 roads, and the release-seal road took one of them.&lt;/p&gt;

&lt;p&gt;After that, the versions can be checked one by one. As of October 14, the nine versions published after the rule, v2.0.5 through v2.3.1, each have both a changelog and a handover note. Release-seal test reports exist for every version from v2.1.0 on (v2.0.5 has none). I do not credit a table alone — the same commit also wrote the release-seal checklist and the required contents of the two release documents. For any of that to work, it has to be read, and what connected it to the entry was that one row of the routing table.&lt;/p&gt;

&lt;p&gt;So the question of this post is not "how do we make the agent remember the spec." It is: &lt;strong&gt;how do we guarantee a road from the entry to every spec that should be read, and that the agent walks only the roads it should.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Three shapes of routing failure
&lt;/h2&gt;

&lt;p&gt;Widen the view from that one release seal to the whole doc system. Routing fails in three shapes, and all three showed up in our project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No road.&lt;/strong&gt; The spec exists and cannot be reached from the entry. That is the opening.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The road is too wide.&lt;/strong&gt; The entry file loads the detailed rules of every kind of work. Every session has to swallow all of it, and the one line that matters is diluted. This is the L0 swelling from post 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The road breaks halfway.&lt;/strong&gt; The routing table delivers the agent to the project-layer spec, and the project layer never says which shared spec it inherits. The agent finishes the project layer and thinks it has read everything. Section 10 goes into this.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The three shapes have one root cause: &lt;strong&gt;the docs are organized the way the writer classifies them, while the agent arrives holding only one question — what am I supposed to do now.&lt;/strong&gt; Writers organize by directory, by document type, by time. That is natural for someone who knows the repo. The agent is a newcomer every session. It does not know what the repo looks like. It only knows the user told it to seal a release, change a table, or review a diff. The key of the route has to be something the agent already holds when it walks in.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Three principles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Principle 1: the entry file only routes. It does not carry content.&lt;/strong&gt; (Answers "context is limited and it costs money.") Post 1 derived the same sentence from "no memory across sessions": the entry is the one thing the agent is certain to read, so it has to exist. Here is the other half. The entry file is loaded in full on every session. Every line that is put into it is paid for by every session. Copy the six-suite definition into the entry, and every session that has nothing to do with a release seal reads it once. Make it a route, and only the release-seal session pays that cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 2: route by work type, not by directory.&lt;/strong&gt; (Answers "no memory across sessions.") A directory tree answers "where is the thing," on the premise that the reader knows what the thing is called and which class it belongs to. That premise is itself memory. The agent has no such memory when it walks in. It has only the task the user handed it. The two-hop reference in the opening is the counterexample: the directories and the references were connected, but the signposts were written as "here is what we have," not as "here is what you are doing." So the first column of the routing table is an action: seal a release, change a table structure, review code. It is not a directory name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 3: every hop declares the next hop and its own parent.&lt;/strong&gt; (Answers "follows the literal text and does not infer intent.") A human who reads a "project database spec" naturally thinks "there is probably a shared one too." An agent does not. If a document does not say "I am a subset of that one," the agent treats it as the whole rule. If it does not say "where to go after reading," the agent stops. Routing is not only the entry's job. Every document along the road has to answer both questions.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Mechanism 1: the "operation → required reading" table
&lt;/h2&gt;

&lt;p&gt;The fragment below is arranged in the recommended shape. The source is the table now in our project's entry file, redacted and excerpted. The original table had only two columns, with the point written in a parenthetical. Here the point is its own column.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Operation | Required spec | One-sentence point |
|---|---|---|
| Release seal / produce a package / tag / say "full suite green" | handbook/engineering/release-process.md | "Full suite green" is allowed only when all six suites pass by the defined criteria |
| DB schema / cross-environment data change | handbook/database/project-database-changes.md | A schema change requires an incremental script; the script must run on the production executor |
| Write / change / review any test | testing/guides/project-testing.md | Pick the layer from the "work type → test layer" table first; do not escalate a UI contract to end-to-end |
| Verify frontend behavior in a browser | The "do not guess selectors" section of the entry file | Browser automation is for locating, and is not acceptance evidence |
| Code review / re-review | The change's handoff.md + test-cases.md | The conclusion must carry "path:line" evidence |
| Change the workflow engine | tracks/refactor/roadmap.md + the current change | Read the historical lessons before changing engine behavior |
| Change the mobile client | The "hard constraints for real-device compatibility" section of the entry file | Check a new dependency against the WebView version floor first |

&lt;span class="gs"&gt;**Layered reading:**&lt;/span&gt; L0 = the hard rules at the top of the entry file, always in force. L1 = the table above, addressed by work type; before starting, read only the document in the matching row. L2 = the module's change plan and handoff; read them when entering that module. Do not read the whole repo for one task, and do not start from memory without reading L1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This table is where principle 2 lands (answers "no memory across sessions"): a newcomer can look up the next hop from the task already in hand. Two things are worth unfolding: what gets a row, and the third column.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bar for a row: high risk, low frequency, easy to get wrong from memory.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High risk: getting it wrong is expensive or irreversible. Production databases, releases, cross-environment data.&lt;/li&gt;
&lt;li&gt;Low frequency: something done once or twice a month. The relevant spec will not happen to be sitting in recent context.&lt;/li&gt;
&lt;li&gt;Easy to get wrong from memory: the agent's common sense produces an answer that looks reasonable and is wrong. "Full suite" is the type case. In common sense, full means "run all the tests." In the project, the definition is a fixed six suites, and none may be omitted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ordinary coding does not get a row. "Write a list page" has no row. It meets none of the three bars. Rules for high-frequency work belong in the development spec, and one link from the entry to that spec is enough. The moment the table starts collecting ordinary operations, it grows into a second development spec.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "one-sentence point" column delivers the most important constraint even if the hop is never taken.&lt;/strong&gt; The routing table is in L0, so it is read every time. The target document is in L1, one hop further. The agent may skip the hop because context is tight, or because it judges "this change is small." The point column is the floor: even without the hop, "a schema change requires an incremental script" is already in front of it. There is a side effect. If a spec's point cannot be written as one sentence, the spec itself often has no core rule, and the target document should be cleaned up first.&lt;/p&gt;

&lt;p&gt;The layered-reading paragraph under the table blocks both directions. "Do not read the whole repo for one task" prevents reading too much. "Do not start from memory without reading L1" prevents reading too little. It is the model from post 1, written as one paragraph inside L0, so the agent knows which layer it is on every time it walks in.&lt;/p&gt;

&lt;p&gt;Look again at the point on the code-review row: the conclusion must carry "path:line" evidence. A review conclusion is ultimately for a human to adjudicate, so the routing table does not only say what the agent reads. It also says what shape the evidence handed to the adjudicator must have. Also, two rows name "some section of the entry file" as the required reading. That is a signal. Section 10 comes back to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Mechanism 2: one entry for every agent
&lt;/h2&gt;

&lt;p&gt;On March 4 the entry file was 128 lines. By August 20 it had grown to 406, and the name had always been CLAUDE.md, the filename Claude Code expects. Codex and Kimi were also in the collaboration. An entry named for one tool's convention is not necessarily loaded when the tool changes. The documentation reshuffle on September 3 gathered the rules into AGENTS.md, and CLAUDE.md shrank to a forwarder. It is 5 lines now, and it says three things. The skeleton, paraphrased:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Compatibility entry for &amp;lt;tool name&amp;gt;&lt;/span&gt;

The only project rules are in AGENTS.md. Read it in full before starting.
Do not repeat any rule in this file. To add or change a rule, edit AGENTS.md,
so every agent uses the same source of truth.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The forwarder may not contain a single rule. The moment it has one, the two entries grow apart, and different agents see different project rules.&lt;/p&gt;

&lt;p&gt;The single entry has another half: &lt;strong&gt;personal memory.&lt;/strong&gt; Some agent tools keep a memory file on the machine for the user and load it across sessions. It is not in the repo. Other agents, and other machines, cannot see it. The batch of project-level lessons in symptom 6 of post 1 was stuck there.&lt;/p&gt;

&lt;p&gt;To decide where an experience goes, ask one question: &lt;strong&gt;if the agent changes, or the machine changes, should this still be in force?&lt;/strong&gt; (Answers "several agents at once.") If yes, it goes into the repo, placed in the entry or in an L1 spec according to its layer. If no, it stays in personal memory. What stays should be only collaboration preferences: reply in Chinese, show the human the diff before committing.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Mechanism 3: shared → project, two layers of inheritance
&lt;/h2&gt;

&lt;p&gt;L1 specs come in two layers. The &lt;strong&gt;shared layer&lt;/strong&gt; writes rules that hold across projects, and it leaves &lt;strong&gt;placeholders&lt;/strong&gt;: the blanks each project has to answer for itself. Which database dialect, where migration scripts live, which suites the full run includes. The &lt;strong&gt;project layer&lt;/strong&gt; writes only four things: the filled placeholders, this project's current state, exceptions to the shared rules, and the pitfalls this project has hit.&lt;/p&gt;

&lt;p&gt;The two layers are joined by one inheritance declaration, and that declaration has to sit at the top of the project layer. The reason is routing. The routing table points at the project layer, because that is where the directly executable rules are, so the agent lands on the project layer. If the project layer does not name its parent, the shared layer is unreachable from it — the same shape as the opening, happening inside L1. (Answers "follows the literal text and does not infer intent.")&lt;/p&gt;

&lt;p&gt;The positive example is the testing landing note of our mobile subproject. The first sentence under the title is the inheritance declaration (the link has been replaced with the document name):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This document is the in-project filling of the Shared Testing Spec (§10). Shared content is not repeated. It fills only this project's placeholders, current state, and exceptions. Living document: when execution hits a new pitfall, write it back here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One sentence answers three questions: who the parent is; what this document writes and what it does not; that this document will be updated. The parent has the matching interface. Section 10 of the shared testing spec, "Landing and exceptions," lists the blanks in a "placeholder | the project must specify" table. The placement rules in the testing-center README add one more constraint: a subproject may have its own landing note, "but it must be reachable from this page, and it must not copy the shared spec into a second source of truth."&lt;/p&gt;

&lt;p&gt;The skeleton of a project layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Database change spec (project layer)&lt;/span&gt;
&lt;span class="gt"&gt;
&amp;gt; Inherits: &amp;lt;shared database change spec&amp;gt;. This document writes only filled&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; placeholders, current state, exceptions, and this project's pitfalls.&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; Whatever is not written here follows the shared layer. Where this document&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; conflicts with the shared layer, the "Exceptions" section of this document wins.&lt;/span&gt;

&lt;span class="gu"&gt;## Filled placeholders&lt;/span&gt;
&lt;span class="gu"&gt;## Current state&lt;/span&gt;
&lt;span class="gu"&gt;## Exceptions&lt;/span&gt;
&lt;span class="gu"&gt;## This project's pitfalls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Who wins on a conflict" has to be written down. When the two layers disagree, an agent that follows the literal text will not guess the author's intent. It will pick one of the two sentences and believe it. Our own counterexample is in section 10.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Mechanism 4: a hop-by-hop routing chain
&lt;/h2&gt;

&lt;p&gt;The routing table solves only the first hop. From the entry to the body, our design is a chain, and each layer answers only "where next":&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entry file → docs home (by responsibility) → top-level directory README (second-level routing + placement rules) → the AI Quick Path at the top of the document → the body&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;docs home&lt;/strong&gt; splits by responsibility. The opening paragraph of ours says "when looking something up, first judge the document's responsibility; stop guessing a location from the filename in the root," and the "quick entry" table under it has only two columns: what you want to know | the entry.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;top-level directory README&lt;/strong&gt; does second-level routing and also writes the placement rules. The testing center's README has only two sections. "Navigation" tells the reading agent which document to open. "Placement rules" tell the writing agent where new content goes. Reading and writing walk the same map, so a new document cannot step around the route. (Placement rules are the subject of post 3.)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;AI Quick Path&lt;/strong&gt; is an ordered reading list at the top of a document: for this kind of task, read this section of the document first, then that document. It is the last hop of the chain. It takes the agent from the door of the document straight to the section it needs.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## AI Quick Path&lt;/span&gt;

For this kind of task, read in order:
&lt;span class="p"&gt;1.&lt;/span&gt; The "core rules" section of this document.
&lt;span class="p"&gt;2.&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;the&lt;/span&gt; &lt;span class="na"&gt;parent&lt;/span&gt; &lt;span class="na"&gt;shared&lt;/span&gt; &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;: rules this document does not cover.
&lt;span class="p"&gt;3.&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;the&lt;/span&gt; &lt;span class="na"&gt;related&lt;/span&gt; &lt;span class="na"&gt;change&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="na"&gt;s&lt;/span&gt; &lt;span class="na"&gt;requirements&lt;/span&gt; &lt;span class="na"&gt;and&lt;/span&gt; &lt;span class="na"&gt;design&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;: why it changed, and what changed.
&lt;span class="p"&gt;4.&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;the&lt;/span&gt; &lt;span class="na"&gt;matching&lt;/span&gt; &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;: take a number, look up history.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why a chain, and not one big table? (Answers "context is limited and it costs money.") Each layer carries only the next hop, so each step the agent takes costs a small piece of tokens. Adding a document changes only the README of the directory it lives in. The entry file is not touched, and the routing table does not grow as the docs grow.&lt;/p&gt;

&lt;p&gt;The last hop is the one we did least of. Before the cleanup, only 2 documents under the docs tree had an AI Quick Path. The earlier layers of the chain were there. The last hop was mostly absent, and the agent still had to start from the top once it reached the door of the document.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Mechanism 5: a handoff document is a one-time route
&lt;/h2&gt;

&lt;p&gt;The routing table serves every session. A handoff document (handoff.md) serves one: a change passing from this agent to the next. In one of our cross-layer handoffs, the section "task notes for the coding agent" opens with the reading order:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Read this directory's requirements, design, implementation, test-cases, and impact-map first, then the root AGENTS.md and the development workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"This directory" means the change's bundle: one change's requirements, design, implementation record, and test cases in a single directory. A later section is called "what every review handoff must include," and it lists 5 items: the branch, the commits, and the deviation from the plan; the IDs of new cases, the actual commands, the results, and where the report is; the key evidence for this stage; when a migration is involved, the number-collision check plus first-run and re-run evidence; and an update to the actual execution record. After that is a "review block list." Each check states "the concrete condition that fails it," for the agent responsible for the review.&lt;/p&gt;

&lt;p&gt;This is a route customized for this change and this successor. It points two ways (answers "no memory across sessions": the agent taking over does not know where the previous session stopped):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;In:&lt;/strong&gt; what to read first, and in what order. Same shape as the routing table, scoped down to one bundle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Out:&lt;/strong&gt; which evidence every review handoff must attach. It specifies the shape of what the agent hands to the next link.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The review block list and the routing table connect. The "code review" row of the routing table points exactly at it. The full path of a review task is therefore: entry routing table → that change's handoff → the block list and the case matrix → the code. The human is at the end of the chain, reads the evidence the agent arranged against the list, and releases or sends it back.&lt;/p&gt;

&lt;p&gt;A handoff document is one-time. When the change is finished, it becomes L2 history and no longer carries a routing duty.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. How each kind of work walks
&lt;/h2&gt;

&lt;p&gt;Put the five mechanisms together and list the full path by work type. The table below is the recommended shape, not a copy of our project's current state. The current state is still some distance from it; see the next section. The "project layer" column says what the project layer has to fill in.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Work type&lt;/th&gt;
&lt;th&gt;L1 entry&lt;/th&gt;
&lt;th&gt;Shared layer&lt;/th&gt;
&lt;th&gt;Project layer&lt;/th&gt;
&lt;th&gt;Next hop&lt;/th&gt;
&lt;th&gt;Human's role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Requirements&lt;/td&gt;
&lt;td&gt;Documentation-model spec&lt;/td&gt;
&lt;td&gt;Requirements template and required sections&lt;/td&gt;
&lt;td&gt;ID and directory conventions&lt;/td&gt;
&lt;td&gt;The change bundle's requirements document&lt;/td&gt;
&lt;td&gt;Make the call on scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;Project development spec&lt;/td&gt;
&lt;td&gt;Shared development spec&lt;/td&gt;
&lt;td&gt;Branch model, this project's hard rules&lt;/td&gt;
&lt;td&gt;The change's design and handoff&lt;/td&gt;
&lt;td&gt;Approve the plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refactoring&lt;/td&gt;
&lt;td&gt;Project development spec&lt;/td&gt;
&lt;td&gt;Shared development spec&lt;/td&gt;
&lt;td&gt;Historical lessons of the refactoring track&lt;/td&gt;
&lt;td&gt;The current change bundle&lt;/td&gt;
&lt;td&gt;Make the call on track priority and on closing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;Project testing spec&lt;/td&gt;
&lt;td&gt;Shared testing spec&lt;/td&gt;
&lt;td&gt;Filled placeholders, subproject landing notes&lt;/td&gt;
&lt;td&gt;Case index, the change's cases&lt;/td&gt;
&lt;td&gt;Make the call on the testing definition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release seal&lt;/td&gt;
&lt;td&gt;Release-seal spec&lt;/td&gt;
&lt;td&gt;Shared release process&lt;/td&gt;
&lt;td&gt;Full-suite definition, release-seal checklist&lt;/td&gt;
&lt;td&gt;The two release documents (write them); L3 release-seal test report&lt;/td&gt;
&lt;td&gt;Accept the release seal: release against the checklist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User manual&lt;/td&gt;
&lt;td&gt;Manual-maintenance convention&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Which changes must update the manual&lt;/td&gt;
&lt;td&gt;The matching manual section (write only; not a source of knowledge)&lt;/td&gt;
&lt;td&gt;Spot-check, then confirm the deliverable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB change&lt;/td&gt;
&lt;td&gt;Project database spec&lt;/td&gt;
&lt;td&gt;Shared database change spec&lt;/td&gt;
&lt;td&gt;Dialect, migration directory, execution tool, pitfalls&lt;/td&gt;
&lt;td&gt;Migration index (take a number)&lt;/td&gt;
&lt;td&gt;Make the call on compatibility rules and exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy&lt;/td&gt;
&lt;td&gt;Deployment runbook&lt;/td&gt;
&lt;td&gt;Shared deployment spec&lt;/td&gt;
&lt;td&gt;Per-environment booklets, package-name rules&lt;/td&gt;
&lt;td&gt;This deploy's migration execution list&lt;/td&gt;
&lt;td&gt;Approve production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code review&lt;/td&gt;
&lt;td&gt;Review checks&lt;/td&gt;
&lt;td&gt;Shared review list&lt;/td&gt;
&lt;td&gt;This project's checks&lt;/td&gt;
&lt;td&gt;The handoff's block list and case matrix&lt;/td&gt;
&lt;td&gt;Release or send back on the evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser debugging&lt;/td&gt;
&lt;td&gt;Browser-debugging convention&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Selector convention&lt;/td&gt;
&lt;td&gt;Frozen into a test case&lt;/td&gt;
&lt;td&gt;Accept: release on the result of the frozen case&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Look at "next hop" first. No cell points back at the entry file. The route only goes downward, and it ends at L2, L3, a deliverable, or the code itself. Cells marked "write" are deliverables. The agent maintains them and does not learn the job from them. The "shared layer" has empty cells, because not every kind of work needs to be split in two. Split only when the rule can be reused across projects. Every cell of "human's role" is a decision point. What the human reads while deciding is exactly the report, the evidence, and the execution list produced by the "next hop" column.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. How routing breaks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Failure 1: the routing table grows into the entry file itself
&lt;/h3&gt;

&lt;p&gt;Two rows in the fragment of mechanism 1 name "some section of the entry file" as the required reading. The original table was clearer: of the 7 rows at the time the rule was written, 3 pointed back at the entry file itself. On October 14 it grew to 12 rows, and the ones pointing back at itself became 5. The layered-reading principle under the table lists four L0 hard rules of its own, and only two of those 5 rows point at them. The other 3 — the checklist for syncing a new business type, the selector convention, and real-device compatibility for the mobile client — are details that only a specific kind of work needs, and they live in an entry file that is loaded every time.&lt;/p&gt;

&lt;p&gt;Another sign is inside the cells. The original table wrote its points in parentheticals. The parenthetical in the browser cell contained the rule text, the evidence standard, and the date the rule was codified. A cell had grown into a paragraph, and the routing table had started to carry content.&lt;/p&gt;

&lt;p&gt;Meanwhile the entry file grew from 424 lines after the rule was written to 457 (after the cleanup it fell to 242; see post 4). The routing table was supposed to take weight off the entry file. Written this way, it makes the file thicker.&lt;/p&gt;

&lt;p&gt;Three counters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a row's target is "some section of the entry file," first ask whether it is on the L0 hard-rule list. If it is not, sink that section into an L1 document and point the route at it.&lt;/li&gt;
&lt;li&gt;The point column is allowed one sentence. Whatever does not fit belongs in the target document.&lt;/li&gt;
&lt;li&gt;Before adding a row, pass it through the bar: high risk, low frequency, easy to get wrong from memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Failure 2: pointing at a document that has been superseded
&lt;/h3&gt;

&lt;p&gt;A broken link at least shows itself: the agent opens it and gets an error. Our entry layer currently has 0 broken links. The real danger is a link that is alive and a target that is no longer current fact. An agent that follows the literal text arrives and believes. "Superseded" has two forms. One is formally replaced by a new version. The other was only ever one historical instance, and it got used as the current definition.&lt;/p&gt;

&lt;p&gt;Our release-seal route had the second form, at the end of the chain. The release-seal spec at the time the rule was written said, of the handover note, "the template is to follow v2.0.2-delivery.md," followed by a list of required items. The last hop of the routing chain landed on a historical delivery document, not on a current definition. The second two-hop path in the opening has the same shape: the signpost mentioned the release seal, and the destination was the requirements of one particular seal. Pointing at a historical instance writes "do it the way we did last time" into the rules. Our handover notes then drifted generation by generation. From v2.0.3 on, they were missing the SHA-256 checksum the spec explicitly required. How to do templates is covered in post 3.&lt;/p&gt;

&lt;p&gt;The first form is a field problem. A document is replaced by a new version, and the header never writes &lt;code&gt;Replaced by&lt;/code&gt;. Post 1 counted this: most of our bundles did not write the field, so the rule "if you read a superseded document, jump first" spun in place.&lt;/p&gt;

&lt;p&gt;The counter: a route points only at "current definition" documents (specs, templates, indexes), or it is written as a pointer that changes with the task, such as "the handoff of the corresponding change." It does not hard-code one instance (some version's delivery document, some historical change plan). A target that can be superseded must carry &lt;code&gt;Replaced by&lt;/code&gt;. A script can check "the link is alive." It cannot check "the target is still in force." The latter depends on the metadata field. How to enforce it is left to post 4.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 3: the project layer does not declare what it inherits
&lt;/h3&gt;

&lt;p&gt;This is the most complete counterexample in our project. Before the cleanup, the project testing spec and the project database spec each cited their shared spec 0 times. The shared database spec was still marked draft v0.1. At the time, the only project-layer document that correctly declared its inheritance was the mobile testing landing note from mechanism 3.&lt;/p&gt;

&lt;p&gt;The irony is that the shared database spec already had an AI Quick Path at the top: step 1, read the shared spec; step 2, read the project adaptation. The parent pointed at the child. The road only ran downward. The routing table delivered the agent straight to the project layer, and the project layer had not one sentence sending it one level up. An agent that came in along the routing table finished the project database spec, and the shared layer's rules did not exist for it. The two layers had become two documents that did not know each other.&lt;/p&gt;

&lt;p&gt;The counter: the inheritance declaration at the top of the project layer is required, and it is the easiest thing for a machine to check (answers "can grep, glob, and run scripts"). The defect itself was found with one grep: whether a project-layer document contains a link to the shared layer is one command. If the shared layer is still a draft, the project layer either pushes it to a final version or declares "this document is self-contained." Do not leave the agent to guess whether a draft counts. During the cleanup both project-layer specs gained an inheritance declaration at the top, the shared database spec was finalized, and a missing inheritance declaration is an error from doc-lint (see post 4).&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A routing failure is often not "buried." It is "no road." A document that cannot be reached from the task in hand does not exist for the agent. A connected reference is not a road. Building the road beats bolding the text: the road in the opening takes one row of the entry.&lt;/li&gt;
&lt;li&gt;Three principles. The entry file only routes and does not carry content (context is limited and it costs money). Route by work type, not by directory (no memory across sessions). Every hop declares the next hop and its own parent (follows the literal text and does not infer intent).&lt;/li&gt;
&lt;li&gt;The routing table collects only operations that are high risk, low frequency, and easy to get wrong from memory. The one-sentence point delivers the most important constraint even if the hop is never taken.&lt;/li&gt;
&lt;li&gt;One entry, and the rest forward. If an experience should still be in force under a different agent, it goes into the repo. It does not stay in personal memory.&lt;/li&gt;
&lt;li&gt;Routing is a chain: entry → docs home → directory README → AI Quick Path → body. A handoff document is a one-time route for the next agent. It breaks in three places: the table grows into the entry, the end of the chain points at history, and the project layer does not acknowledge its parent.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Series
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/agents-arent-amnesiac-they-cant-find-the-entry-point-documentation-layers-for-large-projects-53pk"&gt;Agents Aren't Amnesiac — They Can't Find the Entry Point: Documentation Layers for Large Projects (1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Make the Agent Read Only What It Should: Documentation Routing (2) (this post)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/give-every-document-exactly-one-place-documentation-addressing-3-1oih"&gt;Give Every Document Exactly One Place: Documentation Addressing (3)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/keep-documents-from-rotting-the-document-lifecycle-in-the-agent-era-4-14m8"&gt;Keep Documents from Rotting: The Document Lifecycle in the Agent Era (4)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>documentation</category>
      <category>productivity</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Agents Aren't Amnesiac — They Can't Find the Entry Point: Documentation Layers for Large Projects (1)</title>
      <dc:creator>张旭</dc:creator>
      <pubDate>Fri, 16 Oct 2026 02:04:51 +0000</pubDate>
      <link>https://dev.to/illegal/agents-arent-amnesiac-they-cant-find-the-entry-point-documentation-layers-for-large-projects-53pk</link>
      <guid>https://dev.to/illegal/agents-arent-amnesiac-they-cant-find-the-entry-point-documentation-layers-for-large-projects-53pk</guid>
      <description>&lt;p&gt;&lt;em&gt;English edition of a series I originally wrote in Chinese (&lt;a href="https://github.com/IllegalCreed/agent-doc-architecture/blob/main/blog/01-agent-is-not-amnesiac.md" rel="noopener noreferrer"&gt;original&lt;/a&gt;); translated with AI assistance.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is the first post in the series "Agent-first Documentation Layers for Large Projects." The material comes from seven months of AI-collaborative development on an enterprise payment-approval system: I wrote code, tests, and docs together with Claude Code, Codex, and Kimi. The series covers only the documentation leg. Test methodology is a separate topic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Two release seals, and a spec that was already written
&lt;/h2&gt;

&lt;p&gt;On September 16, an enterprise payment-approval system was tagged twice, two versions on the same day. Neither version had a changelog or a handover note at the time.&lt;/p&gt;

&lt;p&gt;Two days later a commit landed that did three things at once: it added a standalone release-seal spec (release seal: freeze the version, run the full test suite, tag, and produce the package); it added an "operation → required reading" table to the entry file; and it backfilled the release documents those two versions had skipped — a changelog for business users and a handover note for operations. The introduction above the table wrote the lesson into the entry rules for the agent: "sealed a release without reading the full-suite definition, and skipped the release documents."&lt;/p&gt;

&lt;p&gt;The definition of "full" was not new that day. &lt;strong&gt;The spec already existed.&lt;/strong&gt; "The full run always includes the following six suites; none may be omitted" was already in the project testing spec. The docs home mentioned the "six-suite full-run discipline." The change plans for two earlier release seals had also required six-suite test evidence. The spec itself was enough. What was missing was the path from the entry file to it.&lt;/p&gt;

&lt;p&gt;Look at the entry file before that rule was written — the Markdown file loaded automatically at the start of every session. It was 410 lines. On shipping a release, the only text was a tagging rule tucked inside the Git branch conventions. The words "full suite" and "release seal" never appeared. From the entry, nothing pointed a release seal at that testing spec.&lt;/p&gt;

&lt;p&gt;The new release-seal spec spelled out the six-suite definition, the release-seal checklist, and the required contents of the two release documents. The first row of the table in the entry file looked like this (excerpt, redacted):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Required reading&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Release seal / produce a package / tag / say "full suite green"&lt;/td&gt;
&lt;td&gt;Release-seal spec (six-suite definition + checklist + templates for the two release documents)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice the last item in the first column: saying "full suite green." &lt;strong&gt;Saying a sentence&lt;/strong&gt; was registered as an operation. Before the agent may utter those words, it has to read that spec. (The second column says "templates." The spec body actually tells the agent to imitate a historical document. That trap waits for post 3.) As of October 14, the nine versions from v2.0.5 through v2.3.1 after the rule was written each have both a changelog and a handover note. Post 2 goes into the detail.&lt;/p&gt;

&lt;p&gt;So "the agent keeps forgetting things" blames the wrong object. The agent is not amnesiac. It never had memory across sessions. Every session is a newcomer walking in through the entry. The problem is that the entry does not take it where it needs to go. The series has one thesis: &lt;strong&gt;in a project where an agent writes most of the code, the first reader of the docs is the agent, and the doc system is a routing system for that agent.&lt;/strong&gt; This post is the model. The next three cover routing, addressing, and lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The first reader of the docs changed
&lt;/h2&gt;

&lt;p&gt;Start with the scale. Over seven months the project had 3,614 commits. 1,373 of them touched docs or the entry file, and at least 547 carried an agent attribution (a lower bound, counted from the attribution line). As of October 14 the docs tree held 832 Markdown files, 51 structured change bundles (one change's requirements, design, implementation record, and test cases in a single directory — a bundle), and 301 early single-file plans.&lt;/p&gt;

&lt;p&gt;Who reads these? At the start of every session the agent loads the entry file. Before it starts work it follows the entry to the specs. When it enters a module it opens the plans and the handoff records. My position is this: docs are now written for the agent first. What a human reads is what the agent produces at a decision point, not a cover-to-cover pass through the tree.&lt;/p&gt;

&lt;p&gt;The opening thesis has to land on the properties of an agent. The whole mechanism is derived from the table below. Every principle later in the series is tagged with the property it answers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent property&lt;/th&gt;
&lt;th&gt;What it requires of docs&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No memory across sessions&lt;/td&gt;
&lt;td&gt;Every session enters through the entry, so the entry must be a routing table&lt;/td&gt;
&lt;td&gt;L0 + the "operation → required reading" table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context is limited and it costs money&lt;/td&gt;
&lt;td&gt;Tokens are the first cost; drill down on demand&lt;/td&gt;
&lt;td&gt;L0–L3 layering, thin indexes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follows the literal text and does not infer intent&lt;/td&gt;
&lt;td&gt;Status, replacement, and scope are written out explicitly; "you might consult" becomes "you must read this before acting"&lt;/td&gt;
&lt;td&gt;State machine, &lt;code&gt;Replaced by&lt;/code&gt;, required-reading table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can grep, glob, and run scripts&lt;/td&gt;
&lt;td&gt;Names are pattern-matchable, metadata is parseable, rules can be linted&lt;/td&gt;
&lt;td&gt;ID scheme, metadata block, doc-lint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Is also the main author&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A convention that depends on good intentions will drift; a machine has to enforce it&lt;/td&gt;
&lt;td&gt;Hook / CI checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Several agents at once&lt;/td&gt;
&lt;td&gt;A single entry; lessons live in the repo, not in a personal memory&lt;/td&gt;
&lt;td&gt;One entry file (AGENTS.md)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A word on the last two rows. "Is also the main author": the agent that writes the spec may be the same agent that violates it in the next session, so every convention a machine can check eventually becomes a check (hook / CI is the general shape; our project chose another enforcement point, saved for post 4). "Several agents at once": Claude Code, Codex, and Kimi took turns. A lesson that exists only in one agent's personal memory is gone the moment a different agent shows up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code, tests, docs: three legs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Code, tests, and docs are equally necessary. Drop one and the other two fail.&lt;/strong&gt; The agent writes the code. Tests prove the code is right. Docs tell the agent how to write it and where to write the result back. The three lock together: the routing table points the agent at the testing spec; the case index points tests back at requirements; doc-lint is "the test suite for docs." In the opening accident the tests and the testing spec were both there. What was missing was the one hop, inside the doc system, that puts the testing spec in front of the agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three corollaries
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Corollary 1: the human's role shifts from reader to adjudicator.&lt;/strong&gt; (Answers "is also the main author.") If the agent is the main author, the human no longer has to be the daily reader or the maintainer. The human shows up at decision points: approve a plan, make the call, accept a release seal, confirm a deliverable. The human reads the summary, the diff, and the conversation digest the agent prepared for that decision. The human does not browse the doc tree and does not maintain the docs. The quality of the decision then depends on what the agent puts on the table. A spec the agent never read will not appear in the summary or the checklist it prepared. Getting the agent to the right document is the precondition for a human decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Corollary 2: a deliverable is not an engineering document.&lt;/strong&gt; (Answers "context is limited and it costs money.") User manuals, changelogs, and deployment handover notes are read by business users and by operations. They are product deliverables. The agent maintains them, but they do not enter the agent's routing system, or they only burn context. The routing table records only "after which operation this must be updated." We paid for leaving that row out: an approval flow went live on July 7, and the user manual was not updated until July 15. For eight days the flow list in the manual was wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Corollary 3: one document serves one reader.&lt;/strong&gt; (Answers "context is limited and it costs money.") A document that tries to serve both a human and an agent gets fat. Our plan index was supposed to answer only "where are the docs for this change." It was written as a progress board. The "next step" column averaged 98 characters, and the longest row was 1,128. Every time the agent opened the index it first had to swallow a page of progress narrative. The index is for the agent. When a human wants progress, the agent summarizes it on the spot, or a script generates a view.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Six symptoms
&lt;/h2&gt;

&lt;p&gt;"The agent keeps forgetting things" is a vague complaint. Split open, it is six different failures. Each one happened in this project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptom 1: if it does not know which document to read, it reads none.&lt;/strong&gt; The opening release-seal accident is the type case. The spec was there. The path was not. An agent will not search the whole repo because "there ought to be a spec." If the path is not in the entry, it starts from common sense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptom 2: it reads an outdated plan and treats it as current fact.&lt;/strong&gt; The AI Quick Path at the top of the documentation-model spec says: when a document shows it has been superseded, jump first to the new document named by &lt;code&gt;Replaced by&lt;/code&gt;. Of the 51 bundles, 35 did not write &lt;code&gt;Replaced by&lt;/code&gt;, and 16 did not even have a Stable ID (the document's fixed number). The rule was there. The field was not. The rule spun in place. For an agent that follows the literal text, the absence of "superseded" means "still in force." Status itself was unreliable: the spec defines 8 statuses, and the index actually used 9 spellings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptom 3: it reads too much and drowns in history.&lt;/strong&gt; The entry file grew to 457 lines, mixed with a lot of incident narrative. The top of the roadmap stacked 34 date banners (progress announcements that start with a date, one on top of another) and became a second changelog. To start a small task the agent first had to load hundreds of lines of unrelated story, and the constraint that actually mattered was diluted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptom 4: a test case cannot be traced to its requirement.&lt;/strong&gt; The case index called itself "the single source of truth." In practice it was 28 stretches of narrative written batch by batch, plus two derived views kept in sync by hand. The three files together were 909 lines. When the agent wanted to know "which requirement is this case for" or "does this requirement have a case," it could only read the narrative and guess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptom 5: the work is done and nothing is written back.&lt;/strong&gt; 18 completed bundles had no &lt;code&gt;implementation.md&lt;/code&gt;. How it was actually done, and where it departed from the plan, existed only in that session. The next agent could read the plan and could not tell that the plan had not been followed. The late user manual in corollary 2 is the same symptom landing on a deliverable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Symptom 6: each agent reads its own entry.&lt;/strong&gt; Before September 3 the entry file was named CLAUDE.md, after Claude Code's convention. Another tool will not necessarily load it. A separate batch of project-level lessons lived in one agent's personal memory, invisible to the others. Same repo, three agents, three different sets of "the project rules."&lt;/p&gt;

&lt;p&gt;Set the symptoms next to the agent properties and they stop being mysterious:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;The agent property behind it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Does not know which doc to read, so reads none&lt;/td&gt;
&lt;td&gt;No memory across sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Treats an outdated plan as fact&lt;/td&gt;
&lt;td&gt;Follows the literal text and does not infer intent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drowns in history&lt;/td&gt;
&lt;td&gt;Context is limited and it costs money&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A case cannot be traced to its requirement&lt;/td&gt;
&lt;td&gt;Can grep, but there is no relation field to grep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finishes the work and does not write back&lt;/td&gt;
&lt;td&gt;Is also the main author&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Each agent reads its own entry&lt;/td&gt;
&lt;td&gt;Several agents at once&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  4. Root cause: the entry file became an encyclopedia
&lt;/h2&gt;

&lt;p&gt;Follow the six symptoms to the bottom and two distinctions were never made.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first: resident versus on demand.&lt;/strong&gt; The entry file is loaded on every session. It is the most expensive place in the doc system. Every line is paid for, in tokens, on every session. Before the cleanup, the entry file held 9 "code review checks" and 7 "rule codified" marks, plus the incident stories behind them. A session that only changed button copy still had to load the code-review list and the database-migration pitfalls. The route that actually belonged there — "before a release seal, read this" — was absent (before September 18).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The second: current fact versus history.&lt;/strong&gt; Current fact answers "what is true now" and is to be trusted. History answers "why it was like that then" and is to be checked. In our project the two were mixed. The project overview stopped on September 3, and several versions shipped after that without a change to it. In the refactoring track's roadmap, 8 titles were marked ✅ while the body said "not started." A human who reads that contradiction asks. An agent picks one of the two sentences and believes it.&lt;/p&gt;

&lt;p&gt;Leave both distinctions unmade and the result is this: &lt;strong&gt;the entry file became an encyclopedia, not a routing table.&lt;/strong&gt; An encyclopedia answers "what is it." A routing table answers "where do I read." An encyclopedic entry gets longer the more dutiful it is, and the longer it is, the harder it is to find the one line that matters. The opening accident is what happens when the encyclopedia never recorded the fact and the routing table did not exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Three principles
&lt;/h2&gt;

&lt;p&gt;Work backward from the root cause and the model stands on three principles. Each one can be restated in a sentence. The parenthesis is the agent property it answers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 1: the entry only routes. It does not carry content.&lt;/strong&gt; (Answers "no memory across sessions.") The entry is the one thing the agent is certain to read. Aside from a few hard rules, its job is to tell the agent "for this kind of work, read that document," not to move that document's content in. Content that has been moved in goes stale, swells, and finally drowns the route itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 2: the resident layer holds only rules that are too late to discover after the damage, and each rule carries one sentence of reason. Everything else is drilled into on demand.&lt;/strong&gt; (Answers "context is limited and it costs money." The half about the reason answers "follows the literal text and does not infer intent.") To decide whether something should be resident, ask only this: if the agent does not read it this session, will it cause damage before anyone notices? If yes, it stays resident. If no, it goes to L1 or below, and the routing table points at it when it is needed. The reason cannot be skipped. A command with no reason is dropped as soon as the literal wording fails to match. With a reason, the agent can infer the right move in a boundary case the rule does not cover. The full incident goes down to L2 / L3. The entry keeps the link.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principle 3: current fact and history live apart, and status and replacement are written as fields.&lt;/strong&gt; (Answers "follows the literal text and does not infer intent.") A human can hear "this one feels old" in the tone. An agent cannot. Whether a document is in force, and what replaced it, has to be a parseable field, not a narrative that has to be inferred. Writing it as a field also lets the agent grep it and lets a script check it. Posts 3 and 4 expand on that.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The model: four layers, L0–L3
&lt;/h2&gt;

&lt;p&gt;The three principles, laid onto the doc structure, are four layers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;th&gt;When it is read&lt;/th&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;L0 resident&lt;/td&gt;
&lt;td&gt;Hard rules in the entry file + the routing table&lt;/td&gt;
&lt;td&gt;Loaded automatically every session&lt;/td&gt;
&lt;td&gt;Must stay short&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L1 by work type&lt;/td&gt;
&lt;td&gt;Specs (dev / test / DB / release seal / handbook / deploy)&lt;/td&gt;
&lt;td&gt;Before starting, read only the one the routing table names&lt;/td&gt;
&lt;td&gt;Shared layer + project layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L2 by module / change&lt;/td&gt;
&lt;td&gt;Plan bundles, long-running tracks, handoff docs&lt;/td&gt;
&lt;td&gt;When entering that module&lt;/td&gt;
&lt;td&gt;Has status and a replacement link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;L3 evidence&lt;/td&gt;
&lt;td&gt;Execution reports such as release-seal test reports, audit output&lt;/td&gt;
&lt;td&gt;Drill down when evidence is needed&lt;/td&gt;
&lt;td&gt;Does not serve as an entry&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;L0, resident,&lt;/strong&gt; holds two kinds of content: rules that are too late to discover after the damage (one sentence of rule, one sentence of reason, one link), and the routing table that tells the agent which L1 document each kind of work should open. It is loaded every time, so its only constraint is that it stay &lt;strong&gt;short&lt;/strong&gt;. When several agents share the repo, there can be only one L0: a single entry file, AGENTS.md. Entry files required by other tools only forward to it (answers "several agents at once").&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L1, by work type:&lt;/strong&gt; development, testing, database, release seal, user manual, deployment. One spec per kind of work. Before starting, read only the one the routing table names. L1 splits into a shared layer and a project layer. The project layer declares which shared spec it inherits, and writes only this project's fill-in, current state, exceptions, and pitfalls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L2, by module / change:&lt;/strong&gt; a change's plan bundle, a long-running track, a handoff document. Read it only when entering that module. L2 goes stale by nature, so it must &lt;strong&gt;have a status and a replacement link&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L3, evidence:&lt;/strong&gt; release-seal test reports, audit output, incident reports. Drill down only when evidence is needed. L3 &lt;strong&gt;does not serve as an entry&lt;/strong&gt;. A rule that lives only in the conclusion of some report takes effect only when someone happens to read it.&lt;/p&gt;

&lt;p&gt;The skeleton of an L0 entry file looks roughly like this (only the essential parts; the full template is left to the skill at the end of the series):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# AGENTS.md — the only entry (loaded every session; keep it short)&lt;/span&gt;

&lt;span class="gu"&gt;## Hard rules (always in force)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;one-sentence&lt;/span&gt; &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;. Reason: &lt;span class="nt"&gt;&amp;lt;one&lt;/span&gt; &lt;span class="na"&gt;sentence&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;. Detail: &lt;span class="nt"&gt;&amp;lt;L1&lt;/span&gt; &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Operation → required reading&lt;/span&gt;
| Operation | Required spec | One-sentence point |
|---|---|---|
| Release seal / produce a package / tag / say "full suite green" | &lt;span class="nt"&gt;&amp;lt;release-seal&lt;/span&gt; &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; | &lt;span class="nt"&gt;&amp;lt;one&lt;/span&gt; &lt;span class="na"&gt;sentence&lt;/span&gt; &lt;span class="na"&gt;of&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;full-suite&lt;/span&gt; &lt;span class="na"&gt;definition&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; |
| DB schema / cross-environment data change | &lt;span class="nt"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="na"&gt;database&lt;/span&gt; &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; | &lt;span class="nt"&gt;&amp;lt;the&lt;/span&gt; &lt;span class="na"&gt;single&lt;/span&gt; &lt;span class="na"&gt;most&lt;/span&gt; &lt;span class="na"&gt;important&lt;/span&gt; &lt;span class="na"&gt;constraint&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; |

&lt;span class="gu"&gt;## Layered reading&lt;/span&gt;
L0 = this file, always in force. L1 = the table above, addressed by work type; before starting, read only the matching row.
L2 = the module's plan / track / handoff; read it when entering that module. L3 = evidence; drill down when needed.
If you cannot tell which document to read, read the table first, then look for the next index at the top of the matching spec.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every L2 document carries a parseable metadata block at the top. That is where principle 3 lands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gt"&gt;&amp;gt; Status: approved&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; Stable ID: &amp;lt;T&amp;gt;-YYYYMMDD-NNN&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; Replaces: none&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; Replaced by: none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Status&lt;/code&gt; may take only a value from a fixed vocabulary. Free invention is not allowed. When a document is superseded, &lt;code&gt;Replaced by&lt;/code&gt; is filled with the new document's Stable ID, and the agent jumps when it reads it.&lt;/p&gt;

&lt;p&gt;The model leaves three questions, which are the next three posts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Where it lands&lt;/th&gt;
&lt;th&gt;Post&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Which documents should be read?&lt;/td&gt;
&lt;td&gt;The L0 → L1 route: the routing table, a single entry, two-layer inheritance&lt;/td&gt;
&lt;td&gt;2. Routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where do I look, and which copy do I trust?&lt;/td&gt;
&lt;td&gt;Directories, IDs, bundles, indexes, status and the replacement chain&lt;/td&gt;
&lt;td&gt;3. Addressing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where do I write, and how does it not rot?&lt;/td&gt;
&lt;td&gt;Write-back, machine enforcement, memory layers, freshness&lt;/td&gt;
&lt;td&gt;4. Lifecycle&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  7. The system was forced into existence by accidents
&lt;/h2&gt;

&lt;p&gt;I should be plain about this: the four-layer model was not designed up front. It is the shape that became visible, looking back, after accident upon accident across seven months.&lt;/p&gt;

&lt;p&gt;On March 4 the project was initialized. The entry file, CLAUDE.md, was 128 lines. The same day the rule that later mattered most was added, "plan-driven development": write the plan first, touch code only after it is confirmed, backfill the actual execution record when finished, and keep the roadmap current. From then on the docs grew with the development rhythm. Those early single-file plans number 301 today, and 218 of them were backfilled with an "actual execution record."&lt;/p&gt;

&lt;p&gt;On May 26 the entry file was 302 lines. On August 20 it was 406. A large part of the growth was incident narrative: hit a pitfall, add a section to the entry file, write down what happened and what to do next time. Each section made sense alone. Together, the entry file became an accident manual that kept getting thicker.&lt;/p&gt;

&lt;p&gt;September 3 was the first turning point. A documentation-architecture reshuffle touched 235 files. Test material scattered in four places was gathered into one. Plans were collected into three kinds: structured change bundles, long-running tracks, and early single-file plans. AGENTS.md became the only entry, and CLAUDE.md only forwarded to it. That eased "each agent reads its own entry" and "where does this go." It did not answer "which document should be read."&lt;/p&gt;

&lt;p&gt;On September 18, after the opening release-seal accident, the "operation → required reading" table appeared for the first time, and the entry file started to have the shape of a route. On September 29 a database migration collided on its number. Two migrations with the same number had both already run in production. After the move-and-rework, the rule was "before taking a number, check every parallel branch and worktree." On September 30 an old package with the same name was deployed by mistake. After that, package names were required to carry the hour, minute, and second. On October 13 and 14, browser-automation (MCP) actions were treated as acceptance twice. The rule "MCP locates; it does not accept" was written and added to the routing table.&lt;/p&gt;

&lt;p&gt;On October 14 the routing table grew into a layered-reading principle for L0 / L1 / L2 — the predecessor of the model in this post. The entry file was 457 lines. The same day the project opened a dedicated documentation-governance change (ID D-20261014-001), and this series was started at the same time. The result of that governance is in posts 3 and 4.&lt;/p&gt;

&lt;p&gt;Laid out in order, the pattern is obvious: &lt;strong&gt;almost every rule added later has an accident standing behind it.&lt;/strong&gt; The system was forced into existence by accidents. This series is written so the next project does not have to trade accidents for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Where each kind of document sits, who reads it, and what the human does
&lt;/h2&gt;

&lt;p&gt;The model has to treat every document type the same way. The table below places fourteen common kinds into the four layers. In an agent-first system the human is not the daily reader of any of them, but every kind has a decision point where a human has to show up.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Document&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Who reads it&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;Human's role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Requirements&lt;/td&gt;
&lt;td&gt;L2&lt;/td&gt;
&lt;td&gt;The agent implementing or reviewing the change&lt;/td&gt;
&lt;td&gt;The first document read when entering the change&lt;/td&gt;
&lt;td&gt;Confirm "what is wanted" before approval, and make the call on scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Design&lt;/td&gt;
&lt;td&gt;L2&lt;/td&gt;
&lt;td&gt;The agent implementing the change&lt;/td&gt;
&lt;td&gt;Before implementation starts&lt;/td&gt;
&lt;td&gt;Approve the plan, and make the call on design tradeoffs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementation record&lt;/td&gt;
&lt;td&gt;L2&lt;/td&gt;
&lt;td&gt;The agent taking over, reworking, or tracing&lt;/td&gt;
&lt;td&gt;On takeover; when asking "why was it done that way"&lt;/td&gt;
&lt;td&gt;Read the deviation summary and confirm the departure is acceptable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development spec&lt;/td&gt;
&lt;td&gt;L1&lt;/td&gt;
&lt;td&gt;Every agent that writes code&lt;/td&gt;
&lt;td&gt;Before starting, only the one the routing table names&lt;/td&gt;
&lt;td&gt;Make the call on a new rule when an incident is codified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refactoring track&lt;/td&gt;
&lt;td&gt;L2&lt;/td&gt;
&lt;td&gt;The agent changing a related module&lt;/td&gt;
&lt;td&gt;Read the historical lessons before entering the module&lt;/td&gt;
&lt;td&gt;Make the call on track priority, on shelving, and on closing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing spec&lt;/td&gt;
&lt;td&gt;L1&lt;/td&gt;
&lt;td&gt;The agent writing, changing, or reviewing tests&lt;/td&gt;
&lt;td&gt;Before touching tests&lt;/td&gt;
&lt;td&gt;Make the call on the testing definition, such as which suites "full" includes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Case index&lt;/td&gt;
&lt;td&gt;L2&lt;/td&gt;
&lt;td&gt;The agent changing a module or checking coverage&lt;/td&gt;
&lt;td&gt;Before changing a module, look up existing cases and their source requirements&lt;/td&gt;
&lt;td&gt;At acceptance, confirm whether a coverage gap is acceptable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release-seal spec&lt;/td&gt;
&lt;td&gt;L1&lt;/td&gt;
&lt;td&gt;The agent performing the release seal&lt;/td&gt;
&lt;td&gt;Before a release seal, a package, or saying "full suite green"&lt;/td&gt;
&lt;td&gt;Accept the release seal: walk the checklist and release on the evidence summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changelog&lt;/td&gt;
&lt;td&gt;Deliverable&lt;/td&gt;
&lt;td&gt;Business users&lt;/td&gt;
&lt;td&gt;After a new version goes live, to see what changed&lt;/td&gt;
&lt;td&gt;Confirm the deliverable: scope and wording match what shipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User manual&lt;/td&gt;
&lt;td&gt;Deliverable&lt;/td&gt;
&lt;td&gt;Business users&lt;/td&gt;
&lt;td&gt;While using the system or looking up a flow&lt;/td&gt;
&lt;td&gt;Confirm the deliverable: spot-check that flows match the UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB spec&lt;/td&gt;
&lt;td&gt;L1&lt;/td&gt;
&lt;td&gt;The agent changing table structure or cross-environment data&lt;/td&gt;
&lt;td&gt;Before writing a migration script&lt;/td&gt;
&lt;td&gt;Make the call on the compatibility rules and the exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migration index&lt;/td&gt;
&lt;td&gt;L2&lt;/td&gt;
&lt;td&gt;The agent adding a migration or preparing a deploy&lt;/td&gt;
&lt;td&gt;Before taking a number; before deploy, when listing the execution order&lt;/td&gt;
&lt;td&gt;Before deploy, confirm the execution list the agent produced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment runbook&lt;/td&gt;
&lt;td&gt;L1&lt;/td&gt;
&lt;td&gt;The agent performing the deploy&lt;/td&gt;
&lt;td&gt;Before deploying&lt;/td&gt;
&lt;td&gt;Approve production; operations reads a separate handover note&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incident report&lt;/td&gt;
&lt;td&gt;L3&lt;/td&gt;
&lt;td&gt;The agent investigating a similar problem or codifying a rule&lt;/td&gt;
&lt;td&gt;Drill down when evidence is needed&lt;/td&gt;
&lt;td&gt;Confirm the root cause, and decide whether to codify a rule and at which layer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three things are worth noticing. First, no cell in "when" says "anytime." Every document is bound to a trigger. That is what routing means. Second, the changelog and the user manual are not inside L0–L3. The agent maintains them and does not learn the job from them. When the agent traces "what changed in that version," it reads an L3 execution report such as the release-seal test report, not the changelog written for business users. Likewise, the deployment runbook is for the agent, and operations reads the handover note. One document serves one reader. Third, no cell in "human's role" says "maintain" or "read cover to cover." Every cell is approve, make the call, accept, or confirm.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. How this model breaks
&lt;/h2&gt;

&lt;p&gt;None of the following is hypothetical. Each one was the state of our project before the cleanup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 1: L0 swells
&lt;/h3&gt;

&lt;p&gt;The entry file's line count: 128 → 302 → 406 → 457. Look at the last stretch. After it became the only entry on September 3, and after the routing table was added on September 18, it &lt;strong&gt;kept growing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The reason is plain. After each accident, the cheapest move is to add a section to the entry file. The review checks and the "rule codified" marks counted in the root-cause section accumulated that way. Each section is right on its own. The cost of L0 is a multiplication: lines times sessions. Worse, a swollen L0 drowns its own routing table, and the opening accident plays out again inside the file that holds the table. One detail: a heading in the entry file said "four pitfalls" and then listed 5 items. An inconsistency like that can sit forever in a file that is loaded every session, which means the file had grown past the point where anyone checked it line by line.&lt;/p&gt;

&lt;p&gt;The direction of the fix: each L0 entry keeps one sentence of rule, one sentence of reason, and one link (principle 2), and the incident sinks. Links at the entry layer need a machine check. How to write the routing table so it does not itself grow into the entry file is the subject of post 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 2: the layer boundaries blur
&lt;/h3&gt;

&lt;p&gt;If nobody guards the four boundaries, the layers seep into each other.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;L1 mixed in templates and project adaptations.&lt;/strong&gt; The documentation-model spec itself was 1,123 lines. The shared spec, the project adaptation, and about 460 lines of templates sat in one L1 document, violating the "shared → project" layering that the same document had defined.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L2 long-running tracks swallowed short-term plans.&lt;/strong&gt; The refactoring track was supposed to be only a topic navigator. It held 145 single-file plans and a 2,613-line roadmap with status marks. The "title says ✅, body says not started" clash from the root-cause section lived here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L2 and L3 were not separated.&lt;/strong&gt; The date banners at the top of the roadmap recorded the history of one release after another. They belonged in implementation records and in L3 execution reports. They piled up at the top of a current-fact document. The same set of release-seal numbers was repeated in four places: the index banner, the index row, the release documents, and the roadmap. Miss an update in one place, and the agent is looking at four versions of "the fact."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sharpest of these is the first. The spec that defined the layers did not follow the layers itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 3: the agent that writes the docs is amnesiac too
&lt;/h3&gt;

&lt;p&gt;Much of the swelling and the boundary crossing above was written by an agent following the spec. At the moment of writing it obeyed the rule it had read. It does not remember a convention another session settled last week, and it does not go back to check whether the whole system is consistent. The agent is "amnesiac" as a reader, and it is amnesiac as an author. A layering that is kept up by good intentions will drift. That is the subject of post 4: write-back is part of the change, conventions are enforced by a machine and the human only adjudicates, the entry layer has zero tolerance, and deeper layers have a baseline that may only fall, never rise.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The agent is not amnesiac. It never had memory across sessions. Every session is a newcomer coming in through the entry. Fix the entry, not the agent.&lt;/li&gt;
&lt;li&gt;The first reader of the docs is the agent. The human shifts from reader to adjudicator. Deliverables are counted separately. One document serves one reader.&lt;/li&gt;
&lt;li&gt;The entry only routes and does not carry content. The resident layer holds only rules that are too late to discover after the damage. Current fact and history stay apart, and status and replacement are written as fields.&lt;/li&gt;
&lt;li&gt;The four-layer model: L0 resident (must stay short), L1 by work type (read only one), L2 by module / change (has status and a replacement), L3 evidence (never an entry).&lt;/li&gt;
&lt;li&gt;The system was forced into existence by accidents, and it breaks on its own: L0 gets fat, boundaries blur, and the agent that writes the docs is amnesiac too.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Series
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Agents Aren't Amnesiac — They Can't Find the Entry Point: Documentation Layers for Large Projects (1) (this post)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/make-the-agent-read-only-what-it-should-documentation-routing-2-450d"&gt;Make the Agent Read Only What It Should: Documentation Routing (2)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/give-every-document-exactly-one-place-documentation-addressing-3-1oih"&gt;Give Every Document Exactly One Place: Documentation Addressing (3)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/illegal/keep-documents-from-rotting-the-document-lifecycle-in-the-agent-era-4-14m8"&gt;Keep Documents from Rotting: The Document Lifecycle in the Agent Era (4)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>documentation</category>
      <category>productivity</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>See Quick Sort partitioning, one step at a time</title>
      <dc:creator>张旭</dc:creator>
      <pubDate>Wed, 15 Jul 2026 03:30:00 +0000</pubDate>
      <link>https://dev.to/illegal/see-quick-sort-partitioning-one-step-at-a-time-f9i</link>
      <guid>https://dev.to/illegal/see-quick-sort-partitioning-one-step-at-a-time-f9i</guid>
      <description>&lt;p&gt;Quick Sort is easy to summarize: choose a pivot, partition, and repeat. It is much harder to reason about while indices and subranges are moving. This visualizer turns those hidden state changes into a timeline you can inspect.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the visualization shows
&lt;/h2&gt;

&lt;p&gt;This implementation uses Lomuto partitioning with the rightmost item as the pivot. The red &lt;code&gt;i&lt;/code&gt; pointer marks the boundary of the smaller-than-pivot region, while the blue &lt;code&gt;j&lt;/code&gt; pointer scans the active range. When &lt;code&gt;a[j] &amp;lt; pivot&lt;/code&gt;, that value moves into the smaller region and &lt;code&gt;i&lt;/code&gt; advances. Once the scan reaches &lt;code&gt;hi&lt;/code&gt;, the pivot swaps into &lt;code&gt;i&lt;/code&gt; and reaches its final position.&lt;/p&gt;

&lt;p&gt;Pending &lt;code&gt;[lo, hi]&lt;/code&gt; ranges appear on an explicit stack, so the recursive work is visible instead of being hand-waved. Every frame keeps the array, active range, pivot, pointers, swap count, stack, caption, and current source line synchronized. You can compare the same execution against TypeScript, Python, Go, and Rust implementations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try these inputs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An already sorted array shows how a rightmost pivot can create severely unbalanced partitions.&lt;/li&gt;
&lt;li&gt;A reverse-sorted array exposes the same worst-case shape through a different sequence of swaps.&lt;/li&gt;
&lt;li&gt;Duplicate-heavy input reveals that the strict &lt;code&gt;&amp;lt; pivot&lt;/code&gt; comparison leaves equal values on the other side of the boundary.&lt;/li&gt;
&lt;li&gt;A mixed input makes the typical divide-and-conquer behavior easier to see.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to look for
&lt;/h2&gt;

&lt;p&gt;Quick Sort runs in &lt;code&gt;O(n log n)&lt;/code&gt; time on average, but this pivot strategy can degrade to &lt;code&gt;O(n^2)&lt;/code&gt;. Pause at each pivot placement, compare the sizes of the two resulting ranges, and watch the pending stack grow or shrink. Then change the input and replay the exact same checkpoints. The goal is not only to see a sorted result, but to understand how partition balance determines the work required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://algo.illegalscreed.cn/en/docs/quick-sort/?utm_source=dev&amp;amp;utm_medium=community&amp;amp;utm_campaign=c127-t3d3-smoke&amp;amp;utm_content=dev-en-link-1" rel="noopener noreferrer"&gt;Explore the interactive Quick Sort visualizer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>computerscience</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
