From da0ef8139593d21f5e7a666b18cd7fe84eca81e9 Mon Sep 17 00:00:00 2001 From: Serina Mcfall Date: Thu, 27 Aug 2026 17:14:43 +1200 Subject: [PATCH] docs(corpus): add the invariant template node Create launchpad/docs/corpus/templates/invariant.md so a future author has a schema-valid shape, evidence expectations and an adapted Design-by-Contract model to write a corpus node documenting one system invariant, distinct from the normative-language standard (#1320) and the not-yet-written policy template (#1344). Closes #1343 Signed-off-by: Serina Mcfall --- launchpad/docs/corpus/templates/invariant.md | 499 ++++++++++++++++++ ...27-issue-1343-corpus-template-invariant.md | 199 +++++++ 2 files changed, 698 insertions(+) create mode 100644 launchpad/docs/corpus/templates/invariant.md create mode 100644 launchpad/plans/2026-08-27-issue-1343-corpus-template-invariant.md diff --git a/launchpad/docs/corpus/templates/invariant.md b/launchpad/docs/corpus/templates/invariant.md new file mode 100644 index 00000000000..437ec1dc403 --- /dev/null +++ b/launchpad/docs/corpus/templates/invariant.md @@ -0,0 +1,499 @@ +--- +id: corpus-template-invariant +type: governance +status: active +origin: launchpad +audiences: + - agent + - developer + - reviewer +evidence: + - statement: "This node was authored and checked against repository revision a44cf52fc740ebebbdd671427480d14f0bce0115." + entry_class: FACT + evidence: + - "commit a44cf52fc740ebebbdd671427480d14f0bce0115" + - statement: "node.schema.json's type enum has thirteen members -- architecture, layers, capabilities, platforms, implementation, interfaces-events, verification, operations, development, release, governance, agent, ingestion -- and none of them is template, policy, or invariant; the enum names the corpus surface a node documents, not the prose form its body takes." + entry_class: FACT + evidence: + - "launchpad/docs/corpus/schema/node.schema.json" + - statement: "Of the corpus's four nodes merged to origin/launchpad at the recorded revision, AGENTS.md carries type: agent while README.md, standards/confidence.md and standards/decision-references.md all carry type: governance -- the precedent for a node documenting the corpus's own authoring rules rather than a piece of architecture/capability/etc. content." + entry_class: FACT + evidence: + - "launchpad/docs/corpus/AGENTS.md" + - "launchpad/docs/corpus/README.md" + - "launchpad/docs/corpus/standards/confidence.md" + - "launchpad/docs/corpus/standards/decision-references.md" + - statement: "relationships.schema.json defines five relationship types -- depends-on, supersedes, implements, references, part-of -- and states implements' directionality as 'source is the concrete realization of target (e.g. a template instance of a standard)' and references' directionality as 'source cites target as supporting context; no ownership or currency dependency implied'." + entry_class: FACT + evidence: + - "launchpad/docs/corpus/schema/relationships.schema.json" + - statement: "At repository revision a44cf52fc740ebebbdd671427480d14f0bce0115, the corpus tree on origin/launchpad contains exactly four validated nodes -- AGENTS.md, README.md, standards/confidence.md and standards/decision-references.md -- plus the schema/ subtree, which validate.py excludes from checking; none of this batch's siblings (#1329, #1333, #1334, #1348) nor the already-open template/standard PRs #1541 (interface) or #1518 (normative-language) are merged, so none of them are valid relationship targets." + entry_class: FACT + evidence: + - "git_ls_tree(ref='origin/launchpad', path='launchpad/docs/corpus') -> AGENTS.md, README.md, schema/COMPATIBILITY.md, schema/README.md, schema/fixtures/**, schema/node.schema.json, schema/relationships.schema.json, schema/requirements.txt, schema/tests/test_schema.py, standards/confidence.md, standards/decision-references.md, at commit a44cf52fc740ebebbdd671427480d14f0bce0115" + - statement: "Parent Feature #605's acceptance criteria require that 'every template states its purpose, required sections, evidence expectations and the industry model/standard it adapts', and this is the acceptance bar this node is built against rather than issue #1343's own copied-over standards-track Definition of Done." + entry_class: TEAM_KNOWLEDGE + provided_by: "launchpad-26/buzz#605" + - statement: "Issue #1343's own Definition of Done is byte-identical to the standards-track boilerplate ('States scope and authority/source of the policy. Separates MUST requirements from SHOULD guidance. Defines enforcement/checks and exception/escalation process. Links decisions or higher-order policy instead of duplicating them.'), the same text independently found copied across #1326-#1351 by prior batches in this same task set." + entry_class: TEAM_KNOWLEDGE + provided_by: "launchpad-26/buzz#1343 definition of done" + - statement: "This repository's root AGENTS.md states, under 'Thread counters': 'reply_count and descendant_count are materialized on thread root events. Any code that inserts replies must update these counters -- check existing reply handlers for the pattern.'" + entry_class: FACT + evidence: + - "AGENTS.md:182" + - statement: "buzz-db's thread.rs implements that gotcha as two functions, increment_reply_count and decrement_reply_count, each of which always bumps or decrements both the parent's reply_count and the root's descendant_count in one call (decrement floored at zero via GREATEST(count - 1, 0)); nothing in the type system stops a future call site for inserting or deleting a reply from skipping these functions -- the invariant holds only if every such call site actually calls them." + entry_class: FACT + evidence: + - "crates/buzz-db/src/thread.rs:250-287" + - "crates/buzz-db/src/thread.rs:289-327" + - statement: "buzz-core's tenant.rs states, in its module doc under '## The fence': 'The whole multi-tenant safety story rests on one invariant from the formal model (conformance \"row zero\"): a request's community is resolved from the connection host by the server, never supplied or influenced by the client', and explicitly calls this 'a lint-and-review fence, not a compiler fence' because TenantContext::resolved and CommunityId::from_uuid are public and a determined caller elsewhere could call them; the type system removes only the accidental path (deserializing a client-chosen community), and a migration-lint harness plus review closes the deliberate one." + entry_class: FACT + evidence: + - "crates/buzz-core/src/tenant.rs:1-25" + - statement: "buzz-audit's hash.rs carries a test, storage_precision_timestamps_survive_a_database_round_trip, whose comment names 'the invariant the write path must hold: hash what will be stored, so recomputing from the row reproduces the digest', and asserts it with assert_eq!(compute_hash(&written), compute_hash(&read_back)) after simulating the storage round-trip -- an invariant with a test that would fail if it were violated, not merely a comment asserting it." + entry_class: FACT + evidence: + - "crates/buzz-audit/src/hash.rs:201-214" + - statement: "buzz-relay's state.rs documents run_registered_community_connection with: 'The ordering is the archival admission invariant: archive-before-query is observed by the query, while archive-after-registration sees the token' -- an ordering guarantee between two operations, not a state property of one value." + entry_class: FACT + evidence: + - "crates/buzz-relay/src/state.rs:185-188" + - statement: "buzz-db's push.rs documents disable_endpoint_generation with: 'Strict generation monotonicity is the underlying safety invariant. The current-generation predicate makes stale responses clean no-ops' -- enforced by a WHERE-clause predicate comparing the caller's generation against the stored one, not by a type or a test visible at this citation." + entry_class: FACT + evidence: + - "crates/buzz-db/src/push.rs:1195-1198" + - statement: "buzz-acp's pool.rs documents send_prompt_result with: 'Clearing steer_rx here -- rather than per-arm -- makes the install_steer_rx invariant (steer_rx.is_none() at dispatch) structurally unviolatable: a receiver installed for a turn that ends before the read loop's take() ... is always dropped before the agent re-enters the pool, so the next dispatch can never trigger the assert' -- an invariant enforced by concentrating the clearing logic in one shared return path rather than by the type system, backed by a runtime assert as a second line of defense." + entry_class: FACT + evidence: + - "crates/buzz-acp/src/pool.rs:1434-1446" + - statement: "Bertrand Meyer's own page, hosted at ETH Zurich, states that Design by Contract's basic constructs are 'Routine precondition', 'Routine postcondition' and 'Class invariant', tightly integrated with the Eiffel language, and that under inheritance a precondition can only be weakened (require else) while a postcondition can only be strengthened (ensure then)." + entry_class: FACT + evidence: + - "https://se.inf.ethz.ch/~meyer/publications/online/eiffel/basic.html" + - statement: "Eiffel Software's own definitional page states: 'Design by Contract(TM) is a method for building software in which the expected behavior of every component is explicitly defined, checked, and enforced', and defines a precondition as 'what must be true before a component is used', a postcondition as 'what the component guarantees after execution', and an invariant as 'conditions that must always remain true for the system to be valid'." + entry_class: FACT + evidence: + - "https://www.eiffel.com/values/design-by-contract/" + - statement: "Neither of the two web sources above is a citation this repository's checker can open or pin -- both are external, non-GitHub URLs, so a citation to either resolves UNVERIFIED under validate.py, per AGENTS.md's own citation-shape table. They were nonetheless fetched and read directly this session, not copied from memory or a secondary summary." + entry_class: FACT + evidence: + - "launchpad/docs/corpus/AGENTS.md" + - statement: "Grepping the unmerged research note launchpad/Research/project-documentation-templates.md (branch docs/research-project-doc-templates, PR #1466) for 'invariant', 'design by contract', 'precondition', 'postcondition', 'eiffel' and 'bertrand meyer' returns zero matches, confirming it does not cover this template's subject at all." + entry_class: FACT + evidence: + - "grep_repo(pattern='invariant|design by contract|precondition|postcondition|eiffel|bertrand meyer', ref='origin/docs/research-project-doc-templates', path='launchpad/Research/project-documentation-templates.md', case_insensitive=true) -> zero matches, verified 2026-08-27 against commit a44cf52fc740ebebbdd671427480d14f0bce0115" + - statement: "Issue #1320 (PR #1518, unmerged branch task/1320-corpus-standard-normative-language, not present in this worktree's filesystem) proposes launchpad/docs/corpus/standards/normative-language.md, which states it 'governs how MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are spelled, capitalized and used to phrase a requirement inside a hand-authored corpus node ... or decision record' -- a rule about the wording of corpus prose itself, not a claim about Buzz's own runtime behavior." + entry_class: TEAM_KNOWLEDGE + provided_by: "launchpad-26/buzz#1320, PR #1518 (unmerged branch task/1320-corpus-standard-normative-language)" + - statement: "Issue #1344 exists, titled 'task: define the policy corpus template', targeting launchpad/docs/corpus/templates/policy.md as its own Objective; it is not in this batch and templates/policy.md does not exist yet anywhere in this repository." + entry_class: TEAM_KNOWLEDGE + provided_by: "launchpad-26/buzz#1344" + - statement: "Reasoning from Meyer's and Eiffel Software's definitions above (an invariant as a state property true of every instance/for the system to be valid) against the ordinary meaning of 'policy' (a rule governing what participants do), an invariant node and a policy node make categorically different claims: an invariant is falsifiable by inspecting the system's state or behavior directly, while a policy is falsifiable only by inspecting whether participants complied with it. tenant.rs's fence is the sharpest test case available in this repository: it reads almost like a policy ('never accept a client-supplied community') but AGENTS.md's actual text states it as a property of which CommunityId values can come to exist, not as an instruction to a developer -- which is why it is evidence for this node's boundary rather than for a policy template." + entry_class: INFERENCE + evidence: + - "crates/buzz-core/src/tenant.rs:1-25" + - "https://www.eiffel.com/values/design-by-contract/" + confidence: 0.6 +--- + +# Template: invariant + +How to write a corpus node documenting one **invariant** -- a condition about this +system's state or behavior that always holds (or is claimed to), independent of +whether any corpus document ever states it. This template adapts vocabulary from +Bertrand Meyer's Design by Contract rather than adopting its full mechanism; +*Industry model adapted* below explains what is kept and what is reshaped. This is +a template node, not a policy node -- it prescribes the shape of a future +document's *body*, not a MUST/SHOULD rule about corpus-wide behavior. See *Note on +Definition of Done* for why that distinction matters for this specific node. + +## Scope and authority + +**This node covers** what a corpus node's body must contain when it documents one +system invariant: the required sections, the evidence expectations for an +enforcement claim, and the industry model this template adapts (and what changes in +the adaptation). + +**It does not cover:** +- The front-matter contract itself (`node.schema.json` governs that, unconditionally, + for every node type) or how to create/update/retire a node procedurally + (`AGENTS.md` governs that). +- How MUST/SHOULD/MAY keywords are spelled and used inside corpus prose -- + `#1320`'s normative-language standard (PR #1518, unmerged) governs that, and it + is a rule about document wording, not a system property. See *Boundary* below. +- What a participant (developer, agent, reviewer) must or must not do -- a + **policy** node's territory, tracked separately by `#1344` (not yet written, not + in this batch). See *Boundary* below. +- A full interface contract's versioning/compatibility/error-semantics guarantees + -- `#1342`'s template (interface), which may `references` an invariant node this + template produces rather than restate it. + +**Its authority is derived, not original.** The structural half is already law: +`node.schema.json` enforces front matter, `validate.py` runs that schema, and CI +runs `validate.py` on every corpus change. What this node adds is the half no +schema can hold -- which sections an invariant-shaped node needs, what evidence +backs an enforcement claim, and which industry model was adapted and how. That +half is enforced by review, the same way the existing corpus standards describe +their own review-enforced half. + +| For | Read | +|---|---| +| The front-matter contract itself | `launchpad/docs/corpus/schema/node.schema.json` | +| Prose walkthrough of those fields | `launchpad/docs/corpus/schema/README.md` | +| Relationship types and their directionality | `launchpad/docs/corpus/schema/relationships.schema.json` | +| Creating, updating and retiring a node | `launchpad/docs/corpus/AGENTS.md` | +| MUST/SHOULD/MAY wording inside corpus prose | `#1320`'s standard (PR #1518, unmerged) | +| What a participant must do | `#1344`'s template (policy, not yet written) | +| An interface's operations and stability contract | `#1342`'s template (interface, PR #1541) | + +If this node and any of those disagree, **they win** -- this one has drifted and +should be fixed. + +## Industry model adapted: Design by Contract + +**What this template borrows.** Bertrand Meyer's Design by Contract names a class +invariant as one of three basic constructs, alongside a routine's precondition and +postcondition. Eiffel Software's own definitional page states an invariant as +"conditions that must always remain true for the system to be valid" -- exactly the +shape this template's *Invariant statement* required section asks an author to +write down: a condition, not a goal, not an instruction. + +**What does not transfer, and why this template reshapes it rather than adopting +it whole.** + +1. **Meyer's invariant is scoped to one class.** Every instance of that class + satisfies it, checked automatically by the Eiffel runtime on entry and exit of + every exported routine. This repository has no such language feature: nothing + in Rust, SQL or this codebase's own tooling automatically checks an arbitrary + invariant on every call boundary. The invariants actually documented in this + codebase's own comments (see the evidence ledger above) are each held by a + *different* mechanism -- a type that cannot be constructed the wrong way + (`tenant.rs`), a function whose structure makes violation unreachable + (`pool.rs`), a unit test that would fail (`hash.rs`), a `WHERE`-clause + predicate (`push.rs`), an ordering convention between two call sites + (`state.rs`), or nothing but the discipline of every caller remembering to call + the right function (`thread.rs`). **This template's *Enforcement today* required + section exists because Buzz has no uniform contract mechanism** -- an author + must say which of these tiers actually holds the invariant, rather than + implying Eiffel-style automatic enforcement that does not exist here. +2. **Meyer's invariant may be temporarily false mid-routine, restored before the + object is next visible to another object.** That "temporarily broken, always + restored before external visibility" shape recurs directly in this repository: + `thread.rs`'s two-statement update (parent's `reply_count`, then root's + `descendant_count`) is momentarily inconsistent between the two `UPDATE` + statements, and `pool.rs`'s `send_prompt_result` is written specifically so + `steer_rx` is cleared before the agent "re-enters the pool" -- i.e. before it is + next visible to another dispatch. This template keeps that shape as part of + *Scope*, below: an author states not just the invariant but the boundary across + which it must hold (every externally-visible state, not necessarily every + intermediate instruction). +3. **Precondition and postcondition are not adopted as separate required + sections.** They describe one routine's calling contract; this template's + sibling `#1342` (interface) already owns operation-level contracts. An + invariant node may be the shared premise several interfaces or operations rely + on (`tenant.rs`'s fence underpins every scoped query across `buzz-db`, + `buzz-auth`, `buzz-pubsub`, `buzz-search`, `buzz-audit` and `buzz-media`) rather + than belonging to one routine's contract, so this template asks for *Scope* + (which states/types/operations it binds) instead of a precondition/postcondition + pair. + +## A note on `type` + +Unlike `#1342`'s `interfaces-events` value, no single enum member names "documents +one invariant" the way that combined value names interface-and-event documentation. +An invariant is a property that can belong to *any* corpus surface depending on +which layer of the system it concerns: a security boundary invariant like +`tenant.rs`'s fence is architecture- or verification-shaped; a data-consistency +invariant like the thread-counter one is implementation-shaped. **A node built from +this template therefore chooses `type` by the subject matter the invariant +concerns**, using `node.schema.json`'s existing thirteen-member enum, not by a +single fixed value the way an interface-shaped node does. This template node +itself carries `type: governance` because it documents the corpus's own authoring +rules, per the precedent in the evidence ledger above, not because invariant nodes +in general use `governance`. + +## Boundary: what this template is not + +Read this section before drafting. + +- **Not `#1320`'s normative-language standard (PR #1518, unmerged).** That + document governs how the words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are + spelled and used *inside corpus prose* -- a rule about how a corpus document is + *written*. This template's subject is a property of *Buzz itself*, true or false + in production regardless of whether any corpus document ever mentions it. **The + test**: could the claim be true or false even if the corpus did not exist? Yes + -- system invariant, this template. No, because the claim is only meaningful as + a rule about how a document is phrased -- `#1320`'s territory. A node built from + this template may itself use MUST/SHOULD language when stating enforcement + expectations (as this template does, below) without becoming a normative-language + document itself; using those words is not the same as governing their usage + corpus-wide. +- **Not `#1344`'s policy template (not yet written, not in this batch).** A policy + states what a participant -- developer, agent, reviewer -- must or must not do. + An invariant states what always holds, independent of anyone's compliance. The + sharpest case in this repository's own evidence is `tenant.rs`'s fence: it reads + almost like an instruction ("never accept a client-supplied community"), but + `tenant.rs`'s own words state it as a property of which `CommunityId` values can + come to exist, not as a rule addressed to a developer. **The test**: is the + claim falsified by inspecting the system's state or behavior directly (an + invariant), or only by inspecting whether someone complied with a rule (a + policy)? A node that finds itself listing who must do what, rather than what is + true, has picked the wrong template. +- **Not `#1342`'s interface template (PR #1541).** An interface node's *Contract + and stability* section states what a caller may rely on for one boundary. An + invariant node may underpin several such contracts at once (see *Industry model + adapted* point 3) and is the deeper claim an interface's contract can point at + via `references`, rather than a contract itself. +- **Not the front-matter contract or corpus procedure.** Those are + `node.schema.json` and `AGENTS.md`'s territory, unconditionally, for every node + type including this one's own instances. + +A node built from this template that drifts into any of these neighbors has picked +the wrong template, not merely written prose that needs tightening. + +## Required sections + +A corpus node using this template must carry the following in its body, in +addition to whatever schema-required front matter `node.schema.json` demands of +every node: + +1. **Invariant statement.** One or two sentences stating the condition as a + condition -- "X always equals Y", "Z can never be observed in state W" -- not + as a goal ("X should stay consistent") and not as an instruction to a person. + If the sentence cannot be falsified by inspecting the system directly, it has + drifted into *Boundary*'s policy territory. +2. **Scope.** Which states, types, operations or call paths the invariant binds + -- every row in a table, every instance of a type after construction, every + externally-visible moment (per *Industry model adapted* point 2, an invariant + may be momentarily false between two statements of one operation without being + violated). +3. **Enforcement today.** Name which tier actually holds the invariant, honestly, + citing the code: type-system-enforced (construction is impossible any other + way), structurally enforced (the code's shape makes violation unreachable, but + nothing stops a rewrite from reintroducing it), test-enforced (a test would + fail), predicate-enforced (a query-level guard, e.g. a `WHERE` clause), or + convention-and-review only (no compiler, type or test backstop -- every caller + must simply remember). The evidence ledger above cites one worked example of + each tier in this repository. **Naming the weakest true tier is the point of + this section** -- a node that rounds "convention-and-review only" up to + "enforced" misrepresents exactly the risk this section exists to surface. +4. **Consequence of violation.** What actually breaks if the invariant fails -- + grounded in what the code, a test, or a comment says (a security boundary + crossed, data corruption, an assertion firing, a customer-visible count going + wrong), not in what the author assumes would be bad. +5. **Boundary statement.** An explicit paragraph naming what this node does not + cover, using the exclusions in *Boundary: what this template is not* above as + the checklist, plus any node-specific exclusion the author found. +6. **Relationships**, per the guidance below. +7. **Scope and omissions**, per `AGENTS.md`'s own required step 8: what the node + does not cover, who owns it, and separately, what was expected but could not be + verified when the node was written. + +### Template skeleton + +Copy this structure; the bracketed placeholders are not literal content. + +````markdown +# [Invariant name]: invariant + +[One or two sentences: the condition, stated as a condition that always holds -- +not a goal, not an instruction to a person.] + +## Scope + +[Which states/types/operations/call paths this binds, and whether it must hold at +every instant or only at every externally-visible moment.] + +## Enforcement today + +[Name the weakest true tier: type-system-enforced / structurally enforced / +test-enforced / predicate-enforced / convention-and-review only. Cite the code +that actually holds it.] + +## Consequence of violation + +[What breaks, grounded in code, a test, or a comment -- not assumed.] + +## Boundary + +This node does not describe: +- [a rule about corpus document wording -- see corpus-standard-normative-language, + if the claim drifted there] +- [a rule about what a participant must do -- see the policy node for , + if one exists] +- [an interface's full operation/versioning contract -- see the interface node + for , if one exists] +- [any node-specific exclusion] + +## Relationships + +- depends-on: +- implements: corpus-template-invariant + + +## Scope and omissions + +**This node covers** ... + +**It does not cover, and these are gaps rather than silence:** + +| Not covered here | Owned by | +|---|---| +| ... | ... | + +**Expected but not verified when this node was written:** +- ... +```` + +## Evidence expectations + +The corpus-wide evidence rules in `AGENTS.md` apply unchanged: `FACT` means the +author opened the cited source, `INFERENCE` means the author reasoned to the claim +and rated the reasoning, `TEAM_KNOWLEDGE` means an uncorroborated statement +attributed to whoever said it. Nothing about this template relaxes or narrows +that. Three expectations follow specifically from the industry model adapted +above: + +- **An *Enforcement today* claim is a `FACT` only if the cited source actually + enforces the invariant, not merely mentions it.** A code comment saying + "invariant: X" is evidence that the invariant is *intended* -- it is not, by + itself, evidence that anything *enforces* X. `hash.rs`'s worked example in the + evidence ledger above is the strong case: the comment names the invariant and a + test asserts it. `thread.rs`'s is the honest weak case: the comment states the + rule, and no type or test stops a new call site from skipping it -- the node + must say "convention-and-review only," not round it up. +- **Do not imply this repository has Design by Contract's automatic checking.** + Citing Meyer's or Eiffel Software's vocabulary to describe an invariant's + *shape* is this template's own move (see *Industry model adapted*); citing it as + though Buzz enforces every invariant the way Eiffel's runtime enforces a class + invariant would misrepresent the codebase. Say which of the tiers in *Required + sections* item 3 actually applies. +- **A *Consequence of violation* claim needs the actual failure, not an assumed + one.** Cite a test that would fail, an assertion in the code, or a comment + naming the concrete harm -- not what "would probably" go wrong absent a + citation. + +## Relationships + +A node built from this template: + +- **is typically the *target*, not the source, of a `references` edge from an + interface node** (`#1342`'s template) whose *Contract and stability* section + relies on this invariant -- per `relationships.schema.json`'s stated + directionality for `references`, "source cites target as supporting context; + no ownership or currency dependency implied," and per *Boundary* above, an + invariant is "the deeper claim an interface's contract can point at." The + interface node declares the edge, not this one: the loose coupling that + direction gives is that the invariant's own claim stays accurate even if an + interface built on it is later revised, which would not hold if the coupling + ran the other way. A node built from this template does not need to declare + anything itself for this edge to exist -- it is a legitimate `referenced-by` + target once an interface node cites it. +- **may** declare `depends-on` toward another invariant node this one's own + claim requires to be true or current, per that type's directionality: "source + requires target to be true/current for source's own claims to hold." +- **may** declare `implements` toward this template node itself (target: + `corpus-template-invariant`), once this node is merged, if the author wants the + generated `implemented-by` edge -- optional either way, since a node's own + shape (Invariant statement / Scope / Enforcement today) already shows which + template it followed. **This is a corpus-graph relationship, not a claim about + code.** A statement like "tenant.rs enforces the fence invariant" belongs in the + node's own evidence ledger as a cited `FACT`, never as a `relationships` edge -- + that field's target must be another corpus node's `id`, and no corpus node for + `tenant.rs` itself exists or is proposed. +- **must**, per `AGENTS.md`'s own rule, resolve every declared target against + `origin/launchpad` (or whatever the merge-target branch is at the time), never + against the author's own worktree. + +**This node's own relationships.** Declared: none. Checked: the four nodes +present in `origin/launchpad`'s corpus tree at the recorded revision -- +`corpus-agents`, `corpus-readme`, `corpus-standard-confidence`, +`corpus-standard-decision-references` -- are all procedural/meta-documents about +the corpus itself, not invariant-shaped subject matter this template about +invariant documentation would `references`, `depends-on`, or `implements`. None +of this batch's four sibling templates (`#1329`, `#1333`, `#1334`, `#1348`), nor +the already-open `#1342` (interface, PR #1541) or `#1320` (normative-language, PR +#1518), target this node or are targeted by it, deliberately: none of them are +merged to `origin/launchpad`, and a `relationships[].target` naming an id no node +on the merge-target branch carries is a hard validation error there even if it +resolves in a local worktree. The first invariant-shaped instance node is the +natural moment to add a `references` or `implements` edge back to this template, +once it exists. + +## Note on Definition of Done + +Issue `#1343`'s own Definition of Done carries the same four bullets found copied +across `#1326`-`#1351` -- "states scope and authority/source of the policy," +"separates MUST requirements from SHOULD guidance," "defines enforcement/checks +and exception/escalation process," "links decisions or higher-order policy +instead of duplicating them" -- verbatim from the standards-track issues that +produced `standards/confidence.md` and `standards/decision-references.md`. Those +describe a **policy/standard** node (a MUST/SHOULD normative document over +existing corpus behavior); this node is a **template** (a prescription for the +shape of a future document's body, describing a *system* property, not a corpus +authoring rule). The real acceptance criterion, from parent Feature `#605` +itself, is: *"every template states its purpose, required sections, evidence +expectations and the industry model/standard it adapts."* This node is built +against that sentence -- *Required sections*, *Evidence expectations* and +*Industry model adapted* above answer it directly -- rather than against the +standards-track checklist, which does not fit a document with no MUST/SHOULD +normative claims about existing system behavior to separate. (This template does +use MUST language in *Required sections* and *Evidence expectations* to state +what an author must do to use the template correctly -- that is ordinary +technical writing, not this node claiming to be `#1320`'s normative-language +standard; see *Boundary*, above.) + +## Scope and omissions + +**This node covers** what a corpus node's body must contain when it documents one +system invariant: the required sections, the evidence expectations for an +enforcement or consequence claim, the industry model adapted (Design by Contract's +invariant/precondition/postcondition vocabulary) and what changes in the +adaptation, the note that `type` tracks subject matter rather than a fixed value, +the explicit boundary against the normative-language standard and the +not-yet-existing policy and interface templates, and the relationship types a node +built from this template should use. + +**It does not cover, and these are gaps rather than silence:** + +| Not covered here | Owned by | +|---|---| +| MUST/SHOULD/MAY wording inside corpus prose | `#1320` (normative-language standard, PR #1518, unmerged) | +| What a participant must or must not do | `#1344` (policy template, not yet written) | +| An interface's operations and versioning/compatibility contract | `#1342` (interface template, PR #1541) | +| A single Nostr event kind's own wire contract | `#1337` (event-kind template) | +| The front-matter contract itself | `node.schema.json` | +| Creating, updating and retiring a node procedurally | `AGENTS.md` | + +**Expected but not verified when this node was written:** +- **No corpus node instance has yet been drafted from this template.** Every + required section and the skeleton above is validated only against this + repository's own code comments naming invariants (`tenant.rs`, `thread.rs`, + `hash.rs`, `state.rs`, `push.rs`, `pool.rs`) and against Design by Contract's + own vocabulary, not against a real instance node passing `validate.py` end to + end. The first invariant node drafted from this template may surface a required + section that does not fit every invariant shape cleanly -- in particular, + whether an invariant spanning many crates (like `tenant.rs`'s fence, which + affects `buzz-db`, `buzz-auth`, `buzz-pubsub`, `buzz-search`, `buzz-audit` and + `buzz-media`) should be one node or whether `AGENTS.md`'s "one node is one + independently maintainable idea" rule would eventually split it per-layer is + not settled here. +- **Bertrand Meyer's original 1992 IEEE Computer paper, "Applying Design by + Contract," was not read as text.** This environment has no PDF-to-text tool + (`pdftotext`/`poppler-utils` absent, no `pypdf`/`PyPDF2` importable, and + automated web-fetch summarization of the PDF returned only a binary-content + notice, not extracted text). The two sources actually cited above -- Meyer's + own ETH Zurich-hosted HTML page and Eiffel Software's own definitional page -- + were fetched and read directly, and are primary in the sense of being Meyer's + and his company's own words, but the paper most commonly cited as the origin of + this vocabulary was not independently verified here. +- **Whether `implements` (this template's choice, matching `#1342`'s) or + `references` is the corpus-wide convention for a node's optional self-link to + its own template is not settled anywhere outside this node's own reasoning,** + same open question `#1342` already noted for itself. +- **The INFERENCE distinguishing an invariant from a policy (evidence ledger, + final entry) has not been tested against an actual policy-template instance,** + since `#1344` has not been written. Whether every invariant/policy pair in this + repository sorts as cleanly as `tenant.rs`'s fence did is unverified. diff --git a/launchpad/plans/2026-08-27-issue-1343-corpus-template-invariant.md b/launchpad/plans/2026-08-27-issue-1343-corpus-template-invariant.md new file mode 100644 index 00000000000..d5001039d94 --- /dev/null +++ b/launchpad/plans/2026-08-27-issue-1343-corpus-template-invariant.md @@ -0,0 +1,199 @@ +Issue #1343 — task: define the invariant corpus template +Stated size: no `Size` line on the issue -> cap: 12 steps (treated as "more than +an hour" per the skill default; this is a single-documentation-file task with an +unusually deep evidence-ledger requirement — see batch precedent PR #1541, 463 +lines — so the cap is generous rather than binding). + +ALREADY TRUE (verified against git and the worktree, not notes) + - Worktree exists at __worktrees/task-1343-corpus-template-invariant, branch + task/1343-corpus-template-invariant, HEAD == origin/launchpad == + a44cf52fc740ebebbdd671427480d14f0bce0115 (git rev-parse confirms both). + - launchpad/docs/corpus/templates/ does not exist yet in this worktree or on + origin/launchpad (git ls-tree -r --name-only origin/launchpad -- launchpad/docs/corpus + lists AGENTS.md, README.md, schema/**, standards/confidence.md, + standards/decision-references.md only — no templates/ subtree). + - Sibling PR #1541 (task/1342-corpus-template-interface) already created + launchpad/docs/corpus/templates/interface.md at this exact base commit, + open and non-draft, not merged. It is the direct structural precedent for + "template" node shape (evidence ledger + Scope and authority + industry + models considered + boundary + required-sections skeleton + evidence + expectations + relationships + Note on Definition of Done + Scope and + omissions). Read via `gh pr diff 1541 --repo launchpad-26/buzz`. + - node.schema.json, AGENTS.md, schema/README.md, standards/confidence.md + already read in full this session — front-matter contract, evidence-class + rules, and one worked standards-track node are known, not assumed. + - Repo-native invariant examples confirmed present by grep (not yet read at + exact line numbers): crates/buzz-core/src/tenant.rs, crates/buzz-db/src/thread.rs:271,311, + crates/buzz-audit/src/hash.rs:203, crates/buzz-relay/src/state.rs:187, + crates/buzz-db/src/push.rs:1197, crates/buzz-acp/src/pool.rs:1441. + - Design-by-contract web sources fetched this session: se.inf.ethz.ch (Meyer's + own ETH Zurich page, names the three DbC constructs) and eiffel.com (Eiffel + Software's own DbC/precondition/postcondition/invariant definitions). Meyer's + original 1992 IEEE Computer paper could NOT be extracted as text in this + environment (no pdftotext/poppler-utils, no pypdf, WebFetch returned only + binary-PDF summaries) — this is a real, standing gap, not something to + paper over. + - PR #1518 (normative-language standard, issue #1320) diff already read in + full — gives the exact boundary text needed to distinguish an invariant + (a property of the system) from that standard (a rule about MUST/SHOULD/MAY + wording in corpus prose itself). + +STEP 1 [independent] Read the six repo-native invariant sites at their exact + lines and record each one's file:line-range citation and a one-sentence + paraphrase. + done when: a scratch note (scratchpad/i1343/invariant-sources.md) lists + all six with verified file:line ranges pulled from the actual file + content (not the earlier grep's approximate line numbers), plus at + least one additional candidate found by re-grepping + `grep -rn "invariant" crates/ -i` for anything stronger that the first + pass missed. + +STEP 2 [independent] Confirm the relationships question: enumerate + origin/launchpad's corpus tree fresh (git ls-tree -r --name-only + origin/launchpad -- launchpad/docs/corpus) and decide, in writing, + whether any of the four existing nodes is a legitimate `references` + target for an invariant template (precedent: PR #1541 declared none, + reasoning that all four are meta/procedural). + done when: scratchpad/i1343/relationships-decision.md states the + conclusion and the one-line reason, matching or explicitly diverging + from PR #1541's reasoning. + +STEP 3 [needs 1, 2] Draft the front matter (id: corpus-template-invariant, + type: governance, status: active, origin: launchpad, audiences: + agent/developer/reviewer) and the full evidence ledger, following + node.schema.json's FACT/INFERENCE/TEAM_KNOWLEDGE rules, citing: commit + a44cf52f (provenance), node.schema.json's type enum (no template/policy + value — governance is the closest fit, matching all four merged nodes + + PR #1541's template), the six repo-native invariant citations from + Step 1, the two DbC web sources, PR #1518/#1320's boundary language, + and issue #1343's own stale-DoD boilerplate (as TEAM_KNOWLEDGE, per PR + #1541's "Note on Definition of Done" pattern). + done when: the evidence array is written to + scratchpad/i1343/frontmatter-draft.yaml (or inline in a working copy of + the target file) and every FACT/INFERENCE entry's cited source has + actually been opened this session (not assumed from Step 1's notes). + +STEP 4 [needs 3] ← RUNS HERE. Write the body: title, "Scope and authority", "Industry model adapted: + Design by Contract" (precondition/postcondition/class invariant, + adapted rather than adopted wholesale — note Meyer's constructs apply + to one class/routine, while a corpus invariant node may span multiple + Rust types/modules, so state what's kept and what's reshaped), "A note + on `type`", "Boundary: what this template is not" (vs #1320's + normative-language standard: a rule about corpus MUST/SHOULD wording, + not a system property; vs a not-yet-existing policy template: an + invariant states what always holds, a policy states what participants + must do), "Required sections" with a fenced template skeleton (must + include: the invariant statement itself stated as a condition, not a + goal; scope — which states/operations/types it binds; how it is + enforced today — code/type-system/test/review, distinguished from + "documented but not enforced"; consequence of violation; a boundary + paragraph), "Evidence expectations" (an invariant claim is a FACT only + if the cited code/test actually enforces it, not merely mentions it — + mirror interface.md's "an operation-table row is a FACT or nothing" + pattern), "Relationships" guidance, "Note on Definition of Done" + (reusing PR #1541's exact reasoning pattern, re-pointed at #1343), + "Scope and omissions" with the not-covered table and an honest + "Expected but not verified" list that names the Meyer-paper gap from + Step 1. + done when: launchpad/docs/corpus/templates/invariant.md exists with + complete front matter and body; this is the first point at which + anything runs/validates. + +STEP 5 [needs 4] Run the schema tests and the corpus validator; fix anything + either one reports. + done when: + `python3 -m unittest discover -s launchpad/docs/corpus/schema/tests -p "test_*.py" -v` + exits 0 (unpiped) AND `python3 launchpad/project-intelligence/corpus/validate.py` + exits 0, run from the repo root inside the worktree. + +STEP 6 [needs 5] review-plan pass over this plan itself (adversarial: does the + plan's step order actually produce a schema-valid, evidence-honest + node, or does it defer a load-bearing decision to "later" without + saying so). + done when: findings are either resolved with a plan edit or explicitly + deferred with a stated reason. + +STEP 7 [needs 5] build-change execution is Steps 1-5 above, already covered; + this step is the review-code pass on the finished file: check every + FACT cites a source that was actually opened, every INFERENCE carries + a confidence and visible reasoning, no relationship targets an + unmerged/sibling node, and the file does not silently duplicate + content that AGENTS.md/node.schema.json already own. + done when: findings list produced; Blockers fixed in the file itself + before Step 8. + +STEP 8 [needs 5] review-tests: N/A — this issue adds no test code, only a + documentation node validated by the existing schema tests. Skipped, + stated as a skip rather than silently omitted. + done when: this step's N/A status is recorded in the PR body's + pipeline description. + +STEP 9 [needs 7] review-adjudicate: consolidate Step 6/7 findings, re-rate + severity, decide Blocker (fix in this file) vs. escalate (new issue on + launchpad-26/buzz, linked to parent PRD #605, named in PR's + Escalations) per the brief's findings policy. Search for duplicate + escalation issues first (#1532, #1538 already known; check for others + specific to "invariant"). + done when: every finding is classified and, for Blockers, fixed and + re-validated (re-run Step 5's two commands). + +STEP 10 [needs 9] Cross-model final pass substitute: Codex is down (#1467), so + this is a same-model adversarial self-review reading the finished file + cold against parent PRD #605's acceptance sentence and against the + boundary claims in "Boundary: what this template is not" — check they + hold up against a skeptical re-read, not just against the sources + cited when written. + done when: the PR body states this substitution explicitly, per the + brief's §6 requirement. + +STEP 11 [needs 10] Commit (git commit -s, message via -F on a scratch file, no + backticks in -m), then open the draft PR from AGENT_PR_TEMPLATE.md, + filling the provenance table, a real `### Issue type` -> Task heading, + a non-empty "Not verified" naming the Meyer-paper-text gap and + anything else from Step 10, and Escalations naming any Step 9 + non-Blocker findings. + done when: `gh pr create --draft --base launchpad --head + task/1343-corpus-template-invariant --title ... -F body.md` succeeds + as a lone command and returns a PR number. + +STEP 12 [needs 11] Confirm CI: poll `gh pr checks --repo launchpad-26/buzz` + until the `check` job (Validate PR body) reports success; if it fails, + read why and fix the PR body (not the workaround), re-push, re-check. + done when: `gh pr checks --repo launchpad-26/buzz` shows the + `check` job with conclusion SUCCESS in its own output, observed + directly (not assumed from a green icon description). + +PARALLEL Steps 1 and 2 are independent of each other (different files, no + shared state) and could run as parallel subagents; every step from 3 + onward is sequential because each edits or depends on the same + target file (invariant.md) or its immediate predecessor's output. + Given the single-file, single-agent nature of this task, no step is + actually being dispatched to a subagent here — this row states what + *could* parallelize, not what will. +GATES review-plan after Step 5 (Step 6), review-code after Step 5 (Step 7), + review-tests explicitly skipped as N/A (Step 8) — a docs-only change + with no test code, review-adjudicate after Step 7 (Step 9), a + same-model cross-model-substitute final pass after Step 9 (Step 10). + `qa` explore mode does not apply: this issue has no runtime + interface to exercise — it is a Markdown document validated by a + deterministic schema checker, not a running program. +BUDGET Step 4 (writing the body) is most likely to eat the budget — matching + PR #1541's actual scope (~350 lines of body prose plus ~20 evidence + entries), the industry-model section and the required-sections + skeleton both need real drafting, not templated filler. +OPEN Whether `type: governance` is the durably correct enum value for an + invariant template specifically (vs. e.g. a future `reference` or + `verification`-shaped value if the enum grows) is not this task's to + decide — node.schema.json's COMPATIBILITY.md governs adding enum + values, and this task reuses the existing precedent (all four merged + nodes + PR #1541 use governance) rather than proposing a new one. + Whether `implements` or `references` is the corpus-wide convention + for a node's self-link back to its own template is unsettled (noted + by PR #1541 itself) and is not resolved here either. +LEFT OUT Drafting an actual instance node (e.g. documenting the thread-counter + invariant as a real corpus node using this template) — out of scope + per the issue's own "Out of scope" section ("Creating or materially + editing a second hand-authored canonical corpus document"). Deciding + whether `launchpad/decisions/` ADR authoring should itself use this + template for stating system invariants inside ADRs — not this + issue's question, and no existing ADR was found doing so.