[KongchangAI]
· 3 min read· 1,698 words

Giving Every Document a Unique Address: Document Addressing in the AI Agent Era

Giving Every Document a Unique Address: Document Addressing in the AI Agent Era

Giving Every Document a Unique Address: Document Addressing in the AI Agent Era

Document addressing system

When an Agent repeatedly misreads, miswrites, and misreferences the document tree of a large enterprise payment approval system, the problem often doesn't come from the model's capabilities but from the documents themselves lacking an "address." This article is the third installment in the "Agent-first Documentation Layers for Large Projects" series, focusing on a specific question: where should an Agent go to find documents, and once found, which one should it trust. The material comes from a real project—one that has already gone through a round of document cleanup following this methodology, with before-and-after data measured by the same statistics script across two directory trees.

After moving 235 files, the problem wasn't over

On September 3, the project reorganized its document architecture, touching 235 files. Previously, test materials were scattered across four locations, several different splitting schemes coexisted under the plans root directory, and Agents basically had to guess filenames to find documents. After the reorganization, test materials were consolidated in one place, and plans were classified into three types: structured change packages (requirements, design, implementation records, and test cases for a change placed in the same directory), long-term tracks, and early single-file plans.

Location was settled. The problem wasn't.

  • ID collisions: Two changes were both numbered F-20260909-005, two directories sharing one number, since at the time there were neither rules nor checks for taking numbers.
  • Migration collisions: On September 29, two database migration scripts with the same number had already been executed in production, and could only be moved away and redone, with the rule added afterward: "scan all parallel branches and worktrees before taking a number."
  • Indistinguishable package names: On September 30, an old package with the same name was mistakenly deployed, after which package names were forced to include hours, minutes, and seconds.
  • Multiple narratives of the same fact: The change directory README declared "we do not maintain a second status table," then maintained a status list itself, frozen at September 22.

Directories only answer "which area." For an Agent to find and trust a document, it also needs to know which one it is (ID), whether it's still valid (status and replacement relationships), and in which section the information is (template). Together, these four layers are what the author calls addressing.

Five symptoms and one root cause

Addressing failed in this project in five forms:

  1. Guessing. Migrations, change packages, and test reports were named by "date + English slug"; early single-file plans and incident reports used long Chinese titles. No single pattern could uniquely match a class of documents.
  2. Same number. Two directories, one ID—an Agent starting from the ID wouldn't know which directory to enter.
  3. One fact stored in multiple places. Status was written in the package header, the index, and the directory README; the test case index had two manually synced views beside it. When inconsistent, the Agent could only arbitrarily pick one to trust.
  4. Directory names read as status. Early plans were placed under legacy/, literally meaning "legacy," easily read as "completed and archived."
  5. Sections not findable within documents. Each version of same-type reports had different sections, so finding "rollback steps" meant reading from start to finish.

The root cause is just one: the four kinds of information—location, identity, status, and structure—were either merely conventions, written in multiple places, or expressed through something else. Directory names were used to express status; the previous document was used to express structure. Humans fill in the gaps; Agents that execute literally do not: a directory is whatever it's called, and this document looks like the previous one.

Three principles

Principle one: directories are divided by responsibility, not by type, not by status; don't move things after the work is done. Directories only answer "what is this document for." Type and status are both fields. Once directories are divided by status, a completed change must move, and all references pointing to it—other documents, commit messages, comments in migration scripts—break together. Only with stable paths can Agents find documents by pattern rather than by memory.

Principle two: one fact, one source; everything else is a derived view. Two manually maintained copies will inevitably drift. This session changed the package header; next session might forget to change the index. So designate a single source, and other locations either only hold links or are generated by scripts.

Principle three: status is written in documents, not expressed through directories. Status is a field with a fixed vocabulary; replacement relationships are fields pointing to new IDs. A directory name means only one responsibility.

Responsibility directories + placement rules

The steady-state directory structure is as follows (excerpt). "Steady-state" means the shape a new project should have from day one:

docs/
├── README.md                    Documentation homepage: divided by responsibility + placement rules
├── overview.md                  What the system is, where the boundaries are (current facts)
├── roadmap.md                   Global roadmap
├── handbook/                    Long-lived specifications
│   └── templates/               One template per document type
├── testing/
│   ├── guides/                  Testing specifications
│   └── cases/                   Global test case index and its derived views
├── deployment/                  Currently executable deployment manuals
├── plans/
│   ├── index.md                 Change index
│   ├── YYYYMMDD-<t>NNN-<slug>/  One change, one package, flat; stays in place after completion
│   └── tracks/<topic>/          Navigation for long-term tracks
├── reports/                     Execution evidence: tests, audits, incidents, migrations
└── releases/                    Deliverables: changelogs, handoff notes

