Docs are first-class, just like the code and the tests¶
The annotation system looked like a two-legged stool for too long.
One post explained source annotations: codemap: blocks, provenance tiers, a
closed vocabulary, and a hash that proves a judgment still belongs to the code
it describes. Another post explained the test side: closed marks, covers:
capabilities, replay evidence, and log-aligned UI state. Those two posts are
real foundations -- Code comments written for the robot, not the human
and The test that knows what it is -- but
they leave the third surface too implicit.
The third surface is documentation.
In this repo, docs are not supporting prose that trails behind the "real" system. They are annotated, vocabulary-linted, graph-indexed, and review-routed alongside source and tests. A canonical doc is a first-class node in the same machine. It does not use the same exact mechanism as a Python or JavaScript unit, because prose is not code, but it speaks the same capability language and joins the same graph.
That distinction matters. First-class does not mean identical. It means the surface has peer status in the system that agents use to decide what to read, what else might be affected, and how much review a change deserves.
One vocabulary across three surfaces¶
The shared contract is codemap/vocabulary.json.
Source files use the capability tokens in inline codemap: blocks. A block can
say a unit participates in docs, lint, view-filter, hidden-info, or any
other legal capability from that closed list. The token is not a loose label.
lint-annotations validates the annotation shape and its hash currency.
Tests reuse the same capability vocabulary through their covers: marks. A
test does not merely say "this is a server test" or "this is a UI test"; it can
say which capability it protects, using the same token a source unit uses to
describe what it does. That is the bridge the testing post calls out: source
and tests share one subsystem language instead of two drifting naming schemes.
Docs now use that same vocabulary in YAML frontmatter:
capability: is the shared namespace. It answers "what is this document about?"
and it draws only from codemap/vocabulary.json.
doc-kind: is deliberately separate. It is a docs-only namespace for how the
document should be used: adr, blog, devblog, mockup, reference,
runbook, spec, or tutorial. A runbook about WebSocket auth and a blog post
about WebSocket auth can share capability: ws or capability: auth while
remaining different kinds of documents.
The linter boundary matches the source and test boundaries. lint-doc-tags
checks doc capability: and doc-kind: values against closed vocabularies, the
same way lint-annotations checks source annotations and lint-test-marks
checks tests. An invented token that sounds perfect -- retrieval, ai-docs,
annotation-story -- is rejected because nothing else in the graph can join to
it. The cost of a closed vocabulary is occasional awkwardness. The payoff is
that the words are comparable.
The code-only parts stay code-only¶
The tempting mistake would be to copy the whole codemap: mechanism into docs.
That would make the system look symmetrical and less honest.
The source annotation block has three provenance tiers:
auto:for tool-owned facts the annotator can regenerate.ai:for agent-authored semantic judgment.human:for explicit intent or policy.
Those tiers describe who may refresh facts about a source unit. They are useful because code has structural facts a tool can derive and judgment fields an agent must review when the unit changes. Docs do not get those tiers in frontmatter. A documentation page can be reviewed, tagged, linked, and indexed without pretending every paragraph has the same provenance shape as a function.
The hash: currency stamp is even more clearly code-only. In source, the hash
covers the annotated unit after the codemap: block is stripped. If the code
changes and the judgment is not refreshed, the stale hash is machine-detectable.
That is a real guarantee.
A hash over prose would not give the same guarantee. It would prove that the
text changed, but it would not prove whether the capability: tag still tells
the truth, whether the page's advice is obsolete, or whether another doc now
needs to move with it. The doc graph spec states that boundary directly:
docs are indexed by canonical source path and content, not by inline codemap
hashes.
So docs get their own mechanism: closed frontmatter tags, canonical-path graph identity, link and reference edges, and derived review routing. They do not get a borrowed code hash that looks rigorous and answers the wrong question.
The path is the identity¶
Every source, test, and doc node in the graph is keyed by its git-tracked repo path.
That sounds mundane, and it is load-bearing. The path is the thing a ticket puts
under ## Touches. It is the thing a reviewer opens. It is the thing git diff
reports. It is also the key that lets docs sit beside code and tests in one
index without a translation layer.
Canonical docs come from docs/** and workflow/docs/**. Generated staging
trees such as site-src/ and built output such as site/ are not source nodes.
That exclusion prevents one document from having two identities: the real
tracked file and the rewritten build artifact. If a graph hit says
workflow/docs/DOC_CODE_GRAPH.md, an agent can open exactly that file, put that
path in a ticket, and review the diff against that path.
The same principle extends into retrieval. The doc graph spec treats docs as
metadata-bearing nodes: path, first heading, capability, and doc-kind. Code
nodes carry their codemap metadata. Tests carry marks that can be joined through
the same capability vocabulary. The graph can then answer a question grep cannot
answer reliably: "load the surfaces that share this capability before editing."
The deeper retrieval mechanism is the subject of Find every surface related to X. That post is held from public publish right now because it is a mechanism post, but it is the right follow-on inside the repo: it explains the doc/code graph, capability hubs, metadata-card embeddings, and graph expansion layer. This post is the conceptual point that mechanism serves: docs are one of the citizens, not an appendix.
First-class docs change review behavior¶
Once docs are real graph nodes, documentation edits stop being invisible to the same machinery that routes source edits.
Some docs are small local explanations. Some docs are policy, protocol,
workflow, or architecture surfaces that many other files consume. The project
does not ask authors to hand-stamp a criticality: field on each doc. It derives
importance from the graph: canonical paths, frontmatter capabilities, doc links,
inline path mentions, and incoming reach.
That is why the doc-side mechanism matters to review. A change to a
high-blast-radius doc should not travel through the system like a typo in an
unlinked note. The shipped rule surface already reflects this stance:
lint-ticket-doc-tier-floor derives a minimum review tier from doc blast radius.
The precise score is a derived signal, not a prose promise.
The behavioral rules say the same thing in human language:
- Canonical doc edits that introduce, rename, remove, or redefine a named concept must propagate through related docs in the same turn.
- Doc frontmatter tags are a closed vocabulary.
- Glossary entries move with new or changed project vocabulary.
workflow/docs/is the canonical orchestrator-doc source;site-src/is only a staging tree.
Those are not style preferences. They are the obligations that make docs a first-class surface. A source edit can require a test mark change. A test edit can require a vocabulary check. A doc edit can require related docs, glossary terms, nav, and review routing to move with it. The point is not that every surface has the same fields. The point is that every surface participates in the same typed system.
The shape of the machine¶
The pattern is now three-sided:
| Surface | Local metadata | Shared join |
|---|---|---|
| Source | codemap: blocks with provenance, risk, maturity, affinity, and hash: |
capability tokens from codemap/vocabulary.json |
| Tests | taxonomy marks such as touches:, covers:, kind:, and evidence: |
covers:<area>:<capability> uses the same capability tokens |
| Docs | YAML capability: and doc-kind: frontmatter |
capability: uses the same capability tokens |
That table is the idea I do not want buried inside a retrieval post. Source, tests, and docs all carry typed local metadata. They all reject ad hoc labels. They all give an agent something better than "read everything and guess." And they all feed a project memory layer where tickets, run records, code metadata, test marks, and docs can be queried as local artifacts.
Docs are not special because prose is sacred. They are special because prose is where the rules live. If a rule is only prose, an agent can miss it. If the prose is tagged, indexed, linked by canonical path, and routed by derived blast radius, the rule becomes part of the same control plane as the code that must obey it and the tests that must protect it.
That is the first-class claim: not "documentation matters" in the generic sense, but "documentation is a typed, linted, graph-addressable peer in the system that autonomous agents use to change the repo."
Or: subscribe to the newsletter for more posts on building this stuff.