docs(context): answer the review of the repo-map split

- repo-stats reads the gotcha list from context/gotchas.md (the digest in
  repo-map.md no longer measures the doc-growth signal) and reports it as
  `docs.gotchas`; the test fixture and its derived expectations follow.
- AGENTS.md and the wave agent-rules protect gotchas.md and
  file-inventory.md like the repo map they were split from.
- doc-site.md's publication table lists the two new pages.
- file-inventory.md's Context docs rows describe the three files correctly.
- the code-review skill points at gotchas.md for the long form.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Edward Langley
2026-10-09 21:42:24 -07:00
parent f0119a437d
commit cc113c732f
7 changed files with 25 additions and 25 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ them here would let the copy drift:
| Source | What it holds | | Source | What it holds |
|---|---| |---|---|
| `context/design-principles.md` | The "why" — functional-first, polymorphism over conditionals, correctness by construction, layering, §7 testing doctrine | | `context/design-principles.md` | The "why" — functional-first, polymorphism over conditionals, correctness by construction, layering, §7 testing doctrine |
| `context/repo-map.md` | The "where", plus **numbered gotchas** — read the ones your diff touches | | `context/repo-map.md` | The "where", plus a **numbered gotcha digest** (the full text is `context/gotchas.md`) — read the long form of the ones your diff touches |
| `prolog-sessions/kb.pl` | Executable knowledge: `invariant/2`, `doctrine/2`, `forbidden/2`, `decision/3`, and prior `work_done/3` | | `prolog-sessions/kb.pl` | Executable knowledge: `invariant/2`, `doctrine/2`, `forbidden/2`, `decision/3`, and prior `work_done/3` |
| `AGENTS.md` | Working rules: bug-first tests, `Option`/`Result`/`Box` at the edges, history reconciled only by merge, a new conventional commit by default (amend only an unpushed HEAD, for a small fix), and nothing that needs a force push | | `AGENTS.md` | Working rules: bug-first tests, `Option`/`Result`/`Box` at the edges, history reconciled only by merge, a new conventional commit by default (amend only an unpushed HEAD, for a small fix), and nothing that needs a force push |
| `.agents/skills/wave/agent-rules.md`, "GitHub" | The reviewer's rule: what to read before your first comment, how to answer each existing review, and what every review you post must state. Read it before reviewing a PR | | `.agents/skills/wave/agent-rules.md`, "GitHub" | The reviewer's rule: what to read before your first comment, how to answer each existing review, and what every review you post must state. Read it before reviewing a PR |
+2 -1
View File
@@ -341,7 +341,8 @@ out on purpose:
## Files you must not write ## Files you must not write
`.beads/**` (never commit `issues.jsonl`, never `git checkout` it), `.beads/**` (never commit `issues.jsonl`, never `git checkout` it),
`prolog-sessions/kb.pl`, `context/repo-map.md`: `AGENTS.md`, "Who owns what". `prolog-sessions/kb.pl`, `context/repo-map.md`, `context/gotchas.md`,
`context/file-inventory.md`: `AGENTS.md`, "Who owns what".
Also `AGENTS.md`, unless your brief grants you a named section. Report proposed Also `AGENTS.md`, unless your brief grants you a named section. Report proposed
text for any of these in your hand-back. text for any of these in your hand-back.
+2 -1
View File
@@ -160,8 +160,9 @@ badly:
| `.beads/` (incl. `issues.jsonl`) | bd owns it. See "Never hand-edit" below. | | `.beads/` (incl. `issues.jsonl`) | bd owns it. See "Never hand-edit" below. |
| `prolog-sessions/kb.pl` | Written by `scripts/kb` only. See `context/code/kb-tool.md`. | | `prolog-sessions/kb.pl` | Written by `scripts/kb` only. See `context/code/kb-tool.md`. |
| `context/repo-map.md` | Every agent wants to add a gotcha; every wave would conflict. | | `context/repo-map.md` | Every agent wants to add a gotcha; every wave would conflict. |
| `context/gotchas.md`, `context/file-inventory.md` | The long forms the map digests; same conflict, same owner. |
Wave agents **read** all three freely (`bd show`, `bd ready`, grep `kb.pl`) and Wave agents **read** all of them freely (`bd show`, `bd ready`, grep `kb.pl`) and
**write** none of them. They report proposed repo-map lines and `kb.pl` facts as **write** none of them. They report proposed repo-map lines and `kb.pl` facts as
*text* in their hand-back, and the orchestrator applies them. Repo-map lines *text* in their hand-back, and the orchestrator applies them. Repo-map lines
are batched at the end of the wave, after the review, because they conflict. are batched at the end of the wave, after the review, because they conflict.
+1 -1
View File
@@ -18,7 +18,7 @@ Published, in four sections:
| Section | Contents | Why | | Section | Contents | Why |
|---|---|---| |---|---|---|
| Start here | `docs/design-notes.md`, `context/design-principles.md`, `context/repo-map.md` | What the project is for, the doctrine, and the single best explanation of how it is built. | | Start here | `docs/design-notes.md`, `context/design-principles.md`, `context/repo-map.md`, `context/gotchas.md`, `context/file-inventory.md` | What the project is for, the doctrine, and the single best explanation of how it is built. The map is a digest; the last two are its long forms, published so a `gotcha N` citation and the per-file listing have somewhere to land. |
| Decision records | `context/adr/*.md` | An ADR is written for an outside reader by construction — it states the options that were rejected, which is the part that is worth reading and the part that is nowhere else. | | Decision records | `context/adr/*.md` | An ADR is written for an outside reader by construction — it states the options that were rejected, which is the part that is worth reading and the part that is nowhere else. |
| The arguments, by area | `context/code/*.md` | This is the reasoning that used to live in code comments and was moved out under the two-line cap (design-principles §6). It is the most valuable prose in the repository and it is addressed to exactly the reader a documentation site has. | | The arguments, by area | `context/code/*.md` | This is the reasoning that used to live in code comments and was moved out under the two-line cap (design-principles §6). It is the most valuable prose in the repository and it is addressed to exactly the reader a documentation site has. |
| Test evidence | `context/test-evidence/*.md` | One entry per test whose narrative is longer than its code. | | Test evidence | `context/test-evidence/*.md` | One entry per test whose narrative is longer than its code. |
+3 -1
View File
@@ -342,7 +342,9 @@ context/adr/001-concurrent-edits.md Concurrent edits between clients: record-le
context/cloud-agents.md Cloud-session environment: tracker sync, bd pitfalls, what is blocked context/cloud-agents.md Cloud-session environment: tracker sync, bd pitfalls, what is blocked
context/design-principles.md Architectural principles & testing doctrine context/design-principles.md Architectural principles & testing doctrine
context/plan.md Show HN launch plan context/plan.md Show HN launch plan
context/repo-map.md This file context/repo-map.md The map: crates, how-to-find-things, the gotcha digest, a file map
context/gotchas.md The long form of every numbered gotcha (the digest is in repo-map.md)
context/file-inventory.md This file: the per-file listing the repo map summarises
docs/design-notes.md Product vision & non-goals docs/design-notes.md Product vision & non-goals
``` ```
+6 -9
View File
@@ -189,12 +189,9 @@ def read_rows(path):
def gotchas(text): def gotchas(text):
"""(number, lines, chars) for each numbered item of repo-map's Gotchas section.""" """(number, lines, chars) for each numbered item of context/gotchas.md."""
out, cur, inside = [], None, False out, cur, inside = [], None, True
for line in text.split("\n"): for line in text.split("\n"):
if line.startswith("## "):
inside = line.startswith("## Gotchas")
continue
if inside and line.startswith("---"): if inside and line.startswith("---"):
inside = False inside = False
if not inside: if not inside:
@@ -214,13 +211,13 @@ def doc_sizes(repo, sha):
"context").split("\n") if p.endswith(".md")] "context").split("\n") if p.endswith(".md")]
show = lambda p: git(repo, "show", f"{sha}:{p}") show = lambda p: git(repo, "show", f"{sha}:{p}")
lines = {p: len(show(p).splitlines()) for p in sorted(files)} lines = {p: len(show(p).splitlines()) for p in sorted(files)}
gs = gotchas(show("context/repo-map.md")) if "context/repo-map.md" in files else [] gs = gotchas(show("context/gotchas.md")) if "context/gotchas.md" in files else []
longest = max(gs, key=lambda g: g[2], default=None) longest = max(gs, key=lambda g: g[2], default=None)
evidence = {p: sum(1 for l in show(p).split("\n") if re.match(r"^#{3,4} ", l)) evidence = {p: sum(1 for l in show(p).split("\n") if re.match(r"^#{3,4} ", l))
for p in sorted(files) if p.startswith("context/test-evidence/")} for p in sorted(files) if p.startswith("context/test-evidence/")}
return { return {
"context_lines": lines, "context_lines": lines,
"repo_map": {"gotchas": len(gs), "longest": longest and "gotchas": {"count": len(gs), "longest": longest and
{"number": longest[0], "lines": longest[1], "chars": longest[2]}}, {"number": longest[0], "lines": longest[1], "chars": longest[2]}},
"test_evidence": {"entries": sum(evidence.values()), "per_file": evidence}, "test_evidence": {"entries": sum(evidence.values()), "per_file": evidence},
} }
@@ -270,9 +267,9 @@ def text(r, top):
out += ["", TITLES[t]] + table(r["tables"][t]) out += ["", TITLES[t]] + table(r["tables"][t])
out += ["", "Doc sizes (lines)"] out += ["", "Doc sizes (lines)"]
out += [f" {n:>6} {path}" for path, n in r["docs"]["context_lines"].items()] out += [f" {n:>6} {path}" for path, n in r["docs"]["context_lines"].items()]
rm, te = r["docs"]["repo_map"], r["docs"]["test_evidence"] rm, te = r["docs"]["gotchas"], r["docs"]["test_evidence"]
lg = rm["longest"] lg = rm["longest"]
out.append(f" repo-map gotchas: {rm['gotchas']}" + ( out.append(f" gotchas: {rm['count']}" + (
f"; longest is {lg['number']} ({lg['lines']} lines, {lg['chars']} chars)" f"; longest is {lg['number']} ({lg['lines']} lines, {lg['chars']} chars)"
if lg else "")) if lg else ""))
out.append(f" test-evidence entries: {te['entries']}") out.append(f" test-evidence entries: {te['entries']}")
+10 -11
View File
@@ -76,10 +76,9 @@ put context/code/x.md 8 x3
mkdir -p "$repo/context/test-evidence" mkdir -p "$repo/context/test-evidence"
printf '### t_one\ntext\n#### t_two\n## not an entry\n### t_three\n' \ printf '### t_one\ntext\n#### t_two\n## not an entry\n### t_three\n' \
> "$repo/context/test-evidence/y.md" > "$repo/context/test-evidence/y.md"
cat > "$repo/context/repo-map.md" <<'EOF' printf '# Map\n' > "$repo/context/repo-map.md"
# Map cat > "$repo/context/gotchas.md" <<'EOF'
# Gotchas
## Gotchas (read before editing)
1. **One.** short. 1. **One.** short.
2. **Two.** a much longer gotcha, with plenty of words in it to be the longest one 2. **Two.** a much longer gotcha, with plenty of words in it to be the longest one
@@ -136,25 +135,25 @@ if run "$work/stats" text; then
row "docs/*.md is other docs" by-area "other docs" '["2","0","0.000"]' row "docs/*.md is other docs" by-area "other docs" '["2","0","0.000"]'
row "context/code" by-area "context/code" '["8","0","0.000"]' row "context/code" by-area "context/code" '["8","0","0.000"]'
row "context/test-evidence" by-area "context/test-evidence" '["5","0","0.000"]' row "context/test-evidence" by-area "context/test-evidence" '["5","0","0.000"]'
row "repo-map is its own area" by-area "repo-map" '["13","0","0.000"]' row "repo-map is its own area" by-area "repo-map" '["1","0","0.000"]'
row "other context" by-area "other context" '["2","0","0.000"]' row "other context" by-area "other context" '["13","0","0.000"]'
row "kb.pl" by-area "kb.pl" '["5","0","0.000"]' row "kb.pl" by-area "kb.pl" '["5","0","0.000"]'
row "scripts/CI takes .github, and a rename is a delete and an add" \ row "scripts/CI takes .github, and a rename is a delete and an add" \
by-area "scripts/CI" '["15","7","0.467"]' by-area "scripts/CI" '["15","7","0.467"]'
row "web and harness, and its deletion ratio" by-area "web and harness" '["14","9","0.643"]' row "web and harness, and its deletion ratio" by-area "web and harness" '["14","9","0.643"]'
row "a lock file, .beads/ and site/ are generated" by-area "generated" '["14","0","0.000"]' row "a lock file, .beads/ and site/ are generated" by-area "generated" '["14","0","0.000"]'
row "the pre-window README counts nowhere" by-area "other" '["4","0","0.000"]' row "the pre-window README counts nowhere" by-area "other" '["4","0","0.000"]'
row "prose is the context areas, other docs and agent docs" by-kind prose '["43","6","0.140"]' row "prose is the context areas, other docs and agent docs" by-kind prose '["42","6","0.143"]'
row "code is rust, scripts/CI and web" by-kind code '["82","45","0.549"]' row "code is rust, scripts/CI and web" by-kind code '["82","45","0.549"]'
row "prose to code is prose over code" by-kind ProseToCode '["0.524","0.133",""]' row "prose to code is prose over code" by-kind ProseToCode '["0.512","0.133",""]'
is "weeks are columns, first-seen first" '.tables["area-by-week"][0][1:]' '["2026-W24","2026-W23"]' is "weeks are columns, first-seen first" '.tables["area-by-week"][0][1:]' '["2026-W24","2026-W23"]'
row "an area is split by ISO week" area-by-week "web and harness" '["3","11"]' row "an area is split by ISO week" area-by-week "web and harness" '["3","11"]'
is "every day's churn.csv total equals code-maat's abs-churn" \ is "every day's churn.csv total equals code-maat's abs-churn" \
'[.tables["by-day"][1:][]|.[1]==.[2]]|all' 'true' '[.tables["by-day"][1:][]|.[1]==.[2]]|all' 'true'
row "a day's total is every file changed that day" by-day 2026-06-04 '["72","72"]' row "a day's total is every file changed that day" by-day 2026-06-04 '["71","71"]'
is "gotchas end with their section" '.docs.repo_map.gotchas' '3' is "gotchas end with their section" '.docs.gotchas.count' '3'
is "the longest gotcha is measured over its lines" \ is "the longest gotcha is measured over its lines" \
'.docs.repo_map.longest|[.number,.lines]' '[2,2]' '.docs.gotchas.longest|[.number,.lines]' '[2,2]'
is "test-evidence entries are ### and #### headings" '.docs.test_evidence.entries' '3' is "test-evidence entries are ### and #### headings" '.docs.test_evidence.entries' '3'
is "coupling finds the 15 pairs of six files, and web/app.js with scripts/foo.sh" \ is "coupling finds the 15 pairs of six files, and web/app.js with scripts/foo.sh" \
'.coupling|length' '16' '.coupling|length' '16'