# AI Agent Instructions — DocGuard > This project follows **Canonical-Driven Development (CDD)**. > Documentation is the source of truth. Read before coding. > DocGuard is an official [GitHub Spec Kit](https://github.com/github/spec-kit) community extension. ## Workflow 1. **Read** `docs-canonical/` before suggesting changes 2. **Check** existing patterns in the codebase 3. **Run** `docguard diagnose` to see what needs fixing 4. **Confirm** your approach before writing code 5. **Implement** matching existing code style 6. **Log** any deviations in `DRIFT-LOG.md` with `// DRIFT: reason` 7. **Verify** with `docguard guard` — all checks must pass ## Project Stack - **Language**: JavaScript (ES modules) - **Runtime**: Node.js 18+ - **Dependencies**: One — `@babel/parser` (exact-pinned, optional-load); Node.js built-ins otherwise - **Testing**: `node:test` (built-in) - **Distribution**: npm + PyPI; the release also publishes the MCP server image to GHCR, the Claude Desktop MCPB bundle and the Homebrew tap - **Version**: see `package.json` (single source of truth — do not hardcode here) ## Key Files | File | Purpose | |------|---------| | `docs-canonical/ARCHITECTURE.md` | System design | | `docs-canonical/DATA-MODEL.md` | Config, file formats and output contracts | | `docs-canonical/SECURITY.md` | Auth & secrets | | `docs-canonical/TEST-SPEC.md` | Test requirements | | `docs-canonical/ENVIRONMENT.md` | Environment setup | | `docs-canonical/REQUIREMENTS.md` | Spec-kit aligned requirements | | `CHANGELOG.md` | Change tracking | | `DRIFT-LOG.md` | Documented deviations | ## Commands `docguard --help` is the authoritative list (counts intentionally not hardcoded here — they drift). The surface, grouped as `--help` shows it: **The Daily 5** — `init` (bootstrap + scan), `guard` (CI gate, all validators), `diff` (doc↔code gaps; `--since ` for changed-file impact), `sync` (refresh code-truth sections), `score` (CDD maturity 0-100). **Tools** — `demo` (zero-install tour), `diagnose` (guard → AI fix prompts), `fix` (AI fix instructions; `--doc `), `generate` (reverse-engineer docs; `--plan`; `--spec ` for an as-built Spec Kit spec), `agent` (task graph; `--task ` for bounded current evidence), `retire` (remove reviewed docs from active context), `specs` (check/refresh/preflight/approve/complete the spec lifecycle registry; `require` for the spec-first gate; `reanchor` for a squash-discarded revision), `reconcile` (classify code/spec changes since a Git ref without rewriting intent), `review` (doc sections whose covered code changed; `--accept ` records a document review with a fingerprint per referenced file, `--accept #` a section review; `--prune`, `--suggest`), `anchors` (code→requirement links and whether each changed since review; `accept` records a review), `rules` (which instruction files each agent harness loads for a path; `--for `), `explain` (explain a validator/warning), `verify` (declared evidence; `--semantic` claims), `ignore` (every suppression with its reason and expiry), `feedback` (report false positives), `mcp` (MCP server; stdio or loopback HTTP; tool calls stay inside the served directory and each `--root`), `report` (compliance bundle), `ci` (guard + score gate), `policy` (which advisory codes CI history shows are ready to promote), `memory` (what DocGuard remembers; `--pack [--symbols]` for the context pack; `--export`/`--consolidate aidlc` for AWS AI-DLC memory), `trace` (requirements traceability; `--reverse`; `--owners` for the doc ownership map; `--matrix` exports requirement → anchor → test), `upgrade` (check npm for a newer release and migrate the config schema; `--apply` also refreshes out-of-date agent files and the registered Spec Kit extension; `--apply --schema-only` stays offline), `watch` (live re-guard). `sensor --aidlc` speaks AWS AI-DLC's sensor contract for the DocGuard AI-DLC plugin, and stays out of `--help` because it is meant for machines. `score --dora` maps DocGuard's evidence onto DORA's documentation and AI capabilities. **`init --with `** scaffolders — `agents`, `hooks`, `ci`, `badge`, `llms`, `publish` (also reachable as standalone deprecation aliases). **Deprecation aliases** — `setup` → `init --wizard`; `audit` → `guard` (permanent); `impact` → `diff --since`. ## Consuming Guard Output (agents) Prefer the machine contract over parsing prose: `docguard guard --format json` returns `status` (PASS/WARN/FAIL, matches exit code 0/2/1 — but FAIL exits `3`, not `1`, when the project has no `.docguard.json`, so branch on `status` rather than assuming `1`), `findings[]` (`{code, severity, confidence, disposition, evidence, parserTier, message, location, suggestion}`), `nextStep`, `reportable[]` (findings whose code is unmeasured or whose confidence is low — verify before acting), `coverage` (Markdown tier map incl. the `specKit` tier and `unclassified[]`), `evidence`, and `semanticClaims.count` (documented numbers not yet verified against code). When a command is launched from an implicitly selected nested package, inspect stderr for repository-root guidance. Human output supplies an exact rerun; machine modes emit a `docguard.repository-root-guidance` JSON diagnostic there so stdout remains a valid JSON/SARIF/JUnit artifact. The current command still checks only the selected package unless it is explicitly rerun with `--dir`. - Every structured finding has a stable code (`STR001`, `ENV003`, `XRF002`, …). `docguard explain ` gives the contract and fix. - Triage on `disposition`, not on severity alone. `act` means DocGuard asserts a defect and names the correction — apply it. `escalate` means DocGuard observed a signal and the judgement is yours; read the source before changing anything. A high-confidence finding can still be an escalation: FRS002 counts commits exactly and still does not establish that the document is stale. - Read `evidence.status` before trusting a confidence label. `measured` quotes the reviewed corpus with `n` and a Wilson interval; `not-measured` means the label is a maintainer's prior that has never been checked against labelled cases. Most codes are unmeasured — that does not make their findings wrong, only unverified. - Read `parserTier` when a finding concerns source code. `regex-fallback` or `fallback-language` means an AST was unavailable for that file, so absence of a finding is weak evidence; the owning validator also reports `partial`. - When `evidence.configured` is true, inspect `docguard verify --evidence --format json` first. A `verified-within-scope` result covers only its selected statement. Contradictions can indicate code regressions from approved intent; stale reports must be regenerated from their declared inputs. - Mechanical fixes go through `docguard fix --write` (provenance-checked, fail-closed) — never hand-apply what the tool fixes deterministically. - Genuine false positives: suppress at the site with `// docguard:ignore ` (reason required) or ``, and report them via `docguard feedback`. - Doc≠code does not mean the doc is wrong — canonical docs are the spec. If the code regressed from a documented decision, fix the code or log a `// DRIFT: reason` + DRIFT-LOG.md entry instead of rewriting the doc. - Treat `specs/` and planning docs as active intent only. Review candidates with `docguard retire --plan`; retire only explicit, clean tracked documents after their shipped outcomes are represented in current docs and `CHANGELOG.md`. - Run `docguard specs preflight` before drafting and `docguard specs preflight --path ` before planning. Refresh `.docguard-specs.json` with `specs --write` and record approval with `specs approve --id --write`; never edit generated intent/evidence fields or treat bare requirement IDs as completion proof. - Run `docguard reconcile --since --format json` before completing a changed feature. Only its mechanical write plan may run automatically; unsupported changes and possible regressions require review. `docguard specs complete` records that review and regenerates the active context projection. - For a concrete implementation task, prefer `docguard agent --task --format json`. Follow only selected current evidence, retain its retrieval-only assurance, inspect additional repository facts as needed, and fall back to normal discovery when `selection.status` is `abstained`. ## AI Skills DocGuard provides enterprise-grade AI behavior protocols via the Spec Kit extension: | Skill | Purpose | |-------|---------| | `docguard-guard` | 6-step quality gate with severity triage and structured reporting | | `docguard-fix` | 7-step research workflow with validation loops (max 3 iterations) | | `docguard-review` | Read-only semantic cross-document consistency analysis | | `docguard-score` | CDD maturity assessment with ROI-based improvement roadmap | | `docguard-sync` | Mechanical code-truth refresh with explicit prose review boundaries | Skills are located at `extensions/spec-kit-docguard/skills/*/SKILL.md`. They tell agents **how to think**, not just what to run. ## Spec Kit Hooks DocGuard integrates into the spec-kit workflow. `extensions/spec-kit-docguard/extension.yml` is authoritative; a hook is mandatory when its `optional` field is `false`. | Hook | Command | Required? | |------|---------|-----------| | `before_specify` | `speckit.docguard.brief` | Mandatory | | `before_tasks` | `speckit.docguard.preflight` | Mandatory | | `after_tasks` | `speckit.docguard.score` | Optional | | `after_implement` | `speckit.docguard.guard`, then `speckit.docguard.complete` | Mandatory, then optional | | `after_converge` | `speckit.docguard.complete` | Optional | ## Extension Structure ``` extensions/spec-kit-docguard/ ├── skills/ # AI behavior protocols (docguard-guard, -fix, -review, -score, -sync) ├── scripts/bash/ # Orchestration scripts (--json output) ├── commands/ # Spec Kit slash commands ├── templates/ # Hook registration templates └── extension.yml # Commands, scripts, hooks declaration ``` ## Rules - **Spec-first (Constitution X).** A change to `cli/**` runs the Spec Kit pipeline: `specs/###-slug/` spec → plan → tasks → implement. The PR description names the spec (`specs/015-spec-first-gate` or its Spec ID), or declares one line `Spec-Exempt: — `. CI enforces this with `docguard specs require --since origin/main --message-file `. When the area being changed has no spec, write an as-built spec scoped to that area first. - **Changes must not make DocGuard slower, noisier or heavier by accident.** The CI `budget` job compares the PR with its base on one runner: guard wall time (×1.15, 150 ms floor), new error findings on this repository, agent-facing bytes (context pack, task packets, MCP responses, llms bundles; +10%), package weight and runtime dependencies. A cost the change needs is declared in the PR body as `Budget-Exempt: — `. Reproduce locally with `npm run budget -- --base ` (specs/031-non-regression-budgets). The CI test job fails any single test over 100 seconds at reference-runner speed (`TEST_CEILING_MS`, `tools/suite-runtime-budget.mjs`); the suite total is reported, not gated (specs/051-release-cut-green FR-007, TEST-SPEC.md). A change to a parser that reads documents runs `npm run fuzz` (seeded fuzzing: no throw, same answer twice, bounded time; specs/088-validator-fuzz). - **Close a spec in one commit, with no hand edit of the registry.** Tick the last task, run `docguard specs --write`, then `docguard specs complete --id --since --canonical-doc --persistence living --reason --write` (`specs approve` takes the same two flags) and commit once: the tick, the registry record and the outcome land together, so a gate that runs `guard` on every commit never sees DLC002 or a half-written record. The record carries the git blob of every evidence file, so it survives a squash merge or a rebase and needs no second "reanchor" commit, as long as the base branch did not change those evidence files after your last `specs complete`; if it did, merge the base in and run `specs complete` again before merging. Run `docguard specs reanchor` only for a record guard reports as SPR008 (`specs/101-completion-one-commit`). Adding the `@implements`/`@req` tags for the spec being completed does not need a commit of its own: run `specs --write` after tagging and `complete` accepts those edits (any other edit still blocks). - **A change to a closed spec's evidence closes the spec again, in the same pull request.** Guard reports SPR011 (a warning, so strict gates fail on it) when an implementation or test file a `verified` spec recorded has changed since its last review; canonical documents and hub files (four or more specs list them) are not compared. Do not commit the edit first: run the command with the edit uncommitted and commit both together, so a gate on every commit stays clean. Run `docguard specs complete --stale --reason "" --write`: it closes every flagged spec in one transaction, keeps each spec's canonical documents and persistence, and writes nothing when one is blocked. Commit it with the change (`specs/103-completion-upkeep`). - **A requirement with no code target (documentation, a one-time outcome) takes evidence from a test that checks the result.** Use `describes()` (each named document carries the terms the requirement asks for) and `recorded()` (the spec's Verification Notes answer each measured criterion) as `tests/program-verification.test.mjs` does, and tag the test `@req #`; put it in a test file of its own, because every spec tagged in a file is re-flagged by SPR011 when that file changes. - **PR-first workflow — no direct-to-main commits.** Create a branch (`git checkout -b /`), push, `gh pr create`, let CI run, self-review, squash-merge. Tag releases only after merge on `main`. The only acceptable direct-to-main: typo fixes in comments or README badge URLs. - Never commit without updating CHANGELOG.md - Regenerate checked-in generated files in the same change; each has a drift test that fails and names its command: `npm run llms` after editing a canonical or optional doc (`llms.txt`, `llms-full.txt`), `npm run docs:reference` after changing a finding code or a validator's display name (the docs site's finding-code reference), and `npm run generate:precision-evidence` after a reviewed benchmark baseline change (`cli/precision-evidence-data.mjs`). - If code deviates from docs, add `// DRIFT: reason` - Security rules in SECURITY.md are mandatory - Test requirements in TEST-SPEC.md must be met - **Tests that build Git repositories pass the fixture options per call.** Every fixture `git` call goes through `tests/helpers/git.mjs` (`FIXTURE_GIT`, `fixtureGit`): hooks and signing off for that call, never for the whole environment, so DocGuard's own Git calls still meet the machine's config. A scan test rejects a direct `git` spawn without a `fixture-git-exempt:` reason, and the CI `hermeticity` job runs the suite under a hostile global config (specs/066-test-git-hermeticity). - **Workflow jobs run code without a repository write token.** Workflows default to `contents: read`; a job asks for its own scope. A job that runs tests or DocGuard holds no write permission and checks out with `persist-credentials: false`; a job holding `contents`, `actions`, `pull-requests` or `issues` write runs no package manager, build tool or test. `tests/workflow-permissions.test.mjs` enforces it (specs/068-release-least-privilege). - **A changed requirement link is reviewed, not silenced.** This repository commits `.docguard-anchor-lock.jsonl`, so guard reports a link whose code or requirement changed since its review (ANC001, ANC002) or whose tag is gone (ANC003); ANC001 and ANC003 fail guard for a link accepted as reviewed. Read the change, then run `docguard anchors accept --link ''` (or `--file --bulk` for a file you changed) with a `--reason` that says what you checked. Accept only links in files your change touched. The lock merges with `merge=union`, so a merge or rebase never conflicts in it. When both sides reviewed one link, DocGuard keeps the line that matches the code now and ignores the other; a link is reported only if neither matches, and you accept it as above. Run `docguard anchors merge --write` to write one line per link (it also resolves git conflict markers if the lock ever conflicts). Do not restore `origin/main`'s lock by hand. CODEOWNERS sends every lock change to a maintainer (specs/070-requirement-anchors, CI-RECIPES.md Recipe 3f). - Run `docguard guard` before pushing — all checks must pass - Standalone file writes use `safeWrite()` with backups before overwrite. Lifecycle operations spanning multiple files use `commitFileTransaction()` so preparation, rollback, and post-write validation cover the complete set. ## Agent Rules ### Automated agents / bots (Google Jules and any auto-PR agent) - **Jules runs only the routines in `.jules/routines/`** (`bug-hunt`, `test-gaps`, `docs-drift`, weekly), after `.jules/RULES.md`. Each starts with `node .jules/preflight.mjs `; on `STOP` it ends with no PR. **Never ask a question:** take the pessimistic in-scope option, or end the task with a summary and no PR. - **One PR per routine target**, titled `[jules:] — …`. `.github/scripts/jules-policy.mjs` (via `jules-triage.yml`) closes a PR that is empty, a duplicate of an open PR, work declined in the last 60 days, over the five-PR queue, or off its routine's contract. It never closes a PR for its subject: a security or performance fix reaches a maintainer. - **Only new test files merge alone.** `auto-merge.yml` merges a green Jules PR only when every file is a newly added `tests/.test.mjs`, pinned to the head CI verified. Edited tests, fixtures, any Markdown (instruction and prompt files ship to npm), code and CI wait for a maintainer. - **Agents never edit an existing test, `CHANGELOG.md`, `.github/`, `.jules/` or instruction files**, and never mention an agent by handle: in Jules' Reactive Mode a mention is an instruction. Setup: `bash .jules/setup.sh` (fails unless the suite and guard are green). Spec: `specs/067-jules-policy`. ### Dependencies - Never add a package without first verifying it exists on the official registry (npm/PyPI). - Always pin to exact versions in `package.json` and `requirements.txt`. No ^, ~, or >= ranges. - Prefer packages with >10k weekly downloads and >1 maintainer. - If you suggest a package, confirm its first-publish date is older than 30 days. - Never modify .npmrc, pnpm-workspace.yaml, or dependabot.yml without explicit user confirmation. ### CI/CD - Never write a workflow using `pull_request_target` with checkout of PR-controlled refs. - Always pin third-party GitHub Actions to commit SHA, not @v1 or @main. ## Claim probing (TestGuard) `testguard.claims.json` states what this codebase guarantees, as claims with mechanical faults. `testguard probe` injects each fault and reports the ones the suite fails to detect — a test that pins a *defect* stays green under coverage, so this is the check that catches it. This suite runs on `node --test`, which ships no `json` reporter, while TestGuard reads Jest-shape JSON. `tools/node-test-json-reporter.mjs` translates between them, and `testguard.config.json` records how the tests run (the reporter command and `node_modules`), so the commands are plain: ```bash # the claims a change can affect (faults run in parallel by default) npx testguard-cli@0.26.0 probe --changed origin/main # everything (nightly or before a release): the same without --changed; never --serial npx testguard-cli@0.26.0 probe ``` An empty `--changed` selection is not a pass: it lists the changed source files no claim covers. `probe --changed --explain` prints which claims a change selects and why, and probes nothing. On a machine whose `~/.npmrc` sets `min-release-age`, `npx testguard-cli@0.26.0` fails with ETARGET until the release is old enough (2026-11-20 for 0.26.0); use a Homebrew install (`brew upgrade testguard`) and run `testguard probe ...` instead. The reporter in `testguard.config.json` starts with `./`: Node loads a `--test-reporter` value without one as a package name and the defenders fail to load. The probe runs in a scratch worktree built from a commit, so the reporter must be a tracked file. `npx testguard-cli@0.26.0 gate --changed origin/main` reports changed source files that carry no claim. It does not find bugs; it refuses to let a change land without stating what must be true, which is the step that surfaces them. A file the probe cannot observe (for example, one that only runs as a subprocess) is excused in `testguard.ignore.json`, with a reason that names what protects it instead. ## Evidence and contributions A structural score is a maturity proxy. Preserve `assurance` and nullable factual accuracy in automation; a clean guard does not establish arbitrary prose correctness. Review human intent separately from generated code facts. To challenge any finding, run `docguard feedback --code --preview`, inspect the metadata-only public draft, and check the supplied search link for existing open and closed work. Contribute a synthetic failing example paired with a neighboring valid case. Submission remains opt-in.