@adia-ai/adia-ui-forge 0.1.3 → 0.8.0
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 +3 -6
- package/CHANGELOG.md +27 -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 +27 -2
- package/skills/{adia-ui-release → adia-release}/scripts/dispatch-publish.mjs +4 -1
- 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 +13 -21
- package/skills/{adia-ui-release → adia-release}/scripts/tag-lockstep.mjs +5 -1
- 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,91 +0,0 @@
|
|
|
1
|
-
# Spec dating sweep (git-mtime batch)
|
|
2
|
-
|
|
3
|
-
**Added:** 2026-05-02 after F-A7 finding identified ~25 spec files
|
|
4
|
-
lacking date markers.
|
|
5
|
-
|
|
6
|
-
## The drift class
|
|
7
|
-
|
|
8
|
-
Files in `docs/specs/` (or the project's spec dir) should have a
|
|
9
|
-
`date:` frontmatter or `_Last reviewed: YYYY-MM-DD_` line. Without
|
|
10
|
-
this, future readers can't tell if the spec is current, when it was
|
|
11
|
-
last reviewed, or whether to cross-check against more recent
|
|
12
|
-
journal entries.
|
|
13
|
-
|
|
14
|
-
The standard fix is "add a `_Last reviewed: <date>_` line." But
|
|
15
|
-
hard-coding today's date is wrong — it asserts a review that didn't
|
|
16
|
-
happen for files you haven't read.
|
|
17
|
-
|
|
18
|
-
## Detection
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
for f in docs/specs/*.md; do
|
|
22
|
-
if ! grep -qE "^date:|Last reviewed|_Date:_" "$f"; then
|
|
23
|
-
echo " $(basename "$f")"
|
|
24
|
-
fi
|
|
25
|
-
done
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Fix recipe (mechanical Python batch using git-mtime)
|
|
29
|
-
|
|
30
|
-
Use git's last-modification date as the proxy for content freshness:
|
|
31
|
-
|
|
32
|
-
```python
|
|
33
|
-
import pathlib, re, subprocess
|
|
34
|
-
|
|
35
|
-
ROOT = pathlib.Path("/path/to/repo")
|
|
36
|
-
SPECS = ROOT / "docs/specs"
|
|
37
|
-
|
|
38
|
-
def has_date_marker(text):
|
|
39
|
-
head = text[:1500]
|
|
40
|
-
return bool(re.search(r"^date:\s*\d{4}-\d{2}-\d{2}", head, re.MULTILINE)) or \
|
|
41
|
-
"Last reviewed:" in head or "_Date:_" in head
|
|
42
|
-
|
|
43
|
-
def git_mtime(p):
|
|
44
|
-
try:
|
|
45
|
-
out = subprocess.check_output(
|
|
46
|
-
["git", "log", "-1", "--format=%cs", "--", str(p)],
|
|
47
|
-
cwd=ROOT, text=True, stderr=subprocess.DEVNULL
|
|
48
|
-
).strip()
|
|
49
|
-
return out if out else "TODAY"
|
|
50
|
-
except subprocess.CalledProcessError:
|
|
51
|
-
return "TODAY"
|
|
52
|
-
|
|
53
|
-
count = 0
|
|
54
|
-
for p in sorted(SPECS.glob("*.md")):
|
|
55
|
-
if p.name == "INDEX.md":
|
|
56
|
-
continue
|
|
57
|
-
text = p.read_text()
|
|
58
|
-
if has_date_marker(text):
|
|
59
|
-
continue
|
|
60
|
-
mtime = git_mtime(p)
|
|
61
|
-
lines = text.splitlines()
|
|
62
|
-
h1_idx = next((i for i, l in enumerate(lines) if l.startswith("# ")), None)
|
|
63
|
-
if h1_idx is None:
|
|
64
|
-
continue
|
|
65
|
-
new_lines = lines[:h1_idx+1] + [""] + [f"_Last reviewed: {mtime}_"] + lines[h1_idx+1:]
|
|
66
|
-
p.write_text("\n".join(new_lines) + "\n")
|
|
67
|
-
count += 1
|
|
68
|
-
print(f" {p.name} → _Last reviewed: {mtime}_")
|
|
69
|
-
|
|
70
|
-
print(f"\nDated {count} files")
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## Why git-mtime, not today's date
|
|
74
|
-
|
|
75
|
-
- **Accurate:** the file genuinely was reviewed when it was last
|
|
76
|
-
edited; using that date doesn't lie.
|
|
77
|
-
- **Stable:** running the script idempotently doesn't push every
|
|
78
|
-
spec's "Last reviewed" forward to today.
|
|
79
|
-
- **Surface-able:** `find docs/specs -name '*.md' -mtime +180` plus
|
|
80
|
-
the dated content tells you which specs need an actual review.
|
|
81
|
-
|
|
82
|
-
## Prevention
|
|
83
|
-
|
|
84
|
-
The `format-hygiene` audit category catches new undated files. The
|
|
85
|
-
batch sweep runs once on a backlog; ongoing prevention is via the
|
|
86
|
-
audit's per-PR check.
|
|
87
|
-
|
|
88
|
-
## Related
|
|
89
|
-
|
|
90
|
-
- `format-hygiene` audit pattern (sibling).
|
|
91
|
-
- `stale-audit` skill check #5.
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
date: 2026-04-27
|
|
3
|
-
coverage: canonical
|
|
4
|
-
peers:
|
|
5
|
-
- staleness-tooling.md
|
|
6
|
-
- pointer-validation.md
|
|
7
|
-
- orphan-detection.md
|
|
8
|
-
- redundancy-detection.md
|
|
9
|
-
- ../recipes/self-healing-hooks.md
|
|
10
|
-
- ../recipes/continuous-learning-loop.md
|
|
11
|
-
primary_sources:
|
|
12
|
-
- https://djw.fyi/portfolio/preventing-drift/ — Daryl White, "Avoiding the Silent Stale Doc Problem"
|
|
13
|
-
- https://git-scm.com/docs/git-log — git log committer-date semantics
|
|
14
|
-
- https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/
|
|
15
|
-
status: research-verified
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
# Stale-content detection (delivers Promise 3, "less-prone-to-staleness")
|
|
19
|
-
|
|
20
|
-
> **The premise.** A doc can have intact links, no orphans, and a clean structure — and still be wrong. *Stale content* is the case where the prose, examples, or commands no longer match the code they describe. This is the hardest staleness class to detect because there's no broken pointer to grep for; the rot is semantic. Three heuristics catch most of it cheaply. The rest needs LLM-on-diff (covered in `staleness-tooling.md`).
|
|
21
|
-
|
|
22
|
-
## Where this fits
|
|
23
|
-
|
|
24
|
-
`staleness-tooling.md` covers the *tools* (lychee, Vale, markdownlint, LLM-on-diff). This file covers the *patterns* — the heuristic checks the audit runs against the repo. Pair them: the tool tells you *what to run*, the pattern tells you *what to look for*.
|
|
25
|
-
|
|
26
|
-
## Three heuristics
|
|
27
|
-
|
|
28
|
-
| # | Heuristic | What it catches | Cost |
|
|
29
|
-
|---|---|---|---|
|
|
30
|
-
| 1 | **Git-mtime + missing dated frontmatter** | Docs nobody has touched in 6+ months that also nobody has explicitly re-affirmed | Cheap (one `find` + one `grep`) |
|
|
31
|
-
| 2 | **Symbol/path references in prose vs `git ls-files`** | Docs that mention class/function/file names that no longer exist in the codebase | Cheap (grep + set diff) |
|
|
32
|
-
| 3 | **Commands in docs vs current package manifest** | AGENTS.md says `npm install` but `pnpm-lock.yaml` is the only lockfile; or `python setup.py` but the project moved to `uv` | Medium (a few `jq` / file-existence checks) |
|
|
33
|
-
|
|
34
|
-
These three together catch the bulk of stale content. The long tail (semantic rot in prose) needs LLM-on-diff per `staleness-tooling.md`.
|
|
35
|
-
|
|
36
|
-
## Heuristic 1 — git-mtime + missing dated frontmatter
|
|
37
|
-
|
|
38
|
-
A doc unmodified for >6 months *and* without a `_Last reviewed: YYYY-MM-DD_` line is probably stale. The "AND" is critical: a 4-year-old `docs/architecture.md` re-reviewed quarterly is not stale.
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
#!/usr/bin/env bash
|
|
42
|
-
# scripts/find-stale-by-age.sh
|
|
43
|
-
set -euo pipefail
|
|
44
|
-
threshold_days=180
|
|
45
|
-
now=$(date +%s)
|
|
46
|
-
find .brain docs -type f -name '*.md' \
|
|
47
|
-
-not -path '.brain/archive/*' -not -path '.brain/adrs/*' -not -path '.brain/postmortems/*' \
|
|
48
|
-
-not -path 'docs/archive/*' -not -path 'docs/adrs/*' -not -path 'docs/postmortems/*' \
|
|
49
|
-
| while read -r f; do
|
|
50
|
-
last=$(git log -1 --format=%ct -- "$f" 2>/dev/null || echo 0)
|
|
51
|
-
[ "$last" -eq 0 ] && continue
|
|
52
|
-
age=$(( (now - last) / 86400 ))
|
|
53
|
-
[ "$age" -le "$threshold_days" ] && continue
|
|
54
|
-
if rev=$(grep -oE '_Last reviewed:[[:space:]]*[0-9]{4}-[0-9]{2}-[0-9]{2}' "$f" \
|
|
55
|
-
| grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}' | head -1); then
|
|
56
|
-
ru=$(date -j -f '%Y-%m-%d' "$rev" +%s 2>/dev/null \
|
|
57
|
-
|| date -d "$rev" +%s 2>/dev/null || echo 0)
|
|
58
|
-
[ $(( (now - ru) / 86400 )) -le "$threshold_days" ] && continue
|
|
59
|
-
echo "STALE-AGE: $f (commit ${age}d ago; review $(( (now - ru) / 86400 ))d ago)"
|
|
60
|
-
else
|
|
61
|
-
echo "STALE-AGE: $f (commit ${age}d ago; no _Last reviewed:_ line)"
|
|
62
|
-
fi
|
|
63
|
-
done
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Excluded folders: ADRs (immutable — see `../doc-types/adr-pattern.md`) and post-mortems (describe a fixed past event). Use `git log` committer-date, not filesystem mtime — `clone`/`rebase`/`worktree` clobber filesystem mtime.
|
|
67
|
-
|
|
68
|
-
## Heuristic 2 — symbol/path references vs `git ls-files`
|
|
69
|
-
|
|
70
|
-
A doc *names* something (class, function, file path) that the code no longer contains. Catches the canonical class-renamed-doc-not-updated case.
|
|
71
|
-
|
|
72
|
-
```bash
|
|
73
|
-
#!/usr/bin/env bash
|
|
74
|
-
# scripts/find-stale-symbols.sh
|
|
75
|
-
set -euo pipefail
|
|
76
|
-
all_paths=$(git ls-files | sort -u)
|
|
77
|
-
find .brain docs -type f -name '*.md' \
|
|
78
|
-
-not -path '.brain/archive/*' -not -path '.brain/adrs/*' -not -path '.brain/postmortems/*' \
|
|
79
|
-
-not -path 'docs/archive/*' -not -path 'docs/adrs/*' -not -path 'docs/postmortems/*' \
|
|
80
|
-
| while read -r f; do
|
|
81
|
-
# Backticked file paths.
|
|
82
|
-
grep -oE '`[a-zA-Z0-9_./-]+\.(ts|tsx|js|jsx|py|go|rs|java|rb|json|toml|yaml|yml)`' "$f" \
|
|
83
|
-
| tr -d '`' | sort -u | while read -r ref; do
|
|
84
|
-
grep -qxF "$ref" <<<"$all_paths" || echo "STALE-PATH: $f -> $ref"
|
|
85
|
-
done
|
|
86
|
-
# Backticked PascalCase identifiers (cheap class-name proxy).
|
|
87
|
-
grep -oE '`[A-Z][a-zA-Z0-9_]{3,}`' "$f" \
|
|
88
|
-
| tr -d '`' | sort -u | while read -r sym; do
|
|
89
|
-
git grep -q -wF "$sym" -- ':!*.md' ':!.brain/**' ':!docs/**' 2>/dev/null \
|
|
90
|
-
|| echo "STALE-SYMBOL: $f -> $sym"
|
|
91
|
-
done
|
|
92
|
-
done
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
False-positive sources: symbols inside before/after code blocks; aliased symbols (`Foo = Bar`); test-only definitions. Emit as **advisory** — human reviews before acting.
|
|
96
|
-
|
|
97
|
-
## Heuristic 3 — commands vs package manifest
|
|
98
|
-
|
|
99
|
-
AGENTS.md says `npm install` but only `pnpm-lock.yaml` exists; the agent runs npm and pollutes the repo with `package-lock.json`. Precise enough to gate as a pre-commit check.
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
#!/usr/bin/env bash
|
|
103
|
-
# scripts/check-commands-vs-manifest.sh
|
|
104
|
-
set -euo pipefail
|
|
105
|
-
fail=0
|
|
106
|
-
pm=""
|
|
107
|
-
if [ -f pnpm-lock.yaml ]; then pm=pnpm
|
|
108
|
-
elif [ -f yarn.lock ]; then pm=yarn
|
|
109
|
-
elif [ -f bun.lockb ] || [ -f bun.lock ]; then pm=bun
|
|
110
|
-
elif [ -f package-lock.json ]; then pm=npm
|
|
111
|
-
fi
|
|
112
|
-
for f in AGENTS.md CLAUDE.md README.md CONTRIBUTING.md; do
|
|
113
|
-
[ -f "$f" ] && [ ! -L "$f" ] || continue
|
|
114
|
-
if [ -n "$pm" ]; then
|
|
115
|
-
awk '/^```(bash|sh|shell|console)/,/^```$/' "$f" \
|
|
116
|
-
| grep -oE '\b(npm|pnpm|yarn|bun)\b' | sort -u | while read -r p; do
|
|
117
|
-
[ "$p" != "$pm" ] && { echo "STALE-CMD: $f uses '$p'; lockfile is '$pm'"; fail=1; }
|
|
118
|
-
done
|
|
119
|
-
fi
|
|
120
|
-
if [ -f uv.lock ]; then
|
|
121
|
-
awk '/^```(bash|sh|shell|console)/,/^```$/' "$f" \
|
|
122
|
-
| grep -qE '\b(pip install|python setup\.py)\b' \
|
|
123
|
-
&& { echo "STALE-CMD: $f uses pip/setup.py; uv.lock present"; fail=1; }
|
|
124
|
-
fi
|
|
125
|
-
done
|
|
126
|
-
exit $fail
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Extend per ecosystem: Cargo, Go modules, Bundler. Same pattern — canonical manifest vs command-block PM/runner mismatch.
|
|
130
|
-
|
|
131
|
-
## Putting the three together
|
|
132
|
-
|
|
133
|
-
The full stale-content pass:
|
|
134
|
-
|
|
135
|
-
```bash
|
|
136
|
-
echo "=== Heuristic 1: age ==="
|
|
137
|
-
bash scripts/find-stale-by-age.sh
|
|
138
|
-
|
|
139
|
-
echo "=== Heuristic 2: symbols ==="
|
|
140
|
-
bash scripts/find-stale-symbols.sh
|
|
141
|
-
|
|
142
|
-
echo "=== Heuristic 3: commands ==="
|
|
143
|
-
bash scripts/check-commands-vs-manifest.sh
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
Run weekly per `../recipes/self-healing-hooks.md`'s scheduled cron. Findings are filed as a single weekly issue, not as commit-blocking errors — these heuristics produce false positives and shouldn't gate merges.
|
|
147
|
-
|
|
148
|
-
The exception: heuristic 3 is precise enough to gate. If `pnpm-lock.yaml` exists and AGENTS.md says `npm install`, that's an unambiguous bug — wire it as a pre-commit check.
|
|
149
|
-
|
|
150
|
-
## Severity rubric
|
|
151
|
-
|
|
152
|
-
| Finding | Severity | Why |
|
|
153
|
-
|---|---|---|
|
|
154
|
-
| Heuristic 3 — command mismatch (AGENTS.md says `npm` + `pnpm-lock.yaml`) | High | Agent will run wrong PM and pollute repo |
|
|
155
|
-
| Heuristic 2 — symbol referenced in AGENTS.md no longer exists | High | Canonical doc misleads agent on every session |
|
|
156
|
-
| Heuristic 2 — symbol referenced in `.brain/**` or `docs/**` no longer exists | Medium | Lower visibility but still wrong |
|
|
157
|
-
| Heuristic 1 — doc >6mo old, no review line | Medium | Likely-but-not-certainly stale; advisory |
|
|
158
|
-
| Heuristic 1 — doc >12mo old, no review line | High | Almost certainly stale |
|
|
159
|
-
|
|
160
|
-
## What this pattern is NOT for
|
|
161
|
-
|
|
162
|
-
- **Broken intra-repo links** — that's `pointer-validation.md`. Stale content is about *correct prose about wrong code*, not about links.
|
|
163
|
-
- **External link rot** — `staleness-tooling.md`'s lychee.
|
|
164
|
-
- **Live external links pointing at outdated content** — that's a separate concern. `lychee` confirms the URL responds; it doesn't check whether the *fetched content still matches what the doc claims*. For that, see `../recipes/external-reference-verification.md` (WebFetch-powered, autoresearch-DNA).
|
|
165
|
-
- **Outdated dependency versions in lockfiles** — out of doc-audit scope; that's a Dependabot / Renovate problem.
|
|
166
|
-
- **Generic "the doc is too short / shallow"** — quality issues, not staleness; covered by `../guidance/llm-doc-writing.md`.
|
|
167
|
-
|
|
168
|
-
## Companion: when these heuristics aren't enough
|
|
169
|
-
|
|
170
|
-
For semantic rot the heuristics miss ("the doc says we use Postgres but actually we migrated to PlanetScale six months ago" — no symbol-name overlap, no command-block giveaway), the answer is **LLM-on-diff** per `staleness-tooling.md`. On every PR, an LLM compares the code diff against `AGENTS.md` and `.brain/**` + `docs/**` and flags doc sections that should have changed. It's the most expensive layer (API calls per PR) and it's gated on `vars.LLM_DOC_DRIFT_ENABLED` per `../recipes/self-healing-hooks.md`.
|
|
171
|
-
|
|
172
|
-
The three heuristics in this file are the cheap baseline; LLM-on-diff is the deeper-but-pricier complement.
|
|
173
|
-
|
|
174
|
-
## Cross-references
|
|
175
|
-
|
|
176
|
-
- Tooling for staleness (lychee, Vale, markdownlint, LLM-on-diff): `staleness-tooling.md`
|
|
177
|
-
- Pointer validation (link-level rot): `pointer-validation.md`
|
|
178
|
-
- Orphan detection (unreachable docs): `orphan-detection.md`
|
|
179
|
-
- Redundancy detection (drift between fat files): `redundancy-detection.md`
|
|
180
|
-
- Self-healing hooks (where the cron runs): `../recipes/self-healing-hooks.md`
|
|
181
|
-
- Continuous-learning loop (how AGENTS.md *stops* going stale): `../recipes/continuous-learning-loop.md`
|
|
182
|
-
- External-reference verification (WebFetch-powered; catches the gap above): `../recipes/external-reference-verification.md`
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
date: 2026-04-27
|
|
3
|
-
coverage: extended
|
|
4
|
-
peers:
|
|
5
|
-
- stale-content.md
|
|
6
|
-
- pointer-validation.md
|
|
7
|
-
- ../guidance/llm-doc-writing.md
|
|
8
|
-
primary_sources:
|
|
9
|
-
- https://github.com/lycheeverse/lychee — fast Markdown/HTML link checker (Rust)
|
|
10
|
-
- https://github.com/lycheeverse/lychee-action — GitHub Action
|
|
11
|
-
- https://vale.sh/ — markup-aware prose linter
|
|
12
|
-
- https://github.com/DavidAnson/markdownlint — Markdown structure linter
|
|
13
|
-
- https://djw.fyi/portfolio/preventing-drift/ — Daryl White, "Avoiding the Silent Stale Doc Problem"
|
|
14
|
-
- https://docuwriter.ai/ — commercial code-doc-drift product
|
|
15
|
-
status: research-verified
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
# Tooling for staleness detection
|
|
19
|
-
|
|
20
|
-
> **What to actually run** when the audit needs help finding stale links, broken references, prose smells, and code/doc drift. The skill itself implements the simplest checks (existence, last-modified, broken pointers); for deeper coverage, reach for these tools.
|
|
21
|
-
|
|
22
|
-
## Link checking — `lychee`
|
|
23
|
-
|
|
24
|
-
**`lychee`** ([lycheeverse/lychee](https://github.com/lycheeverse/lychee)) is a fast Markdown/HTML/RST/text link checker written in Rust. It's the de-facto standard.
|
|
25
|
-
|
|
26
|
-
Run locally:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
lychee --offline --no-progress '.brain/**/*.md' 'docs/**/*.md' '*.md' '*.MD'
|
|
30
|
-
# --offline skips network; useful for fast intra-repo link audit
|
|
31
|
-
# drop --offline to also check external URLs
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
GitHub Actions:
|
|
35
|
-
|
|
36
|
-
```yaml
|
|
37
|
-
# .github/workflows/links.yml
|
|
38
|
-
name: Links
|
|
39
|
-
on:
|
|
40
|
-
pull_request: { paths: ['**.md'] }
|
|
41
|
-
schedule: [{ cron: '0 0 * * 1' }] # Mondays
|
|
42
|
-
jobs:
|
|
43
|
-
links:
|
|
44
|
-
runs-on: ubuntu-latest
|
|
45
|
-
steps:
|
|
46
|
-
- uses: actions/checkout@v4
|
|
47
|
-
- uses: lycheeverse/lychee-action@v2
|
|
48
|
-
with:
|
|
49
|
-
args: --cache --max-cache-age=1d --no-progress '.brain/**/*.md' 'docs/**/*.md' '*.md'
|
|
50
|
-
fail: true
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
The cache is essential — without it, GitHub rate-limits external link checks quickly.
|
|
54
|
-
|
|
55
|
-
Older alternative: `markdown-link-check` (Node). Slower; lychee is preferred.
|
|
56
|
-
|
|
57
|
-
## Prose linting — `Vale`
|
|
58
|
-
|
|
59
|
-
**`Vale`** ([vale.sh](https://vale.sh/)) is a markup-aware prose linter — used by Datadog, Grafana, Elastic, Microsoft, and others. Custom rule packs let you enforce a style guide (sentence length, weasel words, branding terms, Markdown conventions).
|
|
60
|
-
|
|
61
|
-
Useful for the audit only when the project has a content style guide. If not, skip.
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
vale .brain/ docs/
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Markdown structure linting — `markdownlint`
|
|
68
|
-
|
|
69
|
-
**`markdownlint`** ([DavidAnson/markdownlint](https://github.com/DavidAnson/markdownlint)) checks Markdown structure (heading levels, list indentation, blank lines, etc.). Doesn't check freshness; complements lychee.
|
|
70
|
-
|
|
71
|
-
```bash
|
|
72
|
-
markdownlint .brain/ docs/ *.md
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Code/doc drift detection — the genuinely hard one
|
|
76
|
-
|
|
77
|
-
There's no canonical OSS tool for "this code change should have updated this doc." Two pragmatic approaches in 2026:
|
|
78
|
-
|
|
79
|
-
### LLM-on-diff (the popular DIY)
|
|
80
|
-
|
|
81
|
-
Per [Daryl White's "Avoiding the Silent Stale Doc Problem"](https://djw.fyi/portfolio/preventing-drift/): a GitHub Action that, on each PR, asks an LLM whether the code diff requires doc updates and which docs.
|
|
82
|
-
|
|
83
|
-
Sketch:
|
|
84
|
-
|
|
85
|
-
```yaml
|
|
86
|
-
# .github/workflows/doc-drift.yml
|
|
87
|
-
name: Doc drift
|
|
88
|
-
on: pull_request
|
|
89
|
-
jobs:
|
|
90
|
-
drift:
|
|
91
|
-
runs-on: ubuntu-latest
|
|
92
|
-
steps:
|
|
93
|
-
- uses: actions/checkout@v4
|
|
94
|
-
with: { fetch-depth: 0 }
|
|
95
|
-
- run: |
|
|
96
|
-
git diff origin/${{ github.base_ref }}...HEAD --stat > /tmp/diff.txt
|
|
97
|
-
# Pipe /tmp/diff.txt + AGENTS.md + .brain/ + docs/ index into an LLM that
|
|
98
|
-
# returns "no doc changes needed" or "consider updating: <list>".
|
|
99
|
-
# Post as a PR comment.
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
This is bespoke per project but cheap to maintain.
|
|
103
|
-
|
|
104
|
-
### DocuWriter.ai (commercial)
|
|
105
|
-
|
|
106
|
-
[docuwriter.ai](https://docuwriter.ai/) — uses git diff webhooks + LLM analysis to flag doc-relevant code changes. Useful for large repos where the DIY pattern is too noisy.
|
|
107
|
-
|
|
108
|
-
## Git-heuristic staleness (DIY, baseline)
|
|
109
|
-
|
|
110
|
-
The simplest staleness signal: file last-modified date. The skill itself can implement this directly:
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
# Files in .brain/ + docs/ older than 6 months without a "Last reviewed" line:
|
|
114
|
-
find .brain docs -name '*.md' -mtime +180 | while read f; do
|
|
115
|
-
if ! grep -q '_Last reviewed:' "$f"; then
|
|
116
|
-
echo "STALE: $f (last modified: $(git log -1 --format=%cs -- "$f"))"
|
|
117
|
-
fi
|
|
118
|
-
done
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Use `git log` mtime rather than filesystem mtime — filesystem mtime can be misleading after `git clone` or rebase.
|
|
122
|
-
|
|
123
|
-
## Composing the staleness pass
|
|
124
|
-
|
|
125
|
-
Recommended order:
|
|
126
|
-
|
|
127
|
-
1. **Existence + entry-file pointers** (skill internal) — what does the entry say, what's actually there
|
|
128
|
-
2. **lychee** — flag broken intra-repo and external links
|
|
129
|
-
3. **markdownlint** — flag structural issues
|
|
130
|
-
4. **Git-heuristic mtime check** — flag undated docs older than threshold
|
|
131
|
-
5. **Vale** (if project has style guide) — flag prose smells
|
|
132
|
-
6. **LLM-on-diff** (in CI, ongoing) — flag PRs that should have updated docs
|
|
133
|
-
|
|
134
|
-
The first four are fast; run them on every audit. The last two are slower / more bespoke; run them in CI.
|
|
135
|
-
|
|
136
|
-
## What to put in the audit report
|
|
137
|
-
|
|
138
|
-
For each stale finding:
|
|
139
|
-
|
|
140
|
-
```markdown
|
|
141
|
-
- **STALE — `docs/architecture.md`** (severity: medium)
|
|
142
|
-
- Last modified: 2024-09-12 (583 days ago).
|
|
143
|
-
- No `_Last reviewed:_` line.
|
|
144
|
-
- References `class FooHandler` which was renamed to `RequestHandler` in commit `abc123` (2025-03-15).
|
|
145
|
-
- **Tools that flagged this:**
|
|
146
|
-
- mtime-heuristic (older than 6mo, undated)
|
|
147
|
-
- intra-repo grep (referenced symbol no longer exists)
|
|
148
|
-
- **Recommendation:** review and re-date, OR archive to `.brain/archive/architecture-2024.md`.
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## Cross-references
|
|
152
|
-
|
|
153
|
-
- General staleness audit pattern: `stale-content.md`
|
|
154
|
-
- Pointer validation: `pointer-validation.md`
|
|
155
|
-
- LLM-doc-writing guidance: `../guidance/llm-doc-writing.md`
|
|
156
|
-
- Full audit recipe: `../recipes/audit-existing-repo.md`
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
date: 2026-04-27
|
|
3
|
-
coverage: canonical
|
|
4
|
-
peers:
|
|
5
|
-
- redundancy-detection.md
|
|
6
|
-
- ../guidance/context-budget.md
|
|
7
|
-
- ../guidance/llm-doc-writing.md
|
|
8
|
-
- ../recipes/self-healing-hooks.md
|
|
9
|
-
primary_sources:
|
|
10
|
-
- https://code.claude.com/docs/en/best-practices
|
|
11
|
-
- https://github.blog/ai-and-ml/github-copilot/how-to-write-a-great-agents-md-lessons-from-over-2500-repositories/
|
|
12
|
-
status: research-verified
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
# Token-waste detection (delivers Promise 2, "token-and-context-optimized")
|
|
16
|
-
|
|
17
|
-
> **The premise.** A token loaded into context is a token not available for the work being done. Every byte of AGENTS.md / CLAUDE.md / loaded docs has to *earn its place* — by changing agent behavior in a way no other doc does. This audit catches the bytes that aren't earning.
|
|
18
|
-
|
|
19
|
-
## The five token-waste smells
|
|
20
|
-
|
|
21
|
-
| # | Smell | What it looks like | Severity |
|
|
22
|
-
|---|---|---|---|
|
|
23
|
-
| 1 | **Bloated entry file** | AGENTS.md or CLAUDE.md > 200 lines | High |
|
|
24
|
-
| 2 | **Bloated subfolder doc** | `docs/<not-adrs-or-postmortems>/<file>.md` > 500 lines | Medium |
|
|
25
|
-
| 3 | **Verbose prose** | Long paragraphs where bullets / tables work | Low |
|
|
26
|
-
| 4 | **Cross-doc repetition** | Same content in 3+ files | Medium (handled in `redundancy-detection.md`) |
|
|
27
|
-
| 5 | **Redundant frontmatter / boilerplate** | Long YAML frontmatter, license headers, generated tables of contents in every file | Low |
|
|
28
|
-
|
|
29
|
-
## Smell 1 — bloated entry files (the critical one)
|
|
30
|
-
|
|
31
|
-
**The check:** `wc -l AGENTS.md CLAUDE.md`. Anthropic's published guidance is `<200 lines`; we warn at 150 and fail at 200.
|
|
32
|
-
|
|
33
|
-
**Why it matters:** entry files are loaded *every session*. A 400-line AGENTS.md costs ~5K tokens *every time the agent starts work*. Across a hundred sessions in a year, that's 500K tokens spent on metadata — a meaningful chunk of any per-developer LLM budget, before doing any work.
|
|
34
|
-
|
|
35
|
-
**Auto-detection:**
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
# Pre-commit hook (also in self-healing-hooks.md)
|
|
39
|
-
for f in AGENTS.md CLAUDE.md; do
|
|
40
|
-
[ -f "$f" ] && [ ! -L "$f" ] || continue
|
|
41
|
-
n=$(wc -l < "$f")
|
|
42
|
-
if [ "$n" -gt 200 ]; then
|
|
43
|
-
echo "ERROR: $f is $n lines (>200). See guidance/context-budget.md."
|
|
44
|
-
exit 1
|
|
45
|
-
elif [ "$n" -gt 150 ]; then
|
|
46
|
-
echo "WARN: $f is $n lines (>150). Consider compression."
|
|
47
|
-
fi
|
|
48
|
-
done
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
**Fix recipe:** see `../guidance/context-budget.md` "How to reduce a bloated AGENTS.md (the recipe)" — the 8-step compression pass typically takes a 600-line entry file to 150 without losing instructional value.
|
|
52
|
-
|
|
53
|
-
## Smell 2 — bloated subfolder docs
|
|
54
|
-
|
|
55
|
-
**The check:** `find .brain docs -name '*.md' -not -path '.brain/adrs/*' -not -path '.brain/postmortems/*' -not -path '.brain/architecture/*'` and any file with `wc -l > 500`.
|
|
56
|
-
|
|
57
|
-
**Why it matters:** unlike entry files, subfolder docs aren't loaded every session — but a bloated `docs/setup.md` (1200 lines) is a sign the doc has become a dumping ground rather than a focused unit. Split into a folder.
|
|
58
|
-
|
|
59
|
-
**Exceptions:**
|
|
60
|
-
|
|
61
|
-
- `.brain/adrs/<one>.md` can be any length — one ADR captures one decision in full.
|
|
62
|
-
- `.brain/postmortems/<one>.md` can be any length — incidents need depth.
|
|
63
|
-
- `.brain/architecture/<one>.md` may be long; consider splitting into multiple files in the folder if so.
|
|
64
|
-
- `CHANGELOG.md` is append-only; it grows without bound.
|
|
65
|
-
|
|
66
|
-
**Auto-detection:**
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
find .brain docs -name '*.md' -type f \
|
|
70
|
-
-not -path '.brain/adrs/*' \
|
|
71
|
-
-not -path '.brain/postmortems/*' \
|
|
72
|
-
-not -path '.brain/architecture/*' \
|
|
73
|
-
-not -path '.brain/archive/*' \
|
|
74
|
-
| while read f; do
|
|
75
|
-
n=$(wc -l < "$f")
|
|
76
|
-
if [ "$n" -gt 500 ]; then
|
|
77
|
-
echo "$f: $n lines (consider splitting into a folder)"
|
|
78
|
-
fi
|
|
79
|
-
done
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
**Fix recipe:** if `docs/setup.md` is 1200 lines covering "first-time setup", "deployment", "troubleshooting", "FAQ" — split:
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
docs/setup/
|
|
86
|
-
├── README.md (~30 lines: index)
|
|
87
|
-
├── first-time.md (~150)
|
|
88
|
-
├── deployment.md (~200)
|
|
89
|
-
├── troubleshooting.md (~300)
|
|
90
|
-
└── faq.md (~150)
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
Update entry-file pointers from `docs/setup.md` → `docs/setup/`.
|
|
94
|
-
|
|
95
|
-
## Smell 3 — verbose prose
|
|
96
|
-
|
|
97
|
-
**The check:** prose-vs-bullet ratio. A heuristic: paragraphs longer than 4 sentences, especially in the `Conventions` or `Build` sections of AGENTS.md, are usually compressible.
|
|
98
|
-
|
|
99
|
-
**Why it matters:** the agent doesn't need narrative — it needs constraints. "We use TypeScript with strict mode enabled because we value type safety and prefer to catch errors at compile time rather than runtime" is twice the tokens of "TypeScript: strict" and worse for the agent.
|
|
100
|
-
|
|
101
|
-
**Auto-detection:** hard to automate cleanly. Practical approach: a script that flags any AGENTS.md / CLAUDE.md paragraph >300 characters as a review candidate.
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
# Find long paragraphs (>300 chars between blank lines) in AGENTS.md
|
|
105
|
-
awk -v RS='' -v ORS='\n\n' '{ if (length($0) > 300 && !/^#/) print NR": "$0 }' AGENTS.md
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
**Fix recipe:** convert prose to bullets or tables. Test: can you read it as instructions to follow? If yes, bullets. If no (it's actually motivation/context), move to a different file.
|
|
109
|
-
|
|
110
|
-
## Smell 4 — cross-doc repetition
|
|
111
|
-
|
|
112
|
-
Detailed in `redundancy-detection.md`. Token cost is 2× / 3× / Nx of the same content loaded N times.
|
|
113
|
-
|
|
114
|
-
## Smell 5 — redundant frontmatter / boilerplate
|
|
115
|
-
|
|
116
|
-
**The check:** YAML frontmatter that's longer than 5-10 lines per file, or boilerplate (license headers, "this file is auto-generated", boilerplate ToCs) that adds bytes without information.
|
|
117
|
-
|
|
118
|
-
**Why it matters:** small per-file but it adds up. A 50-line license header in every doc, across 80 docs, is 4000 lines of pure tax.
|
|
119
|
-
|
|
120
|
-
**Auto-detection:**
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
# Find files with YAML frontmatter >10 lines
|
|
124
|
-
for f in .brain/**/*.md docs/**/*.md *.md; do
|
|
125
|
-
[ -f "$f" ] || continue
|
|
126
|
-
if head -1 "$f" | grep -q '^---'; then
|
|
127
|
-
end=$(awk '/^---/{i++; if(i==2) print NR}' "$f")
|
|
128
|
-
[ -n "$end" ] && [ "$end" -gt 12 ] && echo "$f: frontmatter is $end lines"
|
|
129
|
-
fi
|
|
130
|
-
done
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Fix recipe:**
|
|
134
|
-
- Trim YAML frontmatter to: `date`, `coverage`, `peers`, `primary_sources`, `status`. That's it.
|
|
135
|
-
- Move license headers into a single `LICENSE` file at the root (which is the convention anyway).
|
|
136
|
-
- Generate ToCs at render time, not commit time. (For GitHub-rendered docs, the GitHub UI renders the ToC; don't commit one.)
|
|
137
|
-
|
|
138
|
-
## What does NOT count as token waste
|
|
139
|
-
|
|
140
|
-
- **Long ADRs** — one architectural decision captured in full is the right shape; don't compress for its own sake.
|
|
141
|
-
- **Long post-mortems** — incidents with complex root causes need depth.
|
|
142
|
-
- **Long CHANGELOG.md** — append-only history.
|
|
143
|
-
- **Long ARCHITECTURE.md (within reason)** — under ~1500 lines, the depth is earning its tokens. Past that, split.
|
|
144
|
-
- **Code examples** — if an example illustrates a non-obvious pattern, it's earning its place. If it's restating something obvious, cut it.
|
|
145
|
-
|
|
146
|
-
## The full audit pass
|
|
147
|
-
|
|
148
|
-
```bash
|
|
149
|
-
# 1. Entry file length
|
|
150
|
-
for f in AGENTS.md CLAUDE.md; do
|
|
151
|
-
[ -f "$f" ] && [ ! -L "$f" ] || continue
|
|
152
|
-
n=$(wc -l < "$f")
|
|
153
|
-
[ "$n" -gt 150 ] && echo "ENTRY-FILE-LENGTH: $f = $n lines"
|
|
154
|
-
done
|
|
155
|
-
|
|
156
|
-
# 2. Subfolder bloat
|
|
157
|
-
find .brain docs -name '*.md' -type f \
|
|
158
|
-
-not -path '.brain/adrs/*' \
|
|
159
|
-
-not -path '.brain/postmortems/*' \
|
|
160
|
-
-not -path '.brain/architecture/*' \
|
|
161
|
-
-not -path '.brain/archive/*' \
|
|
162
|
-
| while read f; do
|
|
163
|
-
n=$(wc -l < "$f")
|
|
164
|
-
[ "$n" -gt 500 ] && echo "SUBFOLDER-BLOAT: $f = $n lines"
|
|
165
|
-
done
|
|
166
|
-
|
|
167
|
-
# 3. Verbose prose in entry files
|
|
168
|
-
for f in AGENTS.md CLAUDE.md; do
|
|
169
|
-
[ -f "$f" ] && [ ! -L "$f" ] || continue
|
|
170
|
-
awk -v f="$f" -v RS='' '{ if (length($0) > 300 && !/^#/) print "VERBOSE-PARA: "f": "substr($0,1,80)"..." }' "$f"
|
|
171
|
-
done
|
|
172
|
-
|
|
173
|
-
# 4. Frontmatter bloat
|
|
174
|
-
for f in $(find . -name '*.md' -type f -not -path '*/node_modules/*'); do
|
|
175
|
-
head -1 "$f" 2>/dev/null | grep -q '^---' || continue
|
|
176
|
-
end=$(awk '/^---/{i++; if(i==2){ print NR; exit }}' "$f")
|
|
177
|
-
[ -n "$end" ] && [ "$end" -gt 12 ] && echo "FRONTMATTER-BLOAT: $f = $end lines"
|
|
178
|
-
done
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
## Severity rubric
|
|
182
|
-
|
|
183
|
-
| Finding | Severity | Why |
|
|
184
|
-
|---|---|---|
|
|
185
|
-
| Entry file >200 lines | High | Loaded every session; per-session waste |
|
|
186
|
-
| Entry file 150-200 | Medium | Approaching ceiling |
|
|
187
|
-
| Subfolder doc >500 (non-ADR/postmortem) | Medium | One-off cost when loaded; structural smell |
|
|
188
|
-
| Verbose prose in entry file | Low | Per-session waste, individually small |
|
|
189
|
-
| Frontmatter bloat | Low | Per-file waste, individually small |
|
|
190
|
-
|
|
191
|
-
## Cross-references
|
|
192
|
-
|
|
193
|
-
- Context budget (the math behind these limits): `../guidance/context-budget.md`
|
|
194
|
-
- Redundancy detection (cross-doc repetition): `redundancy-detection.md`
|
|
195
|
-
- LLM-doc-writing (content-quality companion): `../guidance/llm-doc-writing.md`
|
|
196
|
-
- Self-healing hooks (where these checks run automatically): `../recipes/self-healing-hooks.md`
|