diff --git a/AGENTS.md b/AGENTS.md index 64bc11b..ed904f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -215,7 +215,10 @@ reader can judge whether it still holds. in CI — it is a recipe a human runs — and an overclaim phrased without those totality words escapes it entirely. It raises the floor; it does not close the class. The underlying rule is the check itself: for every sentence about a gate, name the exact - comparison the code performs, and delete any part of the sentence that outruns it. + comparison the code performs, and delete any part of the sentence that outruns it — and + where the sentence says what the mechanism does *not* cover, name the axes it was checked + against and state whether that list is exhaustive, because an enumeration read as complete + guarantees the axes it omits. - **Never replace a decision procedure without accounting for its old conditions.** List what the previous prose required, then mark each one kept, moved, or deliberately dropped. A rewrite reliably preserves the condition that motivated it and silently loses diff --git a/CLAUDE.md b/CLAUDE.md index 0363e84..fbfc833 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -221,7 +221,12 @@ not all of `.context/`, which would strip the committed `codex-gate.on` adoption NO FINDINGS ``` - Each pass: validate, revise, re-run. (Large/high-risk artifact: optional focused + Each pass: validate, revise, re-run. Before each read pass, settle mechanically what + the artifact asserts and a machine can decide without side effects — cited paths, + quoted passages, stated counts, the syntax of standalone fenced blocks — because a read + pass spends expensive judgement on what a parser settles in seconds and misses it + anyway, inspecting quoted commands rather than running them, since a command quoted in + a spec may be destructive or an intentional failure. (Large/high-risk artifact: optional focused per-dimension passes on top.) - **Gate B — Code.** Tests green, before `git commit`. Tool: `mcp__codex__review` (args `instruction`, `whatWasImplemented`, `baseSha`; `reviewType: full` runs @@ -251,7 +256,11 @@ not all of `.context/`, which would strip the committed `codex-gate.on` adoption asks, nothing enforces the ask or validates the answer, and no comprehensive check covers arbitrary semantic drift. Ask anyway — in one cycle it surfaced a shipped command that would have let a one-line fix skip Gate B entirely, plus two user-facing docs - teaching a rule the same change had just narrowed. + teaching a rule the same change had just narrowed. **Name what this diff changes the + size, value or position of** — a list, a count, a version, an identifier, a cited + line — and grep for where each is described elsewhere, because asked as an open + question alone this lens missed three such statements in one cycle while being carried + with unusual force. **What counts as prose (the only Gate-B exemption).** Every staged path is explanatory documentation — `docs/**.md`, `README.md`, `MANIFEST.md` → N/A. @@ -342,7 +351,11 @@ A check need not be an automated test — where none is possible, a **named veri satisfies it and the entry says which route was taken and why. Either route owes the **counterfactual**: the observation against the prior state. An **unobservable counterfactual is a blocking evidence gap**, not a free pass — stop and surface; the human -may then lower the mode as a logged override. A fabricated test satisfies nothing. +may then lower the mode as a logged override. A fabricated test satisfies nothing. **Name +the observation that would exist if the claim were false, and confirm the wiring could +have produced it** — a check that supplies its own input, runs where the defect cannot +appear, or uses a fixture that never reaches the branch it covers reports success because +of how it was wired, not because the thing it checks succeeded. **The evidence entry lives in the commit body** (see Mechanics), carries the **story path and the named evidence but not the mode value**, and is **revalidated before every Gate-B diff --git a/docs/hardening-log.md b/docs/hardening-log.md index 4a0c1b6..1683a44 100644 --- a/docs/hardening-log.md +++ b/docs/hardening-log.md @@ -32,3 +32,7 @@ escape `\|`, one line), `source` (gate-a|gate-b|bot|manual), | 2026-07-27 | unverified-enforcement-claim | FOURTH occurrence: a methodology paragraph describing the gate hook's path matcher was corrected in four consecutive Gate-B rounds — false rationale, then a categorical claim that agent definitions and hook messages always fire Gate B, then a partial-matcher claim ("a bare `agents/` matches nothing", while non-`.md` paths reach the reminder anyway), then an enforcement blur between an advisory hook and the policy — each correction a subtler version of the last, inside a paragraph being rewritten to fix that very class | gate-b | major | P std | docs/prompt-standards.md item 11, new sub-paragraph "Cite the source; do not paraphrase it" + "when a claim about a mechanism needs a fourth correction, delete the claim rather than refine it a fifth time". Sharpens item 11 rather than adding item 13, deliberately: a new numbered item ripples the checklist count through prompt-standards, the workflow-init template and AGENTS.md invariant 11, all pinned by check 4b. COUNT, stated because the escalation depends on it: the prior lineage is 2026-07-18 (first), 2026-07-19 (second), 2026-07-25 (third, `pending`) and 2026-07-26, which RESOLVES that pending row rather than recording a new defect — so this is the fourth occurrence, not the fifth. NOT ESCALATED to a mechanical rung despite the 2026-07-26 `2 lint` row being the latest: that check guards one spelling (`Target model:` lines) and this defect is outside it — exactly the over-escalation the 2026-07-26 row warns about by name. NO CURRENT DETERMINISTIC RUNG covers this paraphrase shape — the one candidate evaluated, a grep for the matcher's terms, fires on the matcher and the policy file themselves; that rules out the naive check, not every conceivable one, and the recurrence count here is a signal a human reads rather than anything that blocks a commit. The fix that finally held was DELETION — the enumeration was replaced by a pointer to the policy file; the same shape closed a second instance in the same cycle, where three successive `git status` recipes were replaced by naming the paths you edited | | 2026-07-27 | rewrite-drops-prior-condition | NEW CLASS, minted this change: ten instances in one cycle where a multi-condition rule was rewritten, the new condition survived and an old one vanished with the prose it replaced — `process-pr-review` losing the no-story and all-stories branches, then losing fix-triviality so an eligible profile alone authorized a Gate-B skip (a gate-off path invented by the change that exists to close one); a spec surfaces sentence describing the skip as keyed "on the story profile, not on fix size"; story AC 6 dropping triviality in the very edit that added effective-level eligibility; and the §5 authority leaving triviality implicit inside the phrase "triviality skip", which is what every downstream summary copied and then lost | gate-b | major | 1 prose | AGENTS.md Don'ts, "Never replace a decision procedure without accounting for its old conditions" — list what the previous prose required and mark each kept, moved, or deliberately dropped; also check the paragraphs around any criterion you amend, since amending one does not touch the prose that motivated it. Class added to docs/hardening-taxonomy.md in this same change, with its boundary against `docs-drift` stated (drift is two artifacts disagreeing and is fixed by updating the stale one; this is one rewritten rule wrong on its own terms, fixed by restoring the condition). NO DETERMINISTIC RUNG: a dropped condition is textually indistinguishable from text that was never there, so no grep or checker decides it | | 2026-07-27 | docs-drift | FOURTH occurrence, different mechanism from the first three: a change invalidated sentences in files it never touched — `plugins/dev-workflow/commands/process-pr-review.md` (a SHIPPED command still skipping Gate B on fix size alone, which would have let a one-line fix on a security-relevant story bypass the narrowed rule entirely), `docs/getting-started.md` twice, and `docs/coding-workflow.md`. Every one was correct before the change and false after it | gate-b | major | P std | CLAUDE.md §5 Gate B + the workflow-init inline template: a standing lens on every Gate-B call — "which existing statements does this diff falsify?". A TARGETED ASK found all four; the checks that ran — parity diffs, resyncs, greps of the edited paths — are scoped to files the change touched and do not look where these live, since those files were correct until the change landed elsewhere and nothing in the diff points at them. WHAT THIS RUNG DOES NOT DO, stated because the lens is the whole hardening: it is prompt text, nothing enforces that a reviewer asks it or checks the answer, and it guarantees no discovery. A narrow deterministic check could pin one stale spelling the way check 4b pins one count spelling; what does not exist is a comprehensive check for arbitrary semantic drift, which is why the rung is P and not 2. NOT ESCALATED past the 2026-07-26 `2 lint` row for the same reason as the sibling row above: that check guards prose count claims, and this defect is outside its spelling — the over-escalation the row warns about. Escalation from the 2026-07-18 `P std` row (spec-updated-with-fix) is real but narrow: that rule reaches the spec of the change being made, not arbitrary prose elsewhere in the repo | +| 2026-08-04 | docs-drift | fifth occurrence: three statements the 0.8.0 diff falsified — AGENTS.md's prerequisites list omitting `dash` while the battery invokes it twice, a plan's "four script runs" for a block holding five, and a spec citing a hook line the same rewrite had moved — all found by a PR bot, none by the standing Gate-B lens | bot | major | P std | CLAUDE.md §5 Gate-B lens + the same block in the workflow-init inline template: the lens now asks what the diff changes the size, value or position of, and to grep for where each is described elsewhere. PRIOR ROW: 2026-07-27 docs-drift (P std), guard "which existing statements does this diff falsify?" — this finding is INSIDE that guard, so it is a regression repaired at the same rung, not an escalation: the lens was carried on the 0.8.0 cycle (the execution notes record it under "Standing lens, with unusual force") and missed all three. WHAT CHANGED AND WHY THE OLD FORM MISSED IT: the open question asks for recall; all three cases are a fact recorded elsewhere that the diff changed — a list's membership, a count, a position — and enumerating those needs a search. STILL INSTRUCTION-BACKED: nothing runs the grep or validates the answer, so this raises the floor and does not close the class | +| 2026-08-04 | unverified-enforcement-claim | fifth occurrence: "It bounds the **scan**, not memory" named one uncovered axis and left the reader to infer the others were covered — the bound was a size bound, the work was quadratic in size, and the runtime it was read as bounding held only for the shapes that had been measured | gate-a | major | 1 prose | AGENTS.md Don't "Never describe what a gate proves without checking what it actually compares", closing rule extended: where a sentence says what a mechanism does NOT cover, name the axes it was checked against and state whether that list is exhaustive. PRIOR ROW: 2026-07-19 (1 prose), the same Don't, whose operative instruction covers "every sentence about a gate" — INSIDE it, so this is a regression, and the amendment closes a gap the comparison-focused wording left open. GUARD, exactly: the exhaustiveness statement. The motivating sentence names two axes and gives each a verdict, so a rule asking only for the axes checked would approve it; declaring the list exhaustive or not is what it never does. NOT LOGGED HERE: PR #21's C4 and C5 fall inside the same Don't but need nothing it does not already say, so no repair exists to name — they are parked in todos.md as a compliance recurrence with their own trigger. STILL INSTRUCTION-BACKED: no checker reads a coverage claim | +| 2026-08-04 | verification-masks-failure | second occurrence, four cases in one cycle: a dry run that defined `$EVIDENCE` itself, proving the git mechanics and never that the plan defines the variable; a regression test that only ever ran under macOS `sh` while the defect it guards appears under `dash`; timed rows whose single-record fixtures never reach the record accumulator's quadratic path; and release evidence claiming `dash` coverage from a run that executed the harness under `dash` and the hook under `/bin/sh` | gate-b | major | P std | CLAUDE.md §5 Profiles counterfactual + the same block in the workflow-init inline template: name the observation that would exist if the claim were false, and confirm the wiring could have produced it. GUARD, exactly: the second half — a check that supplies its own input, runs where the defect cannot appear, or uses a fixture that never reaches the branch it covers. Each of the four cases fails on it. PRIOR ROW: 2026-07-20 (1 prose), whose ref states its own scope — "nothing checks new plans for the same shape" — so all four are OUTSIDE it and this is the fitting rung rather than an escalation on the count. RUNG P NOT 1: the artifact is a prompt; the rung follows the artifact, not the count. SOURCE: two gate-a cases and two gate-b, so the tie-break applies — the triggering case is the `dash` release evidence, which is gate-b. STILL INSTRUCTION-BACKED: nothing tests whether a check could have failed | +| 2026-08-04 | mechanical-check-skipped-before-review | NEW CLASS, minted this change: eight read-only Gate-A passes over one plan missed eight defects that thirteen machine checks then found in a single sweep, including a rollback that would have byte-verified against the wrong hook | manual | major | P std | CLAUDE.md §5 Gate-A pass procedure + the same block in the workflow-init inline template: before each read pass, settle mechanically what the artifact asserts and a machine can decide without side effects — cited paths, quoted passages, stated counts, the syntax of standalone fenced blocks — because a read pass spends expensive judgement on what a parser settles in seconds and misses it anyway, inspecting quoted commands rather than running them, since a command quoted in a spec may be destructive or an intentional failure. Class added to docs/hardening-taxonomy.md in this same change, with its boundary against verification-masks-failure stated: there a check ran and could not fail; here the cheap check never ran at all. NO PRIOR ROW — this is the first occurrence. STILL INSTRUCTION-BACKED: nothing runs the sweep, records that it ran, or checks what it settled | diff --git a/docs/hardening-taxonomy.md b/docs/hardening-taxonomy.md index 0b26ab6..aab3168 100644 --- a/docs/hardening-taxonomy.md +++ b/docs/hardening-taxonomy.md @@ -94,6 +94,15 @@ the synonyms a future reader might search for instead. writing the thing down at all. Also not `truncated-tool-output-read-as-complete` — nothing here is truncated or misread as complete; the content was simply never durable. +- `mechanical-check-skipped-before-review` — an artifact carrying machine-checkable assertions + goes to an expensive read pass before anything parses it, so attention is spent on what a + tool decides in seconds. Aliases: `sh -n` after the fact, the parser would have caught it, + read pass before the sweep, manual review of machine-decidable claims. + + **Not `verification-masks-failure`.** There a check ran and could not fail; here the cheap + check never ran at all. Grep this one when the sentence is "a parser would have found it + immediately"; grep the other when it is "the check passed and proved nothing". + **Promotion candidate.** These classes are stack-neutral, not project vocabulary, so they belong in the `harden-finding` base list rather than here. They live here because the skill says to mint into this file (the plugin ships the base classes, the project diff --git a/docs/superpowers/plans/2026-08-04-hardening-round-0-8-0-and-pr-21.md b/docs/superpowers/plans/2026-08-04-hardening-round-0-8-0-and-pr-21.md new file mode 100644 index 0000000..0a0b658 --- /dev/null +++ b/docs/superpowers/plans/2026-08-04-hardening-round-0-8-0-and-pr-21.md @@ -0,0 +1,1196 @@ +# Hardening round — the 0.8.0 cycle and PR #21 — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Land four hardenings from the 0.8.0 cycle and PR #21 as text, record them in four ledger rows, park what has no repair, and open five split stories — without changing any skill file. + +**Amended at Gate-B pass 1.** The round opened four stories and made seven `todos.md` row changes as planned; a fifth story and an eighth row change were added during the Gate-B loop, when both reviewers found that the four appended ledger rows cross P8's 20-row threshold (18 → 22) and D3 makes a trigger this round fires a round deliverable. The counts below are the amended ones. + +**Architecture:** Three one-sentence additions to `CLAUDE.md` §5, each mirrored into the inline template in `plugins/dev-workflow/commands/workflow-init.md`; one clause appended to an existing `AGENTS.md` Don't; one new taxonomy class; four appended ledger rows; eight `todos.md` row changes; five new story files. No executable code changes. + +**Tech Stack:** Markdown prompts. The quality battery in `AGENTS.md § Commands` is the test cycle — there is no unit test for prose. + +**Spec:** `docs/superpowers/specs/2026-08-03-hardening-round-0-8-0-and-pr-21-design.md` (`b9111a2`). + +**Story:** `docs/superpowers/stories/2026-08-03-hardening-round-0-8-0-and-pr-21-story.md` — read its profile header fresh at every Gate-B pass. + +## How verification works in this plan + +Most steps end in a **must-be-true** line rather than a shell block. That is deliberate. The +artifacts here are prose, the battery already checks what a machine can check, and Gate B +reviews the diff that actually resulted — a bespoke verification script per step is a second +implementation that needs its own review, and in this plan's own history that scaffolding +produced more defects than it caught. + +**Four executable checks** are kept, because each is load-bearing and simple enough to read at +a glance: the **branch preflight**, the **battery**, the **ledger precondition**, and the +**` +hook suite under sh: +hook suite under dash: +invariants suite: +version-bump suite: +check-invariants.sh: +claude plugin validate --strict: +derived hook-suite assertion count (the suite prints none) — capture the suite's own +exit status separately, and record a count only if that status is 0: + HOOK_SH=sh sh plugins/dev-workflow/hooks/codex-gate.test.sh > out 2>&1; echo $? + grep -c '^ok ' out + -> status , count + +## check (named verification), with its counterfactual +§5.2 applied to the four cases in spec §10 — each must FAIL on the wiring half: + 1. $EVIDENCE dry run -> + 2. single-shell regression -> + 3. timed regression row -> + 4. dash release evidence -> +§5.4 applied to "It bounds the scan, not memory" — must FAIL on exhaustiveness: +counterfactual: all four were reviewed during the 0.8.0 cycle under the §5 text as it +read before this change and each was accepted by at least one review looking for exactly +this; one (the single-shell test) reached the released artifact. + +## prompt conformance +workflow-init.md (invariant 11 surface) — all 12 items: +CLAUDE.md, AGENTS.md (not on invariant 11's list) — items 6,7,8,9,11,12: +five split stories — in-spirit brief review: + +## mirror parity +each §5 sentence appears once in CLAUDE.md and once in workflow-init.md: + +## cross-finding conflict check +Task 0 Step 2 verdict: +``` + +- [ ] **Step 2: Run the battery (KEPT CHECK) and record it** + +Run the quality command from Global Constraints. Record its exit status and each suite's +terminal line verbatim in the evidence file. **Do not write a number you did not read from +output.** The hook suite prints no total, so a count must be derived — and the derivation must +capture the suite's own exit status separately, because a pipeline into `grep -c` returns +grep's status and would record a partial count from a suite that died: + +```bash +cd "$(git rev-parse --show-toplevel)" || exit 1 +out=$(mktemp) || exit 1 +HOOK_SH=sh sh plugins/dev-workflow/hooks/codex-gate.test.sh > "$out" 2>&1 +status=$? +count=$(grep -c '^ok ' "$out" || true) +rm -f "$out" +echo "hook suite (sh): status=$status derived-ok-count=$count" +[ "$status" = "0" ] || { echo "suite failed — the count is not evidence"; exit 1; } +exit 0 +``` + +`mktemp` rather than a fixed path: a literal `out` in the repo root would collide with a user's +file and leave an untracked artifact that the clean-tree precondition then trips over. + +- [ ] **Step 3: Run the self-tests and record them (RECORDED READING, not an executable check)** + +Apply §5.2's sentence, exactly as it now reads in `CLAUDE.md`, to the four cases in spec §10 — +each must **fail** on the second half. Apply §5.4's appended clause to `It bounds the **scan**, +not memory` — it must fail on exhaustiveness. Record all five verdicts with one line of reason +each. + +There is no shell block here on purpose. Deciding whether a prompt sentence rejects a case is a +reading; a script asserting it would be asserting its own author's opinion. + +If any case passes the sentence written to reject it, that sentence is miswired: **stop and +surface**, do not proceed to Gate B. + +- [ ] **Step 4: Record prompt conformance and mirror parity** + +`workflow-init.md` is the only changed file inside invariant 11's enumerated surface — all 12 +items must pass, with an exception only where the checklist item itself authorizes one (items 9 +and 12 do). `CLAUDE.md` and `AGENTS.md` are not on that list and carry no `Target model:` line; +review their edits against items 6, 7, 8, 9, 11 and 12. §5.4's clause continues an existing +prohibition, so cite item 9's own exemption rather than assuming it. Give each split story an +in-spirit brief review. Record all of it, plus the mirror-parity result. + +- [ ] **Step 5: Close the cycle — `CLAUDE.md` §5 governs** + +**The squash, the Gate-B loop, the amend and the close follow `CLAUDE.md` §5 and its Mechanics +section. This plan does not restate that protocol.** A draft did, and the restatement dropped a +step — which is why §5 is cited rather than paraphrased. + +Two things this plan adds, neither of them a re-specification: + +1. **The step the restatement dropped, named so it is not dropped again:** §5 requires that + **every Gate-B fix is amended into the WIP commit before the next review call.** + `mcp__codex__review` reads a **git range**; a fix that is only staged is not in that range, + so the next pass would re-review the pre-fix commit and report clean on unreviewed content. +2. **What Gate B carries from this round:** `reviewType: full`; the story path + `docs/superpowers/stories/2026-08-03-hardening-round-0-8-0-and-pr-21-story.md`; the evidence + file's content verbatim in `additionalContext`; and the standing lens. + +**Must be true at the close:** the working tree is clean, every reviewed fix is in the commit +being amended, and the closing message carries the evidence body. + +- [ ] **Step 6: The ` **Name what this diff changes the size, value or position of** — a list, a count, a version, +> an identifier, a cited line — and grep for where each is described elsewhere, because asked +> as an open question alone this lens missed three such statements in one cycle while being +> carried with unusual force. + +**Diagnosis this rests on.** The lens was carried on the 0.8.0 cycle — the execution notes +record it under the heading "Standing lens, with unusual force" — and still missed C1–C3. The +three share one shape, a fact recorded elsewhere that the diff changed, and differ in which +axis moved: C1 a list's **membership**, C2 a **count**, C3 a **position**. The open question +asks for recall; enumerating needs a search. + +**What this does not do.** It adds no mechanical component. Nothing runs the grep or validates +the answer. + +### 5.2 §5 Profiles counterfactual — F2 (evidence half), F3, F4, F6 + +Site: the `CLAUDE.md` §5 Profiles paragraph containing the sentence `Either route owes the +**counterfactual**: the observation against the prior state.` Appended: + +> **Name the observation that would exist if the claim were false, and confirm the wiring could +> have produced it** — a check that supplies its own input, runs where the defect cannot appear, +> or uses a fixture that never reaches the branch it covers reports success because of how it +> was wired, not because the thing it checks succeeded. + +### 5.3 §5 Gate A pass procedure — F5 + +Site: the `CLAUDE.md` §5 Gate A bullet, after the sentence "Each pass: validate, revise, +re-run." Added: + +> Before each read pass, settle mechanically what the artifact asserts and a machine can decide +> without side effects — cited paths, quoted passages, stated counts, the syntax of standalone +> fenced blocks — because a read pass spends expensive judgement on what a parser settles in +> seconds and misses it anyway, inspecting quoted commands rather than running them, since a +> command quoted in a spec may be destructive or an intentional failure. + +(The "because" clause was added at Gate-B pass 3: the sentence carried a reason for inspecting +quoted commands rather than running them, and none for the sweep itself, which +`docs/prompt-standards.md` item 6 requires of every constraint.) + +### 5.4 The 2026-07-19 `AGENTS.md` Don't, amended — F2 (claim half), F8 + +Site: the Don't opening **"Never describe what a gate proves without checking what it actually +compares."** Its closing rule currently reads "for every sentence about a gate, name the exact +comparison the code performs, and delete any part of the sentence that outruns it." Appended to +that sentence: + +> — and where the sentence says what the mechanism does *not* cover, name the axes it was +> checked against and state whether that list is exhaustive, because an enumeration read as +> complete guarantees the axes it omits. + +**Why this sentence and not a looser one.** It has to reject its own motivating case. "It bounds +the **scan**, not memory" names two axes and gives each a coverage verdict, so any rule asking +only for the axes checked would approve it. What that sentence never does is declare its list +exhaustive or not — so the exhaustiveness statement is the operative requirement, and F8 fails +it. + +**This does not ship downstream.** `AGENTS.md` is repo-local; `/workflow-init` scaffolds a +project's own. + +## 6. The ledger rows + +One row per landed hardening: four rows for four hardenings. Each row's `finding` carries its +motivating cases as worked examples, and for row C each case answers both halves of the §5.2 +sentence. + +| Row | Hardening | Class | Occ | Cases | `source` | `severity` | Rung | +|---|---|---|---|---|---|---|---| +| A | §5.1 lens strengthening | `docs-drift` | 5 | C1, C2, C3 | `bot` | `major` | `P std` | +| B | §5.4 Don't amendment | `unverified-enforcement-claim` | 5 | F8 | `gate-a` | `major` | `1 prose` | +| C | §5.2 counterfactual sentence | `verification-masks-failure` | 2 | `$EVIDENCE`, single-shell test, timed row, `dash` release evidence | `gate-b` | `major` | `P std` | +| D | §5.3 Gate-A sweep sentence | `mechanical-check-skipped-before-review` | new | the eight-pass/thirteen-check sweep | `manual` | `major` | `P std` | + +Row D's class is minted in §6.5. + +**C4 and C5 are dispositioned in `todos.md`, not in a ledger row**, because no hardening is +available for them. Both fall inside the 2026-07-19 Don't, whose operative instruction already +requires exactly what they failed to do — name the exact comparison the code performs, and +delete any part of the sentence that outruns it. The rule was correct and was not followed, +twice. That is a compliance recurrence, and a ledger row would have to name a repair that does +not exist: §5.4's clause governs sentences about what a mechanism does *not* cover, so it +strengthens F8's shape and reaches neither a positive parity claim nor a positive prevention +claim. The parked row records the signal instead, with its trigger: **a third compliance miss +against that Don't, or a feasible mechanical rung emerging from the split skill story.** + +### 6.1 Precheck verdicts, recorded per case + +**Row A is inside** the 2026-07-27 lens: it was carried and missed all three cases. The row +records a regression repaired at the same rung. + +**F8, C4 and C5 are all inside the 2026-07-19 Don't.** Its operative instruction is *"for every +sentence about a gate, name the exact comparison the code performs, and delete any part of the +sentence that outruns it"* — scoped to **every sentence about a gate**, not to comparison claims +alone. + +- **F8** — "It bounds the **scan**, not memory" is a sentence about the gate's scanner whose + implication outran the length comparison the code performs. Its literal content is true; the + defect is what the enumeration implies about the axis it omits. **The scope of a claim is part + of the claim**, so an implied guarantee is still one the mechanism does not deliver. +- **C5** — the README claimed a typo cannot quietly unhook a gate, a claim about what the gate's + matching prevents, made without checking what the parser compares. +- **C4** — the jq-parity criterion claimed unqualified parity between the routing paths, + outrunning what `field()` compares for a malformed outer document. + +**They divide on whether a repair exists.** F8's shape — an enumeration read as complete — is +one the Don't did not spell out, so amending it closes a real gap, and D6 follows: strengthen +that Don't rather than add a sibling. C4 and C5 needed nothing the Don't does not already say; +they are compliance misses, and §5.4's clause does not reach a positive claim. Row B therefore +carries F8 alone, and C4/C5 are parked (§6 above, §9). + +**Row C is outside** every prior guard. The only prior `verification-masks-failure` row is +2026-07-20, whose `ref` states its own scope — "nothing checks new plans for the same shape". + +**Row D** has no prior matching row. + +### 6.2 `source` + +Row A is `bot`: C1–C3 all came from the PR bot on #21. + +Row B is `gate-a`: its single case is F8, which came from Gate A on the plan. C4 and C5 are not +row B's cases — they are parked (§9) — so they contribute no provenance to it. + +Row C's four cases split evenly: `$EVIDENCE` from Gate A on the plan (pass 9) and the +single-shell test from Gate A on the plan (recorded in +`.context/codex-reviews/gate-a-plan-pass-5-dispositions.md`); the timed row from Gate B +(`.context/codex-reviews/gate-b-quality-pass-3.md`) and the `dash` release-evidence claim from +Gate B pass 1. Two and two, so the tie-break applies: the **`dash` release-evidence case is what +triggered this hardening**, and it is Gate B. Row C is **`gate-b`**. + +Row D is `manual`: the sweep-versus-passes comparison was drawn while closing the cycle, not +raised by a gate. + +Where a row's cases differ, `source` takes the majority and the row states the mixed +provenance, following the 2026-07-18 precedent that records "Gate A as the majority and the +trigger". Where no value holds a majority, take the plurality; where the plurality is tied, +take the case that triggered the hardening. + +`severity` is `major` for all four: each let a defect through a check believed to cover it. + +### 6.3 Occurrence counting + +**An occurrence is one ledger event — a defect reaching the ledger and being hardened — not a +distinct defect and not a physical row.** A row that resolves a `pending` predecessor hardens a +defect already counted, so it adds a row without adding an occurrence; and a row may aggregate +several defects under one hardening and count once, because the ladder escalates on how many +times a class has been hardened and come back, not on how many sentences were wrong. + +By that unit `docs-drift` reaches 5 here (2026-07-18 twice, 2026-07-25 as `pending`, 2026-07-26 +resolving it, 2026-07-27 as the fourth) and `unverified-enforcement-claim` reaches 5 +(2026-07-18, 2026-07-19, 2026-07-25 as `pending`, 2026-07-26 resolving it, 2026-07-27 as the +fourth, which is the count that row states for itself). + +### 6.4 Where the audit text goes + +The precheck reading is acceptance-critical (story AC 2) and the structured format for it is +split out, so this round records it in the columns the ledger already has — no schema change. +Each of rows A–C appends, inside `ref`: the **controlling prior row** by date and fingerprint; +its **guard**, quoted or cited precisely enough to re-find; the **verdict per motivating case** +where they differ; and **why the chosen rung fits**, in one clause. Row D records that it has no +prior matching row, plus its rung rationale. + +A literal `|` inside quoted guard text is escaped `\|`, as the ledger header requires for +`finding`. Extending that rule to `ref` formally is one of the split story's open questions +(§4), so this round avoids guard quotations containing pipes rather than relying on an unstated +rule. + +**Concurrency, stated as the gap it is.** This round appends after step 7's re-read. A +same-fingerprint row visible at that read is surfaced and the round stops rather than guessing +whether the two hardenings share a scope. A row landing *after* the read is not detected — step +7 offers no observation point past it, and supplying one is the split story's subject. This +round runs single-writer and claims nothing stronger. + +### 6.5 The minted class + +`docs/hardening-taxonomy.md` gains: + +> - `mechanical-check-skipped-before-review` — an artifact carrying machine-checkable assertions +> goes to an expensive read pass before anything parses it, so attention is spent on what a +> tool decides in seconds. Aliases: `sh -n` after the fact, the parser would have caught it, +> read pass before the sweep, manual review of machine-decidable claims. + +Its boundary against `verification-masks-failure`: there, a check ran and could not fail; here, +the cheap check never ran at all. + +The class is stack-neutral rather than project vocabulary. It lands in the project file anyway, +because the skill instructs minting there and that file's closing note records the same of +several classes already in it, flagging them for promotion when the base taxonomy is next +revised. Moving it into the base list would be a skill edit, which D7 excludes. + +## 7. Story amendments + +The story carries an `**Amendment log:**` block beneath its profile header, one entry per +amendment. An entry that replaces text names the text it replaced; one that extends a section +names what was added. The story is the writable copy, and this section does not restate the +entries. + +## 8. The five split stories + +Five story files at `docs/superpowers/stories/YYYY-MM-DD--story.md`. A note or a +commit-body summary does not satisfy this: the point is an artifact that outlives the session. + +**The fifth was added during the Gate-B loop**, when both reviewers found that this round's four +appended ledger rows take the ledger from 18 to 22 and cross P8's stated 20-row threshold. D3 +covers it — the trigger fires by this round's own action, not in the backlog — so P8's row is +marked fired and its story is a round deliverable like the other four. It is recorded here +rather than only in the plan because the count in this section was falsified by the fix, which +is the drift this round hardens. + +**They are written directly rather than produced by `dev-workflow:intake`, and each file says +so.** Intake excludes "items that already have an approved story/spec or have moved into +solution design", and all five are that: three carry cut-short design analysis from earlier +rounds, the fourth (F7) is the ledger-format work this round declined to fold in and has +already scoped, and the fifth (P8) is a parked row whose analysis was settled when it was +written and whose only open condition was a sample-size threshold. Making an intake-produced artifact acceptance-critical would deadlock the round +against a skill that declines the input. + +**Each file uses the story template's six sections and its `**Date:**`/`**Size:**` header line, +and deliberately omits the profile line** — that is the only departure, and each file names it. +A profile is proposed and human-confirmed at intake time; written now it would be a +confirmed-looking value nobody confirmed, which §5 classifies as unresolvable and stops on. + +**The debt is carried as each story's first acceptance criterion**, and the criterion names the +step rather than an owner-less obligation: + +> Before design resumes on this story, whoever picks it up proposes both axes and the mode +> derived from them, pauses for Daniel's confirmation, and writes the confirmed profile into +> this header. Design continues only after that. + +"Before design *resumes*", not "begins": these stories exist because design on them was cut +short, so a criterion gated on design not having started would be unsatisfiable by construction. +Nothing else would pick the debt up — `intake` declines work already in solution design, +`brainstorming` does not assess profiles, and CLAUDE.md §5 proceeds when a cited story is +unprofiled — so a sentence merely saying the profile is owed would name no step at all, which is +the unbacked-claim shape this round hardens. + +**Each story inherits its source row's conditions, accounted for.** Four of the five replace a +parked `todos.md` row that carries settled analysis, and the `AGENTS.md` Don't "Never replace a +decision procedure without accounting for its old conditions" applies to that replacement as +much as to any rule: each story lists every condition its source row states and marks it kept, +moved, or deliberately dropped. A thinner brief that quietly discards a matured constraint is +the failure mode, and it is acceptance-critical, not advisory. + +**Paths are fixed, and collisions stop the round.** Each story's path is named below. Missing → +write it; byte-identical → reuse it and say so; present and different → stop and surface rather +than overwrite, per invariant 9's rule for scaffolded files. **Classify each path immediately +before writing it, not once up front**, and create with an operation that fails if the path +appeared meanwhile rather than one that truncates: a classification taken early and acted on +later is check-then-write, and another session can create or change the file in the gap. On any +such failure, stop and surface — the same answer as a collision seen at classification time. + +**The round is incomplete until all five exist.** If one cannot be grounded — no statable +problem, outcome, or three checkable criteria — that is a stop-and-surface: say which and why, +rather than padding a story into existence. + +1. **The `harden-finding` guard-scope precheck** → + `docs/superpowers/stories/2026-08-04-harden-finding-guard-scope-precheck-story.md`. + Opening evidence: the six items in §4. No source row is replaced; the parked scope-blind row + remains open and points here (§9). +2. **The ledger's supersession convention (F7)** → + `docs/superpowers/stories/2026-08-04-hardening-ledger-supersession-story.md`. Trigger fired: + the 2026-07-20 row now teaches pre-0.8.0 counting behaviour as current, the second falsified + row its parked trigger names. Conditions to account for, from that row: the append-only rule, + the one-row-per-hardening rule, and the version-qualified supersession note that already + worked once in the 2026-07-20 spec. Open question: which artifacts a supersession rule has to + reach before a reader can trust any row — this repo's ledger alone, or every ledger + `/workflow-init` scaffolds. +3. **A route from a fixed finding to the ledger, for projects that never open PRs (Finding A)** → + `docs/superpowers/stories/2026-08-04-ledger-route-without-pull-requests-story.md`. Trigger + fired: "the next round that touches §5". Evidence: the only mandated ledger check lives in + `process-pr-review` step 5, and one project has 51 Gate-A pass files and zero ledger rows. + Conditions to account for: the scope must match that step exactly — every accepted actionable + fixed finding checked, `harden-finding` invoked only when a class matches or a new one is + clearly warranted; it cannot rest on same-session memory, because a compaction, interruption + or handoff loses the fixed-finding set and nothing detects the loss; a durable handoff needs + real design for identity, deduplication and consumption semantics, which is why it was + refused as a mid-round addition; and `mandatory-step-anchored-to-optional-path` is minted + only when the hardening lands, since minting it earlier leaves a class no row uses. Open + question: what has to be true for a fixed finding to reach the ledger without a pull request. +4. **A §5 version stamp (Finding B)** → + `docs/superpowers/stories/2026-08-04-section-5-version-stamp-story.md`. Trigger fired: "the + next round that touches the §5 template". Conditions to account for: the semantic §5 locator + problem, per-state merge semantics under invariant 9, stamp cardinality (absent, duplicate, + malformed), that the binding must hold on every push path — the version-bump coupling is + false, since invariant 12's checker is `pull_request`-only — that a stamp is a **wire + format**, so shipping a provisional one writes legacy into every scaffolded file, and that + the one known-stale instance is being re-synced by hand, so this carries no schedule + pressure. Open question: how a scaffolded `CLAUDE.md` can tell its reader that it lags the + installed plugin. +5. **Passive metrics, read-only over the ledger and git (P8)** → + `docs/superpowers/stories/2026-08-04-passive-metrics-over-the-ledger-story.md`. **Added at + Gate-B pass 1, not at design time** — the trigger is a sample-size threshold, "10 stories or + 20 ledger rows", and this round's four appended rows cross it (18 → 22); its own fifth story + takes the story count to 10, crossing the other arm. Conditions to account for, from that + row: analysis only, with no new state file, no instrumentation and nothing written back; the + scope is questions the ledger already holds the data for — which fingerprints recur and how + often a rung holds; and the threshold itself, whose reason was that a smaller sample says + more about the last week than about the workflow. Open question: where the analysis lives — a + command, a script, or a documented reading recipe — given that "analysis only" rules out + state but not a surface. + +**Not fired:** the slot-collision row, whose trigger names the §5 **file protocol**. This round +changes no part of it — not the slot names, not the pre-call delete, not the terminator or +acceptance rules. + +## 9. Other deliverables + +- **`todos.md`, the scope-blind row** — records this round as its **third evidence case**, and + points at the split story (§8 item 1) rather than being marked resolved, because the fix it + sketched has not landed. +- **`todos.md`, the three §5-trigger rows** — Finding A and Finding B marked **fired**, each + pointing at its story path from §8; the slot-collision row records that it did **not** fire, + and why. +- **`todos.md`, the ledger-supersession row** — marked **fired** and pointed at §8 item 2's + story path. Its conditions stay in the row; only its status changes. Left unmarked, the row + reads as live work and a later session can open a second story for it. +- **`todos.md`, the `CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS` row** — **stays open; an observation is + appended.** With the variable set to `0`, Gate-A passes 2–6 of this cycle ran 458 s, 550 s, + 757 s, 663 s and 780 s, all staying in the foreground and returning ordinary `success: true` + envelopes the hook could read; pass 1 was not timed. Start and end stamps are tabulated in + `.context/codex-reviews/gate-a-spec-resume.md`. **No control run was made** with the variable + unset, so this is a correlation under one setting, not a demonstration that the variable held + those calls in the foreground; what it establishes is that calls well past 120 s can return as + ordinary foreground results here. The row stays open because its deliverable is a + `/workflow-init` preflight check, which is unbuilt. +- **`todos.md`, a new parked row for C4 and C5** — the 2026-07-19 Don't is correct and was not + followed twice; no textual repair is available, because its operative instruction already + requires what both cases omitted. *Trigger: a third compliance miss against that Don't, or a + feasible mechanical rung emerging from the split skill story (§8 item 1).* +- **`todos.md`, the P8 passive-metrics row** — marked **fired** and pointed at §8 item 5's story + path. Its trigger is a sample-size threshold, and this round's four appended rows cross it + (18 → 22). Found at Gate-B pass 1 by both reviewers, applying the standing lens this round + adds: the diff changed a count, and a statement keyed to that count sat elsewhere. +- **`docs/hardening-taxonomy.md`** — the §6.5 class. +- **`plugins/dev-workflow/.claude-plugin/plugin.json`** — `version` to `0.8.1`, required by + invariant 12. +- **`plugins/dev-workflow/CHANGELOG.md`** — a `## 0.8.1` entry, newest first. + +## 10. Validation evidence + +The mode is read from the story header at each pass. As of this writing it derives +`battery+check`. + +**battery** — the full quality command from `AGENTS.md § Commands`, green. + +**check — a named verification, with its counterfactual.** Automation is not available: the +thing under test is whether a prompt sentence rejects a case, which is a reading. + +**The four inputs, fixed here so the verification is reproducible.** + +1. **`$EVIDENCE` dry run.** Claim: the plan defines `$EVIDENCE` before three commit commands + read it. Wiring: a scratch-clone dry run that assigned `$EVIDENCE` itself before invoking the + commands. Falsifying observation: the commands failing on an unset variable — which this + wiring cannot produce, because it sets the variable the plan was supposed to set. +2. **Single-shell regression test.** Claim: the hook survives a directory at a marker path. + Wiring: a regression test invoking the hook through macOS `sh`. Falsifying observation: a + non-zero exit from the `:` special builtin, which appears under `dash` and not under macOS + `sh`, so this wiring cannot produce it. +3. **Timed regression row.** Claim: the timed rows establish the scan's runtime across the + payloads the ceiling admits. Wiring: timed fixtures built from large **single-record** + payloads. Falsifying observation: a newline-rich payload *under* the ceiling costing far more + than the measured shapes — the record accumulator is quadratic in line count, which no + single-record fixture reaches. The ceiling keeps the work finite, so the falsifying + observation is a runtime far outside the measured range, not an unbounded one. +4. **`dash` coverage in release evidence.** Claim: the suite ran under both shells. Wiring: + `dash codex-gate.test.sh` without `HOOK_SH=dash`, which runs the harness under `dash` and the + hook under `/bin/sh` — bash, on macOS. Falsifying observation: a hook-level `dash` failure, + which this wiring cannot produce because the hook never ran under `dash`. + +**The verification.** Apply §5.2's sentence, as worded, to all four. **Each must fail**, on its +second half. Separately, apply §5.4's appended clause to "It bounds the **scan**, not memory" — +**it must fail too**, on the exhaustiveness requirement. If any case passes the sentence written +to reject it, that sentence is miswired and the verification fails. + +**The counterfactual.** All four were reviewed during the 0.8.0 cycle under the §5 text as it +reads today, and each was accepted by at least one review looking for exactly this — the +`$EVIDENCE` case survived eight read passes. Of the four, one reached the released artifact: the +single-shell test, whose failure the 0.8.0 changelog records as why an invariant-1 violation +shipped. That test also predates the counterfactual text, which landed 2026-07-27 in `27d019b`, +so the current wording had no opportunity to prevent its authoring — only to catch it later, +which it did not. + +**What is deliberately not claimed.** The old text is not re-applied as a pass/fail oracle. It +asks for "the observation against the prior state" and says nothing about wiring, so applying it +yields no determinate verdict on the question the four cases turn on — which is the gap §5.2 +closes. + +**Prompt conformance.** Invariant 11 requires all 12 items of `docs/prompt-standards.md` for +"any skill, command, agent definition, hook message, or scaffolded template". Of everything this +round changes, exactly one file is inside that surface: +`plugins/dev-workflow/commands/workflow-init.md`, both a command and the carrier of the +scaffolded §5 template. It gets a recorded 12-item review in which **all 12 must pass** — an +exception only where the checklist item itself authorizes one (items 9 and 12 do), recorded with +that item's stated reason. + +`CLAUDE.md` and `AGENTS.md` are not on that list, and demanding all 12 of them is unsatisfiable +rather than strict: neither carries a `Target model:` line or an output-format example, because +neither is executed against a named model. Their edits are reviewed against the items that apply +to a rule statement — 6, 7, 8, 9, 11 and 12 — with the result recorded. §5.4's appended clause is +phrased as a continuation of an existing prohibition; item 9's own text exempts rules whose +subject is the prohibition, and that exemption is cited in the record rather than assumed. + +The five split stories are agent inputs, and `docs/prompt-standards.md` holds briefs to the +checklist in spirit. Each gets a recorded in-spirit review — success criteria, stop conditions, +verified claims — without asserting the mandatory 12 apply. + +**Mirror parity.** For each of the three §5 sentences, confirm the inserted text is identical in +`CLAUDE.md` and `workflow-init.md` and sits under the same heading in both. The battery does not +compare them, and a sentence landing in one copy only is the shipped-template drift this repo +treats as load-bearing. + +**Cross-finding conflict check** (story AC 10). Before applying any edit, check whether two +findings pull one artifact in opposite directions; if so, stop and surface rather than choosing. +Current verdict: no conflict. The four hardenings touch four distinct sites — the Gate-B lens +paragraph, the Profiles counterfactual paragraph, the Gate-A pass bullet, and the 2026-07-19 +Don't — and none rewrites text another needs. + +## 11. Gate-A riders, verbatim in every pass prompt + +1. **Sweep before reading.** Mechanically settle whatever this spec asserts that a machine can + decide without side effects — cited paths, quoted passages, counts, durations, section + numbering and internal cross-references. Inspect quoted commands rather than running them. + Several source files are hard-wrapped, so compare code byte-for-byte and prose with + whitespace normalized, and report a wrap artifact as a wrap artifact. Report what the sweep + found separately from what the read found. +2. **Two self-tests.** Apply §5.2's sentence to the four cases in §10 — each must fail. Apply + §5.4's appended clause to "It bounds the scan, not memory" — it must fail. Report both + explicitly either way. +3. **Per-case precheck audit.** §6.1 records a verdict per motivating case against the prior + rows it names. Check each verdict against the guard text it cites. + +## 12. What this round does not do + +- It adds no mechanical check. Every rung is `P std` or `1 prose`; a reader is the detection. +- It does not claim the strengthened lens catches the class. It sharpens one question and names + a search; nothing runs the search. +- It changes no skill file. The guard-scope precheck, and every determinism question raised + about it, belong to the split story (§4). +- It does not resolve the ledger's supersession gap, the no-PR ledger route, or the §5 version + stamp. §8 writes the stories; until they land, the 2026-07-20 row still reads as current. +- It does not detect a same-fingerprint row landing after step 7's re-read (§6.4). +- §5.4's amendment reaches this repository only. Downstream projects inherit the three §5 + sentences, not the `AGENTS.md` Don't. diff --git a/docs/superpowers/stories/2026-08-03-hardening-round-0-8-0-and-pr-21-story.md b/docs/superpowers/stories/2026-08-03-hardening-round-0-8-0-and-pr-21-story.md new file mode 100644 index 0000000..34a2209 --- /dev/null +++ b/docs/superpowers/stories/2026-08-03-hardening-round-0-8-0-and-pr-21-story.md @@ -0,0 +1,217 @@ +# Hardening round — the 0.8.0 cycle and PR #21 — Story + +**Date:** 2026-08-03 · **Size:** story +**Risk:** standard · **Security:** none · **Validation:** battery+check + +**Amendment log:** +- 2026-08-04 · AC added (the P8 criterion) · the round's acceptance criteria covered the four + stories designed up front and said nothing about a parked row whose trigger the round's own + output crosses · both Gate-B pass 1 reviewers found that the four appended ledger rows take + the ledger from 18 to 22, past P8's stated 20-row threshold, and D3 makes a trigger this round + fires a round deliverable. Without the criterion the round could satisfy every stated + criterion while omitting a deliverable D3 required, so the criterion was added rather than the + omission argued away. Raised at Gate-B pass 1; the fifth story and the eighth `todos.md` row + change landed in the same commit. +- 2026-08-03 · AC 1 amended · replaced "has exactly one recorded disposition" with the + current wording · a finding whose halves fall in different fingerprint classes needs a + disposition in each; F2's two halves are `unverified-enforcement-claim` and + `verification-masks-failure`, and forcing one row would file one of them under a class it + does not belong to. Raised at Gate-A spec pass 1. +- 2026-08-03 · AC 8 replaced · it read "Each of the three parked rows whose trigger names the + §5 template (Finding A, Finding B, and the cycle-record-destruction row) states that the + trigger reads a change to §5's substance, not any touch of it." · it now reads "Each of the + three parked rows whose trigger names §5 is read literally against its own text, and the + round records which fired and which did not, with the reason." · the old wording encoded a + misreading of those rows as regression triggers. They are opportunity triggers — "do this + while you are already in §5" — and narrowing them would be `rewrite-drops-prior-condition` + committed by a round that hardens it. The round now honours them as written: Finding A and + Finding B fire and their intakes are opened; the cycle-record row does not fire. Raised at + Gate-A spec pass 1; the entry said "removed" until Gate-A spec pass 3 caught that the story + carries a replacement. +- 2026-08-04 · AC 1 re-amended · the pass-1 amendment replaced "has exactly one recorded + disposition" with "is dispositioned", which solved F2's split by deleting uniqueness for + every finding · uniqueness is restored per class: exactly one disposition per finding, and + for a finding whose halves fall in different fingerprint classes, exactly one per half. + Raised at Gate-A spec pass 3. +- 2026-08-04 · AC 4 replaced · it required `harden-finding` to instruct the guard-scope + precheck · the skill change is split into its own story, and the criterion now requires that + story to exist carrying its named opening evidence · Gate-A spec pass 3 produced four + findings independently demanding row metadata, a ledger format change and a collision state + machine. That is the machinery exit AC 9 names, and it fired on evidence rather than on + judgement. +- 2026-08-04 · AC 7 replaced · it named one intake and specified its "central design question" + · three intakes are now required, and each criterion states problem, evidence and open + question rather than a solution · `dev-workflow:intake` forbids HOW outside its invariant + quotations, so the old wording asked for an artifact intake cannot conformantly produce. + Raised at Gate-A spec pass 3. +- 2026-08-04 · AC 9 amended · it stated the machinery exit as a conditional · it now records + that the condition fired, so the criterion is a completed action rather than a standing test. +- 2026-08-04 · §1 corrected · it said the check-supplies-its-own-input shape "has no home in + the taxonomy" · `verification-masks-failure` already covers it; what the shape lacked was a + recorded application, not a class. Raised at Gate-A spec pass 3. +- 2026-08-04 · §4 extended · invariant 12 added · the round changes paths under + `plugins/dev-workflow/`, so the manifest version gates it. Raised at Gate-A spec pass 3. +- 2026-08-04 · §1 and AC 3 corrected · both said the two classes sit at "six-plus + occurrences" · each lineage calls its 2026-07-27 row the fourth, so four is the prior count + and this round makes five. An unchecked count in the premise of a round about count-blind + escalation. Raised at Gate-A spec pass 4. +- 2026-08-04 · AC 7 amended · it required each story to say it "owes a profile at pickup" · it + now requires each story to carry, as its first acceptance criterion, that its profile — both + axes and the derived mode — is proposed and confirmed by Daniel before design resumes · the + old wording named no owner and no step, and nothing would have picked the debt up: `intake` + declines work already in solution design, `brainstorming` does not assess profiles, and + CLAUDE.md §5 proceeds when a cited story is unprofiled. Raised at Gate-A spec pass 8. +- 2026-08-04 · §1 diagnosis corrected · it said the scope-blind workaround "lives in ledger + prose, which agents do not read — they read the skill" · `harden-finding` step 3 does say to + re-read the log, so the premise was false; the defect is that the decision branch keys on + fingerprint and latest rung without letting the row's guard control the verdict. A round + about unverified claims resting on one. Raised at Gate-A spec pass 7. +- 2026-08-04 · AC 4 and AC 7 re-amended · both required the four stories to be "produced by + `intake`" · they now require the files written directly in the story template's shape and + unprofiled · `intake` excludes items that have moved into solution design, which three of the + four are, so making an intake-produced artifact acceptance-critical would deadlock the round + against its own tooling. Unprofiled because a profile is proposed and human-confirmed at + intake time; written now it would be a confirmed-looking value nobody confirmed. Raised at + Gate-A spec pass 6. +- 2026-08-04 · AC 4, AC 9 and §2 rewritten · they named the split work as solution forms + ("multi-row guard scan", "per-row audit record", "row metadata", "ledger format change", + "collision state machine") · they now name the observed failures and the questions left open, + because `intake` forbids HOW and a criterion demanding named machinery would make a + conformant story fail acceptance. §2's "each row records that reading" also became "each row + with a prior matching row", since the new-class row has none. Raised at Gate-A spec pass 5. + +**Profile rationale:** not an entry in the profile log — no value has moved, and the log's +three kinds (axis change, mode override, adoption) all record a change. Recorded here +because the delimitation is load-bearing: the classifier story that produced these findings +was `high`, this round is `standard`, and the line between them is executable enforcement +core vs. prompt discipline. Shipping downstream is true of both and must not by itself +raise risk — an axis that fires on every artifact this repo ships stops discriminating and +dies. + +## 1. Problem statement + +The 0.8.0 cycle and PR #21 produced findings that were fixed but never hardened. Commit +23b842d says so in its own body: "Not done in this cycle, and owed: harden-finding on +these five." Two ledger classes — `docs-drift` and `unverified-enforcement-claim` — each +stand at **four** prior occurrences, both lineages calling their 2026-07-27 row the fourth, +and this round makes each the fifth. The ledger's own escalation reflex reads a recurrence as +"escalate a rung" regardless of what the prior rung guarded. + +That reflex is the second problem, and it sits in the decision rather than in the reading. +The skill's recurrence step does have you re-read the ledger. What it does not do is let the +row's stated guard control the verdict: the branch keys on a fingerprint match and the latest +row's rung, so a later in-class defect **outside** the guarded spelling is proposed for a +stronger rung than anything justifies — exactly what the 2026-07-26 rows warn about by name. +That warning lives in ledger prose the branch never consults. Daniel has now had to issue the +precheck as a manual instruction, which is the parked row's own diagnosis demonstrating itself. + +Two further conditions have come due: several lessons from the cycle (single-shell test +blindness, mechanical sweep before read-pass, fixture-per-branch perf oracles, incomplete +negative bounds statements) have no ledger record at all; and one defect shape — a check +that supplies the thing it tests — has a taxonomy home in +`verification-masks-failure` but no recorded application of it, so the class reads as a +one-off rather than as the recurring shape it is. + +A third, the ledger's missing supersession convention, is **deliberately out of this +round's scope.** Its trigger has fired — the 2026-07-20 row teaches pre-0.8.0 counting +behaviour as current, and the parked row's trigger reads "the next falsified row — this is +the second." A fired trigger that nobody records evaporates, so this round's deliverables +include opening the intake for it as its own story rather than folding a ledger-format +change into a round about rungs. + +## 2. Desired outcome + +Every finding from the 0.8.0 cycle and PR #21 carries a recorded disposition: a ledger row +at a rung that fits, or a parked backlog row with a stated trigger. Escalation decisions in +this round are made against what the prior rung *claimed to guard*, not against an occurrence +count, and each row with a prior matching row records that reading so a later reader can check +it. Making the skill +instruct that reading turned out to be a design rather than a paragraph, so it leaves as its +own story with its evidence named. The ledger-supersession work leaves the same way, and so +do the two §5 triggers this round fires. The round leaves the repository smaller than the +findings would suggest, because items that turn out to need design are split rather than +squeezed in. + +## 3. Acceptance criteria + +- [ ] Each of the seven findings in this round's scope carries exactly one disposition — a + `docs/hardening-log.md` row at a named rung, or a `todos.md` row with a stated trigger. + A finding whose halves fall in different fingerprint classes carries exactly one per + half, and the round names which halves those are. None is left without one, and none + carries two dispositions in the same class. +- [ ] Every escalation decision in the round records which prior row's guard was read and + whether the finding falls inside or outside it. Outside → the fitting rung, no + escalation. Inside → recorded as a regression of that mechanism, repaired or strengthened. +- [ ] Neither recurring class escalates on count alone; each row states the guard it was + measured against and why the chosen rung fits. +- [ ] A story for the `harden-finding` guard-scope precheck exists on disk, written directly in + the story template's shape and unprofiled, carrying as opening evidence the failures this + round observed — a verdict + reached by reading one prior row and stopping, a rung choice no later reader can check, + a guard citation the ledger's escaping rule does not cover, a same-fingerprint row + treated as a duplicate without reading what it guards, a branch condition that bypasses + an unresolved prerequisite, and an escalation instruction recorded as moved while + nothing carried it — each with the question it leaves open and none with a solution, since + the design is brainstorming's to do. Like the other three, it carries as its first + acceptance criterion that its profile is proposed and confirmed by Daniel before design + resumes. +- [ ] The parked scope-blind row records this round as its third evidence case. +- [ ] The check-supplies-its-own-input defect lands as exactly one of: an extension of the + existing counterfactual prose, or a new class in `docs/hardening-taxonomy.md` — with + the choice justified against that file's "before minting, grep for a near match" rule. +- [ ] Three stories exist on disk, each written directly in the story template's shape and + unprofiled, each carrying the trigger that fired and the evidence it fired on — the + ledger-supersession convention, the no-PR route from a fixed finding to the ledger, and + the §5 version stamp. Each states its problem and its open question, and each says it is + a split from a designed round, and each carries as its first acceptance criterion that its + profile — both axes and the derived mode — is proposed and confirmed by Daniel before + design resumes. +- [ ] Each of the three parked rows whose trigger names §5 is read literally against its own + text, and the round records which fired and which did not, with the reason. +- [ ] **Added at Gate-B pass 1.** Any parked row whose trigger this round's own output crosses is + read the same way, marked fired, and given its story as a round deliverable — which at + minimum covers P8, whose "10 stories or 20 ledger rows" threshold the four appended rows + cross (18 → 22). A fifth story exists on disk on the same terms as the other four — + written directly in the story template's shape, unprofiled, carrying its source row's + conditions marked kept, moved or dropped, and carrying the profile-confirmation criterion + first — and `todos.md` carries the eighth row change that marks P8 fired. +- [ ] **Fired at Gate-A spec pass 3.** Making the precheck work turned out to require decisions + about what a durable record holds, how a guard citation survives the ledger's format, and + what happens when a row appears mid-run — more than the rule paragraph this criterion + budgeted. The round therefore records evidence case 3, carries no edit to + `harden-finding`, and the skill change became its own story. +- [ ] If two findings pull the same artifact in opposite directions, the round stops and + surfaces rather than choosing. +- [ ] Validation mode `battery+check` is satisfied: the full quality battery green, plus a + check that fails without the change, with its counterfactual stated. + +## 4. Affected AGENTS.md invariants + +- `## Key invariants` → `### Prompts and scaffolding` — "10. **The base taxonomy stays + stack-neutral.** Project vocabulary — tables, auth helpers, framework APIs — goes only in + that project's `docs/hardening-taxonomy.md`, never into the `harden-finding` skill. Otherwise + one project leaks into every other." +- `## Key invariants` → `### Prompts and scaffolding` — "11. **Prompt changes pass + `docs/prompt-standards.md`** — all 12 checklist items, for any skill, command, agent + definition, hook message, or scaffolded template." +- `## Don'ts` — "**Never replace a decision procedure without accounting for its old + conditions.** List what the previous prose required, then mark each one kept, moved, or + deliberately dropped." +- `## Don'ts` — "**Never describe what a gate proves without checking what it actually + compares.**" +- `## Don'ts` — "**Never rename or delete a doc section without grepping for references + first.**" +- `## Key invariants` → `### Packaging` — "12. **A plugin change requires a version bump.** A + pull request that changes any path under a `plugins//` directory **that still exists + at HEAD** — `examples/` included, and no exemptions among the paths inside such a directory + — must also change that plugin manifest's `version`, or CI fails." + +## 5. Open questions + +- None. + +## 6. Suggested size + +`story` — one coherent round with one spec → plan → PR, as #16 was. Seven findings is above +that precedent's three, and the parking discipline plus the stop-on-machinery rider are what +keep it from being an epic; if either fires, the split point is already named. diff --git a/docs/superpowers/stories/2026-08-04-harden-finding-guard-scope-precheck-story.md b/docs/superpowers/stories/2026-08-04-harden-finding-guard-scope-precheck-story.md new file mode 100644 index 0000000..496a741 --- /dev/null +++ b/docs/superpowers/stories/2026-08-04-harden-finding-guard-scope-precheck-story.md @@ -0,0 +1,89 @@ +# `harden-finding`'s recurrence rule is scope-blind — Story + +**Date:** 2026-08-04 · **Size:** story + +**Unprofiled, deliberately.** This story is a split from a designed round, not a raw idea, so +it did not pass through `dev-workflow:intake` — intake excludes items that have moved into +solution design. A profile is proposed and human-confirmed at intake time. Writing one here +would produce a header that **looks** confirmed and is not, and nothing would reveal that: +`CLAUDE.md` §5 stops on a profile that is malformed or internally inconsistent, not on one +whose values are well-formed but unconfirmed. That is why the debt is carried as acceptance +criterion 1 rather than by fabricating a header. + +## 1. Problem statement + +`harden-finding`'s recurrence step tells you to re-read the ledger, and then decides from the +fingerprint and the latest matching row's rung. It never lets that row's **stated guard** +control the verdict. So a later in-class defect outside the guarded spelling is proposed for a +stronger rung than anything justifies — which the 2026-07-26 rows warn about by name, in ledger +prose the decision branch does not consult. + +The 2026-08-03 hardening round applied the missing precheck by hand, on a standing instruction +from Daniel, and still reached a wrong verdict twice by reading one prior row's guard and +stopping. Landing the rule in the skill turned out to require decisions a rule paragraph cannot +carry, which is why it is here rather than in that round. + +## 2. Desired outcome + +A recurrence proposal states which prior rows it read, what each guards, and whether this +finding falls inside or outside — and a later reader can check that reasoning against the +ledger. Escalation follows from a guard that failed, never from a count. + +## 3. Acceptance criteria + +- [ ] Before design resumes on this story, whoever picks it up proposes both axes and the mode + derived from them, pauses for Daniel's confirmation, and writes the confirmed profile into + this header. Design continues only after that. +- [ ] A recurrence decision names every prior matching row it considered, not only the latest, + and records what each guards. +- [ ] The record a decision leaves behind is sufficient for a later reader to reach the same + inside/outside verdict without re-deriving it. +- [ ] The rule states what happens when a same-fingerprint row appears mid-run, including when + that row is `pending` or its guard cannot be determined. +- [ ] Whether automatic escalation on recurrence is kept, narrowed, or dropped is settled + explicitly, with a stated reason. +- [ ] The `AGENTS.md` Don't "Never replace a decision procedure without accounting for its old + conditions" is satisfied for every condition the current step 3 and step 7 carry. + +## 4. Affected AGENTS.md invariants + +- `## Key invariants` → `### Prompts and scaffolding` — "10. **The base taxonomy stays + stack-neutral.** Project vocabulary — tables, auth helpers, framework APIs — goes only in + that project's `docs/hardening-taxonomy.md`, never into the `harden-finding` skill. Otherwise + one project leaks into every other." +- `## Key invariants` → `### Prompts and scaffolding` — "11. **Prompt changes pass + `docs/prompt-standards.md`** — all 12 checklist items, for any skill, command, agent + definition, hook message, or scaffolded template." +- `## Key invariants` → `### Packaging` — "12. **A plugin change requires a version bump.**" +- `## Don'ts` — "**Never replace a decision procedure without accounting for its old + conditions.** List what the previous prose required, then mark each one kept, moved, or + deliberately dropped." + +## 5. Open questions + +Six, each paired with the failure that raised it: + +- A verdict was reached by reading one prior row's guard and stopping, when an older row's guard + covered the case. **Which prior rows must a recurrence be judged against before "outside" is + safe?** +- A reader cannot tell whether a past rung choice followed from a guard reading or from a count, + because nothing durable records the reading. **What does a later reader need, and where does + it belong?** +- Guards are frequently exact spellings containing regex alternation, and the ledger's stated + escaping rule covers the `finding` column. **How does a guard citation survive a Markdown + table without changing what the columns mean?** +- Step 7 re-reads the ledger and then appends, so a row landing after that read is not seen, and + a row seen at the read is treated as a duplicate without anyone consulting what it guards. + **What should happen when a row appears mid-run?** +- Widening the branch condition from "latest matching row is a real rung" to "a matching row + exists at a real rung" makes it fire when the latest row is `pending`, bypassing the + prerequisite rule. **How do a guard scan and a prerequisite block compose?** +- The old step-3 text required proposing one rung stronger on recurrence, and a replacement + choosing "the rung that fits the repair" does not carry it. **Is automatic escalation kept, + narrowed, or deliberately dropped?** + +## 6. Suggested size + +`story` — one skill file, one decision procedure, one spec → plan → PR. Above a chore because +the six questions above are real design; below an epic because they all concern one procedure +in one file. diff --git a/docs/superpowers/stories/2026-08-04-hardening-ledger-supersession-story.md b/docs/superpowers/stories/2026-08-04-hardening-ledger-supersession-story.md new file mode 100644 index 0000000..e9ac5ca --- /dev/null +++ b/docs/superpowers/stories/2026-08-04-hardening-ledger-supersession-story.md @@ -0,0 +1,67 @@ +# The hardening ledger has no supersession convention — Story + +**Date:** 2026-08-04 · **Size:** story + +**Unprofiled, deliberately** — a split from a designed round, so it bypassed +`dev-workflow:intake`, which excludes work already in solution design. A profile written now +would look confirmed without being confirmed, and nothing would reveal that; acceptance +criterion 1 carries the debt instead. + +## 1. Problem statement + +`docs/hardening-log.md`'s header says never edit a row, and one row per hardening. When a row's +"what this does NOT do" narration is later falsified by a feature change, neither move is +sanctioned: editing breaks the first rule, appending breaks the second. + +This is live. The 2026-07-20 row describes pre-0.8.0 counting behaviour as current, and a reader +who trusts it is misled about how the gate hook counts today. That is the second falsified row, +which is the condition the parked backlog row named as its trigger. + +### Conditions inherited from the source row + +From `todos.md`, "**The hardening ledger has no supersession convention.**": + +| Condition | Disposition | +|---|---| +| Never edit a row | **kept** — any solution must preserve it | +| One row per hardening | **kept** — any solution must preserve it | +| The 2026-07-20 row now describes pre-0.8.0 behaviour as current | **kept** as the motivating instance | +| The 2026-07-20 *spec* took a version-qualified supersession note and it worked | **kept** as prior art the design should evaluate first | +| Alternative: an explicit "rows are historical, read the newest row for current behaviour" header statement | **kept** as a candidate | +| Trigger: the next row falsified by a later change — this is the second | **moved** — fired, and recorded here | + +## 2. Desired outcome + +A reader who opens any ledger row can tell whether it still describes current behaviour, and a +row falsified by a later change can be marked as such without breaking either standing rule. + +## 3. Acceptance criteria + +- [ ] Before design resumes on this story, whoever picks it up proposes both axes and the mode + derived from them, pauses for Daniel's confirmation, and writes the confirmed profile into + this header. Design continues only after that. +- [ ] The 2026-07-20 row no longer reads as current behaviour, and it was not edited. +- [ ] The append-only rule and the one-row-per-hardening rule both still hold after the change. +- [ ] A reader can determine, from the ledger alone, which row to trust for current behaviour. + +## 4. Affected AGENTS.md invariants + +- `## Key invariants` → `### Prompts and scaffolding` — "8. **`/workflow-init`'s templates stay + inline** in the command body." +- `## Key invariants` → `### Prompts and scaffolding` — "9. **`/workflow-init` never overwrites + silently.** Idempotent: missing → write; identical → report unchanged; present and different → + show the diff and ask; additive files (`.gitattributes`, `.mcp.json`, …) → merge." +- **Conditional, if the design reaches the scaffolded template** — "11. **Prompt changes pass + `docs/prompt-standards.md`**" and "12. **A plugin change requires a version bump.**" Both bind + only if the convention must reach `/workflow-init`'s inline ledger header, which is §5's open + question. + +## 5. Open questions + +- Which artifacts must the convention reach before a reader can trust any row — this repo's + ledger alone, or every ledger `/workflow-init` scaffolds? A repo-only fix ships a rule this + kit's ledger obeys and every scaffolded one does not. + +## 6. Suggested size + +`story` — one file's header convention plus possibly one inline template, one spec → plan → PR. diff --git a/docs/superpowers/stories/2026-08-04-ledger-route-without-pull-requests-story.md b/docs/superpowers/stories/2026-08-04-ledger-route-without-pull-requests-story.md new file mode 100644 index 0000000..f2e7260 --- /dev/null +++ b/docs/superpowers/stories/2026-08-04-ledger-route-without-pull-requests-story.md @@ -0,0 +1,60 @@ +# A route from a fixed finding to the ledger, for projects that never open PRs — Story + +**Date:** 2026-08-04 · **Size:** story + +**Unprofiled, deliberately** — a split from a designed round, so it bypassed +`dev-workflow:intake`. A profile written now would look confirmed without being confirmed; +acceptance criterion 1 carries the debt instead. + +## 1. Problem statement + +The only mandated ledger check lives in `/dev-workflow:process-pr-review` step 5, so a project +that never opens a pull request never reaches it. One project has 51 Gate-A pass files and +**zero** ledger rows: findings were raised, validated and fixed, and none was ever considered +for hardening. + +### Conditions inherited from the source row + +From `todos.md`, "**Finding A — a route from a fixed finding to the ledger for projects that +never open PRs.**": + +| Condition | Disposition | +|---|---| +| Scope must match `process-pr-review` step 5 **exactly** — check every accepted actionable fixed finding, but invoke `harden-finding` only when a class matches or a new one is clearly warranted | **kept** — every approximating draft got this wrong | +| Cannot rest on same-session memory: a compaction, interruption or handoff loses the fixed-finding set and nothing detects the loss | **kept** | +| A durable handoff needs real design — identity, deduplication, consumption semantics | **kept**, and it is why this is a story rather than a mid-round addition | +| It mints `mandatory-step-anchored-to-optional-path` when it lands; minting earlier leaves a class no row uses | **kept** — the class is minted by the change that uses it | +| Evidence: canvas has 51 Gate-A pass files and 0 ledger rows | **kept** as the motivating instance | +| Trigger, first alternative: the next round that touches §5 | **moved** — fired by the 2026-08-03 round, recorded here | +| Trigger, second alternative: a project reporting an empty ledger across cycles that fixed findings | **kept** — it did not fire, and it remains the condition that would raise this independently | + +## 2. Desired outcome + +A project that fixes review findings without opening a pull request still reaches the ledger +check, and a fixed finding that warrants hardening is not lost to a compaction, an interruption +or a handoff. + +## 3. Acceptance criteria + +- [ ] Before design resumes on this story, whoever picks it up proposes both axes and the mode + derived from them, pauses for Daniel's confirmation, and writes the confirmed profile into + this header. Design continues only after that. +- [ ] A project with no pull requests reaches a ledger check on the findings it fixed. +- [ ] The route does not depend on same-session memory. +- [ ] The check's scope matches `process-pr-review` step 5 exactly — no wider, no narrower. + +## 4. Affected AGENTS.md invariants + +- `## Key invariants` → `### Prompts and scaffolding` — "11. **Prompt changes pass + `docs/prompt-standards.md`** — all 12 checklist items, for any skill, command, agent + definition, hook message, or scaffolded template." +- `## Key invariants` → `### Packaging` — "12. **A plugin change requires a version bump.**" + +## 5. Open questions + +- What has to be true for a fixed finding to reach the ledger in a project that never opens a + pull request? + +## 6. Suggested size + +`story` — one route, one spec → plan → PR. diff --git a/docs/superpowers/stories/2026-08-04-passive-metrics-over-the-ledger-story.md b/docs/superpowers/stories/2026-08-04-passive-metrics-over-the-ledger-story.md new file mode 100644 index 0000000..07b4638 --- /dev/null +++ b/docs/superpowers/stories/2026-08-04-passive-metrics-over-the-ledger-story.md @@ -0,0 +1,72 @@ +# Passive metrics, read-only over the ledger and git — Story + +**Date:** 2026-08-04 · **Size:** story + +**Unprofiled, deliberately** — a split from a designed round, so it bypassed +`dev-workflow:intake`, which excludes work already in solution design. A profile written now +would look confirmed without being confirmed; acceptance criterion 1 carries the debt instead. + +## 1. Problem statement + +The hardening ledger already contains the data to answer questions nobody can currently ask of +it — which fingerprints recur, how often a rung holds after it lands, which classes escalate on +count rather than on a guard that failed. Reading those answers today means a human scanning 22 +rows by eye, which is how a recurrence count gets asserted from memory rather than from the +file. + +The row was parked behind a sample-size threshold, and this round crossed it: appending four +rows took the ledger from 18 to 22, past the stated 20. The fifth story of this round also takes +the story count to 10, crossing the other arm of the same trigger. The threshold existed so the +sample would say something about the workflow rather than about the last week, and it now does. + +### Conditions inherited from the source row + +From `todos.md`, "**P8 — passive metrics, read-only over the ledger and git.**": + +| Condition | Disposition | +|---|---| +| Analysis only — no new state file | **kept** — a metrics feature that writes state is a different, larger thing | +| No instrumentation | **kept** — the data already exists; collecting more is out of scope | +| Nothing written back | **kept** — the ledger stays append-only and human-authored | +| It answers questions the ledger already contains the data for — which fingerprints recur, how often a rung holds | **kept** as the scope statement | +| Trigger: 10 stories or 20 ledger rows, below which the sample says more about the last week than about the workflow | **moved** — the 20-row arm fired at 22 rows in the 2026-08-04 round, and the story arm reaches 10 with this story; recorded here | + +## 2. Desired outcome + +A reader can ask the ledger which fingerprints recur and how a rung has held, and get the answer +from the file rather than from recall — without the analysis writing anything back. + +## 3. Acceptance criteria + +- [ ] Before design resumes on this story, whoever picks it up proposes both axes and the mode + derived from them, pauses for Daniel's confirmation, and writes the confirmed profile into + this header. Design continues only after that. +- [ ] The analysis reads the ledger and git and writes nothing — no new state file, no + instrumentation. +- [ ] A recurrence count it reports can be checked against the ledger by a reader who does not + trust it. +- [ ] What the analysis cannot answer from the ledger alone is stated, rather than left for a + reader to infer from what it does answer. + +## 4. Affected AGENTS.md invariants + +- `## Key invariants` → `### Prompts and scaffolding` — "11. **Prompt changes pass + `docs/prompt-standards.md`** — all 12 checklist items, for any skill, command, agent + definition, hook message, or scaffolded template." Binds if the analysis ships as a command + or skill. +- `## Key invariants` → `### Packaging` — "12. **A plugin change requires a version bump.**" + Binds on the same condition. +- `## Don'ts` — "**Never describe what a gate proves without checking what it actually + compares.**" A metrics report is a claim about the ledger's contents, and a count stated + without naming what was counted is the shape this repo logs most often. + +## 5. Open questions + +- Where does the analysis live — a command, a script, or a documented reading recipe? The row + says analysis only, which rules out state but not a surface. +- What does it count, exactly? A fingerprint's recurrence count already drives escalation, and a + second number derived differently would give two answers to one question. + +## 6. Suggested size + +`story` — one read-only analysis over two existing sources, one spec → plan → PR. diff --git a/docs/superpowers/stories/2026-08-04-section-5-version-stamp-story.md b/docs/superpowers/stories/2026-08-04-section-5-version-stamp-story.md new file mode 100644 index 0000000..bc3fe2e --- /dev/null +++ b/docs/superpowers/stories/2026-08-04-section-5-version-stamp-story.md @@ -0,0 +1,63 @@ +# A §5 version stamp, so a scaffolded CLAUDE.md can tell it lags the plugin — Story + +**Date:** 2026-08-04 · **Size:** story + +**Unprofiled, deliberately** — a split from a designed round, so it bypassed +`dev-workflow:intake`. A profile written now would look confirmed without being confirmed; +acceptance criterion 1 carries the debt instead. + +## 1. Problem statement + +`/workflow-init` scaffolds `CLAUDE.md` §5 from an inline template. When the plugin's §5 changes, +every previously scaffolded copy silently lags, and nothing in the scaffolded file tells its +reader so. One known-stale instance exists and is being re-synced by hand. + +### Conditions inherited from the source row + +From `todos.md`, "**Finding B — a §5 version stamp, so a scaffolded CLAUDE.md can tell it lags +the installed plugin.**": + +| Condition | Disposition | +|---|---| +| A semantic §5 locator is needed — `/workflow-init` may append the section renumbered, so "no §5 heading" can misread a valid section and append a duplicate | **kept** | +| Per-state merge semantics: invariant 9 forbids a silent overwrite, and "re-run init to sync" promises what the command cannot give | **kept** | +| Stamp cardinality: absent, duplicate, malformed | **kept** | +| The binding must be real on **every** push path; the version-bump coupling first proposed was false, since invariant 12's checker is `pull_request`-only | **kept** — the false coupling is recorded so it is not re-proposed | +| A stamp is a **wire format**: shipping a provisional one writes legacy into every scaffolded file | **kept** — it is why a provisional stamp is unacceptable | +| The one known-stale instance is being re-synced by hand, so this carries no schedule pressure | **kept** — the work is not urgent | +| Trigger: the next round that touches the §5 template | **moved** — fired by the 2026-08-03 round, recorded here | + +## 2. Desired outcome + +A reader of a scaffolded `CLAUDE.md` can tell whether its §5 matches the installed plugin's, +without comparing the two by hand. + +## 3. Acceptance criteria + +- [ ] Before design resumes on this story, whoever picks it up proposes both axes and the mode + derived from them, pauses for Daniel's confirmation, and writes the confirmed profile into + this header. Design continues only after that. +- [ ] A scaffolded `CLAUDE.md` whose §5 lags the installed plugin is detectable as such. +- [ ] Re-running `/workflow-init` on a stale file behaves per invariant 9 — it does not silently + overwrite accumulated content. +- [ ] Whatever binding the design chooses holds on every push path, not only on pull requests. + +## 4. Affected AGENTS.md invariants + +- `## Key invariants` → `### Prompts and scaffolding` — "8. **`/workflow-init`'s templates stay + inline** in the command body. Claude Code does not expand `${CLAUDE_PLUGIN_ROOT}` inside + command markdown (verified), and the cache path is not an API." +- `## Key invariants` → `### Prompts and scaffolding` — "9. **`/workflow-init` never overwrites + silently.**" +- `## Key invariants` → `### Prompts and scaffolding` — "11. **Prompt changes pass + `docs/prompt-standards.md`** — all 12 checklist items." +- `## Key invariants` → `### Packaging` — "12. **A plugin change requires a version bump.**" + +## 5. Open questions + +- How can a scaffolded `CLAUDE.md` tell its reader that it lags the installed plugin? + +## 6. Suggested size + +`story` — one stamp format and its detection, one spec → plan → PR. Not a chore: a wire format +shipped provisionally cannot be taken back. diff --git a/plugins/dev-workflow/.claude-plugin/plugin.json b/plugins/dev-workflow/.claude-plugin/plugin.json index 654ea02..7ce8394 100644 --- a/plugins/dev-workflow/.claude-plugin/plugin.json +++ b/plugins/dev-workflow/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "dev-workflow", "displayName": "Cross-Model Review Workflow", - "version": "0.8.0", + "version": "0.8.1", "description": "Spec-driven workflow with two independent cross-model review gates, an append-only hardening ledger with an escalation ladder, and repo-enforced quality. Requires the superpowers plugin.", "author": { "name": "Daniel Sänger", diff --git a/plugins/dev-workflow/CHANGELOG.md b/plugins/dev-workflow/CHANGELOG.md index d011622..659e881 100644 --- a/plugins/dev-workflow/CHANGELOG.md +++ b/plugins/dev-workflow/CHANGELOG.md @@ -22,6 +22,23 @@ unambiguously, still fails. Deleting only a plugin's *manifest* while the direct keeps shipping fails too. AGENTS.md invariant 12 carries the complete list. +## 0.8.1 + +- **Three sentences added to the §5 gate protocol, and to the template `/workflow-init` + scaffolds.** The Gate-B standing lens now asks what a diff changes the size, value or position + of, and to grep for where each is described elsewhere — asked as an open question alone it + missed three such statements in one cycle while being carried with unusual force. The Profiles + counterfactual now asks for both halves: name the observation that would exist if the claim + were false, and confirm the wiring could have produced it. The Gate-A pass procedure now asks + for a mechanical sweep before each read pass, inspecting quoted commands rather than running + them, since a command quoted in a spec may be destructive. +- **None of the three is a check.** Nothing runs the grep, tests whether a check could have + failed, or records that a sweep happened. They sharpen questions a reader asks; the ledger + rows say so rather than implying otherwise. +- **No skill changed.** The `harden-finding` guard-scope precheck — the change this round set out + to make — turned out to need decisions about durable records, ledger format and mid-run + collisions that a rule paragraph cannot carry. It is split to its own story. + ## 0.8.0 - **The gate hook reads the result of a gate call before counting it.** Until now it diff --git a/plugins/dev-workflow/commands/workflow-init.md b/plugins/dev-workflow/commands/workflow-init.md index ee704d1..7f35bb7 100644 --- a/plugins/dev-workflow/commands/workflow-init.md +++ b/plugins/dev-workflow/commands/workflow-init.md @@ -416,7 +416,12 @@ Add `/.context/codex-reviews/` to `.gitignore` — that entry specifically, not NO FINDINGS ``` - Each pass: validate, revise, re-run. (Large/high-risk artifact: optional focused + Each pass: validate, revise, re-run. Before each read pass, settle mechanically what the + artifact asserts and a machine can decide without side effects — cited paths, quoted + passages, stated counts, the syntax of standalone fenced blocks — because a read pass + spends expensive judgement on what a parser settles in seconds and misses it anyway, + inspecting quoted commands rather than running them, since a command quoted in a spec + may be destructive or an intentional failure. (Large/high-risk artifact: optional focused per-dimension passes on top.) - **Gate B — Code.** Tests green, before `git commit`. Tool: `mcp__codex__review` (args `instruction`, `whatWasImplemented`, `baseSha`; `reviewType: full` runs @@ -444,6 +449,10 @@ Add `/.context/codex-reviews/` to `.gitignore` — that entry specifically, not the file was correct until your change landed elsewhere. This lens is prompt text: it asks, nothing enforces the ask or validates the answer, and no comprehensive check covers arbitrary semantic drift. Ask anyway; in practice it is what surfaces them. + **Name what this diff changes the size, value or position of** — a list, a count, a + version, an identifier, a cited line — and grep for where each is described elsewhere, + because asked as an open question alone this lens missed three such statements in one + cycle while being carried with unusual force. **What counts as prose (the only Gate-B exemption).** Every staged path is explanatory documentation — `docs/**.md`, `README.md` → N/A. Those describe the @@ -532,7 +541,11 @@ A check need not be an automated test — where none is possible, a **named veri satisfies it and the entry says which route was taken and why. Either route owes the **counterfactual**: the observation against the prior state. An **unobservable counterfactual is a blocking evidence gap**, not a free pass — stop and surface; the human -may then lower the mode as a logged override. A fabricated test satisfies nothing. +may then lower the mode as a logged override. A fabricated test satisfies nothing. **Name +the observation that would exist if the claim were false, and confirm the wiring could have +produced it** — a check that supplies its own input, runs where the defect cannot appear, or +uses a fixture that never reaches the branch it covers reports success because of how it was +wired, not because the thing it checks succeeded. **The evidence entry lives in the commit body** (see Mechanics), carries the **story path and the named evidence but not the mode value**, and is **revalidated before every Gate-B diff --git a/todos.md b/todos.md index f0dfb8c..5a64a95 100644 --- a/todos.md +++ b/todos.md @@ -55,7 +55,11 @@ driven by recurrence rather than by enthusiasm. row now describes pre-0.8.0 counting behaviour as current. The 2026-07-20 *spec* took a version-qualified supersession note and that worked; the ledger needs the same convention written into its header, or an explicit "rows are historical, read the - newest row for current behaviour" statement. *Trigger: the next row falsified by a + newest row for current behaviour" statement. + **TRIGGER FIRED (2026-08-04):** the 2026-07-20 row now teaches pre-0.8.0 counting + behaviour as current — the second falsified row this trigger names. Story: + `docs/superpowers/stories/2026-08-04-hardening-ledger-supersession-story.md`. + *Trigger: the next row falsified by a later change — this is the second.* - [ ] **Locator selects the `text` element by RAW BYTE comparison of `type`.** A Unicode-escaped spelling of `text` is legal JSON meaning `text` and is not selected; @@ -178,6 +182,18 @@ driven by recurrence rather than by enthusiasm. throwaway index writes loose blobs/trees into the user's repo (verified: 3 → 5 objects per review). Unreachable, so gc collects them, but a temporary `GIT_OBJECT_DIRECTORY` with the real store as an alternate would avoid the churn. +- [ ] **The gate-claims Don't is correct and was not followed, twice.** PR #21's C4 (an + unqualified jq-parity criterion that outran what `field()` compares for a malformed outer + document) and C5 (a README claim that a typo cannot quietly unhook a gate) both fall + inside the 2026-07-19 `AGENTS.md` Don't, whose operative instruction already requires + exactly what they omitted — name the exact comparison the code performs, and delete any + part of the sentence that outruns it. **No textual repair exists**, which is why these are + parked rather than logged: a ledger row would have to name a hardening, and a rule needing + no change means the failure was compliance, not wording. The 2026-08-04 amendment covers + coverage enumerations (F8's shape) and reaches neither a positive parity claim nor a + positive prevention claim. *Trigger: a third compliance miss against that Don't, or a + feasible mechanical rung emerging from + `docs/superpowers/stories/2026-08-04-harden-finding-guard-scope-precheck-story.md`.* ## Next @@ -218,6 +234,14 @@ backlog. variable prevents the situation; the hook only recognizes today's spelling of it. The result-classification story documents it in `README.md` § Setup only, deliberately — a preflight check is a second surface and was kept out of that diff. + **OBSERVATION (2026-08-04), and the row stays open.** With the variable set to `0`, five + Gate-A passes of the 2026-08-03 round ran 458 s, 550 s, 757 s, 663 s and 780 s; all five + stayed in the foreground and returned ordinary `success: true` envelopes the hook could + read. **No control run was made** with the variable unset, so this is a correlation + observed under one setting, not a demonstration that the variable held those calls in the + foreground. What it does establish is that calls well past 120 s can return as ordinary + foreground results here. The row's deliverable — a `/workflow-init` preflight check — is + unbuilt, so the row is not discharged by this. *Trigger: after that story lands* (spec: `docs/superpowers/specs/2026-07-31-failed-codex-call-counts-as-a-pass-design.md`). @@ -245,7 +269,13 @@ backlog. - [ ] **P8 — passive metrics, read-only over the ledger and git.** Analysis only: no new state file, no instrumentation, nothing written back. It answers questions the ledger already contains the data for (which fingerprints recur, how often a rung - holds). *Trigger: 10 stories or 20 ledger rows* — below that the sample says more + holds). + **TRIGGER FIRED (2026-08-04):** the 2026-08-03 hardening round appended four ledger rows, + taking the ledger from 18 to 22 and crossing the 20-row arm; its fifth story takes the + story count to 10, crossing the other arm as well. Story: + `docs/superpowers/stories/2026-08-04-passive-metrics-over-the-ledger-story.md`, which + carries this row's conditions with each marked kept, moved or dropped. + *Trigger: 10 stories or 20 ledger rows* — below that the sample says more about the last week than about the workflow. - [ ] **`/capture-finding` as an intake extension of `harden-finding`.** An extension, not a sibling command: a finding captured outside the ladder is how a ledger @@ -294,7 +324,12 @@ backlog. so; it does not cover *sequential cycles* reusing them. Note the dispositions and resume-note companions have the same property. Any fix has to keep the pre-call delete — that check is load-bearing — so it is about naming (a cycle component in the slot) or - archiving, not about relaxing the protocol. *Trigger: the next round touching the §5 + archiving, not about relaxing the protocol. + **NOT FIRED (2026-08-04):** the 2026-08-03 hardening round edits §5 prose and its template + mirror, and changes no part of the §5 **file protocol** this row's trigger names — not the + slot names, not the pre-call delete, not the terminator or acceptance rules. Recorded so a + later reader can check the reading rather than re-derive it. + *Trigger: the next round touching the §5 file protocol.* - [ ] **Finding B — a §5 version stamp, so a scaffolded CLAUDE.md can tell it lags the installed plugin.** Split out of the canvas-findings round after two Gate-A passes @@ -307,16 +342,29 @@ backlog. invariant 12's checker is `pull_request`-only. A stamp is a **wire format**: shipping a provisional one writes legacy into every scaffolded file. *The one known-stale instance (canvas) is being re-synced by hand, so this carries no - schedule pressure.* *Trigger: the next round that touches the §5 template.* + schedule pressure.* + **TRIGGER FIRED (2026-08-04):** the 2026-08-03 hardening round edits the §5 inline + template. Story: + `docs/superpowers/stories/2026-08-04-section-5-version-stamp-story.md`, which carries this + row's conditions with each marked kept, moved or dropped. + *Trigger: the next round that touches the §5 template.* - [ ] **`harden-finding`'s recurrence rule is scope-blind.** Rungs guard *scopes*; the - skill compares only *fingerprints*; a ledger-prose workaround is unenforceable - because agents follow the skill, not the row. Sketched fix — before proposing + skill's recurrence step *does* re-read the ledger, and the defect is that its + **decision branch** keys on the fingerprint and the latest matching row's rung + without letting that row's stated guard control the verdict. Sketched fix — before proposing escalation on a same-fingerprint recurrence, read the prior row's stated guard: **outside** it the prior mechanism never claimed that shape, so its rung did not fail — pick the fitting rung, do **not** escalate; **inside** it, the mechanism was meant to catch this and did not, so that is a regression to repair or strengthen. (An earlier draft had those branches inverted, which would have entrenched the bug - it was filed against; Gate A caught it.) *Trigger: the first human rejection of an + it was filed against; Gate A caught it.) + **Evidence case 3 (2026-08-04):** the 2026-08-03 hardening round ran the precheck as a + standing manual instruction from Daniel — which is this row's own diagnosis, since a rule + that exists only in chat is not one the skill carries — and still reached a wrong verdict + twice by reading a single prior row's guard and stopping. Split to + `docs/superpowers/stories/2026-08-04-harden-finding-guard-scope-precheck-story.md`; this + row stays open because the fix it sketches has not landed. + *Trigger: the first human rejection of an over-escalation the 2026-07-26 rows predicted, or the next round touching the skill.* - [ ] **Finding A — a route from a fixed finding to the ledger for projects that never open PRs.** The only mandated ledger check lives in `process-pr-review` step 5, so a @@ -331,7 +379,11 @@ backlog. needs real design (identity, dedup, consumption semantics), which is why it was refused as a mid-round addition. It mints `mandatory-step-anchored-to-optional-path` when it lands — minting it earlier would - leave a class no row uses. *Trigger: the next round that touches §5, or a project + leave a class no row uses. + **TRIGGER FIRED (2026-08-04):** the 2026-08-03 hardening round edits §5. Story: + `docs/superpowers/stories/2026-08-04-ledger-route-without-pull-requests-story.md`, which + carries this row's conditions with each marked kept, moved or dropped. + *Trigger: the next round that touches §5, or a project reporting an empty ledger across cycles that fixed findings.* - [ ] **Escalation trigger for the invariant checker — read this before patching it.** The checker asserts only the spellings its fixtures cover. Adding one more regex