@adia-ai/adia-ui-forge 0.1.3 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +4 -7
- package/CHANGELOG.md +35 -34
- package/README.md +35 -40
- package/agents/a2ui-engineer.md +22 -53
- package/agents/component-author.md +29 -0
- package/agents/framework-verifier.md +26 -0
- package/agents/release-engineer.md +21 -81
- package/agents/routing-corpus.json +293 -64
- package/bin/demo-postwrite-pattern-gate +94 -0
- package/bin/forge-lint +17 -2
- package/bin/sidecar-prewrite-guard +104 -0
- package/commands/deploy.md +9 -0
- package/commands/dogfood.md +10 -0
- package/commands/gen-review.md +9 -0
- package/commands/release.md +10 -0
- package/hooks/hooks.json +15 -0
- package/package.json +3 -6
- package/references/contracts/a2ui-mcp-surface.md +35 -0
- package/references/contracts/migration-guide-format.md +34 -0
- package/references/shared/content-trust.md +19 -74
- package/skills/adia-a2ui/SKILL.md +99 -0
- package/skills/{adia-ui-a2ui → adia-a2ui}/evals/routing-corpus.json +10 -10
- package/skills/adia-a2ui/references/anti-patterns.md +27 -0
- package/skills/adia-a2ui/references/chunk-authoring.md +71 -0
- package/skills/adia-a2ui/references/corpus-discipline.md +68 -0
- package/skills/adia-a2ui/references/eval-diagnostics.md +86 -0
- package/skills/adia-a2ui/references/format-extension-decisions.md +66 -0
- package/skills/adia-a2ui/references/leverage-rules.md +52 -0
- package/skills/adia-a2ui/references/mcp-pipeline-ops.md +83 -0
- package/skills/adia-a2ui/references/mcp-tool-reference.md +59 -0
- package/skills/adia-a2ui/references/pipeline-overview.md +115 -0
- package/skills/adia-a2ui/references/semantic-fail-lifting.md +74 -0
- package/skills/adia-a2ui/references/strategy-engines.md +109 -0
- package/skills/adia-a2ui/references/zettel-calibration.md +104 -0
- package/skills/adia-author/SKILL.md +112 -0
- package/skills/adia-author/evals/routing-corpus.json +222 -0
- package/skills/{adia-ui-authoring → adia-author}/references/anti-patterns.md +33 -4
- package/skills/{adia-ui-authoring → adia-author}/references/authoring-cycle.md +9 -11
- package/skills/adia-author/references/canonical-pattern-index.md +243 -0
- package/skills/{adia-ui-authoring → adia-author}/references/code-style.md +19 -21
- package/skills/adia-author/references/common-gotchas.md +129 -0
- package/skills/adia-author/references/composite-demo-protocol.md +271 -0
- package/skills/{adia-ui-authoring → adia-author}/references/css-patterns.md +19 -6
- package/skills/{adia-ui-authoring → adia-author}/references/lifecycle-patterns.md +1 -0
- package/skills/{adia-ui-authoring → adia-author}/references/llm-bridge.md +3 -5
- package/skills/{adia-ui-authoring → adia-author}/references/module-promotion.md +20 -38
- package/skills/{adia-ui-authoring → adia-author}/references/primitive-audit.md +2 -4
- package/skills/{adia-ui-authoring → adia-author}/references/shell-patterns.md +12 -18
- package/skills/{adia-ui-authoring → adia-author}/references/token-contract.md +5 -7
- package/skills/{adia-ui-authoring → adia-author}/references/worked-example.md +3 -3
- package/skills/{adia-ui-authoring → adia-author}/references/yaml-contract.md +3 -3
- package/skills/{adia-ui-authoring → adia-author}/scripts/build-canonical-pattern-index.mjs +3 -3
- package/skills/adia-deploy/SKILL.md +128 -0
- package/skills/{adia-ui-ops → adia-deploy}/references/deploy-playbooks.md +47 -30
- package/skills/adia-dogfood/SKILL.md +124 -0
- package/skills/adia-dogfood/references/admin-shell-anatomy.md +77 -0
- package/skills/adia-dogfood/references/app-shell-pitfalls.md +66 -0
- package/skills/adia-dogfood/references/card-anatomy-sweep.md +66 -0
- package/skills/adia-dogfood/references/html-attr-sweep.md +63 -0
- package/skills/adia-dogfood/references/native-leak-annotations.md +73 -0
- package/skills/adia-dogfood/references/visual-probe-triage.md +80 -0
- package/skills/{adia-ui-dogfood → adia-dogfood}/scripts/analyze.mjs +30 -14
- package/skills/adia-gen-review/SKILL.md +127 -0
- package/skills/adia-gen-review/references/corpus-html-patterns.md +279 -0
- package/skills/adia-gen-review/references/loop-protocol.md +216 -0
- package/skills/adia-gen-review/references/rubric-cosmetic.md +100 -0
- package/skills/adia-gen-review/references/rubric-decompose.md +101 -0
- package/skills/adia-gen-review/references/rubric-score.md +214 -0
- package/skills/{adia-ui-gen-review → adia-gen-review}/references/scores.schema.json +2 -2
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-coverage-audit.mjs +4 -4
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-decompose.mjs +10 -6
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-status.mjs +5 -5
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/validate-cycle-scores.mjs +5 -5
- package/skills/adia-llm-internals/SKILL.md +77 -0
- package/skills/{adia-ui-llm → adia-llm-internals}/references/adapter-contract.md +4 -2
- package/skills/{adia-ui-llm → adia-llm-internals}/references/add-a-provider.md +4 -5
- package/skills/{adia-ui-llm → adia-llm-internals}/references/bridge-facade.md +10 -3
- package/skills/{adia-ui-llm → adia-llm-internals}/references/browser-proxy-boundary.md +1 -1
- package/skills/{adia-ui-llm → adia-llm-internals}/references/model-registry.md +5 -3
- package/skills/{adia-ui-llm → adia-llm-internals}/references/streaming-sse.md +2 -2
- package/skills/adia-release/SKILL.md +72 -0
- package/skills/adia-release/references/changelog-discipline.md +119 -0
- package/skills/adia-release/references/cut-procedure.md +247 -0
- package/skills/adia-release/references/gates-catalog.md +222 -0
- package/skills/adia-release/references/independent-package-release.md +52 -0
- package/skills/adia-release/references/migration-guide-authoring.md +86 -0
- package/skills/adia-release/references/notes-authoring.md +90 -0
- package/skills/adia-release/references/recovery-paths.md +106 -0
- package/skills/{adia-ui-release → adia-release}/scripts/bump.mjs +28 -3
- package/skills/{adia-ui-release → adia-release}/scripts/dispatch-publish.mjs +10 -7
- package/skills/{adia-ui-release → adia-release}/scripts/insert-stub.mjs +1 -1
- package/skills/{adia-ui-release → adia-release}/scripts/package-paths.mjs +6 -0
- package/skills/{adia-ui-release → adia-release}/scripts/promote-unreleased.mjs +1 -1
- package/skills/{adia-ui-release → adia-release}/scripts/release-pack.mjs +38 -31
- package/skills/{adia-ui-release → adia-release}/scripts/tag-lockstep.mjs +7 -3
- package/skills/adia-site-docs/SKILL.md +68 -0
- package/skills/adia-site-docs/evals/audit-report.md +30 -0
- package/skills/adia-site-docs/evals/routing-corpus.json +176 -0
- package/skills/adia-site-docs/intent.md +72 -0
- package/agents/README.md +0 -209
- package/agents/author.md +0 -56
- package/agents/repo-steward.md +0 -56
- package/agents/spec-architect.md +0 -53
- package/agents/tech-lead.md +0 -57
- package/agents/verifier.md +0 -55
- package/bin/lib/audit-axes.mjs +0 -555
- package/bin/lib/dry-run-irreversible.mjs +0 -236
- package/bin/lib/run-skill-evals.mjs +0 -487
- package/bin/lib/teach-router.mjs +0 -250
- package/commands/adia-forge-a2ui.md +0 -10
- package/commands/adia-forge-author.md +0 -10
- package/commands/adia-forge-dogfood.md +0 -8
- package/commands/adia-forge-llm.md +0 -8
- package/commands/adia-forge-orient.md +0 -10
- package/commands/adia-forge-release.md +0 -8
- package/commands/adia-forge-review.md +0 -10
- package/references/shared/pev-rationale.md +0 -64
- package/references/shared/skill-conventions.md +0 -133
- package/skills/adia-ui-a2ui/CHANGELOG.md +0 -32
- package/skills/adia-ui-a2ui/SKILL.md +0 -243
- package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +0 -75
- package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +0 -100
- package/skills/adia-ui-a2ui/references/anti-patterns.md +0 -24
- package/skills/adia-ui-a2ui/references/chunk-authoring.md +0 -88
- package/skills/adia-ui-a2ui/references/corpus-discipline.md +0 -56
- package/skills/adia-ui-a2ui/references/eval-diagnostics.md +0 -127
- package/skills/adia-ui-a2ui/references/fragment-graph.md +0 -91
- package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +0 -106
- package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +0 -398
- package/skills/adia-ui-a2ui/references/pipeline-overview.md +0 -175
- package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +0 -120
- package/skills/adia-ui-a2ui/references/strategy-engines.md +0 -111
- package/skills/adia-ui-a2ui/references/teach-protocol.md +0 -220
- package/skills/adia-ui-a2ui/references/zettel-calibration.md +0 -93
- package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +0 -96
- package/skills/adia-ui-a2ui/scripts/teach-route.mjs +0 -157
- package/skills/adia-ui-a2ui/skill.json +0 -38
- package/skills/adia-ui-authoring/CHANGELOG.md +0 -32
- package/skills/adia-ui-authoring/SKILL.md +0 -256
- package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +0 -101
- package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +0 -109
- package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +0 -113
- package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +0 -138
- package/skills/adia-ui-authoring/evals/routing-corpus.json +0 -245
- package/skills/adia-ui-authoring/references/canonical-pattern-index.md +0 -179
- package/skills/adia-ui-authoring/references/common-gotchas.md +0 -93
- package/skills/adia-ui-authoring/references/composite-demo-protocol.md +0 -1084
- package/skills/adia-ui-authoring/references/teach-protocol.md +0 -428
- package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +0 -148
- package/skills/adia-ui-authoring/skill.json +0 -45
- package/skills/adia-ui-dogfood/CHANGELOG.md +0 -17
- package/skills/adia-ui-dogfood/README.md +0 -62
- package/skills/adia-ui-dogfood/SKILL.md +0 -866
- package/skills/adia-ui-dogfood/skill.json +0 -40
- package/skills/adia-ui-forge/SKILL.md +0 -88
- package/skills/adia-ui-forge/evals/routing-corpus.json +0 -30
- package/skills/adia-ui-gen-review/CHANGELOG.md +0 -108
- package/skills/adia-ui-gen-review/SKILL.md +0 -266
- package/skills/adia-ui-gen-review/references/loop-protocol.md +0 -712
- package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +0 -144
- package/skills/adia-ui-gen-review/references/rubric-decompose.md +0 -117
- package/skills/adia-ui-gen-review/references/rubric-score.md +0 -249
- package/skills/adia-ui-gen-review/references/teach-protocol.md +0 -214
- package/skills/adia-ui-gen-review/skill.json +0 -23
- package/skills/adia-ui-llm/CHANGELOG.md +0 -25
- package/skills/adia-ui-llm/SKILL.md +0 -165
- package/skills/adia-ui-llm/evals/adversarial-corpus.json +0 -75
- package/skills/adia-ui-llm/evals/routing-corpus.json +0 -30
- package/skills/adia-ui-llm/evals/teach-routing-cases.json +0 -73
- package/skills/adia-ui-llm/references/teach-protocol.md +0 -78
- package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +0 -93
- package/skills/adia-ui-llm/scripts/teach-route.mjs +0 -119
- package/skills/adia-ui-llm/skill.json +0 -33
- package/skills/adia-ui-ops/CHANGELOG.md +0 -291
- package/skills/adia-ui-ops/SKILL.md +0 -401
- package/skills/adia-ui-ops/references/INDEX.md +0 -158
- package/skills/adia-ui-ops/references/audit-cadence.md +0 -263
- package/skills/adia-ui-ops/references/audit-patterns/archive-link-sweep.md +0 -96
- package/skills/adia-ui-ops/references/audit-patterns/audit-history-ledger.md +0 -162
- package/skills/adia-ui-ops/references/audit-patterns/browser-bundle-node-imports.md +0 -154
- package/skills/adia-ui-ops/references/audit-patterns/changelog-unreleased-bloat.md +0 -75
- package/skills/adia-ui-ops/references/audit-patterns/coverage-gaps.md +0 -187
- package/skills/adia-ui-ops/references/audit-patterns/entry-file-coverage.md +0 -122
- package/skills/adia-ui-ops/references/audit-patterns/format-hygiene.md +0 -217
- package/skills/adia-ui-ops/references/audit-patterns/lockstep-versioning.md +0 -113
- package/skills/adia-ui-ops/references/audit-patterns/memory-fragmentation.md +0 -180
- package/skills/adia-ui-ops/references/audit-patterns/orphan-detection.md +0 -202
- package/skills/adia-ui-ops/references/audit-patterns/pointer-validation.md +0 -180
- package/skills/adia-ui-ops/references/audit-patterns/redundancy-detection.md +0 -210
- package/skills/adia-ui-ops/references/audit-patterns/spec-dating-sweep.md +0 -91
- package/skills/adia-ui-ops/references/audit-patterns/stale-content.md +0 -182
- package/skills/adia-ui-ops/references/audit-patterns/staleness-tooling.md +0 -156
- package/skills/adia-ui-ops/references/audit-patterns/token-waste-detection.md +0 -196
- package/skills/adia-ui-ops/references/doc-types/adr-pattern.md +0 -210
- package/skills/adia-ui-ops/references/doc-types/architecture-md.md +0 -190
- package/skills/adia-ui-ops/references/doc-types/changelog.md +0 -183
- package/skills/adia-ui-ops/references/doc-types/decisions-log.md +0 -146
- package/skills/adia-ui-ops/references/doc-types/plan-roadmap.md +0 -182
- package/skills/adia-ui-ops/references/doc-types/postmortem-pattern.md +0 -206
- package/skills/adia-ui-ops/references/genres/prose-and-writing.md +0 -149
- package/skills/adia-ui-ops/references/guidance/context-budget.md +0 -137
- package/skills/adia-ui-ops/references/guidance/llm-doc-writing.md +0 -116
- package/skills/adia-ui-ops/references/guidance/reliability-dial.md +0 -186
- package/skills/adia-ui-ops/references/recipes/adr-introduction.md +0 -211
- package/skills/adia-ui-ops/references/recipes/audit-existing-repo.md +0 -234
- package/skills/adia-ui-ops/references/recipes/cold-start-harvest.md +0 -263
- package/skills/adia-ui-ops/references/recipes/concurrent-learnings-merge.md +0 -158
- package/skills/adia-ui-ops/references/recipes/continuous-learning-loop.md +0 -169
- package/skills/adia-ui-ops/references/recipes/external-reference-verification.md +0 -158
- package/skills/adia-ui-ops/references/recipes/findings-index-readout.md +0 -126
- package/skills/adia-ui-ops/references/recipes/greenfield-setup.md +0 -252
- package/skills/adia-ui-ops/references/recipes/harvest-repo-brain.md +0 -169
- package/skills/adia-ui-ops/references/recipes/import-repo-brain-harvest.md +0 -153
- package/skills/adia-ui-ops/references/recipes/memory-organization.md +0 -182
- package/skills/adia-ui-ops/references/recipes/recommend-then-validate.md +0 -199
- package/skills/adia-ui-ops/references/recipes/self-healing-hooks.md +0 -366
- package/skills/adia-ui-ops/references/recipes/skill-stewardship-loop.md +0 -113
- package/skills/adia-ui-ops/references/standards/agents-md-spec.md +0 -138
- package/skills/adia-ui-ops/references/standards/claude-md-convention.md +0 -123
- package/skills/adia-ui-ops/references/standards/cross-tool-matrix.md +0 -85
- package/skills/adia-ui-ops/references/standards/readme-conventions.md +0 -232
- package/skills/adia-ui-ops/references/teach-protocol.md +0 -215
- package/skills/adia-ui-ops/scripts/audit-ops-roster.mjs +0 -104
- package/skills/adia-ui-ops/skill.json +0 -65
- package/skills/adia-ui-release/CHANGELOG.md +0 -66
- package/skills/adia-ui-release/SKILL.md +0 -323
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +0 -144
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +0 -155
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +0 -114
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +0 -139
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +0 -124
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +0 -125
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +0 -142
- package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +0 -97
- package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +0 -147
- package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +0 -22
- package/skills/adia-ui-release/evals/evals.json +0 -164
- package/skills/adia-ui-release/references/changelog-discipline.md +0 -250
- package/skills/adia-ui-release/references/cycle-happy-path.md +0 -520
- package/skills/adia-ui-release/references/exe-deploy.md +0 -149
- package/skills/adia-ui-release/references/gates-catalog.md +0 -778
- package/skills/adia-ui-release/references/independent-package-release.md +0 -129
- package/skills/adia-ui-release/references/ledger-discipline.md +0 -232
- package/skills/adia-ui-release/references/migration-guide-authoring.md +0 -174
- package/skills/adia-ui-release/references/multi-agent-baseline.md +0 -207
- package/skills/adia-ui-release/references/notes-authoring.md +0 -212
- package/skills/adia-ui-release/references/recovery-paths.md +0 -262
- package/skills/adia-ui-release/references/rollup-notes.md +0 -208
- package/skills/adia-ui-release/references/teach-protocol.md +0 -468
- package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +0 -196
- package/skills/adia-ui-release/scripts/make-ledger.mjs +0 -200
- package/skills/adia-ui-release/skill.json +0 -77
- package/skills/dogfood-sweep/CHANGELOG.md +0 -37
- package/skills/dogfood-sweep/README.md +0 -105
- package/skills/dogfood-sweep/SKILL.md +0 -1000
- package/skills/dogfood-sweep/analyze.mjs +0 -600
- package/skills/dogfood-sweep/skill.json +0 -31
- /package/skills/{adia-ui-authoring → adia-author}/references/api-contract.md +0 -0
- /package/skills/{adia-ui-release → adia-release}/scripts/assert-monorepo-root.mjs +0 -0
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
date: 2026-04-27
|
|
3
|
-
coverage: canonical
|
|
4
|
-
peers:
|
|
5
|
-
- pointer-validation.md
|
|
6
|
-
- redundancy-detection.md
|
|
7
|
-
- token-waste-detection.md
|
|
8
|
-
- entry-file-coverage.md
|
|
9
|
-
- ../recipes/self-healing-hooks.md
|
|
10
|
-
primary_sources:
|
|
11
|
-
- https://en.wikipedia.org/wiki/Reachability — graph reachability (the formal model behind orphan detection)
|
|
12
|
-
- https://github.com/lycheeverse/lychee — secondary; lychee's `--reachable` mode does similar accounting
|
|
13
|
-
- https://djw.fyi/portfolio/preventing-drift/ — Daryl White on doc archival flows
|
|
14
|
-
status: research-verified
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
# Orphan detection (delivers Promise 1, "less-wasteful")
|
|
18
|
-
|
|
19
|
-
> **The premise.** A doc no one references is a doc no one reads — but it still costs tokens when a globbed `.brain/**/*.md` or `docs/**/*.md` load picks it up, still costs trust when a future reader finds it and can't tell whether it's current, and still drifts because no PR ever touches it. Orphans are the silent compounding cost of an unmaintained doc tree.
|
|
20
|
-
|
|
21
|
-
## The graph model
|
|
22
|
-
|
|
23
|
-
Treat the repo as a directed graph:
|
|
24
|
-
|
|
25
|
-
- **Nodes:** every Markdown file under `.brain/**` and `docs/**` plus the top-level entries (`AGENTS.md`, `CLAUDE.md`, `README.md`, `CONTRIBUTING.md`, etc.).
|
|
26
|
-
- **Edges:** every relative-path link from one node to another (extracted by the same grammar `pointer-validation.md` uses).
|
|
27
|
-
- **Roots:** `AGENTS.md` is the canonical root. `CLAUDE.md` (when fat), `README.md`, and `CONTRIBUTING.md` are secondary roots.
|
|
28
|
-
- **Orphan:** any node in `.brain/**` or `docs/**` that is **not reachable** from any root.
|
|
29
|
-
|
|
30
|
-
Reachability is transitive — if `AGENTS.md` links `ARCHITECTURE.md` and `ARCHITECTURE.md` links `docs/services/auth.md`, then `auth.md` is reachable.
|
|
31
|
-
|
|
32
|
-
## What this pattern catches
|
|
33
|
-
|
|
34
|
-
| # | Class | Example | Severity |
|
|
35
|
-
|---|---|---|---|
|
|
36
|
-
| 1 | **Orphaned active doc** | `docs/setup-2024.md` not referenced; still describes current behavior | Medium |
|
|
37
|
-
| 2 | **Orphaned stale doc** | `docs/old-redis-setup.md` not referenced AND last touched 18 months ago | Medium |
|
|
38
|
-
| 3 | **Orphaned generated content** | `docs/api/Foo.md` from a typedoc dump nobody linked | Low (fix is `.gitignore`, not archive — see `redundancy-detection.md`) |
|
|
39
|
-
| 4 | **Orphan with inbound link from outside `docs/`** | `docs/onboarding.md` linked from a `package.json` script comment but not from any doc | Low (advisory — re-link from AGENTS.md) |
|
|
40
|
-
|
|
41
|
-
## The bash check
|
|
42
|
-
|
|
43
|
-
Two-pass set-difference: build the set of *files in `.brain/` + `docs/`*, build the set of *files referenced from a root or its transitive closure*, diff.
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
#!/usr/bin/env bash
|
|
47
|
-
# scripts/find-orphan-docs.sh — find .brain/*.md and docs/*.md unreachable from any root.
|
|
48
|
-
set -euo pipefail
|
|
49
|
-
|
|
50
|
-
all_docs=$(find .brain docs -type f -name '*.md' 2>/dev/null | sort -u)
|
|
51
|
-
|
|
52
|
-
roots=()
|
|
53
|
-
for r in AGENTS.md CLAUDE.md README.md CONTRIBUTING.md \
|
|
54
|
-
.cursorrules .windsurfrules \
|
|
55
|
-
.github/copilot-instructions.md .github/pull_request_template.md \
|
|
56
|
-
.brain/README.md .brain/INDEX.md docs/README.md docs/INDEX.md; do
|
|
57
|
-
[ -f "$r" ] && roots+=("$r")
|
|
58
|
-
done
|
|
59
|
-
|
|
60
|
-
# Extract resolved relative-path link targets from a Markdown file.
|
|
61
|
-
extract_links() {
|
|
62
|
-
local src_dir; src_dir=$(dirname "$1")
|
|
63
|
-
grep -oE '\]\([^)]+\)' "$1" \
|
|
64
|
-
| sed -E 's/^\]\(//; s/\)$//; s/[[:space:]]+["\x27].*$//; s/#.*$//' \
|
|
65
|
-
| while read -r path; do
|
|
66
|
-
case "$path" in ''|http://*|https://*|mailto:*|file://*) continue ;; esac
|
|
67
|
-
if [ -d "$src_dir/$path" ]; then
|
|
68
|
-
find "$src_dir/$path" -maxdepth 1 -name '*.md' 2>/dev/null
|
|
69
|
-
else
|
|
70
|
-
python3 -c "import os,sys; print(os.path.normpath('$src_dir/$path'))"
|
|
71
|
-
fi
|
|
72
|
-
done
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
# Transitive closure: BFS from roots.
|
|
76
|
-
declare -A reachable
|
|
77
|
-
queue=("${roots[@]}")
|
|
78
|
-
while [ "${#queue[@]}" -gt 0 ]; do
|
|
79
|
-
src="${queue[0]}"; queue=("${queue[@]:1}")
|
|
80
|
-
[ -n "${reachable[$src]:-}" ] && continue
|
|
81
|
-
reachable[$src]=1
|
|
82
|
-
[ -f "$src" ] || continue
|
|
83
|
-
while read -r t; do [ -f "$t" ] && queue+=("$t"); done < <(extract_links "$src")
|
|
84
|
-
done
|
|
85
|
-
|
|
86
|
-
for f in $all_docs; do
|
|
87
|
-
[ -z "${reachable[$f]:-}" ] && \
|
|
88
|
-
echo "ORPHAN: $f (last modified: $(git log -1 --format=%cs -- "$f"))"
|
|
89
|
-
done
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Notes: `python3` only normalizes paths (swap for `realpath --relative-to=.` if unavailable). Reference-style links (`[text][ref]`) aren't parsed — use `lychee --include-paths` if needed.
|
|
93
|
-
|
|
94
|
-
## What to do with each orphan (the three-way choice)
|
|
95
|
-
|
|
96
|
-
For every `ORPHAN: <file>` finding, the audit recommends one of three actions. **Always via PR**, never silently:
|
|
97
|
-
|
|
98
|
-
### (a) Re-link — orphan is still relevant
|
|
99
|
-
|
|
100
|
-
The doc is current but unpointed. Add a link from AGENTS.md's "Where to find things" or the area `docs/<area>/README.md`:
|
|
101
|
-
|
|
102
|
-
```markdown
|
|
103
|
-
# AGENTS.md (excerpt)
|
|
104
|
-
|
|
105
|
-
## Where to find things
|
|
106
|
-
|
|
107
|
-
- **Architecture:** `.brain/architecture/`
|
|
108
|
-
- **ADRs:** `.brain/adrs/`
|
|
109
|
-
- **Onboarding:** `docs/onboarding.md` <-- newly re-linked
|
|
110
|
-
- **Post-mortems:** `.brain/postmortems/`
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### (b) Archive — orphan is obsolete-but-historic
|
|
114
|
-
|
|
115
|
-
Cutting loses institutional memory; keeping in place invites confusion. Move to `.brain/archive/`:
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
git mv docs/old-redis-setup.md .brain/archive/old-redis-setup.md
|
|
119
|
-
git commit -m 'docs: archive old-redis-setup.md (orphan, obsolete since 2025-10 Redis -> Memorystore migration)'
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
The `.brain/archive/` folder gets its own `README.md` listing what's in there and why it's archived. The audit *excludes* `.brain/archive/**` from the orphan scan (everything in there is intentionally orphaned).
|
|
123
|
-
|
|
124
|
-
### (c) Delete — if the orphan adds no value
|
|
125
|
-
|
|
126
|
-
Truly obsolete content with no historical interest. **In a PR**, never silently:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
git rm docs/notes-from-old-design-meeting.md
|
|
130
|
-
git commit -m 'docs: remove obsolete design-meeting notes (orphan, no longer relevant)'
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
The PR description must say *why* — so a future reader who finds the deletion in `git log` understands the rationale.
|
|
134
|
-
|
|
135
|
-
## The 30-day grace period
|
|
136
|
-
|
|
137
|
-
Per `../recipes/self-healing-hooks.md`, the auto-archive workflow does *not* archive on the first scan after a doc goes orphaned. The grace period is **30 days** of being orphaned-and-untouched before auto-archive proposes a PR.
|
|
138
|
-
|
|
139
|
-
Reasons: (1) a PR in flight may temporarily orphan a doc while a link is being moved; (2) a planned re-link may be queued; (3) genuine forgotten orphans accumulate dust within 30 days regardless. The audit *report* always lists current orphans; the *auto-archive workflow* applies the 30-day floor before acting.
|
|
140
|
-
|
|
141
|
-
## Always-referencing files (avoid false positives)
|
|
142
|
-
|
|
143
|
-
Some files act as *indices* — they enumerate every file in a folder, so every file in that folder is reachable by virtue of the index existing. Treat these as ALWAYS_REFERENCING in the orphan-scan reachability graph:
|
|
144
|
-
|
|
145
|
-
| File | What it indexes |
|
|
146
|
-
|---|---|
|
|
147
|
-
| `.brain/adrs/README.md` | every ADR (`0001-*.md` … `NNNN-*.md`) |
|
|
148
|
-
| `.brain/postmortems/README.md` | every postmortem |
|
|
149
|
-
| `.brain/runbooks/README.md` | every runbook |
|
|
150
|
-
| `docs/specs/INDEX.md` | every spec |
|
|
151
|
-
| `docs/journal/README.md` | every dated journal entry |
|
|
152
|
-
|
|
153
|
-
Without them in the entry-file set, every ADR / postmortem / runbook / spec / journal entry shows up as an orphan — a false-positive cluster that drowns the real findings. Observed in the wild on 2026-04-29: an audit pass flagged seven ADRs as orphans because the orphan-scan didn't include `.brain/adrs/README.md` as a referencer. Adding it resolved all seven.
|
|
154
|
-
|
|
155
|
-
The rule is index-shaped: any folder with a `README.md` whose body is a *table* listing the folder's files counts as a referencer. Apply this in the entry-file set:
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
ENTRY_FILES=(
|
|
159
|
-
"AGENTS.md" "CLAUDE.md" "README.md" "PLAN.md" "ROADMAP.md"
|
|
160
|
-
".brain/adrs/README.md" # ← add these
|
|
161
|
-
".brain/postmortems/README.md"
|
|
162
|
-
".brain/runbooks/README.md"
|
|
163
|
-
"docs/specs/INDEX.md"
|
|
164
|
-
"docs/journal/README.md"
|
|
165
|
-
)
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
## Excluding intentional orphans
|
|
169
|
-
|
|
170
|
-
Some docs are intentionally orphaned. Mark them so the audit doesn't keep flagging:
|
|
171
|
-
|
|
172
|
-
| Convention | What it does |
|
|
173
|
-
|---|---|
|
|
174
|
-
| Place file under `.brain/archive/**` | Excluded from orphan scan |
|
|
175
|
-
| Add YAML frontmatter `orphan: intentional` | Excluded from orphan scan |
|
|
176
|
-
| Add file to `.brain/ignore` (one path per line) | Excluded from orphan scan |
|
|
177
|
-
|
|
178
|
-
The skill recommends `.brain/archive/` as the default — it's the most discoverable. Frontmatter and ignorefile are escape hatches.
|
|
179
|
-
|
|
180
|
-
## Severity rubric
|
|
181
|
-
|
|
182
|
-
| Finding | Severity | Why |
|
|
183
|
-
|---|---|---|
|
|
184
|
-
| Orphaned doc that contradicts AGENTS.md (stale active content) | High | Causes drift if anyone finds and follows it |
|
|
185
|
-
| Orphaned active doc (matches code/AGENTS.md but unlinked) | Medium | Useful content, agent never finds it |
|
|
186
|
-
| Orphaned stale doc (>6mo old, no `_Last reviewed:_`) | Medium | Obsolete; archive candidate |
|
|
187
|
-
| Orphaned generated content (typedoc/sphinx/etc.) | Low | Fix is `.gitignore`, not archive — see `redundancy-detection.md` |
|
|
188
|
-
| Orphaned doc with inbound link from non-doc source (script comment, code) | Low | Re-link from AGENTS.md and move on |
|
|
189
|
-
|
|
190
|
-
## What this pattern is NOT for
|
|
191
|
-
|
|
192
|
-
- **Files outside `.brain/` and `docs/`** — orphan-detection scope is `.brain/**` and `docs/**`. Top-level files (`LICENSE`, `package.json`) aren't candidates.
|
|
193
|
-
- **Code files** — dead-code detection is a separate problem. This audit is about *documentation*.
|
|
194
|
-
- **Folders with no `.md`** — assets-only folders (`docs/images/`) don't get scanned. They're referenced from inside `.md` files via ``, which is a `pointer-validation.md` concern.
|
|
195
|
-
|
|
196
|
-
## Cross-references
|
|
197
|
-
|
|
198
|
-
- Pointer validation (the inverse — links that don't resolve): `pointer-validation.md`
|
|
199
|
-
- Redundancy detection (sibling Promise 1 audit): `redundancy-detection.md`
|
|
200
|
-
- Token-waste detection (orphans contribute to glob-load tax): `token-waste-detection.md`
|
|
201
|
-
- Entry-file coverage (the precondition — roots exist): `entry-file-coverage.md`
|
|
202
|
-
- Auto-archive workflow + 30-day grace: `../recipes/self-healing-hooks.md`
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
date: 2026-04-27
|
|
3
|
-
coverage: canonical
|
|
4
|
-
peers:
|
|
5
|
-
- orphan-detection.md
|
|
6
|
-
- stale-content.md
|
|
7
|
-
- staleness-tooling.md
|
|
8
|
-
- entry-file-coverage.md
|
|
9
|
-
- ../recipes/self-healing-hooks.md
|
|
10
|
-
primary_sources:
|
|
11
|
-
- https://github.com/lycheeverse/lychee — fast Markdown/HTML link checker (Rust)
|
|
12
|
-
- https://daringfireball.net/projects/markdown/syntax#link — original Markdown link syntax (Gruber)
|
|
13
|
-
- https://spec.commonmark.org/0.31.2/#links — CommonMark 0.31.2 link grammar
|
|
14
|
-
status: research-verified
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
# Pointer validation (delivers Promise 1, "less-wasteful" + Promise 3, "less-prone-to-staleness")
|
|
18
|
-
|
|
19
|
-
> **The premise.** A pointer that doesn't resolve is worse than no pointer. The agent follows the link, hits a 404, and either gives up (silent failure) or hallucinates (loud failure). This audit catches every relative-path reference in the repo's instruction graph and verifies it lands on a real file before the agent ever has to.
|
|
20
|
-
|
|
21
|
-
## What this pattern catches
|
|
22
|
-
|
|
23
|
-
Five classes of broken pointer:
|
|
24
|
-
|
|
25
|
-
| # | Class | Example | Severity |
|
|
26
|
-
|---|---|---|---|
|
|
27
|
-
| 1 | **Pointer to a renamed/moved file** | AGENTS.md says `docs/architecture.md`; file was moved to `.brain/architecture/overview.md` six months ago | High |
|
|
28
|
-
| 2 | **Pointer to a never-existed file** | Author typo: `.brain/runbooks/databse.md` (missing the `a`) | High |
|
|
29
|
-
| 3 | **Pointer to a folder without an index** | AGENTS.md says `.brain/adrs/`; folder exists but no `README.md` to land on | Medium |
|
|
30
|
-
| 4 | **Pointer from canonical file to missing file** | AGENTS.md links `[Memory primitives](docs/memory.md)`; that file is gone | **Critical** |
|
|
31
|
-
| 5 | **Repo-root file using `docs/`-relative paths** | `PLAN.md` at repo root has `[spec](./specs/foo.md)`; resolves to `./specs/foo.md` (404), should be `./docs/specs/foo.md` | **Critical** |
|
|
32
|
-
|
|
33
|
-
Class 4 is the load-bearing case: the canonical entry file is the agent's single source of truth, and a broken link from it is a broken promise to every agent on every session.
|
|
34
|
-
|
|
35
|
-
Class 5 is a particularly nasty failure mode that surfaces when a doc gets moved between directories without its links being rewritten. **Watch for it** when `PLAN.md` / `ROADMAP.md` / `CHANGELOG.md` move from `docs/` to repo root (or vice versa), or when `docs/foo/bar.md` is hoisted to `docs/bar.md`. The check below catches it because relative-path resolution starts from the source file's directory — but human reviewers miss it constantly because the *display text* often shows the correct path while the link itself is wrong:
|
|
36
|
-
|
|
37
|
-
```markdown
|
|
38
|
-
[`docs/specs/genui-multiturn.md`](./specs/genui-multiturn.md)
|
|
39
|
-
↑ broken — file is at root, not docs/
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Observed in the wild on 2026-04-29: 25+ broken intra-repo links across `PLAN.md` + `ROADMAP.md` after the files were moved from `docs/` to repo root. Display text was correct on every link; targets all wrong.
|
|
43
|
-
|
|
44
|
-
## Scope: what files we validate pointers from
|
|
45
|
-
|
|
46
|
-
| File | Why |
|
|
47
|
-
|---|---|
|
|
48
|
-
| `AGENTS.md` | Canonical entry; every link must resolve |
|
|
49
|
-
| `CLAUDE.md` (when fat) | Same as AGENTS.md while it's still fat; once thin-pointered the only link is to AGENTS.md |
|
|
50
|
-
| `README.md` | Human + agent landing |
|
|
51
|
-
| `CONTRIBUTING.md` | Often points at `docs/dev-setup.md` etc. |
|
|
52
|
-
| `.brain/**/*.md`, `docs/**/*.md` | Doc-to-doc links must resolve too |
|
|
53
|
-
| `.cursor/rules/*.mdc`, `.windsurfrules`, `.github/copilot-instructions.md` | If fat, validate; if thin pointers to AGENTS.md, only the one link to validate |
|
|
54
|
-
| `.github/pull_request_template.md` | Often points at ADR conventions / contribution guides |
|
|
55
|
-
|
|
56
|
-
Validate **relative paths only**. External URLs are `lychee`'s job — see `staleness-tooling.md` for the tool wiring. `lychee` will *also* check intra-repo paths, but the skill implements its own check so the audit can run offline and produce structured findings independent of `lychee` being installed.
|
|
57
|
-
|
|
58
|
-
## The bash check (intra-repo links)
|
|
59
|
-
|
|
60
|
-
The link-extraction grammar (CommonMark 0.31.2 §6.3) is `[text](target)` with optional title. We capture `target` and check it.
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
#!/usr/bin/env bash
|
|
64
|
-
# scripts/check-pointers.sh
|
|
65
|
-
# Validate every relative-path link in entry + docs files.
|
|
66
|
-
set -euo pipefail
|
|
67
|
-
|
|
68
|
-
# Files we extract pointers from.
|
|
69
|
-
candidates=(
|
|
70
|
-
AGENTS.md CLAUDE.md README.md CONTRIBUTING.md
|
|
71
|
-
.cursorrules .windsurfrules
|
|
72
|
-
.github/copilot-instructions.md
|
|
73
|
-
.github/pull_request_template.md
|
|
74
|
-
)
|
|
75
|
-
for f in .brain/**/*.md .brain/*.md docs/**/*.md docs/*.md .cursor/rules/*.mdc 2>/dev/null; do
|
|
76
|
-
[ -f "$f" ] && candidates+=("$f")
|
|
77
|
-
done
|
|
78
|
-
|
|
79
|
-
fail=0
|
|
80
|
-
for src in "${candidates[@]}"; do
|
|
81
|
-
[ -f "$src" ] || continue
|
|
82
|
-
# Extract the (target) of every Markdown link, drop quoted titles.
|
|
83
|
-
grep -oE '\]\([^)]+\)' "$src" \
|
|
84
|
-
| sed -E 's/^\]\(//; s/\)$//; s/[[:space:]]+["\x27].*$//' \
|
|
85
|
-
| sort -u \
|
|
86
|
-
| while read -r target; do
|
|
87
|
-
# Skip empty, anchors-only, mailto:, and absolute URLs.
|
|
88
|
-
case "$target" in
|
|
89
|
-
''|'#'*|mailto:*|http://*|https://*|ftp://*|file://*) continue ;;
|
|
90
|
-
esac
|
|
91
|
-
# Strip fragment (#section) before resolving.
|
|
92
|
-
path="${target%%#*}"
|
|
93
|
-
[ -z "$path" ] && continue
|
|
94
|
-
# Resolve relative to the source file's directory.
|
|
95
|
-
src_dir=$(dirname "$src")
|
|
96
|
-
resolved="$src_dir/$path"
|
|
97
|
-
# Folder pointer? Accept if folder exists and has a README.md or any *.md.
|
|
98
|
-
if [ -d "$resolved" ]; then
|
|
99
|
-
if [ -f "$resolved/README.md" ] || ls "$resolved"/*.md >/dev/null 2>&1; then
|
|
100
|
-
continue
|
|
101
|
-
fi
|
|
102
|
-
echo "EMPTY-FOLDER-POINTER: $src -> $target (folder exists but no .md inside)"
|
|
103
|
-
fail=1
|
|
104
|
-
continue
|
|
105
|
-
fi
|
|
106
|
-
# File pointer.
|
|
107
|
-
if [ ! -f "$resolved" ]; then
|
|
108
|
-
echo "BROKEN-POINTER: $src -> $target (resolved: $resolved)"
|
|
109
|
-
fail=1
|
|
110
|
-
fi
|
|
111
|
-
done
|
|
112
|
-
done
|
|
113
|
-
exit $fail
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Dedupe is done by `sort -u` per file — the same target referenced 5 times in one file emits only one finding.
|
|
117
|
-
|
|
118
|
-
## The dedupe step (why it matters for the report)
|
|
119
|
-
|
|
120
|
-
A 200-line AGENTS.md often references `.brain/adrs/` 4-6 times (Where-to-find-things, Memory-primitives, Conventions, etc.). Without dedupe, a single broken folder produces a 6-line report. Dedupe per-source-file before reporting; the same target broken from multiple files is genuinely worth multiple findings (it tells you *where* to fix).
|
|
121
|
-
|
|
122
|
-
## Folder-pointer special case
|
|
123
|
-
|
|
124
|
-
A pointer like `.brain/adrs/` is not a file — it's a folder. The check passes if:
|
|
125
|
-
|
|
126
|
-
1. The folder exists, AND
|
|
127
|
-
2. The folder has a `README.md`, OR
|
|
128
|
-
3. The folder contains at least one `.md` file (so the agent can list and pick).
|
|
129
|
-
|
|
130
|
-
A folder with `.png` and `.svg` only is not a valid landing target for a doc pointer. Flag it with `EMPTY-FOLDER-POINTER`.
|
|
131
|
-
|
|
132
|
-
## External link validation (out of scope for this check)
|
|
133
|
-
|
|
134
|
-
External URLs (`https://...`) are validated by `lychee` per `staleness-tooling.md`. Reasons we keep them separate:
|
|
135
|
-
|
|
136
|
-
- External checks need network; intra-repo checks must run offline.
|
|
137
|
-
- External links rate-limit; intra-repo links are free.
|
|
138
|
-
- External links are validated weekly (cron) per `../recipes/self-healing-hooks.md`; intra-repo links are validated on every commit.
|
|
139
|
-
|
|
140
|
-
If you want a one-shot "all links" pass, run both sequentially:
|
|
141
|
-
|
|
142
|
-
```bash
|
|
143
|
-
bash scripts/check-pointers.sh # intra-repo, fast, offline
|
|
144
|
-
lychee --no-progress '.brain/**/*.md' 'docs/**/*.md' '*.md' # external + intra-repo, network-bound
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
The intra-repo check is allowed to be redundant with `lychee --offline`; what it adds is structured output the audit pipeline can ingest without parsing `lychee` JSON.
|
|
148
|
-
|
|
149
|
-
## Severity rubric
|
|
150
|
-
|
|
151
|
-
| Finding | Severity | Why |
|
|
152
|
-
|---|---|---|
|
|
153
|
-
| Broken pointer **from AGENTS.md** to a canonical file (`.brain/adrs/`, `.brain/postmortems/`, `.brain/architecture/`) | **Critical** | Agent's primary memory map is wrong; trust collapses |
|
|
154
|
-
| Broken pointer from any other entry file (`CLAUDE.md`, `README.md`, `.cursor/rules/`) | High | Agent following that tool will hit 404 |
|
|
155
|
-
| Broken doc-to-doc pointer in `.brain/**` or `docs/**` | High | Reachable but breaks navigation graph |
|
|
156
|
-
| Empty-folder pointer (`.brain/runbooks/` exists, no `.md` inside) | Medium | Agent lands but finds nothing actionable |
|
|
157
|
-
| Pointer with stale fragment (`docs/foo.md#old-section` where the file exists but the anchor doesn't) | Low | File loads, agent skim-reads; advisory |
|
|
158
|
-
|
|
159
|
-
The fragment check (`#section-name` resolves to a real heading slug) is supported by `lychee --include-fragments` and is the cleanest way to catch class 5. Don't reimplement slug-generation in bash; pay the lychee dependency.
|
|
160
|
-
|
|
161
|
-
## What this pattern is NOT for
|
|
162
|
-
|
|
163
|
-
- **External links** — `staleness-tooling.md`'s `lychee` integration owns those.
|
|
164
|
-
- **Symbol references inside prose** — "the `FooHandler` class" is a *content* claim, not a pointer; it's covered by `stale-content.md`.
|
|
165
|
-
- **Image / asset paths** — same machinery works (``), but assets are usually checked-in alongside the doc, so failures are rare. The check above will catch them anyway.
|
|
166
|
-
- **Reference-style links** (`[text][ref]` with `[ref]: target` later in the file) — the bash above doesn't parse them. CommonMark §6.3.2. If your repo uses them, swap to a Python parser (`markdown-it-py`) or run `lychee --offline` which handles them natively.
|
|
167
|
-
|
|
168
|
-
## How AGENTS.md's pointers become self-healing
|
|
169
|
-
|
|
170
|
-
The pre-commit hook in `../recipes/self-healing-hooks.md` runs this check on every commit that touches `AGENTS.md`, `CLAUDE.md`, `README.md`, `CONTRIBUTING.md`, or any file under `.brain/**` or `docs/**`. A broken pointer fails the commit — the author is forced to fix it locally. This is the cheapest place to catch it; by the time it reaches CI, the author has already context-switched.
|
|
171
|
-
|
|
172
|
-
The weekly cron (`repo-brain-weekly.yml`) re-runs the check across the whole repo (in case a PR touched a target without touching the source pointing at it — the canonical "rename without re-link" case).
|
|
173
|
-
|
|
174
|
-
## Cross-references
|
|
175
|
-
|
|
176
|
-
- Orphan detection (the inverse — files no one points at): `orphan-detection.md`
|
|
177
|
-
- Stale content (symbol-level rot): `stale-content.md`
|
|
178
|
-
- External link checking: `staleness-tooling.md`
|
|
179
|
-
- Entry-file coverage (the precondition — entry files exist): `entry-file-coverage.md`
|
|
180
|
-
- Self-healing hooks (where the check runs automatically): `../recipes/self-healing-hooks.md`
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
date: 2026-04-27
|
|
3
|
-
coverage: canonical
|
|
4
|
-
peers:
|
|
5
|
-
- orphan-detection.md
|
|
6
|
-
- token-waste-detection.md
|
|
7
|
-
- ../standards/claude-md-convention.md
|
|
8
|
-
- ../recipes/self-healing-hooks.md
|
|
9
|
-
primary_sources:
|
|
10
|
-
- https://github.com/anthropics/claude-code/issues/31005
|
|
11
|
-
status: research-verified
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
# Redundancy detection (delivers Promise 1, "less-wasteful")
|
|
15
|
-
|
|
16
|
-
> **The premise.** Redundancy is waste *and* drift waiting to happen. Two files saying "use pnpm" will, eventually, disagree — one will say `pnpm`, the other will say `npm` after a sloppy edit. The audit's job is to detect redundancy *before* it drifts.
|
|
17
|
-
|
|
18
|
-
## What this pattern catches
|
|
19
|
-
|
|
20
|
-
Five classes of redundancy:
|
|
21
|
-
|
|
22
|
-
| Class | Example | Severity |
|
|
23
|
-
|---|---|---|
|
|
24
|
-
| **Drift between entry files** | CLAUDE.md and AGENTS.md both fat with substantively different content | Critical |
|
|
25
|
-
| **Repeated commands** | Build commands listed in AGENTS.md, README.md, AND CONTRIBUTING.md | Medium |
|
|
26
|
-
| **Repeated facts** | "We use Postgres" stated in AGENTS.md, ARCHITECTURE.md, AND README.md | Low |
|
|
27
|
-
| **Verbose-vs-tabular duplication** | Same conventions written as prose AND as a table later in the same file | Low |
|
|
28
|
-
| **Generated content checked in alongside source** | API reference markdown alongside the docstrings it was generated from | Medium |
|
|
29
|
-
|
|
30
|
-
## Drift between entry files (the critical case)
|
|
31
|
-
|
|
32
|
-
This is the audit's single most-important check. Claude Code reads CLAUDE.md, every other agent reads AGENTS.md natively (per `cross-tool-matrix.md`). When the two files contain different instructions, the team has two failure modes simultaneously:
|
|
33
|
-
|
|
34
|
-
1. **Claude Code follows out-of-date rules** (because CLAUDE.md is stale).
|
|
35
|
-
2. **Cursor/Codex/Devin follow out-of-date rules** (because AGENTS.md is stale).
|
|
36
|
-
|
|
37
|
-
The fix is the canonical model from this skill: AGENTS.md fat, CLAUDE.md as symlink or thin pointer.
|
|
38
|
-
|
|
39
|
-
### Detection logic
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
# Pseudocode for the drift check
|
|
43
|
-
if [ ! -f AGENTS.md ] || [ ! -f CLAUDE.md ]; then
|
|
44
|
-
# No drift possible
|
|
45
|
-
exit 0
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
if [ -L CLAUDE.md ] && [ "$(readlink CLAUDE.md)" = "AGENTS.md" ]; then
|
|
49
|
-
# Symlink → identical content
|
|
50
|
-
exit 0
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
claude_lines=$(wc -l < CLAUDE.md)
|
|
54
|
-
if [ "$claude_lines" -le 15 ] && grep -qi 'AGENTS.md' CLAUDE.md; then
|
|
55
|
-
# Thin pointer → no fat content to drift
|
|
56
|
-
exit 0
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
# Both fat → drift risk; emit finding
|
|
60
|
-
echo "DRIFT — CLAUDE.md ($claude_lines lines) vs AGENTS.md ($(wc -l <AGENTS.md) lines)"
|
|
61
|
-
echo " Diff highlights:"
|
|
62
|
-
diff <(grep -v '^$' CLAUDE.md) <(grep -v '^$' AGENTS.md) | head -20
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
The full hook is in `../recipes/self-healing-hooks.md`.
|
|
66
|
-
|
|
67
|
-
### Recipe to fix
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
# Pick AGENTS.md as canonical (recommended).
|
|
71
|
-
# Move CLAUDE.md content into AGENTS.md, deduplicating.
|
|
72
|
-
# Then:
|
|
73
|
-
|
|
74
|
-
# Option A: symlink (cleanest)
|
|
75
|
-
rm CLAUDE.md
|
|
76
|
-
ln -s AGENTS.md CLAUDE.md
|
|
77
|
-
git add CLAUDE.md
|
|
78
|
-
git commit -m 'docs: CLAUDE.md → symlink to AGENTS.md (eliminate drift)'
|
|
79
|
-
|
|
80
|
-
# Option B: thin pointer (works on Windows, no symlink quirks)
|
|
81
|
-
cat > CLAUDE.md <<'EOF'
|
|
82
|
-
# CLAUDE.md
|
|
83
|
-
|
|
84
|
-
This repo's instructions for LLM coding agents live in [`AGENTS.md`](./AGENTS.md).
|
|
85
|
-
Please read that file. The contents apply identically to Claude Code.
|
|
86
|
-
|
|
87
|
-
_Last reviewed: 2026-04-27_
|
|
88
|
-
EOF
|
|
89
|
-
git add CLAUDE.md
|
|
90
|
-
git commit -m 'docs: CLAUDE.md → thin pointer to AGENTS.md'
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Repeated commands
|
|
94
|
-
|
|
95
|
-
A common pattern: build/test/run commands appear in AGENTS.md, README.md, and CONTRIBUTING.md. Each is true at the moment of writing; one will eventually drift.
|
|
96
|
-
|
|
97
|
-
### Detection
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
# Heuristic: extract code-fenced shell blocks from each file, hash them, find duplicates.
|
|
101
|
-
for f in AGENTS.md README.md CONTRIBUTING.md .brain/**/*.md docs/**/*.md; do
|
|
102
|
-
[ -f "$f" ] || continue
|
|
103
|
-
awk '/^```(bash|sh|shell)/,/^```$/' "$f" | sha256sum | awk -v f="$f" '{print f, $1}'
|
|
104
|
-
done | sort -k2 | awk '{
|
|
105
|
-
if ($2 == prev_hash) print "DUPLICATE: " prev_file " <-> " $1;
|
|
106
|
-
prev_file = $1; prev_hash = $2
|
|
107
|
-
}'
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Tighter heuristic: parse out individual commands (lines starting with `$ ` or matching `pnpm|npm|yarn|cargo|go run`) and find which appear in multiple files.
|
|
111
|
-
|
|
112
|
-
### Fix recipe
|
|
113
|
-
|
|
114
|
-
Pick a canonical home (AGENTS.md is typical). Other files reference it:
|
|
115
|
-
|
|
116
|
-
```markdown
|
|
117
|
-
# README.md (excerpt)
|
|
118
|
-
|
|
119
|
-
## Build / test / run
|
|
120
|
-
|
|
121
|
-
See [`AGENTS.md`](./AGENTS.md) for build, test, and run commands.
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
```markdown
|
|
125
|
-
# CONTRIBUTING.md (excerpt)
|
|
126
|
-
|
|
127
|
-
## Local setup
|
|
128
|
-
|
|
129
|
-
For build / test / run commands, see [`AGENTS.md`](./AGENTS.md). For
|
|
130
|
-
contribution conventions, continue below.
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
The agent reads AGENTS.md every session anyway, so this isn't a navigation cost.
|
|
134
|
-
|
|
135
|
-
## Repeated facts
|
|
136
|
-
|
|
137
|
-
Lower severity: same fact ("we use Postgres") appears in 3 places. The fact rarely changes, so drift risk is low — but tokens are wasted on each load.
|
|
138
|
-
|
|
139
|
-
### Detection
|
|
140
|
-
|
|
141
|
-
This is hard to automate well — paraphrased duplicates evade hashing. Practical approach: look for *named entities* (technology names, version numbers, named services) that appear in multiple top-level docs, and surface as "consider consolidating."
|
|
142
|
-
|
|
143
|
-
```bash
|
|
144
|
-
# Heuristic: find tech names that appear in 3+ entry-level docs
|
|
145
|
-
grep -l -i -E '\b(postgres|mysql|redis|kafka|s3|aws|gcp|azure)\b' \
|
|
146
|
-
AGENTS.md README.md CONTRIBUTING.md ARCHITECTURE.md 2>/dev/null \
|
|
147
|
-
| sort | uniq -c | sort -rn | awk '$1 >= 3 { print "POSSIBLE-REDUNDANCY: " $2 }'
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Treat as advisory; humans decide whether to consolidate.
|
|
151
|
-
|
|
152
|
-
## Verbose-vs-tabular duplication within one file
|
|
153
|
-
|
|
154
|
-
A surprisingly common pattern: AGENTS.md says
|
|
155
|
-
|
|
156
|
-
> "We use TypeScript with strict mode enabled. Our test runner is Vitest. We use Conventional Commits for commit messages. ESLint is configured for our coding style."
|
|
157
|
-
|
|
158
|
-
...and then *also* has:
|
|
159
|
-
|
|
160
|
-
```markdown
|
|
161
|
-
| Tool | Choice |
|
|
162
|
-
|---|---|
|
|
163
|
-
| Language | TypeScript (strict) |
|
|
164
|
-
| Tests | Vitest |
|
|
165
|
-
| Commits | Conventional |
|
|
166
|
-
| Linting | ESLint |
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
Both communicate the same facts. Cut the prose, keep the table — bullets/tables are denser and the agent prefers them.
|
|
170
|
-
|
|
171
|
-
### Detection
|
|
172
|
-
|
|
173
|
-
Hard. Practical heuristic: flag files with both `^>` blockquotes longer than 100 chars AND a Markdown table covering similar content. Manual review.
|
|
174
|
-
|
|
175
|
-
## Generated content checked in
|
|
176
|
-
|
|
177
|
-
If the project generates API reference markdown (TypeDoc, sphinx, mkdocs-jsdoc, rustdoc → md, etc.), don't check in the output:
|
|
178
|
-
|
|
179
|
-
```
|
|
180
|
-
# .gitignore (excerpt)
|
|
181
|
-
docs/api/ # generated by `pnpm docs:generate`
|
|
182
|
-
docs/typedoc/ # generated by TypeDoc
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
The audit signal: many `.md` files in `docs/api/` or `docs/typedoc/` that look auto-generated (template-driven structure, very uniform front-matter, mass-modified in single commits). These get loaded as orphan candidates by the orphan-detection pass — but the right fix is `.gitignore`, not archive.
|
|
186
|
-
|
|
187
|
-
## Severity rubric
|
|
188
|
-
|
|
189
|
-
| Finding | Severity | Why |
|
|
190
|
-
|---|---|---|
|
|
191
|
-
| AGENTS.md / CLAUDE.md drift (both fat, divergent) | Critical | Causes Claude Code and other agents to follow different rules |
|
|
192
|
-
| Repeated build commands across 3+ files | Medium | High drift risk over time |
|
|
193
|
-
| Repeated facts (named entities) across 3+ docs | Low | Drift risk lower; tokens wasted |
|
|
194
|
-
| Verbose+tabular duplication in same file | Low | Internal redundancy; advisory |
|
|
195
|
-
| Generated content checked in | Medium | Bloats repo, confuses orphan detection |
|
|
196
|
-
|
|
197
|
-
## What this pattern is NOT for
|
|
198
|
-
|
|
199
|
-
- **Repeated *links*** are good. Linking to AGENTS.md from README.md is fine — it's *not* duplicating content, it's pointing to it.
|
|
200
|
-
- **Repeated section headers** (every ADR has a "Status:" section) are not redundancy — they're structural convention.
|
|
201
|
-
- **Repeated examples** in docs (e.g., the same `pnpm install` shown in a setup walkthrough) are not redundancy if each appears in a different procedural context.
|
|
202
|
-
|
|
203
|
-
## Cross-references
|
|
204
|
-
|
|
205
|
-
- Orphan detection (sibling Promise 1 audit): `orphan-detection.md`
|
|
206
|
-
- Token-waste detection (sibling Promise 2 audit): `token-waste-detection.md`
|
|
207
|
-
- CLAUDE.md thin-pointer recipe: `../standards/claude-md-convention.md`
|
|
208
|
-
- Self-healing hooks (where the drift trip-wire lives): `../recipes/self-healing-hooks.md`
|
|
209
|
-
- Concurrent learnings merge (uses this trip-wire as the post-merge backstop): `../recipes/concurrent-learnings-merge.md`
|
|
210
|
-
- Cold-start harvest (uses this trip-wire to detect imported conflicts in phase 5): `../recipes/cold-start-harvest.md`
|