NAI OS

/nai-document — staged documentation, checked before it publishes

One run, one target, one deliverable — staged to a run folder, promoted only when every citation and every publish check holds.

You type /nai-document <target>. The command “document[s] this workspace from its own files” — resolving that one slug to a lane by five tests applied in order, where “First match wins”; assembling the source list that lane calls for; then running three waves. doc-scoper inventories the sources and writes the scoping brief “the rest of the run is grounded in”. One generator writes a staged page — doc-surveyor in the survey lane, doc-renderer in the blog lane, and in the editorial lane “no generator runs”. Then doc-checker re-opens every cited file and verdicts each claim. Generators “write to 12 Documentation/runs/<target>/proposed/”, never to docs/; promotion — “copying 12 Documentation/runs/<target>/proposed/<file> to the page path” — is “the only publishing act in this system”.

%%{init: {'theme':'neutral', 'flowchart':{'htmlLabels':false,'nodeSpacing':45,'rankSpacing':55}}}%%
flowchart TD
    H["Human types<br/>/nai-document &lt;target&gt;"] --> C

    subgraph L1 ["1 - Orchestrator (.claude/commands)"]
        C["/nai-document<br/>5 ordered tests, first match wins<br/>then assembles the source list"]
    end

    C -->|"no match"| E1["STOP<br/>missing: no target named &lt;target&gt;"]
    C -->|"more than one test matches"| E3["STOP<br/>ask the human which they meant"]

    C -->|"Wave 1"| S["doc-scoper<br/>inventory + create/update mode"]
    S -.->|"working/scope.md"| C

    subgraph L2 ["2 - Wave 2: one generator, chosen by lane"]
        G1["doc-surveyor<br/>survey lane"]
        G2["doc-renderer<br/>blog lane"]
        G3["editorial lane<br/>no generator runs"]
    end

    C -->|"survey"| G1
    C -->|"blog"| G2
    C -->|"editorial"| G3

    G1 -.->|"proposed/ page + claims.json"| K
    G2 -.->|"proposed/ page + claims.json"| K
    G3 -.->|"scoper's claims.json"| K

    subgraph L3 ["3 - Wave 3: every lane, every run"]
        K["doc-checker<br/>re-opens each cited file<br/>CONFIRMED / UNSUPPORTED, terminal"]
    end

    K -.->|"working/verdicts.json"| GD
    GD["Publish guard<br/>orchestrator itself, no subagent<br/>serve path / reachable / links resolve"]
    GD -->|"create + all CONFIRMED + all &check;"| P["Promote: copy proposed/ file<br/>to the page path"]
    GD -->|"otherwise"| ST["Staged, not published<br/>STOP and tell the human which condition failed"]
    GD ==> R["12 Documentation/runs/&lt;target&gt;/report.md"]

    classDef orch fill:#dcefe9,stroke:#0f6f63,stroke-width:2px
    classDef tool fill:#f4f1ea,stroke:#8a7a5a
    classDef human fill:#fdf3e0,stroke:#8a5a00,stroke-width:2px
    classDef agent fill:#fff,stroke:#555
    class C,GD orch
    class S,G1,G2,G3,K agent
    class H,E1,E3,P,ST,R human
Three waves, and two STOPs before any of them (no match; ambiguous target). The generator that runs is decided by the lane, and only one ever runs. The publish guard is orchestrator work, not a subagent's — and promotion is conjunctive: mode create, every verdict CONFIRMED, every guard line ✓.

Resolving a target to a lane

The command applies its five tests in order, and “First match wins”. Test 1: “11 Blog/<target>.md exists → lane blog, page docs/blog/<target>.html”. Test 2: “<target> is a key in the system map below → lane survey, page as mapped”. Test 3: “docs/architecture/concept-<target>.html exists → lane survey, page is that file”. Test 4: “<target> is in the editorial list below → lane editorial, page none”. Test 5: “No match → STOP. Report missing: no target named <target>. Write nothing.

