Skip to content

feat(docs): add doc-reference drift gate#318

Merged
laurigates merged 2 commits into
mainfrom
docs/add-doc-reference-check
Jul 12, 2026
Merged

feat(docs): add doc-reference drift gate#318
laurigates merged 2 commits into
mainfrom
docs/add-doc-reference-check

Conversation

@laurigates

Copy link
Copy Markdown
Owner

Summary

The guardrail follow-up to #317. A deterministic gate for the mechanical slice of doc drift — docs referencing scripts/paths/links that no longer exist — so the class #317 fixed by hand can't silently return.

Stacked on #317 (base = docs/fix-stale-references): it needs the fixed docs to pass. Merge #317 first, then this. (If #317 is squash-merged and its branch deleted, retarget this PR's base to main before that merge to avoid the auto-close trap — GitHub doesn't retarget stacked children.)

Why not surface?

Researched the surface tool you linked + /configure:surface. It anchors prose claims to code-symbol logic fingerprints (flipped operator, dropped await) in Rust/TS/Python/Go. This is a dotfiles repo — almost no code symbols to anchor to — and every one of #317's 19 findings was a dead reference or superseded prose, none logic drift. Surface would've caught zero of them. Right tool, wrong repo. Built the reference-existence checker your claude-plugins repo's check-plugin-readme-currency.sh pattern already points at instead.

What it does

