# Working on docs/design/

Loaded when a session touches these documents; the root AGENTS.md carries what fires from elsewhere (the same-PR amendment rule, prose counting claims, release-log claims, backlog triage, markdown line width, guard-test negative controls).

rules.md states intended parsing behavior and is NORMATIVE; decisions.md is the dated ADR log behind it; mechanisms.md catalogs reusable patterns and verification shapes. Each declares its own conventions at the top — read those before describing what one of them contains (axis 4).

**Starting a design: pin the negative space first.** A spec's "X is not supported" is a claim about today's parser; hold it with a case row for X written BEFORE the implementation, so a row that passes unexpectedly kills the premise before any code exists. The 2026-07-30 comma-suffix spec did this for one item (`suffix_phrase_is_comma_scoped`) and not for its central premise, which is the half that cost a full plan (#291).

**Landing a design.** The gitignored spec (docs/superpowers/specs/) is the working medium; it dies with the branch, and the docs are the record. Before a design PR merges, walk its spec (including amendments) and distill the durable residue: decisions made or reversed → decisions.md entries; proposals rejected with evidence → Declined:; vocabulary that must stay out → Excluded:; behavior the design settled → rules.md (with a deviates: marker if unshipped); reusable patterns → mechanisms.md; options weighed → a weighing entry. Then check the spec cites nothing the docs don't now carry — a spec section with no committed home when the PR merges is lost, not deferred (a 2026-08-16 sweep of eight weeks of specs recovered nine such items). The same-PR amendment rule above covers code-driven changes; this covers the design-driven ones.

**A count in a dated entry is evidence, not a live fact.** decisions.md entries are snapshots by convention, so measurements belong in them — but a reader wanting TODAY's number must not have to trust the snapshot's date. Where an entry quotes something that drifts (vocabulary sizes, corpus counts, set compositions), give the one-liner that recomputes it, and phrase the argument so it survives the digits moving — "the two shares differ by orders of magnitude" outlives "58% vs 0.65%". A count that carries no argument is better deleted than dated: "over every name in the corpus, no prefilter" says what "over all 782 names" says, and cannot go stale. Do NOT reach for a test asserting the count — that is the constant-content pattern, and it fails on every legitimate vocabulary addition. #326 is the cautionary case: it quoted a vocabulary composition, carried a date, and was stale in five days.

**Primary-source review.** When doc content is distilled from a session's work, have that session (or its transcript) review its own sections before or soon after landing — attribution flattening and inverted arguments are visible only to the source. Reviewers state which tree each measurement ran on (stale fetches produced three rounds of already-fixed findings), and landed corrections are re-verified here before committing.

Two shapes, needing different instruments. CROSS-SESSION distillation is what the rule above is written for — the #381 arc harvested nine named sessions, and for each the source transcript is an artifact independent of the reviewing session, so "review your own sections" is a two-artifact comparison. SAME-SESSION design work (#386) has no such artifact: spec and docs were written in one context, so the same instruction degenerates into re-reading the working memory that produced any error, and the rationale inverts — when the source is NOW, the source is what got it wrong. There, compare against artifacts instead, in this measured order of yield: re-derive every number with a freshly written script (2 wrong claims on #386); re-read the written spec against a checklist for FIDELITY, not just presence (1); interrogate NAMED contested rule pairs one at a time (2). Unaided prose reading found none — so a general "coherence read" by the author is the one form to distrust, because a green report from the weakest instrument manufactures confidence rather than supplying it.

**Reviewing a docs/design change: the ten axes.** Run these before merging anything that touches rules.md, decisions.md, mechanisms.md — or THIS FILE, which carries the conventions those three rest on and has the same failure modes. Axes 3, 5 and 6 are rules.md-specific (they need rule statements, examples and `interacts:`); the other seven apply to any of the four. Say which axes you ran and which you skipped as inapplicable — a checklist that asks for something meaningless teaches the reader to skim it. Each was earned by a defect that survived every other check on #386, where the review passes surfaced ten issues in total — nine of them by these axes, one by the spec self-review above — and unaided prose reading surfaced none. The parenthetical is the case that earned the axis.

Skip what the suite already enforces. `tests/v2/test_rules_doc.py` and `tests/v2/test_doc_citations.py` machine-check example lines and their `deviates:` today-values, boundaries, the one-of `implemented:`/`tracked:` rule, `implemented:` against the modules that actually cite the rule, `interacts:` and cross-doc anchor IDs, citation excerpts quoted verbatim, and legacy citation forms. Assume they pass; a finding one of them would catch is a false positive. Read those two files rather than trusting this sentence — it named five of the nine guards when written, and goes stale as guards are added.

1. **Recompute every number.** Any count, ratio or "N names" in the diff gets recomputed by a script written now, never read. (A filter described as returning one name returned three; a rule's stated blast radius listed two names its own scope excluded.)
2. **Your detector is a second, unreviewed implementation.** It must read the same vocabulary and boundaries the rule reads. (A particle-run detector that walked only the never-given half — the rule chains through ANY particle — reported 50 false movers where the true count was 1, printing cleanly both times.)
3. **Rule vs. its own examples.** (P1 said the fold takes "the particle and the one name word"; its example `de la Vega` is two particles onto one word.)
4. **Any claim about a document vs. what that document says about itself.** Two directions, both bitten. Inward: follow every `history:` and read the entry (P6's rationale asserted what decisions.md#vocabulary-collisions denies, and the rule's own guard exists because the assertion is false). Outward: when prose anywhere — this file, a code comment, another design doc — describes what one of these files contains or does, read that file's preamble or entry conventions first (an AGENTS.md bullet claimed rules.md names the pipeline stages; its preamble forbids exactly that — "Statements are implementation-free: no stage names, no function names, no regexes"). Those declarations sit at the top of files people cite without reading. Mind their scope before calling a violation: rules.md's ban binds STATEMENTS, which `_statements()` in test_doc_citations.py defines as rule-block text up to the first example, so Backgrounds and Not-in-scope may name functions and do.
5. **Does the change move another rule's OUTPUT?** A marker lands on the rule whose STATEMENT changed; a rule can move another's output without touching its statement, and the runner cannot see it. Walk `interacts:`. (family_base moved initials(); R3 had no marker.)
6. **Contested inputs: is precedence stated?** `interacts:` is advisory and pins nothing. This document states precedence in the rule's own statement (H2, M1, W3). (P6 and S2 both claimed `Berg, Jan vd`.)
7. **General clause vs. adjudicated scope.** Enumerate the vocabulary a shape-clause reaches. (P6's precedence was argued for `vd` and swept in `do` and `mc`.)
8. **Guard docstring vs. what the guard enforces**, especially when the promise spans two test modules.
9. **Prose is input to the doc parsers.** A line starting with `"` inside a rule block is an example; a comment's quoted values join the citation block above them.
10. **Re-run every capability claim.** Axis 1 recomputes numbers; this one recomputes CAN and CANNOT. A statement that the parser never or cannot produce some reading is measured by a parse run written now, never derived from a mechanism — most of all when it justifies building something. (#291 derived "a multi-word credential is unparseable" from "vocabulary is matched one token at a time" — which `given_name_titles` had been an exception to since 2026-07-19, a month before that arc, and `maiden_markers` is a second since #434 — and specified a vocabulary set, a matching unit, a predicate and a `Lexicon` field on it; `parse("John Smith, MD PhD").suffix` had been `'MD PhD'` since 1.4.0.)

Also: before reporting "N names move", report the size of the population that COULD move — a small count over a corpus blind to the shape is evidence about the corpus. For rules.md#P6: of 782 corpus names, 245 carry a comma, two of those end in a particle, and one of the two clears the words-to-spare guard. Running axis 1 over this very list caught an earlier wording of that sentence conflating the population with the movers, so the count above is the corrected one.
