Adopting DocGuard in an Existing Project
You do not need complete documentation before DocGuard is useful. Adopt it in place: let it read the code, freeze the findings that already exist, gate new drift in CI, and pay the old findings down at your own pace.
This guide takes an existing repository from a clean branch to a first pull request with the gate on. Each step says what to run, what it writes, and why it comes in that order.
Do not start with “document everything”. A first goal of complete, accurate documentation for the whole system turns adoption into a long project that blocks the gate. Get a baseline and a passing gate first. Then improve the documents that matter for the next change you make.
1. Start from a reviewable baseline
Commit or stash your work and create a branch for the adoption:
git status # should be clean
git switch -c adopt-docguard
Adoption writes several files at once. On a branch, every one of them shows up in a normal code review, and you can drop the branch if you change your mind.
The files you can expect by the end of this guide:
| Path | Written by | Commit it? |
|---|---|---|
.docguard.json | init | Yes |
docs-canonical/, docs-implementation/ | generate --plan --write, init | Yes |
AGENTS.md, CHANGELOG.md, DRIFT-LOG.md | init --fix (skipped when they exist) | Yes |
.docguardignore | init (skeleton path) or init --fix | Yes |
.docguard.baseline.json | guard --update-baseline | Yes |
Agent skills and commands (.claude/skills/, .agent/, …) | init | Yes, if your team shares agent setup |
.specify/ | init, when the Spec Kit CLI is installed | Yes, if you use Spec Kit |
.github/workflows/docguard.yml | init --with ci | Yes |
.docguard/ | Most commands (local state) | No. It contains a .gitignore that ignores the whole directory |
Note: Avoid
--forceon the first run. It overwrites existing files (keeping a.bakcopy). Without it, DocGuard skips files that already exist.
2. Install DocGuard
DocGuard needs Node.js 18 or later. Git is optional, but the history-based checks (freshness, diff suspicion, reference existence) need it. python3 is optional and gives syntax-tree accuracy on Python code.
For a team, pin the version as a dev dependency so everyone and CI run the same release:
npm install --save-dev docguard-cli
npx docguard --version
Or install it globally with npm i -g docguard-cli. The other channels (PyPI, Homebrew, Docker, the Spec Kit extension) are in Installation. The rest of this guide writes docguard; use npx docguard if you installed it as a dev dependency.
3. Run init on the existing code
docguard init
On an existing repository, init does three things before it writes anything:
- Looks for documents you already have. If two or more canonical documents (architecture, data model, security, and so on) sit together in a directory other than
docs-canonical/,initoffers to use that directory and records the mapping asdocs.rolesin.docguard.json. In a non-interactive run it adopts the directory. A Spec Kit feature folder such asspecs/003-feature/is never adopted. - Detects the project type (
api,webapp,cliorlibrary) from the manifest at the root (package.json,pyproject.toml,Cargo.toml,go.mod, …). The type decides whether checks such as environment variables and database documentation apply. The detection table is in Configuration, under “Project Type Detection”. - Chooses smart mode or the template path. If the repository has code but no canonical documents,
initswitches to smart mode.
Smart mode (the default for existing code)
Smart mode starts when there are no canonical documents and the repository looks like it has code: a cli/, src/, lib/ or app/ directory, a Django manage.py, a Python web framework, or ten or more source files at the top level.
It runs the same scan as docguard generate --plan, prints the plan, and writes only .docguard.json with the standard profile and the detected type. It writes no documents. Example output on a small Express service:
🔍 DocGuard Init — Smart Mode
Detected existing project with code but no canonical docs.
...
Code-truth surface: 1 modules · 0 test files (0 cases) · 2 endpoints · 0 entities · 0 screens · 2 env vars
Documents to build (7):
docs-canonical/ARCHITECTURE.md (3 code section(s), 2 agent task(s))
docs-canonical/API-REFERENCE.md (1 code section(s), 1 agent task(s))
...
✅ Created: .docguard.json (profile: standard, type: api, 4 canonical doc(s) from the plan)
Any of these flags turns smart mode off and uses the template path instead: --skeleton, --profile <p>, --skip-prompts, --wizard, --fix.
The template path
The template path writes blank document templates for a profile, plus AGENTS.md, CHANGELOG.md, DRIFT-LOG.md, .docguardignore and .docguard.json. Existing files are skipped.
| Command | Use it when |
|---|---|
docguard init | You want DocGuard to scan first and propose (smart mode) |
docguard init --profile cli --skip-prompts | You already know the profile, and want templates without prompts |
docguard init --skeleton | You want blank templates and no scan |
docguard init --wizard | You want guided, interactive setup |
docguard init --fix | You want any missing required files created from templates, headless |
Smart mode does not create the tracking files or install agent skills. After step 4, run docguard init --fix once to add AGENTS.md, CHANGELOG.md, DRIFT-LOG.md, .docguardignore and any missing profile templates, and to install DocGuard’s skills for your coding agent.
Spec Kit and scaffolders
If the specify CLI (Spec Kit 0.11.2 or later) is on your PATH, the template path also initializes Spec Kit for your coding agent and registers the DocGuard extension. Pass --no-spec-kit to skip that. DocGuard’s own agent skills still install. The starter profile skips Spec Kit unless you pass --spec-kit.
--with <name> adds optional scaffolders after init finishes. Names are comma-separated: agents, hooks, ci, badge, llms, publish. Steps 8 and 9 use ci and hooks.
4. Generate the canonical documents from the code
Preview first:
docguard generate --plan
The plan lists the languages and frameworks found, the code-truth surface (modules, test files, endpoints, entities, environment variables), the documents it would build, and the prose tasks an agent must write. Add --format json for the machine-readable manifest, or --profile <p> to preview another profile’s document set.
Then write the skeleton:
docguard generate --plan --write
Each document is split into marked sections:
<!-- docguard:generated true -->
## Environment Variables
<!-- docguard:section id=env-vars source=code -->
| Variable | Required | Default | Source | Description |
| --- | --- | --- | --- | --- |
| `DATABASE_URL` | Yes | — | `src/server.js` | <!-- describe --> |
| `PORT` | No | `3000` | `src/server.js` | <!-- describe --> |
<!-- /docguard:section -->
## Setup Steps
<!-- docguard:section id=setup source=human -->
> **AI task:** Write the Prerequisites and Setup Steps (clone → install → run) for this stack.
<!-- docguard:pending agent writes this section -->
<!-- /docguard:section -->
source=codesections come from the same scannersguarduses: routes, entities, environment variables, modules, test files. Each row cites the source file it came from. Runningguardstraight aftergeneratereports nothing about DocGuard’s own output.source=humansections are prose that needs judgement: what the system is for, why the layers exist, how to set it up. DocGuard writes no prose. It leaves an AI task in each one. Have your coding agent write them, and review the result like any other change.docguard diagnoseprints fix prompts for every open issue, anddocguard fix --doc <name>prints the prompt for one document. See AI Integration.
Why this split: facts that can be read from code stay correct because a tool refreshes them (step 11), and the sentences that need a human are marked as such instead of being guessed.
generate --plan --write also adds the documents it creates to requiredFiles.canonical in .docguard.json. Re-running it is safe. It inserts missing sections and refreshes unpinned code sections. It never replaces prose in a source=human section, and it keeps a .bak of any document you edited.
Note:
guarddoes not report adocguard:pendingplaceholder. To list the prose still owed, search for the marker:grep -rn "docguard:pending" docs-canonical docs-implementation.
Note: If your documents live somewhere else and you mapped them with
docs.roles, DocGuard writes into a mapped document only inside onesource=codesection you added to it. The rules are in Configuration, under “Existing documentation layouts”.
Plain docguard generate (without --plan) also exists. It writes pre-filled documents in one step, and --force overwrites existing ones with a .bak copy. This guide uses --plan because you see the plan before anything is written.
How much DocGuard can read depends on the language. JavaScript, TypeScript and Python are read with a syntax tree. Go, Java, Kotlin, Ruby, Rust, PHP and C# are read by pattern, and the result is reported as partial. The per-language detail is in Languages.
5. Run guard and read the findings
docguard guard
Guard prints one line per validator, then each finding with its code, message and suggested fix:
⚠️ Traceability [MEDIUM] 3/4 checks passed
⚠ [TRC002] TEST-SPEC.md — exists but no matching source code found (unlinked doc)
→ Link a source file explicitly with a header annotation if the code lives in a non-standard location
// @doc TEST-SPEC.md
...
❌ FAIL — 29/37 passed, 3 blocking issue(s), 6 warning(s)
8 to fix · 1 to review (fix = DocGuard names the correction; review = the judgement is yours)
Check coverage: 12 of 31 validator(s) checked · 2 disabled · 19 no matches
How to work through a first run:
- Fix the structural errors first. Missing required files (STR001, STR002) block the gate and are mechanical.
docguard init --fixcreates them from templates. - Then the “to fix” findings. DocGuard names the correction.
docguard diagnoseturns them into prompts for your agent. - Then judge the “to review” findings. These are signals, such as a document that many commits have passed since its last review. The right answer can be “checked, still correct”.
- Look up any code you do not recognise with
docguard explain <CODE>, or in the finding reference.
The “Check coverage” line says how many validators actually checked something. A validator with “no matches” found nothing to check, which is not the same as passing.
Guard exits 0 on pass, 1 on blocking errors, 2 on warnings only, and 3 when the project has no .docguard.json. Every exit code, and the five fields on each finding, are explained in Troubleshooting. Every command and flag is in the Commands Reference.
You do not need to fix everything now. Fix what is cheap, then freeze the rest.
6. Freeze the existing findings with a baseline
docguard guard --update-baseline
git add .docguard.baseline.json
This writes .docguard.baseline.json at the repository root. From then on, guard, ci and report suppress the frozen findings and gate only new ones. The baseline covers errors and warnings alike.
Why now: the gate you turn on in step 8 should block new drift from the first day, without failing every pull request on findings that predate the adoption.
What is fingerprinted, for each finding:
- its code;
- its file path, without the line number;
- its message, with every number masked.
Line churn and changing counts (“13 commits since…”) therefore do not break the match. Each fingerprint also stores how many times it occurred: freezing one instance suppresses one, and a second instance of the same finding in the same file is reported.
Suppression is always visible:
📋 8 pre-existing finding(s) suppressed by .docguard.baseline.json — 8 warning (--no-baseline to show)
baseline is 0 day(s) old; 8 still suppressed.
guard --format json carries the same facts in baselineSuppressed, baselineBySeverity, baselineStillFiring, baselineStale and baselineAgeDays.
| To | Run |
|---|---|
| See everything, ignoring the baseline once | docguard guard --no-baseline |
| Ignore the baseline permanently | Set "baseline": false in .docguard.json |
| Drop entries for findings you fixed | docguard guard --update-baseline |
Note: Re-freezing captures everything that fires at that moment, including new findings. Run
docguard guard --no-baselineand read the list before you re-freeze.
Note:
--update-baselinecannot be combined with--changed-only, and exits1if you try. The baseline must freeze the full validator set.
The baseline does not change docguard score. More detail is in Configuration, under “Adoption baseline”.
7. Choose a profile and plan when to graduate
A profile sets which documents are required and which validators run.
| Profile | Required canonical documents | Use it for |
|---|---|---|
starter | ARCHITECTURE.md | Side projects and prototypes. Turns off drift, architecture, test-spec, security, environment and freshness checks |
standard (default) | ARCHITECTURE.md, DATA-MODEL.md, SECURITY.md, TEST-SPEC.md, ENVIRONMENT.md | Team projects. Architecture (import graph) and security (secrets) checks are off |
cli | ARCHITECTURE.md, TEST-SPEC.md, SECURITY.md, ENVIRONMENT.md | Command-line tools: no HTTP API or database assumed |
library | ARCHITECTURE.md, API-REFERENCE.md, TEST-SPEC.md | Packages with a public API and no server or database |
enterprise | The standard set | Regulated or critical systems. Every validator on, including architecture and security |
Every profile also requires an agent file (AGENTS.md or CLAUDE.md), CHANGELOG.md and DRIFT-LOG.md.
Smart mode writes standard. To choose another profile at the start, run docguard init --profile <p> --skip-prompts instead of plain init, then step 4. generate --plan --write fills the code sections into the profile’s templates.
Note: Settings in
.docguard.jsonoverride the profile.initwrites an explicitvalidatorsblock and arequiredFiles.canonicallist, so changing"profile"alone does not change what those keys already set. When you graduate (for example fromstandardtoenterprise), editvalidatorsandrequiredFiles.canonicalas well. Then rundocguard guardto see the effect.
When a validator genuinely does not apply, declare it rather than switch it off. For example, add this line to a canonical document or to AGENTS.md:
<!-- docguard:validator testSpec n/a — POC, no automated tests yet -->
The validator then shows as N/A with your reason, in the repository, instead of silently disappearing. To soften one noisy finding code without turning off its validator, set "findingSeverity": { "<CODE>": "low" }. The finding stays visible but never fails the build.
docguard score --tax estimates the weekly maintenance cost of the current profile. See Profiles and Configuration.
8. Turn on CI
Pick one of the two options below. Both need the full git history for the history-based checks.
Option A: the GitHub Action.
Replace
X.Y.Zwith the release you pin; Installation shows how to find the latest.
name: DocGuard
on: [pull_request, push]
permissions: { contents: read, pull-requests: write }
jobs:
docguard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: raccioly/docguard@vX.Y.Z
with:
command: guard
The action installs the docguard-cli version it was released with, so pinning the action pins the CLI. On warnings it prints a ::warning and passes. Set fail-on-warning: 'true' to fail instead. On pull requests it adds inline annotations and a sticky comment by default (the comment needs pull-requests: write).
Option B: a generated workflow.
docguard init --with ci --skip-prompts
This writes .github/workflows/docguard.yml, which installs a pinned CLI and runs docguard ci --format json --threshold 70 --no-history. The job exits with ci’s exit code, so warnings (exit 2) fail it as well as errors.
Note:
--thresholdcompares the score fromdocguard score, which the baseline does not change. Rundocguard scorefirst and set the threshold at or below today’s number, or the first run fails.
Start lenient: block on errors only, and treat warnings as information. Once the baseline has shrunk, add fail-on-warning (Action) or --fail-on-warning (docguard ci). GitLab, Jenkins, Azure DevOps and other CI systems are covered in CI.
9. Add local Git hooks (optional)
Hooks catch drift before it reaches a pull request.
docguard init --with hooks --skip-prompts # all three hooks
docguard init --with hooks --type pre-commit --skip-prompts # one hook
| Hook | What it runs | Blocks when |
|---|---|---|
pre-commit | docguard guard | Guard exits 1. Warnings (2) and a project with no .docguard.json (3) are allowed through |
pre-push | docguard score --format json | The score is under 60, or the command fails |
commit-msg | A conventional-commit format check | The message does not match |
--skip-prompts matters on an adopted project: without it, init asks the document questions again before installing the hook. Skip a hook for one commit with git commit --no-verify.
If your repository uses husky, lefthook or simple-git-hooks, DocGuard does not overwrite the manager’s dispatcher. It prints where to add the command instead: npx docguard-cli guard --changed-only for pre-commit and npx docguard-cli ci for pre-push. --changed-only runs a smaller, fast validator set on the files changed since HEAD~1 (or --since <ref>).
The pre-commit framework is also supported, with the docguard-guard (changed-only) and docguard-guard-full (pre-push) hook IDs:
repos:
- repo: https://github.com/raccioly/docguard
rev: vX.Y.Z
hooks: [{ id: docguard-guard }]
10. If you use Spec Kit
Spec Kit’s own advice for existing projects holds here too: specify the next bounded change, not the whole system. DocGuard adds two things for existing code.
As-built specs for areas that have none. For one directory, DocGuard proposes a spec built from facts it can read without a model: routes, exported JavaScript and TypeScript symbols, environment variables read, and data entities.
docguard generate --spec src/billing # preview the candidates
docguard generate --spec src/billing --write # create and register the spec
Each fact becomes one FR-NNN candidate, with a docguard:fact marker and the file and line it came from. DocGuard writes no requirement prose; every statement is a task for your agent. --write creates the spec in the next free feature directory (specs/NNN-as-built-src-billing/spec.md) and registers it in .docguard-specs.json with origin as_built. From then on, guard’s SPR007 reports facts under that path that the spec neither covers nor lists as out of scope.
Do this for an area you are about to change, not for every directory up front.
The spec registry. docguard specs --write refreshes the observed evidence in .docguard-specs.json and keeps your reviewed fields. Add docguard specs --check to CI. It exits 2 when the committed registry is missing, stale or inconsistent.
The extension hooks, the lifecycle commands (specs approve, specs complete) and the spec-first gate are in Spec Kit.
11. Decide how the documentation will age
After the first pull request, agree on how each kind of document stays current.
| Kind of content | Keeps current through | Commands |
|---|---|---|
Code-truth sections (source=code) | Regenerated from the scanners | docguard sync previews; docguard sync --write applies; --since <ref> limits it to sections whose sources changed |
| Prose that describes specific code | A recorded review, re-opened when that code changes | Add covers="src/pricing.mjs#discount" to the section marker, then docguard review --accept <doc>#<id> --reason "<what you checked>". Guard reports DLK001 when the covered code changes |
| Other prose | Freshness review signals (FRS findings) | Review the document, then record the date with <!-- docguard:last-reviewed YYYY-MM-DD --> under its first heading |
| Records of past decisions | Marked historical, so currentness checks skip them | <!-- docguard:status historical --> |
| Documents nobody should load anymore | Retired from active agent context, recoverable from git | docguard retire --plan, then docguard retire --write --path <doc> --reason "<why>" |
docguard review --suggest <doc> proposes covers= values from the paths a document already mentions. Its suggestions are low confidence, and it never writes. The section-marker syntax is in Doc Sections.
Finally, schedule the baseline burn-down. The guard output reports how many baselined findings no longer occur. When that count is not zero, review with --no-baseline and re-freeze.
What the first pull request contains
.docguard.jsonwith the profile and validators you chose.- The canonical documents, code-truth sections filled, prose written by your agent and reviewed by you (or still marked
docguard:pending, if you chose to merge the skeleton first). AGENTS.md,CHANGELOG.md,DRIFT-LOG.md,.docguardignore..docguard.baseline.json.- The CI workflow, and optionally hooks configuration and agent skills.
docguard guard passes on the branch, and new drift fails the gate from the next pull request on.
Next steps
- CI: other CI systems, SARIF and JUnit output.
- Spec Kit: the full spec lifecycle.
- Monorepos: several packages in one repository.
- Troubleshooting: exit codes, false positives, partial coverage.