“If more than one test matches, STOP and ask the human which they meant. Do not take the first.” cohort-census is a system-map key like the others: “| `cohort-census` | `docs/architecture/system-cohort-census.html` |”. That page was created by hand on 2026-08-06; until then nai-cohort-census was the one command with no page and this orchestrator told you to STOP on it. It no longer does. The reasoning behind that old STOP still stands for any future command with no page: add the page first, then the map row.

The system map is a table, not a rule: “Page names do not derive from command names. This table is the truth; never construct a filename from a slug.” This page is one of its rows — | `document` | `docs/architecture/system-document.html` |. The map also carries a deliberate mismatch: “system-monthly-tax.html is stale naming left from the 2026-07-31 rename of the command to nai-tax-theo. The map records reality. Do not rename the file — renaming a live URL is a deletion by another name.”

The editorial lane has no pattern fallback either: “Exactly these targets, and no pattern fallback: index, features, user-guide, claude-md, verification-patterns, agent-conventions, and readme-<NN> for each numbered section folder (e.g. readme-06).”

The source list, per lane

LaneSources, verbatim from the command file
editorial“the file checked, plus every file under .claude/commands/ and .claude/agents/. Those are what settle a factual claim.”
survey“the command file for the system, plus every agent file it dispatches. Read the command file first to find out which those are — do not guess from names.”
blog11 Blog/<target>.md alone.”

The four subagents

Each row's tools: column is copied exactly from that agent file's frontmatter. No agent in this system has a web tool: “It uses no web tools, and neither does any agent it dispatches.”

SubagentWave / lanetools: grantWhat it does
doc-scoperWave 1 — every laneRead, Write, Glob, Grep “Inventories the source files behind one documentation target, detects whether its page is being created or updated, and in the editorial lane extracts the page's checkable factual claims.” It “never write[s] a documentation page.”
doc-surveyorWave 2 — survey laneRead, Write, Glob “Regenerates one architecture page as an as-built survey of the agent and command files it documents, anchoring every factual assertion to a verbatim quote. Writes a staged page only; never publishes, and never writes prose it cannot cite.”
doc-rendererWave 2 — blog laneRead, Write “Renders one markdown blog draft into a styled, self-contained HTML page in the site's design language. A view only — it computes nothing, adds nothing, and every sentence on the page appears verbatim in the markdown.”
doc-checkerWave 3 — every laneRead, Write, Grep “Adversarially checks every claim in a documentation run — re-opens the cited file, hunts for the quoted evidence, and verdicts CONFIRMED or UNSUPPORTED with a reason. Never edits, adds, or drops a claim; never rewrites a page.”

The scoper decides the run's mode: “Determine mode: update if page names a file that exists, create if it does not, and update is never assumed for page: none — the editorial lane is always mode update, because its page always already exists.” And the checker runs regardless of what came before it: “The checker runs in every lane, every run, including when there are zero claims. Its absence is never silent.”

The guardrails, each from the file that enforces it

Enforced inThe rule, verbatim
.claude/commands/nai-document.md “It never creates or edits a file under .claude/agents/ or .claude/commands/. Documentation follows the system. If the docs and the agents disagree, either could be wrong, and only the human decides which.”
.claude/commands/nai-document.md “It never runs an agent system to find out what it does. Everything comes from reading files.”
.claude/commands/nai-document.md “It never deletes or renames a page. A stale page is reported; a dead URL is forever.”
.claude/commands/nai-document.md “It never authors prose. It renders drafts the human wrote and surveys files the human wrote.”
.claude/commands/nai-document.md “Generators write to 12 Documentation/runs/<target>/proposed/. Never dispatch a generator with a path under docs/.
.claude/commands/nai-document.md “If it returns missing:, STOP and report that line verbatim. Do not continue on a partial source list.”
.claude/agents/doc-scoper.md “Never substitute a similar file for a missing one, and never proceed on a partial source list.”
.claude/agents/doc-scoper.md “Editorial judgment is not checkable and must not become a claim” — “When unsure, leave it out — a missed claim costs nothing, a bogus UNSUPPORTED verdict costs trust.”
.claude/agents/doc-scoper.md source is always a file, never a directory — the checker opens it and searches it, and a directory has no text to search.”
.claude/agents/doc-surveyor.md “Never survey a system from a partial source list. A page that documents three of four subagents is worse than no page, because nothing on it says which one is absent.”
.claude/agents/doc-surveyor.md “Anything you cannot anchor to a quote becomes an open question, never a sentence on the page. A page that says less and cites all of it beats a page that reads well and cannot be checked.”
.claude/agents/doc-renderer.md You never author, edit, shorten, expand, correct, or reorder the author's prose. Not for grammar, not for clarity, not for length. Every sentence on the page appears verbatim in the markdown.”
.claude/agents/doc-renderer.md “Nothing else is added. No summary, no table of contents, no “further reading”, no author bio, no date you were not given.”
.claude/agents/doc-checker.md “A quote that appears only in a different file than the one cited is UNSUPPORTED. The citation is part of the claim.”
.claude/agents/doc-checker.md “A quote matching except for wording, tense, or number is UNSUPPORTED. Verbatim means verbatim. Differences in surrounding whitespace or line wrapping are not differences in wording and may be ignored.”
.claude/agents/doc-checker.md “You never widen a search to make a claim pass.” — “You never mark a claim CONFIRMED because it is obviously true. You are checking the citation, not the fact.”

