# docguard
> The enforcement tool for Canonical-Driven Development (CDD). Audit, generate, and guard your project documentation.

<!-- llms-full.txt — full-content form. The link-index form is llms.txt. -->
<!-- Generated by DocGuard (docguard llms --full). Regenerate after doc changes. -->

---

## docs-canonical/ARCHITECTURE.md
> System architecture, component boundaries, and tech stack

# Architecture

<!-- docguard:version 0.6.0 -->
<!-- docguard:status active -->
<!-- docguard:last-reviewed 2026-09-11 -->

| Metadata | Value |
|----------|-------|
| **Status** | ![Status](https://img.shields.io/badge/status-active-brightgreen) |
| **Version** | `0.6.0` |
| **Last Updated** | 2026-05-31 |
| **Project Size** | ~24K lines across `cli/` |

---

## System Overview

DocGuard is a near-zero-dependency Node.js CLI tool. It carries one exact-pinned npm runtime dependency, `@babel/parser`, for AST-accurate JS/TS parsing, and uses the developer's own `python3` (no pip/npm dependency) for an AST-accurate Python tier. Both AST tiers load **optionally** with a per-file regex fallback, so the CLI stays robust when a parser is absent — they make JS/TS and Python full-support languages while every other language stays on the regex (beta) tier. It enforces **Canonical-Driven Development (CDD)** — a methodology where documentation is the source of truth. DocGuard audits, scores, and guards project documentation. It generates AI-actionable fix prompts and integrates with CI/CD pipelines.

It targets development teams and AI coding agents that need to maintain documentation quality across projects of any stack (JavaScript, Python, Java, etc.).

## Component Map

| Component | Responsibility | Location | Key Files |
|-----------|---------------|----------|-----------|
| **CLI Entry Point** | Argument parsing, config loading, command routing | `cli/` | `docguard.mjs` |
| **Commands** | User-facing commands (the Daily 5 — init/guard/diff/sync/score — plus situational tools: diagnose, fix, generate, trace, explain, verify, feedback, retire, memory, agent, mcp, upgrade, watch, demo, and `init --with` scaffolders) | `cli/commands/` | `*.mjs` |
| **Document lifecycle** | Finds exact terminal-status docs and completed-task review candidates; explicit retirement removes documentation from active context only after its source revision is reachable from a retained Git ref | `cli/scanners/document-lifecycle.mjs`, `cli/validators/document-lifecycle.mjs`, `cli/commands/retire.mjs` | Scanner is read-only; completion remains advisory; only the command writes after explicit selection |
| **Validators** | Independent validation modules that check specific aspects of CDD compliance — all emitting structured findings with stable codes (the `CODES` registry in `findings.mjs`) | `cli/validators/` | `*.mjs` |
| **Scanners** | Project file scanners for test discovery, route detection, schema mapping, CDK/IaC, doc-tools, integrations, frontend surface, spec-kit, memory-plan, semantic claims, agent readability | `cli/scanners/` | `*.mjs` |
| **Writers** | Deterministic doc-mutation and output modules — section-addressable edits, mechanical fix registry, API-Reference writer, generate I/O + doc builders (split from generate.mjs), SARIF emitter (no LLM) | `cli/writers/` | `mechanical.mjs`, `sections.mjs`, `api-reference.mjs`, `generate-io.mjs`, `doc-generators.mjs`, `sarif.mjs` |
| **Config** | Configuration loading — defaults, `.docguard.json` merge, profile presets, project-type detection (extracted from the entry point to keep the import graph acyclic) | `cli/` | `config.mjs` |
| **Shared** | Cross-cutting utilities — ignore/glob filters, source-root resolution, Git helpers, declaration-shaped requirement identity parsing, and the shared doc→code trace patterns used by both `trace` and the Traceability validator | `cli/` | `shared-ignore.mjs`, `shared-source.mjs`, `shared-git.mjs`, `shared-requirements.mjs`, `shared-trace-patterns.mjs`, `shared.mjs` |
| **Templates** | Document skeletons (ARCHITECTURE, SECURITY, etc.) and slash command files for AI agents | `templates/` | `*.template`, `commands/*.md` |
| **Extension** | Spec Kit extension with 5 AI skills, 4 bash scripts, workflow hooks | `extensions/spec-kit-docguard/` | `skills/*/SKILL.md`, `scripts/bash/*.sh` |
| **Tests** | Per-validator unit tests + command-level integration tests using `node:test` | `tests/` | `*.test.mjs` |

## Tech Stack

| Category | Technology | Rationale |
|----------|-----------|-----------|
| Language | JavaScript (ES Modules) | Universal runtime, zero-friction `npx` usage |
| Runtime | Node.js ≥ 18 | Native `node:test`, `node:fs`, `node:child_process` |
| Dependencies | **One npm dep** — `@babel/parser` (exact-pinned, optional-load) | AST-accurate JS/TS parsing; minimal, vetted supply-chain surface |
| Optional external | `python3` (the developer's own) | AST-accurate Python parsing; not an npm/pip dependency, regex fallback when absent |
| Package Manager | npm | Standard for Node.js CLIs |
| Testing | `node:test` + `node:assert` | Built-in, no test framework dependency |
| Docker | `Dockerfile` (MCP server image) | Published to GHCR for stdio MCP use; HTTP transport is also available with explicit configuration |

### Recognized Config Files

DocGuard recognizes and validates these project config files:

| File | Purpose |
|------|---------|
| `.docguard.json` | Project-level DocGuard configuration |
| `.docguardignore` | Per-project file exclusions (like `.gitignore`) |
| `vitest.config.ts` / `jest.config.ts` | Test runner config (scanned for custom test patterns) |
| `.storybook/` | Component documentation tool (detected for docs-coverage) |
| `.jules-setup.sh` | This repo's own Google Jules environment bootstrap script (internal tooling, not shipped) |
| `.pre-commit-hooks.yaml` | This repo as a pre-commit hook source — consumers reference `repo: raccioly/docguard` to run `docguard-guard` (changed-only) per commit |
| `glama.json` | Glama MCP directory metadata — declares repo maintainers so the Glama listing can be claimed/managed |
| `server.json` | Official MCP Registry manifest (`io.github.raccioly/docguard`) — server name, npm package, stdio transport |

## Layer Boundaries

The architecture separates command orchestration, validation, extraction, output, configuration, and shared utilities. The boundaries below describe responsibilities and permitted dependencies.

| Layer | Contains | Can Import From | Cannot Import From |
|-------|----------|----------------|--------------------|
| **Extension** (`extensions/spec-kit-docguard/`) | AI skills (SKILL.md), bash scripts, hooks, commands | CLI (via npx), Node.js built-ins | Isolated — spec-kit integration layer |
| **Commands** (`cli/commands/`) | User-facing command logic | Validators, Config (via `docguard.mjs` exports) | Isolated — each command is self-contained |
| **Validators** (`cli/validators/`) | Independent validation modules | Scanners, Shared utilities, Node.js built-ins | Cannot import from Commands or Writers |
| **Scanners** (`cli/scanners/`) | Project intelligence — detect routes, schemas, IaC, frontend surface | Shared utilities, Node.js built-ins | Cannot import from Validators, Commands, Writers |
| **Writers** (`cli/writers/`) | Mutate canonical docs surgically (section-addressable, no LLM) | Shared helpers, Scanners for generated content, Node.js built-ins | Cannot import from Commands or Validators |
| **Shared** (`cli/shared-*.mjs`) | Cross-cutting utilities: ignore/glob filters, source-root resolution, git helpers, shared trace patterns | Node.js built-ins only | Cannot import from any other layer |
| **Config** (`cli/config.mjs`) | `loadConfig` + defaults/profile merge + project-type detection | Shared utilities, Node.js built-ins | Cannot import from Commands (extracted so `demo`→`docguard` is no longer a cycle) |
| **Entry Point** (`cli/docguard.mjs`) | ANSI colors, argument parsing, command dispatch, banner/help | Commands, Config (`loadConfig`) | Calls validators only through commands |

### Key rule

**Key Rule**: Validators are pure functions. They receive `projectDir` and `config`, then return results. They stay isolated from commands and the CLI entry point. The Extension layer operates independently, using the CLI as an external tool.

### Layer graph

```mermaid
graph TD
    A["CLI Entry Point<br/>docguard.mjs"] --> B["Shared Constants<br/>shared.mjs"]
    A --> C["Commands<br/>cli/commands/*.mjs"]
    C --> B
    C --> D["Validators<br/>cli/validators/*.mjs"]
    D --> E["Node.js Built-ins<br/>fs, path, child_process"]
    C --> E
    A --> F[".docguard.json<br/>Project Config"]
    D --> G["docs-canonical/<br/>Canonical Docs"]

    style A fill:#4a9eff,color:#fff
    style B fill:#6c757d,color:#fff
    style C fill:#28a745,color:#fff
    style D fill:#ffc107,color:#000
    style F fill:#17a2b8,color:#fff
    style G fill:#e83e8c,color:#fff
```

## Data Flow

### Request Lifecycle: `docguard guard`

```
User runs: npx docguard guard
     │
     ▼
docguard.mjs
  ├── parseArgs(process.argv)      → flags: { format, dir, ... }
  ├── loadConfig(projectDir)       → .docguard.json → merged with defaults
  │     ├── Reads .docguard.json
  │     ├── Reads package.json (name, type detection)
  │     └── Merges: defaults ← config ← CLI flags
  │
  ▼
guard.mjs
  ├── For each enabled validator:
  │     ├── structure.mjs    → checks docs-canonical/ exists, required files present
  │     ├── docs-sync.mjs    → checks DocGuard metadata headers
  │     ├── drift.mjs        → checks DRIFT-LOG.md for staleness
  │     ├── changelog.mjs    → checks Unreleased section, version entries
  │     ├── architecture.mjs → validates component map, layer boundaries
  │     ├── test-spec.mjs    → checks test framework, coverage docs
  │     ├── security.mjs     → checks auth, secrets documentation
  │     ├── environment.mjs  → checks setup steps, env vars documentation
  │     └── freshness.mjs    → checks git commit dates vs doc last-modified
  │
  ├── Collects: { pass: [...], warn: [...], fail: [...] }
  │
  ▼
Output (text | json)
  └── Exit code: 0 (pass) | 1 (fail) | 2 (warn)
```

### AI Fix Flow: `docguard fix --doc architecture`

```
fix.mjs
  ├── Looks up DOC_EXPECTATIONS['docs-canonical/ARCHITECTURE.md']
  ├── assessDocQuality(content, expectations)
  │     └── Checks: line count, placeholder count, content quality signals
  ├── Outputs: TASK, PURPOSE, RESEARCH STEPS, WRITE THE DOCUMENT
  │
  ▼
AI Agent (Claude Code, Cursor, Copilot, etc.)
  ├── Reads stdout (the research instructions)
  ├── Executes research: reads package.json, scans directories, maps imports
  ├── Writes docs-canonical/ARCHITECTURE.md with real content
  │
  ▼
docguard guard → validates the newly written document
```

## Key Design Decisions

| Decision | Rationale |
|----------|-----------|
| **Minimal dependencies** | One exact-pinned, vetted runtime dep (`@babel/parser`) earns its place by fixing silent regex truncation; it loads optionally so installs stay robust. Everything else is Node.js built-ins. |
| **Config-driven validation** | `.docguard.json` lets projects customize which validators run. A CLI project can skip database docs. |
| **Validators are independent** | Each validator is a self-contained module. Adding a validator keeps existing ones stable. |
| **AI as author, CLI as orchestrator** | The CLI detects problems and generates structured prompts. Documentation writing is the AI's responsibility. |
| **Exit codes for CI** | `0` (pass), `1` (fail), `2` (warn) enables `docguard ci` to gate deployments. |

---

## External Dependencies

DocGuard declares one exact-pinned runtime dependency, `@babel/parser`. It loads optionally: installations without Babel use a less precise regex fallback. The modules below supply the remaining runtime functionality.

| Module | Usage |
|--------|-------|
| `node:fs` | File system operations (read docs, check existence) |
| `node:path` | Path resolution and manipulation |
| `node:child_process` | Git operations (freshness checks) |
| `node:url` | ES Module URL resolution |
| `node:readline` | Interactive prompts (init command) |
| `node:test` | Built-in test framework |
| `node:assert` | Test assertions |
| `node:os` | Temp directory for tests |

**Dev dependencies**: None. Tests use `node:test` (built-in since Node.js 18).

---

## Revision History

| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 0.6.0 | 2026-05-31 | DocGuard Team | Refresh for v0.24.0: Python promoted to full support via a `python3` AST tier (`cli/scanners/py-ast.mjs`); JS/TS route extraction extended with cross-file mount-prefix resolution, object-form route declarations, and AST router-screen detection (`cli/scanners/js-ast.mjs`); removed the retired editor extension from the tech stack |
| 0.5.0 | 2026-05-29 | DocGuard Team | Refresh for v0.22–v0.23: validator + scanner set updated, new `config.mjs` (config extracted to break the demo↔docguard cycle) and `shared-trace-patterns.mjs` (shared multilingual trace patterns) |
| 0.4.0 | 2026-03-13 | DocGuard Team | Complete rewrite with real project data, AI orchestration architecture |
| 0.1.0 | 2026-03-13 | DocGuard Generate | Auto-generated skeleton |


### Requirement identity across documents

Requirement definitions are identified by repository-relative document path plus ID. A bare test annotation such as `@req FR-001` earns linkage credit only when that ID is defined in one document. When features reuse an ID, qualify the declaration: `@req specs/payments/spec.md#FR-001`. The same spelling works in a test label. Use forward slashes; an optional leading `./` is accepted. Qualifiers are exact repository-relative paths, not paths relative to the test file.

Validation and `trace --features` share definition parsing and reference resolution. A qualified reference credits only its target document. Ambiguous bare references credit neither feature and produce a review finding for each unresolved definition. A wrong qualifier is an orphan reference and never falls back to a bare match. Repeated mentions within one document do not create additional identities. Linkage remains evidence of a declaration, not proof of behavioral correctness; lifecycle and arbitrary verification-link semantics are separate concerns.


---

## docs-canonical/CI-RECIPES.md

# CI Recipes

<!-- docguard:last-reviewed 2026-09-11 -->
<!-- docguard:status active -->

## Recipe 1 — Guard (mandatory CI gate)

Run `docguard init --with ci` to create `.github/workflows/docguard.yml`. Existing workflows are preserved; explicit `--force` backs up and replaces the file. The standalone `docguard ci` command continues to execute checks. Start from `templates/ci/github-actions.yml` or the Spec Kit guard workflow in `extensions/spec-kit-docguard/templates/github-workflows/`. These checked-in templates are the maintained source for action pins, runtime selection, and report handling. Copying a template does not configure repository branch protection; require its check independently.

Use a fixed tool version, full Git history for freshness, and explicit warning policy. Run the check against the actual revision proposed for merging. A missing executable, malformed report, or unexpected nonzero exit is a tool failure, not a successful scan. Configure merge-queue triggers if the repository uses a merge queue.

```sh
node_modules/.bin/docguard ci --format json --no-history > docguard-report.json
```

The CLI exits 0 for pass, 1 for failure, and 2 for warning-only results. A plain shell step treats both 1 and 2 as failures. To permit warnings, capture the exit status explicitly and allow only 0 or 2. To block warnings, use `ci --fail-on-warning`. Severity overrides retain their configured meaning.

## Recipe 2 — Auto-Fix (PR-time mechanical fixes)

Run `fix --write` on a controlled checkout when documentation mutation is intended. Review the resulting diff and rerun guard. Preserve human-authored intent; a disagreement may require fixing implementation rather than rewriting the specification.

Mechanical replacements require their existing provenance and generated-section safeguards. A scheduled or PR repair workflow should create a reviewable branch/PR and deduplicate existing repair work. Grant write privileges only to that explicitly enabled workflow. Fork contributions should receive read-only verification unless a separate trusted process handles repair.

The shipped auto-fix template and composite action expose optional commit/comment behavior. Review those flags and their permissions before enabling them. A generated workflow is executable code and deserves the same review as another repository change.

## Recipe 3 — Sync (memory refresh on a schedule or pre-merge)

`sync --write` regenerates sections declared as code-derived. Human sections retain judgment and rationale. Cache identity reflects relevant inputs, so ordinary source edits invalidate a prior plan.

On a schedule, produce a diff, check for an existing repair PR, and create a new proposal only when meaningful work remains. Keep clean runs quiet. Set an owner and response expectation for unresolved findings. Scheduled source scans cannot detect every external deployment or vendor change; operational checks need their own evidence.

## Recipe 4 — Score (track CDD maturity over time)

`score --format json` reports structural maturity. Its numeric threshold is stable, while `assurance` explicitly states that factual accuracy remains unverified. Comparing scores is meaningful only with the same tool/configuration and a comparable coverage scope.

Use guard findings and declared verification evidence for enforcement. A high score alone does not establish current documentation, correct prose, or regulatory compliance.

## Recipe 1b — GitLab CI / Jenkins (JUnit output)

`guard --format junit` emits a test report suitable for GitLab/Jenkins ingestion. Install a fixed DocGuard version in the job, capture the exit status, and upload the report even on failures. Permitting exit 2 is an explicit warning policy; other nonzero statuses remain failures.

## Recipe 4b — Score history across ephemeral CI runs

`ci` records history by default. `--no-history` opts out. Ephemeral runners need an explicitly configured artifact or cache policy if trends are to span runs. Treat restored history as informational data, not proof that the current checkout was verified. Avoid sharing writable caches between untrusted pull requests and privileged release workflows.

## Recipe 4c — Multi-repo scorecard (no extra tooling)

Run `ci --format json` per repository and retain project, revision, tool version, configuration, status, and assurance scope. Aggregate findings by code while preserving their repository ownership. Report unsupported and unclassified coverage alongside successful checks.

## Pre-commit hook (no GitHub Actions required)

`docguard hooks --type pre-commit` installs a local gate that prefers the repository's installed DocGuard binary. The hook blocks an unavailable runtime. `--auto-fix` additionally applies mechanical fixes and stages their output; enable it only when that mutation is intended.

Regenerate installed hooks after upgrading to pick up changes in hook behavior. The pre-push score hook parses real JSON and enforces its configured minimum; it complements the full CI gate. Local hooks can be bypassed, so protected merges remain necessary for shared enforcement.

## Recipe 5 — Pre-commit lite (changed files only)

`guard --changed-only --since <ref>` runs its curated validator subset with changed-file scoping, plus explicitly escalated validators. Use a full guard at the merge boundary. The entry point and `guard.mjs` define the current subset; a copied list in this recipe would drift.

## Permissions cheatsheet

| Operation | Default authority | Additional authority |
|---|---|---|
| Guard, score, report | Repository read | Artifact storage if configured |
| Mechanical repair | Read/write controlled checkout | Branch/PR publication only when enabled |
| Feedback preview | Local analysis | User submits reviewed public metadata voluntarily |
| Scheduled review | Repository read | Notification or publication only when explicitly configured |

## Action inputs reference

`action.yml` is the authoritative composite-action input contract. Review command selection, warning policy, score threshold, working directory, and optional commit/comment flags. Pin the action to a reviewed commit and retain the corresponding release label for maintenance.

## Action outputs reference

Read the outputs declared in `action.yml` and the command's JSON schema before wiring downstream steps. Preserve unknown/unverified values. An integrity digest detects changes to covered report data; it is neither a trusted signature nor proof of a correct scanner.


---

## docs-canonical/DATA-MODEL.md
> Database schemas, entity relationships, and data flow

# Data Model

<!-- docguard:version 0.6.0 -->
<!-- docguard:status active -->
<!-- docguard:last-reviewed 2026-09-14 -->

| Metadata | Value |
|----------|-------|
| **Status** | ![Status](https://img.shields.io/badge/status-active-brightgreen) |
| **Version** | `0.6.0` |
| **Database** | None — DocGuard is a stateless CLI tool |
| **Storage** | File-system only (reads project files, writes generated docs) |

---

## Entities

DocGuard uses filesystem artifacts for configuration, optional caches, and history. Commands read project files and produce structured output. The "data model" consists of the configuration schemas, validator output formats, and document metadata structures documented below. All data is file-system based — DocGuard reads `.docguard.json`, scans the project directory, and validates canonical documents against the codebase.

## Configuration: `.docguard.json`

The primary data structure. Controls all CLI behavior.

### Identity and required files

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `projectName` | `string` | No | Inferred from `package.json` name or directory | Display name for reports |
| `version` | `string` | No | `"0.1"` | Config schema version |
| `projectType` | `string` | No | Auto-detected | One of: `cli`, `webapp`, `api`, `library`, `monorepo` |
| `requiredFiles.canonical` | `string[]` | No | 5 docs-canonical files | Paths to required CDD documents |
| `requiredFiles.agentFile` | `string[]` | No | `["AGENTS.md", "CLAUDE.md"]` | AI agent config file options |
| `requiredFiles.changelog` | `string` | No | `"CHANGELOG.md"` | Changelog file path |
| `requiredFiles.driftLog` | `string` | No | `"DRIFT-LOG.md"` | Drift log file path |

### Project-type behavior

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `projectTypeConfig.needsEnvVars` | `boolean` | No | `true` | Whether ENVIRONMENT.md should check for env var docs |
| `projectTypeConfig.needsEnvExample` | `boolean` | No | `true` | Whether `.env.example` is expected |
| `projectTypeConfig.needsE2E` | `boolean` | No | `true` | Whether E2E test docs are expected |
| `projectTypeConfig.needsDatabase` | `boolean` | No | `true` | Whether DATA-MODEL should expect entity docs |
| `projectTypeConfig.testFramework` | `string` | No | Auto-detected | Test framework name (e.g., `"node:test"`, `"jest"`) |
| `projectTypeConfig.runCommand` | `string` | No | Auto-detected | Command to run the project |

### Validator tuning

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `validators.*` | `boolean` | No | `true` | Enable/disable individual validators |
| `collections.*` | `string` (glob) | No | — | Binds a documentation noun to a code collection: `"extractors": "src/extractors/*.py"` lets Metrics-Consistency flag a documented count that disagrees with the file count |
| `docs.dirs` | `string[]` | No | Auto-detected | EXTENDS the auto-detected documentation homes (docs/, documentation/, guides/, …) with non-standard dirs; exclude via `.docguardignore` |
| `severity.*` | `"high" \| "medium" \| "low"` | No | `"medium"` | Per-validator exit-code weight — `high` promotes warnings to blocking, `low` demotes them (display unchanged) |

### Example Configuration

```json
{
  "projectName": "docguard",
  "version": "0.3",
  "projectType": "cli",
  "requiredFiles": {
    "canonical": [
      "docs-canonical/ARCHITECTURE.md",
      "docs-canonical/DATA-MODEL.md",
      "docs-canonical/SECURITY.md",
      "docs-canonical/TEST-SPEC.md",
      "docs-canonical/ENVIRONMENT.md"
    ],
    "agentFile": ["AGENTS.md", "CLAUDE.md"],
    "changelog": "CHANGELOG.md",
    "driftLog": "DRIFT-LOG.md"
  },
  "projectTypeConfig": {
    "needsEnvVars": false,
    "needsE2E": false,
    "needsDatabase": false,
    "testFramework": "node:test"
  },
  "validators": {
    "structure": true,
    "docsSync": true,
    "drift": true,
    "changelog": true,
    "architecture": false,
    "testSpec": true,
    "security": false,
    "environment": true,
    "freshness": true
  }
}
```

## Retirement Manifest: `.docguard-archive.json`

The manifest is an append-only recovery ledger for documentation removed from
active context by `docguard retire`. Git content remains authoritative; the
manifest stores no retired prose.

| Field | Type | Description |
|-------|------|-------------|
| `schemaVersion` | `number` | Manifest contract version; currently `1` |
| `strategy` | `"git-history"` | Recovery storage strategy |
| `entries[].path` | `string` | Former repository-relative document path |
| `entries[].archivedAt` | ISO timestamp | Historical field name for retirement time |
| `entries[].archivedFrom` | Git object ID | Source revision containing the exact document |
| `entries[].blob` | Git object ID | Exact retired content identity; length follows repository object format |
| `entries[].reason` | `string` | Reviewed retirement rationale |
| `entries[].supersededBy` | `string` | Optional current replacement document |
| `entries[].evidence` | `string[]` | Optional clean documents containing consolidated outcomes |
| `entries[].requirementIds` | `string[]` | Requirement identities declared by the retired file; traceability keeps them as tombstones and never treats them as active requirements |
| `entries[].retentionRef` | `string` | Branch ref proven to contain the source revision |
| `entries[].objectFormat` | `"sha1" \| "sha256"` | Git repository object format |
| `entries[].recoverability` | `"verified"` | Result of the retained-ref ancestor check at retirement time |
| `entries[].restore` | `string` | Convenience command derived from structured source/path fields |

Existing manifests may carry one shared top-level `retention` record for a
batch created before per-entry retention metadata was introduced. A future
registry validator will project both forms into one normalized model. Lifecycle
and traceability consumers reject incomplete recovery entries; an unverified
manifest cannot suppress active-context or orphan-reference findings.

## Document Metadata Headers

Every CDD document includes DocGuard metadata as HTML comments at the top:

| Header | Type | Required | Description |
|--------|------|----------|-------------|
| `docguard:version` | `string` | Yes | Semantic version of the document |
| `docguard:status` | `string` | Yes | One of: `draft`, `active`, `deprecated` |
| `docguard:last-reviewed` | `string` | Yes | ISO date (`YYYY-MM-DD`) |
| `docguard:generated` | `boolean` | No | `true` if auto-generated by DocGuard |

### Example Metadata Header

```markdown
<!-- docguard:version 0.4.0 -->
<!-- docguard:status active -->
<!-- docguard:last-reviewed 2026-03-13 -->
```

## Validator Output Format

Validators emit findings and aggregate counts. The guard adapter adds names and statuses:

| Field | Type | Description |
|-------|------|-------------|
| `name` | `string` | Validator name (e.g., `"structure"`, `"changelog"`) |
| `status` | `string` | `"pass"`, `"warn"`, or `"fail"` |
| `findings` | `object[]` | Stable code, validator, severity, confidence, location, message, suggestion |
| `passed`, `total` | `number` | Applicable check counts |
| `errors`, `warnings` | `string[]` | Compatibility message arrays |
| `applicable` | `boolean` | Optional applicability indicator; false becomes N/A |

## Fix Command Issue Format

The `fix --format json` output follows this structure:

| Field | Type | Description |
|-------|------|-------------|
| `status` | `string` | `"clean"` or `"issues-found"` |
| `project` | `string` | Project name |
| `projectType` | `string` | Detected project type |
| `issueCount` | `number` | Total issues found |
| `autoFixable` | `number` | Issues fixable by `--auto` |
| `issues[].type` | `string` | `"missing-file"`, `"empty-doc"`, `"partial-doc"`, `"missing-config"` |
| `issues[].severity` | `string` | `"error"`, `"warning"`, `"info"` |
| `issues[].file` | `string` | Affected file path |
| `issues[].autoFixable` | `boolean` | Can be auto-fixed |
| `issues[].fix.action` | `string` | `"create"`, `"rewrite"`, `"improve"` |
| `issues[].fix.ai_instruction` | `string` | AI-actionable fix instruction |

## Score Output Format

The `score --format json` output:

| Field | Type | Description |
|-------|------|-------------|
| `score` | `number` | CDD maturity score (0-100) |
| `grade` | `string` | Letter grade: `A+`, `A`, `B`, `C`, `D`, `F` |
| `categories` | `object` | Per-category score, weight, weighted contribution, and axis |
| `scoreKind` | `string` | `structural-maturity` |
| `assurance` | `object` | Factual accuracy remains unverified; extracted candidate count is heuristic |
| `memory` | `object` | Completeness and structural alignment proxies; accuracy is null |

---

## Revision History

| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 0.6.0 | 2026-09-14 | DocGuard Team | Add the document-retirement recovery manifest, retained-ref proof, and retired requirement tombstones |
| 0.4.0 | 2026-03-13 | DocGuard Team | Complete rewrite — documented all config formats, output schemas, metadata headers |
| 0.1.0 | 2026-03-13 | DocGuard Generate | Auto-generated skeleton |

## Score assurance contract

The numeric CDD score estimates structural maturity. Factual accuracy and regulatory assurance require separate evidence. Existing score and grade thresholds remain stable. Score JSON identifies its scope as `structural-maturity`. `memory.accuracy` is nullable: `null` represents unverified factual accuracy; the former proxy is exposed as `memory.structuralAlignment`. Consumers must preserve null as an unknown value.

An `assurance` object accompanies score, diagnose, CI, and report output. It contains `status` (`unverified`), `factualAccuracy` (`null`), and `unverifiedClaims` (a count of extracted candidates, or null if extraction failed). Even zero extracted candidates leaves prose unverified. Claim discovery uses a bounded heuristic. These fields explain evidence limits while existing CI thresholds retain their numeric meaning.

## Feedback contribution contract

`feedback` defaults to uncertain findings. `--code <CODE>` selects a finding regardless of confidence; `--all` includes all active findings. `--preview` emits reviewable output and skips feedback-record writes. Unknown codes fail with a clear error. Shared issue URLs contain only allowlisted finding identity, tool version, and contribution instructions. Source-derived messages, paths, snippets, and suggestions stay in the local record. Each result includes a search URL covering existing issues and pull requests, including closed work, so contributors can check for duplicates before submitting. The user controls submission through the reviewed issue draft.

## Check coverage and document roles

Each guard validator adds applicability with status and reason. checkCoverage contains counts by status, limitations naming checks that were not fully performed, and an explanatory limitation. These fields describe coverage independently from legacy status, totals, findings, and exit codes. CI/report consumers preserve them, including disabled-check counts.

Optional docs.roles maps canonical roles to safe project-relative Markdown paths. Configuration normalization replaces each mapped default in requiredFiles.canonical and documentTypes. Read-only callers accept the normalized mapping. Legacy document writers reject custom mappings until write semantics support existing layouts safely. The configuration schema and docs/configuration.md define the current role names and supported operations.


---

## docs-canonical/ENVIRONMENT.md
> Setup instructions, environment variables, and prerequisites

# Environment

<!-- docguard:quality negation-load off — an environment doc precisely describes the ABSENCE of requirements (no env vars, no install step, no API keys, no database); the prohibitive phrasing is accurate and intentional, not sloppy writing -->

<!-- docguard:version 0.6.0 -->
<!-- docguard:status active -->
<!-- docguard:last-reviewed 2026-09-11 -->

> DocGuard needs no environment variables. It has a single optional-load npm dependency (`@babel/parser`) and optionally uses the developer's own `python3`; everything else is Node.js built-ins.

| Metadata | Value |
|----------|-------|
| **Status** | ![Status](https://img.shields.io/badge/status-active-brightgreen) |
| **Version** | `0.6.0` |

---

## Prerequisites

| Tool | Version | Installation |
|------|---------|-------------|
| Node.js | ≥18.0.0 | [nodejs.org](https://nodejs.org) |
| npm | ≥8 | Included with Node.js |
| Git | Any | [git-scm.com](https://git-scm.com) |
| Python 3 | **Optional** — ≥3.8, enables the AST-accurate Python scanning tier; the scanners use regex otherwise | [python.org](https://python.org) |

## Environment Variables

> **None required.** DocGuard reads project files directly. No `.env` file,
> no API keys, no database connections. (Its one npm dependency, `@babel/parser`,
> needs no configuration.)

## Setup Steps

1. Clone the repository: `git clone https://github.com/raccioly/docguard.git`
2. Run `npm ci` to install the locked Babel parser dependency for the full JS/TS extraction tier
3. Run directly: `node cli/docguard.mjs --help`
4. Or use via npx: `npx docguard-cli --help`

## Development

```bash
# Run CLI locally
node cli/docguard.mjs audit

# Run the full test suite (node:test)
npm test

# Test a command on a target project
node cli/docguard.mjs diagnose --dir /path/to/project

# Quick health check
node cli/docguard.mjs guard --format json
```

## CI/CD

```bash
# GitHub Actions — use the shipped template
cp templates/ci/github-actions.yml .github/workflows/docguard.yml

# Or run CI command directly
node cli/docguard.mjs ci --threshold 70 --format json
```

---

## Revision History

| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 0.6.0 | 2026-05-31 | DocGuard Team | v0.24.0: documented Python 3 as an optional prerequisite (enables the AST Python tier; regex fallback when absent); de-bristled the test-count example |
| 0.5.0 | 2026-03-13 | @raccioly | Added diagnose, CI template, development examples |
| 0.3.0 | 2026-03-12 | @raccioly | Proper CLI environment docs, no env vars |
| 0.1.0 | 2026-03-12 | DocGuard Generate | Auto-generated (corrected) |


---

## docs-canonical/REQUIREMENTS.md

# Requirements

<!-- docguard:version 0.2.0 -->
<!-- docguard:status active -->
<!-- docguard:last-reviewed 2026-09-11 -->

## Functional Requirements

| ID | Priority | Requirement | Verification |
|---|---|---|---|
| FR-001 | P1 | Score distinguishes structural maturity from unverified factual accuracy, even when candidate extraction finds nothing. | tests/score-assurance.test.mjs |
| FR-002 | P1 | Users can dispute any active finding, preview feedback, and prepare public metadata without sharing source-derived strings automatically. | tests/feedback-contributions.test.mjs |
| FR-003 | P1 | CI, diagnose, and report preserve score assurance limits in machine output. Existing score thresholds keep their numeric meaning. | tests/score-assurance.test.mjs |

## Non-Functional Requirements

| ID | Category | Requirement | Verification |
|---|---|---|---|
| NFR-001 | Security | Untrusted input passed to subprocesses uses argv-based invocation and validation appropriate to the command. | tests/security-init-injection.test.mjs |
| NFR-002 | Portability | The distributed CLI runs on supported Node versions. Babel supplies the full JS/TS tier; the CLI retains a regex fallback when the parser is absent. | tests/npm-pack-smoke.test.mjs |
| NFR-003 | Correctness | Cached memory plans invalidate when relevant working-tree inputs, configuration, or scanner implementation change. Unreadable or unsupported cache inputs cause a miss. | tests/plan-disk-cache.test.mjs |

## Success Criteria

The full supported-runtime test matrix and guard determine local release readiness. Detector quality requires independent positive and negative examples. External precision, recall, and agent productivity targets belong to the evaluation plan; a structural score is not evidence of those outcomes.

## User Scenarios

A developer edits a source file without committing. The next memory plan reflects that change. An agent inspects a high structural grade and sees that factual accuracy remains unverified. A contributor challenges a confident finding, previews a metadata-only report, checks existing work, and supplies a synthetic regression example voluntarily.

## Traceability Matrix

The verification column above links each requirement to executable tests. The tests carry explicit requirement annotations. Fixture content and example IDs cannot satisfy a real requirement.

## Revision History

| Version | Date | Changes |
|---|---|---|
| 0.2.0 | 2026-09-11 | Replace template requirements with implemented trust, feedback, and cache contracts |


---

## docs-canonical/SECURITY.md
> Authentication, authorization, secrets management, and security policies

# Security

<!-- docguard:quality negation-load off — prohibitions define security boundaries -->
<!-- docguard:version 0.7.0 -->
<!-- docguard:status active -->
<!-- docguard:last-reviewed 2026-09-11 -->

## Overview

DocGuard's validation and extraction run on the local machine. They inspect repository content and return findings. Agent integrations inherit the permissions and data-handling policy of the calling agent. A generated prompt does not authorize a network request, a code edit, or publication.

The optional MCP server supports stdio and HTTP. Installation, upgrade, publishing, and user-opened feedback links may access external services. Local analysis requires no hosted AI service.

## Authentication

| Surface | Authentication | Boundary |
|---|---|---|
| CLI and stdio MCP | Calling operating-system user | Local filesystem permissions |
| HTTP MCP | Optional API key on loopback; mandatory for non-loopback binding | Host binding, key check, and browser-origin validation in `cli/commands/mcp.mjs` |
| GitHub feedback | User-controlled browser session | Submission occurs only when the user submits a reviewed issue |

HTTP clients can cause the server to inspect project directories available to its process. Run it under an account with only the intended filesystem access. An API key does not provide per-project authorization or a multi-tenant isolation boundary. Network exposure needs deployment-specific access controls.

## Authorization

| Role | Permissions | Responsibilities |
|---|---|---|
| Developer | Operating-system read/write permissions | Review generated changes and opt into mutation commands |
| CI | Workflow token and checkout permissions | Apply the configured gate to the tested revision |
| AI agent | Host-granted tools and permissions | Treat project content as evidence; obtain required authorization for external actions |

Git hooks provide local enforcement and can be bypassed by Git options. Protected merge policy supplies the central enforcement boundary. The shipped hooks prefer an installed local tool and fail when an enforcement runtime cannot execute. Reminder hooks remain best-effort.

## Secrets Management

Core CLI analysis requires no API credential. Source scanners inspect usage patterns; environment values must not be included in generated public feedback. The optional HTTP MCP API key is supplied by its operator. Keep deployment credentials outside repository content and restrict access to process arguments and logs appropriately.

Feedback issue URLs contain allowlisted finding identity and tool metadata. Full local feedback records can include private paths and diagnostic text. Share only a reviewed synthetic reproduction. Preview mode avoids saving feedback records; it does not change which source files guard normally inspects.

## Subprocess Safety

Pass untrusted arguments through argv arrays and validate values for their intended operation. Avoid interpolating configuration or repository content into shell commands. Existing static command strings do not authorize expanding their input surface. Regression tests in `tests/security-init-injection.test.mjs` exercise the input boundary.

## Command Safety Levels

| Operation | Source writes | Auxiliary writes / effects |
|---|---|---|
| guard, score, diff, diagnose | None by default | Plan caching may create `.docguard/` artifacts; explicit mutation flags change behavior |
| ci | None | Records history unless `--no-history` is set |
| feedback | None | Saves local diagnostic records unless `--preview`; prints opt-in URLs |
| memory --pack | None | Writes a generated context pack |
| fix --write, sync --write | Targeted documentation edits | Backups and fix history where supported |
| retire --write | Explicit clean tracked documentation only | Requires retained-ref recovery proof, clean replacement/evidence docs, and no live Markdown backreferences |
| init, generate | Documentation and configuration scaffolding | Explicit force options may overwrite content |
| hooks | Hook configuration and executable scripts | Auto-fix hooks may edit and stage documentation |
| report | None by default | `--out` writes an artifact |

Review the exact command and flags before assigning privileges. CLI help is the authoritative command inventory.

## Supply Chain

The package declares one exact-pinned dependency, `@babel/parser`, with its transitive Babel dependencies recorded in `package-lock.json`. AST extraction degrades to a regex fallback when Babel is unavailable. Python AST extraction optionally uses the installed `python3` runtime. No additional runtime package is introduced by the trust improvements.

Dependency audit results are time-specific observations. Run the current audit and supported Node-version matrix before release; a historical clean audit is not a continuing guarantee. Pin third-party CI actions to verified commit SHAs and install from the lockfile.

## .gitignore Audit

Exclude `node_modules`, environment values, generated build output, and private local files from version control. `.docguardignore` controls analysis coverage separately; it is not a secrecy boundary for every tool that runs in the repository.

## Security Rules Checklist

- Validate subprocess inputs at their call boundaries.
- Preserve provenance checks before mechanical edits.
- Keep private diagnostics separate from public feedback payloads.
- Treat submitted reproductions as untrusted data.
- Require credentials for non-loopback HTTP MCP binding.
- Disclose unknown or unsupported verification instead of asserting success.
- Verify protected merge policy independently of local hook installation.

## Revision History

| Version | Date | Changes |
|---|---|---|
| 0.7.0 | 2026-09-11 | Document HTTP MCP, auxiliary writes, enforcement scope, and feedback privacy |


---

## docs-canonical/TEST-SPEC.md
> Test coverage requirements, testing strategy, and quality rules

# Test Specification

<!-- docguard:version 0.8.0 -->
<!-- docguard:status active -->
<!-- docguard:last-reviewed 2026-09-11 -->

> DocGuard has a single optional-load npm dependency (`@babel/parser`) and an optional `python3` AST tier. CLI integration tests cover the full stack with `node:test` (zero dev dependencies) and exercise both AST extractors (`js-ast`, `py-ast`) plus their regex fallbacks. The Python AST tests skip themselves automatically on a machine that lacks `python3`.

| Metadata | Value |
|----------|-------|
| **Status** | ![Status](https://img.shields.io/badge/status-active-brightgreen) |
| **Project Type** | CLI |
| **Test Framework** | `node:test` (built-in) |
| **Test Files** | `tests/` |

---

DocGuard's tests verify command behavior through subprocess execution. Each test runs the full CLI binary via execSync, capturing stdout and checking output patterns. This approach tests the complete stack in a single pass: argument parsing, config loading, validator execution, and output formatting.

Tests are designed to be config-aware. They verify that project-type settings like needsEnvExample and testFramework correctly influence scoring and validation behavior. Regression guards preserve known failures with dedicated assertions and neighboring valid cases.

All tests use the built-in node:test framework with zero test dependencies. CI runs the suite on Node 18, 20, 22, and 24. Its runtime budget catches large regressions; local timing depends on runtime and filesystem. Record measured timing with its environment rather than asserting a universal duration.

Test names follow the pattern: "verb + expected behavior" (e.g., "runs and shows a score", "respects projectTypeConfig"). Each test should isolate its mutable fixtures and clean up its resources.

## Test Categories

| Category | Framework | Location | Run Command |
|----------|-----------|----------|-------------|
| Unit | node:test | tests/ | `npm test` |
| CLI Integration | node:test | tests/ | `npm test` |

> **CLI integration tests cover the full stack** — this is a CLI tool with zero UI surface.
> Commands are validated end-to-end via Node.js subprocess execution, making separate E2E tests redundant.

All test files live in `tests/` and match the glob `tests/*.test.mjs` — the test runner supplies the current inventory as the suite grows; see the Source-to-Test Map below for the source→test traceability that matters.

## Coverage Rules

| Metric | Target | Current |
|--------|:------:|:-------:|
| Command Coverage | Every public command | Scenario coverage; inspect tests before claiming exhaustive behavior |
| Validator Coverage | Every validator | Positive, negative, and regression cases |
| Flag Coverage | Risk-based | Tested scenarios; no exhaustive coverage claim |
| Test Count | — | Current count is emitted by `npm test` |

## Source-to-Test Map

| Source File | Test File | Status |
|------------|-----------|:------:|
| `cli/docguard.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/shared.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/init.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/guard.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/score.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/diff.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/generate.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/agents.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/hooks.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/diagnose.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/badge.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/ci.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/fix.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/watch.mjs` | `tests/commands.test.mjs` | ✅ pass |
| `cli/commands/publish.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/commands/trace.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/shared-requirements.mjs` | `tests/traceability.test.mjs`, `tests/archive.test.mjs` | ✅ |
| `cli/commands/retire.mjs` | `tests/archive.test.mjs` | ✅ |
| `cli/validators/document-lifecycle.mjs` | `tests/document-lifecycle.test.mjs` | ✅ |
| `cli/validators/structure.mjs` | `tests/commands.test.mjs` | ✅ |
| `cli/validators/docs-diff.mjs` | `tests/commands.test.mjs` | ✅ |

> **Note**: `watch.mjs` is an interactive file-watcher (uses `fs.watch` + process signals). It is
> covered by automated lifecycle tests, including filesystem watcher error handling.
> Manual checks supplement platform-specific event behavior.

## Critical CLI Flows

| # | Flow | Test File | Status |
|---|------|-----------|:------:|
| 1 | `docguard audit` | `tests/commands.test.mjs` | ✅ |
| 2 | `docguard init` | `tests/commands.test.mjs` | ✅ |
| 3 | `docguard guard` | `tests/commands.test.mjs` | ✅ |
| 4 | `docguard guard --format json` | `tests/commands.test.mjs` | ✅ |
| 5 | `docguard score` | `tests/commands.test.mjs` | ✅ |
| 6 | `docguard score --format json` | `tests/commands.test.mjs` | ✅ |
| 7 | `docguard score --tax` | `tests/commands.test.mjs` | ✅ |
| 8 | `docguard diagnose` | `tests/commands.test.mjs` | ✅ |
| 9 | `docguard diagnose --format json` | `tests/commands.test.mjs` | ✅ |
| 10 | `docguard generate` | `tests/commands.test.mjs` | ✅ |
| 11 | `docguard init --profile starter` | `tests/commands.test.mjs` | ✅ |

---

## Revision History

| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 0.7.0 | 2026-03-13 | @raccioly | Added trace, publish; watch.mjs coverage justified (ISO 29119); 15 commands |
| 0.5.0 | 2026-03-13 | @raccioly | Added diagnose, guard JSON, profile, tax tests (24→30) |
| 0.3.0 | 2026-03-12 | @raccioly | Real tests, project-type-aware spec |
| 0.1.0 | 2026-03-12 | DocGuard Generate | Auto-generated (corrected) |

## Trust regression scenarios

`tests/score-assurance.test.mjs` checks that structural grades never claim factual verification and that CI, diagnose, and reports retain this boundary. `tests/feedback-contributions.test.mjs` checks confident-finding selection, preview behavior, and outbound metadata privacy. Cache tests must change source contents without changing a manifest or Git HEAD, including repeated edits and fresh-process reads. Hook tests execute generated scripts against controlled runtimes rather than merely matching shell text. Traceability tests pair synthetic fixture IDs with genuine requirement annotations.

A detector fix should include a clean near-miss and a real defect. Held-out neighboring cases are required to evaluate generalization. The proposed external benchmark is specified in `ROADMAP.md`; its targets are acceptance criteria, not measured results.

Retirement tests use disposable Git repositories and verify both sides of the boundary: completed planning material is reported for review, while active neighboring material stays clean. Write-path tests must prove retained-ref recovery metadata and refusal of source code, dirty, untracked, required, symlinked, private, protected, submodule, and out-of-root paths. Read-only plan and check modes must not modify repository state.

## Enterprise precision regressions

Regression cases are synthetic and name no consumer repositories. Keep a valid near-neighbor beside every detected defect: formatting versus declaration deletion; negated versus current technology use; explained versus unexplained skips; mock expectations versus credentials; implemented versus omitted contract endpoints; Worker bindings versus local variables; historical versus active documents. Check coverage tests distinguish unsupported and missing inputs from executed checks. Document-role tests exercise mapped findings, raw/loaded configuration parity, unsafe paths, and read-only planning without writes.

Independent review must challenge suppression paths, not only the original false-positive example. Cross-project runs use disposable snapshots and verify consumer content remains unchanged. Finding counts alone cannot establish precision or recall.


---

## DRIFT-LOG.md
> Known deviations from canonical documentation

# Drift Log

> Documents conscious deviations from canonical specifications.
> Every `// DRIFT: reason` in code must have a corresponding entry here.

| Date | File | Canonical Doc | Drift Description | Severity | Resolution |
|------|------|---------------|-------------------|----------|------------|
| 2026-03-13 | `cli/commands/generate.mjs` | ARCHITECTURE.md | AGENTS.md template includes `// DRIFT: reason` as an instruction pattern for end users. These are template strings, not actual code deviations. | Info | By design — template content |
| 2026-03-13 | `cli/commands/generate.mjs` | ARCHITECTURE.md | DRIFT-LOG.md template includes `// DRIFT: reason` as placeholder text. | Info | By design — template content |
| 2026-03-13 | `cli/commands/agents.mjs` | ARCHITECTURE.md | Agent config generators include `// DRIFT: reason` as instruction text for AI agents. 3 occurrences across Windsurf, Cursor, and generic agent configs. | Info | By design — instruction content |
| 2026-03-13 | `cli/validators/drift.mjs` | ARCHITECTURE.md | Drift validator references `// DRIFT:` pattern in JSDoc and regex. | Info | By design — validator implementation |
| 2026-05-12 | `tests/drift.test.mjs` | ARCHITECTURE.md | Drift validator tests use `// DRIFT:` comments to simulate project files having drift comments. | Info | By design — test implementation |
| 2026-05-26 | `tests/scoping-extended.test.mjs` | ARCHITECTURE.md | v0.15 P3 test fixture builds `// D' + 'RIFT:` strings via concat to test changed-files scoping without false-positiving the outer scan. | Info | By design — test implementation; mitigated by v0.15.1 hotfix that skips test files by default in Drift-Comments |
| 2026-05-26 | `cli/validators/drift.mjs` | ARCHITECTURE.md | Drift-Comments validator v0.15.1+ skips test files by default (matches TODO-Tracking's pattern). Opt in via `config.drift.includeTestFiles` if your project genuinely uses DRIFT markers in test code. | Info | By design — defensive default to prevent fixture false-positives |
| 2026-05-26 | `CHANGELOG.md` / `extensions/spec-kit-docguard/skills/*` | None | v0.12-v0.15 changelogs and release notes reference `// DRIFT:` in feature descriptions (e.g. K-3 .docguardignore, v0.13 sync, v0.14 P3 scoping). Documentation prose only, not actionable drift. | Info | By design — release notes |
| 2026-07-03 | `templates/commands/*`, `CHANGELOG.md`, `docs/ai-integration.md` | None | v0.29 batch audit: the DRIFT mentions in recently-committed files are the known by-design classes above (template instruction text, validator docstrings, changelog prose, and the new AI-integration guide's workflow step 6 teaching the drift protocol). No new code deviations from canonical docs were introduced by the findings migration, generate split, or integration-surface work. | Info | Audited — no actionable drift |
| 2026-07-03 | post-v0.29 batch (`cli/scanners/speckit.mjs`, `tests/speckit-phantom.test.mjs`, `packaging/*`) | None | Post-release batch audit (phantom detection, instruction audit, trace --features, distribution files): DRIFT mentions are validator/test/doc prose of the by-design classes above. No new code deviations. | Info | Audited — no actionable drift |


---

## CHANGELOG.md
> Version history and release notes

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- `docguard retire` introduces a document-lifecycle boundary for specs, plans,
  migrations, and historical audits. Plan mode is read-only; `--check` can gate
  remaining candidates; writes require explicit clean tracked paths and a
  reason. Retired prose stays recoverable from a verified retention ref while
  `.docguard-archive.json` records source commits, blob identities, rationale,
  replacements, evidence documents, retired requirement identities, Git object
  format, and restore commands. Traceability recognizes those identities as
  tombstones, preventing historical test annotations from becoming orphans or
  silently rebinding to a later spec. Incomplete recovery entries cannot supply
  tombstones or suppress lifecycle findings.
- Guard includes a Document-Lifecycle validator with stable `DLC001`–`DLC004`
  findings. Exact terminal status is high-confidence; `Completed` maturity and a
  fully checked task list remain low-confidence review signals. Incomplete scan
  coverage and manifest/working-tree disagreement cannot produce a false clean.
  Projects outside a Git working tree report the validator as non-applicable,
  and every lifecycle result is discoverable through `docguard explain`.

### Changed

- The roadmap now contains current intent and contribution-ready work only.
  Released specs and historical implementation documents are retired from the
  active tree so AI agents cannot interpret them as current requirements.
- The active lifecycle specification separates safe mechanical fact refreshes
  from requirement reconciliation: implementation drift never authorizes
  silently rewriting approved intent.
- The llms.txt generators now use the shared backup-before-write path, including
  when active context indexes are regenerated after archival.
- Feature completion evidence now requires document-qualified requirement
  annotations. Bare IDs remain available in the repository-wide matrix but
  cannot rebind to a different feature after retirement.

## [0.36.2] - 2026-09-14

### Fixed

- Requirement identities include the defining document, preventing duplicate IDs across specs from sharing test coverage. Validator and feature scores share resolution; unique bare IDs remain compatible, and qualified references target exactly one document.

- Legacy badge command tests run in disposable projects with host setup tooling excluded, preventing source-checkout mutations during the test suite.

- `agents --check` preserves the repository and never bootstraps skills or invokes Spec Kit; stale managed files still exit 2, and explicit setup remains available.
- Documentation coverage honors configured documentation directories and raw role mappings, while excluded/private/symlinked documents cannot supply evidence. Reproduced against a pinned SvelteKit checkout.
- Configuration path construction and existence checks produce low-confidence review signals instead of asserting that directories are undocumented config files. Parsed comments and example strings do not establish file usage; findings identify the scanned documentation scope.
- Schema synchronization honors file exclusions and counts each source file once across overlapping roots, preserving separate models with the same name. Verified against a pinned Django checkout.
- Feature trace scoring uses the validator's explicit test annotations and labels; fixture JSON and incidental source strings no longer inflate requirement linkage. This does not yet solve duplicate requirement identities across specs.
- Diagnosis preserves freshness as a review task instead of suggesting incomplete or automatic document rewrites, and its AI prompt no longer demands removal of every warning.

## [0.36.1] - 2026-09-11

### Fixed

- Removed stale validator counts from the Spec Kit extension installation description, README, and repair skill. Found by installing the published v0.36.0 ZIP in a disposable Spec Kit project. The validation engine is unchanged.
- The Spec Kit verbose helper reads structured score/guard JSON, preserves PASS/WARN/FAIL, and rejects malformed reports. CLI resolution prefers local installs, quotes paths safely, and requires an installed CLI instead of fetching through npx.


## [0.36.0] - 2026-09-11

### Fixed

- Catalog submission drafts leave verification checkboxes unchecked, remove unsupported exclusivity claims, and require release-specific evidence before submission.

- Enterprise precision: preserve implementation evidence when API contracts omit routes; avoid formatting-only drift, negated technology claims, explained-skip noise, and narrowly identified synthetic mock passwords. Paired true-defect controls prevent broad suppression.

- **CI scaffolding**: `init --with ci` now writes the maintained workflow instead of executing a CI check; preserves existing files and rejects unsafe paths.

- Independent review fixes bound disk-cache promotion, reject malformed cached plans, preserve unknown report states, and report feedback persistence failures explicitly.
- Memory-plan caches invalidate on relevant source/document/configuration content and scanner changes, including ordinary uncommitted edits and fresh-process reads. Partial identities bypass reuse; disk writes are atomic and cache paths avoid symlink/private targets.
- Generated Git enforcement hooks parse formatted JSON, prefer installed local tools, and block unavailable or failed runtimes. Warning policy stays explicit. The Spec Kit after-implementation hook matches its mandatory contract.
- Traceability distinguishes requirement annotations and test labels from fixture strings. Freshness covers configured/nested docs and source additions/deletions, batches history reads, and rejects future review dates. Watch mode handles asynchronous errors and cleans up resources.
- CI templates use verified action commit pins, a fixed CLI version, full history, and checked JSON/exit status. The repository CI matrix explicitly emits TAP for its runtime budget.

### Changed

- Score, diagnose, CI, and report identify the grade as structural maturity and expose unverified factual accuracy. **Machine-contract correction:** score JSON now returns null for memory.accuracy; the former proxy is memory.structuralAlignment. Category axes use structuralAlignment. Numeric score thresholds are unchanged. Consumers must preserve unknown values.
- Canonical documentation now reflects the Babel dependency, HTTP MCP security boundary, auxiliary files, current score contract, and limits of coverage claims. CI recipes refer to maintained templates; generated audit/probe artifacts have explicit analysis exclusions.

### Added

- Explicit validator applicability/check coverage, bounded Worker binding extraction, and canonical document-role mappings for existing Markdown layouts. Custom mappings support validation/read-only plans; legacy automatic writes fail closed.
- Freshness emits low-confidence review tasks; explicit historical/superseded/deprecated documents retain their recorded intent without currentness assertions.

- Feedback selection with --code or --all, including confident findings, plus --preview to skip feedback-record writes. Public issue drafts contain metadata only, and search links help contributors check open and closed issues/PRs. Test-only synthetic reproductions are documented as a contribution path.
- Stable semantic-claim identifiers and bounded snapshot evidence for agent tasks; revision/dirty metadata and explicit uncertainty in context packs. Hashes identify inputs rather than assert review or correctness.
- A research-backed trust roadmap with competitor capabilities, proposed evaluations, contribution economics, and staged acceptance criteria.

### Migration

- Regenerate installed Git hooks and update installed Spec Kit registrations to receive their new behavior. Enforcement requires an installed local DocGuard or a binary on PATH; hooks no longer fetch a package through npx. Review nullable accuracy handling in JSON consumers before upgrading automation.

## [0.35.0] - 2026-09-11

### Added

- **The MCP server image is now published to GHCR** as `ghcr.io/raccioly/docguard` (version tag + `latest`), and documented in the README for the first time. The Dockerfile already existed and worked, but was only ever built *from source* by MCP directory inspectors on every check, and had zero mentions in `README.md` or `docs/` — so it was effectively invisible. A published image means inspectors and CI users pull a prebuilt one instead.
  - **GHCR rather than GitHub's npm registry:** public GHCR images pull with no authentication, whereas `npm.pkg.github.com` requires a PAT even for public packages — mirroring the npm package there would have been strictly worse than npmjs.com and purely cosmetic.
  - The job **smoke-tests the image before pushing**: it feeds a JSON-RPC `initialize` over stdio and requires a valid response, so a container that builds but doesn't serve MCP never reaches the registry. Verified locally end to end before shipping.
  - Authenticates with `GITHUB_TOKEN` — no stored credential, consistent with npm and PyPI now both being credential-free.
  - **Nothing depends on this job**, so a Docker failure cannot block npm, PyPI, the GitHub Release, or the catalog reminder. Deliberately *not* `continue-on-error`, which would report a failed job as successful and hide a broken image behind a green run.
  - Uses the docker CLI rather than third-party actions: two separate action-pin problems bit this pipeline today, and this needs no action versions at all.

## [0.34.9] - 2026-09-11

No code changes. Fixes the PyPI publish that v0.34.8 broke, and re-syncs the registries (npm reached 0.34.8; PyPI did not).

### Fixed

- **PyPI Trusted Publishing confirmed working; `PYPI_API_TOKEN` deleted.** v0.34.9 published to PyPI with no `TWINE_USERNAME`/`TWINE_PASSWORD` anywhere in the workflow, which means the OIDC path is what authenticated. Both registries and the git tag now serve 0.34.9, and **no workflow references any stored publishing credential** — neither registry depends on something that can expire.
- **Bumped `pypa/gh-action-pypi-publish` to v1.14.2.** v0.34.8's PyPI publish failed with `InvalidDistribution: Invalid distribution metadata: '2.5' is not a valid metadata version` — the older pin (copied from `raccioly/websec-validator`) bundles a twine too old to understand `Metadata-Version: 2.5`, which current `setuptools`/`build` emits. The failure was in metadata validation, not authentication, so it says nothing either way about the Trusted Publishing migration; that still needs a green run to be confirmed. Pinned by dereferenced **commit** SHA, not the annotated tag object's own SHA — those differ, and pinning the wrong one silently fails to resolve.
  - Note for `websec-validator`: it still carries the old pin and will hit this same wall as soon as its `setuptools` moves forward.

## [0.34.8] - 2026-09-11

No code changes. Completes the credential-free release pipeline.

### Changed

- **PyPI publishing migrated from a stored API token to Trusted Publishing (OIDC)**, matching what `publish-npm` now does. `PYPI_API_TOKEN` was last rotated 2026-03-15 and was the next stored credential due to expire silently and take the pipeline down — exactly the failure that cost seven releases on the npm side (bug-256/bug-259). `publish-pypi` now runs with `environment: pypi` + `id-token: write` and publishes via `pypa/gh-action-pypi-publish`; the trusted publisher is registered on PyPI as `raccioly` / `docguard` / `release.yml` / environment `pypi`. Mirrors the working setup already in `raccioly/websec-validator`. Neither registry now depends on a credential that can expire.
- Added an OIDC precondition assert to `publish-pypi` so a missing `id-token: write` fails with a message naming the cause rather than as an opaque auth error.

## [0.34.7] - 2026-09-11

**npm publishing is fixed.** First release since v0.34.0 to reach npm, PyPI, and GitHub in sync. Seven releases were needed because three independent faults were stacked, each masking the next.

### Fixed

- **Node 24 *and* no `registry-url` together.** Tracing every prior failure showed each run had exactly one of the two blockers, never neither: v0.34.4 (Node 20 + `registry-url`) → `E404`, Node below the 22.14.0 OIDC floor so the empty `_authToken` was used; v0.34.5 (Node 20, no `registry-url`) → `ENEEDAUTH`, no token but Node still too old; v0.34.6 (Node 24 + `registry-url`) → `E404`, Node fine but the empty token `setup-node` writes took the token path. Node ≥ 22.14 clears the OIDC floor and omitting `registry-url` stops an empty `_authToken` being written; npm defaults to registry.npmjs.org regardless.
- **Added auth-state diagnostics and `--loglevel verbose` to the publish step**, so a further failure reports which auth path npm chose rather than leaving it to inference from a generic 404.
- **The final fault: the npm trusted publisher was stored with a trailing slash in its Repository field** (`docguard/`), so the saved config read `raccioly/docguard/` and never matched the OIDC claim `raccioly/docguard`. This was invisible until the first two faults were cleared, because only then did npm get far enough to attempt the exchange and say so: `POST /-/npm/v1/oidc/token/exchange/package/docguard-cli → "OIDC token exchange error - package not found"`. Fixed by adding a second trusted publisher with the exact value (additively — never deleting the only publisher on a package mid-repair). The `--loglevel verbose` added in this same release is what surfaced it.

## [0.34.6] - 2026-09-11

No code changes. The actual root cause of the npm publish failure, after five wrong diagnoses.

### Fixed

- **`publish-npm` now runs Node 24 (was Node 20).** This was the real blocker the whole time. npm Trusted Publishing requires **npm CLI >= 11.5.1 AND Node >= 22.14.0**. The job ran Node 20 for five straight releases, so npm never *attempted* the OIDC exchange — it silently fell through to token auth and failed there. That produced two convincing red herrings in sequence: `E404 "not found or you do not have permission"` while an empty `_authToken` was present, then `ENEEDAUTH` once it wasn't. Neither error names the version floor, which is what made this take so long.
- **Restored `registry-url` on `setup-node`** — v0.34.5 removed it on a wrong theory (below). npm's documented example sets it; the empty `_authToken` it writes only ever mattered *because* Node 20 had already taken OIDC off the table.
- **Dropped the `--provenance` flag.** Under Trusted Publishing npm generates and publishes provenance attestations automatically; the flag is redundant.
- **Replaced the auth-token guard with a precondition assert.** It now verifies Node >= 22.14.0, npm >= 11.5.1, and that `ACTIONS_ID_TOKEN_REQUEST_URL` is present (i.e. `id-token: write` actually took effect) — failing with a message that names the real cause instead of surfacing it as an unrelated-looking auth error three minutes later. Boundary cases verified locally: 22.13.0 blocked / 22.14.0 passes, 11.5.0 blocked / 11.5.1 passes.

## [0.34.5] - 2026-09-11

Fifth attempt at the npm publish. **The diagnosis in this entry was wrong** — see v0.34.6 for the actual cause. Tagged, GitHub-released, and on PyPI; never reached npm.

### Fixed

- **Fixed the flaky watch-mode test that was blocking releases** (`tests/commands.test.mjs`, "starts watch mode and reacts to file changes"). It waited on `'Watching 5 directories' || 'Watching for changes'` — but the real count is 4, so the first branch never matched and it always fell through to the second, which prints *before* the initial guard run and before the fs watchers are registered. The subsequent file write then landed in that gap and was missed. On a fast runner the guard run finished inside the 500ms buffer and it passed; on a loaded one it didn't — which is why it failed 2 of 3 release runs, on Node 24 and then Node 22. Now waits on `/Watching \d+ directories/`, the marker printed only once watchers are live. Verified 5x clean, plus 3x under deliberate CPU saturation to mimic a loaded runner. *(This fix was real and holds.)*
- ~~**Removed `registry-url` from the publish job's `setup-node`.**~~ **Incorrect — reverted in v0.34.6.** The theory was that it was breaking Trusted Publishing. `setup-node`'s `registry-url` writes an `.npmrc` containing `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}`; with `NODE_AUTH_TOKEN` removed in v0.34.3, that expanded to an **empty** token. npm then took the token-auth path holding no token and 404'd, never attempting the OIDC exchange — which is why the log showed provenance signing succeed, no OIDC notice at all, and then `404 ... could not be found or you do not have permission`. npm defaults to `registry.npmjs.org` anyway, so nothing is lost. Verified the npm-side trusted publisher config is correct and saved (`raccioly/docguard` → `release.yml`, with both `npm publish` and `npm stage publish` permissions).
- ~~**Added a fail-loud guard asserting no `_authToken` exists.**~~ **Superseded in v0.34.6** by a precondition assert on the Node/npm version floors, which is what actually needed guarding. (Worth keeping the detail that led here: `npm config get` refuses on auth keys — "protected" — and `npm config list --json` omits them entirely, so detecting a configured token requires grepping the `.npmrc` files directly.)
- **`scheduled-release.yml` no longer pushes directly to `main`.** Two stacked blockers made that impossible once branch protection landed: `main` now requires a PR and `github-actions[bot]` has no bypass; and a PR opened with `GITHUB_TOKEN` doesn't trigger workflows, so the required `test (18/20/22/24)` checks would never report and the PR could never merge. It now pushes a `release/vX.Y.Z` branch, opens a PR, and explicitly dispatches `ci.yml` against that branch so the checks do report. `auto-merge.yml` gained a correspondingly narrow rule for these: `github-actions[bot]` PRs merge only from a `release/` branch and only when they touch nothing beyond version, changelog, and generated extension metadata.

## [0.34.4] - 2026-09-11

No code changes from v0.34.3 — v0.34.3's own npm-publish fix (below) turned out to have a bug, caught by this very release attempt: `npm install -g npm@latest` resolved to npm 12.0.2, which requires Node `^22.22.2 || ^24.15.0 || >=26.0.0` — newer than the Node 20 this job runs on, so the self-update step itself failed before publish ever ran. Same class of mistake as the `@babel/parser` major bump earlier (bug-255): an unpinned "latest" floated past what the job's Node version supports.

### Fixed

- **Pinned the npm self-update to the 11.x line (`npm@11`, not `npm@latest`).** npm 11.x declares `engines: {node: '^20.17.0 || >=22.9.0'}` — compatible with the Node 20.20.2 this job actually runs, and 11.19.1 (what it resolves to) is well past the 11.5.0 floor Trusted Publishing needs. Verified locally in an isolated prefix on Node 20.20.2 (the exact runner version from the failure log) before pushing.

### Added

- **Test-runtime budget in CI (`TEST_BUDGET_MS`, 120s).** A test that walks an unintended tree still *passes* — it just takes forever, so no correctness gate catches it. PR #328 did exactly that (a test passed `/` as a project dir): `tests/schemas.test.mjs` went 118ms → >10s and the whole suite ~25s → 190s, green the entire time. CI now fails on the duration instead. Normal runs are ~33s (Node 24) to ~51s (Node 18), so the budget catches catastrophes without tripping on runner jitter.
- **`auto-merge.yml` — hands-off merging for green bot PRs.** Fires on CI completion, independently re-verifies all four Node legs via the API, then merges. Deliberately does *not* use `gh pr merge --auto`: GitHub's native auto-merge needs repo-level `allow_auto_merge` plus **required** status checks, and without required checks configured it merges immediately without waiting for CI — worse than merging by hand. Policy: dependabot patch/minor auto-merges (majors never — a `@babel/parser` major silently dropped Node 18 and broke a release); Jules PRs auto-merge only when test-only or docs-only, with anything touching `cli/` logic, `.github/`, or `package.json` labeled and held for a human.
- **`scheduled-release.yml` — weekly batched releases.** Mondays 09:00 UTC (plus `workflow_dispatch` for on-demand, with a patch/minor choice). Bumps the version, generates a changelog entry from the commits since the last tag, re-runs the suite and `guard` before committing, then pushes — which triggers `release.yml` to do the actual tag/GitHub Release/npm/PyPI publish. No-ops when nothing releasable has landed (`.wolf/` bookkeeping alone never justifies a release), so it won't cut empty versions.

### Changed

- **`jules-triage.yml` now detects duplicates by changed-file overlap, not just normalized titles.** Title-equality was too strict: #324 ("add tests for untested shared-ignore utilities"), #325 ("add missing tests for shared-ignore functions") and #329 ("Add unit tests for shared-ignore detection utilities") were the same work, all three sat open, and two would have collided on the same import block if merged together. Still metadata-only — never checks out PR code.

## [0.34.3] - 2026-09-11

No functional code changes from v0.34.2 — this release was meant to exercise the Trusted Publishing fix end to end (a re-run of a past workflow job uses the workflow file as it existed at that run's commit, so testing a workflow-file fix requires a fresh run) but its own fix had a bug — see v0.34.4. Tag, GitHub release, and PyPI landed; npm did not.

### Fixed

- **npm publish switched from a stored `NODE_AUTH_TOKEN` to Trusted Publishing (OIDC).** The token that broke v0.34.1/v0.34.2's npm publish (see their entries below) is retired — `publish-npm` now authenticates via the `id-token: write` OIDC token exchanged directly with npm's registry, matched against a trusted-publisher connection configured on npmjs.com (raccioly/docguard, `release.yml`). Nothing here can expire the way a stored token did; a short-lived token is minted per run.
- v0.34.1 and v0.34.2 remain permanently missing from npm (tag/GitHub release/PyPI all landed for both) — not worth backfilling now that publishing works again going forward. v0.34.3 joined them for a different reason (see above); v0.34.4 is the one to actually check landed.

## [0.34.2] - 2026-09-11

### Fixed

- **`@babel/parser` 7.29.7 → 7.29.8** — the newest 7.x patch, confirmed Node-18-compatible. Supersedes dependabot's repeat proposal of the 8.0.4 major bump (already reverted once in 0.34.1 — see below) with the correct fix. (#333)
- **`docs-diff`'s `collectCodeTests` ignored `.docguardignore`/`config.ignore` during recursive test-directory scans**, so excluded test files could still count toward test-coverage validation, producing false positives on projects that explicitly excluded fixture/generated test dirs. Both recursive walkers now filter through `shouldIgnore` before collecting. (#334)
- Doc example for `generate --dir` used a non-existent absolute path (`/path/to/project`), which fails with `EACCES` if anyone actually runs it verbatim. Now a relative path. (#338)

### Changed

- Bumped the `osv-scanner-action` reusable workflows (supply-chain scanning) from v2.3.8 to v2.5.1. (#336)
- **`.github/dependabot.yml`: added an ignore rule for `@babel/parser` major-version bumps.** The 8.x line requires Node `^22.18.0 || >=24.11.0`, silently dropping Node 18 (which this project supports and CI gates on) — confirmed twice now (#331 in 0.34.1, #335 closed this release) that dependabot will keep proposing it every cycle since it can't see the `engines` mismatch. Patches/minors on the 7.x line still flow through normally.

### Tests

- Added coverage for previously-untested exports: `surfaceConfidence`, `astTierAvailable`, `parseCheckedTasks`, `compileGlob`, `isRunnerEnvVar`. (#337, #339)

### Known issue

- **npm publish is blocked on an expired `NPM_TOKEN`.** `v0.34.1`'s npm publish failed identically three times (Aug 28 ×2, Sep 11) with a `404` on the registry PUT immediately after successful provenance signing — not a registry incident (npm status green throughout, `docguard-cli` package unaffected on the registry). The token was last rotated 2026-05-22 and last worked for the 2026-08-13 (v0.34.0) publish; the failure window is consistent with npm's ~90-day Automation-token expiration. `v0.34.1` and `v0.34.2` are both tagged, GitHub-released, and on PyPI, but missing from npm until the token is rotated.

## [0.34.1] - 2026-08-28

### Fixed

- **OpenAPI schema field with no `type` crashed schema sync.** `extractOpenAPIRelationships` called `.toLowerCase()` on `field.type` unconditionally — a field with no `type` (valid OpenAPI, e.g. a bare `$ref`) threw instead of being skipped. (#328)
- **TODO-Tracking never recognized Python/Go/Ruby test-file naming.** The test-file exclusion regex only matched the dotted JS convention (`foo.test.py`), which nobody writes — real Python/Go/Ruby test files are `test_foo.py`, `foo_test.py`, `foo_test.go`, `foo_spec.rb`. TODOs inside those files' actual test functions were flagged as untracked source TODOs. Now recognizes the idiomatic naming for all three. (#328)

### Changed

- Bumped `actions/setup-node` and `actions/setup-python` to v7 in CI/release workflows. (#330)

### Reverted

- **`@babel/parser` 8.0.4 → back to 7.29.7.** Dependabot's major-bump PR (#331) tested green locally and merged, but `8.0.4` requires Node `^22.18.0 || >=24.11.0` — it silently dropped Node 18 (and, per its own `engines` field, isn't really supported on 20 either, though it happened to run there). This project declares `"engines": {"node": ">=18.0.0"}` and CI gates all four Node versions; the release pipeline itself caught it — `test (18)` failed with 20 broken AST-parsing tests while 20/22/24 passed, so nothing published (npm/PyPI publish correctly never ran). Reverted and re-verified locally across Node 18, 20, 22, and 24 before re-releasing. Lesson: a major bump of a parsing library needs the full supported-version matrix tested, not just the developer's local Node version — `engines` mismatches don't show up as install failures, they show up as silent behavioral differences.

### Tests

- Added coverage for `walkFiles`, `isNonProductDir`, and `isNonProductPath` in `shared-ignore.mjs`. (#324)

## [0.34.0] - 2026-08-13

### Fixed

- **Canonical docs in subfolders were invisible across 19 call sites (`listCanonicalDocs`).** Every consumer of `docs-canonical/` enumerated it by hand with a flat `readdirSync(...).filter(f => f.endsWith('.md'))`, so a project that groups its canonical docs — `docs-canonical/01-architecture/MODULE-MAP.md`, a common convention past a handful of files — was scanned as if those docs did not exist. Replaced all 19 with one shared recursive enumerator (`listCanonicalDocs` in `shared-ignore.mjs`, built on the existing `walkFiles`), which honors `.docguardignore` per-doc against the full relative path, skips dot-directories while keeping dot-markdown files, and returns sorted project-relative POSIX paths.

  First pass (5 sites) fixed the worst offenders: **docs-sync** reported services as undocumented while the documentation sat right there (an *unfixable* DSY002 — editing the nested doc could never clear it); `docguard:validator … n/a` **markers** in nested docs were dropped, so a validator declared N/A ran anyway; **semantic-claims** never scanned nested docs for unverified claims; **agent-readability** scored an empty set; **ALCOA** reported "all docs updated within 30 days" against zero documents, so a stale nested tree scored green.

  Second pass (14 more sites, across `impact`, `init`, `llms`, `memory`, `trace`, `hooks`, and the `api-doc-smells`/`cross-reference`/`diff-suspicion`/`doc-quality`/`docs-coverage`/`generated-staleness`/`reference-existence`/`traceability` validators) closed the rest. The standout: **generated-staleness**'s cheap pre-flight check missed a nested `docguard:section source=code` marker and concluded "nothing to do" — the *entire validator* silently never ran, disabling drift detection outright rather than just under-reporting it. Also fixed: `trace --reverse <file>` falsely reporting no canonical doc references a file documented only in a nested doc; `reference-existence` never indexing symbol references made from nested docs; `init`'s re-init detection treating an already-initialized nested-docs project as first-run; a post-commit hook nudge that couldn't find docs referencing an edited file; and orphaned-doc detection in `traceability` that was blind to strays outside the top level.

  Two behavior notes: `.md` matching is now uniformly case-insensitive (some call sites lowercased, some didn't — one tool must not hold two opinions about what a canonical doc is), and projects with nested canonical docs will see freshness, readability, and claim counts move as those documents become visible for the first time. Where a site's message or `location` text is user-visible (validator findings, CLI output), the fix preserves the exact flat-tree wording — only visibility into nested docs changed, not the format of existing output. Verified against DocGuard's own repo (flat `docs-canonical/`, so a correct fix must produce identical output): `guard`/`score` JSON byte-identical apart from timings across both passes (282/291, 0 errors, 14 warnings). 18 regression tests across `tests/canonical-docs-nested.test.mjs` and `tests/canonical-docs-nested-phase2.test.mjs`; 9 of them fail against the previous release, proving they actually catch the bug.

## [0.33.1] - 2026-07-16

### Added

- **Claude Desktop one-click extension (`.mcpb`).** Each release now attaches `docguard-v<version>.mcpb` — the official MCP Bundle format: drag into Claude Desktop → Settings → Extensions, pick the project folder, done. No npm, no JSON editing. Built by the new `build-mcpb` release job from the exact npm-pack payload (manifest template in `mcpb/`, packed with `@anthropic-ai/mcpb`). README gained one-click install badges for Cursor and VS Code (MCP deeplinks) alongside the Claude Code and registry paths.

- **npm provenance attestation.** Releases now publish with `npm publish --provenance` (OIDC + Sigstore): every tarball carries a signed statement that it was built by this repo's GitHub Actions from a specific commit. This is what "unknown package" legitimacy checks (Claude Code, socket.dev, npm's Provenance badge) verify — DocGuard installs are now cryptographically attributable.
- **PRIVACY.md** — the short, honest policy: DocGuard collects nothing, all analysis is local, no telemetry; the three explicit user-initiated outbound paths (`feedback` URL, `gh`-backed PR commands, opt-in HTTP transport) are enumerated. Ships in the npm package and unblocks Anthropic Connectors Directory submission (a missing privacy policy is an instant rejection there).
- **FAQ**: why AI agents flag DocGuard as "unknown" on first install, and how to pre-trust it (project `.mcp.json`, Always allow, managed-settings allowlist).

## [0.33.0] - 2026-07-16

### Added

- **Adoption baseline — `guard --update-baseline` + committed `.docguard.baseline.json`.** The ESLint/semgrep-style brownfield pattern: freeze a legacy repo's existing findings once, commit the file, and guard/ci gate only NEW drift from then on — no red pipeline on adoption day. Fingerprints are content-addressed (finding code + location path with line numbers stripped + message with digit-runs normalized), so they survive line churn and volatile counts ("21 commits since…") — and they carry **occurrence counts**: freezing one hardcoded-secret finding in a file suppresses exactly one; a second instance of the same class surfaces and gates (the ESLint-baseline semantics). Suppression is never silent: the summary prints "N pre-existing finding(s) suppressed", `baselineSuppressed` rides the JSON contract, and `--no-baseline` (or `"baseline": false` in `.docguard.json`) shows everything. A malformed baseline is treated as absent — corruption can never un-gate CI. Applied inside `runGuardInternal`, so guard, ci, report, SARIF/JUnit outputs, and the MCP tools all honor it uniformly. Also: DocGuard's own files (`.docguard.json`, `.docguardignore`, `.docguard.baseline.json`) are exempt from Docs-Coverage's undocumented-config check — writing the baseline no longer instantly creates a DCV001 about the baseline.

- **`guard --format junit` — JUnit XML for the rest of the CI world.** SARIF covers GitHub Code Scanning; JUnit covers GitLab (`artifacts:reports:junit`), Jenkins (`junit` step), Azure DevOps, and CircleCI. One testcase per validator: error findings render as `<failure>` (red in every CI UI), a crashed validator renders as `<error>` (also red — never a silent pass), warn-only validators pass with findings in `<system-out>`, skipped/N/A validators map to `<skipped/>`. Strict XML escaping, exit codes identical to `guard`'s json/sarif branches, banner-free stdout. GitLab and Jenkins recipes added to CI-RECIPES (Recipe 1b).

- **`docguard_report` MCP tool.** The compliance-evidence bundle is now callable by agents: same payload as `docguard report --format json` (guard verdict, findings by code, score, ALCOA+, fix history, integrity hash), read-only annotations, over both stdio and HTTP transports. MCP tool count 5 → 6.

- **Score history + `score --trend`.** Every `docguard ci` run appends one line to `.docguard/history.jsonl` ({timestamp, commit, score, grade, guard counts, status}); `docguard score --trend` renders the trajectory — sparkline, first→latest delta, and the last 10 runs with commit stamps (`--format json` for the raw series). Opt out per run with `ci --no-history`. Local-first: `.docguard/` stays gitignored; in ephemeral CI, persist the file across runs with a cache/artifact step (see CI-RECIPES). The append is silent-on-failure — recording history never fails the pipeline it records.

- **`docguard report` — compliance-evidence bundle for audits.** Runs guard + score internally and emits a deterministic evidence report: tool version, git commit/branch/dirty state, per-validator guard verdict, findings grouped by stable code, CDD score with categories, the ALCOA+ data-integrity attribute table, and the mechanical-fix history from `.docguard/fixed.json`. The bundle carries a tamper-evident `sha256` integrity hash over the git-stable sections of the payload (the generation timestamp and the ALCOA+ block are excluded — the latter's Contemporaneous attribute is mtime/wall-clock-relative — so the same commit always reproduces the same hash, including on a fresh clone). Markdown to stdout by default, `--format json` for the machine bundle, `--out <file>` to write either to a file. Report is **evidence, not a gate**: it always exits 0 — `guard` and `ci` remain the commands that fail builds, so evidence collection never self-censors. Headless in both formats (stdout is the artifact; no banner bytes). Enterprise rationale: docs platforms ship no audit logs at any tier — DocGuard now generates a documentation audit trail from the repo itself.

### Fixed

- **Pre-release adversarial review — 12 findings fixed before ship.** An independent review pass over this release's batch caught and fixed, among others: `ci` missing from the read-only command set (a bare text-mode `ci` still scaffolded skills into the workspace it gates); baseline fingerprints suppressing NEW same-class findings in the same file (now occurrence-counted); `report`/`ci` not disclosing baseline suppression (now in the payload, the markdown summary, and history entries); JUnit rendering a crashed validator as a passing testcase; `ci` gating on raw instead of severity-aware counts (it now matches `guard` exactly); threshold failures recorded as `PASS` in history; `--update-baseline --changed-only` shrinking the committed baseline to the lite validator subset (now refused); and a non-atomic history trim. Every fix ships with a regression test.

- **`docguard ci` un-deprecated and made safe for pipelines.** The v0.20 consolidation routed `ci` through `init --with ci`, which (a) scaffolded missing canonical docs INTO the CI workspace — a validate command mutating the tree it validates — and (b) printed the deprecation warning + init chrome into `--format json` stdout, corrupting it for parsers. `ci` now dispatches straight to the gate (guard + score, read-only, machine-clean) and is a first-class command again; `init --with ci` still runs the gate once after init. `runCI` also moved off `console.log + process.exit` to `stdout.write + process.exitCode` — the >8 KB pipe-truncation class fixed for `guard --format json` in v0.28.

## [0.32.0] - 2026-07-11

Graph-informed release — detection improvements and integration surfaces
adapted from patterns proven in
[graphify](https://github.com/Graphify-Labs/graphify) (MIT), rebuilt
zero-dependency and deterministic for DocGuard. The detectors were empirically
validated read-only against five real production repos before shipping: zero
false positives; the indirect-impact analysis surfaced genuine, explainable
chains on two of them. Validator count unchanged (27).

### Added
- **VALIDATION.md** — an honest benchmarks-style page documenting the
  empirical method every detector goes through before shipping enabled
  (read-only corpus runs on real production repos, keep/cut/tune, dogfooding)
  and the measured v0.31/v0.32 results, including what DocGuard does NOT
  claim. Linked from the README header.
- **PR doc-conflict analysis — `docguard impact --prs`.** Maps every open
  PR's changed files to the canonical docs they impact (same reference index
  as regular impact; a PR editing a canonical doc directly counts too) and
  reports pairs of PRs impacting the SAME doc — a merge-order risk: whichever
  lands second must re-verify the shared doc. Uses the `gh` CLI (no token
  handling in DocGuard); degrades to a clear message when `gh` is missing or
  the repo isn't on GitHub. Capped at 20 open PRs per scan. The
  graph-community version of this idea ships in graphify's `prs --conflicts`;
  this is the doc-integrity equivalent.
- **MCP Streamable HTTP transport — `docguard mcp --transport http`.** One
  shared process can now serve the DocGuard tools to a whole team: JSON-RPC
  over POST (single + batch), 202 for notification-only bodies, session id
  issued on initialize (stateless server — accepted, never required), GET
  correctly 405s (no SSE stream offered). Zero-dep (`node:http`). Security
  posture: binds `127.0.0.1` by default; binding any non-loopback host
  REFUSES to start without `--api-key`/`DOCGUARD_API_KEY`; when a key is set
  every request must carry it (`Authorization: Bearer` or `X-API-Key`);
  browser cross-site origins are rejected (DNS-rebinding guard); 4 MiB body
  cap. Flags: `--port` (default 8585), `--host`, `--api-key`, `--path`
  (default `/mcp`). The stdio transport is unchanged and remains the default;
  both share one JSON-RPC dispatcher.
- **Agent nudge hook — `docguard hooks --claude`** (graphify's always-on-hook
  distribution pattern, pointed at doc integrity). Registers a `PostToolUse`
  hook in the project's `.claude/settings.json`: after the agent edits a
  canonical/agent doc it is nudged to run `docguard guard --changed-only`;
  after it edits a code file the docs reference, it is nudged toward
  `docguard impact`. Merge-safe (only DocGuard's own entry is added/removed;
  an unparseable settings.json is never touched), idempotent, throttled (one
  nudge per file per 30 min via `.docguard/nudge-state.json`), and the
  `docguard nudge-hook` runtime is silent-on-error by contract — it can never
  break an agent session. Explicit opt-in; `init`/`ensureSkills` never install
  it. Remove with `docguard hooks --claude --remove`.
- **ADR-citation check (REF002, reference-existence)** — the code→doc direction
  of reference existence. A code comment citing a decision record (uppercase
  `ADR-` + number in a comment) is now verified against the ADR documents the
  repo actually defines (single-file `ADR.md` sections, `ADR-*.md` filenames,
  and madr-style `docs/adr/0007-*.md`). Numbers compare as integers
  (`ADR-0011` matches `ADR-11`). Citations only count inside comments — string
  literals and identifiers are ignored — and tests/fixtures are excluded
  (non-product scoping). IETF RFC citations are deliberately out of scope
  (external registry — would false-positive on every `RFC 793` comment). Soft
  (`confidence: low`), capped at 10 findings, suppressible with
  `// docguard:ignore REF002`, disable with
  `referenceExistence.adrCitations: false`.
- **Obsidian wikilink support (Cross-Reference + impact)** — `[[Doc]]`,
  `[[Doc#Heading]]`, and `[[Doc|alias]]` are now validated like inline links
  (broken target → XRF001, broken heading → XRF002), and `impact`'s doc→doc
  blast radius sees wikilink dependents. Precision-gated: wikilinks are only
  validated when the repo demonstrably uses them as FILE links (`.obsidian/`
  exists, or at least one wikilink target resolves) — repos using `[[name]]`
  as a non-file convention are skipped silently. Image embeds `![[x.png]]`
  never count. Wikilink targets resolve sibling-first, then vault-wide by
  basename across the project's doc homes.
- **Indirect impact via the import graph (`docguard impact`)** — a changed file
  with no doc references can still invalidate docs about the modules that
  IMPORT it. `impact` now walks the reverse import graph (reusing the
  Architecture validator's graph builder — one builder, not two) up to 2 hops
  and reports docs describing an importer of a changed file, with the
  explainable chain (`doc describes X, which imports changed Y`). Hub modules
  (>15 imports, e.g. a CLI dispatcher) are suppressed — their docs would flag
  on every dependency change. Docs already directly affected are not repeated.
  JSON adds `indirectDocs`; disable with `--no-indirect`. JS/TS import graphs
  only (the graph builder's scope).
- **Graphify knowledge-graph interop (Traceability)** — teams that commit
  `graphify-out/graph.json` (a tree-sitter knowledge graph) get its doc↔code
  edges counted as linkage evidence before an "unlinked doc" (TRC002) is
  raised. Trust rules: only `EXTRACTED` edges count (never the LLM-derived
  `INFERRED`/`AMBIGUOUS` tiers), at least one linked code file must still
  exist (a stale graph can't vouch), and the graph is evidence-only — it can
  turn a warning into a pass but never produces a finding. Zero-dependency:
  one JSON read; malformed graphs are silently ignored.

### Fixed
- **Cross-Reference link parsing** — query strings are stripped before target
  resolution (`./DOC.md?plain=1#anchor` now resolves to `DOC.md` instead of a
  phantom file), and CommonMark angle-bracket targets with spaces
  (`[t](<my doc.md>)`) are resolved instead of being silently skipped.
- **Semantic-claim extractor honors `.docguardignore`** — a doc the user
  explicitly excluded from validation no longer feeds the "unverified claims"
  pool (guard notice, `verify --semantic`, the ALCOA `Accurate` pillar). On
  DocGuard's own repo an ignored historical audit contributed 28 of 39
  reported claims, burying the actionable ones.

## [0.31.0] - 2026-07-07

Accuracy release — six research-backed detectors that make drift detection
change-aware and language-agnostic, built on one shared diff foundation. Every
new check was empirically tuned read-only against six real production repos
(TypeScript + Python) before shipping; all are deterministic (no LLM at
validation time) and soft (`confidence: low`, never break CI). Validator count
24 → 27.

### Added
- **`docguard impact` — doc→doc blast radius + agent-instruction files** (feat 1).
  Agent-instruction files (AGENTS.md/CLAUDE.md/GEMINI.md) are now indexed, so a
  changed code file they reference is surfaced. New: when a canonical/agent doc
  changes, the docs that reference it — including agent-instruction files — are
  flagged as a "blast radius" (`{ changedDocs, blastRadius }` in JSON). No
  verified competitor propagates doc staleness across the doc graph. Proven on a
  real repo: an ARCHITECTURE.md change flags the AGENTS.md/CLAUDE.md that cite it.
- **Diff-Suspicion validator (DSP001)** (feat 3) — change-driven. A doc that BOTH
  references a code file changed since the ref AND shares domain tokens removed
  in that diff is flagged for review. Deterministic diff-overlap rule
  (arXiv 2010.01625, F1 74.7); path/module refs + domain-token filtering +
  per-doc cap keep it quiet at PR granularity.
- **Reference-Existence validator (REF001)** (feat 2) — two-revision check. A
  compound code identifier backticked in a doc that existed when the doc was last
  updated but has ZERO matches at HEAD is flagged as outdated (arXiv 2212.01479).
  In-memory HEAD identifier set + authoritative git-grep confirmation; zero false
  positives across the corpus.
- **API-Doc-Smells validator (APS001 Bloated / APS002 Lazy)** (feat 4) —
  deterministic length signals on signature-headed doc units (F1 0.90 / 0.95).
- **IR-based traceability soft-matching** (feat 5) — `cli/shared-ir.mjs`
  (zero-dep TF-IDF + cosine). An untraced requirement now surfaces the

<!-- truncated: 2251 more lines — read CHANGELOG.md directly -->

---

## ROADMAP.md
> Planned features and development roadmap

# DocGuard Roadmap

<!-- docguard:last-reviewed 2026-09-14 -->

This file contains current product intent only. Released work belongs in
`CHANGELOG.md`; implementation history remains recoverable from Git. Completed or
superseded specifications leave the working tree through `docguard retire` so
people and AI agents do not mistake old plans for current requirements.

DocGuard's product goal is dependable, low-maintenance evidence that connects
approved intent, implementation facts, tests, and operational reality. A clean
structural score is useful, but it is not proof that arbitrary prose is true.

## Current priorities

### R1 — Document lifecycle and context hygiene (in progress)

Give specifications and planning documents an explicit end of life.

- [ ] Ship `docguard retire --plan|--check` and explicit, fail-closed writes.
- [ ] Keep archived content in Git and record only recovery metadata in
  `.docguard-archive.json`; do not copy obsolete prose into a second document tree.
- [ ] Retire DocGuard's own completed specs, migration plans, and historical
  audits after their current outcomes are represented in canonical docs and the
  changelog.
- [ ] Add lifecycle status validation for `active`, `completed`, `superseded`,
  and `archived`; task completion and `Completed` artifact maturity remain
  review signals rather than proof of retirement.
- [ ] Add optional Spec Kit hooks that check archive readiness after convergence.
- [ ] Add `.docguard-specs.json`, a committed lifecycle control plane. Reviewed
  approval, delivery, context, storage, persistence policy, lineage, and scope
  are authoritative; requirement references and
  implementation/test evidence are deterministic projections. Approved prose
  remains the source of behavioral intent.
- [ ] Add a dedicated `docguard specs` command family: deterministic
  `--write|--check`, advisory request briefing, generated-spec preflight, and a
  reviewed completion transition. No other command writes lifecycle state.
- [ ] Add a `before_specify` briefing and a generated-spec gate so the actual
  draft is checked against active and prior requirements plus current code before
  planning starts. The briefing informs; only the reviewable draft can be gated.
- [ ] Give every spec an immutable metadata ID; use
  `specId#requirementId` for completion evidence and preserve retired identities
  as registry tombstones so bare IDs cannot rebind.
- [ ] Add an `implemented → verified` completion transaction that appends a
  bounded outcome record, refreshes mechanical facts, records reconciliation,
  and regenerates active AI context.
- [ ] Cross-check spec storage state against `.docguard-archive.json`. The
  archive manifest owns document recovery; the spec registry owns governance,
  and disagreement between them blocks a transition.

Contribution slices: registry schema and invariants, pure registry projector,
trace evidence extraction, transition validator, additional status formats,
monorepo path handling, archive-manifest consistency, and Spec Kit lifecycle
fixtures. Every detector change needs a stale example and a neighboring current
example.

### R2 — Reconcile behavior changes made outside a spec (planned)

Detect post-hoc implementation changes without silently redefining approved
intent. `docguard reconcile --since <ref>` will classify affected material:

1. mechanical code facts that `sync` can safely refresh;
2. approved requirements that may indicate a code regression;
3. superseded decisions that need a replacement or archive action;
4. unsupported or ambiguous evidence that needs human review.

The command will produce a review plan before any write. It must never rewrite a
requirement merely because the current code differs. Acceptance requires seeded
examples for intentional behavior changes, accidental regressions, and unrelated
edits; each class must remain distinguishable in JSON output.

Spec Kit already publishes persistence models and supports lifecycle hooks, while
community Archive and Reconcile extensions perform agent-authored artifact
updates. DocGuard will validate and index those outcomes rather than duplicate
their prompt workflows. A future upstream contribution should standardize only
the generic lifecycle metadata or hook contract after interoperability is proven.

Contribution slices: changed-symbol-to-spec impact mapping, replacement-spec
links, decision record support, Archive/Reconcile extension fixtures, and
`after_implement`/`after_converge` evidence gates.

### R3 — Independent precision benchmark (planned)

Build a reproducible corpus beyond the maintainer's projects. Sample JavaScript,
TypeScript, Python, fallback-language, monorepo, generated-code, and sparse-doc
repositories. Label clean controls, real defects, synthetic mutations, ambiguous
cases, and unsupported syntax independently of DocGuard output.

Report precision, recall, false positives per repository, abstention, unsupported
coverage, cold/warm runtime, and accepted repairs by detector family and parser
tier. Split development and evaluation by repository and causal bug family.
Thresholds will be set after measuring baseline variance; lowering warnings by
skipping supported cases does not qualify as an improvement.

Contribution slices: redistributable fixture snapshots, adjudication schema,
corpus runner, result visualizer, and language-specific labeled cases.

### R4 — Contribution-to-regression loop (planned)

Turn disputed findings into safe public regression cases. Extend `feedback` with
a fixture manifest that records detector family, configuration, expected result,
and the opposite control. Classify false positive, false negative, unsupported
syntax, and policy disagreement separately.

Public payloads remain opt-in and use synthetic content. Search open and closed
issues and pull requests before submission. An accepted detection change must
include the reproduction, its neighboring control, and a regression test.

Contribution slices: false-negative intake, fixture reducer with an explicit
interestingness predicate, duplicate identity, maintainer triage commands, and
test-only contribution templates.

### R5 — Evidence-scoped verification (planned)

Replace broad age-based review prompts with declared source-to-document
dependencies where available. Start with bounded claim types such as named JSON
values, enum sets, and counts tied to documented collections. Results remain one
of verified-within-scope, contradicted, unsupported, inconclusive, or stale.

Contribution slices: dependency declarations, exact claim predicates, saved
oasdiff/Buf evidence adapters, and review invalidation fixtures. Upstream tools
retain ownership of their domain semantics; DocGuard links results to affected
prose, examples, requirements, and migration guidance.

### R6 — Language and repository coverage (planned)

Add capabilities only with explicit applicability and controls. Priorities are
Python import relationships, additional Worker binding forms, custom document
role writers with section ownership, and repository-root guidance for monorepos.
Unsupported extraction must remain visible and must not become a success claim.

Contribution slices: one parser or framework per pull request, paired supported
and unsupported fixtures, and benchmark deltas for any performance-sensitive
scanner change.

### R7 — Task-specific agent context (research)

Evaluate targeted evidence packets against ordinary repository context and the
existing DocGuard context pack. Freeze repository snapshots, model/harness
versions, prompts, and budgets; measure hidden-test success, requirement
violations, unnecessary edits, tokens, latency, and human intervention.

Ship only if repeated trials improve task outcomes or reduce cost within a
predeclared non-inferiority margin. An LLM judge or DocGuard score alone is not
sufficient evidence.

## Contribution standard

Before opening work, search existing open and closed issues and pull requests.
Each proposal should name the failure mode, include a minimal reproduction and a
valid control, state supported and unsupported scope, and define the acceptance
test. See `CONTRIBUTING.md` for repository mechanics.

## Deferred ideas

A hosted dashboard, leaderboards, and notification integrations remain deferred
until user research shows that the CLI, CI outputs, and existing observability
systems cannot meet a concrete team need. They are not active commitments.


---

## AGENTS.md
> AI agent behavior rules and workflow instructions

# AI Agent Instructions — DocGuard

<!-- docguard:last-reviewed 2026-09-11 -->

> 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
- **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` | Database schemas |
| `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 <ref>` 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 <name>`), `generate` (reverse-engineer docs;
`--plan`), `retire` (remove reviewed docs from active context),
`explain` (explain a validator/warning), `memory` (what DocGuard
remembers), `trace` (requirements traceability; `--reverse`), `upgrade` (migrate
config/CLI), `watch` (live re-guard).

**`init --with <name>`** 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), `findings[]`
(`{code, severity, confidence, message, location, suggestion}`), `nextStep`,
`reportable[]` (low-confidence findings — verify before acting), `coverage`
(Markdown tier map incl. `unclassified[]`), and `semanticClaims.count`
(documented numbers not yet verified against code).

- Every finding has a stable code (`STR001`, `ENV003`, `XRF002`, …) — all 28
  validators emit them. `docguard explain <CODE>` gives the contract and fix.
- 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 <CODE>`
  (reason required) or `<!-- docguard:validator <key> n/a — reason -->`, 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`.

## 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 |

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:

| Hook | When | Required? |
|------|------|-----------|
| `after_implement` | After `/speckit.implement` | Mandatory |
| `before_tasks` | Before `/speckit.tasks` | Optional |
| `after_tasks` | After `/speckit.tasks` | Optional |

## Extension Structure

```
extensions/spec-kit-docguard/
├── skills/                    # AI behavior protocols
│   ├── docguard-guard/SKILL.md
│   ├── docguard-fix/SKILL.md
│   ├── docguard-review/SKILL.md
│   └── docguard-score/SKILL.md
├── scripts/bash/              # Orchestration scripts (--json output)
├── commands/                  # Spec Kit slash commands
├── templates/                 # Hook registration templates
└── extension.yml              # Skills, scripts, hooks declaration
```

## Rules

- **PR-first workflow — no direct-to-main commits.** Create a branch (`git checkout -b <type>/<slug>`), 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
- If code deviates from docs, add `// DRIFT: reason`
- Security rules in SECURITY.md are mandatory
- Test requirements in TEST-SPEC.md must be met
- Run `docguard guard` before pushing — all checks must pass
- All file writes use `safeWrite()` — backups before overwrite


## Agent Rules

### Automated agents / bots (Jules "Sentinel", "Bolt", "Palette", and any auto-PR agent)
- **Never open a duplicate PR.** Before opening ANY PR, search existing **open
  AND closed** PRs and issues for the same topic/title. If it exists, STOP — do
  not open another. (Dozens of duplicate command-injection and diff-optimization
  PRs were closed as noise.)
- **Do not re-open resolved work.** See `.jules/sentinel.md` (execSync/command
  injection — RESOLVED in v0.21.1 + #296) and `.jules/bolt.md` (diff/scan
  micro-optimizations — already applied; code refactored since). These are
  historical learnings, **not** standing mandates to re-scan every run.
- **Bar for a new PR:** a genuinely new, unaddressed finding, with evidence — a
  concrete exploit path / failing test (security) or a benchmark showing >20%
  real-workload improvement (performance). A Big-O note alone is insufficient.
- This repo has **no web UI and no VS Code extension** — skip all UX tasks.

### 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.

## 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 <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.


---
Generated by DocGuard | [docguard-cli](https://www.npmjs.com/package/docguard-cli)