Layer Behavior
scripts/check-doc-references.py Precision-first. Flags relative markdown links that don't resolve + inline-code paths anchored to a real top-level repo entry (incl. ./name root scripts). chezmoi-aware (resolves rendered names against executable_/dot_/*.tmpl sources). Skips slash commands, domains, placeholders, ~/, fenced code. Structured KEY=VALUE/STATUS= output.
Pre-commit hook Advisory — always exits 0, verbose nudge. Never blocks a commit (judgment calls shouldn't hard-gate).
mise run lint:docs Strict (--strict, exit 1) — the enforcing variant for CI; also folded advisory into mise run lint.
.doc-reference-allow Scopes out immutable ADRs + the exact_dot_claude/ global tree (references external plugins / other projects, not this repo).
scripts/tests/test-check-doc-references.sh Pins the flag/clean contract; asserts live tree STATUS=OK.

Design split (why mechanical + semantic, not one tool)

  • Mechanical gate (this PR): reproducible "does X exist?" — a script, run every commit, free of context cost. Catches Themes 1/2/4/8.
  • Semantic backstop: the doc-drift-sweep workflow, run on a cadence, for "is this prose still true?" (Themes 3/5/6) — judgment a hash-checker can't make.

Validation

  • Live tree: DANGLING_COUNT=0, STATUS=OK.
  • Fire test: flags ./update-ai-tools.sh, dead full paths, broken links; passes chezmoi-managed scripts, slash commands, domains, valid links.
  • Regression test + shellcheck + ruff: all pass. Pre-commit advisory hook runs in 0.05s.

🤖 Generated with Claude Code

laurigates and others added 2 commits July 12, 2026 05:18
A multi-agent sweep of all 126 repo docs (24 agents, adversarially
verified) found references to scripts, tasks, files, and models that
changed but whose docs never followed. Fixes span 8 root causes:

- MCP scoping: drop the deleted update-ai-tools.sh; document the
  per-project .mcp.json model + /configure:mcp (README, CLAUDE.md,
  package-registries.md, .chezmoidata.toml, cleanup-mcp-servers.sh).
- Secret scanning: detect-secrets -> gitleaks; .secrets.baseline was
  removed in #169 (CLAUDE.md, scripts/README.md, project-overview.md).
- .chezmoidata split: registries moved to .chezmoidata/*.toml; repoint
  completions + uv_tools refs (shell-completions.md,
  homebrew-mise-migration-analysis.md, project-overview.md).
- Blueprint externalized to blueprint-plugin: note the in-repo
  skills/commands no longer exist (exact_dot_claude/docs/blueprint-development/*).
- Dangling sub-doc links: remove exact_dot_claude/commands|skills/CLAUDE.md
  bullets (CLAUDE.md).
- nvim AI plugin swap: CodeCompanion -> claudecode.nvim/opencode.nvim
  (docs/components.md, neovim-configuration/SKILL.md).
- mise task rename: test:docker -> docker (testing.md).
- Stale paths/versions: blueprint v3.2.0 -> v3.3.0, drop nonexistent
  work-orders/, ai_docs/, and private_i3/ rows (blueprint/README.md,
  private_dot_config/CLAUDE.md).

Also folds in pre-existing README markdownlint fixes (blank lines before
code fences; claude /plugin install -> claude plugin install).

ADRs left untouched as immutable point-in-time records.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrwvogo4vM5kfpyrjoaPad
A deterministic guardrail for the mechanical slice of doc drift that PR #317
just fixed by hand: docs referencing scripts/paths/links that no longer exist.
Complements the periodic LLM sweep, which owns the semantic "is this prose
still true?" tail — the mechanical check owns "does X exist?".

- scripts/check-doc-references.py: precision-first checker. Flags relative
  markdown links that don't resolve, and inline-code path tokens anchored to a
  real top-level repo entry (incl. `./name` root scripts). chezmoi-aware
  (resolves rendered names against executable_/dot_/*.tmpl sources), skips
  slash commands, domains, placeholders, ~/ paths, and fenced code. Structured
  KEY=VALUE/STATUS output; advisory by default, `--strict` exits 1.
- .doc-reference-allow: scopes out immutable ADRs and the exact_dot_claude/
  global tree (references external plugins / other projects, not this repo).
- Pre-commit: advisory hook (always exits 0, verbose nudge) — never blocks.
- mise: `lint:docs` (strict, for CI) + advisory step folded into `lint`.
- scripts/tests/test-check-doc-references.sh: pins the flag/clean contract and
  asserts the live tree is STATUS=OK.

Verified: catches update-ai-tools.sh / dead full paths / broken links; passes
chezmoi-managed scripts, slash commands, domains. Live tree clean (0 dangling).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lrwvogo4vM5kfpyrjoaPad
@github-actions github-actions Bot added documentation Improvements or additions to documentation security Security-related fix or improvement size/m labels Jul 12, 2026
Base automatically changed from docs/fix-stale-references to main July 12, 2026 03:12
@laurigates
laurigates merged commit 004819e into main Jul 12, 2026
2 checks passed
@laurigates
laurigates deleted the docs/add-doc-reference-check branch July 12, 2026 03:13
laurigates added a commit that referenced this pull request Jul 12, 2026
## Summary

The queued follow-up from the doc-drift work. ADR 0015 replaced
detect-secrets with **gitleaks** (#169) and deleted `.secrets.baseline`,
but the detect-secrets integration lingered across the repo — recipes,
tasks, hooks, and permission grants all pointing at the now-missing
baseline. This excises it and routes every secret scan through gitleaks
(already installed via `mise` `aqua:gitleaks`).

## Removed
- `scripts/audit-secrets-baseline.py`,
`scripts/audit-secrets-selective.py` — baseline-audit tools for the
deleted `.secrets.baseline`
- `scripts/README.md` — was *entirely* the detect-secrets audit manual
(no inbound links; preserved in git history)

## Rewired detect-secrets → gitleaks
Using the `dir` subcommand (gitleaks **v8.18+ dropped `detect`**):

| Site | Change |
|---|---|
| `justfile` | `secrets` recipe + `doctor` optional-tools list |
| `mise` config | `security:audit` step, `security:scan` task, commented
enter-hook |
| `nixos/home/development.nix` | pre-commit hook → gitleaks v8.30.1 |
| `.github/claude-tools-config.json`,
`exact_dot_claude/modify_settings.json` | permission grants → narrow
`gitleaks dir` / `gitleaks` |
| `.github/labeler.yml` | security-label glob `.secrets.baseline` →
`.gitleaks.toml` |
| `docs/mise-migration-guide.md` | task descriptions |

## Bonus fix
`CLAUDE.md` used `gitleaks detect --source .` (from #317) — but `detect`
was removed in gitleaks 8.18+. Corrected to `gitleaks dir .` (verified
against the installed 8.30.1).

## Left as-is (deliberate)
- **ADR 0012 / 0015** — immutable point-in-time records; they document
the detect-secrets→gitleaks decision historically.
- **`exact_dot_claude/docs/prds/daily-catchup.PRD.md`** —
external-facing PRD for an unrelated feature.

## Guardrail earned its keep
The #318 doc-reference gate flagged a **latent bug from #318 itself**
mid-change: `scripts/CLAUDE.md` referenced `tests/…` (resolves to
top-level `tests/`) instead of `scripts/tests/…`. Fixed here.

## Validation
- `mise run lint:docs` → `STATUS=OK` (0 dangling)
- `git rev-parse` sanity: `gitleaks dir .` runs clean on the tree (no
leaks); `just --list` parses; `modify_settings.json` overlay yields
valid JSON
- Regression test + pre-commit (incl. gitleaks) → all pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation security Security-related fix or improvement size/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant