Open questions
The complement to discoveries.md: discoveries record what has been found; this records what is open. It is the SSOT for the library’s authored architectural questions — the deliberately-deferred decisions, documented honestly rather than re-litigated each session. (CLAUDE.md points here and stays agent-operational; it no longer carries these.)
When a session has a strong case for resolving one, the move is: (a) write the case down, (b) execute the resulting refactor as a single discrete pass that updates every reference across the library, (c) update this file.
Authored architectural questions
Replace the prose Lineage with a ledger and a decision log
Status: landed 2026-08-27. Agreed the same day with the bar tightened by the author — change history is for huge directional changes; everything else goes into the detailed commits — piloted on Login, then applied to all fifty-two patterns: Ledger grammar and --census in the linter (check R, gating, with a missing Ledger a finding), Ledgers for every pattern (the fifteen partially resolved ones from their gate verdicts and routed rosters, the thirty-seven grounded ones with open: none), Decisions where a directional turn exists (twenty-one patterns), spec-format.md / pressure-testing.md / contributing.md / AGENTS.md / execution-contract.md rewritten to the new sections, and the fifty-two Lineages deleted in one closing commit. Kept here as the record of the case.
The case. The corpus carries 2.1M characters of per-pattern history — 33% of everything, 49% of the size of the specifications themselves, and on two patterns more than the specification. Measured by shape, 37% of it is finding bullets carrying prescribed fixes, which a future round genuinely needs; 63% is round-by-round narrative, which decays fast and is duplicated by the commit messages that landed the same rounds. That history is also, silently, the corpus’s health ledger — the place you count open findings from — and prose is the wrong medium for a ledger. On 2026-08-27 that broke three ways in one day: the roadmap’s aggregate was stale (it said seven patterns; there were fifteen), five patterns’ Status lines contradicted their own Lineage (two of them created by the rounds that closed the findings), and a census by machine returned 15 or 112 depending on the regex, because closures are recorded inline in some patterns and in later entries in others and the word open appears inside closed findings’ text. It took a full read of fifteen patterns to produce a number, and the number was 46.
The history is doing three jobs in one register, and only one of them justifies the cost.
| Job | Needed? | Right medium | Today |
|---|---|---|---|
| Ledger — what is open now, per pattern | yes, and it must be countable | a structured block with one place to update | prose, scattered, contradictory |
| Decisions — why a load-bearing choice was made, what was ruled out | yes — this is the history that checks decisions | a short dated list, one screen per pattern | interleaved with narrative, unfindable |
| Narrative — what happened in round N | no | git log, which already has it, with the commit message | 63% of the volume, in the spec file |
The proposal. ## Status and the prose Lineage are replaced by two sections, and the narrative dies.
1. ## Ledger — the countable SSOT for a pattern’s health. A fixed line grammar the linter parses, not free prose:
## Ledger
status: partially resolved
formal: verified — login.tla + 1 twin, 2026-06-03
last gate: 2026-08-26 — fresh reader — 5 foundational, all since closed
open:
- 2026-08-27-a · foundational · Action wiring, cascade step 4 · `failures` is consumed by the completion event and by check 3 and initialized nowhere → initialize at step 4; increment on 5b's failure arm
- 2026-08-27-b · foundational · Generation acceptance check 3 · joins per-session events on `initiation_event_id`, which none of the three per-session cascade events carries → add it to their payloads
Five fields per line — id, class, where, defect, fix — with → separating defect from fix. Ids are date-based, not gate-based, so a finding does not need to know which Final Critique it came from. When a finding closes, its line is deleted. If the closure carried a decision, the decision goes in section 2; the fact that it was found and closed is in git. Closed findings do not accumulate, which is the single largest volume win and the thing that makes the block stay one screen. The linter derives what it can and refuses contradictions by construction: status: grounded requires zero open foundational; every open line has five fields; and the corpus census becomes python3 lint.py --census instead of a day’s reading.
2. ## Decisions — directional changes only. Not every load-bearing choice: the turns a future reader must know the pattern took — a change to what it promises, to how it attributes, to what it composes. Three fields, no narrative:
- **2026-08-26 — Every Login event is composition-attributed.** *Chose:* attest under `application_actor_ref`, human parties in `data`. *Over:* attesting under the principal's own credential. *Because:* Login's callers are end principals whose material is the thing under test, not registry actors — and a failed login has no valid credential to attest with.
The bar is directional, and it is high on purpose. An implementation choice — a sandwich read, a completion event, a bound stated — is not a direction; it is in the commit. A classification detail is not a direction. A gate’s roster is not a direction. The test is whether the pattern would be a different pattern had it gone the other way. A mature pattern has two or three entries, not nine.
Which makes the commit message load-bearing, and that should be said out loud. Every rationale that used to sit in the Lineage now sits in the commit that made the change, and git log -- <file> is the per-pattern index of it. The corpus’s rule stays — never commit; propose the message in chat — but the message is no longer a courtesy summary. A commit that changes a spec carries the why in its message; the spec carries only the what, and for directional changes, the why. A round whose commit message would not let a future reader reconstruct its reasoning has not finished.
3. Everything else is deleted, with a one-line pointer: History before 2026-08-28 is in git log -- <file>. Round narratives that carry a methodology lesson already go to discoveries.md; that practice continues. Gate provenance — who cleared what, when — collapses to the last gate: line, since a pattern’s health is its current state, not its record of past states.
Pilot: Login, landed. Its 51K characters of history reduce to a ledger of two open lines and two decisions. A first draft had nine; against the directional bar seven of them were implementation choices whose reasoning belongs in their commits — the formal model’s presence (provenance, now the Ledger’s formal: line), the sandwich read, the union-defined cascade set, derived expiry (the constituent’s choice, not this pattern’s), the truth-bearing window classification, the completion event, the tier-0 bound. The two that survive change what the pattern is:
- 2026-08-26 — Every Login event is composition-attributed. Chose: attest under
application_actor_ref, human parties indata. Over: attesting under the principal’s own credential. Because: Login’s callers are end principals whose material is the thing under test, not registry actors — and a failed login has no valid credential to attest with. - 2026-08-26 — Invariant 2 promises less than it did. Chose: cascade completeness only for verifies that committed after the revocation, with the in-flight window named and a second cascade prescribed. Over: single-cascade sufficiency. Because: a login whose verify committed before the revocation can land its session after the snapshot, and no single cascade can see it.
Two decisions, under 1K characters. The other 50K were narrative or implementation rationale, and every one of those rounds has a commit message saying the same thing — which is where it now lives.
What the pass touches beyond the patterns. spec-format.md and pressure-testing.md §Where the journey gets recorded mandate the current Lineage and change with it; the linter’s Status-grammar checks (G, H, I) are replaced by the Ledger grammar; roadmap.md’s hand-maintained tallies are replaced by the census command’s output. The operating rule from 2026-08-27 — a round that closes a finding must close it in every place the pattern claims it is open — becomes unnecessary, because there is one place.
Sequencing, so nothing is lost. Pilot on Login and one grounded pattern to prove both shapes. Land the linter grammar and --census. Then a mechanical pass: the fifteen partially resolved patterns’ ledgers are already written, in the census; the thirty-seven grounded patterns’ ledgers are trivial (no open lines); the decision logs are the judgment work, roughly one pattern per short round or batched with review. Delete the Lineages last, in one commit, so git log has a single line to point at.
Taxonomy axes
Current pattern categories (productivity, temporal, resource-lifecycle, compliance, messaging, workflow, healthcare) mix conceptual axes — healthcare is domain-scoped while the others are concept-scoped; compliance mixes pure-compliance-infrastructure atoms with atoms that happen to be regulated. The right axial split will be forced by content as the catalog grows past the size where preemptive cuts are reasonable; restructuring earlier would relocate the same confusion under different labels. The workflow/ sub-question — whether one atom justifies the category — is resolved (2026-06-04): the category now holds two grounded atoms, Approval Step (the fixed-state pole — states fixed by the atom) and Workflow / State Machine (atom #9, the general-declared pole — states declared by the deployment), so it stands on present catalog evidence rather than on a planned atom. The broader axial split question across all categories is independently open. See the Open question on the current axes paragraph in the Taxonomy section of the-spec-layer.md, and roadmap.md §”Open taxonomy question” for the parallel ROADMAP framing.
Status (2026-06-08): RESOLVED — executed. The usage-derived taxonomy (atoms/TAXONOMY.md) landed: atoms are stored flat (atoms/<name>.md), cross-cutting classification (regulated / security / standards) is derived from the composition graph as overlays, and domain is the one intrinsic, EOS-gated axis. This dissolves the mixed-axes problem — there is no longer one folder per atom to mis-assign. See the 2026-06-08 entry in discoveries.md.
Regulation as folder vs. attribute
The former compliance/ folder conflated two things: atoms whose primary domain is compliance infrastructure (Actor Identity, Retention Window, Tamper Evidence — these have no meaningful non-regulated use case), and atoms that belong to other domains but carry a heavy regulated surface (Soft Delete is resource-lifecycle by nature; Medication Order and Clinical Observation are healthcare; Legal Hold could reasonably be resource-lifecycle or temporal). As the library grows, “regulation” may belong as a frontmatter attribute — regulated: true, or a standards: [GDPR, HIPAA, FRCP] field — rather than a folder that atoms are placed in by consequence rather than by domain. The practical implication: the compliance/ folder may eventually narrow to pure compliance infrastructure primitives, while regulated atoms in other domains carry their regulatory surface as metadata. This restructuring will cause significant tree churn and cross-reference updates across every regulated atom’s Composition notes; it should be executed as a single discrete refactor pass once the content forces the decision, not incrementally. Deferred until the catalog is large enough to make the right cut obvious.
Status (2026-06-08): RESOLVED — executed. regulated / standards / security are now derived from the composition graph (never a folder, never a stored attribute); the move landed flat storage plus generated browse-by-overlay views. The live sub-questions carry forward in atoms/TAXONOMY.md: uncomposed atoms, and the regulated-overlay stewardship obligation that is intrinsic to compliance-infrastructure atoms even when derived classification says otherwise (e.g. selective-disclosure, which the generated view should lint for the overlay sections separately from classification).
Substrate-composing overlay and reverse-leverage view (added 2026-06-12)
A composition that names another composition in its ## Composes (a “super” / substrate composition) is a structural fact derivable straight from the composition graph — does Composes reference ≥1 composition? — exactly like the regulated / security overlays the usage-derived taxonomy already derives. Today it is legible only by reading each spec (a 2026-06-12 hand count: 14 of 24 grounded compositions are substrate-composing; 13 build on Audit Trail, Data Subject Rights Fulfillment is two layers deep, Privileged Access Provisioning composes two compositions at once). It should be a derived overlay in tools/taxonomy/, never a hand-applied tag — a hand-maintained one would drift like a count snapshot. Inverting the same edge yields the reverse-leverage view — “Audit Trail: composed by 13” — which surfaces the reuse leverage a flat pattern count hides (and guards the count against being misread as independent-concept breadth). Open — to generate; trivial once the taxonomy generator gains a composition→composition edge pass.
Status (2026-07-06): RESOLVED — executed. tools/taxonomy/generate_graph.py parses the composition→composition edges (Composes-section constituent list items only — prose mentions and deliberate non-compositions do not count as edges) and emits the reverse-leverage view as the generated graph.md page (atom fan-in and substrate fan-in tables, the substrate spine as a Mermaid diagram, the full graph) plus per-pattern card data (_data/patterns.json) rendered onto every pattern page at Jekyll build time — the canonical files untouched. The derived numbers confirmed the 2026-06-12 hand count exactly: Audit Trail substrate-of-13, the two-layer chain, the dual-substrate case. If a second consumer of the substrate edge appears, the parse graduates from generate_graph.py into reverse_index.py per the note in its header.
Guided-process state → phase → action mapping
For the human-facing guided process (the double-diamond entry model — create/select/extract at the vision / composition / atom levels, seeded rough, refined by diverge→converge), the irreducible thing to define is the mapping from an artifact’s state — MUSE completeness state, pressure-testing round, conformance status, ## Composes graph position — to its diamond phase and the prescribed next diverge/converge action. The state substrate already exists; the mapping does not. It is the prerequisite the guided tooling would project from (the tooling derives “where am I / what’s next” from artifact state rather than asking the human to declare it). Open — to define.
Readability ↔ completeness — dialing in the accessibility balance
The library’s load-bearing premise is the bridge principle (the-spec-layer.md §Bridges): the canonical text must be readable by every audience at once, humans included. In practice the corpus still skews toward terse, expert shorthand in places — formal coverage: Inv 4 pending rather than formal coverage of Invariant 4 pending; colon-packed status markers and abbreviations a fluent author parses instantly but that cost every other reader a beat on every access. Self-explanatory phrasing is strictly better there: same meaning, no glossary, no parse, for a few more words the library already commits to paying (verbosity is the architecture of the bridge). The accessibility machinery exists — three reading tiers, the Tier-1-clean Summary, the no-undefined-acronym Pass 1 check — but it governs new prose; it has never been driven back through the accumulated terse surface.
Why this is open, not a TODO. Readability sweeps have been attempted before and failed to stick — a one-pass “make it readable” edit fights the same completeness pressure that produced the terseness, and the next dense pattern reintroduces it. This is one of Daniel Jackson’s standing complaints about software specification: rigor and accessibility get treated as a trade rather than a both. The library’s defensible sequencing has been complete before accessible — a spec cannot be made readable before it is correct and complete, so completeness was bought first, deliberately, accepting terse surface as interim debt. That sequencing was right; the balance is now dial-able.
The open question: what readability discipline actually holds — a standing authoring rule (self-explanatory over shorthand, enforced like the acronym check), a derived linter (flag colon-packed / abbreviated status markers and undefined shorthand), or a readability arm on the scheduled rescan — rather than a one-off sweep that decays. Until that is decided: prefer the fuller self-explanatory form when authoring or touching a line (the 2026-06-10 C12/C3 status correction adopted grounded — formal coverage of Invariant 4 pending on exactly this basis), and do not relaunch a corpus-wide readability sweep on the old failed model. Open — to define the discipline that sticks.
Status-line grammar and the roadmap status mirror (added 2026-06-11)
Two coupled problems surfaced by the 2026-06-11 root-doc scan. First, there is no canonical status grammar. The corpus carries at least six shapes: `grounded` with a parenthetical, a Tier-1 gloss inside the backticks (Soft Delete), grounded on Final Critique N with and without an em-dash date, qualifier statuses (grounded — formal coverage of Invariant 4 pending), and — in two compositions (Session-Gated Authorization, External Onboarding) — Status carried as a top-of-file **Status:** line instead of the ## Status section spec-format.md requires, a canonical-shape deviation by that document’s own rule. Second, roadmap.md mirrors ~50 status cells by hand with no defined semantics and no mechanical check — the 2026-06-11 scan found six rows stale (qualifiers missing on C3/C12; markers and dates behind on C4, C13, C14, C16, Attributed Permissions Admin) plus a duplicated row and a constituent-list mismatch, all while the linter scanned clean the same day. The linter today is pre-alpha, proof-of-concept grade: six high-precision checks, wired to no gate, and the status-mirror class is unwritable against six grammars.
The dependency chain to close it: (1) pin one status grammar in spec-format.md (one marker form, one date semantic — the latest complete round, per pressure-testing.md §Status line format — and ## Status as the only placement); (2) migrate the corpus’s Status lines to it in one discrete pass; (3) add the status-mirror linter check (pattern Status line ⇔ roadmap row) plus a table-duplicate check — both ~20 lines once the grammar exists; (4) wire the linter to an enforcement point — done 2026-06-11: the lint gate runs in continuous integration (.github/workflows/lint.yml), so the checks from step 3 enforce on arrival. Steps 1–2 are a real review pass with touch implications; until they land, roadmap rows are corrected opportunistically (the 2026-06-11 note in the roadmap marks the verified-vs-unverified boundary). Sub-item (added 2026-06-11, guided-tool design review F3): the token taxonomy is as unpinned as the grammar was — unresolved vs partially resolved has no stated boundary anywhere (pressure-testing.md §Status line format points at “the taxonomy above,” which defines none); pin the boundary alongside step (1). The guided tool meanwhile carries its own local mapping (unacknowledged findings → unresolved; resolved-or-carried → partially resolved). Open — steps 1–3 remain; sequencing decided, execution unscheduled.
Canonical vs. internal-staging legibility (added 2026-06-14)
The non-canonical material — the working-ideas/ staging docs (call-prep, the falsifiability metric, the prior-art/positioning note, the outbound-contract-ports registry, the strange-pattern forecast) — is distinguished from canon only de facto: it lives under working-ideas/, most files carry a > Status: internal staging, not canonical blockquote, and (unlike the canonical docs) they lack Jekyll front-matter and a roadmap row, so they are already nav-excluded from the published site. But the canonical/staging tier is derivable, not uniformly labeled — a reader, or an AI agent, landing mid-document cannot always tell at a glance whether a sharply-written staging sketch is canon. That is a drift/misread risk: a staging argument cited as settled canon is exactly the failure the no-snapshot discipline guards against elsewhere, and it grows as the working-ideas set does.
Why this is open, not a TODO. The de-facto convention mostly works, and the right fix is a labeling/rule hardening, not a relocation — moving files would churn the cross-references that now point into working-ideas/ for no gain. So the decision is which hardening: (1) a working-ideas/README that tags the whole folder at once; (2) a required, uniformly-worded staging blockquote on every non-canonical doc; (3) machine-readable front-matter (nav_exclude: true, or a status: staging field) so tooling and the published site filter automatically; (4) the rule written into AGENTS.md / contributing.md — non-canonical lives under working-ideas/ and carries the tag; canonical lives at root / atoms / compositions with front-matter and a roadmap row. Cheapest high-leverage combo is (1)+(4); (3) is the upgrade if an agent ever mis-cites. (Surfaced 2026-06-14 while staging the boundary/positioning notes; logged rather than acted on so it is captured, not lost.)
Trigger to resolve: when the working-ideas set outgrows easy familiarity, or the first time a staging doc is cited as canon (human or AI) — whichever comes first. Open — to decide the hardening, then execute as one pass that tags every non-canonical doc and writes the rule down.
Generation trust — the check surface is the effective contract (added 2026-07-06)
The projector’s determinism bar is contract fulfillment, not byte identity: two generations of the same spec are “the same program” when they agree under the same evaluators, exactly as the seven hand-authored renders do (tools/conformance/, multi-render agreement). This is already doctrine — be as creative as you like with the implementation, as long as it satisfies every contract (the-spec-layer.md); generated code is deliberately boring, and prettiness is explicitly a non-goal. The definition is honest and measurable, and it has a consequence worth stating plainly: it converts generation correctness into a coverage question. A generator driven by the validator as fitness function (regen.mjs) satisfies exactly the checks it can see — Goodhart’s law (a measure that becomes a target stops measuring) operating as designed, not a defect. So any load-bearing invariant without a mechanical check is not part of the effective contract for generation purposes, and generation is trustworthy only where the check surface is complete.
The known hole is concurrency. Records-alone conformance cannot observe under-serialization — a generated implementation that races can still produce clean-looking record sets on most runs, so execution-contract.md’s conformance condition 5 (invariants hold under interleaved callers per the implementation’s serialization guarantees) is the one condition the current validator class structurally cannot clear. The closing move exists once, not generally: the Mongo render’s prove-serialization.mjs measures Event Log’s serialize clause under genuinely concurrent operations (no fork, no gap, chain re-verifies). Nothing generalizes it — no per-atom serialization probe class, no multi-write-atomicity probes for compositions whose load-bearing invariant is an all-or-nothing commit.
The proposed shape — a generation coverage matrix. The formal-layer coverage cross-check (pressure-testing.md §The coverage cross-check), applied one level down: per pattern, one row per load-bearing invariant, with a verdict naming which check class catches its violation — records-alone conformance query, compiled property test (execution-contract.md §Testing model), concurrency probe, formal model at bounded scope, or by-construction — and any unmapped load-bearing invariant a GAP that blocks trusting generation for that pattern. The same matrix is also where the Contract’s strongest verb gets honest: “the compiler must prove T preserves sequence-safety invariants” is discharged today at bounded scope by hand-lowered models; the matrix records, per invariant, whether the real bar is proved, verified at bounded scope, or measured on records — stated rather than implied.
Why this is open, not a TODO. The matrix lands with the projector (roadmap methodology debts #7/#8 territory), and building it before any generator exists risks the methodology-bloat failure risks.md names — machinery ahead of need. But generating code against the current surface without it invites the exact false-confidence failure the methodology exists to prevent: output that is green against the visible checks and wrong against an uncovered invariant. Open sub-questions: which document owns the matrix (the Contract’s testing model, pressure-testing, or tools/conformance/); whether it is derived rather than hand-authored (it must be derivable, or it becomes the next drifted registry); and whether the concurrency probe class extends the harness or the conformance validator.
Trigger to resolve: the first projector deliverable that emits runnable code for a pattern — that pattern’s generation coverage matrix precedes trusting the output; or the first observed instance of a generated artifact passing its checks while violating an uncovered invariant, whichever comes first.
GRACE lang — who admits a form, and what obeying one means (added 2026-09-11)
The language grows by Principle 2: a form is admitted only when its concept recurs across specifications and the concept is contested (GRACE-lang.md §1). The rewrite’s watch list — the pressures a rewriter flags at the site and the corpus counts — lives beside the candidate forms in §18, because that is where a rewriter looks. What §18 cannot hold is the three questions the council’s self-hosting read left open, which are about the process and the meaning rather than the forms:
Who arbitrates admission — resolved 2026-09-11. The maintainer, the human in charge of the corpus, decides every admission under Principle 2 (GRACE-lang.md Principle 8); the council advises and cannot admit, and is constituted in governance.md §The language council. Whether a flag count over a threshold ever becomes a checkable trigger for the maintainer’s decision stays with the watch list.
What obey means. §2 declares it in one line — every run satisfies the obligation under the condition, a violation is a conformance failure decided from records — and an inequality rule obliges whoever binds the terms. That is enough for the rewrite and not for a satisfaction relation: what a violated WITHIN is (nonconformance, or an obligation to compensate), and whether trace conformance, monitored enforcement or design review is the standard. On the watch list as satisfaction; the relation itself is v0.3x work.
Whether the parser exists. It does not. A forty-line scan has run over the grammar and Recoverable Invocation; the parser reads the corpus after the rewrite (the migration plan). The council’s findings 1 through 4 on v0.29 are the output the parser’s first run should reproduce, and are its first test corpus.
Trigger to resolve the two that remain: the first rewritten spec whose acceptance checks need a violation class obey does not name; and the first council finding a checker could have made, which is the parser’s first slice.
GRACE lang — the classes the migration is accumulating (added 2026-09-12)
The rolling fixes cure a finding one document after it is raised; what they cannot cure is a class — a shape that recurs across specs and belongs to the grammar rather than to any one of them. Each of these carries the recurrence count Principle 2’s first half asks for; each is waiting on the second half, a finding against the class itself, and on the maintainer’s decision (Principle 8). They are counted in GRACE-lang.md §18’s watch list and listed here because §18 is a counting ledger, not a docket.
| Class | Count | What it is | What would decide it |
|---|---|---|---|
| The term entry grammar | 11 specs | Kind, Member of, Field of, Parameter of, Role, Projection: (named Projects: until council read 101), Wire: pinned — the whole type lattice, rendered as reader sugar on the nothing surface, drifting per document. Kind cannot tell a stored field from a derived projection from a pinned wire literal, and the Terms-to-term-entry relation is unowned in both directions — Permissions declares twelve terms against eighteen term entries, five terms with no term entry and ten term entries with no term (council read 15). A benign collision now sits beside the contested one and belongs in the same ruling: State Machine declares transition in three senses — the execution contract’s evaluation of one call, a declared transition (an edge in the declaration), and the transition_id naming a history entry — and the collision is harmless because all three are declared and every rule names which. The proof check collision between Actor Identity and Tamper Evidence is the same shape and is not harmless, because the two are declared in different specs with different value sets and nothing makes a citing rule say which. The difference is not the name; it is whether one registry owns every sense (council read 31). | A declared form, or a ruling that the term entries stay prose and the drift is acceptable. The wire layer the watch list counts is a subset of this question (council read 9 – council read 12). |
| The total read | 5 specs | A query that refuses nothing, because a malformed argument has a correct answer rather than an error — Duplicate Prevention’s check, Lease’s remaining, Actor Identity’s and Tamper Evidence’s verify, Subscription’s two queries, which is the first spec to state the principle. The count is wrong and has been since it was written. A mechanical pass over the migrated corpus — an action whose signature block carries no rejected(...) arm — returns 21 actions across 17 specs, where this row says five and GRACE-lang.md §18 says counted at six and then Five instances in the same sentence. Consent’s Operation 35 ([Check] MUST NOT refuse a call) predates Invitation’s Operation 44, which §18 credits as the first to state it as a rule, by a day; and Permissions and Session each state it as a rule too and appear in neither list. The mechanical number is an upper bound rather than the class: it catches writes that cannot refuse (Duplicate Prevention’s record, Recoverable Invocation’s yield) alongside the queries the class is about, and separating them needs a write/read discriminator no instrument carries. Fifth hand-written census in this window to be wrong on inspection, and the cure is the one the family counts, the ≥ census and the category set all took: a tool decides it (council read 48). | Whether total is a property the grammar can carry on an action, or stays a per-spec WHY — and, before either, a W-total-read detector that draws the class’s boundary at a non-writing action with no rejection arm, so the number stops depending on who is counting (council read 12, council read 48). |
| The declined constituent assignment | 2 in one composition, no declared form | A constituent’s composition note says a composing pattern MUST own X, and the composing pattern does not want X. Shared Todo declines two at once: Assignment’s Composition note 4 (whether a completed task’s assignment is recalled — both answers defensible, so the deployment picks) and Permissions’s Composition note 3 (the caller-to-subject binding — actor_ref passes through unauthenticated). Both are MUST own obligations that end the migration discharged by nobody. The composition writes the refusal down — Non-goal 4 and Non-goal 12 refuse, Composition note 2 and Composition note 3 re-assign to the deployment by name — because a composition that neither owns an assignment nor names a receiver leaves an obligation falling between two layers with no rule anywhere holding it, and a reader of either document alone would conclude it was held. This is council read 53’s seam ruling from the other side: that ruling says a composition cites what it inherits and owns what it adds, and says nothing about what a composition does with an obligation it is assigned and declines (council read 55). | Whether declining is admissible at all, or whether MUST own means the composing pattern must decide rather than defer — and if declining is admissible, what form records it, since state a non-goal and restate the assignment one layer down is a convention invented here and carried by nothing. An instrument is conceivable and would be the first to read across the seam in this direction: for every constituent composition note of the form a composing pattern MUST own X, find the composing spec’s discharge or its declared decline. |
| Provenance answered, extraction skipped | 2 compositions carry no classification | execution-contract.md §Composition state states the extraction rule unconditionally — composition state that carries truth not reconstructible from constituent stores is a not-yet-extracted atom, declared as extraction-pending with the proposed atom named, because an unflagged truth-bearing composition store is a conformance finding. Three compositions carried no classification of any kind: Authenticated Actor, Compensable Workflow and Resolve a Person’s Data Rights, and all three describe owning emergent state. Two remain, and the one that left is the row’s own evidence: Authenticated Actor classified at migration, on a declaration council read 59 wrote against the prose spec before a rule existed. Every migrated composition now carries a classification in some phrasing, and no unmigrated one does — so the classification arrives with the rewrite unless somebody declares it first, which is the case for the mechanical presence check below rather than against it. The cause is not oversight but a second question that reads like an answer to the first. Authenticated Actor answers §Capability provenance — where did this capability legitimately come from — at length and correctly: the bijective binding is a composition-introduced surface, one of the four admitted sources. That is a real answer to a real question and it is not this one. Provenance asks whether a composition may hold a capability; extraction asks whether the truth it holds is an unextracted atom. They are orthogonal, and a spec that answers the first convincingly reads as having discharged the second — Idempotent Reservation could equally have claimed provenance and instead declared the flag, and nothing in either document says which a composition owes (council read 59). | Whether the contract states that provenance and extraction are both owed, and whether presence of a classification becomes mechanical — a composition whose state section describes an owned store and carries no classification line is a lint.py check, since presence is checkable even where correctness is not. The harder half is the diagnosis, and Login’s migration moved it. Authenticated Actor’s attest_log names its atom as a question — an Attempt Log if the divergence from Event Log is real, and nothing at all if it is Event Log wired with a composition-supplied entry schema — against Notification Fanout, which routes the identical concept out to Event Log and has from the start. Login is the third and it answers outright: login_event_log is extraction-pending against Event Log, a second Event Log instance for the login-attempt journal, at which point the classification question dissolves. Two of three say Event Log, and the split is no longer even: the composition-supplied entry schema is the likely landing, and Authenticated Actor’s proposal should ride that ruling rather than mint a third answer. What would overturn it is a stated divergence — one obligation an attempt journal owes that Event Log’s append surface cannot carry — and no spec has named one (council read 59, council read 62). |
| The multitasking keyword | 4 core tokens, 4 rows that did not know they were related — and one census failure of its own | The parent class of four docket rows, named by the maintainer after the EXISTS census and confirmed by running the audit it implies. EXISTS carried presence, store-state and membership across 224 rule-surface sites, and was split into EXISTS, EQUALS blank and IS IN at council read 99 — the first of the four to be cured. EXCEEDS was ruled at council read 104 to carry one sense after all — later or greater in the value’s order, on instants, quantities and lengths alike, as EQUALS compares any value — and the numeric ≥ gap the next sentence describes closed with DOES NOT EXCEED. EXCEEDS compares instants, durations and counts across 63 rule-surface sites, and carries a second form, NOT EXCEEDS, at 4. MUST says always and eventually in the same breath. EVERY ranges over a store and over the world. Each was found by accident, on its own, in a different read — none by looking for this — and each already has a row describing its symptom without naming its cause. EXCEEDS is the one that makes the case: council read 31 spent a read establishing that the ≥ class splits by operand type, a ≥ between instants having a one-arm cure where a ≥ between numbers has none, and that split exists precisely because one token compares three kinds of thing. The corpus documented the consequence and missed the source. The grammar’s own frame has the gap in plain sight: Principle 6 puts complexity in the number and arrangement of simple rules and Principle 7 keeps it out of one rule’s grammar, and nothing keeps it out of one keyword’s meaning. Draft, for the maintainer: Principle 12: Complexity MUST NOT live in the meaning of one keyword. A candidate beside them, and a declared record verb this time: stand, 390 uses in the rules of the forty-one migrated specifications. After the verb come in (73), as (55), a state or property word (57 — immutable, non-blank), a place or a span — at, inside, outside, for, within, under, on (29) — and 176 others, the write stand X in Y among them. That is a pattern count by the following word, not yet a census by sense (council read 116). | Whether the principle is admitted — earned four times over with every instance independently found, which satisfies both halves of Principle 2 — and if so whether it is retrospective. Admitting it does not by itself split any token; it makes each of the four rows an instance with a stated cause, and it makes the audit repeatable: for every admitted keyword, name the jobs it carries, and treat more than one as a defect awaiting a ruling rather than a reading to get used to. The row’s own first census was wrong in the exact way the corpus wrote a rule against. It counted 290 EXISTS and 85 EXCEEDS — occurrences of a word, including prose, WHY lines and a Decisions entry, where no obligation lives. The rule surface is 224 and 63. It missed NOT EXCEEDS as a second form entirely, and the chat summary dropped an other bucket of 5 so the EXCEEDS subdivision appeared to sum to 80 of 85, which is what a reviewer caught. pressure-testing.md states the cure, dated 2026-09-10 and written after the same failure propagated into a roadmap entry and a language proposal: a vocabulary measurement states its match pattern, its case rule and its file set, or it is not a measurement of a form. Four days later, a row arguing that keywords must be measured by sense measured them by word. Both rows now carry their match pattern. And the pre-correction draft is in permanent history: commit eae32fd asserts 290 and 85 as site counts, calls them the 224 bare sites, and closes by reporting a defect that does not exist — the EXCEEDS subdivision sums to 80 of 85, five sites classified nowhere — where the subdivision does sum and the real defect was word-versus-form. It is pushed, so it cannot be amended; this row is the pointer, named here because a reader chasing a census number lands on the row and not on a commit message. The class is the corpus’s own shipped-fabrication shape at one remove — a wrong number quoted forward with nothing linking it to its correction — and the cheap standing cure is that a corrected census names the commit carrying the stale one (council read 52, council read 55). |
| An invariant resting on an unowned surface | 2 instances in one atom, 0 detectable | The inverse of the checks resting on unowned invariants row: a rule that carries force while its scope lives on a surface that carries none. Duplicate Prevention’s deleted Invariant 1.1 claimed EVERY identity in the recorded set stands under guard, and only the WHY beside it narrowed that to an eager host — a surface Surface 15 tells the parser to ignore and Surface 9 lets a writer delete, so a lazy host the atom’s own MAY rules license violated an invariant it broke no obligation to reach. Tombstoned rather than restated, because the restatement on offer was Operation 7’s contrapositive (council read 52). Its sibling is still standing and is watched: Invariant 4.1 reads as safety and is meant as liveness, with the distinction carried by the word Eventual in a family heading — a weaker surface still, and the safety-and-liveness-share-one-modal row’s second specimen. Neither instance is mechanically detectable: no instrument reads a WHY or a heading for normative content, which is by design, and the defect is precisely normative content living there. The two specimens also fix the class’s boundary, which the first one alone would have drawn too narrowly. The WHY instance suggests the class is prose in a WHY; the heading instance says it is normative content on any nothing surface, and the heading is the weaker of the two — a parser that will one day read WHY lines deliberately is not going to start reading family headings. Whatever detector this class earns has to be pointed at the rule’s own completeness, not at the surfaces around it (council read 52). | Whether the class earns a check at all — a scope-bearing word in a WHY beside an unconditional rule may be too soft a signal to detect — or whether the cure stays a reading discipline stated once: an atom with MAY-shaped implementation modes puts the condition in the rule, not beside it. The liveness half waits on the modal ruling. |
| The degenerate bound | 5 instances | An inequality or bound whose two readings make different rules real: Lease’s less, Duplicate Prevention’s window, Retention Window’s deleted Invariant 6.3 and its degenerate duration, Lease’s arrival-term boundary, Provenance’s sequence_range — [5, 5] is one entry or none, and no term declares the range’s inclusivity (council read 28). | A named check, or a declared form for a bound that must strictly advance something (council read 11). Message Preference is the first instance with a designed cure rather than a repair: declare the degenerate case as a term, forbid it at the seam as a host obligation, and fail-stop the transition rather than reporting a host fault as a caller error (Instance 11, Instance 13 through 16). Define, forbid, fail-stop — generalizable, and the candidate form if the class is admitted (council read 18). Duplicate Prevention’s window, listed above since council read 11 as an undeclared boundary, turned out to be the opposite and the row gains a shape it did not carry: the boundary was declared twice, on opposite sides — Term under guard as elapsed term less than window duration, Invariant 4.1 and Invariant 4.2 as IF the elapsed term EXCEEDS window duration — which made Invariant 1.1 false at exactly elapsed term = window duration, an instant where the identity is not under guard and no rule obliged a host to drop it. A silent boundary makes a reader choose; a doubly-declared one makes the spec disagree with itself while reading as finished, and no instrument separates them. The cure was neither a new operator nor a new term: route the rules through the predicate the spec already declares, which is what Operation 6 and Operation 7 were doing all along. So this instance closes, and it closes by subtraction (council read 51). |
| The auditor’s vocabulary | every regulated spec | Generation acceptance needs verbs an atom’s own rules never use — find, reproduce, compute, reconstruct, confirm. Nothing owns the split between what an atom does to records and what an auditor does with them. | The generation run: an implementer becomes the auditor, and these verbs become the test language (council read 11). |
| The rule-family namespace | Clock semantics in 5; one concept under two family names | A family outside the standard set, carrying different rules in each spec, with nothing diffing them (tools/grace/cites.py --drift) — and now a concept filed under two families at once: timestamp ordering is an Invariant in Permissions, Notification and Legal Hold and a Temporal property in Message Preference, both faithful to their own prose originals. This is the term entry grammar’s question one floor up: term declarations below, family names above, and a ruling that reaches only the lower floor leaves the drift alive where the corpus is supposed to be stable (council read 10, council read 18). | Whether the standard set grows by recurrence alone, or a family stays local until contested — and whether a family name resolves to the grammar or to the spec. |
| Multiplicity polarity | 8 atoms | Every atom answers does this concept admit more than one record over its key, and what does a duplicate mean? — Subscription at-most-one per pair, Tamper Evidence many and unordered, Retention Window many gated per-retention, Actor Identity many with fresh ids, Personal Todo one description per active set, Duplicate Prevention one stamp, Assignment at-most-one active per task, Permissions many grants. Answered locally seven times, never declared as a shared question — and it is the axis a composition’s load-bearing wiring decision turns on. | A declared question every atom answers in its Identity model, or a ruling that polarity stays local (council read 13). |
| Checks resting on unowned invariants | 3 specimens | A Generation acceptance check that leans on a property the spec does not hold: Subscription’s exact timeline reconstruction over a best-effort timestamp, Tamper Evidence’s forensic bound over a clock it refuses to vouch for. Both were repaired by softening the check or moving it to External checks. The third inverts: Message Preference’s Check 2.2 fixes the half-open currency interval that no rule in the spec states, so the check is not resting on a rule — it is the rule, wearing a check’s clothes. The fourth is a different failure again: Capability’s Check 1.1 asked an auditor to confirm fields unchanged and Check 5.2 to confirm a counter as the revocation found it, both historical facts a current record cannot evidence, in a section that declares the store and a clock as the whole audit surface. Each was split — the present-state property to the check, the history to an External check over the composing journal — which is the same cure Tamper Evidence took at council read 13 and is now a repeatable move (council read 12, council read 13, council read 18, council read 22). The fifth inverts again and is the rule’s side rather than the check’s: Provenance’s Atomic writes 6 and Atomic writes 7 forbid the store serving a read or accepting an action before a dangling transition resolves, and never-servable is not a property a records-only auditor can clear at all — no check was written, correctly, and the obligation sits uncovered (council read 28). | Whether a check must name an invariant of its own spec, which cites.py --unchecked half-answers from the other side, and whether an invariant with no clearable check earns a declared probe class in the coverage matrix rather than silence. |
| The purge seam | 3 atoms | A durability invariant (the record MUST NOT change, the set MUST NOT shrink) beside a retention obligation (a deployment with sensitive content MUST compose Retention Window). Every path from the obligation lands on a violated MUST unless purge means a destruction the record survives. Notification states the resolution — leave every stored field as written — and Permissions carries the same seam latently (council read 16). | Whether the corpus declares one purge semantics (shredding-class, record survives) that every durability invariant is read against, or each atom states its own. Message Preference is the first to scope the invariant instead of the purge — store durability over this atom’s own surface, with lawful disposal named as the composed pattern’s declared and recorded act — which is the cure Notification’s payload-retention rules and Legal Hold’s own store could copy (council read 18). |
| Scenario-exercised powers beyond the signature | 2 specs, 6 scenarios | An adversarial scenario querying the store by payload content or time range, through operations no signature offers. The Generation acceptance preamble says the audit surface is the store rather than the action surface, which licenses it — but no rule says an auditor may read the store directly, so the scenarios rest on a preamble (council read 15, council read 16). | A rule granting the auditor the store, or scenarios restricted to the declared queries. |
| A query’s answer is not a tag | 6 specs, and the finding has landed | A value-set line whose member is a variable record or a collection rather than an outcome name: Subscription’s subscriber_refs, Assignment’s an assignment \| none and its history_for line carrying an ordering clause; Provenance’s read answers, where the ordering clause was removed to Operation 46 under Authority 3 but the line still answers with entries rather than a tag (council read 28). The signature block is the action’s value set (Closed vocabulary 21), and a query that answers with data has no tag for a composition to cite (council read 14). Audit Trail is the specimen the class was waiting for: read_record → audit_record | not-known and verify_record → outcome | not-known, where the [Audit Record] term entry calls the answer the composition’s emergent output. The Provenance read predicted the class would break on the first composer needing to cite a read outcome by name; that composer is the audit surface every other composition reads, and it has no name to cite (council read 29). | A form for a query’s answer, or a ruling that a query declares its shape in prose and compositions cite the action rather than the answer. The class now has its finding-against-the-class and is decidable. |
| The connectives drift | 4 shapes | ONLY IF inverted (council read 15), EXACTLY ONE OF stretched over a catch-all (council read 17), WHERE used undeclared where IF is the admitted form (council read 17), and a lettered label used for a renumber dodge rather than a WHEN child. The grammar declares each connective’s form; it declares no truth table, so a writer reaching for rhythm drifts and nothing reads it. | Declared semantics per connective, checker-visible — two of the four already are (E-not-exclusive, and the ONLY IF sweep), which is the argument for finishing the set. |
| Addressable sections | first specimen | A rule or term entry citing a section rather than a label — Subscription’s Subscription Id term entry cited a Configuration section no atom shape carries. Labels resolve; sections do not (council read 12). | A form for naming a section, or a rule that a citation must name a label. |
| The wrong-instance read | 2 atoms | A query routed to the wrong store instance answers nothing found, and the caller cannot tell that answer from nothing exists. Legal Hold closed it by answering the store_name the read was routed to; Message Preference cannot copy that cure — the store name is deliberately not a parameter and not a stored field, so a wrong-instance [Current For] answers none, the fanout-on-no-record policy fires, and delivery proceeds against preferences held in another instance. No auditor can detect it from either store (council read 17, council read 18). | Whether the corpus admits one instance-echo form on a read’s answer, or rules the routing layer’s correctness out of scope for every atom — the fork Legal Hold took locally and Message Preference forecloses locally. |
| The dangerous-side retry | 3 instances | A storage failure that fails closed on the safe side and open on the dangerous one, with no retry obligation stated. Permissions’ Revoke persistence 1 through 4 is the corpus’s only apparatus; Legal Hold’s failed place leaves a record unprotected while litigation pends; Message Preference’s failed set leaves the superseded record governing, so the opt-out that did not land keeps the prior email: preferred shaping delivery — against this atom’s own CAN-SPAM reference (council read 16, council read 17, council read 18). | Whether the grammar carries a form for the asymmetry (a rejection whose failure direction is dangerous obliges the caller), or every atom re-derives Permissions’ four rules. |
| Which sections an atom carries | 4 migrated atoms, 2 shapes | Permissions, Notification and Legal Hold each carry four External checks; Message Preference carries none, and substitutes required deployment disclosures inside the conformance checks (clock tolerance, fanout-on-no-record, opaque-input size bound). Both are defensible audit theories — read another pattern’s records versus require a declaration — but nothing says which sections an atom must carry, so a template break and a deliberate redesign are indistinguishable from the outside (council read 18). | Whether the grammar owns an atom’s section shape, and whether a disclosure is an acceptance check or a fourth thing. |
| Safety and liveness share one modal | 1 rule, 1 model, measured | GRACE’s MUST carries no temporal quantifier, so always at most one and eventually exactly one are the same sentence, and a rule can straddle the line without any instrument seeing it. Provisional Commitment’s draft Invariant 2.3 — EXACTLY ONE resolving action against one commitment MUST commit — did exactly that: its at-most-one half was already owned by Invariant 2.1 and Invariant 2.2, and its marginal content was the at-least-one half, which this atom cannot deliver, because it decides nothing about when [Expire] fires and licenses lazy expiry. The formal layer settles what the rule could not say: provisional-commitment.cfg declares INVARIANT Safety and no temporal property, and Inv_SingleResolution checks that a written resolution matches the state — the safety half, silent on whether any resolution is ever written. The spec said exactly one and the model checked at most one, and nothing reported the gap; the rule was decomposed rather than kept (council read 38). | Whether the grammar gains a temporal-scope marker, or whether a liveness claim is declared out of the rule surface entirely and routed to a deployment’s declared cadence — which is where this one went (Non-goal 25, Capability requirement 12). The second is cheaper and is what the corpus already does by accident; the first is what a citation between a rule and a model would need to mean the same thing on both sides. |
| A blank reference and a reference to nothing are two answers | 2 atoms, 1 design | Most atoms fold them: a not-known covers both an id naming no record and an id that is blank. Party Identity split them — a blank party_id is invalid-request, decided before the store is consulted, and a well-formed one naming nothing is not-known — on the ground that a caller who cannot tell them apart cannot tell a bug in its own code from a record that was never created. Medication Order is the second instance and generalizes it a level: one blank-input rejection term covering invalid-order and invalid-request, and a field fault term that keeps the id check out of the after-the-state-checks group where every other field fault sits, so invalid-request legitimately appears at two heights in one priority order. Two atoms, one design, ported deliberately rather than reinvented (council read 43, council read 45). | Whether the split is the corpus’s posture or one designer’s preference. It costs a rejection arm and a second priority position in every atom that takes it, and the atoms that folded them are not wrong — they are answering a caller who has no bug. |
| A field the rules turn on, with no term entry | 4 fields, 1 atom, no instrument | The registry is the citation substrate: a rule names a field, a [Marker] resolves through a link line to a term entry, and a reader lands on what the field is. Medication Order’s migration ruled over administered_at, amendment_reason, reinstated_by and reinstated_at — each named in State and Operation rules, each declared in the records vocabulary — and gave none of them an entry. A claim with no address. O-term-unreachable now catches the adjacent case (an entry the link block never names), but not this one, and the reason is that the Term records line cannot be read mechanically: Provenance declares two records on it, Audit Trail declares named derived indexes on it, and a naive field-coverage check reports both as missing. A check that fires on correct specs teaches readers to ignore it, so this stayed a row rather than becoming an instrument (council read 45). | Whether the records vocabulary gains structure a checker can read — records, fields and derived structures distinguished rather than listed together — which would make field coverage mechanical and would also give the wire layer the naming convention §18 says it lacks. |
| An instant that cannot precede another, and nothing says so | 1 atom, 1 pair | Medication Order rules each instant’s upper bound and never relates two of them: administered_at may precede the dispensed_at of the same order, which is physically impossible — the dose cannot be given before the pharmacy released it — and is representable in a conforming store. Clock semantics 4 and Clock semantics 5 declare the asymmetry honestly (a prescription cannot be dated tomorrow; an administration may be documented late) and the WHY names its own limit, both directions are backdatable. What neither reaches is the cross-field claim. The temporal-postures row now counts three members — Party Identity’s insertion-order-authoritative, Medication Order’s asymmetry-declared, Observation’s future-bound-only — and none of the three relates two instants of one record (council read 45). | Whether a cross-field temporal invariant is a spec-by-spec obligation or a shape the grammar should make cheap. The atoms that need it are the ones whose records carry a sequence of stamped events, which is most of the regulated set. |
| The attribute schema, and the interpretation riding on it | 1 atom, 2 questions | Party Identity is the strip test’s third outcome — no domain tag, no rename, and the domain coupling in the field schema: name, date_of_birth, document_type, document_ref is natural-person identity-document vocabulary, and the atom already treats two of the four as opaque strings it never reads. That is the vocabulary half. The second half is sharper and rides the same schema: date_of_birth is not merely named, it is interpreted — Operation 2 refuses a value that parses as no calendar date, Operation 3 refuses one that exceeds now. That is the corpus’s first typed-content semantics on caller input, and it is the exact inverse of Observation’s law, which is to record and never interpret what the spec was not told how to validate. A calendar is not a domain, so the strip test passes either way; the interpretation posture is a separate question the same field raises (council read 43). | Whether the schema becomes an opaque attribute bag — and if it does, whether Operation 2 and Operation 3 move to a composing validator or die with it. The two halves can be ruled apart: a spec could keep a named schema and still refuse to interpret it, which is what every other atom in the corpus does. |
| A bound consumed and never declared | 2 atoms | Party Identity’s String 7 and Credential’s String 7 each refuse a string input exceeding the deployment’s length bound, and each obliges the deployment to set one, while neither declared the bound as a term — Party Identity’s bounds category was empty and Credential’s named only its default deadline. So a load-bearing value entered the rules through prose, which is the same shape as the seam gap M-seam-injection now catches one layer over: a rule consuming something the spec’s own vocabulary does not name. Both now declare length bound and the rules cite it (council read 43). | Whether the bounds category gets the treatment the seam declaration just got — a cross-check that a rule naming a bound finds it declared. Closed vocabulary 4 already requires every identifier to resolve; the gap is that an undeclared bound reads as prose rather than as an identifier, so nothing looks for it. |
| One name, two definitions, one day apart | 2 atoms, 1 collision | Invitation declares live as a pending invitation whose expires_at exceeds now — a mandatory deadline. Credential declares live as an active credential whose expires_at EQUALS blank, OR whose expires_at exceeds now — an optional one. Same name, different predicate, migrated a day apart, and neither composes the other so neither can cite the other. Provisional Commitment’s open beside Invitation’s live was the benign specimen — different names for the same shape; this is the same address for a different building, and a reader crossing between the two specs has nothing on either page telling them the word changed meaning (council read 41). | Whether a term name that recurs across specs is owned anywhere, or whether every spec’s Terms registry is a private namespace and a reader is expected to re-read the declaration at every crossing. The window-reading watch entry now counts three declarations of the shape and this is the first divergence inside it. |
| A missing deployment default gets two answers | 2 atoms | Invitation refuses the caller: IF the default ttl EQUALS blank THEN [Initiate] MUST answer invalid-request. Credential does not refuse at all — a deployment declaring no default expires_at leaves the credential’s expires_at absent, and the credential reads live forever. The same configuration miss, opposite answers, same migration day, neither citing the other. This is the config-miss sibling of the rare-condition-routing row above: there the question is which existing arm a rare cause borrows, here it is whether a deployment’s own gap is the caller’s problem at all (council read 41). | Whether an undeclared deployment default is a refusal, a silent absence, or a spec-by-spec decision — and if the third, whether the spec must say which it chose. Credential says so in a term declaration; Invitation says so in a rule; a reader comparing them has to notice the difference to find it. |
| The closed answer set charges rare conditions to the nearest arm | 3 sites, 2 atoms | An action’s outcomes are exactly the arms its signature block declares (Closed vocabulary 20 through 22), so a condition nobody anticipated is routed to whichever declared arm is least wrong. Invitation does it twice: a token collision is refused by the store and answered storage-failure — true about the outcome, loose about the cause, since a collision is a correct refusal of a well-formed call rather than a store that failed, and the remedy is fresh token material rather than the same write retried; and an absent ttl with no declared default answers invalid-request, charging the deployment’s configuration gap to the caller. Medication Order’s lease-death is the third. Each is defensible alone and the set is not: three conditions with three different remedies wearing two existing names, because adding an arm is the one thing the grammar makes expensive (council read 39). | Whether a spec may declare a rare-condition arm without paying the full outcome-taxonomy cost, or whether routing to the nearest arm is the declared posture and each site must say in a WHY which cause it is borrowing a name for. The second is what Invitation now does and is cheap; the first is what a generated client would need to branch correctly. |
| How a refusal names what blocked it | 3 shapes, 3 atoms | One question — why can I not act on this record? — answered three structurally different ways. Negation: Provisional Commitment’s not-held, which says only that the guard failed. State-name: Invitation’s expired and Medication Order’s on-hold, which name the standing that blocked it. Parameterized event: Invitation’s already-resolved(accepted), the corpus’s first payload-bearing refusal, where Invariant 6.1 makes the payload mandatory and a bare already-resolved non-conformant. The three carry strictly increasing information and cost strictly increasing grammar — the third needs two rules to say what the payload must and must not carry, because a signature block’s arms are names and a parameterized arm parses as one opaque token (council read 39). | Whether the corpus declares one refusal-naming posture, or declares the three as a named set a spec picks from. The payload shape is the one with no grammar at all, and it is also the one a caller can act on without a second read. |
| A spec’s actors are not marked as agents | 3 atoms | GRACE-lang declares the split — an actor is any identifier that may be a rule’s subject, an agent is an actor that can perform a rule’s verb, and a rule whose subject is an actor and not an agent constrains the writer — and then declares its own agents and gives a spec no place to. So Invitation’s vocabulary lists a rejection and an answer among its actors, which is correct by the definition, and Invariant 10.2 reads as an obligation on a rejection rather than on the implementation. The grammar already has the concept; the spec-level vocabulary block has no category for it (council read 39). | Whether agents joins the category value set beside actors, or whether the distinction stays GRACE-lang’s own and a reader is expected to derive it. A generator reading the vocabulary cannot tell which subjects it must implement. Party Identity is the third and adds a flavor: its actors list carries a party’s verification event list length and a party’s state change log length — aggregate quantities, which are subjects of real rules and are not entities at all (council read 43). |
| The input-handling regime | 4 regimes, 6 atoms | Permissions and Notification carry String 1 through 7; Legal Hold carried blank load-bearing and undefined until it was declared; Message Preference declared blank and then refuses to bound anything at all, handing every size cap to the deployment, with exact-match equality resting on two Identity rules and no no-trim or no-case-fold rule beside them. Provisional Commitment is the fourth posture and the one that shows the question is not only about caps: its migration shipped with no String family at all, so two presence checks tested caller strings for absence with no declared reading of whitespace anywhere on the page, and a whitespace-only resource was neither a hold nor a refusal. It now carries String 1 through 6, bounds duration by declaration and bounds its strings by nothing — a declared cap, a delegated cap, and silence, with silence twice arrived at by different routes. Invitation is the sixth and repeats Provisional Commitment’s failure exactly: prose that said non-null and non-empty required eight times, no String family, no blank, and therefore no declared reading of a whitespace-only context or reason anywhere on the page. Two migrations running, the same gap, found the same way — which is the argument for a declared posture rather than a per-atom habit (council read 17, council read 18, council read 38). One third of the posture is now declared: since GRACE-lang v0.52 the grammar owns blank, so what a presence check refuses is no longer an atom’s choice; equality and size still are (council read 99). | One declared posture on opaque inputs — what a presence check refuses, what equality does, who bounds size — or a ruling that the posture is the domain’s. |
| May a guard read the clock? | 2 atoms, at rule level | Legal Hold’s Operation 8 refuses a placed_at that exceeds the injected now — a guard consulting the clock to refuse admission. Message Preference declares guard as a term and forbids it outright (Operation 42 through 43), so a skewed clock can degrade a stored stamp but never admit or refuse a call. Both are coherent; neither declares its jurisdiction, and the grammar is silent. The proposed boundary, offered for a ruling rather than assumed: a guard may read the clock exactly where the action accepts a caller-supplied instant, because refusing a dishonest one is the only thing that needs it — which makes the two atoms agree rather than fork, and makes the third atom’s answer derivable instead of invented (council read 17, council read 18). | A rule stating when a guard may consult the injected reading, or a ruling that the input surface settles it per atom. |
| Obligations resting on unowned wiring | 1 specimen, first named | A standards reference legislating a composition the rules of neither atom carry: Message Preference’s GDPR Article 21(2) note says a deployment recording an opt-out here without revoking in Consent has not discharged Article 21 — a propagation obligation across two atoms, stated in prose at the references layer. The sequencing that is owned runs one way only (Composition note 6 through 7 gate the read on the permission decision); nothing propagates the objection back. The cousin of checks-resting-on-unowned-invariants, one layer out: the check rests on a rule the spec lacks, this rests on wiring no spec declares (council read 18). | Whether a standards reference may state a compositional obligation, or must cite a Composition note that carries it — and which spec owns a two-atom propagation. |
| Uniqueness asserted over randomness, with no collision path | 3 atoms, 2 cures | An absolute invariant — two records MUST NOT share an id — over an id drawn from a random source, with no action in the signature that can report a collision. Session’s [Issue] answers a token, invalid-request or storage-failure and nothing else; Subscription and Capability carry the same shape. 128 bits makes a collision fantastically unlikely; it does not make the invariant logically true (council read 19). Capability is the exception and the cure: Invariant 12.2 makes the store refuse a colliding write, so uniqueness is enforced rather than hoped for and a collision surfaces as an existing outcome, [Storage Failure] — no new arm, no probabilistic invariant. Session and Subscription assert the same uniqueness with no such rule. Invitation is the second atom to carry the cure, and its migration states it as an Identity rule rather than an Invariant — IF the store holds the invitation_token THEN [Initiate] MUST answer storage-failure — with the store-side half as an explicit Capability requirement, which is the shape the ruling would generalize if it goes that way (council read 38). | Whether the store-refusal rule Capability carries becomes the corpus’s answer and propagates to the other two, or each atom keeps its own posture. The cure needs no new grammar, which is the argument for propagating it. |
| The fault ontology | 2 atoms, now disagreeing | A transition’s failure is one of three things — the caller’s, the store’s, or the deployment’s — and only the first two have declared outcomes. Message Preference fail-stops a degenerate host injection rather than reporting it as a caller error; Session answers invalid-request when the deployment’s default session duration is absent, having called that absence a misconfiguration in the same breath. Two atoms, two treatments of one class, and no member of any value set names a host fault while Hard invariant 20 forbids inferring one (council read 18, council read 19). | A third outcome class for a host fault, or a ruling that a deployment fault is reported to the caller as a caller error and the prose stops calling it a misconfiguration. |
| Revoking the lapsed | 2 atoms, opposite rulings | One mechanic — a bearer token, a derived lapse, an explicit cancel — and two answers to may a lapsed record still be revoked? Session says yes: a lapse is not a stored terminal, so [Revoke] succeeds and records who closed it and why, which is what a breach response wants. Capability says no: a lapsed capability answers [Already Terminal], by derivation, writing nothing. Both are defensible and both are deliberate — Session’s own prose notes it changed posture from an earlier stored-[Expired] design — and neither atom declares the fork or cites the other. The consequence is asymmetric audit: a breach triage can attribute the closure of an exposed session and cannot attribute the closure of an exposed capability (council read 21). | Whether an attributed closure of an already-dead record is a capability the corpus offers or withholds, and whether terminal means no further write or no further state. The two atoms disagree on that definition, not on the mechanics. |
| A mutating action with no write-failure outcome | 1 atom, blocking | Capability’s [Redeem] lowers the counter and can move the status to [Redeemed] in the same operation, and its signature carries redeemed(...) and four invalid(...) arms and nothing for the write failing — while [Allocate] and [Revoke], which write less, both carry [Storage Failure]. The prose spec states the gap as a feature ([Redeem] is not a rejection-based action — all five outcomes are first-class results, not errors), which is how it survived Final Critique 5: every outcome named is indeed a result, and the unnamed one is the write. Beneath it sits the commit-ambiguity problem Recoverable Invocation exists for — a crash past a durable commit and before the reply leaves a completion nobody can resolve from this atom’s surface (council read 22). | Whether the arm is added to [Redeem], or the atom declares the write unfailable and names the host obligation that makes it so — and separately, who owns resolving an ambiguous redemption. A blocker either way: today there is no legal answer to a failed redeem. |
| A claimed actor recorded as an attributed one | 2 atoms | An action accepts an actor reference from the caller, checks only that it is present, and records it permanently under a field an invariant calls attribution. Capability’s revoked_by_ref and allocator_ref and Session’s revoked_by_ref and issued_by_ref all work this way, so the record means the caller supplied Alice while revoking this, not Alice revoked this. Both atoms name Actor Identity as the attestation composition, and both call the unattested field attribution in the meantime (council read 22). | Whether an unattested reference may be called attribution, or the corpus names the claimed and the attested forms apart — a vocabulary ruling, not a mechanism one. |
| A claimed contribution with no owner | 1 specimen, 5 dimensions | the-spec-layer.md §128 specifies complexity ratings in full — five dimensions (logical, interaction, domain, consequence, temporal), rated automatically, driving four decisions: examples required per rule, how aggressively contradictions are checked, which model generates, and how much human review a spec needs before deployment. §242 lists the ratings as one of the project’s four novel contributions. Nothing implements them: no pattern carries a rating and _data/patterns.json has no field for one. This is council read 22’s obligations resting on unowned wiring, one layer out — a positioning document legislating machinery the corpus does not hold. | Whether the ratings are derived or dropped. The migration has just made all five measurable for the first time: logical from rule count and family spread, interaction from constituent count and the cross-spec citations cites.py already walks, domain from standards references and regulated_by, consequence from regulated_by and security (both already in patterns.json), temporal from the Clock semantics rule count and whether the action surface accepts a caller-supplied instant. Derived rather than hand-assigned, per §Generated index’s discipline — a hand-assigned rating rots and a computed one cannot. It measures only migrated specs, so it covers 19 of 54 today, which argues for building it after the corpus rewrite and using it as a progress instrument in the meantime. |
| A spec with no version of its own | 54 specs, 0 versions | Nothing versions a spec. The Ledger carries status, formal, last gate and open; the qualifiers line stamps the grammar version a spec was written in — migrated — rewritten in GRACE lang v0.35 — never the spec’s own. So a deployment can say only Capability as of commit 2a695c4: git is the version, which works inside a repo and not in a citation between organizations, and a generated implementation has nothing to name as the thing it conforms to. The substrate is already built and unnamed. Hard invariant 26 and Hard invariant 27 — never renumber an invariant, never reuse a tombstoned label — are already the compatibility guarantee a version number normally has to promise, and Hard invariant 25’s tombstone is already the deprecation marker that lets a removal be reported as removed rather than as never existed. Reverse diff 1 through 5 already specifies the instrument that would compute the bump, over normalized obligations, and that instrument is unbuilt — this would be its first real customer. | Whether a spec carries a version, where it lives (the Ledger is the natural home, beside last gate), and whether it is derived from the reverse diff or declared by the maintainer — derived being the discipline everything else here follows. The shape is the hard part, because a spec has two audiences and a change breaks them asymmetrically: an ADDED obligation breaks implementations, whose generated code now fails a rule it never saw, and is harmless to citers; a REMOVED obligation is harmless to implementations and breaks citers, whose cited label dangles; CHANGED breaks both. One number cannot carry that. Two numbers, or one number and a kind. |
| The gate is softer than the grammar | 3 classes, 76 sites | tools/grace/check.py --gate fails only on non-advisory findings, and three classes the grammar states as Hard invariants sit on the advisory side or were absent entirely. Hard invariant 8 rejects OR outside a condition or a term declaration; the uppercase case gates and the lower-case one is advisory, at 46 sites. Hard invariant 5 rejects a sentence carrying two obligations and had no implementation at all — now a heuristic at 27 sites, 23 of them in the two pilot compositions, which dates the miss to before the split discipline matured. Hard invariant 4’s pronoun declaration names this, that, these and those standing alone and names this file as what enforces them, which it did not; two of the four are now detected, at 3 sites. Every count is small enough to clear and large enough to have hidden (council read 24). | Which of the three promote to gating, and in what order — each promotion turns a green corpus red until its sites are cleared, so the order is a scheduling decision. The deeper ruling is whether an advisory code may ever carry a Hard invariant: the grammar says the parser MUST reject, and an advisory does not reject. |
| The implicit success path | 181 sites, every migrated spec | An action carries rejection rules and, separately, unconditional effect rules — so a refused call is obliged to reject and to perform the effect. Capacity’s [Allocate] must answer closed against a closed pool and, by a second rule with no condition on it, raise the running total and append an event. The prose specs relied on procedural case-order — if we reached this line, the guards passed — which is how every spec document reads and which Hard invariant 15 and Hard invariant 16 explicitly forbid inferring. The case table knows the state machine; the normative surface does not. Detected corpus-wide at 181 sites across all seventeen migrated documents, with no spec exempt (council read 26). | The cure is uniform and needs no grammar: a declared admitted-call term per action, taken as the effect rules’ subject — an admitted allocate MUST raise allocated to the requested total. One declaration and a subject swap per rule. The ruling is whether that becomes the corpus’s standard form, and whether W-unconditional-effect gates once the sites are cleared. |
| The checker validated no condition | 5 sites found, 0 rules | check.py had no validation of a condition against the operator set, so IF capacity is not a whole count THEN … passed, and so did MUST refuse a call whose new_capacity allocated EXCEEDS — a line with no readable grammar at all. Both were written by the chair and both were reported clean. W-condition-operator now catches the English comparison; nothing yet catches a statement that parses as a label and a modal and then dissolves (council read 26). | Whether the shape checks extend to the statement’s interior, which is the parser §16 specifies rather than the form-reader this tool is. The honest reading is that clean has meant nothing the checker knows how to look for, and the gap between that and correct is what every council read has been paid to find. |
| A promotion converts every local restatement into a defect | 3 promotions, 4 sites, 0 reported | Standard label 2 forbids a specification redeclaring a standard label family, so a spec that stated its own leg’s meaning in domain prose was conformant the day before a promotion and carries a second owner the day after. Council read 69 trimmed Customer Onboarding’s Term reconciliation and Actor Suspension’s Term sweep; council read 71 trimmed Attributed Permissions Admin’s Term failed-grant leg and Idempotent Reservation’s Term eviction leg on Housekeeping’s promotion. Nothing in the toolchain reported any of the four — each was found by reading the specs the promotion named, which works while a candidate has three members and stops working at the scale the census reports: eleven non-standard families stand at three or more and no drift gate has been run on any of them (council read 71). Read 69 deferred this row on the rule that two sites is a repair and not a class; the third promotion produced the same residue, which is the condition that entry set for opening it. | Whether the cure is a promotion-time sweep or a standing check. A standing check would compare every migrated spec’s Term declarations against the grammar’s Term standard label family line and report one that restates a standard family’s meaning — which needs a reading of restates that a regex does not have. A promotion-time sweep needs only the candidate’s member list, which tools/grace/cites.py --drift already prints, and would have caught all four at the moment the promotion made them defects. The second is cheap; the first is the one that catches a spec written after the promotion. |
| A check written at its first specimen reports its first specimen | 3 instruments, 1 audited | A check is built when a defect is found, and it is written against the defect that was found — which scopes it to that specimen unless someone widens it deliberately. Three now share the biography. M-terms-doubled reported a doubled ## Terms and nothing else for forty-two reads while two specs carried a doubled ## Generation acceptance (council read 73). M-orphan-forthcoming’s first cut asked whether a forthcoming name appeared in roadmap.md at all, and the tightening to a listed row found Condition Register, which neither the loose check nor the hand sweep had (council read 68). V-dup-vocab was built in one direction and missed the other. In every case the narrow check ran green over a live defect, so a green run proved the specimen absent and nothing more. The corpus has no record of the same question being asked of the rest of either tool. | The cheap audit, and it is V-dead-vocab’s economics exactly: one synthetic fixture per check that sits outside the specimen it was built on — the defect’s shape at a different site — and a check that goes green on it is cut. What the audit cannot settle is the prior question, which is whether a narrow check is a defect or a deliberate scope: T-seal-key is about seal keys and widening it would mean nothing. The test is whether the check’s own message names a shape wider than the check reads, which is what all three of these did. |
Trigger to resolve any of these: a finding against the class — two specs that disagree because the class has no owner, rather than two specs that merely share a shape. Recurrence alone is half of Principle 2, and the half that admits noise.
What resists automation (added 2026-09-12)
The goal is to automate as much of the pipeline as the pipeline admits. This section is the map of what does not automate, kept so that effort lands there rather than on the parts already mechanical. It is written from one measured day — seventeen atoms migrated, sixteen council reads applied — not from estimate.
| Stage | State | What resists, and why | |
|---|---|---|---|
| Shape checking | automated — tools/grace/check.py | Nothing. A form-reader decides it without semantics: does the line parse as a rule, does it use admitted forms only, is every verb after a modal declared, does a reference resolve to a label that exists. Deterministic, runs on every file, and caught two of its author’s own errors on the day it was extended. | |
| Normalization and contradiction | specified, unbuilt — §16, §18’s watch list | Nothing, once built. Two rules normalizing to X MUST a and X MUST NOT a under identical conditions is mechanical after normalization; the grammar already says so and calls it nearly free to detect. This is the single highest-value unbuilt instrument, because it moves contradiction from a reader’s job to the checker’s. | |
| Reverse diff | specified, unbuilt — §17 | Nothing, once the normalizer exists. Its two customers are already waiting: the nothing but language changed claim every migration Decisions entry makes and nobody has verified, and per-spec versioning. | |
| Outcome completeness | detectable, unbuilt | Newly clear, and the surprise of the day: an action whose rules write, whose signature block carries no write-failure arm is a mechanical check, and it is the shape of council read 22’s blocker in Capability. The class that looked like it needed a reader turns out to need a rule. | |
| Term-registry drift | 1 composition, unenforced; one withdrawn near-miss | Closed vocabulary 17 forbids a specification redeclaring a constituent’s term, and nothing checks it. Audit Trail’s cited line attributes every borrowed token to its owning atom — attestation_id, retention_until, seal-proof-invalid and thirty more — and no instrument diffs a composition’s Terms against its constituents’ registries to confirm the composition uses each with the owner’s semantics rather than its own. The prefix discipline on reasons is stated per-rule; the citation of fields carries no rule at all (council read 29). The first near-miss on record came from the chair: a draft of council read 117 declared Term act in five specifications, four of which compose Audit Trail, whose rules use act in another sense; it was withdrawn on reading, not on any finding (council read 117, council read 118). | cites.py --drift one layer up: term registries rather than label families. The tool’s next natural customer, and the only instrument that would make Closed vocabulary 17 real. |
| The world-scope invariant | 1 specimen, and it is load-bearing | Selective Disclosure’s Invariant 5.1 — EVERY transmission of a subject’s data to a party beside the subject MUST produce a disclosure record — quantifies over events the atom cannot observe. Every other invariant in the corpus ranges over a store; this one ranges over the world, and Invariant 5.2 says so in the same fence. Nothing in the atom can falsify it, no conformance check can clear it (External check 2 sends it outside), and only a composition owning the sole disclosure surface closes it — which Immutable Transaction Ledger does. The atom generates its own composition requirement (council read 30). | Whether a world-scope invariant is a distinct declared kind — an obligation on the deployment rather than on the spec — and what a coverage matrix records for a row no probe can ever reach. |
| The correction link | 2 atoms, 1 enforceable | Both append-only atoms cure an erroneous record by appending a correcting one, and they link it differently. Provenance’s Correction 4 names the corrected entry’s sequence_number — a field, mechanically resolvable. Selective Disclosure’s Correction 4 names it inside a declared correction narrative, which is the scope or the authority_reference, because those are the only two fields that hold prose. A correction linked by prose has no referential integrity: an auditor cannot resolve corrects the earlier one the way they resolve sequence_number: 4, and the corpus’s own structural-relation templates would flag the shape if it appeared between two records (council read 30). | Whether correction-by-append earns a dedicated link field, or a ruling that a prose link is what an atom with no ordering field can offer. |
| The answer-capture check | 4 atoms, one reason | A conformance check that needs the answers an action gave, not the records it left: matching a store’s entry count against the count of calls that succeeded. Capacity was forced into the split by council read 22; Selective Disclosure took it unforced; State Machine and Approval Step were written with it. The reason is identical all three times and is worth stating once — a production auditor reading the store cannot enumerate the calls that succeeded, because the store is exactly what would be missing one. Each spec’s cure is also identical: an External check for the capture, and a store-alone check from the other direction (a gap-free sequence, an unchanged re-read) that catches a lost record without needing the call log (council read 31). | Whether the capture is a declared External check shape the grammar names, or stays three specs independently reinventing one sentence. |
| The temporal lower bound | 2 atoms, 2 postures | Two append-only atoms bound a caller-supplied instant below, and disagree on whether there is a floor. Selective Disclosure has none by design — a disclosure discovered in an audit years later must be recordable, and Non-goal 21 says so. State Machine’s Operation 20 refuses an instant preceding the instance’s own creation, because a transition cannot fire before the instance existed. Both are right about their own domain and nothing declares the fork, which is the shape the string-cap divergence already has one row above (council read 31). | Whether a caller-supplied instant’s floor is a per-atom domain fact or a declared posture an atom picks from a named set. |
| The unqualified cross-spec citation | 1 confirmed, 24 candidates | §11 declares the qualified form for a cross-spec label — Recoverable Invocation Allowance 2 — and nothing enforces it. Approval Step cited Selective Disclosure’s Invariant 5 in the bare local form, where this spec’s own Invariant 5 is an unrelated rule, and the citation-aim audit resolved it against the local registry and got the wrong rule. A census over the corpus returns 24 sites where a bare label citation sits near a spec link, and nearly all are local citations that merely follow a link — so the class is real and the detector is not, because a bare Non-goal 19 after a link to another spec is usually this spec’s Non-goal 19 (council read 32). | Whether the qualified form becomes a rule a checker can enforce — which needs a way to tell a local citation from a cross-spec one that no current instrument has — or stays a convention the aim audit catches. |
| The lost answer | 3 surfaces, 1 problem | A caller whose answer is lost cannot tell refused, nothing written from succeeded, answer lost, and the corpus meets that fact in three places without naming it once. Caller-side: Approval Step’s Indeterminate outcome 1 through 3 — read before retrying, never read a transport fault as a storage-failure, and a retried resolving action is self-detecting because the step has moved while a retried [Submit] is not, because submission is not idempotent. Auditor-side: the answer-capture row above exists because lost answers are indistinguishable from refusals, which is why the store alone cannot enumerate the calls that succeeded. Composition-side: Audit Trail’s 2026-08-30-b open line is the same shape one layer up — a step-4 recording failure that never returns the event_id, so the retry appends a second event and the first is unretained and unmarked. The fork is the useful part, and it has three members rather than two: self-detecting because the action moved state (a retried resolve answers not-pending), self-detecting because the guard inverted (Soft Delete’s second purge answers not-deleted, since the guard tests standing-in-deleted and a purged record does not), and duplicating because the action only appended. Soft Delete is the fourth surface and the sharpest argument for a declared treatment: it needs the discipline — a retried [Soft Delete] after a lost answer creates nothing but a rejection, while a retried [Purge] answers a reason that does not say whether the record was destroyed — and it states no Indeterminate outcome family, because its prose carried none to raise. Adding one would be logic (council read 33, council read 34). Provisional Commitment is the family’s most inconsistent specimen and the reason the row is not only about retries: it routed rejection observability to a composing Event Log whose own composition note obliged an append on admitted actions only, so the routing target was empty for exactly the content routed to it. The note now covers refused actions too. Three atoms, three treatments — capture the answers, route to a log, route to a log that does not take them — and still no declared ruling (council read 38). Invitation changes the row’s status rather than its content: it arrived carrying Composition note 4 and Composition note 5 — append on every admitted action and on every refused action — which is the exact treatment the Provisional Commitment repair pass had invented one atom earlier, reached independently. Two specimens, one treatment, so the recurrence half of Principle 2 is satisfied and the contested half is a decision rather than a wait. Invitation’s External check 1 also names the newest wrinkle: the already-resolved payload is itself answer-only, so even a log of refusals does not carry it unless the caller records what the caller was told (council read 39). Undo History is the fifth surface, the first composition-authored one, and it adds the fork’s fourth member — the one with no self-detection at all. A retried [Undo] after a lost answer does not collide with the first: Action wiring 15 targets the undo target, and the first undo’s committed event has already advanced that target past itself, so the second call admits, appends a second well-formed undo event, and answers the next forward event’s type. Every member above is either self-detecting (the retry meets a rejection) or duplicating visibly (two identical appends the auditor can see); this one duplicates and answers success, and both events are correct by every rule in the spec. The caller’s only recourse is [Read History] and a count of undo events — which is exactly Approval Step’s read-before-retrying — so the cure is already in the corpus and the spec is the one place it cannot be stated. It is not stated, and for the Soft Delete reason: the prose carries no lost-answer material, so adding an Indeterminate outcome family would be logic. That ruling now has its second instance and its worst consequence, which is the argument for making the treatment declared rather than per-spec — a hazard class that is invisible in the answer cannot be left to whether a particular prose author happened to think of it (council read 46, council read 47). | Whether the three surfaces get one declared treatment — a named idempotency posture per action — or stay three specs and one open line describing one fact. |
| Identity by allocation or by reference | 1 divergent atom | Every migrated atom has the host allocating an opaque id at the seam — until Soft Delete, whose Identity 2 and Identity 3 say the caller supplies record_id and the atom allocates nothing, because the atom overlays a lifecycle onto records the host already owns and has nothing to allocate an id for. The consequences are declared rather than discovered: a record_id naming nothing in the host still produces a valid lifecycle record (Identity 10), and the first write creates the record with no registration action (Operation 7, Operation 8). This is not a different value in the standard identity shape; it is a second shape, and the corpus now has one of each (council read 34). | Whether the identity model declares which shape an atom takes, so a reader knows before reading Identity 1 whether the atom mints ids or accepts them. |
| The withdrawn constraint | 1 atom, unruled | Observation’s Operation 5 and Operation 6 validate a value against the observation_type’s currently declared constraint, at the moment of the call, on both [Record] and [Amend]. Nothing rules whether a deployment may withdraw or narrow a constraint while observations of that type stand. A withdrawal strands them: permanently unamendable (invalid-observation), retractable only, and readable forever — a lifecycle the atom neither intends nor forbids. The cheapest close is one family line obliging the deployment not to withdraw a declared constraint while observations of that type stand; whether that is the atom’s obligation or a composing pattern’s is the ruling (council read 36). | Whether a deployment-declared bound is immutable while records validated against it stand, and where the rule lives. |
| Which direction a rule may point across a seam | RULED 2026-09-14 | Composition note 10 in three atoms reads a composing pattern reading the store MUST NOT write to the store — an atom legislating a composition’s surface, where the other nine notes in the same family only route ownership outward (a composing pattern MUST own X). Composition notes are labelled rules carrying one obligation each, so the polarity is real: nothing rules whether an atom may bind a composition, a composition may bind an atom, or both. No conflict has surfaced in the corpus yet, which is why this is a docket row rather than a finding (council read 36). One has now, and it is about credit rather than content. Session’s Composition note 4 states the gate — IF Validate gives an invalid answer THEN a composing pattern MUST NOT call Permissions — and Session’s WHY names Session-Gated Authorization as the pattern that carries it. That composition’s Summary calls the same gate its load-bearing emergent invariant, belonging to neither atom. Both forbid the identical call, so nothing is contradicted; what is contested is whether a composition’s reason to exist can be a rule its constituent already owns. The contrast inside one document settles the shape of the question: Permissions’s Composition note 3 assigns the caller-to-subject binding without stating the rule, so that composition’s Invariant 2 is genuinely emergent, while Invariant 1 is inherited. An assignment and a prohibition are different obligations wearing one family name. Ruled by the maintainer, 2026-09-14: atoms may bind compositions; a composition cites what the composition inherits and owns what the composition adds. The direction question is answered — obligations may point down the seam, and the composing side’s duty is citation rather than silence or restatement, which is Authority 5 and Authority 6 already written and never before applied across the seam. Session-Gated Authorization is the worked example: Composes 7 cites Composition note 4, the three rules enumerating it are tombstoned to it, and Invariant 1.1 stands because it is strictly stronger — the note fires on an invalid answer given, the invariant on no valid answer given, so a deployment that skipped validate entirely satisfies one and breaches the other. Authority 4 is what keeps the two apart: a citing spec may add, and what it adds is its own. The residue, small and left open: nothing yet rules whether a composition may bind an atom — every instance so far points down (council read 36, council read 53). | Whether obligations may point up the composition seam as well as down, and what a composition does with an atom’s rule it disagrees with. |
| The atom with no acceptance surface | 3 of 28, and the composition epoch inherits it | Generation acceptance is where a spec says what an external auditor can clear from records and the query surface alone, and three atoms carry none at all — no section, no Check family, nothing auditing any rule: Duplicate Prevention, Event Log, Personal Todo. cites.py --unchecked states it flatly for the largest — no checks — nothing audits any of its 85 rules. Two of the three are exactly the atoms that closed the set without a council read, and all three are the earliest migration wave; Consent is that same wave with a full section, so the cause is not vintage but the loop — the reads are what produced acceptance sections, and the specs that skipped the loop skipped the section. The cost is not paid by the atoms, which are small and much-read. It is paid one layer up: Undo History composes Event Log and Personal Todo, and its thirteen Check rules and four External check rules rest on two constituents with no acceptance surface between them, so the first composition of the epoch is auditable exactly as far as its own rules reach (council read 49). Complete as of council read 51: all three have now been read, each returned a different class — an unchecked sharpest invariant in Consent, an uncited frozen contract surface in Event Log, a doubly-declared boundary in Duplicate Prevention — and none of the three reads produced an acceptance section, because writing one is work the reads did not have a ruling for. The correlation the row rests on is therefore as strong as it is going to get without that ruling. | Whether every migrated spec must carry the section — presence is mechanical, so it would be a lint.py check — or whether an atom may decline it and say so, in which case a composition needs a way to know its constituent’s guarantees are unaudited before resting thirteen checks on them. |
| One family vocabulary, or two | 1 composition, and the epoch just opened | Term standard label family declares ten families, and the set was written while every migrated document was an atom. One of the ten — Composes — is a composition’s family, so the set was already provisioned for this epoch; the question is how much more it needs. Undo History, the first composition migrated after the atom set, added four families outside the standard set: Action wiring, now at three specs with Audit Trail and Recoverable Invocation, which meets Principle 2’s recurrence bar, and Event schema, Replay and Wiring decision at one spec each, which does not. The absence is the other half of the evidence and is sharper than the presence: this composition declares no Clock semantics, no String, no Concurrency, no Capability requirement — the four most-used non-standard families in the corpus, at 25, 16, 20 and standard respectively — because the edge surfaces stayed with the constituents, which is what Composes 6 forbids it from touching. So the two document kinds are not drifting apart in the same vocabulary; they are drawing on disjoint halves of it (council read 47). | Whether the standard set is one vocabulary both document kinds draw from, with a composition simply using fewer of it, or whether an atom set and a composition set get declared apart — and if apart, whether a family may appear in both. Twenty-three compositions will answer the count; only the maintainer answers the shape. |
| Citation walking | automated — tools/grace/cites.py | The walk automates; the re-read does not. Principle 8 holds the decision: the tool names the reading, a human does it. That is a designed hold point, not a capability gap. | |
| Prose to GRACE rewriting | AI, high throughput, non-zero defect | The throughput is real — seventeen atoms in a day, accelerating as vocabulary got reused. The defect rate is also real: roughly fourteen defects the reviews caught, and twelve of the fourteen in the two atoms that received an adversarial read. The uncomfortable inference is the useful one — the fifteen atoms that received a lighter read or none most likely carry defects at a similar rate, which is what cites.py --unread exists to name. Review is not polish on this step; it is the step. | |
| Review | AI, parallelizable, and the union matters | Each member found a different class on one day: one found recurring classes across specs, one found semantic holes a form-reader cannot see, one validated the architecture and independently prescribed a rule the corpus already had, one arrived with a parser and found three Hard invariants the checker did not enforce. No single member found another’s findings. So review automates as a fan-out and not as a pipeline — running members in parallel on one spec beats running them in sequence on many, which is a scheduling consequence with a cost attached. | |
| Admission | human, by rule | Principle 8: the maintainer decides every admission. Not a gap to close — the grammar’s defence against its own growth, and the reason the docket is a docket rather than a queue of automatic merges. | |
| Domain truth | human | Whether a password-reset window should be an hour, whether a preference record should supersede rather than mutate, whether revoking a lapsed record should be attributable — no instrument decides these. Every one of them is a decision the spec then holds. This is the irreducible remainder and it is smaller than it looks, because most of what appears to be domain judgement turns out to be a missing declaration. | |
| Host obligations | human, and unverifiable from the corpus | The load-bearing gap in the generation story. Every atom hands something to the deployment — serialize on this key, choose an isolation level, supply an honest clock, refuse a colliding write, bound an opaque input. The spec names them, which is the win. But generated code assumes them, and no generated test can check them: they constrain the environment, not the code. They are enumerable today (Clock semantics, Concurrency, Configuration, String, Opaque input bound), and nothing yet collects them into the conformance obligation a deployment would have to sign. Two shapes are now visible inside the class: an obligation with a corpus default a deployment may override (Duplicate Prevention’s 1024-codepoint description cap) and one with none at all (Provenance’s maximum length, in an atom whose own WHY says a regulated store must be able to state its maximum record size) — and nothing says which shape an obligation should take (council read 28). The census, which the in-document watch flags no longer carry: five atoms set no cap (Observation, Soft Delete, Approval Step, State Machine, Selective Disclosure), Duplicate Prevention declares one, Provenance obliges the deployment to set one — seven atoms, three postures. A third posture landed one atom later: Selective Disclosure sets no cap at all, on a store that is append-only and never deletes — exactly the unbounded-payload sink Provenance’s own WHY argued a regulated store must be able to bound. Three specs, three answers, no owner: the docket’s trigger shape, and the in-document watch flag is the evidence rather than the cure (council read 30). |
Where the effort belongs, in order: the normalizer, because it converts a reader’s class into a checker’s; the outcome-completeness check, because a blocker class turns out to be mechanical; the host-obligation collection, because it is the only part of the generation story with no instrument at all; and review capacity, because it is the step whose defect rate is measured and non-zero.
On the ceiling. Something near 85% of the effort looks automatable on this evidence. The caveat is the one worth carrying: the residue is not evenly risky. Shape errors are cheap and caught; the unautomated remainder — admission, domain truth, host obligations — is where every expensive mistake lives. Automating the first 85% does not reduce the risk, it concentrates it, and the value of doing so is that the remaining judgement gets the whole of the attention instead of a share of it.
Generated index (planned, not built)
Open questions are also scattered through the artifacts as markers: *(forthcoming)* links, atoms/compositions marked unresolved / partially resolved, open CORNERS items, and the sub-question sections in proposals (e.g. atoms/TAXONOMY.md). Those are derivable — a future generator should project them into an index appended here rather than anyone hand-maintaining the list. Same discipline as the taxonomy itself: name the irreducible (the authored architectural questions above), derive the rest.
Scope addition (2026-07-06): the roadmap dashboard. An external readability review asked the roadmap for an executive summary — the request is legitimate and the hand-written form is forbidden (a summary block is a snapshot that rots; the roadmap now says so in its orientation note). The permitted form is the same generated-view move: a derived dashboard — pattern counts from _data/patterns.json (already emitted by tools/taxonomy/generate_graph.py), open methodology debts extracted from the roadmap’s numbered headers, open questions from this file’s section list — regenerated alongside the graph page, never hand-edited. Rides this generated-index plan; build it when the generator gains its next pass, not before (smallest machinery).