reports/ is evidence for Agents to drill into, releases/ is deliverables for business and operations—that's the reason for the split. Under plans/ there are no done/ or bugfix/ subdirectories; type and status are both fields. Each top-level directory's README has two sections: "navigation" for reading Agents, and "placement rules" for writing Agents.

Stable ID scheme

A stable ID is a document's fixed number, taken at creation time, never changing with title, directory, or type afterwards. The most copy-worthy part of the scheme is this table:

ObjectID / FilenameMechanical relationship
Change<T>-YYYYMMDD-NNN ↔ directory YYYYMMDD-<t>NNN-<slug>Mutually derivable; the initial letter is fixed by the type at creation time
Database migrationNNNN-YYYYMMDD-<slug>.sqlA global sequence number determines execution order
Test caseTC-<module>-NNNThe module can be read from the ID
Reports and other recordsYYYYMMDD-<slug>.mdOrdered by date; release-sealed test report slugs include the version number, greppable by version
Release documentsvX.Y.Z.md / vX.Y.Z-delivery.mdChangelogs and handoff notes appear in pairs by version
Release packagevX.Y.Z-YYYYMMDD-HHMMSS-dist.zipMultiple packages on the same day can be distinguished

Number-taking rules are the most underrated piece. Multiple Agents working in parallel on different branches and worktrees each look at their own directory for the "next number"—collisions are almost inevitable. The rule is: before taking a number, scan every local branch, remote branch, and worktree, and take the globally first empty number; when a conflict occurs, the later applicant moves—directory, ID, and all references change together, content stays put, and the original number is written down in the removal record. Migration script collisions are costlier because the sequence number is the execution order. Adding hours, minutes, and seconds to release packages is the same reasoning.

worktree is Git's multi-working-directory mechanism: the same repository can be checked out to multiple physical directories simultaneously, each on a different branch or commit, sharing the same object database. It solves the problem of a developer (or AI Agent) wanting to work in parallel on another branch without switching the current branch or interrupting compilation or test processes. git worktree add creates a new directory and binds it to a branch. This differs essentially from git clone—clone copies the whole repository, producing unrelated histories; in a worktree, all working directories share the same .git and reference space, with branches, tags, and commits immediately visible across them. For the number-taking scenario in this article, the key point is: each worktree has its own filesystem view, so if an Agent only scans files in its own directory, it can't see numbers already taken in other worktrees—so all worktree paths must be included in the scan before taking a number.

Branch is a movable pointer in Git pointing to a commit, letting development lines fork, run in parallel, and merge again. It's itself merely a lightweight reference, nearly zero-cost to create, so it's often used to open an independent development line for each feature or fix. When multiple branches coexist, they may each modify the same set of files. When two branches both add a change package numbered F-20260909-005, a merge conflict results—not a file content conflict, but two different files competing for the same ID. Beyond local branches there are remote branches (origin/xxx), representing number states already advanced on other machines or by other people. So the requirement in this article to "scan every local branch, remote branch, and worktree" essentially means grounding the number-taking action in the repository's global number space rather than some local view.

Change packages and lightweight forms

The four-piece change package each answers one question: requirements answer "what's wanted," design answers "how we plan to do it," implementation records answer "how it was actually done and where it deviated," and test cases answer "how to prove it." Only four are defined, and reality will grow others on its own—before cleanup, packages contained 7 hand-written test cases, 5 impact analyses, 4 handoff documents, and over a dozen phase booklets, none of which were defined in the specification. After cleanup, this became an optional file whitelist: impact map, handoff document, manual test cases, independent review records, phase booklets, each with conditions for need and timing for reading. New files beyond the whitelist are all considered errors, with scripts in scripts/.

The other end is small changes. Before cleanup, 20 packages had only requirements files, and 18 completed packages had no implementation records—the write-back loop broke here. After cleanup, a lightweight form was defined: small fixes, documentation changes, or release-type changes need only requirements, and when status advances to "implemented," the document must contain an "Im

#AI Agent 文档管理#文档寻址#稳定 ID 方案#Agent-first 文档架构#文档模板漂移#大型项目文档规范#文档生命周期
Share:

Related articles