{"id":51890963,"url":"https://github.com/jakubsuplicki/codument","last_synced_at":"2026-07-26T04:00:17.368Z","repository":{"id":347911102,"uuid":"1195274238","full_name":"jakubsuplicki/codument","owner":"jakubsuplicki","description":"Docs-based guardrails for AI coding workflows: coverage scoring, doc-drift checks, and diff safety review.","archived":false,"fork":false,"pushed_at":"2026-07-05T13:42:28.000Z","size":4289,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-05T14:18:07.059Z","etag":null,"topics":["ai","ai-workflow","claude","cli","developer-tools","documentation"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/codument","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jakubsuplicki.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-29T13:17:54.000Z","updated_at":"2026-07-05T13:42:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jakubsuplicki/codument","commit_stats":null,"previous_names":["jakubsuplicki/codument"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/jakubsuplicki/codument","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubsuplicki%2Fcodument","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubsuplicki%2Fcodument/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubsuplicki%2Fcodument/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubsuplicki%2Fcodument/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jakubsuplicki","download_url":"https://codeload.github.com/jakubsuplicki/codument/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakubsuplicki%2Fcodument/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35899883,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-07-20T02:08:10.276Z","status":"online","status_checked_at":"2026-07-26T02:00:06.503Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-workflow","claude","cli","developer-tools","documentation"],"created_at":"2026-07-26T04:00:16.509Z","updated_at":"2026-07-26T04:00:17.339Z","avatar_url":"https://github.com/jakubsuplicki.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"docs/assets/readme-hero.png\" alt=\"codument — change control for AI-made changes\" width=\"880\"\u003e\n\n\u003cbr\u003e\u003cbr\u003e\n\nA deterministic, git-native safety layer for what your coding agent touches.\nTwo independent adversarial gates, and the docs-backed workflow that produces them.\n\n\u003c!-- status --\u003e\n[![npm](https://img.shields.io/npm/v/codument?style=flat\u0026logo=npm\u0026label=npm\u0026color=CB3837)](https://www.npmjs.com/package/codument)\n[![License: Apache 2.0](https://img.shields.io/badge/license-Apache_2.0-blue?style=flat)](LICENSE)\n[![Node](https://img.shields.io/badge/node-%E2%89%A518-339933?style=flat\u0026logo=nodedotjs\u0026logoColor=white)](package.json)\n[![Tests](https://img.shields.io/badge/tests-1100%2B_passing-brightgreen?style=flat)](tests)\n\n\u003c!-- what it is --\u003e\n[![deterministic core](https://img.shields.io/badge/core-deterministic-0d9488?style=flat)](#3--check--terminal-deterministic-core--independent-adversarial-gates)\n[![no network](https://img.shields.io/badge/-no_network-0d9488?style=flat)](#3--check--terminal-deterministic-core--independent-adversarial-gates)\n[![no AI on the verdict path](https://img.shields.io/badge/-no_AI_on_the_verdict_path-0d9488?style=flat)](#3--check--terminal-deterministic-core--independent-adversarial-gates)\n[![git-native](https://img.shields.io/badge/git-native-181717?style=flat\u0026logo=git\u0026logoColor=white)](#what-it-is)\n\n\u003c!-- works with --\u003e\n[![Claude Code · native](https://img.shields.io/badge/Claude_Code-native-D97757?style=flat\u0026logo=anthropic\u0026logoColor=white)](#1--set-up--terminal-once)\n[![Codex · portable](https://img.shields.io/badge/Codex-portable-412991?style=flat\u0026logo=openai\u0026logoColor=white)](#1--set-up--terminal-once)\n[![any AGENTS.md agent](https://img.shields.io/badge/AGENTS.md-any_agent-64748b?style=flat)](#1--set-up--terminal-once)\n\n\u003cbr\u003e\n\n\u003cimg src=\"docs/assets/codument-watch-hero.png\" alt=\"codument watch: a live, deterministic panel attributing agent spend to each feature; verdict CLEAN, cost $4,007.22 across 31 sessions, with a per-feature 'where it went' breakdown\" width=\"820\"\u003e\n\n\u003csub\u003e\u003ccode\u003ecodument watch\u003c/code\u003e · estimated from captured token usage · facts, not a bill\u003c/sub\u003e\n\n\u003cbr\u003e\n\n**[Website](https://codument.studio/)** · **[Quick start](#try-it-on-your-own-repo--two-commands-zero-commitment)** · **[30-second demo](#try-it-in-30-seconds)** · **[Autopilot](#autopilot)** · **[How it works](#what-it-is)** · **[Docs](docs/)** · **[Report an issue](https://github.com/jakubsuplicki/codument/issues)**\n\n\u003c/div\u003e\n\n## What it is\n\nCodument has two sides that work together, plus an independent adversarial layer for when you want more than facts:\n\n- **A delivery workflow your agent runs.** Docs-backed planning, source-to-doc ownership, review discipline, and commit hygiene. You just chat; your agent routes intent into the right phase. Core loop: `grill → plan → approve → implement → verify → document → review → commit`.\n- **Deterministic CLI checks you run.** Local, no-network, no-AI commands that read the repo and report the facts: `doctor` (coverage + lint), `review` (what a change touched, what went stale, per-symbol drift), `watch` (a live view). Same repo state, same output. No model, no network, reproducible.\n- **Two independent adversarial gates that verify, don't trust.** A plan adversary contests the written plan before code exists and never blocks; a review adversary contests a non-trivial diff and blocks only when a finding's named test is genuinely red on a live re-run. The AI proposes; a deterministic oracle decides, so adding AI here never undercuts \"deterministic by default\".\n\nThe connective tissue is **`docs/.registry.json`**, a registry mapping each source file to the feature/doc that owns it: the workflow writes it as it builds, the checks read it to reason about every change, and the gates project it into the contract an adversary attacks.\n\n\u003cdetails\u003e\n\u003csummary\u003eArchitecture at a glance\u003c/summary\u003e\n\n```mermaid\nflowchart TB\n  subgraph WF[\"Delivery workflow · your agent\"]\n    L[\"grill → plan → implement → review → commit\"]\n  end\n  REG[(\"docs/.registry.json\u003cbr/\u003ewhich doc owns each source file\")]\n  subgraph CLI[\"Deterministic checks · no AI, no network\"]\n    C[\"doctor · review · watch\"]\n  end\n  subgraph ADV[\"Independent adversarial gates · proportional · verify, don't trust\"]\n    PA[\"plan adversary\u003cbr/\u003emap check --plan\u003cbr/\u003enever blocks\"]\n    RA[\"review adversary\u003cbr/\u003ereview --require-review\u003cbr/\u003eblocks only on a red re-run test\"]\n  end\n  WF --\u003e|writes \u0026amp; updates docs as it builds| REG\n  REG --\u003e|read to reason about every change| CLI\n  REG -.-\u003e|projected into the contract an adversary attacks| ADV\n```\n\n\u003c/details\u003e\n\n## How you run it\n\ncodument is two tools used in two places, and keeping them straight is the whole trick:\n\n| Where | What it's for | Examples |\n| --- | --- | --- |\n| 🖥️ **Your terminal** (you type) | setup, the deterministic checks, upgrades | `codument init`/`scan`/`adopt` · `codument doctor`/`review`/`watch` · `codument update` |\n| 💬 **Your agent** (you just chat) | the delivery workflow and the fixes | `grill → … → commit` · `/update-docs` · `/review-work` |\n\n**Rule of thumb: the CLI finds and reports; your agent fixes.** Codument never writes your code or docs.\n\n## Works with your stack\n\n| Language | Files | Resolution | Since |\n| --- | --- | --- | --- |\n| TypeScript | `.ts` `.tsx` `.mts` `.cts` | per-symbol | 0.7.0 |\n| Python | `.py` `.pyi` | per-symbol | 0.9.0 |\n| Go | `.go` | per-symbol | 0.9.0 |\n| Rust | `.rs` | per-symbol | 0.9.0 |\n| C# | `.cs` | per-symbol | 0.9.0 |\n| Java / Kotlin | `.java` `.kt` `.kts` | per-symbol | 0.9.0 |\n| Vue / Svelte / Astro | `.vue` `.svelte` `.astro` | blocks | 0.9.0 |\n\nPer-symbol resolution for TypeScript, Python, Go, Rust, C#, Java, and Kotlin; per-part for Vue/Svelte/Astro; whole-file for JavaScript. Every other registered file is surfaced on change, never judged. The table is parity-tested against the adapter registry (`tests/language-matrix.test.ts`), so a shipped-but-unlisted or listed-but-unshipped language is a red test, not a stale claim.\n\n\u003cdetails\u003e\n\u003csummary\u003ePer-language anchor semantics (honest bounds)\u003c/summary\u003e\n\nTypeScript (`.ts`/`.tsx`, module flavors `.mts`/`.cts` included) resolves **per symbol** — including config files shaped like `export default defineNuxtConfig({...})`, which carry a precise `default.` anchor (comment and formatting edits fire nothing; a payload edit is one ackable finding; swapping the producing callee is a contract change). **Python** (`.py`/`.pyi`) resolves **per symbol** through a bundled tree-sitter grammar (no interpreter, no ambient toolchain): a static `__all__` is honored as the public surface (its edit is a contract move), otherwise the underscore convention decides; a def's decorators, parameters, defaults, and return annotation are contract while the suite (docstrings included) is ackable body; classes split per member; a module assignment's value is ackable body while its target and annotation are contract — so a `settings.py` value flip is one named ackable finding, and pytest conventions (`test_*.py`, `*_test.py`, `conftest.py`) plus environment trees (`venv`, `__pycache__`) stay out of scope. **Go** (`.go`) resolves **per symbol** through a bundled tree-sitter grammar: exported means capitalized (Go's own law), methods anchor under their receiver type with pointer and value receivers sharing one identity, grouped `const`/`var` blocks anchor per spec (names co-declared in one spec share a span, and an iota-style block anchors whole — inserting a member shifts later values, so it reads as a contract move, never silence), a struct's exported fields and tags are contract while unexported fields are ackable body, and `_test.go` files stay out of scope. **Rust** (`.rs`) resolves **per symbol**: any `pub` form anchors (including `pub(crate)` — load-bearing inside the repo), impl members anchor under their type and trait-impl members under a trait-qualified identity, derives and attributes are contract, pub struct fields are contract while private fields are ackable body, and a macro definition is one all-signature anchor with invocations honestly bounded to the residual (no expansion without rustc). **C#** (`.cs`) resolves **per symbol** (its members are its symbols): types anchor as contract frames while methods, properties, and fields anchor individually under nested type chains, partial-class fragments in one file fold into one identity, a property's accessor list (`get; set;` vs `get; init;`) is contract while accessor bodies and initializers are ackable, record positional parameters are contract, and top-level-statement files (minimal hosting) still gate at residual grain. **Java and Kotlin** (`.java`/`.kt`/`.kts`) resolve **per symbol** through one anchor model over two bundled grammars, so a mixed JVM repo gates coherently: types are contract frames and methods, fields, and properties anchor individually under nested chains; annotations are contract (framework wiring like `@Service`/`@GetMapping` IS the interface); a data class's primary-constructor parameters are contract (the equality surface); enums anchor whole and overloads fold per name. Visibility follows each language's own rule — Java anchors `public`/`protected` while a bare package-private default joins the closure pool, whereas Kotlin's default is public so every non-`private` declaration anchors and `internal` counts as public within the repo. Canonical `src/test` source sets and `*Test`/`*Spec` files stay out of scope; a pathologically compact single-line Kotlin body classifies unevaluable (fail-loud) rather than mis-anchoring, while realistic multi-line code gates per symbol. **Vue, Svelte, and Astro components** (`.vue`/`.svelte`/`.astro`) resolve **per part**: script blocks get full per-symbol treatment through the TypeScript engine (a `\u003cscript setup\u003e` block's top-level declarations are the component's public surface), while template and style are named, body-grain anchors — a markup tweak is one ackable finding, a markup comment or reformat is silence, and a script contract change refuses the ack path. JavaScript (`.js`/`.jsx`/`.mjs`/`.cjs`) is gated at **whole-file grain**: any content change wakes the owning doc, cleared by a doc update or a file-grain ack. Declaration artifacts (`.d.ts`/`.d.mts`/`.d.cts`) are excluded outright — generated API surface, not judged; if you register one anyway, it is named in the ungated section rather than dropped. Everything else — `.css`, `.json`, another language entirely — is **never judged**: register such a file and `review` names it with its owning doc(s) in an info-only \"Registered but ungated\" section instead of staying silent, but no staleness verdict is computed for it. New languages arrive as adapters on the gate's language-adapter seam, each required to pass the same eight-behavior conformance battery before it ships. The agent workflow around the gate — plans, registry, the docs standard, the adversarial gates — is language-agnostic.\n\n\u003c/details\u003e\n\n\n**Repo layouts.** A single repository, or a monorepo whose packages are each their own git repository — submodule super-repos included. Point codument at the directory containing them and it aggregates each member's own git view; what a single ref cannot honestly name across several repositories it refuses rather than guesses. Details in [Monorepos of nested repositories](#reference).\n## Try it in 30 seconds\n\nOne command runs a click-through showcase on a throwaway sample repo: your docs today, an AI makes a sweeping change, then exactly what that change broke that you'd otherwise merge blind, opened as an HTML report. Press Enter to advance each scene (or add `--auto`).\n\n```bash\nnpx codument demo\n```\n\nOr watch it live in a single terminal, the change-state panel starting clean, then lighting up in place as the AI change lands:\n\n```bash\nnpx codument demo --live\n```\n\n(From a checkout of this repo: `npm run demo` or `npm run demo:live`.)\n\n## Try it on your own repo — two commands, zero commitment\n\nBefore adopting anything, quantify the doc drift your committed history already carries:\n\n```bash\nnpx codument scan                    # propose a registry + doc scaffolds (nothing committed)\nnpx codument audit v1.0.0..HEAD      # replay your history against that map\n```\n\n`scan` proposes which docs would own which sources; `audit` then reports every feature whose source moved in the range while its doc got no attention, per symbol, with an honest \"doc never committed\" where none existed yet. That is the drift the live gate would have caught. Nothing is gated and nothing needs committing: delete the scaffolds and you've adopted nothing.\n\n## 1 · Set up — terminal, once\n\n```bash\nnpm install -D codument\n```\n\nThen run **one** of these, matching your project:\n\n```bash\nnpx codument init      # new project: scaffold docs + the agent workflow\nnpx codument scan      # existing code, no docs yet: propose a registry + scaffolds\nnpx codument adopt     # existing Codument project: normalize + refresh\n```\n\n`init` installs the Claude profile by default (`AGENTS.md`/`CLAUDE.md`, `.claude/` skills + agents + rules, `docs/` with the registry). Pick profiles explicitly with `--agents claude`, `--agents codex`, or `--agents codex,claude`.\n\n**Start a fresh agent session after setup.** Your agent reads its Codument workflow from files this step writes: `CLAUDE.md`/`AGENTS.md`, plus the `.claude/` skills and subagents. Coding agents load these when a session starts, so one you already had open won't see them. After `init` (or `scan`/`adopt`), start a new session (or run `/clear`) and your agent picks up the delivery loop, the skills, and the `/update-docs` step below. The git pre-commit gate from `codument hooks install` is the exception: git honors it on the next commit, no restart needed.\n\n**Then have your agent write the docs.** On an existing codebase, `scan` only lays down empty scaffolds (marked `needs-review`). Tell your agent **`/update-docs`** and it reads your source to fill the registry's feature and concept docs with real content, giving `doctor` and `review` something to check against. That is the agent skill, not the `codument update` CLI in step 5 (which only re-syncs codument's own managed files on a version bump).\n\n\u003cdetails\u003e\n\u003csummary\u003eWhat each entry point does, in full\u003c/summary\u003e\n\n### New project → `init`\n\n```bash\nnpx codument init\n```\n\nInstalls the Claude profile by default:\n\n- `AGENTS.md` and `CLAUDE.md` with the shared delivery workflow\n- `.claude/skills/` with the core workflow skills\n- `.claude/agents/`, `.claude/rules/`, and `.claude/settings.json` for Claude-specific subagents, rules, and the documentation hook\n- `docs/` with feature, concept, guide, and ADR structure\n- `docs/.registry.json` mapping source files to docs\n- `.codument-meta.json` recording installed agent profiles\n\nPick profiles explicitly with `--agents claude`, `--agents codex`, or `--agents codex,claude`. The Codex/generic profile writes `AGENTS.md` and `.agents/skills/` only — portable across any agent that reads `AGENTS.md`.\n\n### Existing code, no docs yet → `scan`\n\n```bash\nnpx codument scan\n```\n\nGroups source files into feature and concept docs, creates scaffolds, and populates `docs/.registry.json`. New entries are marked `needs-review`; run `/update-docs` (the agent) to fill them with real content.\n\n### Existing Codument project → `adopt`\n\n```bash\nnpx codument adopt --dry-run --agents codex,claude\nnpx codument adopt --agents codex,claude\n```\n\nUse `adopt` when a project already has Codument docs or an older `.codument-meta.json`. It normalizes `docs/.registry.json` into the ownership shape (`primary_sources`, `related_sources`, `docs`, `depends_on`, `risk`), backs the previous file up as `docs/.registry.backup.json`, refreshes `.codument-meta.json`, and installs/updates the selected agent profiles. To re-derive the registry from source at any time, re-run `scan` — it overwrites the machine-derived entries while preserving your human-authored `docs`/`depends_on`/`risk`.\n\n\u003c/details\u003e\n\n## 2 · Build — your agent, ongoing\n\nChat normally. Codument's always-loaded instructions route clear intent into the right delivery skill; slash commands are just explicit overrides when you want to force a phase.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/assets/codument-workflow.png\" alt=\"The codument delivery workflow: charter (new project, once) → grill → plan → plan adversary (proportional) → approve (you decide) → implement \u0026 verify → document → review (you decide) → review adversary (proportional) → commit; two independent adversarial gates, verify don't trust; the CLI finds and reports, your agent fixes\" width=\"760\"\u003e\n\u003c/p\u003e\n\n### Autopilot\n\nOnce a plan is approved, tell your agent **\"codument, run the plan\"** (also recognized: \"run the plan\", \"codument this plan\", \"autopilot\", or `/work-step --auto`). It then works the approved plan end to end, implementing, reviewing, and committing each remaining step for you: one focused commit per step, under your own identity, no AI co-author trailer.\n\nIt is opt-in per run and off by default. The per-step gates still run; autopilot only stops *waiting* for your routine confirmation. It hard-pauses for anything that needs a real decision, and you can say **\"pause\"** or **\"stop autopilot\"** to drop back to one gated step at a time.\n\n\u003cdetails\u003e\n\u003csummary\u003eAutopilot precondition, pause conditions, and why there's no CLI command for it\u003c/summary\u003e\n\nCodument never runs your coding agent — your agent does. So you trigger autopilot by telling your agent, not by running a CLI command. Running `codument run` (alias `autopilot`) only prints this reminder and points you back at the phrase; the CLI itself does setup and deterministic checks, never your agent.\n\nAutopilot will not start until the plan is approved (`Status: approved`), and it pauses to ask when something needs a real decision:\n\n- a review finding that needs a human judgment call (and always for changes touching public interfaces, security, data loss, or dependencies),\n- a failing verification, or\n- a change that would fall outside the approved plan.\n\nTo run a single fully-gated step instead, say **\"work the next step\"** or `/work-step` without `--auto`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHow the workflow routes intent, and the installed skills\u003c/summary\u003e\n\n```mermaid\nflowchart LR\n  CH[charter?] --\u003e G[grill] --\u003e P[plan] --\u003e PADV{{\"plan adversary\u003cbr/\u003egrounded · never blocks\"}} --\u003e A{approved?}\n  A --\u003e|yes| I[implement] --\u003e V[verify] --\u003e D[document] --\u003e R[review] --\u003e RADV{{\"review adversary\u003cbr/\u003eblocks only on a red re-run test\"}} --\u003e C[commit]\n  C --\u003e|next step| G\n  A --\u003e|not yet| P\n```\n\n0. On an uncharted project, the first real-work-intent message triggers `establish-charter` once: it asks whether this is a quick demo or a serious app, then walks the core tech and architecture choices recommendation-first — explained in plain language with trade-offs, so even a non-technical user understands the decisions — and writes `docs/charter.md`. A pure question doesn't trip it; a charted project skips it.\n1. Before any source edit the agent names the assumption the change depends on; a load-bearing one it cannot confirm — or a rough, ambiguous request — triggers `grill-with-docs`.\n2. Settled scope triggers `plan-with-docs`, which writes the durable plan and stops for approval.\n3. Approved plans trigger `work-step` for the next unchecked step.\n4. Any source edit gets reviewed before commit — `review-work` inside a plan, the same bar for an ad-hoc fix.\n5. Clean or explicitly resolved reviews offer `commit-work` as the next gated action.\n\nThe installed workflow skills:\n\n| Skill | Purpose |\n| --- | --- |\n| `establish-charter` | On an uncharted project, set its seriousness (demo vs. serious) and walk the core tech/architecture choices recommendation-first, then write `docs/charter.md` — once, before the first grill |\n| `grill-with-docs` | Resolve the load-bearing assumptions a change depends on, against docs, code, ADRs, and edge cases, before planning |\n| `plan-with-docs` | Turn resolved decisions into a compact feature plan with steps and acceptance criteria |\n| `tdd` | Implement one behavior slice at a time with the strongest practical feedback loop |\n| `work-step` | Execute the next approved plan step without skipping ahead |\n| `review-work` | Review the diff against the approved plan, tests, docs, registry, and architecture |\n| `commit-work` | Verify, stage, and commit focused work with a conventional commit |\n| `update-docs` | Fill scaffold docs, or update/compact mapped docs after source changes |\n\nThey travel with a set of domain-expertise skills — `senior-backend`, `senior-architect`, `senior-frontend`, `frontend-design`, `motion-craft`, `code-reviewer`, `review-codebase`. Those are advisory craft depth your agent consults when a step fits their domain; they never replace the workflow gates above.\n\nKeep working state compact. A feature doc carries the standard's durable layers — plain-terms orientation, design approach, invariants with their test pointers, decisions, key files. A plan's delivery scaffolding (checklist, acceptance criteria, verification) lives in the doc only while the work is in flight and compacts out when it ships, never a transcript of every agent turn.\n\n\u003c/details\u003e\n\n## 3 · Check — terminal (deterministic core + independent adversarial gates)\n\nThe commands below are local, need no network and no AI model, and produce the same output for the same repo state: they read the registry, the filesystem, and `git`. The two **adversarial gates** at the end of this section are the opt-in exception: they involve an AI reviewer but decide every verdict with a deterministic oracle (a re-run test, a grounding projection), so the default path stays reproducible.\n\n### `codument doctor` — documentation coverage\n\n\"Test coverage for your docs.\" A deterministic gap-finder, not a quality judge.\n\n```bash\nnpx codument doctor\nnpx codument doctor --strict   # exit 1 on findings, to gate a CI step\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eCoverage / lint / notes / scope channels, and every flag\u003c/summary\u003e\n\n```bash\nnpx codument doctor\nnpx codument doctor --json     # stable machine contract for CI/badges\nnpx codument doctor --write    # write .codument/coverage.json + an SVG badge\nnpx codument doctor --strict   # exit 1 if there are findings, to gate a CI step\n```\n\nIt reports separate channels, never blended into one number:\n\n- **Coverage (scored):** ownership (in-scope source files with a documented owner), dependency (mature entries declaring `depends_on`), and risk (declared high-risk areas with a durable doc). The headline score is the equal-weight average of the ratios that apply; a ratio with no denominator is excluded, never counted as 0% or 100%. Freshness/drift is deliberately *not* scored here — staleness is the change-control gate's job (`codument review`), and a coverage ratio for it lands only once it can be re-sourced from that same signal instead of a second, disagreeing definition.\n- **Lint (warnings):** missing/leaked sources, missing docs, empty or dangling `depends_on` edges, unmapped sources, dead intra-repo doc links, and bloated docs (whole-doc size, oversized sections, never-compacted completed-step logs — tunable with `--max-doc-lines`, `--max-section-lines`, `--max-completed-log`). These are *findings* — a clean registry has zero.\n- **Notes (informational):** high-fanout files (a file mapped across many features), thin docs (a doc claimed done with no narrated orientation layer), orphan doc pages (a feature/concept page no registry entry owns, which the staleness gate therefore cannot cover), and three prose-altitude smells that read a doc against the documentation standard — `symbol-mirror` (prose restating an exported identifier and a verb), `line-anchor` (a `path.ext:NNN` reference, which rots on every edit), and `path-enumeration` (a section restating the file list; test citations are exempt, because the standard *asks* you to link each invariant to its enforcing test). Awareness-only — they never count toward \"clean\", because acting on them blindly degrades the registry (see the findings table below).\n\n- **Scope (disclosure):** a coverage number is only as good as the scope it was computed over, so the scope travels with it. `doctor` prints a note beside the headline whenever it could not fully verify what it measured — `.gitignore` rules it could not determine, a declared `exclude` block it could not read, a directory it could not open — and names the exclusions a project *did* declare. `--json` carries all of it additively under `scope` (`gitIgnore`, `reason`, `declaredScope`, `configuredExclusions`, `members`, `unreadableDirs`); `version` is unchanged, so a consumer that ignores the field reads exactly what it read before. It is disclosure, never a finding: it moves neither the lint count nor the exit code. This matters because a scope that silently *shrinks* makes the percentage read **better** than the truth — most confident exactly where it is most wrong.\n\n`doctor` is warning-only by default: neither findings nor notes change the exit code. Add `--strict` to make findings exit 1 (notes still never do), so a CI step can block a merge until they are cleared.\n\n`--verify-invariants` is a separate, opt-in mode: it parses each doc's `## Invariants \u0026 boundaries` test pointers and *runs* those tests, so \"this doc's invariants are enforced\" becomes a checkable claim rather than a decoration. It touches your environment (it shells out to your test runner, overridable with `--test-command`), which is why it is off by default; its results fold into the `--strict` exit and appear in `--json` only when the mode is on.\n\n\u003c/details\u003e\n\n### `codument review` — review an AI change\n\nReads the uncommitted git diff against the registry and reports what changed and what is suspicious: changed files grouped by owning feature, **stale docs** (a source moved but its mapped doc did not), high-risk areas touched, out-of-plan changes, and unmapped sources. It reports repo facts and gaps; it does not certify that a change is safe.\n\n```bash\nnpx codument review\nnpx codument review --json          # machine-readable\nnpx codument review --base main     # branch drift since the merge-base with \u003cref\u003e, not just uncommitted\n```\n\nIn a workspace of nested member repositories it names the members and each one's base HEAD, and resolves drift across them — see [Monorepos of nested repositories](#reference) below for the topology rules and what it refuses there.\n\n\u003cdetails\u003e\n\u003csummary\u003eEvery review flag, per-symbol drift, and SARIF output for CI\u003c/summary\u003e\n\nThe `review` command has grown beyond the default report. Every flag below is optional; with no flags it is the deterministic reporter above.\n\n```bash\nnpx codument review --strict          # step-sync gate: exit 1 on new unmapped source or a stale mapped doc\nnpx codument review --base main       # review branch drift since the merge-base with \u003cref\u003e, not just uncommitted changes\nnpx codument review --log             # append a caught snapshot to .codument/events.jsonl (impact ledger)\nnpx codument review --bundle          # emit the adversarial-review bundle as JSON, then exit\nnpx codument review --record findings.json   # record a fingerprint-bound review from a findings JSON, then enforce it\nnpx codument review --require-review  # exit 1 if a non-trivial diff has no current review artifact, or one with unresolved findings\nnpx codument review --require-review --test-command \"npx tsx --test {file}\"   # how a finding's named test is re-run ({file} = resolved path)\n```\n\n- **`--strict`** is the **step-sync gate**: it exits 1 while a step left a new source unmapped or a mapped doc stale. It is what Autopilot runs before checking a step off — materialize the file(s) and update the stale doc(s), then re-run until clean.\n- **`--base \u003cref\u003e`** reviews the whole branch's drift (merge-base..working-tree), not just uncommitted changes — pair it with `codument ack --base \u003cref\u003e` so a symbol move resolves against the same ref.\n- **`--bundle`** emits the adversarial-review bundle (the documented invariants + their tests + the diff) as JSON — the contract an independent reviewer attacks. The deterministic oracle that *decides* is the re-run of a finding's named test, never the bundle itself. **`--record \u003cfile\u003e`** records a fingerprint-bound review from a findings JSON (`{invariantsChecked, findings, signer}`) that **`--require-review`** then enforces — exiting 1 on a non-trivial diff with no current artifact, or one carrying unresolved confirmed findings. A finding **blocks only** when its named test is red on a live re-run (`--test-command`, `{file}` = the resolved path; default `npx --no-install tsx --test {file}` — resolved locally, **never fetched from the network**); point it at a TAP-emitting runner for non-`node:test` projects. When no runner is resolvable without a fetch, the summary says so by name (\"confirm step could not run — pass `--test-command`\") instead of silently reading advisory. Opt-in today; the default-on flip is soak-deferred.\n\n**Per-symbol drift.** Staleness is resolved **per symbol**, not per whole file. `review` fingerprints each exported declaration's token stream across two git refs; when a documented symbol **moved** and its owning doc did not, only that symbol's owning feature wakes — the old whole-file cascade is dissolved. The verdict is a pure, reproducible function of `(base, head, codument version, algoStamp)` with no clock input. It enforces that a moved documented symbol and its owning doc stay **in sync** (waking the feature when they don't), not that the prose is correct — a born-wrong or already-drifted doc is out of scope by construction. A separate name-match signal (does the doc even mention the symbol) is kept as **info-only telemetry**, never a verdict input. Before hashing, each declaration's token stream is **canonicalized**: a name bound within the declaration (a parameter, a block local, a destructured or catch binding, a generic type parameter) is rewritten to a positional index, so a meaning-preserving local rename does not move the fingerprint at all. What still fires is a real change — a different free/imported/global reference, a type or contract-name change (a property key, an object shorthand, a constructor parameter property), or a structural edit.\n\n**SARIF for CI (`--format sarif`).** `review --format sarif` emits the verdict as [SARIF 2.1.0](https://sarifweb.azurewebsites.net/), the format GitHub code-scanning renders as inline annotations on a pull request's changed lines. No bot and no hosted service: it is a static file your existing CI step uploads. Every stale doc, unmapped source, out-of-plan change, and ownership ambiguity becomes one annotation, and a stale-doc annotation names the symbol that moved and its fingerprint transition. It is mutually exclusive with `--json` and changes only stdout — the exit code still comes from `--strict`, so **one step both prints the annotations and fails the check**. Two steps are the whole recipe: run `review` writing SARIF to a file, then upload it.\n\n```yaml\n# .github/workflows/codument.yml — annotate PRs, no bot, no network\nname: codument\non: pull_request\njobs:\n  docs:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      security-events: write        # required to upload SARIF\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0            # full history so --base can find the merge-base\n      - run: npx codument review --strict --base \"origin/${{ github.base_ref }}\" --format sarif \u003e codument.sarif\n      - if: always()                # upload even on the run that fails the check, so annotations still appear\n        uses: github/codeql-action/upload-sarif@v3\n        with:\n          sarif_file: codument.sarif\n```\n\n`reviewdog` consumes the same file if you prefer it to code-scanning. When the gate cannot run (not a git repository, a wrong root, a git failure) the SARIF marks the invocation unsuccessful rather than reporting a false \"clean.\"\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eEnforce the gate, ack a neutral move, or audit history: \u003ccode\u003ehooks\u003c/code\u003e · \u003ccode\u003eack\u003c/code\u003e · \u003ccode\u003eaudit\u003c/code\u003e\u003c/summary\u003e\n\n### `codument hooks` — make the gate enforced, not advisory\n\nEverything above exits nonzero when a step is out of sync, but an exit code only gates a commit if something runs it at commit time. Two arms close that hole:\n\n```bash\ncodument hooks install          # local: a pre-commit hook that runs `review --strict`\ncodument hooks install --ci     # + remote: scaffold .github/workflows/codument.yml (PR gate)\ncodument hooks status           # is the gate enforced here, and where\ncodument hooks uninstall        # remove the managed block; your own hook lines survive\n```\n\nThe pre-commit hook is a **managed block**: markers delimit the only region codument ever touches, an existing shell hook is appended to (never rewritten), a non-shell hook is refused with the one line to add manually, and `core.hooksPath`/worktree setups are honored by asking git. A red gate blocks the commit and names both escapes — `git commit --no-verify` or `CODUMENT_SKIP_GATE=1 git commit` — so skipping is a stated act, never a slip. If the codument binary is missing (a wiped `node_modules`), the hook warns loudly and lets the commit pass rather than bricking every commit. Honest limit: the gate evaluates the **working tree**, not the staged bytes, so with partial staging it is a speed bump, not a proof of the commit's contents.\n\nThe local hook can always be skipped; the **CI check is the authority**. The scaffolded workflow runs the same strict gate against the PR's merge base — make it a *required* status check in branch protection and a red gate becomes a merge blocker. The workflow file is yours to evolve: it refreshes on reinstall only while its managed marker is present, and codument refuses to touch it once you delete the marker. `init --hooks` installs the pre-commit arm during project setup. At a workspace root containing member repositories the install is refused: one hook there would block each member's commit on the other members' staleness, so install it inside the member repository you want gated.\n\n### `codument ack` — clear a change that owes no doc change\n\nWhen a symbol moves but no documented contract changed, you don't paper over the gate with a mirror edit — you **acknowledge** it. An ack records a fingerprint-bound, **auto-invalidating** decision so `review` stops flagging it, and it takes two forms.\n\n```bash\n# per-symbol: a moved symbol was a contract-neutral refactor\nnpx codument ack src/registry.ts::readRegistry --reason \"return shape unchanged\"\n\n# file-grain (bare path): a changed source file's current content owes no doc change\nnpx codument ack src/registry.ts --reason \"added a helper export; no contract change\"\n\nnpx codument ack --list                 # list recorded acks with their handles\nnpx codument ack --remove \u003chandle\u003e      # remove one by handle\nnpx codument ack src/foo.ts::bar --base main --signer alice   # match review --base; attribute the signer\n```\n\n- **Per-symbol ack (`\u003cpath\u003e::\u003csymbol\u003e`)** is the agent-judge resolution that a **moved** symbol was a contract-neutral refactor owing no doc change. It is bound to the exact `from → to` fingerprint transition, so it **auto-invalidates the next time the anchor moves** — no ride-forever exemption. The gate verifies the ack's **form** only (it exists, is attributed with non-empty fields, and names the exact moved fingerprint), **never its semantic truth** — code/doc equivalence is undecidable, so honesty rests on the visible ack-rate and the durable audit trail, not a truth check.\n- **File-grain ack (bare `\u003cpath\u003e`, per ADR 012)** vouches that a changed source file's **current content** owes no doc change. It clears only **additive** (added/removed-symbol), **concept-umbrella**, and **coarse/non-TS** staleness, bound to the file's content fingerprint (auto-invalidating on the next change). It **never masks a moved symbol**: a `changed` (moved) owned symbol still wakes its feature, so a real contract change is never laundered. It **counts as an ack** — a distinct `file-acked` line on the no-doc-change-owed side, never as a doc update — so over-acking stays visible and the friction rate is not deflated. A parse-unevaluable file **cannot** be file-acked into freshness (the fail-loud stance holds).\n- **Flags:** `--reason \u003ctext\u003e` names the contract that stayed constant; `--base \u003cref\u003e` resolves the move against the merge-base with `\u003cref\u003e` (match the ref `review --base` used; like `review --base` it is refused in a workspace of member repositories, where one ref cannot name several histories); `--signer \u003cid\u003e` sets attribution (defaults to the git author; an independent signer is what strict-mode independence checks); `--list` / `--remove \u003chandle\u003e` manage recorded acks; `--root \u003cdir\u003e` sets the project root.\n\n*Honest limit:* the additive-owes-no-doc judgment (like the per-symbol ack's semantic claim) is **prose-enforced, not test-backed** — the gate checks the ack's form and fingerprint, never whether the human was right that no doc was owed.\n\n### `codument audit` — score doc drift over committed history\n\nThe live gate pointed backwards: for each documented feature, symbol moves in a commit range whose owning doc got no attention in the same range. Runnable on a repo that has adopted nothing (pair it with `scan`, above) and on any release range of an adopted one.\n\n```bash\nnpx codument audit v1.0.0..HEAD\nnpx codument audit v0.7.0..v0.8.0 --json   # version-tagged; byte-identical for the same repo state\n```\n\n- Same analyzer, same semantics as `review` — per-symbol staleness, deletions first-class (a rename's old path included), the registry-entry-removal dodge closed, parse-broken files surfaced instead of trusted. The range is diffed from the merge-base, so merged-in commits are not misattributed.\n- Acknowledgments don't apply retroactively — an ack adjudicates the live working tree, not an arbitrary historical range — so the audit reports raw drift.\n- **Informational by contract:** findings never change the exit code; `--json` carries `driftedCount` so you can threshold it yourself. Only an audit that *could not run* (bad range, unreachable ref, broken git, or a workspace of member repositories whose histories a single range cannot name) exits non-zero — \"could not look\" never reads as \"no drift\".\n\n\u003c/details\u003e\n\n### `codument watch` — live terminal view\n\nA second terminal that continuously refreshes the same change-state while your agent works (no daemon, zero extra dependencies). It leads with a plain-words verdict (`✓ CLEAN`, `▲ DRIFTING`, `■ AT RISK`, or `⊘ OFF-PLAN`) over the all-sessions estimated cost and a per-feature breakdown, and it reuses the exact analyzer `review` uses, so the live view and the snapshot can never disagree.\n\n```bash\nnpx codument watch\nnpx codument watch --once          # one frame, for CI/inspection\n```\n\n\u003cdetails\u003e\n\u003csummary\u003ewatch flags, the event log (\u003ccode\u003efeed\u003c/code\u003e, \u003ccode\u003esteps\u003c/code\u003e), and estimated token cost (\u003ccode\u003eemit\u003c/code\u003e, \u003ccode\u003erates\u003c/code\u003e, \u003ccode\u003ecost\u003c/code\u003e)\u003c/summary\u003e\n\n```bash\nnpx codument watch\nnpx codument watch --once          # one frame, for CI/inspection\nnpx codument watch --interval 1000\nnpx codument watch --dir ../other  # watch another repo without cd\n```\n\n`watch` tails the append-only `.codument/events.jsonl` flow log.\n\n### `codument feed` — populate the event log from the agent's session\n\n`feed` is the producer behind the live view: it tails the active Claude Code session transcript (the per-turn log Claude Code already writes), normalizes each turn's token usage + tool activity into `.codument/events.jsonl`, and attributes it to a feature via the registry. `watch` runs it for you each refresh (disable with `watch --no-feed`); call it directly for a one-shot backfill or a headless/CI populate.\n\n```bash\nnpx codument feed              # tail the session log continuously\nnpx codument feed --once       # single backfill pass, then exit\nnpx codument feed --dir ../other\n```\n\nIt reads telemetry that already exists (no extra token cost), is idempotent (a byte-offset cursor means restarts never double-count), and is best-effort against Claude Code's internal transcript format. It's the Claude-specific adapter for the otherwise vendor-neutral `emit` + events-log seam.\n\n### `codument steps` — mirror the active plan's checklist\n\nPrints the active plan's delivery-plan checklist so you can mirror it into a native to-do panel, and optionally logs the active step for `watch`:\n\n```bash\nnpx codument steps                       # the single approved plan with an unchecked step\nnpx codument steps --json                # machine-readable, with per-step to-do status\nnpx codument steps --emit                # append a `step` event to .codument/events.jsonl (for watch)\nnpx codument steps --plan docs/features/foo.md\n```\n\n### `codument emit tokens` — estimated token cost, per feature\n\ncodument never calls an AI model, so it can't meter tokens itself. Instead your agent (or a small hook that reads its session transcript) reports usage as it works, and `watch` shows an **estimated** running cost, attributed to the feature being worked:\n\n```bash\ncodument emit tokens --model opus-4.8 --input 1200 --output 340 \\\n  --cache-read 8000 --feature auth --step 3\n```\n\nThat appends a **counts-only** record to `.codument/events.jsonl` — no dollars are stored. `watch` prices it live, under the verdict headline:\n\n```text\n  ✓ CLEAN    1 feature touched · docs current\n  Cost       $0.50 estimated · 1 session\n    auth         $0.32\n    billing      $0.18\n```\n\nThe four token buckets are priced separately — cache reads are ~10× cheaper than fresh input and dominate the count, so a single blended rate would overstate the bill badly. The figure is **always an estimate**, derived from a rate table at display time, never an invoice; per-feature numbers are an allocation of what the agent tagged.\n\n**Setting prices.** codument bundles rates for **Claude models only** — deliberately, so it stays agent-neutral without shipping prices for vendors it can't keep current. To price anything else — Codex/GPT, Gemini, a fine-tune — or to override a default, add a `.codument/rates.json` to your project (USD per million tokens, per bucket; any bucket you omit is treated as `$0`):\n\n```json\n{\n  \"codex-1\":  { \"input\": 1.5, \"output\": 6, \"cacheRead\": 0.2 },\n  \"gpt-5\":    { \"input\": 2,   \"output\": 8 },\n  \"opus-4.8\": { \"output\": 30 }\n}\n```\n\nThe numbers above are **illustrative** — fill in each provider's current published rates. Your file merges *over* the built-in defaults — add new models, or override a single bucket of an existing one. A model with no rate isn't an error: its tokens are still counted and it's flagged `unpriced` rather than priced wrong. And because only counts are stored, changing a rate re-prices everything — past logs included — on the next `watch`.\n\n### `codument cost` — the full token-cost ledger\n\n`watch` shows a glanceable top-3 of where spend went; `codument cost` prints the **whole** ledger from the captured `.codument/events.jsonl` — the all-sessions estimated total, then every feature, model, and (when attributed) step, sorted by spend with each line's share:\n\n```bash\nnpx codument cost                 # the full ledger\nnpx codument cost --json          # the raw token summary, for scripts\nnpx codument cost --dir ../other  # another repo without cd\n```\n\n```text\ncodument cost  ·  my-app\n\n  $3,992.79 estimated  ·  22006 events\n  2.8M in · 33.9M out · 4.7B cache-read · 107.3M cache-create\n\nby feature\n  ingredient-catalog      $729.72   18%\n  cook-voice-loop-ux…     $447.39   11%\n  …\n\nby model\n  opus-4.8              $2,869.03   72%\n  opus-4.7                $906.51   23%\n```\n\nIt's a pure read — it never tails or mutates the log (refresh capture with `feed`/`watch` first) and needs no git repo, just a `.codument/events.jsonl`. Cost is derived from the rate table at read time (an **estimate**, never a bill); an unknown model is flagged `unpriced` rather than priced wrong.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eThe same review as a shareable HTML page: \u003ccode\u003ecodument report\u003c/code\u003e\u003c/summary\u003e\n\n```bash\nnpx codument report          # writes .codument/report.html and opens it\nnpx codument report --no-open --out review.html\n```\n\nA self-contained page (no network, no JS) that leads with a plain-language verdict and the coverage delta, with finding cards and a collapsible per-file breakdown — instead of a wall of terminal text.\n\n\u003c/details\u003e\n\n### The two adversarial gates (independent, proportional)\n\nAlongside the deterministic checks, Codument can run two **adversarial** gates. Both are proportional (they fire on the work that warrants them, not on trivial edits), both project the same committed docs and registry into a contract an independent reviewer attacks, and neither introduces a new source of truth or a model call on the verdict path. The principle is **verify, don't trust**: an AI raises the objection or finding, and a deterministic oracle decides what it means. A **plan adversary** (`map check --plan`) contests the plan before code exists and never blocks; the human adjudicates. A **review adversary** (`review --require-review`) contests a non-trivial diff and hard-blocks only when a finding's named test is genuinely red on a live re-run.\n\n\u003cdetails\u003e\n\u003csummary\u003eHow each gate works, and its honest limits\u003c/summary\u003e\n\n**Plan adversary — `codument map check --plan \u003cpath\u003e`.** Before any code is written, an independent adversary reads *only* the plan plus a deterministic grounding projection over `docs/.registry.json` and the committed feature docs (invariants, test pointers, dependency edges, risk tags, Feature-Map rows — emit it with `map check --plan \u003cpath\u003e --json`). It surfaces only **grounded** objections — each must cite a committed constraint the plan contradicts or name a load-bearing assumption the grill left unresolved — one tight line each, most-serious-first, folded into the same open-questions block of the approval summary you already read. It **never blocks**, never rewrites the plan, never reopens the grill; the **human adjudicates** at the existing approve/change gate. \"No material objections\" is the correct, expected output for a well-grilled plan, not a failure. A plan with no Feature Map runs no adversary (proportionality skip).\n*Honest limit:* its quality is **prompt-enforced, not test-backed**. A plan has no executable oracle, so groundedness — not correctness — is the only honest deterministic analog; no mechanism can prove an objection is grounded or catch a fabricated one, and manufacturing a weak objection is the cardinal failure the mandate guards against but cannot mechanically prevent. On a host without subagents no automatic independent pass runs at all — it degrades to a manual handoff (grounding + a paste-ready prompt + a plain statement that no independent pass ran), so the guarantee is genuinely weaker there. And because it never blocks, a wrong plan a human waves through is not stopped by the tool.\n\n**Review adversary — `codument review --require-review`.** After the work, an independent adversary presumed to be hunting for failure is handed a precise **bundle** to attack (`review --bundle`): the diff, the documented invariants it must not break and the tests that pin them, the relevant plan slice, and ownership/blast facts. The verdict is **verify, don't trust** — a finding hard-**blocks only** when its named test is genuinely red when the gate **re-runs it on the spot** (a nonzero exit counts as red only with TAP evidence the runner actually executed tests); the fix flips it green. The gate re-derives every status and never trusts what an artifact claims. The artifact (`.codument/reviews/\u003cid\u003e.json`) is fingerprint-bound over the full change set *and* the named tests, so editing the diff or tampering a test after review auto-reopens the gate. It is opt-in; proportionality skips trivial edits; non-testable/judgment findings are recorded and routed to the review decision point, never auto-blocked.\n*Honest limit:* an **empty or omitted-findings review still passes** — the gate enforces the review *ritual* (a diff-bound artifact enumerating the invariants checked) and verifies *declared* findings, but it does **not** certify thoroughness. Requiring TAP evidence to call a red test blocking means a runner that does not emit TAP (vitest/jest in default reporters) makes a real red test read as unrunnable → advisory (**fail-open**); a non-`node:test` project must point `--test-command` at a TAP-emitting runner or its findings stay advisory. The default runner resolves **local-only** (`npx --no-install`): the verdict path never downloads code, and a project where nothing resolves gets a named \"confirm step could not run\" condition in the summary rather than a silent always-green. Default-on is soak-deferred, so it is opt-in today, and only a finding reducible to a runnable failing test can ever block.\n\n\u003c/details\u003e\n\n## 4 · Fix — from findings to clean\n\n`doctor` and `review` report findings; they never auto-fix, so there is no `codument fix`. You clear findings the way you build features: your agent fixes them with the installed skills, then you re-run the check to confirm. A finding that re-runs clean is the \"done\" signal. The skills already know this loop: `/update-docs` starts from `codument doctor` and `/review-work` starts from `codument review`, so your agent pulls the findings and clears them without you reciting them.\n\n\u003cdetails\u003e\n\u003csummary\u003eWhat each finding type means and how to clear it (and why high-fanout is a note, not a finding)\u003c/summary\u003e\n\nThe finding's *type* tells you which lever to pull:\n\n| Finding | What it means | How to clear it |\n| --- | --- | --- |\n| **stale doc** (`review`) | a source changed but its mapped doc did not | `/update-docs` — update the doc from the current source; or, for a contract-neutral move, `codument ack \u003cpath\u003e::\u003csymbol\u003e` instead of editing prose |\n| **bloated-doc** | a doc is too long, has an oversized section, or carries a never-compacted `[x]` completed-log | `/update-docs` — **compact** it: drop the done log (it lives in git history), split big sections, keep the durable decisions. Not a rewrite. |\n| **missing-doc** | a registered feature has no doc | `/update-docs` — write it from the template |\n| **unmapped-source** | a real source file has no owning feature | add it to a feature's `primary_sources` in `docs/.registry.json` (or `codument scan` to propose mappings) |\n| **generated-leakage** | a file matching an exclusion rule (build/generated/test/data, e.g. `dist/**`, `*.seed.json`), or one your repository git-ignores, is listed as a source | de-list it — it is not tracked source. If the heuristic misfired on genuinely authored content, that content belongs in the registry and the rule that caught it is the thing to narrow |\n| **empty-depends-on** | a mature, *isolated* entry declares no dependencies — nothing depends on it and it depends on nothing (a foundation that other entries depend on is exempt: it legitimately depends on nothing) | add its real `depends_on` edges, or set `depends_on_confirmed: true` on the entry after reviewing that a true leaf really has none (fresh `needs-review` scaffolds are exempt until reviewed) |\n| **dangling-depends-on** | a `depends_on` slug names no registry entry — review's impact fan-out and the dependency score silently lose that edge | register the missing entry, or fix the slug if it is a typo |\n| **link-rot** | a doc's intra-repo link or `[[wikilink]]` points at a file that does not exist | fix the link target, or remove the link if the page is gone for good |\n| **thin-doc** *(note)* | a doc claimed done (`status: current`) has no narrated orientation layer — half-documented reads green to every other check | write the doc's \"In plain terms\" layer, or flip the entry to an honest in-flight status (`needs-review`/`draft`) |\n| **orphan-doc** *(note)* | a feature/concept page no registry entry points at — the staleness gate structurally cannot cover it, so it rots silently | add it to the owning entry's `doc`/`docs` so the gate covers it, or knowingly leave it unowned (the note never blocks) |\n\n**`high-fanout` is a note, not a finding — don't \"clear\" it.** A file mapped across many features is usually *correct*: shared infra (security rules, shared types, a root layout, a barrel file) is supposed to be mapped widely, and that breadth is exactly what lets `review` flag every dependent when it changes. Collapsing it to one owner to zero the count **severs that signal** — and the single owner is often the wrong one. Act only when the breadth is genuinely wrong (a test helper or unrelated utility mapped into features that don't own it); otherwise leave shared infra mapped widely, or raise `--high-fanout` if the threshold is noisy for your repo. \"Clean\" never requires touching it.\n\nKeep docs compact as you go and `bloated-doc` rarely fires.\n\n\u003c/details\u003e\n\n## 5 · Upgrade — terminal, on version bumps\n\nAfter bumping the codument package, re-sync the managed files (skills, rules, `AGENTS.md`/`CLAUDE.md`) for the agent profiles recorded in `.codument-meta.json`:\n\n```bash\nnpx codument update --dry-run   # preview first\nnpx codument update\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eOverride stored profiles, and what \u003ccode\u003eupdate\u003c/code\u003e touches\u003c/summary\u003e\n\n```bash\nnpx codument update --agents codex,claude   # override stored profiles\n```\n\n`update` only refreshes codument's own managed files — it never touches your docs or code. Entries you've customized are backed up to `\u003cfile\u003e.backup`; symlinked/pointer skill entries are left untouched.\n\n\u003c/details\u003e\n\n---\n\n## Reference\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eScoping what counts as documentable (build output, deploy trees, generated files)\u003c/b\u003e\u003c/summary\u003e\n\nCodument's denominator is \"source files that should have a documented owner\" — so anything in it that is *not* authored source drags your coverage down, gets proposed into your registry by `scan`, and shows up in `review` as unmapped change. The built-in exclusions cover the conventions (`dist/`, `build/`, `node_modules/`, `coverage/`, each language's test conventions, declaration artifacts), and everything your repository git-ignores is subtracted on top of them.\n\nWhat that cannot reach is the part only your project knows: a `tsc` `outDir` named something else, a deploy tree, generated-but-committed files, vendored code. Declare those in `.codument-meta.json`:\n\n```json\n{\n  \"exclude\": {\n    \"dirs\": [\"out\", \"public-preprod\"],\n    \"globs\": [\"**/*.gen.ts\", \"vendor/**\"]\n  }\n}\n```\n\n- **`dirs`** are bare directory names, matched at any depth. A path like `\"build/out\"` is rejected — use `globs` for that.\n- **`globs`** are matched against the repository-relative path (`*` and `**` supported).\n- Both keys are optional and **additive**: they widen the built-in exclusions, never replace or re-open them. There is deliberately no way to *remove* a built-in exclusion, so no project can quietly re-admit its test files into a coverage number.\n- The file extensions codument treats as source are **not** configurable — that list is the language matrix, and letting a project extend it would let codument claim support for a language it has no adapter for.\n\nEvery surface honors the same declaration: `doctor`'s denominator, `review`'s verdict, `scan` discovery, `audit`, and the editor nudge hook. `doctor` and `scan` both print what is in effect, and `doctor --json` carries it as `scope.configuredExclusions`:\n\n```text\ndoctor:  scope: also excluding 2 dir(s): out, public-preprod — .codument-meta.json\nscan:    scope: also excluding dirs: out, public-preprod — .codument-meta.json\n```\n\nA typo is an error, not a silent no-op: an unknown key, a non-string entry, an empty entry, or a path in `dirs` fails the command by name. Declaring an excluded path that some registry entry still lists as a source keeps firing `generated-leakage`, so an exclusion can silence the gate only visibly.\n\nThere is no `--exclude` flag on purpose. Scope is a repository artifact your reviewers see in the diff, not an invocation choice — a flag would let two runs of the same commit disagree about what was measured.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eMonorepos of nested repositories (and submodules)\u003c/b\u003e\u003c/summary\u003e\n\nCodument sees a workspace, not just one git work tree. If your repository contains other git\nrepositories — packages that are each their own repo, or git submodules — an outer `git` reports\neach of them as a single opaque gitlink and cannot see inside. Run codument at the workspace root\n(the directory that *contains* the member repositories; it need not be a repository itself) and it\nresolves the members, aggregates each one's own git view, and reasons over the union:\n\n```text\ncodument review\n\n  workspace: 2 member repositories (applications-service, apply-exp) — git scope aggregated\n    base applications-service: 1495d24aa8df\n    base apply-exp: 49dcd6b05913\n```\n\nThe worktree gate resolves per-symbol drift across members, diffing an owned source inside a member\nagainst that member's own HEAD, and prints each member's base so any run is reproducible. A plain\nsingle repository is unaffected — it takes the exact path it always did.\n\nWhat a single ref cannot honestly name across several repositories, codument refuses rather than\nguesses: ref-ranged review (`review --base`, and the CI workflow a `hooks install --ci` scaffolds around it), a history `audit` range, and a `hooks install` at the\nworkspace root each fail with a `wrong-topology` diagnostic that points you at the member repository\nto run them inside. For a nested-member monorepo, CI enforcement is `doctor` plus the worktree gate,\nnot the two-ref PR gate. See [ADR-016](docs/architecture/decisions/016-nested-repo-workspace-aggregation.md).\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eProof benchmarks\u003c/b\u003e\u003c/summary\u003e\n\nCodument ships self-contained proof benchmarks. They do not call an AI model, require telemetry, or judge work subjectively.\n\nThe **context benchmark** compares two deterministic context-selection strategies over a packaged fixture:\n\n```bash\nnpx codument benchmark context\nnpx codument benchmark context --json\n```\n\n```text\nNaive context:    3,068 estimated file-context tokens (16 files)\nCodument context: 1,746 estimated file-context tokens (8 files)\nReduction:        43.1%\n\nRelevance:\n  Required docs found:       3/3\n  Required source files:     4/4\n  Irrelevant files included: 0/8\n```\n\nThese are estimated file-context tokens using `ceil(characters / 4)`. The benchmark proves the packaged registry can route a known task to a smaller relevant working set. It does not claim every real task reduces total model tokens; small tasks may spend more on workflow than they save.\n\nThe **quality benchmark** gives any coding agent the same fixture task and scores the final repo deterministically:\n\n```bash\nnpx codument benchmark init /tmp/codument-bench --agents codex\ncd /tmp/codument-bench\n# Give BENCHMARK_TASK.md to your agent.\nnpm test\nnpx codument benchmark score /tmp/codument-bench\n```\n\n`benchmark score` checks the final files for passing tests, required behavior, docs updates, registry coverage, protected fixture metadata, source boundaries, and benchmark-specific shortcuts. Expected shape:\n\n```text\nFresh fixture:     6/9 FAIL\nCompleted fixture: 9/9 PASS\n```\n\nTo compare a baseline against Codument, initialize two fixture directories and give both agents the same task — one solving directly, one following `AGENTS.md` and the skills — then score both with the same command.\n\nThe **catch-rate benchmark** is the ground-truth proof behind the review step. It ships a feature diff that carries planted bugs as uncommitted work over a committed baseline, and scores how many your agent catches before commit — once with the review loop, once without:\n\n```bash\n# no-loop: ship the diff straight to commit\nnpx codument benchmark init /tmp/bench-noloop --seeded\n# (commit the diff as-is, then:)\nnpx codument benchmark score /tmp/bench-noloop --mode no-loop\n\n# loop: review the diff, fix what you find, then commit\nnpx codument benchmark init /tmp/bench-loop --seeded\n# (run codument review + the review-work skill, fix, commit, then:)\nnpx codument benchmark score /tmp/bench-loop --mode loop --baseline /tmp/bench-noloop\n```\n\nEach bug has a hidden detector test that passes only when the bug is fixed; the answer key never ships into the scenario, so the agent can't read it. The `--baseline` comparison reports the loop-vs-no-loop delta. Because the no-loop baseline catches ~nothing by construction, the honest claim is \"review catches X% that would otherwise ship,\" not a natural-catch-rate comparison.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eThe documentation model\u003c/b\u003e\u003c/summary\u003e\n\nCodument's docs follow two ideas that make them durable rather than decorative:\n\n- **Registry-owned docs.** Every source file has an owning doc, recorded in `docs/.registry.json`. Ownership is what makes drift *detectable*: when a source file changes but its owner doesn't, that's a stale doc the deterministic checks can flag — not a judgment call.\n\n- **One source, layered by audience.** A doc is never split into a \"human version\" and an \"agent version\" — two copies drift, which is the exact failure Codument exists to prevent. Instead each doc carries ordered layers in a single file, from plain to precise:\n\n  ```text\n  ## In plain terms            — what it does and why, no jargon\n  ## Design approach           — why it is shaped this way, at guide level\n  ## Invariants \u0026 boundaries   — what must always hold, each linked to the test that enforces it\n  ## Decisions                 — the durable \"why\", pointing into ADRs\n  ## Key files                 — where to start reading, by role\n  ```\n\n  A human reads the top and expands downward to learn; the agent reads all of it. Audience is a *presentation* concern, never a storage one — so there's only ever one thing to keep true. The machine-readable side — ownership, dependencies, risk — lives solely in `docs/.registry.json`, never duplicated into doc frontmatter where it would drift. And the line for what belongs in prose at all: keep only what survives a refactor that renames every symbol; mechanism is read live from the code.\n\nDocs come in types — **features** (a capability), **concepts** (a cross-cutting idea), and **ADRs** (a recorded architecture decision) — and the layering applies within each.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eDocumentation structure\u003c/b\u003e\u003c/summary\u003e\n\n```text\ndocs/\n  .registry.json\n  overview.md\n  getting-started.md\n  features/\n  concepts/\n  architecture/decisions/\n  guides/\n```\n\nThe registry is the source of truth for which docs own which source files. Agents must check it before and after source edits.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cb\u003eDeveloping codument\u003c/b\u003e\u003c/summary\u003e\n\nTest a local checkout against another project by building the CLI and running it directly:\n\n```bash\ncd /path/to/codument\nnpm run build\n\ncd /path/to/existing-project\nnode ../codument/dist/cli.js adopt --dry-run --agents codex,claude\n```\n\nTo test hooks that call `node_modules/codument/...`, install a local packed copy:\n\n```bash\ncd /path/to/codument\nnpm --cache /private/tmp/codument-npm-cache pack\n\ncd /path/to/existing-project\nnpm install -D ../codument/codument-0.9.0.tgz\nnpx codument adopt --agents codex,claude\n```\n\n\u003c/details\u003e\n\n## Contributing\n\ncodument is a solo-authored, source-available project. I build and maintain it on my own, so it's both a working tool and a portfolio of how I approach change control for AI-made changes.\n\nI'm not accepting code contributions, so please don't open a pull request. It's nothing personal; I just want to keep the codebase something I can fully stand behind.\n\nWhat is very welcome:\n\n- 🐛 Bug reports and 💡 ideas → open an issue\n- 🧪 \"I ran it on my repo and here's what happened\" → issues or Discussions\n- ⭐ a star, if it's useful to you\n\nThe Apache-2.0 license lets you fork, run, and adapt it freely for your own use.\n\n## Acknowledgements\n\nThanks to Matt Pocock's [mattpocock/skills](https://github.com/mattpocock/skills), especially `/grill-with-docs`, which helped shape Codument's habit of grilling ideas against the docs before coding.\n\n## Requirements\n\n- Node.js \u003e= 18\n- An AI coding agent that can read repo instructions and markdown skills\n- A supported-language codebase for the staleness gate (see [Works with your stack](#works-with-your-stack)). Other file types are surfaced via registration, not judged.\n- Git. A single repository, or a **monorepo whose packages are their own repositories** (and submodule super-repos) — run codument at the directory containing them, which need not itself be a repository. See [Monorepos of nested repositories](#reference).\n\n## License\n\nCodument is open-source software released under the [Apache License 2.0](./LICENSE). See also the [NOTICE](./NOTICE) file for attribution.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakubsuplicki%2Fcodument","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakubsuplicki%2Fcodument","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakubsuplicki%2Fcodument/lists"}