From e6da0880a363ec062427288d2028d48c35dbeffd Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 13:22:03 +0000 Subject: [PATCH 1/7] =?UTF-8?q?R9:=20OKF=20bundle=20conformance=20?= =?UTF-8?q?=E2=80=94=20frontmatter=20policy,=20one-way=20links,=20Q7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The doc root becomes an Open Knowledge Format (OKF v0.2) bundle: required frontmatter on content docs, type: index on indexes (a documented R9 extension; okf_version on the root), index lines derived from the frontmatter one level down, directory-shaped map of maps with the same-commit split rule, one-way link policy with the capped Related section, conventions.md as the self-hosting doc, AGENTS.md upgraded to a first-class routing block, and new falsifying question Q7 for the bundle contract. Comment policy untouched. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CH1Y4ssZbAKJYwqBX1qxCF --- .../rules/R9-repo-brain.md | 115 ++++++++++++++++-- 1 file changed, 103 insertions(+), 12 deletions(-) diff --git a/go-linter-driven-development/rules/R9-repo-brain.md b/go-linter-driven-development/rules/R9-repo-brain.md index 2ec7c49..4d7f4db 100644 --- a/go-linter-driven-development/rules/R9-repo-brain.md +++ b/go-linter-driven-development/rules/R9-repo-brain.md @@ -8,7 +8,9 @@ carry it, higher rungs summarizing and pointing down, never duplicating. Two invariants hold the network together: **reachability** (every doc is reachable from the root: CLAUDE.md → index.md → doc — no orphans) and **bidirectionality** (code points up at its feature doc; docs point down at code via greppable symbols; the -index points everywhere). +index points everywhere). The doc root itself is an Open Knowledge Format (OKF +v0.2) bundle: every file carries YAML frontmatter, a file's path is its identity, +and the map is derived from the frontmatter below it (bundle policy below). ## Why @@ -123,12 +125,24 @@ func (p Policy) Do(ctx context.Context, op Op) error { ```markdown +--- +type: feature +title: Retry policy +description: why retries use capped full jitter; `Policy` API +timestamp: 2026-08-20T00:00:00Z +--- Entry point: `Policy.Do`. Construction: `ParsePolicy` — validates the cap against the base delay, so an unbounded backoff cannot exist. ``` ```markdown +--- +type: index +title: Repo map +description: map of all repo docs +okf_version: "0.2" +--- # Repo map **Resilience** @@ -146,7 +160,9 @@ the WHY the code cannot (the incident) and carries the upward edge to the featur doc; the doc points down with the greppable tokens `Policy.Do` and `ParsePolicy` — no path, no line number — and is listed in the index; CLAUDE.md imports the index, so the whole map is in context at session start. Grep `Policy` or open CLAUDE.md: -either way, the jitter incident is two hops away. +either way, the jitter incident is two hops away. And the index line is not +authored twice: it IS `retry-policy.md`'s `description`, derived from the +frontmatter — the map regenerates instead of drifting. ## Design guidance @@ -327,17 +343,71 @@ rule's (Q4 below — they must carry why/context, not restate the identifier). devices at **package/directory granularity** — a directories-only tree is just a set of package-path citations; file-level leaf entries are the violation. Prune the leaves, keep the tree. +- **Links are one-way — write an edge only when no structure implies it.** A doc's + parent is `index.md` in its own directory, derivable from the path alone: never + write a child→parent backlink, and never a `related:` frontmatter key — body + links ARE the machine-readable graph. Lateral doc→doc links go inline, with the + relationship stated in the sentence that carries the link ("auth retries use the + capped-jitter policy — [retry-policy.md](retry-policy.md)"). The one axis no + structure carries is code↔docs — which is exactly why those edges are written in + both directions and grep-verified (Q2). +- **Optional `## Related` section**: for real relationships that found no natural + sentence in the body. At most 3 entries — a cap, not a quota; zero is a valid + count, and a doc with none has no section. Every entry carries a reason clause + (a bare link is a violation, same as a WHAT-comment), and never duplicates a + link already inline. Entries are ordinary edges to Q2. + +### Frontmatter — the doc root as an OKF bundle (rungs 2–3) + +The doc root conforms to Open Knowledge Format v0.2 (markdown bundle: one concept +per file, path = identity, links form the graph), plus documented R9 extensions. + +- **Content docs** carry required `type` (`feature` / `architecture` / `guide`), + `title`, `description`, and `timestamp`; optional `tags`, and optional lifecycle + keys `status: draft|stable|deprecated` and `stale_after` — the frontmatter-native + form of the ⚠️ stale flag. +- **Indexes carry frontmatter as an R9 extension** (OKF v0.2 keeps reserved + `index.md` files frontmatter-free except a root `okf_version`): every index gets + `type: index`, `title`, `description`, `tags`; the root index adds + `okf_version: "0.2"`. **Never a `timestamp` on an index** — the index is derived, + not authored, and a churning timestamp in a derived file is drift bait. +- **Derivation rule**: every index line is derived from the frontmatter one level + down — a doc's line from its `description`, prefixed ⚠️ when its lifecycle says + so (`status: deprecated`, or `stale_after` in the past) or when a bootstrap pass + classified it stale; a sub-index's line in the root map from that sub-index's + `description` + `tags`. The map is regenerable, so it cannot drift from the + frontmatter that owns each fact. +- **Never emit `log.md`** — OKF reserves it for change history; this rule is + behavior-not-history, so the file must not exist in a doc root. +- **Broken links stay violations.** OKF tells consumers to tolerate dangling links + as not-yet-written knowledge; internally that tolerance would silence the drift + alarm. The *(planned)* marker (Q2) is the one sanctioned form of a + not-yet-written reference. +- Copy-pasteable templates (content doc, index, root index, conventions doc) live + in @documentation's reference.md; only the policy lives here. ### The index (rung 3) and the root - `index.md` lives in the doc root and MUST stay short: a concise reference guide, **one line per doc**, grouped by topic. It is the map, not a doc. -- Past ~300 lines it becomes a **map of maps**: the root index shrinks to links to - short topic or sub-project sub-indexes. This keeps the imported root cheap and - every doc still two hops from CLAUDE.md. +- Past ~300 lines it becomes a **map of maps**, and the split is directory-shaped: + each topic becomes a subdirectory with its own frontmattered `index.md` (OKF's + per-directory reserved file), and the root index shrinks to one derived line per + sub-index. The imported root stays cheap and every doc is still two hops away + (the root map is hop 0 — it rides in with the CLAUDE.md import). The split moves + files, so it lands in the **same commit** as the Q2-driven rewrite of code-side + `See docs/...` paths — a moved doc with a stale code edge is a broken network + between commits. - **Root wiring**: CLAUDE.md embeds the map via an `@/index.md` import - (e.g. `@docs/index.md`) so it is in context at session start. AGENTS.md is the - fallback host where CLAUDE.md is not used. + (e.g. `@docs/index.md`) so it is in context at session start. AGENTS.md — which + has no import syntax — carries a short plain routing block (start at the index; + conventions in `/conventions.md`) at the repo root and, in a monorepo, + nested per sub-project (closest file wins). It serves every tool that reads + AGENTS.md instead of CLAUDE.md, not just repos without a CLAUDE.md. +- **`/conventions.md` is the self-hosting doc** (`type: guide`): the + network's own maintenance rules — frontmatter templates, link rules, the Related + policy, the never-list — written for a contributor without this plugin. It is + listed FIRST in the index, one pointer line. ### Doc root discovery and monorepos @@ -359,7 +429,12 @@ rule's (Q4 below — they must carry why/context, not restate the identifier). (`See docs/.md`) from the package or type it describes — both invariants, reachability and bidirectionality, in one move. - **Wire the root**: add or repair the `@/index.md` import in CLAUDE.md - (AGENTS.md has no import syntax — use a plain reference line). + and the AGENTS.md routing block (no import syntax there — plain lines pointing + at the index and `conventions.md`). +- **Add missing frontmatter**: verify-or-add the required keys on any doc or index + that lacks them; derive the index line from the `description`. A `type` that + cannot be inferred from the doc's content is reported for a human call, never + guessed silently. - **Update the stale doc with the behavior change**: rewrite the affected section to describe current behavior — never append a changelog entry (the behavior-not-history discipline lives in @documentation). @@ -368,7 +443,9 @@ rule's (Q4 below — they must carry why/context, not restate the identifier). Answer each with evidence (`file:line`, command output) — never a bare verdict. Determine the doc root first (discovery order above); `` below is that -directory. +directory. Q1–Q3 and Q7 are fully mechanical: the plugin ships them as +`scripts/check-repo-brain.sh` (installed into the repo by the bootstrap pass), so +one command answers all four. 1. **Is any doc an orphan?** Detection: `find -name '*.md' ! -name 'index.md'` versus the link @@ -388,9 +465,11 @@ directory. detection: `grep -nE '\.go(:[0-9]+)?|line [0-9]+' /*.md | grep -v '://'` (the `://` filter exempts URLs, e.g. pkg.go.dev links) — regardless of whether the coordinate currently resolves. - Two exemptions: an index line carrying the ⚠️ stale flag (cites an unresolved - `Symbol`) is a recorded finding, not a broken edge — the decision to refresh, - remove, or keep it is the user's. And backticks are a resolvability contract — + Two exemptions, both scoped to symbol resolution (the file-path ban has no + exemption beyond URLs): an index line carrying the ⚠️ stale flag (cites an + unresolved `Symbol`) is a recorded finding, not a broken edge — the decision to + refresh, remove, or keep it is the user's. And backticks are a resolvability + contract — a future/roadmap symbol is written in prose or explicitly marked *(planned)*, and *(planned)*-marked citations are exempt from resolution. @@ -424,3 +503,15 @@ directory. Violation (advisory): a package with a citing feature doc changed and the doc did not — flag it with the doc's path as evidence; the fix is updating the affected section, never appending history. + +7. **Does any file break the bundle contract?** + Detection: for every `.md` under ``, check the first line is `---` + (e.g. `head -1` per file); for every `index.md`, check its frontmatter block + contains `type: index` and no `timestamp:` key; `grep -rn '^related:'` over + doc-root frontmatter; `find -name 'log.md'`. + Violation: a doc-root `.md` with no frontmatter block; an index missing + `type: index`; a `timestamp` key on an index; a `related:` frontmatter key + anywhere; a `log.md` anywhere in the doc root. + Advisory branch: a doc whose `stale_after` is in the past (or + `status: deprecated`) with no ⚠️ on its index line — recorded staleness the + map does not show; the fix is re-deriving the line (derivation rule above). From c63b9c541a70ea8b32d160f6aeafdf7135d7283a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 13:22:03 +0000 Subject: [PATCH 2/7] documentation skill: OKF templates, conventions self-hosting, bootstrap migration reference.md gains frontmatter templates (content doc / sub-index / root index), the conventions.md template, the AGENTS.md routing block, the class-to-type mapping, and the brownfield frontmatter-migration guidance; the Feature Doc Template's Related section is reconciled with the one-way link policy (optional, max 3, reason clauses). SKILL.md's FEATURE and BOOTSTRAP steps produce and verify the bundle: derived index lines, the new teach-and-enforce step (conventions.md + check script install), and Q7 in the self-checks. /wire-repo-brain's outcomes follow suit. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CH1Y4ssZbAKJYwqBX1qxCF --- .../commands/wire-repo-brain.md | 30 ++- .../skills/documentation/SKILL.md | 95 +++++--- .../skills/documentation/reference.md | 202 ++++++++++++++++-- 3 files changed, 261 insertions(+), 66 deletions(-) diff --git a/go-linter-driven-development/commands/wire-repo-brain.md b/go-linter-driven-development/commands/wire-repo-brain.md index a897c33..27b4b7a 100644 --- a/go-linter-driven-development/commands/wire-repo-brain.md +++ b/go-linter-driven-development/commands/wire-repo-brain.md @@ -19,20 +19,32 @@ mode** against `$ARGUMENTS` (default: the current repo root). The skill's protoc authoritative; this command adds nothing to it. One pass delivers the whole chain: 1. Doc root discovered (`.ai/` → `.ainav/` → `docs/`; per sub-project in a monorepo) -2. Existing docs inventoried and classified (stale docs indexed with a ⚠️ flag) -3. `index.md` built — short, grouped, one line per doc (map of maps past ~300 lines) -4. CLAUDE.md wired with the `@/index.md` import (AGENTS.md: plain reference) -5. **Upward edges wired**: every confidently-anchorable doc gets its one-line +2. Existing docs inventoried and classified (stale docs indexed with a ⚠️ flag); + OKF frontmatter verified-or-added (un-inferable types reported) +3. `index.md` built — frontmattered, short, grouped, one line per doc derived from + each doc's `description` (directory-shaped map of maps past ~300 lines) +4. CLAUDE.md wired with the `@/index.md` import + AGENTS.md routing block + (root, and nested per sub-project in a monorepo) +5. `/conventions.md` created/verified (listed first in the index) and the + plugin's `scripts/check-repo-brain.sh` installed — the report suggests the CI + one-liner +6. **Upward edges wired**: every confidently-anchorable doc gets its one-line `// See /.md ...` edge on its front-door symbol -6. R9 confirmation pass + the advisory findings report (broken edges, edge-policy - violations, rung-2 gaps, stale/unwired docs) +7. R9 confirmation pass — Q1–Q3 and Q7 via the installed script — + the advisory + findings report (broken edges, edge-policy violations, rung-2 gaps, + stale/unwired docs, types needing a human call) **What this command does NOT do** (by design — the skill's constraints): - Generate or rewrite content docs — gaps are reported for FEATURE mode to fill + (conventions.md and the copied check script are the two sanctioned artifacts) - Decide the fate of stale docs — refresh / remove / keep-as-roadmap is your call -- Touch anything beyond doc files, `index.md`, CLAUDE.md/AGENTS.md, and one-line - godoc edge additions (verified with `go vet` after each) +- Add CI workflows — the report only suggests `bash scripts/check-repo-brain.sh` +- Touch anything beyond doc files, `index.md`, `conventions.md`, + CLAUDE.md/AGENTS.md, the copied check script, and one-line godoc edge additions + (verified with `go vet` after each) When it finishes, review the report, then `git diff` — the changes should read as pure documentation-network wiring. Re-run any time: the pass is idempotent (existing -index lines are refreshed, existing edges and wiring are verified, not duplicated). +index lines are refreshed from frontmatter; existing edges, wiring, conventions, and +the script are verified, not duplicated — a repo wired by an older plugin version +converges to the current rules in one pass). diff --git a/go-linter-driven-development/skills/documentation/SKILL.md b/go-linter-driven-development/skills/documentation/SKILL.md index 2c7ef04..18f2c0c 100644 --- a/go-linter-driven-development/skills/documentation/SKILL.md +++ b/go-linter-driven-development/skills/documentation/SKILL.md @@ -8,8 +8,9 @@ description: | it into the network. BOOTSTRAP mode: on request ("set up docs", "create an index", "make this repo AI-navigable", /wire-repo-brain) or when FEATURE mode finds no doc root — discovers - the doc root, builds index.md, wires CLAUDE.md, wires missing code→docs edges, - reports gaps. + the doc root, verifies-or-adds OKF frontmatter, builds index.md, wires + CLAUDE.md/AGENTS.md, wires missing code→docs edges, installs conventions.md and + the conformance check script, reports gaps. NOT a changelog - documents current behavior, not change history. allowed-tools: - Read @@ -25,8 +26,9 @@ allowed-tools: Author and maintain the repo brain: a documentation network where any entry point — a grep hit on a symbol, a file open, CLAUDE.md at session start — reaches full context within two hops. Everything normative (the documentation ladder, both network -invariants, the comment policy, the edge policy, the index policy, root wiring, -doc-root discovery) lives ONCE in `../../rules/R9-repo-brain.md`; this skill is the +invariants, the comment policy, the edge policy, the index policy, the OKF +frontmatter/bundle policy, root wiring, doc-root discovery) lives ONCE in +`../../rules/R9-repo-brain.md`; this skill is the actor that applies it. Templates live in `reference.md` — they are menus, never forms. @@ -83,17 +85,20 @@ unless an R9 Q6 check shows a doc citing the reshaped code. recommendation in the report — never extra lines. Add testable examples (`Example_*`) for complex/core types. 4. **Rung 2 — feature doc**: create/update `/.md` from the - reference.md template: `Related` edges to sibling docs; key players as - `Symbol | Role | Package`; entry points cite symbols — never file paths or line - numbers (R9 edge policy). Bug fix → update the existing doc's affected section; - do not create a new doc. -5. **Rung 3 — the map**: add/refresh the doc's one line in `index.md`; verify root - wiring (`@/index.md` import in CLAUDE.md, AGENTS.md fallback). + reference.md template, with OKF frontmatter (required keys — R9's bundle + policy); optional `Related` edges (≤3, each with its reason — R9 edge policy); + key players as `Symbol | Role | Package`; entry points cite symbols — never + file paths or line numbers (R9 edge policy). Bug fix → update the existing + doc's affected section; do not create a new doc. +5. **Rung 3 — the map**: add/refresh the doc's one line in `index.md` — derived + from the doc's `description` (R9 derivation rule); verify root wiring + (`@/index.md` import in CLAUDE.md, AGENTS.md routing block). 6. **Self-check**: run R9's falsifying-question detections on the touched scope — - Q1–Q3 mechanically (orphans, broken edges in both directions, unwired root), - Q4–Q6 over the diff (WHAT-comments, naked exported API, silently-changed doc). - The detection commands live in R9; never restate them. Fix every hit before - reporting. + Q1–Q3 and Q7 mechanically (orphans, broken edges in both directions, unwired + root, bundle contract — the repo's `scripts/check-repo-brain.sh` runs all four + in one pass when installed), Q4–Q6 over the diff (WHAT-comments, naked exported + API, silently-changed doc). The detection commands live in R9; never restate + them. Fix every hit before reporting. 7. **Comment critique**: spawn the `comment-critic` agent (Agent tool) on the full diff — not just the comments this run wrote; in-body comments left by earlier phases are in scope too. Its spawn prompt MUST contain: (a) R9's comment-policy @@ -112,27 +117,40 @@ unless an R9 Q6 check shows a doc citing the reshaped code. 1. **Discover doc root(s)** per R9's discovery order (`.ai/` → `.ainav/` → `docs/`; create `docs/` if none exists). Monorepo → one doc root + index per sub-project. -2. **Inventory existing docs** and classify each: feature / architecture / guide / - stale (classification table in reference.md). -3. **Build or rebuild `index.md`**: a short reference guide — grouped by topic, one - line per doc; past ~300 lines it becomes a map of maps with short sub-indexes - (R9 index policy; templates in reference.md). +2. **Inventory existing docs**, classify each (feature / architecture / guide / + stale — classification table in reference.md), and **verify-or-add frontmatter** + (migration guidance in reference.md): a doc already conformant is left alone; an + un-inferable `type` goes to the advisory report, never guessed. +3. **Build or rebuild `index.md`**: frontmattered, grouped by topic, one line per + doc — each line derived from the frontmatter one level down (R9 derivation + rule); past ~300 lines it becomes a directory-shaped map of maps, and the split + lands in the same commit as the `See docs/...` path rewrite (R9 index policy; + templates in reference.md). 4. **Wire the root**: add the `@/index.md` import to CLAUDE.md (create a - minimal CLAUDE.md section if none exists); AGENTS.md has no import syntax — use - the plain-reference fallback. Snippets in reference.md. -5. **Wire missing upward edges**: for each indexed (non-stale) doc with no code-side + minimal CLAUDE.md section if none exists) and the AGENTS.md routing block — + repo root and, in a monorepo, nested per sub-project. Add or verify; snippets + in reference.md. +5. **Teach and enforce**: create-or-verify `/conventions.md` (template in + reference.md) — the ONE content file bootstrap generates (network + infrastructure, not a content doc) — listed FIRST in the index; copy the + plugin's `scripts/check-repo-brain.sh` into the target repo's `scripts/` + (verify-or-copy — a diverged copy is reported, never overwritten). The report + suggests CI wiring as plain `bash scripts/check-repo-brain.sh`; never add a + workflow file. +6. **Wire missing upward edges**: for each indexed (non-stale) doc with no code-side edge, add ONE line — `// See /.md ...` — to the front-door anchor's existing doc comment (anchor heuristic in reference.md), then confirm the package still vets. Wiring only: never rewrite the comment around it, never wire a stale doc (its ⚠️ index flag is the finding), and skip — as a reported gap — any doc whose anchor you cannot identify with confidence. -6. **Confirm and report**: re-run R9 Q1–Q3 as confirmation — a Q1 hit (a doc with no - index line) means step 3 didn't land and a Q3 hit means step 4 didn't; repair - either before reporting, and verify every edge added in step 5 resolves. The - ADVISORY findings list carries Q2 hits plus rung-2 gaps (two-signal criterion in - reference.md) and any doc left unwired in step 5. Bootstrap wires and maps; it - NEVER mass-generates content docs — those are written incrementally by FEATURE - mode. +7. **Confirm and report**: re-run R9 Q1–Q3 and Q7 as confirmation — via the + installed script — a Q1 hit (a doc with no index line) means step 3 didn't land, + a Q3 hit means step 4 didn't, a Q7 hit means step 2 or 5 didn't; repair any + before reporting, and verify every edge added in step 6 resolves. The ADVISORY + findings list carries Q2 hits plus rung-2 gaps (two-signal criterion in + reference.md), any doc left unwired in step 6, and any `type` needing a human + call. Bootstrap wires and maps; it NEVER mass-generates content docs — those are + written incrementally by FEATURE mode. @@ -152,7 +170,7 @@ Network edges added: - docs→code: - root: @/index.md in CLAUDE.md (verified/added) -R9 self-check: Q1–Q3 clean · Q4–Q6 clean over diff +R9 self-check: Q1–Q3, Q7 clean · Q4–Q6 clean over diff (or per hit: : — fixed by ) Comment critic: reviewed — deleted · trimmed · rewritten · @@ -169,7 +187,10 @@ BOOTSTRAP mode: BOOTSTRAP COMPLETE Doc root(s): Index: /index.md built — docs, groups; map of maps: -Root wiring: CLAUDE.md @import (or AGENTS.md plain reference) +Frontmatter: verified, added +Root wiring: CLAUDE.md @import · AGENTS.md routing block +Conventions: /conventions.md +Check script: scripts/check-repo-brain.sh — suggest CI: bash scripts/check-repo-brain.sh Upward edges: wired — (), ... Advisory findings (reported, not fixed — FEATURE mode writes content): @@ -177,6 +198,8 @@ Advisory findings (reported, not fixed — FEATURE mode writes content): - broken edge: (unresolved) - gap: - stale: — indexed with ⚠️ flag; cites unresolved ; not edge-wired +- type?: — class not inferable; needs a human call +- diverged script: scripts/check-repo-brain.sh differs from the plugin's — not overwritten ``` @@ -188,8 +211,11 @@ Advisory findings (reported, not fixed — FEATURE mode writes content): - FEATURE: the comment-critic ran over the full diff, every non-KEEP verdict was applied (R3 routes reported, not fixed), and the one re-critique confirmed clean — or the remainder is reported as-is. -- BOOTSTRAP: root(s) + index + root wiring exist; every confidently-anchorable doc - has an upward edge; gaps reported; zero content docs generated. +- BOOTSTRAP: root(s) + frontmattered index + root wiring + conventions.md + check + script exist; frontmatter verified-or-added on every doc; every + confidently-anchorable doc has an upward edge; gaps reported; zero content docs + generated (conventions.md and the copied script are the two sanctioned + artifacts). - All prose passes the 5-year reader test; zero changelog-style entries. @@ -198,7 +224,8 @@ This skill MUST NOT: - Restate R9 content — the documentation ladder, invariants, and policies are cited, never copied. - Append change history to docs — current behavior only, always. -- Mass-generate content docs in BOOTSTRAP mode — advisory gap report only. +- Mass-generate content docs in BOOTSTRAP mode — advisory gap report only + (conventions.md and the copied check script are the two sanctioned artifacts). - Fill templates for their own sake — reference.md's templates are menus; R9's comment policy decides what earns its place. - Spawn anything other than `comment-critic`, loop the critique more than one diff --git a/go-linter-driven-development/skills/documentation/reference.md b/go-linter-driven-development/skills/documentation/reference.md index 2d68947..ac1615b 100644 --- a/go-linter-driven-development/skills/documentation/reference.md +++ b/go-linter-driven-development/skills/documentation/reference.md @@ -9,10 +9,12 @@ policy, index policy, root wiring, doc-root discovery — lives ONCE in - [Comment Value Toolbox](#comment-value-toolbox) — the growable catalog of ways a comment delivers value - [Godoc Menus](#godoc-menus) — package, type, function menus; testable examples -- [Feature Doc Template](#feature-doc-template) — with `Related` edges and symbol-cited key players -- [The Index and Root Wiring](#the-index-and-root-wiring) — index.md, map of maps, CLAUDE.md/AGENTS.md snippets +- [Frontmatter Templates (OKF Bundle)](#frontmatter-templates-okf-bundle) — content doc, index, root index +- [Feature Doc Template](#feature-doc-template) — frontmatter, symbol-cited key players, optional `Related` +- [The Index and Root Wiring](#the-index-and-root-wiring) — index.md, map of maps, CLAUDE.md import, AGENTS.md routing block +- [Conventions Doc (Self-Hosting)](#conventions-doc-self-hosting) — the `conventions.md` template bootstrap installs - [Doc Roots and Monorepos](#doc-roots-and-monorepos) -- [Bootstrap Classification](#bootstrap-classification) — feature / architecture / guide / stale; rung-2 gap criterion; upward-edge anchor heuristic +- [Bootstrap Classification](#bootstrap-classification) — feature / architecture / guide / stale; frontmatter migration; rung-2 gap criterion; upward-edge anchor heuristic - [Checklists](#checklists) — feature docs, code comments, quality gates - [Guidelines](#guidelines) — bug-fix documentation, managing documentation size - [Examples](#examples) — good vs bad worked examples @@ -313,16 +315,71 @@ feature docs. --- +## Frontmatter Templates (OKF Bundle) + +Every `.md` in the doc root starts with YAML frontmatter (R9's bundle policy — the +one non-menu part of any template: the required keys are not optional). Index lines +are derived from the `description` one level down, so write the description as the +index line you want. + +**Content doc** (feature / architecture / guide): + +```yaml +--- +type: feature +title: Retry policy +description: why retries use capped full jitter; `Policy` API +timestamp: 2026-08-20T00:00:00Z +# optional: +# tags: [resilience, retry] +# status: stable # draft | stable | deprecated +# stale_after: 2027-01-01 # past this date the index line gets the ⚠️ flag +--- +``` + +**Sub-index** (`/index.md` — an R9 extension; OKF keeps indexes bare): + +```yaml +--- +type: index +title: Resilience +description: retries, circuit breaking, timeouts +tags: [resilience, retry, backoff] +--- +``` + +**Root index** (`/index.md`) — same keys plus the bundle version; never a +`timestamp` on any index (derived files don't get authored churn): + +```yaml +--- +type: index +title: Repo map +description: map of all repo docs +tags: [go] +okf_version: "0.2" +--- +``` + +--- + ## Feature Doc Template -The sections are a menu too: a small feature may need only Problem & Solution, Entry -Points, and Related. All code citations follow R9's edge policy: exported symbols +The sections are a menu too: a small feature may need only Problem & Solution and +Entry Points. The frontmatter block is the exception — its required keys always +ship (Frontmatter Templates above). All code citations follow R9's edge policy: exported symbols first — the shortest token that greps uniquely, package-qualified only on ambiguity; package or directory paths when a location is genuinely needed (directories for symbol-less artifacts like examples/, paired with the symbols they demonstrate); file paths and line numbers never. ```markdown +--- +type: feature +title: [Feature Name] +description: [the index line — one line, what and why; key symbols] +timestamp: [ISO 8601] +--- # [Feature Name] ## Problem & Solution @@ -377,7 +434,9 @@ Input → Validation → Processing → Storage → Output - [Known limitations, potential extensions] ## Related -Edges to sibling docs: +Optional, and capped at 3 — a ceiling, not a quota: zero entries means no section. +Only relationships that found no natural sentence in the body; every entry carries +its reason clause, and never duplicates a link already inline (R9 edge policy): - [auth.md](auth.md) — how sessions authenticate created users - [notifications.md](notifications.md) — welcome-email delivery ``` @@ -389,11 +448,22 @@ Edges to sibling docs: ### index.md Template A short reference guide: grouped by topic, ONE line per doc (size and style are -normative in R9's index policy): +normative in R9's index policy). Each line IS the linked doc's `description` — +derived, never authored twice; the ⚠️ flag rides in from the doc's lifecycle keys +or a stale classification (R9's derivation rule): ```markdown +--- +type: index +title: Repo Map +description: map of all repo docs +tags: [go] +okf_version: "0.2" +--- # Repo Map +- [conventions.md](conventions.md) — how to maintain this doc root (read before editing docs) + **Resilience** - [retry-policy.md](retry-policy.md) — why retries use capped full jitter; `Policy` API @@ -404,16 +474,18 @@ normative in R9's index policy): ### Map of Maps (past ~300 lines) -The root index shrinks to links to short topic or sub-project sub-indexes (R9): +The split is directory-shaped: each topic becomes a subdirectory with its own +frontmattered `index.md`, and the root index shrinks to one line per sub-index, +derived from that sub-index's `description` + `tags` (R9). The split moves files — +it lands in the same commit as the rewrite of the code-side `See docs/...` paths: ```markdown -# Repo Map - - [Resilience](resilience/index.md) — retries, circuit breaking, timeouts - [Users](users/index.md) — identity, sessions, notifications ``` -Each sub-index follows the one-line-per-doc form above. +Each sub-index follows the one-line-per-doc form above, with sub-index frontmatter +(Frontmatter Templates; no `okf_version` — that key is the root's alone). ### CLAUDE.md Wiring Snippet @@ -422,12 +494,77 @@ Each sub-index follows the one-line-per-doc form above. @docs/index.md ``` -The `@` import puts the map in context at session start. AGENTS.md has no import -syntax — fall back to a plain reference: +The `@` import puts the map in context at session start. + +### AGENTS.md Routing Block + +AGENTS.md has no import syntax; it carries a short routing block instead — for +every tool that reads AGENTS.md rather than CLAUDE.md, not just repos without a +CLAUDE.md. At the repo root and, in a monorepo, nested per sub-project (agents use +the closest file, so each sub-project's block names its own doc root): ```markdown ## Documentation -Start at docs/index.md — the map of all repo docs. +Docs live in docs/ — start at docs/index.md, the map of all repo docs. +Before adding or editing anything under docs/, read docs/conventions.md +(frontmatter, link rules, what never to do). +When you change exported API behavior, update the doc that cites it and its +index line. Check your work: bash scripts/check-repo-brain.sh +``` + +--- + +## Conventions Doc (Self-Hosting) + +`/conventions.md` is the network's own maintenance manual, written for a +contributor without this plugin — the ONE content file bootstrap generates (network +infrastructure, not a content doc). Listed FIRST in the index. Template: + +```markdown +--- +type: guide +title: Doc conventions +description: how to maintain this doc root (read before editing docs) +timestamp: [ISO 8601] +--- +# Doc Conventions + +This directory is the repo's documentation network — an OKF bundle. Markdown files +with YAML frontmatter; `index.md` is the map; links form the graph. Rules: + +## Frontmatter +Every `.md` here starts with frontmatter. Content docs (copy-paste, fill in): + + --- + type: feature # feature | architecture | guide + title: + description: + timestamp: + --- + +Index files use `type: index` and never a `timestamp`. Optional on content docs: +`tags`, `status: draft|stable|deprecated`, `stale_after: `. + +## Links +- The index line for a doc IS its `description` — update the doc's frontmatter, + then mirror the line in `index.md`. +- Cite code by exported symbol (`` or `.`), never by file + path or line number. Backticks are a promise: a backticked symbol must grep in + this repo (mark future ones *(planned)* and write them without backticks). +- Link related docs inline, in the sentence that explains the relationship. + Links are one-way: never add a link back to `index.md` or a parent. +- An optional `## Related` section may close a doc: at most 3 entries, each with + a reason ("— how sessions authenticate created users"), none duplicating an + inline link. + +## Never +- No `log.md`, no changelog sections — docs describe current behavior, not history. +- No `related:` key in frontmatter — links live in the body. +- No file paths or line numbers as code references. + +## Check your work +Run `bash scripts/check-repo-brain.sh` from the repo root — it verifies the rules +above mechanically and points at this file when something breaks. ``` --- @@ -448,14 +585,16 @@ Start at docs/index.md — the map of all repo docs. ## Bootstrap Classification -Classify each inventoried doc; the class decides its index line and grouping: +Classify each inventoried doc; the class decides its index line, grouping, and +frontmatter `type` (stale is a lifecycle, not a type — it keeps the class it would +otherwise have, expressed via `status`/`stale_after` plus the flagged line): -| Class | Signals | Index treatment | -|-------|---------|-----------------| -| **feature** | describes one capability's behavior; cites its symbols | group under its topic | -| **architecture** | cross-feature structure, system-wide patterns | its own "Architecture" group | -| **guide** | setup, how-to, onboarding, runbooks | "Guides" group | -| **stale** | cites symbols/packages that no longer resolve; describes removed behavior | index with a FLAGGED line (below); the flag is the advisory finding | +| Class | Signals | Index treatment | Frontmatter `type` | +|-------|---------|-----------------|--------------------| +| **feature** | describes one capability's behavior; cites its symbols | group under its topic | `feature` | +| **architecture** | cross-feature structure, system-wide patterns | its own "Architecture" group | `architecture` | +| **guide** | setup, how-to, onboarding, runbooks | "Guides" group | `guide` | +| **stale** | cites symbols/packages that no longer resolve; describes removed behavior | index with a FLAGGED line (below); the flag is the advisory finding | its underlying class | **Stale never means unindexed** — R9's Q1 reachability invariant always wins. A stale doc gets a flagged index line naming the unresolved symbol: @@ -472,6 +611,18 @@ Bootstrap never decides. When unsure between feature and architecture: one capability → feature; the seams between capabilities → architecture. +### Frontmatter Migration (Brownfield) + +An existing network without frontmatter — wired by hand, or by a plugin version +before the OKF layer — is just another brownfield state. **Verify-or-add, never +duplicate**: a doc that already has conformant frontmatter is left alone; a doc +without gets the required keys, with `description` written as its index line and +`timestamp` from the doc's last substantive git touch when evident. A `type` the +classification table cannot settle goes to the advisory report +(`type?: — class not inferable`) — never guessed silently. Same for +`conventions.md` and the check script: create or verify, and report a diverged +script rather than overwriting it. + ### Rung-2 Gap Criterion (BOOTSTRAP) FEATURE mode anchors R9 Q5 on the diff; bootstrap has no diff. Report a rung-2 gap @@ -508,14 +659,19 @@ vets after the edit. ### Feature Documentation Checklist +- [ ] Frontmatter present with the four required keys (`type`, `title`, + `description`, `timestamp`); `description` reads as the index line - [ ] Clear problem statement and high-level solution approach - [ ] Entry points listed, cited by symbol (e.g. `POST /users` → `UserHandler.Create`) - [ ] Key players table with Symbol, Role, and Package — no file paths, no line numbers - [ ] Design decisions explained with rationale, connected to coding principles - [ ] Data flow and integration points documented - [ ] Usage examples are runnable and copy-pasteable -- [ ] `Related` section carries edges to sibling docs -- [ ] Doc has its one line in `index.md`, and at least one code-side edge names it +- [ ] `Related` section, if present, has ≤3 entries, each with a reason clause, + none duplicating an inline link +- [ ] Doc has its one line in `index.md` — derived from its `description` — and at + least one code-side edge names it +- [ ] No `log.md`, no changelog sections, no `related:` frontmatter key ### Code Comments Checklist From c1e31124bd60acce96ac5ce2e8137bc5cac12011 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 13:22:03 +0000 Subject: [PATCH 3/7] scripts: add check-repo-brain.sh conformance gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dependency-free bash gate running R9's mechanical questions — Q1 orphans, Q2 edges in both directions plus the file:line ban, Q3 root wiring, Q7 bundle contract — with exit 0 clean/not-adopted, 1 violations, 2 usage error. Installed into target repos by the documentation skill's BOOTSTRAP pass; every failure message points at the doc root's conventions.md. Verified against a 15-case fixture matrix (true positives for each check, the two symbol-resolution exemptions, URL exemption, fence skipping). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CH1Y4ssZbAKJYwqBX1qxCF --- .../scripts/check-repo-brain.sh | 193 ++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100755 go-linter-driven-development/scripts/check-repo-brain.sh diff --git a/go-linter-driven-development/scripts/check-repo-brain.sh b/go-linter-driven-development/scripts/check-repo-brain.sh new file mode 100755 index 0000000..d33442e --- /dev/null +++ b/go-linter-driven-development/scripts/check-repo-brain.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +# Repo-brain conformance gate for the go-linter-driven-development plugin. +# +# Runs R9's mechanical falsifying questions over the repo's doc root (an OKF +# bundle) so CI — and developers without the plugin — can hold the +# documentation network's invariants. Installed into target repos by the +# documentation skill's BOOTSTRAP pass (/wire-repo-brain). +# +# Usage: bash scripts/check-repo-brain.sh [repo-root] (default: cwd) +# CI: one line — bash scripts/check-repo-brain.sh +# +# Checks (numbering follows rules/R9-repo-brain.md's falsifying questions): +# Q1 orphans — every non-index doc has an index line reaching it +# Q2 edges — code→docs paths resolve; doc-cited exported symbols +# grep in the repo; no file:line citations (URLs exempt) +# Q3 root wiring — CLAUDE.md or AGENTS.md references the index +# Q7 bundle contract— frontmatter on every doc-root .md; `type: index` and +# no `timestamp:` on indexes; no `related:` key; no log.md +# +# Heuristics (documented, deliberate): +# - docs→code checks only backticked tokens shaped like exported Go +# identifiers (`Foo`, `Foo.Bar`) that contain a lowercase letter; other +# backticks (paths, flags, ALL-CAPS initialisms, ) are skipped. +# - lines carrying the ⚠️ stale flag or a *(planned)* marker are exempt from +# symbol resolution (R9 Q2's two exemptions); the file:line ban has no +# exemption beyond URLs (lines containing ://). +# - fenced code blocks are skipped for symbol resolution. +# +# Exit codes: 0 clean (or repo has no doc root yet — advisory no-op) +# 1 one or more violations (details on stderr, summary last) +# 2 usage error +# +# Uses only POSIX-portable tools: find, grep, sed, head, sort, wc. No jq/python. + +set -u + +REPO_ROOT="${1:-$(pwd)}" +if [[ ! -d "$REPO_ROOT" ]]; then + echo "check-repo-brain: not a directory: $REPO_ROOT" >&2 + exit 2 +fi +cd "$REPO_ROOT" || exit 2 + +DOCROOT="" +for d in .ai .ainav docs; do + [[ -d "$d" ]] && DOCROOT="$d" && break +done +if [[ -z "$DOCROOT" ]]; then + echo "check-repo-brain: no doc root (.ai/, .ainav/, docs/) — nothing to check yet; run /wire-repo-brain to bootstrap" + exit 0 +fi + +violations=0 +fail() { + echo " $1 — see $DOCROOT/conventions.md" >&2 + violations=$((violations + 1)) +} + +# canon -> physical path with .. resolved (empty if parent dir missing) +canon() { + local dir base + dir=$(dirname "$1") + base=$(basename "$1") + (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$base") +} + +# resolve_link -> absolute path ('' for URLs/anchors) +resolve_link() { + local from="$1" target="$2" + target="${target%%#*}" + [[ -z "$target" || "$target" == *"://"* ]] && return 0 + if [[ "$target" == /* ]]; then + printf '%s\n' "$(canon "$DOCROOT/${target#/}")" # bundle-relative (OKF) + else + printf '%s\n' "$(canon "$(dirname "$from")/$target")" + fi +} + +# ---------- Q1: orphans — every non-index doc reachable from an index ---------- +indexed_targets="" +while IFS= read -r idx; do + while IFS= read -r raw; do + t="${raw#](}"; t="${t%)}" + [[ "$t" == *.md ]] || continue + resolved=$(resolve_link "$idx" "$t") + [[ -n "$resolved" ]] && indexed_targets="$indexed_targets$resolved +" + done < <(grep -oE '\]\([^)]+\)' "$idx" 2>/dev/null) +done < <(find "$DOCROOT" -type f -name 'index.md') + +while IFS= read -r doc; do + c=$(canon "$doc") + if ! printf '%s' "$indexed_targets" | grep -Fxq "$c"; then + fail "[Q1] $doc — orphan: no index.md lists it" + fi +done < <(find "$DOCROOT" -type f -name '*.md' ! -name 'index.md') + +# ---------- Q2: code→docs edges and doc→doc links resolve ---------- +have_go=0 +if find . -name '*.go' -not -path './vendor/*' -not -path './.git/*' -print -quit 2>/dev/null | grep -q .; then + have_go=1 +fi + +if (( have_go )); then + while IFS= read -r hit; do + file="${hit%%:*}"; rest="${hit#*:}"; line="${rest%%:*}"; target="${rest#*:}" + [[ -f "$target" ]] || fail "[Q2] $file:$line — code edge points at missing $target" + done < <(grep -rnoE '(docs|\.ai|\.ainav)/[A-Za-z0-9._/-]+\.md' \ + --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null) +fi + +while IFS= read -r md; do + while IFS= read -r raw; do + t="${raw#](}"; t="${t%)}" + [[ "$t" == *.md* ]] || continue + resolved=$(resolve_link "$md" "$t") + [[ -z "$resolved" ]] && continue + [[ -f "$resolved" ]] || fail "[Q2] $md — link target does not exist: $t" + done < <(grep -oE '\]\([^)]+\)' "$md" 2>/dev/null) +done < <(find "$DOCROOT" -type f -name '*.md') + +# ---------- Q2: docs→code — backticked exported symbols must grep ---------- +if (( have_go )); then + while IFS= read -r md; do + in_fence=0 + lineno=0 + while IFS= read -r line; do + lineno=$((lineno + 1)) + case "$line" in '```'*) in_fence=$((1 - in_fence)); continue ;; esac + (( in_fence )) && continue + case "$line" in *'⚠️'*|*'*(planned)*'*) continue ;; esac + while IFS= read -r tok; do + tok="${tok#\`}"; tok="${tok%\`}" + printf '%s' "$tok" | grep -qE '^[A-Z][A-Za-z0-9]*(\.[A-Z][A-Za-z0-9]*)?$' || continue + printf '%s' "$tok" | grep -q '[a-z]' || continue + if [[ "$tok" == *.* ]]; then + method="${tok#*.}" + grep -rqE "\) ?[A-Za-z0-9_]* ?\*?[A-Za-z0-9_]*\) ${method}\(|func .*\) ${method}\(" \ + --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null && continue + grep -rqE "func ${method}\(" \ + --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null && continue + fail "[Q2] $md:$lineno — backticked \`$tok\` does not resolve (method ${method} not found)" + else + grep -rqE "(type|func) ${tok}\b" \ + --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null && continue + fail "[Q2] $md:$lineno — backticked \`$tok\` does not resolve (no type/func ${tok})" + fi + done < <(printf '%s\n' "$line" | grep -oE '`[^`]+`') + done < "$md" + done < <(find "$DOCROOT" -type f -name '*.md') +fi + +# ---------- Q2: file:line citation ban (URLs exempt) ---------- +while IFS= read -r hit; do + file="${hit%%:*}"; rest="${hit#*:}"; line="${rest%%:*}" + fail "[Q2] $file:$line — cites a file path or line number (churn-prone coordinate)" +done < <(grep -rnE '\.go(:[0-9]+)?|line [0-9]+' "$DOCROOT" --include='*.md' 2>/dev/null | grep -v '://') + +# ---------- Q3: root wiring ---------- +if ! grep -l 'index.md' CLAUDE.md AGENTS.md >/dev/null 2>&1; then + fail "[Q3] repo root — neither CLAUDE.md nor AGENTS.md references $DOCROOT/index.md" +fi + +# ---------- Q7: bundle contract ---------- +while IFS= read -r md; do + if [[ "$(head -1 "$md" 2>/dev/null)" != "---" ]]; then + fail "[Q7] $md — no frontmatter block (first line must be ---)" + continue + fi + fm=$(sed -n '2,/^---$/p' "$md") + if printf '%s\n' "$fm" | grep -q '^related:'; then + fail "[Q7] $md — 'related:' frontmatter key (links live in the body)" + fi + if [[ "$(basename "$md")" == "index.md" ]]; then + printf '%s\n' "$fm" | grep -q '^type: index' \ + || fail "[Q7] $md — index frontmatter missing 'type: index'" + if printf '%s\n' "$fm" | grep -q '^timestamp:'; then + fail "[Q7] $md — timestamp on an index (derived files get no authored churn)" + fi + fi +done < <(find "$DOCROOT" -type f -name '*.md') + +while IFS= read -r lg; do + fail "[Q7] $lg — log.md is reserved for change history; docs describe current behavior" +done < <(find "$DOCROOT" -type f -name 'log.md') + +# ---------- summary ---------- +if (( violations > 0 )); then + echo "check-repo-brain: $violations violation(s) in $DOCROOT/ — rules: $DOCROOT/conventions.md" >&2 + exit 1 +fi +echo "check-repo-brain: clean ($DOCROOT/)" +exit 0 From cd4478a62945e3907981877d350b864f92f60bd8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 13:22:03 +0000 Subject: [PATCH 4/7] =?UTF-8?q?bookkeeping:=202.10.0=20=E2=80=94=20changel?= =?UTF-8?q?og,=20READMEs,=20hunter=20hunt-focus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Version bump with the 2.10.0 changelog entry, plugin README tree and table rows for the OKF layer and the new scripts/ directory, the stale root-README version cell fixed in passing, and the pre-commit-review R9 hunter's hunt-focus extended with bundle-contract breaks. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CH1Y4ssZbAKJYwqBX1qxCF --- README.md | 2 +- .../.claude-plugin/plugin.json | 4 +- go-linter-driven-development/CHANGELOG.md | 51 +++++++++++++++++++ go-linter-driven-development/README.md | 7 +-- .../skills/pre-commit-review/SKILL.md | 2 +- 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6a2571e..66cd18c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A [Claude Code](https://claude.ai/code) plugin marketplace for **linter-driven d | | Plugin | Version | For | |---|--------|---------|-----| -| 🐹 | [`go-linter-driven-development`](go-linter-driven-development/README.md) | 2.6.0 | Go | +| 🐹 | [`go-linter-driven-development`](go-linter-driven-development/README.md) | 2.10.0 | Go | | ⚛️ | [`ts-react-linter-driven-development`](ts-react-linter-driven-development/README.md) | 1.2.0 | TypeScript + React | Plus the standalone rule documents the plugins grew out of: diff --git a/go-linter-driven-development/.claude-plugin/plugin.json b/go-linter-driven-development/.claude-plugin/plugin.json index 10e6e29..dc78570 100644 --- a/go-linter-driven-development/.claude-plugin/plugin.json +++ b/go-linter-driven-development/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "go-linter-driven-development", - "version": "2.9.1", - "description": "Rules-as-data linter-driven development workflow for Go: 12 single-source rule files, thin directional skills (design, TDD implementation, refactoring, testing, review, documentation), and a hunter/skeptic/critic review architecture with parallel single-rule reviewers, an over-abstraction skeptic, and a comment critic enforcing the Comment Value Toolbox", + "version": "2.10.0", + "description": "Rules-as-data linter-driven development workflow for Go: 12 single-source rule files, thin directional skills (design, TDD implementation, refactoring, testing, review, documentation), a hunter/skeptic/critic review architecture with parallel single-rule reviewers, an over-abstraction skeptic, and a comment critic enforcing the Comment Value Toolbox, plus an OKF-conformant repo brain with a shippable CI conformance gate", "author": { "name": "Dan Mordechay" }, diff --git a/go-linter-driven-development/CHANGELOG.md b/go-linter-driven-development/CHANGELOG.md index f3447ad..753b038 100644 --- a/go-linter-driven-development/CHANGELOG.md +++ b/go-linter-driven-development/CHANGELOG.md @@ -3,6 +3,57 @@ All notable changes to the `go-linter-driven-development` plugin are documented here. Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [Semantic Versioning](https://semver.org/). +## [2.10.0] - 2026-08-20 + +The repo brain had one audience: a session with this plugin installed. In a +monorepo most contributors — and their agents — don't have it, so the network's +rules lived nowhere they could find and nothing enforced them. This release +makes the doc root a standard, self-describing artifact: an Open Knowledge +Format (OKF v0.2) bundle any tool can consume, with its own maintenance manual +inside and a CI gate outside. + +### Added + +- **OKF v0.2 bundle conformance (R9)**: every doc-root file carries YAML + frontmatter — content docs `type`/`title`/`description`/`timestamp` (+ + optional `tags` and lifecycle `status`/`stale_after`, the frontmatter-native + form of the ⚠️ stale flag); indexes carry `type: index` (a documented R9 + extension — OKF keeps reserved index.md bare) and never a `timestamp`; the + root index carries `okf_version`. New falsifying question **Q7** checks the + bundle contract mechanically. +- **Derivation rule (R9)**: every index line is derived from the frontmatter + one level down — a doc's line IS its `description` — so the map is + regenerable and cannot drift. The map of maps is directory-shaped (per-topic + subdirectories with their own frontmattered index.md), and the split lands in + the same commit as the `See docs/...` path rewrite. +- **One-way link policy (R9)**: write an edge only when no structure implies + it — no child→parent backlinks, no `related:` frontmatter key, lateral links + inline with the reason in the sentence; an optional `## Related` section is + capped at 3 reason-claused entries (a ceiling, not a quota). `log.md` is + never emitted. +- **Self-hosting conventions doc**: BOOTSTRAP creates `/conventions.md` + (template in reference.md) — the network's maintenance rules written for + contributors without this plugin, listed first in the index. +- **AGENTS.md routing block**: upgraded from "fallback when CLAUDE.md is + absent" to first-class multi-tool coverage — root and nested per sub-project + (closest file wins), pointing every AGENTS.md-reading agent at the index and + conventions.md. +- **New `scripts/check-repo-brain.sh`**: dependency-free conformance gate + running Q1–Q3 and Q7 (orphans, both edge directions, file:line ban, root + wiring, bundle contract); BOOTSTRAP installs it into target repos and + suggests the one-line CI wiring. Exit 0 clean/not-adopted, 1 violations, + 2 usage error; every failure message points at conventions.md. + +### Changed + +- **BOOTSTRAP is now a migration pass too**: frontmatter is verified-or-added + (never duplicated), so a network wired by an older plugin version converges + to the current rules in one idempotent re-run; an un-inferable `type` goes to + the advisory report, never guessed. +- **Feature Doc Template's `Related` section** reconciled with the one-way link + policy: optional, ≤3 entries, each with a reason clause, none duplicating an + inline link. + ## [2.9.1] - 2026-07-23 A real 143-line file surfaced the gap v2.9.0 left open: every one of its nine diff --git a/go-linter-driven-development/README.md b/go-linter-driven-development/README.md index 3df8656..41cfe0c 100644 --- a/go-linter-driven-development/README.md +++ b/go-linter-driven-development/README.md @@ -24,6 +24,7 @@ go-linter-driven-development/ │ pre-commit-review · testing · documentation (thin directional views) ├── agents/ rule-hunter · overabstraction-skeptic · lint-fixer (isolated workers) ├── commands/ go-ldd-analyze · autopilot · quickfix · prepare · review · status · wire-repo-brain +├── scripts/ check-repo-brain.sh — repo-brain conformance gate, installed into target repos by /wire-repo-brain └── hooks/ package-size gate ``` @@ -82,7 +83,7 @@ Isolated contexts matter: the `lint-fixer` loop's token noise stays out of your | R6 | [`rules/R6-test-only-interfaces.md`](rules/R6-test-only-interfaces.md) | No interface whose only second implementer is a test double | | R7 | [`rules/R7-test-placement.md`](rules/R7-test-placement.md) | `pkg_test` only, no wantErr conditionals, right-rung tests, no sleeps | | R8 | [`rules/R8-no-globals.md`](rules/R8-no-globals.md) | No package-level state; no `context.Background()` in library code | -| R9 | [`rules/R9-repo-brain.md`](rules/R9-repo-brain.md) | Documentation network: fact at its lowest rung, reachable from the root, edges both directions; index wired into CLAUDE.md | +| R9 | [`rules/R9-repo-brain.md`](rules/R9-repo-brain.md) | Documentation network: fact at its lowest rung, reachable from the root, edges both directions; index wired into CLAUDE.md; doc root is an OKF bundle (frontmatter, derived index lines) | | R10 | [`rules/R10-concurrency-safety.md`](rules/R10-concurrency-safety.md) | Goroutines with owners and exit paths; shared state guarded where it lives; no production sleeps | | R11 | [`rules/R11-conditional-dispatch.md`](rules/R11-conditional-dispatch.md) | One dispatch owner per kind/variant family (Anti-IF): duplicated kind-switches become interface/map dispatch chosen once at the boundary; a single switch stays and goes exhaustive | | R12 | [`rules/R12-mutation-discipline.md`](rules/R12-mutation-discipline.md) | Mutation only through invariant-owning methods: constructors copy collections in, queries copy (or iterate) out, no query/modifier hybrids, no setters around validating constructors | @@ -106,7 +107,7 @@ Isolated contexts matter: the `lint-fixer` loop's token noise stays out of your | [`@refactoring`](skills/refactoring/SKILL.md) | BACKWARD view — routes each linter/review failure to its owning rule's Fix pattern; preparatory mode reshapes ahead of a planned change (Phase 1.5) | | [`@pre-commit-review`](skills/pre-commit-review/SKILL.md) | Orchestrates the hunter/skeptic review (Phase 4); reports, never edits | | [`@testing`](skills/testing/SKILL.md) | The composition ladder — test each behavior at the lowest rung that contains it | -| [`@documentation`](skills/documentation/SKILL.md) | Repo-brain author (R9) — behavior docs + network wiring; FEATURE mode (Phase 5) / BOOTSTRAP mode | +| [`@documentation`](skills/documentation/SKILL.md) | Repo-brain author (R9) — behavior docs + network wiring, OKF conformance + conventions self-hosting; FEATURE mode (Phase 5) / BOOTSTRAP mode | **Agents → spawned by** (payload-fed, isolated): @@ -126,7 +127,7 @@ Isolated contexts matter: the `lint-fixer` loop's token noise stays out of your | [`/go-ldd-analyze [files]`](commands/go-ldd-analyze.md) | 🔍 Tests + lint + review, combined report | ❌ No | ✅ Optional | | [`/go-ldd-review [files]`](commands/go-ldd-review.md) | 🔍 Commit-readiness check | ❌ No | ✅ Optional | | [`/go-ldd-status`](commands/go-ldd-status.md) | Show current phase + progress | N/A | — | -| [`/wire-repo-brain [path]`](commands/wire-repo-brain.md) | Wire the documentation network in one pass: upward edges → docs → index.md → CLAUDE.md (@documentation BOOTSTRAP) | ✅ Wiring only | ✅ Optional | +| [`/wire-repo-brain [path]`](commands/wire-repo-brain.md) | Wire the documentation network in one pass: frontmatter → upward edges → docs → index.md → CLAUDE.md/AGENTS.md + conventions.md + conformance script (@documentation BOOTSTRAP) | ✅ Wiring only | ✅ Optional | ## How Auto-Detection Works diff --git a/go-linter-driven-development/skills/pre-commit-review/SKILL.md b/go-linter-driven-development/skills/pre-commit-review/SKILL.md index 229f5e8..a790b3c 100644 --- a/go-linter-driven-development/skills/pre-commit-review/SKILL.md +++ b/go-linter-driven-development/skills/pre-commit-review/SKILL.md @@ -53,7 +53,7 @@ A rule with zero hits is skipped — no hunter spawned for it. | R6 | `../../rules/R6-test-only-interfaces.md` | interfaces whose only second implementer is a test double | | R7 | `../../rules/R7-test-placement.md` | internal test packages; wantErr conditionals; wrong-rung tests; sleeps | | R8 | `../../rules/R8-no-globals.md` | package-level state; `context.Background()` in library code | -| R9 | `../../rules/R9-repo-brain.md` | orphan docs; broken doc edges (both directions); WHAT-comments on exported API; unwired root | +| R9 | `../../rules/R9-repo-brain.md` | orphan docs; broken doc edges (both directions); WHAT-comments on exported API; unwired root; bundle-contract breaks (missing frontmatter, index timestamps, log.md) | | R10 | `../../rules/R10-concurrency-safety.md` | goroutines without exit paths or owners; unguarded shared-state writes; production sleeps; decorative mutexes | | R11 | `../../rules/R11-conditional-dispatch.md` | one discriminator switched in ≥2 places; type switches in domain logic; unknown-kind defaults away from the boundary; flag arguments; unearned dispatch abstractions (inverse) | | R12 | `../../rules/R12-mutation-discipline.md` | internal slices/maps returned by reference; constructors aliasing caller collections; query/modifier hybrids; setters around validating constructors; ceremony copies (inverse) | From 1a94a997a91407b981718d79f34b6910a28273ef Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 15:16:40 +0000 Subject: [PATCH 5/7] R9 + reference: full bundle contract in Q3/Q7, tags in example, inline-links rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Copilot review on the normative side: Q3 requires the exact /index.md path per doc root (bare index.md mentions no longer count; a monorepo sub-root may ride the repo-root index; a missing AGENTS.md routing reference is an advisory). Q7 states the full bundle contract — terminated frontmatter, per-class required keys, okf_version on the root index alone, and the index-line-equals-description derivation check with the warning-flag exemption. The canonical example's index gains tags, and the conventions template pins links to inline form (reference-style links are not checked). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CH1Y4ssZbAKJYwqBX1qxCF --- .../rules/R9-repo-brain.md | 32 +++++++++++++------ .../skills/documentation/reference.md | 2 ++ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/go-linter-driven-development/rules/R9-repo-brain.md b/go-linter-driven-development/rules/R9-repo-brain.md index 4d7f4db..151dd00 100644 --- a/go-linter-driven-development/rules/R9-repo-brain.md +++ b/go-linter-driven-development/rules/R9-repo-brain.md @@ -141,6 +141,7 @@ against the base delay, so an unbounded backoff cannot exist. type: index title: Repo map description: map of all repo docs +tags: [go] okf_version: "0.2" --- # Repo map @@ -474,9 +475,14 @@ one command answers all four. and *(planned)*-marked citations are exempt from resolution. 3. **Is the root unwired?** - Detection: `grep -l 'index.md' CLAUDE.md AGENTS.md 2>/dev/null`. - Violation: no hit — the map exists but is not in context at session start; - the `@/index.md` import is missing. + Detection: for each doc root, `grep -l '/index.md' CLAUDE.md AGENTS.md + 2>/dev/null` in the root's owning project directory — the exact path, never a + bare `index.md` mention. A monorepo sub-root also counts as wired when the + repo-root index links into it. + Violation: no hit anywhere — the map exists but is not in context at session + start; the `@/index.md` import is missing. + Advisory: CLAUDE.md is wired but AGENTS.md lacks the routing reference — every + tool that reads AGENTS.md instead of CLAUDE.md starts blind. 4. **Does a doc comment on an exported symbol state WHAT instead of WHY?** Detection: for each exported declaration in the diff @@ -505,13 +511,19 @@ one command answers all four. affected section, never appending history. 7. **Does any file break the bundle contract?** - Detection: for every `.md` under ``, check the first line is `---` - (e.g. `head -1` per file); for every `index.md`, check its frontmatter block - contains `type: index` and no `timestamp:` key; `grep -rn '^related:'` over - doc-root frontmatter; `find -name 'log.md'`. - Violation: a doc-root `.md` with no frontmatter block; an index missing - `type: index`; a `timestamp` key on an index; a `related:` frontmatter key - anywhere; a `log.md` anywhere in the doc root. + Detection: for every `.md` under ``: the first line is `---` and a + closing `---` follows (an unterminated block is broken); the block carries the + required keys — content docs `type`, `title`, `description`, `timestamp`; + indexes `type: index`, `title`, `description`, `tags` and never `timestamp`; + the root index also `okf_version`, and only the root (the key is the root's + alone). `grep -rn '^related:'` over doc-root frontmatter; + `find -name 'log.md'`. For every index line shaped + `- [doc](path) — text`, compare the text against the target's `description` + (⚠️-flagged lines exempt — they are recorded findings, not derivable lines). + Violation: a missing or unterminated frontmatter block; a missing required + key; a `timestamp` on an index; `okf_version` off the root index; a + `related:` key anywhere; a `log.md` anywhere in the doc root; an index line + that drifted from the `description` it derives from. Advisory branch: a doc whose `stale_after` is in the past (or `status: deprecated`) with no ⚠️ on its index line — recorded staleness the map does not show; the fix is re-deriving the line (derivation rule above). diff --git a/go-linter-driven-development/skills/documentation/reference.md b/go-linter-driven-development/skills/documentation/reference.md index ac1615b..9952919 100644 --- a/go-linter-driven-development/skills/documentation/reference.md +++ b/go-linter-driven-development/skills/documentation/reference.md @@ -553,6 +553,8 @@ Index files use `type: index` and never a `timestamp`. Optional on content docs: this repo (mark future ones *(planned)* and write them without backticks). - Link related docs inline, in the sentence that explains the relationship. Links are one-way: never add a link back to `index.md` or a parent. + Use inline links only — `[name](path.md)`; reference-style links are not + checked by the conformance gate. - An optional `## Related` section may close a doc: at most 3 entries, each with a reason ("— how sessions authenticate created users"), none duplicating an inline link. From 6845e3bc222bd6285a4fe6c16871513024b684ef Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 15:16:41 +0000 Subject: [PATCH 6/7] =?UTF-8?q?check-repo-brain:=20address=20Copilot=20rev?= =?UTF-8?q?iew=20=E2=80=94=20multi-root,=20transitive=20reachability,=20fu?= =?UTF-8?q?ll=20contract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate now discovers a doc root at the repo root AND every go.mod sub-project, resolving code edges against the citing file's own project. Q1 walks the bundle from its root index, so an unlinked sub-index no longer launders its children. Q3 matches the exact /index.md path, accepts sub-roots linked from the repo-root index, and downgrades a missing AGENTS.md reference to an advisory. Q7 enforces the full frontmatter contract (termination, per-class required keys, root-only okf_version) and the derivation rule (index line text equals the target's description, warning-flagged lines exempt). Link extraction strips optional titles; fences match ``` and ~~~ indented up to three spaces; the file:line ban strips URL spans instead of whole lines. Verified against the extended fixture matrix (regressions + 12 new cases, including a two-bundle monorepo). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CH1Y4ssZbAKJYwqBX1qxCF --- go-linter-driven-development/CHANGELOG.md | 13 +- .../scripts/check-repo-brain.sh | 391 +++++++++++++----- 2 files changed, 289 insertions(+), 115 deletions(-) diff --git a/go-linter-driven-development/CHANGELOG.md b/go-linter-driven-development/CHANGELOG.md index 753b038..f001332 100644 --- a/go-linter-driven-development/CHANGELOG.md +++ b/go-linter-driven-development/CHANGELOG.md @@ -39,10 +39,15 @@ inside and a CI gate outside. (closest file wins), pointing every AGENTS.md-reading agent at the index and conventions.md. - **New `scripts/check-repo-brain.sh`**: dependency-free conformance gate - running Q1–Q3 and Q7 (orphans, both edge directions, file:line ban, root - wiring, bundle contract); BOOTSTRAP installs it into target repos and - suggests the one-line CI wiring. Exit 0 clean/not-adopted, 1 violations, - 2 usage error; every failure message points at conventions.md. + running Q1–Q3 and Q7 over every doc root (repo root plus each go.mod + sub-project): transitive reachability from the root index, both edge + directions, the file:line ban (URL spans stripped, not whole lines), + exact-path root wiring (missing AGENTS.md routing is an advisory), the full + frontmatter contract (termination, per-class required keys, root-only + `okf_version`), and index-line derivation against each target's + `description`. BOOTSTRAP installs it into target repos and suggests the + one-line CI wiring. Exit 0 clean/not-adopted, 1 violations, 2 usage error; + every failure message points at conventions.md. ### Changed diff --git a/go-linter-driven-development/scripts/check-repo-brain.sh b/go-linter-driven-development/scripts/check-repo-brain.sh index d33442e..e7ef3e0 100755 --- a/go-linter-driven-development/scripts/check-repo-brain.sh +++ b/go-linter-driven-development/scripts/check-repo-brain.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Repo-brain conformance gate for the go-linter-driven-development plugin. # -# Runs R9's mechanical falsifying questions over the repo's doc root (an OKF +# Runs R9's mechanical falsifying questions over every doc root (each an OKF # bundle) so CI — and developers without the plugin — can hold the # documentation network's invariants. Installed into target repos by the # documentation skill's BOOTSTRAP pass (/wire-repo-brain). @@ -9,28 +9,43 @@ # Usage: bash scripts/check-repo-brain.sh [repo-root] (default: cwd) # CI: one line — bash scripts/check-repo-brain.sh # +# Doc roots are discovered at the repo root AND at every sub-project (a +# directory holding go.mod), using R9's order: .ai/ -> .ainav/ -> docs/. +# # Checks (numbering follows rules/R9-repo-brain.md's falsifying questions): -# Q1 orphans — every non-index doc has an index line reaching it -# Q2 edges — code→docs paths resolve; doc-cited exported symbols -# grep in the repo; no file:line citations (URLs exempt) -# Q3 root wiring — CLAUDE.md or AGENTS.md references the index -# Q7 bundle contract— frontmatter on every doc-root .md; `type: index` and -# no `timestamp:` on indexes; no `related:` key; no log.md +# Q1 orphans — every doc is reachable from its bundle's root index, +# transitively through sub-indexes +# Q2 edges — code→docs paths resolve; doc links resolve; doc-cited +# exported symbols grep in the repo; no file:line +# citations (URL spans stripped before the test) +# Q3 root wiring — CLAUDE.md or AGENTS.md in the root's owning project +# carries the exact /index.md path (a monorepo +# sub-root may instead be linked from the repo-root +# index); AGENTS.md missing the reference is an advisory +# Q7 bundle contract— frontmatter present and terminated with the required +# keys (content docs: type/title/description/timestamp; +# indexes: type: index/title/description/tags, never +# timestamp; root index alone adds okf_version); no +# `related:` key; no log.md; every index line's text +# matches the target's `description` (⚠️ lines exempt) # # Heuristics (documented, deliberate): +# - links are inline-markdown only (`[name](path.md)`, optional "title" +# stripped); reference-style links are not checked. # - docs→code checks only backticked tokens shaped like exported Go # identifiers (`Foo`, `Foo.Bar`) that contain a lowercase letter; other # backticks (paths, flags, ALL-CAPS initialisms, ) are skipped. # - lines carrying the ⚠️ stale flag or a *(planned)* marker are exempt from -# symbol resolution (R9 Q2's two exemptions); the file:line ban has no -# exemption beyond URLs (lines containing ://). -# - fenced code blocks are skipped for symbol resolution. +# symbol resolution and derivation (R9 Q2/Q7 exemptions); the file:line ban +# has no exemption beyond URL spans. +# - fenced code blocks (``` or ~~~, indented up to 3 spaces; toggle, not +# length-matched) are skipped for symbol resolution. # # Exit codes: 0 clean (or repo has no doc root yet — advisory no-op) # 1 one or more violations (details on stderr, summary last) # 2 usage error # -# Uses only POSIX-portable tools: find, grep, sed, head, sort, wc. No jq/python. +# Uses only POSIX-portable tools: find, grep, sed, awk, head, sort, wc. No jq/python. set -u @@ -41,20 +56,50 @@ if [[ ! -d "$REPO_ROOT" ]]; then fi cd "$REPO_ROOT" || exit 2 -DOCROOT="" -for d in .ai .ainav docs; do - [[ -d "$d" ]] && DOCROOT="$d" && break -done -if [[ -z "$DOCROOT" ]]; then - echo "check-repo-brain: no doc root (.ai/, .ainav/, docs/) — nothing to check yet; run /wire-repo-brain to bootstrap" +# ---------- doc-root discovery: repo root + every go.mod directory ---------- +discover_docroot() { # -> docroot path or '' + local base="$1" d p + for d in .ai .ainav docs; do + if [[ "$base" == "." ]]; then p="$d"; else p="$base/$d"; fi + [[ -d "$p" ]] && { printf '%s\n' "$p"; return; } + done +} + +PROJS=() +ROOTS=() +seen_roots=" " +add_root() { # + local r + r=$(discover_docroot "$1") + [[ -z "$r" ]] && return + case "$seen_roots" in *" $r "*) return ;; esac + seen_roots="$seen_roots$r " + PROJS+=("$1") + ROOTS+=("$r") +} +add_root "." +while IFS= read -r gm; do + p=$(dirname "$gm"); p="${p#./}" + [[ "$p" == "." || -z "$p" ]] && continue + add_root "$p" +done < <(find . -name go.mod -not -path '*/vendor/*' -not -path './.git/*' 2>/dev/null | sort) + +if (( ${#ROOTS[@]} == 0 )); then + echo "check-repo-brain: no doc root (.ai/, .ainav/, docs/) at the repo root or any go.mod sub-project — nothing to check yet; run /wire-repo-brain to bootstrap" exit 0 fi +ROOT_BUNDLE="" +for i in "${!PROJS[@]}"; do + [[ "${PROJS[$i]}" == "." ]] && ROOT_BUNDLE="${ROOTS[$i]}" +done violations=0 +CUR_DOCROOT="${ROOTS[0]}" fail() { - echo " $1 — see $DOCROOT/conventions.md" >&2 + echo " $1 — see $CUR_DOCROOT/conventions.md" >&2 violations=$((violations + 1)) } +note() { echo " advisory: $1"; } # canon -> physical path with .. resolved (empty if parent dir missing) canon() { @@ -64,130 +109,254 @@ canon() { (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$base") } -# resolve_link -> absolute path ('' for URLs/anchors) +# resolve_link -> absolute path ('' for URLs/anchors) resolve_link() { - local from="$1" target="$2" + local from="$1" target="$2" docroot="$3" target="${target%%#*}" + target="${target%% *}" # strip optional "title" [[ -z "$target" || "$target" == *"://"* ]] && return 0 if [[ "$target" == /* ]]; then - printf '%s\n' "$(canon "$DOCROOT/${target#/}")" # bundle-relative (OKF) + printf '%s\n' "$(canon "$docroot/${target#/}")" # bundle-relative (OKF) else printf '%s\n' "$(canon "$(dirname "$from")/$target")" fi } -# ---------- Q1: orphans — every non-index doc reachable from an index ---------- -indexed_targets="" -while IFS= read -r idx; do - while IFS= read -r raw; do - t="${raw#](}"; t="${t%)}" - [[ "$t" == *.md ]] || continue - resolved=$(resolve_link "$idx" "$t") - [[ -n "$resolved" ]] && indexed_targets="$indexed_targets$resolved -" - done < <(grep -oE '\]\([^)]+\)' "$idx" 2>/dev/null) -done < <(find "$DOCROOT" -type f -name 'index.md') - -while IFS= read -r doc; do - c=$(canon "$doc") - if ! printf '%s' "$indexed_targets" | grep -Fxq "$c"; then - fail "[Q1] $doc — orphan: no index.md lists it" - fi -done < <(find "$DOCROOT" -type f -name '*.md' ! -name 'index.md') +# frontmatter helpers ----------------------------------------------------- +fm_close_line() { # -> line number of closing --- (or '') + awk 'NR > 1 && /^---$/ { print NR; exit }' "$1" +} +fm_block() { # -> frontmatter body + sed -n "2,$(( $2 - 1 ))p" "$1" +} +desc_of() { # -> description value ('' if none) + local close + [[ "$(head -1 "$1" 2>/dev/null)" == "---" ]] || return 0 + close=$(fm_close_line "$1") + [[ -z "$close" ]] && return 0 + fm_block "$1" "$close" | grep -m1 '^description:' \ + | sed -e 's/^description:[[:space:]]*//' -e 's/[[:space:]]*$//' +} -# ---------- Q2: code→docs edges and doc→doc links resolve ---------- have_go=0 -if find . -name '*.go' -not -path './vendor/*' -not -path './.git/*' -print -quit 2>/dev/null | grep -q .; then +if find . -name '*.go' -not -path './vendor/*' -not -path '*/vendor/*' -not -path './.git/*' -print -quit 2>/dev/null | grep -q .; then have_go=1 fi +# ---------- Q2: code→docs edges (repo-wide; resolved from repo root, then the +# citing file's own sub-project) ---------- +docroot_for_file() { # -> docroot of the longest matching project dir + local f="${1#./}" best="" i + for i in "${!PROJS[@]}"; do + local p="${PROJS[$i]}" + [[ "$p" == "." ]] && { [[ -z "$best" ]] && best="${ROOTS[$i]}"; continue; } + case "$f" in "$p"/*) best="${ROOTS[$i]}" ;; esac + done + printf '%s\n' "${best:-${ROOTS[0]}}" +} + if (( have_go )); then while IFS= read -r hit; do file="${hit%%:*}"; rest="${hit#*:}"; line="${rest%%:*}"; target="${rest#*:}" - [[ -f "$target" ]] || fail "[Q2] $file:$line — code edge points at missing $target" + [[ -f "$target" ]] && continue + proj_ok=0 + for i in "${!PROJS[@]}"; do + p="${PROJS[$i]}"; [[ "$p" == "." ]] && continue + case "${file#./}" in "$p"/*) [[ -f "$p/$target" ]] && proj_ok=1 ;; esac + done + if (( ! proj_ok )); then + CUR_DOCROOT=$(docroot_for_file "$file") + fail "[Q2] $file:$line — code edge points at missing $target" + fi done < <(grep -rnoE '(docs|\.ai|\.ainav)/[A-Za-z0-9._/-]+\.md' \ --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null) fi -while IFS= read -r md; do - while IFS= read -r raw; do - t="${raw#](}"; t="${t%)}" - [[ "$t" == *.md* ]] || continue - resolved=$(resolve_link "$md" "$t") - [[ -z "$resolved" ]] && continue - [[ -f "$resolved" ]] || fail "[Q2] $md — link target does not exist: $t" - done < <(grep -oE '\]\([^)]+\)' "$md" 2>/dev/null) -done < <(find "$DOCROOT" -type f -name '*.md') - -# ---------- Q2: docs→code — backticked exported symbols must grep ---------- -if (( have_go )); then - while IFS= read -r md; do - in_fence=0 - lineno=0 - while IFS= read -r line; do - lineno=$((lineno + 1)) - case "$line" in '```'*) in_fence=$((1 - in_fence)); continue ;; esac - (( in_fence )) && continue - case "$line" in *'⚠️'*|*'*(planned)*'*) continue ;; esac - while IFS= read -r tok; do - tok="${tok#\`}"; tok="${tok%\`}" - printf '%s' "$tok" | grep -qE '^[A-Z][A-Za-z0-9]*(\.[A-Z][A-Za-z0-9]*)?$' || continue - printf '%s' "$tok" | grep -q '[a-z]' || continue - if [[ "$tok" == *.* ]]; then - method="${tok#*.}" - grep -rqE "\) ?[A-Za-z0-9_]* ?\*?[A-Za-z0-9_]*\) ${method}\(|func .*\) ${method}\(" \ - --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null && continue - grep -rqE "func ${method}\(" \ - --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null && continue - fail "[Q2] $md:$lineno — backticked \`$tok\` does not resolve (method ${method} not found)" - else - grep -rqE "(type|func) ${tok}\b" \ - --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null && continue - fail "[Q2] $md:$lineno — backticked \`$tok\` does not resolve (no type/func ${tok})" - fi - done < <(printf '%s\n' "$line" | grep -oE '`[^`]+`') - done < "$md" - done < <(find "$DOCROOT" -type f -name '*.md') -fi +# ---------- per-bundle checks ---------- +check_bundle() { # + local proj="$1" docroot="$2" + CUR_DOCROOT="$docroot" + local root_index="$docroot/index.md" + local root_index_c + root_index_c=$(canon "$root_index") -# ---------- Q2: file:line citation ban (URLs exempt) ---------- -while IFS= read -r hit; do - file="${hit%%:*}"; rest="${hit#*:}"; line="${rest%%:*}" - fail "[Q2] $file:$line — cites a file path or line number (churn-prone coordinate)" -done < <(grep -rnE '\.go(:[0-9]+)?|line [0-9]+' "$DOCROOT" --include='*.md' 2>/dev/null | grep -v '://') + # --- Q1: transitive reachability from the bundle's root index --- + local reachable="" visited="" queue=("$root_index") + while (( ${#queue[@]} > 0 )); do + local idx="${queue[0]}"; queue=("${queue[@]:1}") + local idx_c; idx_c=$(canon "$idx") + case "$visited" in *"$idx_c"$'\n'*) continue ;; esac + visited="$visited$idx_c"$'\n' + [[ -f "$idx" ]] || continue + while IFS= read -r raw; do + local t="${raw#](}"; t="${t%)}" + [[ "$t" == *.md* ]] || continue + local resolved; resolved=$(resolve_link "$idx" "$t" "$docroot") + [[ -z "$resolved" ]] && continue + reachable="$reachable$resolved"$'\n' + [[ "$(basename "$resolved")" == "index.md" ]] && queue+=("$resolved") + done < <(grep -oE '\]\([^)]+\)' "$idx" 2>/dev/null) + done + if [[ ! -f "$root_index" ]]; then + fail "[Q1] $docroot — no index.md: the bundle has no map" + fi + while IFS= read -r doc; do + local c; c=$(canon "$doc") + [[ "$c" == "$root_index_c" ]] && continue + case "$reachable" in *"$c"$'\n'*) ;; *) + fail "[Q1] $doc — orphan: not reachable from $root_index" ;; + esac + done < <(find "$docroot" -type f -name '*.md') -# ---------- Q3: root wiring ---------- -if ! grep -l 'index.md' CLAUDE.md AGENTS.md >/dev/null 2>&1; then - fail "[Q3] repo root — neither CLAUDE.md nor AGENTS.md references $DOCROOT/index.md" -fi + # --- Q2: every doc link resolves --- + while IFS= read -r md; do + while IFS= read -r raw; do + local t="${raw#](}"; t="${t%)}" + [[ "$t" == *.md* ]] || continue + local resolved; resolved=$(resolve_link "$md" "$t" "$docroot") + [[ -z "$resolved" ]] && continue + [[ -f "$resolved" ]] || fail "[Q2] $md — link target does not exist: $t" + done < <(grep -oE '\]\([^)]+\)' "$md" 2>/dev/null) + done < <(find "$docroot" -type f -name '*.md') -# ---------- Q7: bundle contract ---------- -while IFS= read -r md; do - if [[ "$(head -1 "$md" 2>/dev/null)" != "---" ]]; then - fail "[Q7] $md — no frontmatter block (first line must be ---)" - continue - fi - fm=$(sed -n '2,/^---$/p' "$md") - if printf '%s\n' "$fm" | grep -q '^related:'; then - fail "[Q7] $md — 'related:' frontmatter key (links live in the body)" + # --- Q2: docs→code — backticked exported symbols must grep --- + if (( have_go )); then + while IFS= read -r md; do + local in_fence=0 lineno=0 line + while IFS= read -r line; do + lineno=$((lineno + 1)) + if printf '%s' "$line" | grep -qE '^ {0,3}(```|~~~)'; then + in_fence=$((1 - in_fence)); continue + fi + (( in_fence )) && continue + case "$line" in *'⚠️'*|*'*(planned)*'*) continue ;; esac + local tok + while IFS= read -r tok; do + tok="${tok#\`}"; tok="${tok%\`}" + printf '%s' "$tok" | grep -qE '^[A-Z][A-Za-z0-9]*(\.[A-Z][A-Za-z0-9]*)?$' || continue + printf '%s' "$tok" | grep -q '[a-z]' || continue + if [[ "$tok" == *.* ]]; then + local method="${tok#*.}" + grep -rqE "func .*\) ${method}\(|func ${method}\(" \ + --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null && continue + fail "[Q2] $md:$lineno — backticked \`$tok\` does not resolve (method ${method} not found)" + else + grep -rqE "(type|func) ${tok}\b" \ + --include='*.go' --exclude-dir=vendor --exclude-dir=.git . 2>/dev/null && continue + fail "[Q2] $md:$lineno — backticked \`$tok\` does not resolve (no type/func ${tok})" + fi + done < <(printf '%s\n' "$line" | grep -oE '`[^`]+`') + done < "$md" + done < <(find "$docroot" -type f -name '*.md') fi - if [[ "$(basename "$md")" == "index.md" ]]; then - printf '%s\n' "$fm" | grep -q '^type: index' \ - || fail "[Q7] $md — index frontmatter missing 'type: index'" - if printf '%s\n' "$fm" | grep -q '^timestamp:'; then - fail "[Q7] $md — timestamp on an index (derived files get no authored churn)" + + # --- Q2: file:line citation ban (URL spans stripped, not whole lines) --- + while IFS= read -r hit; do + local file="${hit%%:*}"; local rest="${hit#*:}"; local line="${rest%%:*}" + local text="${rest#*:}" + local stripped + stripped=$(printf '%s' "$text" | sed -E 's#[A-Za-z][A-Za-z0-9+.-]*://[^ )>]*##g') + if printf '%s' "$stripped" | grep -qE '\.go(:[0-9]+)?|line [0-9]+'; then + fail "[Q2] $file:$line — cites a file path or line number (churn-prone coordinate)" fi + done < <(grep -rnE '\.go(:[0-9]+)?|line [0-9]+' "$docroot" --include='*.md' 2>/dev/null) + + # --- Q3: root wiring (exact path; sub-roots may ride the repo-root index) --- + local rel="$docroot" + [[ "$proj" != "." ]] && rel="${docroot#$proj/}" + local claude="CLAUDE.md" agents="AGENTS.md" + [[ "$proj" != "." ]] && { claude="$proj/CLAUDE.md"; agents="$proj/AGENTS.md"; } + local wired_claude=0 wired_agents=0 + [[ -f "$claude" ]] && grep -q "$rel/index.md" "$claude" && wired_claude=1 + [[ -f "$agents" ]] && grep -q "$rel/index.md" "$agents" && wired_agents=1 + if (( ! wired_claude && ! wired_agents )); then + local via_root=0 + if [[ "$proj" != "." && -n "$ROOT_BUNDLE" ]]; then + grep -rq "$docroot/index.md" "$ROOT_BUNDLE" --include='*.md' 2>/dev/null && via_root=1 + fi + if (( ! via_root )); then + fail "[Q3] $proj — neither $claude nor $agents references $rel/index.md" + fi + elif (( ! wired_agents )); then + note "[Q3] $agents lacks the $rel/index.md routing reference — AGENTS.md-reading tools start blind" fi -done < <(find "$DOCROOT" -type f -name '*.md') -while IFS= read -r lg; do - fail "[Q7] $lg — log.md is reserved for change history; docs describe current behavior" -done < <(find "$DOCROOT" -type f -name 'log.md') + # --- Q7: bundle contract --- + while IFS= read -r md; do + if [[ "$(head -1 "$md" 2>/dev/null)" != "---" ]]; then + fail "[Q7] $md — no frontmatter block (first line must be ---)" + continue + fi + local close; close=$(fm_close_line "$md") + if [[ -z "$close" ]]; then + fail "[Q7] $md — unterminated frontmatter (no closing ---)" + continue + fi + local fm; fm=$(fm_block "$md" "$close") + if printf '%s\n' "$fm" | grep -q '^related:'; then + fail "[Q7] $md — 'related:' frontmatter key (links live in the body)" + fi + local key + if [[ "$(basename "$md")" == "index.md" ]]; then + printf '%s\n' "$fm" | grep -q '^type: index' \ + || fail "[Q7] $md — index frontmatter missing 'type: index'" + for key in title description tags; do + printf '%s\n' "$fm" | grep -q "^${key}:" \ + || fail "[Q7] $md — index frontmatter missing '${key}:'" + done + if printf '%s\n' "$fm" | grep -q '^timestamp:'; then + fail "[Q7] $md — timestamp on an index (derived files get no authored churn)" + fi + local is_root=0 + [[ "$(canon "$md")" == "$root_index_c" ]] && is_root=1 + if (( is_root )); then + printf '%s\n' "$fm" | grep -q '^okf_version:' \ + || fail "[Q7] $md — root index missing 'okf_version:'" + elif printf '%s\n' "$fm" | grep -q '^okf_version:'; then + fail "[Q7] $md — okf_version on a sub-index (the key is the root's alone)" + fi + else + for key in type title description timestamp; do + printf '%s\n' "$fm" | grep -q "^${key}:" \ + || fail "[Q7] $md — frontmatter missing '${key}:'" + done + fi + done < <(find "$docroot" -type f -name '*.md') + + # --- Q7: derivation — index line text == target's description (⚠️ exempt) --- + while IFS= read -r idx; do + local lineno=0 line + while IFS= read -r line; do + lineno=$((lineno + 1)) + case "$line" in *'⚠️'*) continue ;; esac + printf '%s' "$line" | grep -qE '^- \[[^]]*\]\([^)]*\.md[^)]*\) — ' || continue + local t; t=$(printf '%s' "$line" | sed -E 's/^- \[[^]]*\]\(([^)]*)\).*/\1/') + local tail="${line#* — }" + tail=$(printf '%s' "$tail" | sed -e 's/[[:space:]]*$//') + local resolved; resolved=$(resolve_link "$idx" "$t" "$docroot") + [[ -n "$resolved" && -f "$resolved" ]] || continue + local desc; desc=$(desc_of "$resolved") + [[ -z "$desc" ]] && continue + if [[ "$tail" != "$desc" ]]; then + fail "[Q7] $idx:$lineno — index line drifted from $(basename "$resolved")'s description" + fi + done < "$idx" + done < <(find "$docroot" -type f -name 'index.md') + + # --- Q7: no log.md --- + while IFS= read -r lg; do + fail "[Q7] $lg — log.md is reserved for change history; docs describe current behavior" + done < <(find "$docroot" -type f -name 'log.md') +} + +for i in "${!PROJS[@]}"; do + check_bundle "${PROJS[$i]}" "${ROOTS[$i]}" +done # ---------- summary ---------- if (( violations > 0 )); then - echo "check-repo-brain: $violations violation(s) in $DOCROOT/ — rules: $DOCROOT/conventions.md" >&2 + echo "check-repo-brain: $violations violation(s) — rules: /conventions.md" >&2 exit 1 fi -echo "check-repo-brain: clean ($DOCROOT/)" +echo "check-repo-brain: clean (${ROOTS[*]})" exit 0 From 4f99e66ecf84b043fe50157b66888618c8bbf4b2 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 21:09:53 +0000 Subject: [PATCH 7/7] root wiring: CLAUDE.md embeds AGENTS.md instead of duplicating the routing block MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The routing block is authored once, in AGENTS.md (root and nested per sub-project); CLAUDE.md carries two imports — @AGENTS.md and @/index.md — so Claude sessions get the same routing prose other tools read, without a second copy that could drift. R9's root wiring and fix pattern, the reference snippets, the bootstrap step, and the command outcome all state the single-source form; the conformance gate accepts both files as wiring evidence, unchanged. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01CH1Y4ssZbAKJYwqBX1qxCF --- go-linter-driven-development/CHANGELOG.md | 3 ++- .../commands/wire-repo-brain.md | 4 ++-- .../rules/R9-repo-brain.md | 20 ++++++++++--------- .../skills/documentation/SKILL.md | 9 +++++---- .../skills/documentation/reference.md | 14 ++++++++----- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/go-linter-driven-development/CHANGELOG.md b/go-linter-driven-development/CHANGELOG.md index f001332..3cbd246 100644 --- a/go-linter-driven-development/CHANGELOG.md +++ b/go-linter-driven-development/CHANGELOG.md @@ -37,7 +37,8 @@ inside and a CI gate outside. - **AGENTS.md routing block**: upgraded from "fallback when CLAUDE.md is absent" to first-class multi-tool coverage — root and nested per sub-project (closest file wins), pointing every AGENTS.md-reading agent at the index and - conventions.md. + conventions.md. Authored once: CLAUDE.md embeds it via `@AGENTS.md` (plus the + `@/index.md` map import) instead of duplicating the routing prose. - **New `scripts/check-repo-brain.sh`**: dependency-free conformance gate running Q1–Q3 and Q7 over every doc root (repo root plus each go.mod sub-project): transitive reachability from the root index, both edge diff --git a/go-linter-driven-development/commands/wire-repo-brain.md b/go-linter-driven-development/commands/wire-repo-brain.md index 27b4b7a..fd3023a 100644 --- a/go-linter-driven-development/commands/wire-repo-brain.md +++ b/go-linter-driven-development/commands/wire-repo-brain.md @@ -23,8 +23,8 @@ authoritative; this command adds nothing to it. One pass delivers the whole chai OKF frontmatter verified-or-added (un-inferable types reported) 3. `index.md` built — frontmattered, short, grouped, one line per doc derived from each doc's `description` (directory-shaped map of maps past ~300 lines) -4. CLAUDE.md wired with the `@/index.md` import + AGENTS.md routing block - (root, and nested per sub-project in a monorepo) +4. AGENTS.md routing block authored once (root, and nested per sub-project in a + monorepo); CLAUDE.md embeds it (`@AGENTS.md`) + the `@/index.md` import 5. `/conventions.md` created/verified (listed first in the index) and the plugin's `scripts/check-repo-brain.sh` installed — the report suggests the CI one-liner diff --git a/go-linter-driven-development/rules/R9-repo-brain.md b/go-linter-driven-development/rules/R9-repo-brain.md index 151dd00..2ab3595 100644 --- a/go-linter-driven-development/rules/R9-repo-brain.md +++ b/go-linter-driven-development/rules/R9-repo-brain.md @@ -399,12 +399,13 @@ per file, path = identity, links form the graph), plus documented R9 extensions. files, so it lands in the **same commit** as the Q2-driven rewrite of code-side `See docs/...` paths — a moved doc with a stale code edge is a broken network between commits. -- **Root wiring**: CLAUDE.md embeds the map via an `@/index.md` import - (e.g. `@docs/index.md`) so it is in context at session start. AGENTS.md — which - has no import syntax — carries a short plain routing block (start at the index; - conventions in `/conventions.md`) at the repo root and, in a monorepo, - nested per sub-project (closest file wins). It serves every tool that reads - AGENTS.md instead of CLAUDE.md, not just repos without a CLAUDE.md. +- **Root wiring**: the routing block is authored ONCE, in AGENTS.md — a short + plain block (start at the index; conventions in `/conventions.md`) at + the repo root and, in a monorepo, nested per sub-project (closest file wins). + It serves every tool that reads AGENTS.md instead of CLAUDE.md. CLAUDE.md never + duplicates it: it embeds AGENTS.md via `@AGENTS.md` and adds the + `@/index.md` import (e.g. `@docs/index.md`) so the map itself is in + context at session start. - **`/conventions.md` is the self-hosting doc** (`type: guide`): the network's own maintenance rules — frontmatter templates, link rules, the Related policy, the never-list — written for a contributor without this plugin. It is @@ -429,9 +430,10 @@ per file, path = identity, links form the graph), plus documented R9 extensions. - **Rewire orphan doc**: add its one line to `index.md` *and* add a code-side edge (`See docs/.md`) from the package or type it describes — both invariants, reachability and bidirectionality, in one move. -- **Wire the root**: add or repair the `@/index.md` import in CLAUDE.md - and the AGENTS.md routing block (no import syntax there — plain lines pointing - at the index and `conventions.md`). +- **Wire the root**: add or repair the AGENTS.md routing block (plain lines + pointing at the index and `conventions.md` — authored once, there) and + CLAUDE.md's two imports: `@AGENTS.md` and `@/index.md`. CLAUDE.md + never restates the routing prose. - **Add missing frontmatter**: verify-or-add the required keys on any doc or index that lacks them; derive the index line from the `description`. A `type` that cannot be inferred from the doc's content is reported for a human call, never diff --git a/go-linter-driven-development/skills/documentation/SKILL.md b/go-linter-driven-development/skills/documentation/SKILL.md index 18f2c0c..156ac0e 100644 --- a/go-linter-driven-development/skills/documentation/SKILL.md +++ b/go-linter-driven-development/skills/documentation/SKILL.md @@ -126,10 +126,11 @@ unless an R9 Q6 check shows a doc citing the reshaped code. rule); past ~300 lines it becomes a directory-shaped map of maps, and the split lands in the same commit as the `See docs/...` path rewrite (R9 index policy; templates in reference.md). -4. **Wire the root**: add the `@/index.md` import to CLAUDE.md (create a - minimal CLAUDE.md section if none exists) and the AGENTS.md routing block — - repo root and, in a monorepo, nested per sub-project. Add or verify; snippets - in reference.md. +4. **Wire the root**: author the routing block once, in AGENTS.md — repo root + and, in a monorepo, nested per sub-project — then wire CLAUDE.md with the + `@AGENTS.md` embed plus the `@/index.md` import (create a minimal + CLAUDE.md section if none exists; never restate the routing prose there). + Add or verify; snippets in reference.md. 5. **Teach and enforce**: create-or-verify `/conventions.md` (template in reference.md) — the ONE content file bootstrap generates (network infrastructure, not a content doc) — listed FIRST in the index; copy the diff --git a/go-linter-driven-development/skills/documentation/reference.md b/go-linter-driven-development/skills/documentation/reference.md index 9952919..44bcafa 100644 --- a/go-linter-driven-development/skills/documentation/reference.md +++ b/go-linter-driven-development/skills/documentation/reference.md @@ -489,19 +489,23 @@ Each sub-index follows the one-line-per-doc form above, with sub-index frontmatt ### CLAUDE.md Wiring Snippet +CLAUDE.md never restates the routing prose — it embeds AGENTS.md (the single +authored routing block, below) and imports the map: + ```markdown ## Documentation +@AGENTS.md @docs/index.md ``` -The `@` import puts the map in context at session start. +The `@` imports put the routing block and the map in context at session start. ### AGENTS.md Routing Block -AGENTS.md has no import syntax; it carries a short routing block instead — for -every tool that reads AGENTS.md rather than CLAUDE.md, not just repos without a -CLAUDE.md. At the repo root and, in a monorepo, nested per sub-project (agents use -the closest file, so each sub-project's block names its own doc root): +The routing block is authored once, here — for every tool that reads AGENTS.md, +with CLAUDE.md embedding this file rather than duplicating it. At the repo root +and, in a monorepo, nested per sub-project (agents use the closest file, so each +sub-project's block names its own doc root): ```markdown ## Documentation