Why there is no redraft loop

“Your verdict is terminal: a claim you mark UNSUPPORTED is reported to the human, never sent back to be redrafted. Iterating would let the generator reword a claim until it slipped past you, which would destroy the citation discipline you exist to enforce.”

Promotion, and the guard before it

The publish guard is the orchestrator's own work: “You do this yourself. No subagent. Record each line as ✓ or ✗ in the report.” Three lines. “Serve path — is the page's path re-included in .vercelignore? Read the file and check.” “Reachable — for a page being created, is it linked from docs/index.html, docs/architecture.html, or another page already reachable? If not, ✗ with the reason.” “Links resolve — for every relative href and src in the staged page, does the target file exist? List each broken one.” In the editorial lane, “there is no page, so record all three lines as n/a.”

Promotion is conjunctive — “Promote automatically only if all three hold: mode is create (the page does not already exist), and every verdict is CONFIRMED, and every publish-guard line is ✓.” Otherwise: “Otherwise do not promote. Report what is staged and STOP, telling the human exactly which of the three conditions failed and how to promote it afterwards.” The editorial lane is outside this entirely: “The editorial lane never promotes anything — it has no page. Its report is the deliverable.”

What the output looks like

ArtefactWritten byLanes
12 Documentation/runs/<target>/working/scope.mddoc-scoper “Always write” — every lane
12 Documentation/runs/<target>/working/claims.json doc-scoper (editorial), doc-surveyor (survey), doc-renderer (blog) Editorial lane only, additionally write” for the scoper; the two generators each write it in their own lane
12 Documentation/runs/<target>/proposed/<file> doc-surveyor or doc-renderersurvey, blog
12 Documentation/runs/<target>/working/verdicts.jsondoc-checker every lane
12 Documentation/runs/<target>/report.mdthe orchestrator every lane

The staged page differs by lane. The surveyor's must cover “what it does, in one paragraph”, “the flow, as a <pre class="mermaid"> block whose source is editable text, never a pre-rendered SVG”, “a table of every subagent with its exact tools: grant, copied from the agent file”, “the guardrails, each one quoted from the file that enforces it”, and “what the output looks like” — matching a sibling's house style rather than inventing one: “Match the house style; do not invent a new one.” The renderer's is “a complete, self-contained HTML document, styles inline in a <style> block, no external stylesheet, no external script”, taking its design language from docs/index.html.

verdicts.json is strict about shape: “Every id in claims.json appears exactly once, in the same order. verdict is exactly CONFIRMED or UNSUPPORTED; there is no third value.” An empty run is still a run — “An empty claims array is not missing. Process it normally and produce an empty verdicts array.”

report.md carries the lane, the mode, the outcome (“<promoted|staged, not published>”), a verdicts table, a publish-guard table, the open questions (“Anything the generator could not anchor to a quote, verbatim as it reported it”), and what to do next. The last instruction in the command is about honesty in the summary: “Never claim a page is published when it was only staged.”

NextThe subagent roster