From 95e9d20ab54bb8b079e7a1e68241e6679848730f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Thu, 27 Aug 2026 18:42:02 +0200 Subject: [PATCH 001/117] docs(field-report): preserve the fic2 Gate-B cycle evidence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verbatim copy of .context/codex-reviews/gate-b-fic2-parked-review-economics.md, which .gitignore excludes and which the next cycle's slot reuse would overwrite. It is the opening evidence for the review-economics story: Q1-Q6, the two instrument defects, the seven-pass shape with both stop-and-surfaces, and the two undiagnosed observations. Only a provenance header was added; the body is byte-identical to the source. No machine-local absolute path was present, so the field-report path-neutrality convention required no substitution -- the check `grep -cE '/Users/|/home/|/var/folders/|/private/|[A-Za-z]:\\'` returns 0 on the committed file. Staged with `git add -f`: `docs/field-reports/` sits in this clone's local .git/info/exclude, which is per-clone scratch and not repo policy -- the two existing field reports in that directory are tracked. Gate B: N/A -- one staged path, docs/field-reports/*.md, explanatory documentation under CLAUDE.md §5's prose exemption. No prompt, no plugin path, no non-.md file. --- .../2026-08-26-fic2-cycle-evidence.md | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 docs/field-reports/2026-08-26-fic2-cycle-evidence.md diff --git a/docs/field-reports/2026-08-26-fic2-cycle-evidence.md b/docs/field-reports/2026-08-26-fic2-cycle-evidence.md new file mode 100644 index 0000000..be7be2e --- /dev/null +++ b/docs/field-reports/2026-08-26-fic2-cycle-evidence.md @@ -0,0 +1,238 @@ + +> **Why this is committed.** Written during the cycle at +> `.context/codex-reviews/gate-b-fic2-parked-review-economics.md`, where `.gitignore` +> excludes all of `.context/` and each gate cycle overwrites the previous cycle's slots. +> It is the opening evidence for the review-economics story, so it is committed here +> rather than left to a `.context/` clear. Verbatim copy: no machine-local absolute +> paths were present, so the field-report path-neutrality convention required no +> substitution. The 14 `fic2` pass files it cites stay local — their per-pass figures +> are carried in the tables below. + +# Parked: opening-evidence package for the review-economics story + +Written 2026-08-26 during Gate-B cycle `fic2` on branch `field-intake-canvas-a1-a5`. +Not a findings file — it participates in no pass validation. It exists because the +material below was produced by a live loop and would otherwise die with the session. + +**Disposition (Daniel, 2026-08-26): revert and park.** The cycle does not absorb the §5 +surgery these findings imply. The assigned fix set contracts back to the eleven +dispositions plus the two loop rules that survived the earlier 12-pass cycle, plus this +cycle's verified error fixes. Everything below moves to a story, unanswered. + +## What was reverted, and why it is not a retreat + +Two clauses were written into CLAUDE.md §5 and the `/workflow-init` template mirror +during this cycle and then taken back out: + +- **Q1 — clean completion outranks the two-tell stop.** A Blocker/Major-free pass at or + above the floor would close even with two or more tells present, the tells going into + the closing status report rather than blocking the close. +- **Q2 — a declined expansion has a defined exit.** A finding the user declines to bring + in-set leaves the cycle as a recorded out-of-scope item, with the Blocker/Major-resolve + duty scoped to in-set findings. + +Both were correct answers to real defects. Pass 1 found the defects; Daniel confirmed both +answers. Pass 2 then found that shipping them requires qualifying **three §5 rules nobody +proposed changing** — the universal Blocker/Major-resolve duty, the rule that a surfaced +finding stays open with resolution unwaived, and the rule that no pass carrying it counts +as clean. That is the scope expansion the cycle declined. + +## The three questions that go to the story unanswered + +- **Q3.** Does a *scope stop* outrank clean completion, or the reverse? "Clean completion + outranks both exits" was written, but §5 has three exits — the scope stop, the + clearly-stuck exit, and the two-tell stop. If scope is included, the sentence + contradicts the rule that even a Minor opening a contract question must stop. +- **Q4.** Does a decline bind *later passes in the same cycle*? As drafted it governed one + stop only, so the same out-of-set Blocker could stop every subsequent pass and re-ask the + same question indefinitely. +- **Q5.** Is qualifying the three universal rules with an in-set boundary acceptable at + all? Yes makes Q2 shippable and is §5 surgery; no means Q2 cannot ship in that form. + +## The instrument is broken, and that is the most reusable finding here + +The `battery+check` evidence for a prose-only change was a decision matrix: N review +states, complete inputs, one expected output each, scored against the old text and the new +text and against both copies independently. Two defects were found in it by Gate B, and +both are properties of the *technique*, not of this instance: + +1. **A state's inputs must include every input the rule reads.** Rows 3 and 10 carried + identical recorded inputs and different expected outputs, because the user's expansion + answer was never an input column. A matrix that omits an input cannot distinguish the + states that input separates, and it will still look complete. +2. **A counterfactual must distinguish ABSENT from CONTRADICTORY.** The entry claimed the + parent commit was "contradictory" on one state. `git show 17d5ad3:CLAUDE.md` has no + two-tell rule at all — only the undefined phrase "clearly stuck" at line 78. The + contradiction existed solely in an intermediate draft produced *inside this cycle*, so + the check reported a failure mode the prior state could not produce. + +Both survived a full Gate-B pass before being caught on the next one. + +## Loop economics, measured on this cycle + +| Pass | Findings | Blocker | Major | +|---|---|---|---| +| 1 | 14 | 3 | 5 | +| 2 | 24 | 4 | 13 | + +Four of the five tells present at pass 2: finding count rising; Blocker count failing to +fall; findings clustering on the **instrument** (6 of 24, on the matrix rather than on the +rules it scores); findings clustering on **prose about** the rules (9 of 24 — CHANGELOG, +closure record, ledger entry, a parked story). No require↔withdraw pair: pass 2 narrowed +what pass 1 required, which is a qualification and not a withdrawal. + +The reporting duty formally begins at pass 4. These were readable at pass 2, which is the +argument for the duty starting earlier — or for the trend being computed rather than +narrated. + +## Cross-references + +- Findings: `.context/codex-reviews/gate-b-{spec,quality}-fic2-pass-{1,2}.md` +- The matrix as it now stands (nine states, both clauses removed): + `docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md`, section "The named + verification behind the `battery+check` entry" +- Slot-name variance for this cycle: `fic2` discriminates it from the 2026-08-17 cycle + whose records occupy the bare `gate-b-{spec,quality}-pass-N` slots. + +--- + +## Parked at pass 5 (Daniel, 2026-08-26: bounded fix, then close) + +Four findings from Gate-B pass 5 were parked rather than repaired. All four are true. All four +are about the *instrument* or about the entry conditions of unstarted work, and none is about +what §5 tells an agent to do. Pass 5 returned **zero product-behaviour findings**. + +### The one that is this story's subject matter, verbatim + +**A prose rule's check demands a fixture per predicate.** Pass 4 found that the nine-state matrix +could not see the reporting duty at all — no input it read changed when that duty was deleted. A +five-state table was added. Pass 5 then found that the new table takes the *tell count* as a +precomputed input rather than deriving it from raw observations, so a draft could delete or invert +one of the five tell definitions and every row would keep its expected result. The repair asked for +is raw input columns exercising each of the five tell predicates, against both prompt copies at both +revisions: roughly 5 × 2 × 2 hand-scored fixtures, for a rule whose entire product surface is one +paragraph. + +The finding is correct and the repair is disproportionate. That gap — **what differential evidence +a prose-only rule can actually carry, and where the cost of the fixture exceeds the value of the +coverage** — is the question. `battery+check` says "a check that fails without the change" and says +nothing about how much instrument a one-paragraph rule is worth. + +Sequence worth keeping: pass 4 asked for the table, pass 5 asked for it to be rebuilt, and the thing +being measured did not change between them. + +### Three on a parked story's acceptance criterion + +`docs/superpowers/stories/2026-08-17-arms-race-remedy-as-procedure-story.md`. Its kept/moved/dropped +criterion should enumerate individually, per pass 5: the three line contents; all five tell +definitions; and the threshold's consequence (report the triggering tells, hand the decision to the +user). Its problem statement also says §5 carries "one recognition heuristic and a terminal action" +while the same story later inventories two stop paths — a contradiction in the story's own opening. + +Each is true. Each refines the entry conditions of work nobody has begun. One condition from this +group *was* applied rather than parked — the pass-4 activation boundary — because a future rewrite +could otherwise move the duty to pass 1 while checking off every other listed condition. + +### Cycle shape at the point of closing + +| Pass | Findings | Blocker | Major | +|---|---|---|---| +| 1 | 14 | 3 | 5 | +| 2 | 24 | 4 | 13 | +| 3 | 12 | 0 | 6 | +| 4 | 3 | 0 | 2 | +| 5 | 6 | 0 | 5 | + +Two stop-and-surfaces, at pass 2 (four tells) and pass 5 (three tells). Only the pass-5 stop was +required by the shipped duty, which begins at pass 4; the pass-2 stop was an early surface chosen +because the tells were already readable, which is the argument the duty's activation boundary +invites rather than a rule it enforces. The pass-2 stop produced a revert; the pass-5 stop produced +this bounded close. Both were decided by the maintainer, neither by the loop. + +--- + +## Observation recorded 2026-08-26: pass counter disagreed with the pass record + +Raw facts only. **The cause is UNDIAGNOSED**, and no attribution to any existing ledger row +is made here — attribution without diagnosis is the class the ledger polices. + +- Cycle: `fic2`, branch `field-intake-canvas-a1-a5`, closed 2026-08-26 at commit `3cdd075`. +- Passes actually run and validated: **7**. Each wrote both branch files, each file carried a + well-formed terminator and a count matching its finding lines: + `.context/codex-reviews/gate-b-{spec,quality}-fic2-pass-{1..7}.md` — 14 files on disk. +- What the gate hook reported at the closing commit: **"only 1/3 mcp__codex__review pass(es) + since the last commit"**. +- What it reported at each intermediate amend: **"1 recorded pass(es) this cycle"**, from the + amend following pass 1 onward. The value did not advance across passes 2 through 7. +- One intermediate amend instead reported **"no fingerprint is recorded for this cycle"**. +- `.context/codex-gate.passCount` read `7` at the start of the session, before this cycle began; + that value belongs to the previous cycle and was not re-read afterwards. +- Every one of the 7 calls returned `success: true` with a normal result envelope. None timed + out, none was aborted, none returned an `INCOMPLETE` reply. +- The cycle used a slot-name discriminator (`fic2`) rather than the bare + `gate-b-{spec,quality}-pass-N` names. Whether that is related is **not established** — the + hook is documented as never reading the findings file at all. +- Each pass was followed by `git commit --amend` on a single `WIP:`-prefixed commit. + +Not diagnosed, and deliberately not guessed at: whether the counter was reset, never +incremented, incremented and overwritten, or read from a different key than it was written to. +Nobody inspected the hook's state files during the cycle, so there is no evidence either way. + +Consequence for this cycle: none. `CLAUDE.md` §5 says the counter is not evidence and that every +incomplete pass is discounted regardless of what it says; the close rested on the 14 validated +findings files, not on the counter. The observation matters for the instrument, not for this +artifact. + +--- + +## Parked from PR #25 review (Daniel, 2026-08-26): the unavailable-history gap + +Greptile raised one P1 on PR #25 against `CLAUDE.md:143`, the pass-4-onward reporting duty. +Thread: https://github.com/dsnger/dev-workflow-kit/pull/25#discussion_r3864986788 + +**Split verdict.** + +*The stated mechanism is FALSE.* The claim was that "the mandatory findings format cannot +retain all of that history". It can. §5 mandates one findings file per pass per branch at a +**pass-numbered** slot, one finding per line, severity as a leading closed-set field, and a +terminator carrying the count. Both historical inputs the three-line report needs are +therefore derivable from the mandated artifacts alone — trend by counting finding lines and +`^BLOCKER` lines per pass, require↔withdraw by comparing across those same files. Run over +this cycle it reproduced the reported figures exactly (findings 14, 24, 12, 3, 6, 6, 2; +Blockers 3, 4, 0, 0, 0, 0, 0) with no optional artifact consulted. The deletion rule does not +erase history either: §5 deletes only the slot about to be written, and slots are numbered. + +*The gap is REAL, and it is availability rather than format.* Where prior-pass files are +genuinely absent — a fresh checkout, a cleared `.context/`, another machine, a cycle resumed +elsewhere — §5 defines **no behaviour** for the report from pass 4 onward. The cycle-stable +resume note is not the fallback: it is explicitly optional, and `CLAUDE.md:224` says "Nothing +depends on it existing." An agent in that position must invent the trend, omit the line, or +decide for itself, and the two-tell threshold is mandatory on top of whatever it decides. + +**Why parked rather than fixed:** closing it needs new normative §5 content, which is the +surgery this cycle declined twice. It joins Q3-Q5 above as a fourth open question of the same +shape — a gap in the shipped rule whose repair is a contract decision. + +**Q6.** What does the pass-4-onward report do when the prior-pass record is unavailable? The +candidate answers are not obviously equal: report the lines that *are* computable and say +which are not; treat unavailable history as a stop condition of its own; make the resume note +mandatory for cycles that cross a session boundary (which changes an artifact §5 currently +calls advisory); or start the duty's clock at the first pass of the *current* record rather +than of the cycle. + +## Raw observation, undiagnosed: CodeRabbit plan metadata disagrees with the routing file + +Recorded 2026-08-26, not attributed and not acted on. + +- CodeRabbit's run configuration on PR #25 reports **`Plan: Pro Plus`** (Run ID + `cdbb25fa-8c3d-45fb-a259-6b973b2ea965`, review profile CHILL). +- `docs/pr-review-bots.md`'s CodeRabbit row records **Plan: Free** "(per Daniel)", and states + that the earlier Pro Plus reading "was observed on PR #1 only and no longer describes the + account". +- These disagree. **The cause is UNDIAGNOSED.** Two candidates, not distinguished: the plan + actually changed since that row was written, or the run-configuration metadata is + unreliable. +- Why it matters: that row's review-limit reasoning — and part of the argument for routing + CodeRabbit opportunistically rather than blocking on it — rests on the Free reading. +- Not this PR's business; `docs/pr-review-bots.md` is untouched by PR #25. A docs-only + follow-up can correct it **after** diagnosis, not before. From 81c77b3d21c83d7ec6a76ad91db79d8017fa2ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 10:05:25 +0200 Subject: [PATCH 002/117] docs(intake): add review-loop-economics-pass-floor story --- ...-review-loop-economics-pass-floor-story.md | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md new file mode 100644 index 0000000..e2793cb --- /dev/null +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -0,0 +1,183 @@ +# Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Story + +**Date:** 2026-08-28 · **Size:** story +**Risk:** high · **Security:** none · **Validation:** battery+check+verification + +## 1. Problem statement + +Development drags. Review loops are opaque for minutes to hours at a stretch, and +Blocker/Major assessment is mis-calibrated, so a loop burns passes on trivia and on its +own test instruments before any product behaviour has been examined. (Daniel, 2026-08-26.) + +Four measurements, each citable: + +- **Loops do not converge on count.** In the kit's heaviest consumer, one design spec past + 2800 lines ran nineteen measured Gate-A passes; findings fell from 43 into a 2–19 range + after pass 6 and never reached zero, while Blockers fell from 11 to 0–1 from pass 7 on + (`CLAUDE.md:157-159`). The substance converged and the number did not. +- **Severity lands on the instrument, not the product.** PR #23's closing commit records + that "of 27 Blocker/Major findings, 16 were in the never-committed scratch harness, 10 in + the design spec's narration, 1 in the plan" (`7bbdb14`, quoted at `todos.md:268-270`) — + 16 of 27 on the instrument, in that row's own summary of it. +- **The most recent cycle reproduced both.** Gate-B cycle `fic2` ran findings 14 · 24 · 12 · + 3 · 6 · 6 · 2 across seven passes. The pass-2 spike was caused by mid-cycle scope + expansion; pass 5 returned **zero** product-behaviour findings and still cost a full pass + each on two branches. Two mandatory stop-and-surfaces, one revert, and two false + counterfactuals that each survived a full clean pass before the next one caught them. + (`docs/field-reports/2026-08-26-fic2-cycle-evidence.md`.) +- **Individual passes are slow.** Five Gate-A passes of the 2026-08-03 round ran 458 s, + 550 s, 757 s, 663 s and 780 s (`todos.md:463-469`). That row states its own limit and this + story carries it unchanged: **no control run was made**, so this is a correlation observed + under one setting, not a demonstration of what caused those durations. + +A three-pass floor is charged to every cycle regardless of what the change is worth +reviewing, and a docs-only or trivial story pays the same toll as one that rewrites a gate. + +**A worked example of the same class, found while writing this story.** The brief that +commissioned it cited "34 Gate-A passes on one 2848-line spec" against the 2026-08-17 ledger +row. That row says exactly that, and it has been superseded five times +(`docs/hardening-log.md:78,81,84,85,87`) — the line count is expressly excluded from the +consumer's evidence, and the pass total was taken from counting artifacts and then +attributed to a record that does not carry it. Supersession deliberately never alters a row, +so the row is still present, still matching its own grep, still counting, and still handing a +reader two retracted figures. Nothing was broken; the convention worked as designed. It is +recorded here as narrative evidence of what an opaque review record costs, not as a defect +this story repairs. + +## 2. Desired outcome + +Three outcomes, each observable in the shipped prompt text. + +1. **A cycle's mandatory pass count reflects what the story is worth reviewing**, instead of + charging every cycle the same three passes per gate. A trivial or docs-only story stops + paying a toll that buys nothing; a standard or high story keeps the floor it has today. +2. **Severity means product behaviour.** A reader of either §5 copy can decide, without + judgement calls, whether a finding about narration, about prose describing a mechanism, or + about a test instrument's internals is allowed to hold the loop open — and knows the one + case where an instrument finding still must (it demonstrates a false green on product + behaviour). Coverage-first is unchanged: the reviewer still reports everything, and the + filter stays ours. +3. **The §5 loop rules stop being amended one clause at a time.** The six open contract + questions the `fic2` cycle raised and declined to answer get one coherent answer, taken + together, with Daniel's five recorded decisions as settled inputs. The alternative — + answering them as they surface — is what produced two stop-and-surfaces and a revert + inside a single cycle. + +**Why now, and why together.** Parts 1 and 2 each change what a loop is allowed to do; part +3 is the accumulated debt of changing that a clause at a time. Shipping them separately would +reproduce the churn this story exists to end, which is why the consolidation is the +deliverable rather than a convenience. + +**Named out of scope**, so no criterion below absorbs them: + +- **Hook code.** No change to any file under `plugins/dev-workflow/hooks/`. Part 1 is + prompt-only policy over the already-shipped `.context/codex-gate.floor` knob (Daniel, + 2026-08-27). If design concludes prompt-only cannot hold, that is a stop-and-ask, not a + silent expansion. +- **Gate-call observability** — upstream in `mcp-codex-dev`, a different repo. +- **The pass-counter anomaly.** During `fic2`, seven validated passes were reported by the + hook as one. The cause is undiagnosed and diagnosing it needs hook-state inspection; it is + its own finding. +- **The CodeRabbit plan-metadata contradiction** — a docs follow-up, and only after + diagnosis. +- **The fixture-per-predicate question** — how much instrument a one-paragraph prose rule is + worth. The evidence doc carries it as an open question, not a commitment. +- **Any remedy to the supersession convention** the worked example above illustrates. Parked + as a `todos.md` candidate. + +## 3. Acceptance criteria + +- [ ] **The floor scales by profile, and both copies say which gates it moves.** `CLAUDE.md` + §5 and the `/workflow-init` inline template mirror each state a mandatory pass floor of + **1** for a trivial or docs-only cycle and **3** otherwise, and each states that this one + value governs **Gate A and Gate B alike**. Two levels, not three: `high` gets no extra + mandatory passes, taking its added rigor from lens sets and evidence mode instead. + Checkable by reading both copies. The two-gates clause is not decoration — + `codex-gate.sh:119` sets a single `floor`, consumed at `:946` for Gate B and at `:966` + for Gate A — which is why the docs-only arm is phrased as a **Gate-A** claim in both + copies: a docs-only cycle already has no Gate B to floor. +- [ ] **A non-default floor leaves a trace in history.** Both copies require a cycle that ran + a floor other than the default to record `floor N per ` in its closing + commit body. Checkable: the requirement is stated in both copies, and any cycle in this + story's own branch that runs a reduced floor carries the line. +- [ ] **The gate-off residual is named in the shipped text, not merely avoided.** Both copies + state that the floor value is agent-written, lives in per-clone gitignored state that no + reviewer sees in a diff, and that nothing verifies the written value against the story + profile — and that a floor of 1 is therefore the cheapest available gate-off lever. + Checkable by reading. This is a disclosure, not a guard: no mechanism is claimed for it. +- [ ] **Severity is pinned as a closed decision in both copies, with the carve-out stated at + the same place.** Both state that Blocker and Major claim product behaviour, an + invariant, or a contract, and that a finding whose subject is narration, prose about a + mechanism, or a test instrument's internals is **Minor** — collect, never iterate — + **except** an instrument finding that demonstrates a false green on product behaviour, + which keeps its severity. Both also still state that the reviewer reports every finding + with severity and confidence and that the filter is applied downstream by us; a copy that + drops coverage-first fails this criterion even if the severity rule is correct. +- [ ] **Each of Q1–Q6 is answered or rejected in the shipped text, with a reason, and the + answers do not contradict each other.** Q1 (clean-completion precedence), Q2 (a declined + expansion's exit), Q3 (scope stop vs. clean completion), Q4 (whether a decline binds + later passes in the same cycle), Q5 (whether the three universal rules may carry an + in-set qualification), Q6 (what the pass-4 report does when prior-pass history is + unavailable) — as stated in `docs/field-reports/2026-08-26-fic2-cycle-evidence.md`. Where + an answer qualifies one of the three universal rules — the Blocker/Major-resolve duty, + the rule that a surfaced finding stays open with resolution unwaived, and the rule that + no pass carrying it counts as clean — the qualification appears **at each of those three + rules** in both copies, not only at the new clause. Q2 was reverted last cycle precisely + because that did not happen. +- [ ] **Every condition of the replaced prose is accounted for.** The change lists what each + replaced §5 passage required and marks each requirement kept, moved, or deliberately + dropped, per the AGENTS.md Don't quoted in §4. Checkable: the accounting exists and + covers every rule the diff rewrites. The failure this guards against has occurred: the + profiles cycle lost conditions ten times, once making an eligible profile *sufficient* + for a Gate-B skip — the gate-off path that change existed to close. +- [ ] **The two copies stay in parity.** Every rule this story changes reads the same in + `CLAUDE.md` §5 and in the `/workflow-init` template, except where a wording difference is + deliberate and stated as such. Checkable by diffing the two regions. + +## 4. Affected AGENTS.md invariants + +- `## Hook` — "**The hook always exits 0.** It is advisory; a reminder that can fail closed + would make the workflow unusable whenever Codex is down or the environment is odd." The + prompt-only decision for part 1 rests on this: today's floor of 3 is already advisory, so an + agent-written floor adds no new enforcement class. +- `## Hook` — "**Loose in the firing direction.** On uncertainty, fire. A missed commit + (false ✓) is the dangerous direction; a redundant warning is the accepted price." Lowering a + floor moves against this direction, which is what the residual-risk criterion discloses. +- `## Prompts and scaffolding` — "**`/workflow-init`'s templates stay inline** in the command + body." The mirror edit lands in the command body, never in a file read from disk. +- `## Prompts and scaffolding` — "**Prompt changes pass `docs/prompt-standards.md`** — all 12 + checklist items… **no comprehensive mechanical checker exists for them**: review is the + gate." Named because one of the three narrow checks that *do* exist is "the finding-severity + vocabulary stated as a closed set in both prompt copies", which part 2 edits directly. +- `## Packaging` — "**A plugin change requires a version bump.** A pull request that changes + any path under a `plugins//` directory **that still exists at HEAD**… must also change + that plugin manifest's `version`, or CI fails." The template mirror is under + `plugins/dev-workflow/`, so this fires. +- `## 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." This is the governing constraint on part 3 and the basis of its + criterion. +- `## Don'ts` — "**Never describe what a gate proves without checking what it actually + compares.**… for every sentence about a gate, name the exact comparison the code performs, + and delete any part of the sentence that outruns it." Every sentence this story writes about + what a floor or a severity class *does* is subject to it. + +## 5. Open questions + +- **Which floor governs a cycle citing several stories with different profiles?** §5 already + aggregates the other dimensions explicitly — the battery runs once, each profiled story + satisfies its own mode, lens sets are unioned, skip-eligibility requires unanimity — and a + scaled floor adds a dimension with no aggregation rule. Lowest, highest, and per-story are + all defensible and they disagree. +- **Does a profile change mid-cycle move the floor for passes already run?** §5 makes the + story header the single writable copy, read fresh at each pass, and says passes run under a + lower profile keep counting toward the floor. If the floor itself is now profile-derived, a + mid-cycle raise changes the target after some passes are already banked. +- **What floor does an unprofiled cycle get?** §5 has three defined cases for an unresolvable + or absent profile; the default of 3 is the obvious answer, but it is currently unstated. + +## 6. Suggested size + +`story` — three parts, but one coherent change to one subsystem's rules in two mirrored +copies. Part 3's value *is* being done once; splitting it would reproduce the clause-by-clause +churn the story exists to end. From 1773b321489f59383bb9372e95b37a6fe0cabdf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 10:11:06 +0200 Subject: [PATCH 003/117] chore: ignore docs/ideas/ and docs/research/ repo-wide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces a per-clone .git/info/exclude, so the policy travels with the repo instead of living in one checkout. docs/field-reports/ is deliberately NOT ignored -- field reports are tracked, because a field-intake round cites them as its evidence. The two directories' contents are not committed. Gate B: SKIPPED under CLAUDE.md §5's triviality skip -- not exempt. .gitignore is a non-.md file, so §5's prose exemption (docs/**.md, README.md, MANIFEST.md) does not reach it and it would fire full Gate B by default. The skip applies instead because the change cites no story, so it is unprofiled and the pre-existing judgement test is the whole test -- and adding two ignore patterns is behaviourally trivial: it changes which paths git reports as untracked, and no prompt, hook, script or CI input reads .gitignore. Battery: green, exit 0 -- the full AGENTS.md § Commands chain. shellcheck over all six shell files; the hook suite under sh and under dash; check-invariants.test.sh (148 assertions) and check-invariants.sh; check-version-bump.test.sh (36 assertions) and check-version-bump.sh main; claude plugin validate . --strict. Zero failing assertions. --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 92dad75..f4fea79 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,10 @@ _unrelated-seo-work/ # passCount hands every fresh clone a pre-counted Gate-B pass. .context/* !.context/codex-gate.on + +# Local drafts and reading notes — working material, not repo content. `docs/field-reports/` +# is deliberately NOT here: field reports are tracked, because a field-intake round cites +# them as its evidence. These two replace a per-clone `.git/info/exclude`, so the policy +# travels with the repo instead of living in one checkout. +docs/ideas/ +docs/research/ From 808d83e73a099e322f244c7708809f11f65b2d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:24:01 +0200 Subject: [PATCH 004/117] docs(story): add the floor-demonstration criterion and route the economics to P8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Daniel, 2026-08-28, on brainstorming question 1: the story grounded its problem in four measurements and accepted entirely in prose -- none of its seven criteria would have failed if the change made loops more expensive. Criterion 8 closes the near half: one docs-only or trivial cycle on this branch runs its gate at floor 1 and its closing body carries `floor 1 per `, against the 3 it owed before. The criterion states its own ceiling -- it shows the knob and provenance path work, and is not evidence that loops got cheaper. Reading a working mechanism as an improved outcome is the overclaim class AGENTS.md calls this repo's most persistent defect, so the wording forecloses it rather than leaving it to a reader. The far half -- whether loops actually got cheaper -- is deferred to the P8 passive-metrics story rather than a fresh backlog row, because loose deferred items rot here: the pass-counter anomaly, the fixture-per-predicate question and the durations row's missing control run are all still open. Trigger: after roughly three profiled cycles under the new rules, read their pass counts and finding distributions against the fic2 baseline 14 · 24 · 12 · 3 · 6 · 6 · 2. The P8 annotation records two things the hand-off must not assume, since that row's scope rests on "the data already exists": the per-pass curve reaches git only by habit (3cdd075 and baa75c1 carry it, 7bbdb14 carries only the total) and the findings files behind it live under gitignored .context/; and whether closing bodies should be REQUIRED to carry the curve is a §5 rule this story does not decide. P8's scope is unchanged -- read-only, no instrumentation, nothing written back. Profile unchanged (high / none / battery+check+verification), so no profile-log line: the log records changes, and this is not one. Gate B: N/A -- both staged paths are docs/**.md under CLAUDE.md §5's prose exemption. No prompt, no plugin path, no non-.md file. --- ...4-passive-metrics-over-the-ledger-story.md | 26 +++++++++++++++++++ ...-review-loop-economics-pass-floor-story.md | 21 +++++++++++++++ 2 files changed, 47 insertions(+) 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 index 07b4638..dc89293 100644 --- 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 @@ -31,6 +31,32 @@ From `todos.md`, "**P8 — passive metrics, read-only over the ledger and git.** | 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 | +### Second question routed here, added 2026-08-28 + +`docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` defers its +economic measurement to this story rather than to a fresh backlog row. The question it hands +over: **after roughly three profiled cycles run under the new floor and severity rules, do their +pass counts and finding distributions differ from the `fic2` baseline curve of +14 · 24 · 12 · 3 · 6 · 6 · 2** (`docs/field-reports/2026-08-26-fic2-cycle-evidence.md`)? That +story cannot answer it inside its own cycle, and a single cycle would not answer it in any case. + +Two things this hand-off does not silently assume, because the row's "the data already exists" +condition is doing real work: + +- **The per-pass curve is only sometimes in git.** Closing commit bodies carry it by habit, not + by rule: `3cdd075` records "Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0" and + `baa75c1` records per-pass counts, while `7bbdb14` gives the pass total and no distribution. + The findings files those numbers come from live under `.context/`, which is gitignored and + per-clone. So this question is answerable from git only for cycles whose author wrote the + curve down. +- **Whether closing bodies should be *required* to carry the curve is not decided here**, and + it is not this story's to decide — it is a §5 rule, and it belongs to whoever owns that text. + Recorded so the gap is visible rather than discovered later by an analysis that quietly + reports on the subset of cycles that happened to be legible. + +Neither point changes this story's scope: still read-only, still no instrumentation, still +nothing written back. + ## 2. Desired outcome A reader can ask the ledger which fingerprints recur and how a rung has held, and get the answer diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index e2793cb..705584c 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -84,6 +84,18 @@ deliverable rather than a convenience. worth. The evidence doc carries it as an open question, not a commitment. - **Any remedy to the supersession convention** the worked example above illustrates. Parked as a `todos.md` candidate. +- **The economic measurement itself** — whether loops actually got cheaper. It cannot be + observed inside this cycle: the severity rule's effect appears only across several cycles + run under it, and claiming otherwise from a single cycle would be the fabricated-evidence + failure §5 names. It is **deferred to a named vehicle rather than to a new backlog row** — + `docs/superpowers/stories/2026-08-04-passive-metrics-over-the-ledger-story.md` (the P8 + passive-metrics story, `todos.md:546`), annotated with this trigger: after roughly three + profiled cycles under the new rules, read their pass counts and finding distributions + against the `fic2` baseline curve of 14 · 24 · 12 · 3 · 6 · 6 · 2 + (`docs/field-reports/2026-08-26-fic2-cycle-evidence.md`). A named vehicle rather than a + fresh row because loose deferred items rot here — the pass-counter anomaly, the + fixture-per-predicate question and the durations row's missing control run are all still + open, and all three are named above or in §1. ## 3. Acceptance criteria @@ -133,6 +145,15 @@ deliverable rather than a convenience. - [ ] **The two copies stay in parity.** Every rule this story changes reads the same in `CLAUDE.md` §5 and in the `/workflow-init` template, except where a wording difference is deliberate and stated as such. Checkable by diffing the two regions. +- [ ] **A reduced floor is demonstrated once, end to end, on this branch.** A docs-only or + trivial cycle here runs its gate at floor 1 and its closing commit body carries + `floor 1 per ` — against the 3 that same cycle owed before this change. + **What this shows is bounded, and the criterion is worded so it cannot be read wider: + the knob and the provenance path work.** It is not evidence that review loops became + cheaper. One cycle at a reduced floor measures the mechanism, not the economics, and + reading a working mechanism as an improved outcome is the overclaim class AGENTS.md + names as this repo's most persistent defect. The economics are measured afterwards, by + the follow-up named in §2 — this criterion deliberately does not stand in for it. ## 4. Affected AGENTS.md invariants From 996825258afee630e8fd49551bc9b464fe8003a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 12:48:33 +0200 Subject: [PATCH 005/117] docs(story): one floor predicate, and require the per-pass curve in closing bodies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two approved changes from the sectioned design (Daniel, 2026-08-28). Criterion 1 drops the `docs-only` arm for a single predicate, `max(risk, security) == 0`. The arm did no work and, read path-wise, did the wrong work: a diff-derived reading cannot serve Gate A, which runs on a spec before any diff exists; a story-declared reading is already subsumed, since intake defines `trivial` as no behavioural effect and a documentation change has none. And path-derived would be wrong in this repo specifically -- docs/hardening-log.md is a docs/**.md path that drives rung escalation, so "docs" does not imply "changes nothing". That is part 2's reachability test deciding a part 1 question, which is the argument for one change rather than two. Story open question 3 is answered by the same move and kept struck through rather than deleted. New criterion: closing commit bodies carry the per-pass finding and Blocker counts in one pinned form, per the 3cdd075 precedent. Approved as a scope addition. Without it the economics measurement routed to P8 reads only the cycles whose author happened to write the curve down -- 3cdd075 and baa75c1 did, 7bbdb14 recorded the total and no distribution -- because the findings files live under gitignored .context/. It is also the durable half of Q6: a curve in a commit body survives a fresh checkout and a cleared .context/. Profile unchanged (high / none / battery+check+verification), so no profile-log line. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- ...-review-loop-economics-pass-floor-story.md | 43 ++++++++++++++----- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 705584c..91b78e3 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -99,15 +99,34 @@ deliverable rather than a convenience. ## 3. Acceptance criteria -- [ ] **The floor scales by profile, and both copies say which gates it moves.** `CLAUDE.md` - §5 and the `/workflow-init` inline template mirror each state a mandatory pass floor of - **1** for a trivial or docs-only cycle and **3** otherwise, and each states that this one - value governs **Gate A and Gate B alike**. Two levels, not three: `high` gets no extra - mandatory passes, taking its added rigor from lens sets and evidence mode instead. - Checkable by reading both copies. The two-gates clause is not decoration — - `codex-gate.sh:119` sets a single `floor`, consumed at `:946` for Gate B and at `:966` - for Gate A — which is why the docs-only arm is phrased as a **Gate-A** claim in both - copies: a docs-only cycle already has no Gate B to floor. +- [ ] **The floor scales by profile, on one predicate, and both copies say which gates it + moves.** `CLAUDE.md` §5 and the `/workflow-init` inline template mirror each state a + mandatory pass floor of **1** where `max(risk, security)` is 0 and **3** otherwise — + unprofiled cycles included, which keep today's 3. Two levels, not three: `high` gets no + extra mandatory passes, taking its added rigor from lens sets and evidence mode instead. + Each copy also states that this one value governs **Gate A and Gate B alike**, which is + not decoration: `codex-gate.sh:119` sets a single `floor`, consumed at `:946` for Gate B + and at `:966` for Gate A. + **One predicate, not two** (Daniel, 2026-08-28). An earlier draft added a `docs-only` + arm; it is dropped because it does no work and, read path-wise, does the wrong work. + A diff-derived reading cannot serve Gate A at all — Gate A runs on a spec, before any + diff exists. A story-declared reading is already subsumed: intake defines `trivial` as + "no behavioural effect in the artifact's own execution context", which a documentation + change has none of. And a path-derived arm would be **wrong in this repo specifically** — + `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation, so "docs" + does not imply "changes nothing". That is Section A's reachability test deciding a floor + question, which is why the two parts belong in one change. +- [ ] **A cycle's closing commit body carries its per-pass shape.** Both copies require the + closing commit of a Gate-B cycle to record the per-pass finding and Blocker counts, in + one pinned greppable form, following the `3cdd075` precedent + (`Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0.`). Checkable: the + requirement is stated in both copies, and this story's own closing commit carries it. + **Why it is in scope** (approved as a scope addition, Daniel, 2026-08-28): the deferred + economics measurement routed to P8 is otherwise answerable only for cycles whose author + happened to write the curve down — `3cdd075` and `baa75c1` did, `7bbdb14` recorded the + pass total and no distribution — because the findings files behind those numbers live + under gitignored `.context/`. It is also the durable half of Q6: a curve in a commit body + survives a fresh checkout, a cleared `.context/` and a different machine. - [ ] **A non-default floor leaves a trace in history.** Both copies require a cycle that ran a floor other than the default to record `floor N per ` in its closing commit body. Checkable: the requirement is stated in both copies, and any cycle in this @@ -194,8 +213,10 @@ deliverable rather than a convenience. story header the single writable copy, read fresh at each pass, and says passes run under a lower profile keep counting toward the floor. If the floor itself is now profile-derived, a mid-cycle raise changes the target after some passes are already banked. -- **What floor does an unprofiled cycle get?** §5 has three defined cases for an unresolvable - or absent profile; the default of 3 is the obvious answer, but it is currently unstated. +- ~~**What floor does an unprofiled cycle get?**~~ **Answered 2026-08-28** by the + single-predicate decision in criterion 1: no profile means `max(risk, security)` is not 0, + so the default of 3 stands. Kept rather than deleted, because the answer is only obvious + once the predicate is one thing. ## 6. Suggested size From 95040b8635089688c8d8a3479ebf1b58b5a6da7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:23:24 +0200 Subject: [PATCH 006/117] docs(spec): design the review-loop economics change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec for docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md (risk high / security none / battery+check+verification, read from that header). The design rests on one finding: §5 has four ways a cycle can stop and four standing duties, and never says which wins when two apply. That is why the fic2 cycle could not ship two small clauses without qualifying three rules nobody proposed changing. §5 already contains the resolution in two places and never draws it -- only clean completion CLOSES; the scope stop, the clearly-stuck exit and the two-tell stop SUSPEND. Suspensions therefore compose rather than conflict, and three of the four duties turn out to be preconditions rather than participants. Three reachable conflicts remain, of which one needed deciding. Part 3 encodes that ordering plus the settled qualification: the no-clean-pass-carrying-a-surfaced-finding duty is qualified for, and only for, a finding the user has explicitly declined -- defined tightly as a recorded, attributable decision on that specific finding, never silence, never a general remark, never inferred. Part 2 keys severity on consequence, not artifact kind, via a named-reader test; the field record falsifies the artifact-kind form, since one fic2 pass-5 finding on a story criterion was correctly acted on while two on the same artifact were correctly parked. Part 1 is one predicate, max(risk, security) == 0, with no docs-only arm: a path-derived arm would be wrong in this repo, where docs/hardening-log.md is a docs/**.md path that drives rung escalation. Also carried: Q6 answered by computability with a mandatory cause-naming disclosure, its stated failure direction, and its durable half (per-pass curves in closing bodies); the mid-cycle profile rule with the raise consequence that the floor arithmetic otherwise hides; and an old-conditions table of ten sites, eight changing -- including one that no search for "3" finds and that silently inverts under floor 1. Every line citation, quoted passage and commit hash in the spec was verified mechanically before commit. The one quote absent from the template is the prerequisite §7 names, not an error. Gate A: not yet run -- this commit is the artifact Gate A reviews. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 362 ++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-28-review-loop-economics-design.md diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md new file mode 100644 index 0000000..596293e --- /dev/null +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -0,0 +1,362 @@ +# Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design + +**Date:** 2026-08-28 +**Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` +**Profile (read from that header, not copied):** risk `high` · security `none` · +validation `battery+check+verification`, no `+abuse-path`. + +Prompt-only. No file under `plugins/dev-workflow/hooks/` changes. + +**Two surfaces, both edited in every change below:** `CLAUDE.md` §5 (lines 65–589) and its +mirror inside `/workflow-init`'s inline `CLAUDE.md` template +(`plugins/dev-workflow/commands/workflow-init.md`, fenced at 192–778; §5 is 257–777). The two +already differ on 192 lines across 20 hunks; this design touches only the rules it changes, +per story criterion 7, and states each deliberate variance where it creates one. + +--- + +## 1. The finding this design is built on + +§5 has **four ways a cycle can stop** and **four standing duties**, each stated in its own +bolded paragraph, each qualifying the ones before it — and nowhere does §5 say which wins when +two apply at once. + +That is not a wording gap. It is why the `fic2` cycle could not ship two small clauses without +qualifying three rules nobody proposed changing: two clauses met an eight-way lattice with no +stated ordering, and the lattice pushed back. The revert was correct. + +So "consolidated, done once instead of clause-by-clause" does not mean *answer six questions in +one sitting*. It means **state the ordering once**, after which most of the answers are +readings of it rather than new rules. + +### 1.1 Only one exit closes + +Both facts are already in §5, in two separate places, and neither draws the conclusion: + +> "Stopping this way is **not an exit from the gate**: the floor, the Blocker/Major filter and +> the clean-final-pass rule all stand, and the loop resumes on the revised artifact once the +> question is answered." + +> "You surface *with the finding still open* — the resolve rule is not waived, no pass is +> credited as clean, and the loop resumes on whatever the user decides." + +**Clean completion closes. The scope stop, the clearly-stuck exit and the two-tell stop +suspend** — they surface to the human and the loop resumes. + +Consequence: **suspension × suspension is not a conflict.** Two suspensions at once means the +pass report carries both reasons. Three pairs, no ordering, one sentence. + +### 1.2 Three of the four duties are not participants + +- **The floor** is a quantity gating closure — "**Below the floor nothing closes**". Not + orderable. +- **Blocker/Major must resolve** is definitional: a clean pass is Blocker/Major-free by + construction. +- **A surfaced finding stays open** is the *mechanism* that makes the other three exits + suspensions rather than closes. +- **No pass carrying a surfaced finding counts as clean** is the only genuine participant. It + couples an open finding to closure, and every remaining question lives there. + +--- + +## 2. The precedence structure (part 3) + +Shipped as one short ordering, stated once per copy, with the individual rules referencing it +rather than restating it. + +| Conflict | Resolution | +|---|---| +| clean completion × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim rather than re-derived. Recorded as settled and probably unreachable — a clean pass has no regenerating Blocker/Major, so the exit's third condition fails. Kept because dropping a sentence §5 already spends is the dropped-condition failure story criterion 6 exists to prevent. | +| clean completion × two-tell stop | **Clean wins.** Daniel's recorded decision, encoded not reopened. | +| clean completion × scope stop | **Clean wins only when every open surfaced finding has been explicitly declined.** Otherwise the scope stop's finding keeps the pass unclean and outranks closure automatically. | + +**Why the third is the only one that needed deciding, and why it reads as it does.** A scope +stop is triggered by a *specific finding*. While the duty stands unqualified, that finding is +open, so the triggering pass cannot be clean, so the scope stop wins automatically — no cell +required. The cell exists only because the duty may now be qualified. + +**The asymmetry that explains the whole history:** the two-tell stop is triggered by +*statistics about findings* — the five tells — not by a finding. Nothing is left open, the duty +does not bite, and clean can win. The scope stop's trigger **is** a finding. The reverted clause +tried to unbite that by qualifying three universal rules at once. The lattice was not being +awkward; it was correct. + +### 2.1 The qualification + +**The duty "no pass carrying a surfaced finding counts as clean" is qualified for, and only +for, a finding the user has explicitly declined.** + +Per story criterion 5, this qualification appears **at each of the three universal rules** in +both copies — the Blocker/Major-resolve duty, the surfaced-finding-stays-open rule, and the +no-clean-pass-carrying-it rule — not only at the new clause. Placing it only at the new clause +is what made the earlier attempt unshippable. + +**"Explicitly declined" is defined tightly, because the safety of the whole qualification rests +on it.** A decline is a **recorded user decision on that specific finding**, attributable and +unambiguous, durably written down — the `-dispositions.md` companion is the natural home. +It is **never** silence, never a general remark about scope, and never the agent inferring a +decline from context. The shipped text says all three negatives explicitly; a loose reading here +converts a human gate into an agent's judgement, which is the failure the whole rule exists to +prevent. + +**Consequence for Q4, encoded rather than asked again:** a decline binds for the remainder of +the cycle. A declined finding does not re-stop later passes. An *undecided* out-of-set finding +still holds the cycle open — nothing closes unanswered. + +**What this makes shippable:** the reverted clause, in decline-keyed form. + +--- + +## 3. Severity semantics (part 2) + +**The rule.** Blocker and Major claim product behaviour, an invariant, or a contract. A finding +whose subject is narration, prose about a mechanism, or a test instrument's internals is +**Minor** — collect, never iterate — **except** an instrument finding demonstrating a false +green on product behaviour, which keeps its severity. + +**The decision procedure, keyed on consequence and not on artifact kind:** + +> Name the gate, skill, rule, escalation procedure or scaffolded template that reads this text, +> and the decision it takes differently if the text is wrong. If you cannot name an in-system +> reader and a changed decision, it is Minor. + +**A human reader never satisfies the test.** That cost class is already priced as non-gating by +§5's prose exemption — "a wrong sentence costs a confused reader, not broken behaviour". Without +this clause the test admits everything, since a future maintainer reads every file. + +**Why not a list of demotable artifact kinds.** The field record already falsifies that form. Of +three `fic2` pass-5 findings on one parked story's acceptance criterion, two were correctly +parked and **one was correctly acted on** — "the pass-4 activation boundary — because a future +rewrite could otherwise move the duty to pass 1 while checking off every other listed +condition." Same artifact, same pass, same cluster, opposite correct answers. Artifact class +gets that wrong; consequence gets it right. An enumeration would also travel into scaffolded +repos whose artifact kinds we have never seen, which is invariant 10's stack-neutrality problem +in a new place. + +**Kinship, stated in the shipped text.** This is the **finding-level analog of the path-level +prose exemption** — one principle at two granularities: text that *describes* the product versus +text that *is* the product. Naming the kinship makes each rule the other's consistency check and +gives Gate A something to check the predicate against. Two rules sharing an unstated principle +drift apart. + +**The boundary case, shipped with its example.** Rationale prose inside a rule file — §5's +"Recorded rationale" paragraphs, the why-sentences under AGENTS.md invariants — states no rule, +so no decision flips if it is wrong. **Minor.** Named explicitly because "it's in `CLAUDE.md`, +agents read `CLAUDE.md`" is the first stretch a reviewer will try. + +**Coverage-first is unchanged and stated alongside:** the reviewer reports every finding with +severity and confidence; the filter is ours, never Codex's. + +**Expected effect, stated so Gate A reads it as designed rather than finding it as a gap.** +PR #23-class distributions — 16 findings in a scratch harness, 10 in narration — demote almost +entirely. `fic2`'s pass-2 meta cluster demotes only **partially**: ledger rows keep their +severity because rung escalation reads the recurrence count, and story criteria keep theirs +because the assigned-fix-set rule reads them. **This design demotes less than the story's +problem statement might suggest, and that is correct rather than a shortfall.** + +--- + +## 4. The pass floor (part 1) + +**One predicate.** `max(risk, security) == 0` → floor **1**. Everything else → **3**, unprofiled +cycles included. + +Two levels, not three: `high` gets no extra mandatory passes, taking its added rigor from lens +sets and evidence mode. Raising the high floor would worsen the cost this story exists to reduce. + +**One value, both gates, stated in both copies.** `codex-gate.sh:119` sets a single `floor`, +consumed at `:946` for Gate B and `:966` for Gate A. A reader who does not know this will write +a rule for one gate and silently move the other. + +**Why there is no `docs-only` arm.** A diff-derived reading cannot serve Gate A at all — Gate A +runs on a spec, before any diff exists. A story-declared reading is already subsumed: intake +defines `trivial` as "no behavioural effect in the artifact's own execution context", which a +documentation change has none of. And a path-derived arm would be **wrong in this repo +specifically**: `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation, so +"docs" does not imply "changes nothing." That is §3's reachability test deciding a §4 question, +which is the argument for shipping the two parts together. + +**Mechanism and its residual, disclosed in the shipped text.** The floor is carried by the +already-shipped `.context/codex-gate.floor` knob, written by the agent from the story's profile. +The text states plainly that the value is agent-written, that it lives in per-clone gitignored +state no reviewer sees in a diff, that nothing verifies it against the story profile, and that +a floor of 1 is therefore the cheapest available gate-off lever. **This is a disclosure, not a +guard** — no mechanism is claimed for it. The knob rejects `0` and non-numeric input +(`codex-gate.sh:124-127`), which bounds typos and not intent. + +**Provenance.** A cycle running a floor other than the default records `floor N per ` in its closing commit body, so the value is auditable in history rather than only in +per-clone state. + +### 4.1 A profile that moves mid-cycle + +Composed from three rules §5 already has; no new rule. + +- The floor derives from the **current** profile at each pass — §5 already requires the header + to be read fresh at each pass, never remembered or copied. +- **Passes already run keep counting**, per the existing rule. +- **Closing requires meeting the floor as currently derived.** + +A raise moves the target prospectively; a lower lets already-banked passes suffice sooner. + +**The consequence that must be stated, or the arithmetic reads wrong:** under a **raise**, at +least one further pass is required *regardless of the floor arithmetic*, because §5 already +requires the final clean pass to run under the current profile. A previously-final clean pass +stops qualifying the moment the profile moves — so even a raise that leaves the floor unchanged +(`standard` → `high`, both 3) still costs a pass. Without this, "passes already run keep +counting" reads as "nothing further is needed." + +**On lowering, recorded so nobody later reads the floor as having opened this.** A lower drops +the floor *and* the lens sets *and* the evidence mode, so a `high` cycle lowered to `trivial` +can close on one pass. That is the pre-existing profile-change path — human-confirmed in both +directions and logged. The variable floor rides that path; it does not create it. + +--- + +## 5. Q6 — the pass-4 report when prior-pass history is unavailable + +**Answer: report what is computable, name what is not and why, and disclose the reduced +sensitivity.** Not a new stop condition, and not a mandatory resume note. + +The reasoning is arithmetic. Of the five tells, **three need history** — finding count rising, +Blocker count failing to fall, a require↔withdraw pair — and **two are computable from the +current pass alone**: findings clustering on the instrument, and findings clustering on prose. +So with no prior record the two-tell threshold **remains reachable** on the cluster pair. The +duty does not become inoperative; it loses sensitivity. + +**The disclosure is not optional, and names the cause.** The report says which tells could not +be computed **and why** — a fresh checkout, a cleared `.context/`, another machine, a cycle +resumed elsewhere — so the human sees a degraded reading rather than a clean one. + +**Why the alternatives are rejected**, recorded per story criterion 5: making the resume note +mandatory changes an artifact §5 explicitly calls advisory ("Nothing depends on it existing"); +treating unavailable history as its own stop condition would halt every cycle resumed on a +fresh checkout; restarting the pass-4 clock at the first *visible* pass silently lowers coverage +without saying so. + +**Stated risk, because this answer has a direction.** Reporting rather than stopping fails +*toward continuing* the loop, which is the direction AGENTS.md invariant 2 questions for the +hook. The mandatory cause-naming disclosure is what makes the answer acceptable; without it the +design would take a different one. + +### 5.1 Q6's durable half + +Closing commit bodies carry the per-pass finding and Blocker counts, in one pinned greppable +form following the `3cdd075` precedent: + +``` +Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0. +``` + +Two reasons, both load-bearing. **History that survives**: a curve in a commit body outlives a +fresh checkout, a cleared `.context/` and a different machine, which is exactly the availability +gap Q6 names. **A measurable subset**: the economics measurement deferred to the P8 story is +otherwise answerable only for cycles whose author happened to write the curve down — `3cdd075` +and `baa75c1` did, `7bbdb14` recorded the pass total and no distribution — because the findings +files behind those numbers live under gitignored `.context/`. Without this, P8 would report on a +self-selected subset with nothing marking that it did. + +--- + +## 6. Old-conditions accounting + +Required by the AGENTS.md Don't and by story criterion 6. Making the floor variable falsifies +prose that assumes it is 3. **Ten sites, eight changing**, identical in both copies. + +| # | `CLAUDE.md` | template | Text | Disposition | +|---|---|---|---|---| +| 1 | `:72` | `:272` | "min 3 passes per run" | **changes** — the floor statement itself | +| 2 | `:77` | `:277` | "if pass 3 still finds Blocker/Major, keep going until clean" | **changes** — at floor 1 the relevant pass is not pass 3 | +| 3 | `:236` | `:421` | "don't count it toward the 3-pass floor" | **changes** → "the floor" | +| 4 | `:403` | `:582` | "The 3-pass floor, the Blocker/Major filter…" | **changes** → "the floor" | +| 5 | `:249` | `:434` | "PR #23's Gate-B pass 3 returned all four findings at `IMPORTANT`" | **stays 3** — cites an actual historical pass, not a rule | + +**Plus the one that no search for "3" finds**, in both copies: + +> "**Below the floor nothing closes**, and a zero-finding pass remains the only exception, +> exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps looping." + +Correct under floor 3. **False under floor 1**, where pass 1 is *at* the floor and therefore +closes. It becomes "a Blocker/Major-free pass **below the floor** carrying a Minor keeps +looping." This is the site worth the most attention in the whole change: a rule that silently +inverts in exactly the configuration part 1 introduces, invisible to any grep for the digit +because it says "pass 1." + +The plan carries the exact replacement wording per site. + +--- + +## 7. Prerequisite: the template lacks the sentence §3's kinship points at + +§3 names the new severity rule as the finding-level analog of the path-level prose exemption. +The rationale sentence it points at — "a wrong sentence costs a confused reader, not broken +behaviour" — exists in `CLAUDE.md` and is **absent from the `/workflow-init` template**. + +**Resolution: the template gets the rationale sentence**, so the kinship claim has a referent in +both copies. This is a deliberate reduction of the existing 192-line divergence at exactly one +seam, made because the new rule depends on it — not a general reconciliation, which stays out of +scope. + +--- + +## 8. Evidence plan + +Mode `battery+check+verification` (no `+abuse-path`; security is `none`). + +- **Battery** — the full `AGENTS.md` § Commands chain, green. +- **A check that fails without the change.** No automated test is possible for prose, so this + takes §5's other permitted route — a **named verification**, which owes the same + counterfactual. The subject is the §6 site list, which is differential by construction: + **posed as a question about behaviour under floor 1, the pre-change text answers two sites + wrongly** — site 2 sends a reader to "pass 3" when the floor is 1, and the "pass 1 carrying a + Minor keeps looping" sentence says a pass closes-or-loops opposite to what floor 1 requires — + **while the post-change text answers both correctly.** That is the observation that would + exist if the claim were false, and it is observable: it is a reading of two identified + sentences at two revisions, not a derivation from the change itself. + **The wiring must be able to produce the failure.** A verification that consults only the + post-change text cannot fail and would report success because of how it was wired. Both + revisions get read, and the entry says which sentences were read at which revision. The plan + pins the exact procedure; the spec fixes only its shape and its counterfactual. +- **A named verification of the risk path.** The risk path is the gate-off lever: a floor of 1 + in effect without a profile licensing it. Named here, procedure in the plan. + +**Instrument discipline, learned from this story's own evidence.** Two defects in the `fic2` +cycle's decision matrix were properties of the technique, not of that instance, and both apply +here: **a state's inputs must include every input the rule reads** (a matrix omitting an input +cannot distinguish the states that input separates, and still looks complete), and **a +counterfactual must distinguish ABSENT from CONTRADICTORY** (claiming a prior state contradicted +a rule it never contained reports a failure mode that state could not produce). Both survived a +full clean pass before being caught. + +**Known open question, not resolved here.** How much instrument a one-paragraph prose rule is +worth is carried by the evidence doc as a question, not a commitment. This design does not +answer it and does not pretend the fixture-per-predicate demand is settled. + +--- + +## 9. Out of scope + +Named so no part of the design absorbs them: hook code (any change under +`plugins/dev-workflow/hooks/`); gate-call observability (upstream, `mcp-codex-dev`); the +pass-counter anomaly, undiagnosed and needing hook-state inspection; the CodeRabbit +plan-metadata contradiction; the fixture-per-predicate question; any remedy to the supersession +convention; and general reconciliation of the two copies' 192-line divergence beyond the one +seam §7 names. + +--- + +## 10. Risks + +- **The disclosed gate-off lever is real and unguarded.** A floor of 1 in per-clone gitignored + state, agent-written, unverified against the profile. The design discloses it and claims no + mechanism. If that is unacceptable, the answer is hook code, which is out of scope by + decision — and re-opening it is a stop-and-ask, not a silent expansion. +- **The reachability test needs judgement** at the moment §5 is trying to remove judgement. The + named-reader-and-changed-decision phrasing is what makes it decidable; if Gate A finds it + admits or excludes a case wrongly, that is a finding about this design, not about the settled + contract. +- **Q6's answer fails toward continuing the loop.** Stated in §5 with the disclosure that makes + it acceptable. +- **This spec edits the rules that govern its own review.** Its Gate A runs under the old rules; + the new ones bind afterwards. Nothing here is retroactive, and the design does not assume + otherwise. From 057a9249c28768086f202e65e77df0aba5e05580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:25:09 +0200 Subject: [PATCH 007/117] docs(spec): correct the old-conditions count to match its own enumeration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The section claimed "Ten sites, eight changing" while enumerating twelve and ten: the five paired table rows are ten sites (row 5's pair stays), and the pass-1 sentence that follows the table is two more, both changing. Found in spec review by the sparring session under Daniel's 2026-08-28 delegation. A stated-count mismatch is precisely what CLAUDE.md §5's mechanical settle tells a reviewer to catch before spending judgement -- in the spec that carries that instruction. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- .../specs/2026-08-28-review-loop-economics-design.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 596293e..aeb4931 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -261,7 +261,9 @@ self-selected subset with nothing marking that it did. ## 6. Old-conditions accounting Required by the AGENTS.md Don't and by story criterion 6. Making the floor variable falsifies -prose that assumes it is 3. **Ten sites, eight changing**, identical in both copies. +prose that assumes it is 3. **Twelve sites, ten changing**, identical in both copies: the five +paired rows below (ten sites, of which row 5's pair stays) plus the paired sentence that follows +the table (two sites, both changing). | # | `CLAUDE.md` | template | Text | Disposition | |---|---|---|---|---| From 27562ba727f72ad597f9ebda7e47cb56d12d3cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:42:10 +0200 Subject: [PATCH 008/117] docs(story): re-aim criterion 8 at the floor this branch actually licenses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate-A pass 1 BLOCKER: criterion 8 was unsatisfiable. It demanded a floor-1 cycle on this branch, but this story is risk high, so max(risk, security) is 2 and every cycle citing it owes floor 3. I introduced the contradiction two commits earlier by dropping the docs-only arm: under the old two-arm reading a docs-only commit could reach floor 1; under the single predicate the floor comes from the profile. Criterion 8 now demonstrates the derivation and provenance line at floor 3 -- the value this branch really licenses -- and the floor-1 case becomes the first checkpoint of the P8 measurement: the first post-merge cycle whose cited-story set licenses floor 1 must carry the floor-1 provenance line. Deliberately NOT satisfied by minting a level-0 micro-story for the purpose. A fixture built to make a criterion pass is the fabricated-evidence class §5 names by name. Two other docs-only references corrected for the same reason: §2 desired-outcome 1 and the §1 problem statement now speak in profile terms. The only remaining mention is criterion 1's account of why the arm was dropped, which is correct as it stands. Decided by the sparring session under Daniel's 2026-08-28 delegation, and flagged to him for final-version review because criterion 8 was his explicit choice in a decision round. The falsifiability he chose is preserved by three things: the §8 differential named verification, the provenance line on this branch now, and the P8 checkpoint later. Profile unchanged, so no profile-log line. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- ...-review-loop-economics-pass-floor-story.md | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 91b78e3..8cc510b 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -31,7 +31,8 @@ Four measurements, each citable: under one setting, not a demonstration of what caused those durations. A three-pass floor is charged to every cycle regardless of what the change is worth -reviewing, and a docs-only or trivial story pays the same toll as one that rewrites a gate. +reviewing, so a story with no behavioural surface pays the same toll as one that rewrites a +gate. **A worked example of the same class, found while writing this story.** The brief that commissioned it cited "34 Gate-A passes on one 2848-line spec" against the 2026-08-17 ledger @@ -49,8 +50,9 @@ this story repairs. Three outcomes, each observable in the shipped prompt text. 1. **A cycle's mandatory pass count reflects what the story is worth reviewing**, instead of - charging every cycle the same three passes per gate. A trivial or docs-only story stops - paying a toll that buys nothing; a standard or high story keeps the floor it has today. + charging every cycle the same three passes per gate. A story the profile puts at level 0 + stops paying a toll that buys nothing; a standard or high story keeps the floor it has + today. 2. **Severity means product behaviour.** A reader of either §5 copy can decide, without judgement calls, whether a finding about narration, about prose describing a mechanism, or about a test instrument's internals is allowed to hold the loop open — and knows the one @@ -164,15 +166,26 @@ deliverable rather than a convenience. - [ ] **The two copies stay in parity.** Every rule this story changes reads the same in `CLAUDE.md` §5 and in the `/workflow-init` template, except where a wording difference is deliberate and stated as such. Checkable by diffing the two regions. -- [ ] **A reduced floor is demonstrated once, end to end, on this branch.** A docs-only or - trivial cycle here runs its gate at floor 1 and its closing commit body carries - `floor 1 per ` — against the 3 that same cycle owed before this change. - **What this shows is bounded, and the criterion is worded so it cannot be read wider: - the knob and the provenance path work.** It is not evidence that review loops became - cheaper. One cycle at a reduced floor measures the mechanism, not the economics, and - reading a working mechanism as an improved outcome is the overclaim class AGENTS.md - names as this repo's most persistent defect. The economics are measured afterwards, by - the follow-up named in §2 — this criterion deliberately does not stand in for it. +- [ ] **The provenance path is demonstrated end to end on this branch, at the floor this + branch actually licenses.** This story is risk `high`, so every cycle citing it owes + floor **3** — and its closing commit body carries `floor 3 per `, showing the + derivation and the provenance line working at a real value. + **The floor-1 demonstration is not on this branch, deliberately.** It was in an earlier + draft and was unsatisfiable: a criterion demanding a floor-1 cycle here contradicts this + story's own profile, and the only way to satisfy it as written would have been to mint a + level-0 micro-story for the purpose — a fixture built to make a criterion pass, which is + the fabricated-evidence class §5 names. Instead the floor-1 case becomes the **first + checkpoint of the P8 measurement**: the first post-merge cycle whose cited-story set + licenses floor 1 must carry the floor-1 provenance line, and P8 reads it. + **What is demonstrated here stays bounded, and the wording forecloses reading it wider:** + the derivation, the knob and the provenance path work. It is not evidence that review + loops became cheaper. Reading a working mechanism as an improved outcome is the overclaim + class AGENTS.md names as this repo's most persistent defect, and the economics are + measured afterwards by the follow-up named in §2. + *(Revised 2026-08-28 after Gate-A pass 1 found the original unsatisfiable — sparring + session, under Daniel's 2026-08-28 delegation; flagged to Daniel for final-version review + because criterion 8 was his explicit choice. The falsifiability he chose is preserved by + the §8 differential verification, the provenance line now, and the P8 checkpoint later.)* ## 4. Affected AGENTS.md invariants From dc774b0e5e6a7a4e7cddee9c3a4ea7c73f3a8511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:45:32 +0200 Subject: [PATCH 009/117] =?UTF-8?q?docs(spec):=20revision=202=20=E2=80=94?= =?UTF-8?q?=20all=2024=20Gate-A=20pass-1=20Blocker/Major=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 1 returned 27 findings (5 BLOCKER, 19 MAJOR, 3 MINOR). Four opened contract questions and were routed; all four came back answered and are folded in here with the rest. The five Blockers: - The docs-only arm survived in the story after being dropped from the predicate, making criterion 8 unsatisfiable (fixed in 27562ba). - §6's old-conditions accounting covered only part 1's floor wording while parts 2 and 3 rewrite eleven further passages -- the AGENTS.md Don't violated inside the section citing it. §6 now enumerates all twelve passages by bold lead-in, each verified present exactly once in both copies. - The floor knob had no lifecycle, so a 1 written by a trivial cycle would persist into the next high cycle -- the gate-off lever firing by accident. §4.1 now writes at cycle start, re-derives on profile change, verifies the read-back, and removes at the closing amend; absence is the safe state. - The decline read as contradicting Mechanics' "Scope, and it is narrow". §2.1 now separates them explicitly: the human-exception form authorizes nothing by §5's own words; the decline reuses its TRANSPORT as a distinct record type whose effect comes from the loop's scope rule, not from human assent. - The multi-story floor was unanswered. Now unanimity -- floor 1 only if every cited story is profiled at level 0 -- following §5's own skip-eligibility precedent and invariant 2's firing direction. Nineteen Majors, the load-bearing ones: the decline's durable home moves from the advisory gitignored dispositions companion to the commit body; the accept branch is specified symmetrically with the decline (the surfaced-finding hold ends on the user's answer either way, so the deadlock was apparent, not real); findings get an identity rule that resolves toward "new finding, hold applies" when unclear; the severity subject-list becomes illustration and the reachability test governs alone; the instrument carve-out runs in both directions, since a false red also changes what the gate concludes; §5.1's durability claim is corrected to ACROSS cycles, because a closing commit does not exist at pass 4 of the cycle still running; Q6 gains partial, malformed and stale history as distinct shapes; the curve's form is pinned for two-branch passes; squash carry gains the three new record types; the pass report must expose the derived floor and the value read back; concurrency is stated as a limitation, not guarded; rollout says a template edit does not update downstream copies; invariant 12's version bump and CHANGELOG enter the implementation surface; parity verification walks every changed passage; and §10 fixes the activation boundary -- a cycle in flight finishes under the rules it started with. One Major was validated against the tree before applying and changed the answer: the rationale-prose boundary case claimed rationale never flips a decision, but docs/prompt-standards.md:49-51 requires rationale precisely because models follow motivated rules better, and invariant 11 makes that checklist binding. The exclusion is now qualified -- Minor only where no rule's application depends on the rationale -- rather than blanket. Contract answers by the sparring session under Daniel's 2026-08-28 delegation; criterion 8's edit flagged to Daniel for final-version review. Gate A: pass 1 recorded at .context/codex-reviews/gate-a-spec-rle-pass-1.md; this revision is what pass 2 reviews. Floor 3, nothing clean yet. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 406 +++++++++++++----- 1 file changed, 287 insertions(+), 119 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index aeb4931..e7c2a1a 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 +**Date:** 2026-08-28 · **Revision:** 2, after Gate-A pass 1 (27 findings; 5 Blocker, 19 Major) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. @@ -17,17 +17,21 @@ per story criterion 7, and states each deliberate variance where it creates one. ## 1. The finding this design is built on -§5 has **four ways a cycle can stop** and **four standing duties**, each stated in its own -bolded paragraph, each qualifying the ones before it — and nowhere does §5 say which wins when -two apply at once. +**The loop has four exits and four standing duties**, each stated in its own bolded paragraph, +each qualifying the ones before it — and nowhere does §5 say which wins when two apply at once. -That is not a wording gap. It is why the `fic2` cycle could not ship two small clauses without -qualifying three rules nobody proposed changing: two clauses met an eight-way lattice with no -stated ordering, and the lattice pushed back. The revert was correct. +*Scope of that claim:* four exits **of the loop**. §5 carries other mandatory stops that are not +loop exits — a target file surviving deletion, an exhausted recovery budget, an unresolvable +profile, a blocking evidence or setup gap. Those halt the *procedure* rather than resolving the +*cycle*, they do not compete with clean completion, and this design does not reorder them. + +The missing ordering is why the `fic2` cycle could not ship two small clauses without qualifying +three rules nobody proposed changing: two clauses met a lattice with no stated ordering, and the +lattice pushed back. The revert was correct. So "consolidated, done once instead of clause-by-clause" does not mean *answer six questions in -one sitting*. It means **state the ordering once**, after which most of the answers are -readings of it rather than new rules. +one sitting*. It means **state the ordering once**, after which most of the answers are readings +of it rather than new rules. ### 1.1 Only one exit closes @@ -68,20 +72,20 @@ rather than restating it. |---|---| | clean completion × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim rather than re-derived. Recorded as settled and probably unreachable — a clean pass has no regenerating Blocker/Major, so the exit's third condition fails. Kept because dropping a sentence §5 already spends is the dropped-condition failure story criterion 6 exists to prevent. | | clean completion × two-tell stop | **Clean wins.** Daniel's recorded decision, encoded not reopened. | -| clean completion × scope stop | **Clean wins only when every open surfaced finding has been explicitly declined.** Otherwise the scope stop's finding keeps the pass unclean and outranks closure automatically. | +| clean completion × scope stop | **Clean wins only when every surfaced finding still open has been explicitly declined.** Otherwise the scope stop's finding keeps the pass unclean and outranks closure automatically. | -**Why the third is the only one that needed deciding, and why it reads as it does.** A scope -stop is triggered by a *specific finding*. While the duty stands unqualified, that finding is -open, so the triggering pass cannot be clean, so the scope stop wins automatically — no cell -required. The cell exists only because the duty may now be qualified. +**Why the third is the only one that needed deciding.** A scope stop is triggered by a *specific +finding*. While the duty stands unqualified, that finding is open, so the triggering pass cannot +be clean, so the scope stop wins automatically — no cell required. The cell exists only because +the duty may now be qualified. -**The asymmetry that explains the whole history:** the two-tell stop is triggered by -*statistics about findings* — the five tells — not by a finding. Nothing is left open, the duty -does not bite, and clean can win. The scope stop's trigger **is** a finding. The reverted clause -tried to unbite that by qualifying three universal rules at once. The lattice was not being -awkward; it was correct. +**The asymmetry that explains the whole history:** the two-tell stop is triggered by *statistics +about findings* — the five tells — not by a finding. Nothing is left open, the duty does not +bite, and clean can win. The scope stop's trigger **is** a finding. The reverted clause tried to +unbite that by qualifying three universal rules at once. The lattice was not being awkward; it +was correct. -### 2.1 The qualification +### 2.1 The qualification, and how a decision on a finding is recorded **The duty "no pass carrying a surfaced finding counts as clean" is qualified for, and only for, a finding the user has explicitly declined.** @@ -91,17 +95,63 @@ both copies — the Blocker/Major-resolve duty, the surfaced-finding-stays-open no-clean-pass-carrying-it rule — not only at the new clause. Placing it only at the new clause is what made the earlier attempt unshippable. +**Both branches, stated symmetrically in one sentence.** §5 already says the loop "resumes the +moment the user says whether the set now includes it" — resumption *is* the hold ending, and it +ends whichever way the answer went. So: + +- **Declined** → the finding is released as recorded-declined and stops blocking closure for the + remainder of the cycle. It does not re-stop later passes. +- **Accepted** → the finding enters the assigned set, where its **severity governs exactly as + Mechanics already says**: an accepted Blocker or Major must resolve; an accepted Minor or Nit + is collected and never iterated. In-set Minors have never blocked a clean pass at or above the + floor, so there is no deadlock — the apparent one assumes the surfaced-finding hold outlives + the user's answer, and the resume sentence says it does not. +- **Undecided** → the hold stands. Nothing closes unanswered. + +**Where the decision is recorded — the commit body, on rails §5 already ships.** The +human-exception machinery already solves exactly this transport problem: "an ungated change +records it in that commit; a Gate-A cycle in the spec or plan commit; a Gate-B cycle in the WIP +commit, restated by the closing amend", folded in mid-cycle by amend, carried into the squash +body, with an empty commit as a last-resort destination. **The decline reuses that transport and +is a different record type**, with its own label: + +``` +Declined finding: · · +Reason: +``` + +**The distinction from the human-exception form is stated explicitly in the shipped text, and it +is the substance of the Blocker-4 repair.** They are not the same record and must not read as +one: + +- The **human-exception form authorizes nothing** — §5 says so in its own words, and says it is + never the answer to a below-floor pass, an unclean final pass, or a `STOP and surface`, and + that neither a human's assent nor the record lets an agent close or continue a cycle. +- The **decline record has §5-defined effect**: it releases one specific finding from the + surfaced-finding hold. That effect comes from the loop's own scope rule — which already routes + set membership to the user and already resumes on the answer — not from human assent + overriding a mandatory rule. Every other mandatory rule stands: the floor, the Blocker/Major + filter, the clean-final-pass requirement. + +Without that paragraph a reader meets two instructions that give opposite actions for the same +declined scope finding. + **"Explicitly declined" is defined tightly, because the safety of the whole qualification rests on it.** A decline is a **recorded user decision on that specific finding**, attributable and -unambiguous, durably written down — the `-dispositions.md` companion is the natural home. -It is **never** silence, never a general remark about scope, and never the agent inferring a -decline from context. The shipped text says all three negatives explicitly; a loose reading here -converts a human gate into an agent's judgement, which is the failure the whole rule exists to -prevent. +unambiguous. It is **never** silence, never a general remark about scope, and never the agent +inferring a decline from context. The shipped text carries all three negatives; a loose reading +converts a human gate into an agent's judgement, which is the failure the rule exists to prevent. -**Consequence for Q4, encoded rather than asked again:** a decline binds for the remainder of -the cycle. A declined finding does not re-stop later passes. An *undecided* out-of-set finding -still holds the cycle open — nothing closes unanswered. +**Identity — how a declined finding is recognized again.** Findings carry no stable identifier, +so the decline record names the finding by **pass number, slot and line position at the time of +decline, plus its verbatim location field and a short quotation of its defect field**. A later +finding is the same finding when its location and defect match; **when that is unclear it is a +new finding and the hold applies**, which costs a question and never a silent release. A finding +that is split, merged or materially reworded is new by that test. This mirrors §5's existing +treatment of unclear set membership, which resolves toward *outside* for the same reason. + +**The dispositions companion stays what §5 says it is** — the advisory working copy, deletable +and rebuildable. The commit body is the record. No new artifact class, no `.gitignore` change. **What this makes shippable:** the reverted clause, in decline-keyed form. @@ -109,50 +159,66 @@ still holds the cycle open — nothing closes unanswered. ## 3. Severity semantics (part 2) -**The rule.** Blocker and Major claim product behaviour, an invariant, or a contract. A finding -whose subject is narration, prose about a mechanism, or a test instrument's internals is -**Minor** — collect, never iterate — **except** an instrument finding demonstrating a false -green on product behaviour, which keeps its severity. - -**The decision procedure, keyed on consequence and not on artifact kind:** +**The decision procedure is the rule. The subject list is illustration.** > Name the gate, skill, rule, escalation procedure or scaffolded template that reads this text, > and the decision it takes differently if the text is wrong. If you cannot name an in-system -> reader and a changed decision, it is Minor. +> reader and a changed decision, the finding is **Minor** — collect, never iterate. + +Findings about narration, about prose describing a mechanism, and about a test instrument's +internals are the **cases this usually catches**, and they are shipped as examples of the test, +not as a second rule beside it. Stating them as a categorical demotion *and* stating the test +would give two procedures that can disagree on the same finding; the test governs. **A human reader never satisfies the test.** That cost class is already priced as non-gating by §5's prose exemption — "a wrong sentence costs a confused reader, not broken behaviour". Without this clause the test admits everything, since a future maintainer reads every file. +**The instrument carve-out runs in both directions.** An instrument finding keeps its severity +when it shows the instrument **changes what the gate concludes about product behaviour** — a +false green, and equally a false red, a check that blocks a valid change, or instrument logic +that would drive an unnecessary product rewrite. A false green is the most common case, not the +only one; naming it alone would demote a check that fails for wiring reasons and costs a +correct change. + **Why not a list of demotable artifact kinds.** The field record already falsifies that form. Of three `fic2` pass-5 findings on one parked story's acceptance criterion, two were correctly parked and **one was correctly acted on** — "the pass-4 activation boundary — because a future rewrite could otherwise move the duty to pass 1 while checking off every other listed -condition." Same artifact, same pass, same cluster, opposite correct answers. Artifact class -gets that wrong; consequence gets it right. An enumeration would also travel into scaffolded -repos whose artifact kinds we have never seen, which is invariant 10's stack-neutrality problem -in a new place. +condition." Same artifact, same pass, same cluster, opposite correct answers. An enumeration +would also travel into scaffolded repos whose artifact kinds we have never seen, which is +invariant 10's stack-neutrality problem in a new place. **Kinship, stated in the shipped text.** This is the **finding-level analog of the path-level prose exemption** — one principle at two granularities: text that *describes* the product versus text that *is* the product. Naming the kinship makes each rule the other's consistency check and -gives Gate A something to check the predicate against. Two rules sharing an unstated principle -drift apart. - -**The boundary case, shipped with its example.** Rationale prose inside a rule file — §5's -"Recorded rationale" paragraphs, the why-sentences under AGENTS.md invariants — states no rule, -so no decision flips if it is wrong. **Minor.** Named explicitly because "it's in `CLAUDE.md`, -agents read `CLAUDE.md`" is the first stretch a reviewer will try. +gives Gate A something to check the predicate against. + +**The boundary case, qualified rather than blanket.** Rationale prose inside a rule file — §5's +"Recorded rationale" paragraphs, the why-sentences under AGENTS.md invariants — is **Minor when +no rule's application depends on it**. It is **not** categorically Minor: +`docs/prompt-standards.md:49-51` requires that "Rules carry their why", on the stated ground that +"models follow motivated rules better, and reviewers can judge whether the rule still applies" — +and invariant 11 makes that checklist binding. So rationale that a reader must consult to decide +whether or how a rule applies **is** read by an in-system reader and passes the test. What is +Minor is rationale that only explains, historically or motivationally, a rule whose application +is fully determined without it. Named explicitly because "it's in `CLAUDE.md`, agents read +`CLAUDE.md`" is the first stretch a reviewer will try, and the blanket form of this exclusion +would have contradicted a checklist item this project is required to pass. **Coverage-first is unchanged and stated alongside:** the reviewer reports every finding with severity and confidence; the filter is ours, never Codex's. -**Expected effect, stated so Gate A reads it as designed rather than finding it as a gap.** -PR #23-class distributions — 16 findings in a scratch harness, 10 in narration — demote almost -entirely. `fic2`'s pass-2 meta cluster demotes only **partially**: ledger rows keep their -severity because rung escalation reads the recurrence count, and story criteria keep theirs -because the assigned-fix-set rule reads them. **This design demotes less than the story's -problem statement might suggest, and that is correct rather than a shortfall.** +**Expected effect, stated with its limit.** The intent is that distributions like PR #23's — 16 +findings in a never-committed scratch harness, 10 in narration, 1 in a plan — demote +substantially. **How much is not predictable from the recorded counts**, because `7bbdb14`'s own +body describes harness defects that could make checks pass for wiring reasons, and those are +exactly what the two-directional carve-out keeps. `fic2`'s pass-2 meta cluster demotes only +partially: ledger rows keep their severity because rung escalation reads the recurrence count, +and story criteria keep theirs because the assigned-fix-set rule reads them. **This design +demotes less than the story's problem statement might suggest, and the amount is a prediction +rather than a measurement** — which is why the story routes the measurement to P8 instead of +asserting an outcome here. --- @@ -164,6 +230,13 @@ cycles included. Two levels, not three: `high` gets no extra mandatory passes, taking its added rigor from lens sets and evidence mode. Raising the high floor would worsen the cost this story exists to reduce. +**A cycle citing several stories: unanimity.** Floor 1 **if and only if every cited story is +profiled and every one is at level 0**. Any other set — mixed levels, any unprofiled member, any +higher profile — yields 3. This is §5's own aggregation precedent for the analogous relaxation +("the cycle is skip-eligible only if **every** cited story is") applied to a new dimension, and +it is the only reading consistent with invariant 2's firing direction: the lowest-cited-floor +reading would under-review a cycle that also touches a high-risk story. + **One value, both gates, stated in both copies.** `codex-gate.sh:119` sets a single `floor`, consumed at `:946` for Gate B and `:966` for Gate A. A reader who does not know this will write a rule for one gate and silently move the other. @@ -176,19 +249,42 @@ specifically**: `docs/hardening-log.md` is a `docs/**.md` path that drives rung "docs" does not imply "changes nothing." That is §3's reachability test deciding a §4 question, which is the argument for shipping the two parts together. -**Mechanism and its residual, disclosed in the shipped text.** The floor is carried by the -already-shipped `.context/codex-gate.floor` knob, written by the agent from the story's profile. -The text states plainly that the value is agent-written, that it lives in per-clone gitignored -state no reviewer sees in a diff, that nothing verifies it against the story profile, and that -a floor of 1 is therefore the cheapest available gate-off lever. **This is a disclosure, not a -guard** — no mechanism is claimed for it. The knob rejects `0` and non-numeric input -(`codex-gate.sh:124-127`), which bounds typos and not intent. +### 4.1 The knob's lifecycle + +Without this, a floor of 1 written by one trivial cycle persists into the next high or +unprofiled cycle and silently costs two required passes. That is the gate-off lever firing *by +accident*, which is worse than by intent because nobody chose it, and it runs against invariant +2's firing direction. + +**Absence of the file is the safe state** — the hook defaults to 3 when it is missing +(`codex-gate.sh:119`), and it rejects `0` and non-numeric values (`:124-127`), which bounds +typos and not intent. -**Provenance.** A cycle running a floor other than the default records `floor N per ` in its closing commit body, so the value is auditable in history rather than only in -per-clone state. +- **Write** `.context/codex-gate.floor` at **cycle start**, derived from the complete cited-story + set by the unanimity rule above. +- **Re-derive and re-assert** it whenever the cited set or any cited profile changes mid-cycle + (§4.2), and **verify the stored value** rather than assuming the write took. +- **Remove** it as part of the **closing amend step**, so the next cycle starts from the default. -### 4.1 A profile that moves mid-cycle +A floor that must be re-asserted each cycle cannot silently persist into the next one, and +removal-on-close introduces no new state. + +**Exposure, so the value is not invisible until closure.** Every pass report states the **parsed +axes, the derived floor, and the knob value actually read back**. Without this the only account +of the floor is the closing line, written by the same agent that chose it, after every pass has +already been skipped or run. + +**A stated limitation, not a guarded one.** `.context/codex-gate.floor` is one checkout-global +mutable value. Two cycles running concurrently in one checkout with different profiles would +share it, and nothing serializes them. Cycles are sequential by construction here, so this is a +**stated limitation** in the same shape as §5's existing note that concurrent calls on one slot +race — not a mechanism, and the shipped text says so rather than implying safety. + +**Provenance.** A cycle records `floor N per ` in its closing commit body — for +every cited story, so a multi-story cycle's derivation is reconstructible — making the value +auditable in history rather than only in per-clone state. + +### 4.2 A profile that moves mid-cycle Composed from three rules §5 already has; no new rule. @@ -197,14 +293,11 @@ Composed from three rules §5 already has; no new rule. - **Passes already run keep counting**, per the existing rule. - **Closing requires meeting the floor as currently derived.** -A raise moves the target prospectively; a lower lets already-banked passes suffice sooner. - **The consequence that must be stated, or the arithmetic reads wrong:** under a **raise**, at least one further pass is required *regardless of the floor arithmetic*, because §5 already requires the final clean pass to run under the current profile. A previously-final clean pass stops qualifying the moment the profile moves — so even a raise that leaves the floor unchanged -(`standard` → `high`, both 3) still costs a pass. Without this, "passes already run keep -counting" reads as "nothing further is needed." +(`standard` → `high`, both 3) still costs a pass. **On lowering, recorded so nobody later reads the floor as having opened this.** A lower drops the floor *and* the lens sets *and* the evidence mode, so a `high` cycle lowered to `trivial` @@ -224,46 +317,97 @@ current pass alone**: findings clustering on the instrument, and findings cluste So with no prior record the two-tell threshold **remains reachable** on the cluster pair. The duty does not become inoperative; it loses sensitivity. -**The disclosure is not optional, and names the cause.** The report says which tells could not -be computed **and why** — a fresh checkout, a cleared `.context/`, another machine, a cycle -resumed elsewhere — so the human sees a degraded reading rather than a clean one. +**History is unavailable in more shapes than total loss**, and the rule covers each: + +| Shape | Treatment | +|---|---| +| absent — fresh checkout, cleared `.context/`, another machine, cycle resumed elsewhere | the tell is uncomputable; report it as such | +| **partial** — some passes present, others missing | compute the historical tells over the passes present and **say which pass numbers are missing**; a trend over an unstated subset reads as a trend over the cycle | +| **malformed or unreadable** — a file failing the pass-acceptance checks | treated as absent for that pass, **never** as a zero-finding pass; a count read from a file that failed validation is not evidence | +| **stale** — a file at the slot from an earlier cycle | treated as absent, and its presence reported, because a foreign curve is worse than no curve | + +**The disclosure is not optional, and names the cause.** The report says which tells could not be +computed, **which shape above applies**, and which pass numbers are affected — so the human sees +a degraded reading rather than a clean one. **Why the alternatives are rejected**, recorded per story criterion 5: making the resume note mandatory changes an artifact §5 explicitly calls advisory ("Nothing depends on it existing"); -treating unavailable history as its own stop condition would halt every cycle resumed on a -fresh checkout; restarting the pass-4 clock at the first *visible* pass silently lowers coverage +treating unavailable history as its own stop condition would halt every cycle resumed on a fresh +checkout; restarting the pass-4 clock at the first *visible* pass silently lowers coverage without saying so. **Stated risk, because this answer has a direction.** Reporting rather than stopping fails *toward continuing* the loop, which is the direction AGENTS.md invariant 2 questions for the -hook. The mandatory cause-naming disclosure is what makes the answer acceptable; without it the -design would take a different one. +hook. The mandatory disclosure is what makes the answer acceptable; without it the design would +take a different one. -### 5.1 Q6's durable half +### 5.1 The curve in the closing body — what it does and does not reach -Closing commit bodies carry the per-pass finding and Blocker counts, in one pinned greppable -form following the `3cdd075` precedent: +The closing commit of a **Gate-B cycle** carries the per-pass finding and Blocker counts, in one +pinned greppable form following the `3cdd075` precedent: ``` Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0. ``` -Two reasons, both load-bearing. **History that survives**: a curve in a commit body outlives a -fresh checkout, a cleared `.context/` and a different machine, which is exactly the availability -gap Q6 names. **A measurable subset**: the economics measurement deferred to the P8 story is -otherwise answerable only for cycles whose author happened to write the curve down — `3cdd075` -and `baa75c1` did, `7bbdb14` recorded the pass total and no distribution — because the findings -files behind those numbers live under gitignored `.context/`. Without this, P8 would report on a -self-selected subset with nothing marking that it did. +**The form is pinned, because an unpinned one is unparseable.** One entry per **valid** pass, in +pass order, comma-separated. A `reviewType: full` pass contributes **one entry, the sum of its +two branch files**, since the pass is the unit the floor counts. **Incomplete passes are +excluded** — they are not reviews, and §5 already says they do not count. A **valid pass with +zero findings is written as `0`**, never omitted, so position always equals pass number. + +**What it reaches, corrected.** This is the durable half **across cycles** — it survives a fresh +checkout, a cleared `.context/` and a different machine, which is what P8 and any future +resumption need. **It does not restore history within a running cycle**: the closing commit does +not exist until the cycle closes, so it is no help at pass 4 of the cycle still running. There +the §5 degraded-sensitivity answer governs. A cycle that wants mid-cycle durability may fold the +running curve into the `WIP:` body by amend — **permitted, not required.** + +**Without the curve requirement**, the economics measurement deferred to the P8 story is +answerable only for cycles whose author happened to write it down — `3cdd075` and `baa75c1` did, +`7bbdb14` recorded the pass total and no distribution — because the findings files behind those +numbers live under gitignored `.context/`. P8 would report on a self-selected subset with +nothing marking that it did. + +**Squash carry.** §5's existing squash-merge rule names only evidence entries and human-exception +records. The **decline records (§2.1), the floor provenance line (§4.1) and this curve** are +added to that list, in both copies. A record that does not survive the squash is unreachable from +`main`'s history, which is the whole reason that rule exists. --- ## 6. Old-conditions accounting -Required by the AGENTS.md Don't and by story criterion 6. Making the floor variable falsifies -prose that assumes it is 3. **Twelve sites, ten changing**, identical in both copies: the five -paired rows below (ten sites, of which row 5's pair stays) plus the paired sentence that follows -the table (two sites, both changing). +Required by the AGENTS.md Don't and by story criterion 6. **It covers every passage this change +rewrites, not only the floor wording** — an accounting scoped to one part, inside a section +citing that Don't, is the failure the Don't describes. + +**Method:** for each passage below, list what the existing prose required, then mark each +requirement **kept**, **moved**, or **deliberately dropped**. The plan carries the marked list +per passage and the exact replacement wording; the spec fixes the enumeration, so no passage is +rewritten without an accounting existing for it. + +**Every passage rewritten, by its bold lead-in — each verified present exactly once in both +copies:** + +| Passage | Rewritten by | +|---|---| +| "What a loop absorbs, and what stops it" | part 3 — the scope stop becomes an exit in a stated ordering | +| "Recognizing \"clearly stuck\"" | part 3 — becomes a suspension; clean-completion precedence moves into the ordering | +| "Surfacing does not close the cycle" | part 3 — this is the mechanism sentence §1.2 names | +| "From pass 4 onward every pass report carries three lines" | part 3 (two-tell stop as suspension) and §5 (Q6 computability and disclosure) | +| "The two rules above do not compete" | part 3 — superseded by the ordering, kept or retired explicitly | +| "**Both gates are a LOOP with a HARD FLOOR**" | part 1 — the floor statement itself | +| "The Gate-B triviality skip needs two independent conditions" | part 1 — adjacent profile-derived relaxation; checked for consistency with the floor predicate | +| "A cycle citing several stories" | part 1 — gains the floor dimension under unanimity | +| "**Severity:** Blocker (wrong/unsafe/breaks invariant)…" | part 2 — the severity definition | +| "Scope, and it is narrow" | part 3 — must distinguish the human-exception form from the decline record | +| "Recording a human exception" | part 3 — the decline record reuses this transport and must not be confused with it | +| "On squash-merge, copy every evidence entry…" | §5.1 — three new record types added to the carry | + +**Plus the floor-wording sites, which are mechanical.** Making the floor variable falsifies prose +that assumes it is 3. **Twelve sites, ten changing**, identical in both copies: five paired rows +(ten sites, of which row 5's pair stays) plus the paired sentence that follows. | # | `CLAUDE.md` | template | Text | Disposition | |---|---|---|---|---| @@ -284,20 +428,30 @@ looping." This is the site worth the most attention in the whole change: a rule inverts in exactly the configuration part 1 introduces, invisible to any grep for the digit because it says "pass 1." -The plan carries the exact replacement wording per site. - --- -## 7. Prerequisite: the template lacks the sentence §3's kinship points at - -§3 names the new severity rule as the finding-level analog of the path-level prose exemption. -The rationale sentence it points at — "a wrong sentence costs a confused reader, not broken -behaviour" — exists in `CLAUDE.md` and is **absent from the `/workflow-init` template**. - -**Resolution: the template gets the rationale sentence**, so the kinship claim has a referent in -both copies. This is a deliberate reduction of the existing 192-line divergence at exactly one -seam, made because the new rule depends on it — not a general reconciliation, which stays out of -scope. +## 7. Prerequisite and rollout + +**The template lacks the sentence §3's kinship points at.** The rationale sentence — "a wrong +sentence costs a confused reader, not broken behaviour" — exists in `CLAUDE.md` and is **absent +from the `/workflow-init` template**. **Resolution: the template gets it**, so the kinship claim +has a referent in both copies. A deliberate reduction of the existing 192-line divergence at +exactly one seam, made because the new rule depends on it — not a general reconciliation, which +stays out of scope. + +**What the template edit does and does not reach.** Editing the inline template changes what +`/workflow-init` **writes into new or re-initialized projects**. It does **not** update the +`CLAUDE.md` already sitting in a downstream project that ran `/workflow-init` earlier — those +are ordinary project files that accumulate local content, and invariant 9 forbids overwriting +them silently. So downstream repos adopt these rules by re-running `/workflow-init` and taking +the diff it offers, not by upgrading the plugin. The spec states this rather than leaving a +reader to assume a plugin bump propagates rules. + +**Packaging.** The change edits `plugins/dev-workflow/commands/workflow-init.md`, so invariant 12 +applies: the implementation surface includes a `plugins/dev-workflow/.claude-plugin/plugin.json` +**version bump** and a `CHANGELOG.md` entry. CI enforces the bump on pull requests +(`scripts/check-version-bump.sh`); the CHANGELOG entry is a convention this repo keeps for every +manifest version. --- @@ -311,16 +465,23 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). counterfactual. The subject is the §6 site list, which is differential by construction: **posed as a question about behaviour under floor 1, the pre-change text answers two sites wrongly** — site 2 sends a reader to "pass 3" when the floor is 1, and the "pass 1 carrying a - Minor keeps looping" sentence says a pass closes-or-loops opposite to what floor 1 requires — - **while the post-change text answers both correctly.** That is the observation that would - exist if the claim were false, and it is observable: it is a reading of two identified - sentences at two revisions, not a derivation from the change itself. - **The wiring must be able to produce the failure.** A verification that consults only the + Minor keeps looping" sentence says a pass loops where floor 1 requires it to close — **while + the post-change text answers both correctly.** That is the observation that would exist if the + claim were false, and it is observable: a reading of two identified sentences at two revisions, + not a derivation from the change itself. + **The wiring must be able to produce the failure.** A verification consulting only the post-change text cannot fail and would report success because of how it was wired. Both - revisions get read, and the entry says which sentences were read at which revision. The plan - pins the exact procedure; the spec fixes only its shape and its counterfactual. + revisions get read, and the entry says which sentences were read at which revision. - **A named verification of the risk path.** The risk path is the gate-off lever: a floor of 1 - in effect without a profile licensing it. Named here, procedure in the plan. + in effect without a profile licensing it. The verification exercises the §4.1 lifecycle — + a floor written by a level-0 cycle must be **absent** at the start of the next cycle, and the + observation that would exist if the claim were false is a surviving `1` after a close. +- **Parity verification across every changed rule**, per story criterion 7. The two copies + already differ on 192 lines, so parity cannot be asserted from a whole-section diff. The + verification walks **each passage named in §6's first table** and compares its post-change text + across the two copies, recording each difference as deliberate-and-stated or as a defect. The + narrow severity-spelling check in `scripts/check-invariants.sh` covers one item and is not + coverage; nothing mechanical checks the rest, which is invariant 11's stated condition. **Instrument discipline, learned from this story's own evidence.** Two defects in the `fic2` cycle's decision matrix were properties of the technique, not of that instance, and both apply @@ -331,8 +492,8 @@ a rule it never contained reports a failure mode that state could not produce). full clean pass before being caught. **Known open question, not resolved here.** How much instrument a one-paragraph prose rule is -worth is carried by the evidence doc as a question, not a commitment. This design does not -answer it and does not pretend the fixture-per-predicate demand is settled. +worth is carried by the evidence doc as a question, not a commitment. This design does not answer +it and does not pretend the fixture-per-predicate demand is settled. --- @@ -347,18 +508,25 @@ seam §7 names. --- -## 10. Risks - -- **The disclosed gate-off lever is real and unguarded.** A floor of 1 in per-clone gitignored - state, agent-written, unverified against the profile. The design discloses it and claims no - mechanism. If that is unacceptable, the answer is hook code, which is out of scope by - decision — and re-opening it is a stop-and-ask, not a silent expansion. +## 10. Risks and activation + +- **When these rules bind.** They take effect from the commit that ships them. **A cycle already + in flight finishes under the rules it started with** — the floor it derived, the severity + definition it applied, the exits it knew — because re-deriving a floor mid-cycle from a rule + that did not exist when passes were banked would invalidate a count nobody could reconstruct. + This is stated in the shipped text, not left to inference. It also disposes of this design's + own case: its Gate A runs under the old rules, and nothing here is retroactive. +- **The disclosed gate-off lever is real and only partly mitigated.** §4.1's lifecycle removes + the *accidental* persistence path; the *deliberate* one remains — an agent-written value in + per-clone gitignored state, unverified against the profile. §4.1's exposure requirement and + §4's provenance line make it visible in the pass report and in history; neither is a guard. If + that is unacceptable, the answer is hook code, which is out of scope by decision — and + re-opening it is a stop-and-ask, not a silent expansion. - **The reachability test needs judgement** at the moment §5 is trying to remove judgement. The named-reader-and-changed-decision phrasing is what makes it decidable; if Gate A finds it admits or excludes a case wrongly, that is a finding about this design, not about the settled contract. - **Q6's answer fails toward continuing the loop.** Stated in §5 with the disclosure that makes it acceptable. -- **This spec edits the rules that govern its own review.** Its Gate A runs under the old rules; - the new ones bind afterwards. Nothing here is retroactive, and the design does not assume - otherwise. +- **The expected demotion is a prediction.** §3 says so; P8 measures it. If it demotes far less + than hoped, the rule is still correct and the economics claim was the thing that was wrong. From c4af71404e296e4fd7d48a4474e24b02e8d1aa12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:03:40 +0200 Subject: [PATCH 010/117] docs(story): encode the settled severity test and fix two scope contradictions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three criteria corrected after Gate-A pass 2 (30 findings; 2 BLOCKER, 20 MAJOR). BLOCKER: the severity criterion still described the pre-decision form -- artifact-kind demotion with a false-green-only carve-out -- which no implementation could satisfy alongside the settled consequence-keyed contract. It now carries the named-reader test, states that the subject list is a set of worked examples rather than a second rule beside it (two procedures can disagree on one finding), and carries the bidirectional instrument carve-out: a false red or a check blocking a valid change alters what the gate concludes just as a false green does. The provenance criterion required the line only for a NON-DEFAULT floor, which contradicted criterion 9 -- this story is risk high, runs at the default 3, and must still demonstrate the provenance path. It also made an absent line ambiguous between "default floor" and "someone forgot", and left the newly decided user-knob divergence with nowhere to be disclosed. Now every cycle records its floor, one entry per cited story, with both values where a user-set workspace knob diverges from the profile derivation. The curve criterion covered Gate-B cycles only. Pass 1 had steered it there and pass 2 found the narrowing wrong -- a require/withdraw pair, resolved by scope ruling rather than by one reviewer winning. All three loops now carry a curve: the Gate-A spec loop in the spec commit, the Gate-A plan loop in the plan commit, Gate B in the closing amend. The evidence this story cites is mostly Gate-A -- nineteen measured passes on one spec -- so a Gate-B-only requirement would leave the dominant cost unmeasured. Decided by the sparring session under Daniel's 2026-08-28 delegation. Profile unchanged, so no profile-log line. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- ...-review-loop-economics-pass-floor-story.md | 62 ++++++++++++++----- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 8cc510b..c458b21 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -118,34 +118,64 @@ deliverable rather than a convenience. `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation, so "docs" does not imply "changes nothing". That is Section A's reachability test deciding a floor question, which is why the two parts belong in one change. -- [ ] **A cycle's closing commit body carries its per-pass shape.** Both copies require the - closing commit of a Gate-B cycle to record the per-pass finding and Blocker counts, in - one pinned greppable form, following the `3cdd075` precedent +- [ ] **Every cycle's closing commit body carries its per-pass shape — all three cycle types.** + Both copies require the **Gate-A spec loop** (in the spec's commit body), the **Gate-A + plan loop** (in the plan's commit body) and the **Gate-B cycle** (in the closing amend) to + record that loop's per-pass finding and Blocker counts, in one pinned greppable form, + following the `3cdd075` precedent (`Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0.`). Checkable: the - requirement is stated in both copies, and this story's own closing commit carries it. + requirement is stated in both copies, and this story's own commits carry it for each loop + that ran. + **All three, not Gate B alone** (revised 2026-08-28 after Gate-A pass 2). A Gate-B-only + requirement would leave the *dominant* cost unmeasured: the loops this story cites as + evidence are Gate-A loops — nineteen measured Gate-A passes on one spec, and this story's + own Gate-A run — so P8 without Gate-A curves cannot measure the thing the problem + statement is about. **Why it is in scope** (approved as a scope addition, Daniel, 2026-08-28): the deferred economics measurement routed to P8 is otherwise answerable only for cycles whose author happened to write the curve down — `3cdd075` and `baa75c1` did, `7bbdb14` recorded the pass total and no distribution — because the findings files behind those numbers live under gitignored `.context/`. It is also the durable half of Q6: a curve in a commit body survives a fresh checkout, a cleared `.context/` and a different machine. -- [ ] **A non-default floor leaves a trace in history.** Both copies require a cycle that ran - a floor other than the default to record `floor N per ` in its closing - commit body. Checkable: the requirement is stated in both copies, and any cycle in this - story's own branch that runs a reduced floor carries the line. +- [ ] **Every cycle's floor leaves a trace in history, default or not.** Both copies require a + cycle to record `floor N per ` in its closing commit body — one entry + per cited story — and, where a workspace knob set by the user diverges from the profile + derivation, to record both: `floor N per workspace knob; profile derivation M per `. Checkable: the requirement is stated in both copies, and this story's own closing + commits carry it. + **Every cycle, not only a non-default one** (revised 2026-08-28 after Gate-A pass 2). + An earlier wording required the line only for a non-default floor, which contradicted + criterion 9 — this story is risk `high` and runs at the default 3, yet must demonstrate + the provenance path. It also made an absent line ambiguous between "default floor" and + "someone forgot", and left the user-knob divergence with nowhere to be disclosed. - [ ] **The gate-off residual is named in the shipped text, not merely avoided.** Both copies state that the floor value is agent-written, lives in per-clone gitignored state that no reviewer sees in a diff, and that nothing verifies the written value against the story profile — and that a floor of 1 is therefore the cheapest available gate-off lever. Checkable by reading. This is a disclosure, not a guard: no mechanism is claimed for it. -- [ ] **Severity is pinned as a closed decision in both copies, with the carve-out stated at - the same place.** Both state that Blocker and Major claim product behaviour, an - invariant, or a contract, and that a finding whose subject is narration, prose about a - mechanism, or a test instrument's internals is **Minor** — collect, never iterate — - **except** an instrument finding that demonstrates a false green on product behaviour, - which keeps its severity. Both also still state that the reviewer reports every finding - with severity and confidence and that the filter is applied downstream by us; a copy that - drops coverage-first fails this criterion even if the severity rule is correct. +- [ ] **Severity is pinned as a closed decision in both copies, keyed on consequence, with the + carve-out stated at the same place.** Both state that Blocker and Major claim product + behaviour, an invariant, or a contract, and both state the deciding test: **name the + in-system reader of this text — a gate, skill, rule, escalation procedure or scaffolded + template — and the decision it takes differently if the text is wrong; if you can name + neither, the finding is Minor, collect and never iterate.** A human reader never + satisfies the test. Findings about narration, prose describing a mechanism, and test + instrument internals appear as **worked examples of that test, not as a second rule + beside it** — a copy stating a categorical demotion by subject *and* the test fails this + criterion, because two procedures can disagree on one finding. + **The instrument carve-out runs in both directions:** an instrument finding keeps its + severity when it shows the instrument changes what the gate concludes about product + behaviour — a false green, and equally a false red or a check that blocks a valid change. + Naming only false green would demote a check that fails for wiring reasons and costs a + correct change. + Both copies also still state that the reviewer reports every finding with severity and + confidence and that the filter is applied downstream by us; a copy that drops + coverage-first fails this criterion even if the severity rule is correct. + *(Revised 2026-08-28 after Gate-A pass 2 found this criterion still describing the + pre-decision form — artifact-kind demotion with a false-green-only carve-out — which no + implementation could satisfy alongside the settled contract. Encodes the settled + consequence-keyed decision and the bidirectional carve-out; sparring session, under + Daniel's 2026-08-28 delegation.)* - [ ] **Each of Q1–Q6 is answered or rejected in the shipped text, with a reason, and the answers do not contradict each other.** Q1 (clean-completion precedence), Q2 (a declined expansion's exit), Q3 (scope stop vs. clean completion), Q4 (whether a decline binds From 60f6b8c48bd8ccdb1b7a097626f6e1edf810a686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:07:43 +0200 Subject: [PATCH 011/117] =?UTF-8?q?docs(spec):=20revision=203=20=E2=80=94?= =?UTF-8?q?=20Gate-A=20pass-2=20findings,=20and=20the=20accounting=20moves?= =?UTF-8?q?=20in?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 2: 30 findings (2 BLOCKER, 20 MAJOR, 7 MINOR, 1 NIT). Surfaced early on two tells -- findings rising 27->30 and a verbatim require/withdraw pair on the curve scope. Both routed items came back decided; all 22 Blocker/Major are folded in. The knob is a user's before it is ours. README.md:130 and docs/getting-started.md:85-86 ship codex-gate.floor as a documented per-workspace user capability; revision 2 had the agent writing and deleting it unconditionally, destroying a deliberate setting. Now: a floor file with no marker beside it is user-set -- never overwritten, never removed, and it IS the effective floor, with the cycle disclosing the divergence rather than resolving it. The agent writes only when no user value exists, alongside a marker recording the cited set, and removes both at close. A marker-bearing file at cycle start is stale agent state from a crashed cycle: re-derive. No hook reads the marker, so this stays prompt-only. Cleanup now attaches to each cycle type's own closing event -- one knob governs both gates, so a Gate-B-amend-only rule would leave a Gate-A-derived floor standing. Write, read back, compare; a failed write, failed read-back, mismatch or failed removal each stop and name which occurred. The curve covers all three loops, not Gate B alone. The evidence this story cites is mostly Gate-A -- nineteen measured passes on one spec -- so a Gate-B-only rule would leave the dominant cost unmeasured. Pass 1 had steered it the other way; recorded as resolved by scope ruling, not by one reviewer winning. §6 now performs the accounting instead of deferring it. Twelve passages, each with what its prose currently requires and the disposition of each requirement. And the floor-site inventory is GENERATED: the stated grep's output is the table, so the count cannot drift -- I hand-derived it three times and got three different answers. Twelve digit sites plus two the grep provably cannot find (the "pass 1 carrying a Minor" sentence, which inverts under floor 1), fourteen in total. Both limitations stated: digit-only, and a floor rather than coverage. Also: §2's precedence cell was self-contradictory, requiring findings to be both "still open" and declined when a decline releases them -- restated once from the hold's side; finding identity now treats a severity, consequence or suggested-fix change as a new finding; the decline record adopts the human-exception form's unverified-assertion honesty; §5's history shapes gain per-shape checks and distinct remedies, with stale detection honestly described as undetectable after the fact; §7 names the six user-facing sentences this change falsifies, including getting-started.md:84 "Gate A's floor is unchanged at every level"; §10 gets an activation start-marker, abandonment and rollback behaviour, the widened gate-off surface, and keeps a user-set floor distinguishable from the agent-written lever. Contract answers by the sparring session under Daniel's 2026-08-28 delegation. Gate A: passes 1-2 at .context/codex-reviews/gate-a-spec-rle-pass-{1,2}.md. Findings 27, 30. Blockers 5, 2. Floor 3, nothing clean yet. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 766 +++++++++--------- 1 file changed, 403 insertions(+), 363 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index e7c2a1a..5594841 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,24 +1,24 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 2, after Gate-A pass 1 (27 findings; 5 Blocker, 19 Major) +**Date:** 2026-08-28 · **Revision:** 3, after Gate-A passes 1 (27 findings) and 2 (30) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. Prompt-only. No file under `plugins/dev-workflow/hooks/` changes. -**Two surfaces, both edited in every change below:** `CLAUDE.md` §5 (lines 65–589) and its -mirror inside `/workflow-init`'s inline `CLAUDE.md` template -(`plugins/dev-workflow/commands/workflow-init.md`, fenced at 192–778; §5 is 257–777). The two -already differ on 192 lines across 20 hunks; this design touches only the rules it changes, -per story criterion 7, and states each deliberate variance where it creates one. +**Surfaces edited.** `CLAUDE.md` §5 (65–589) and its mirror inside `/workflow-init`'s inline +`CLAUDE.md` template (`plugins/dev-workflow/commands/workflow-init.md`, fenced 192–778; §5 is +257–777). Plus the user-facing statements this change falsifies (§7). The two §5 copies already +differ on 192 lines across 20 hunks; this design touches only the rules it changes, per story +criterion 7, and states each deliberate variance where it creates one. --- ## 1. The finding this design is built on -**The loop has four exits and four standing duties**, each stated in its own bolded paragraph, -each qualifying the ones before it — and nowhere does §5 say which wins when two apply at once. +**The loop has four exits and four standing duties**, each in its own bolded paragraph, each +qualifying the ones before it — and nowhere does §5 say which wins when two apply at once. *Scope of that claim:* four exits **of the loop**. §5 carries other mandatory stops that are not loop exits — a target file surviving deletion, an exhausted recovery budget, an unresolvable @@ -26,16 +26,13 @@ profile, a blocking evidence or setup gap. Those halt the *procedure* rather tha *cycle*, they do not compete with clean completion, and this design does not reorder them. The missing ordering is why the `fic2` cycle could not ship two small clauses without qualifying -three rules nobody proposed changing: two clauses met a lattice with no stated ordering, and the -lattice pushed back. The revert was correct. - -So "consolidated, done once instead of clause-by-clause" does not mean *answer six questions in -one sitting*. It means **state the ordering once**, after which most of the answers are readings -of it rather than new rules. +three rules nobody proposed changing. So "consolidated, done once instead of clause-by-clause" +means **state the ordering once**, after which most answers are readings of it rather than new +rules. ### 1.1 Only one exit closes -Both facts are already in §5, in two separate places, and neither draws the conclusion: +Both facts are already in §5, in two places, and neither draws the conclusion: > "Stopping this way is **not an exit from the gate**: the floor, the Blocker/Major filter and > the clean-final-pass rule all stand, and the loop resumes on the revised artifact once the @@ -45,115 +42,110 @@ Both facts are already in §5, in two separate places, and neither draws the con > credited as clean, and the loop resumes on whatever the user decides." **Clean completion closes. The scope stop, the clearly-stuck exit and the two-tell stop -suspend** — they surface to the human and the loop resumes. - -Consequence: **suspension × suspension is not a conflict.** Two suspensions at once means the -pass report carries both reasons. Three pairs, no ordering, one sentence. +suspend.** Consequence: **suspension × suspension is not a conflict** — two at once means the +report carries both reasons. Three pairs, no ordering, one sentence. ### 1.2 Three of the four duties are not participants -- **The floor** is a quantity gating closure — "**Below the floor nothing closes**". Not - orderable. -- **Blocker/Major must resolve** is definitional: a clean pass is Blocker/Major-free by - construction. -- **A surfaced finding stays open** is the *mechanism* that makes the other three exits - suspensions rather than closes. -- **No pass carrying a surfaced finding counts as clean** is the only genuine participant. It - couples an open finding to closure, and every remaining question lives there. +- **The floor** is a quantity gating closure — "**Below the floor nothing closes**". +- **Blocker/Major must resolve** is definitional: a clean pass is Blocker/Major-free. +- **A surfaced finding stays open** is the *mechanism* making the other exits suspensions. +- **No pass carrying a surfaced finding counts as clean** is the only participant. Every + remaining question lives there. --- ## 2. The precedence structure (part 3) -Shipped as one short ordering, stated once per copy, with the individual rules referencing it -rather than restating it. - | Conflict | Resolution | |---|---| -| clean completion × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim rather than re-derived. Recorded as settled and probably unreachable — a clean pass has no regenerating Blocker/Major, so the exit's third condition fails. Kept because dropping a sentence §5 already spends is the dropped-condition failure story criterion 6 exists to prevent. | +| clean completion × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim. Settled and probably unreachable — a clean pass has no regenerating Blocker/Major, so the exit's third condition fails. Kept because dropping a sentence §5 already spends is the dropped-condition failure criterion 6 exists to prevent. | | clean completion × two-tell stop | **Clean wins.** Daniel's recorded decision, encoded not reopened. | -| clean completion × scope stop | **Clean wins only when every surfaced finding still open has been explicitly declined.** Otherwise the scope stop's finding keeps the pass unclean and outranks closure automatically. | +| clean completion × scope stop | **The scope stop outranks closure while any surfaced finding remains unresolved and undeclined.** A finding the user has explicitly declined is resolved *for this purpose* and no longer holds the cycle. | + +**The third cell's wording is deliberate.** An earlier draft read "clean wins only when every +surfaced finding still open has been explicitly declined", which is self-contradictory: §2.1 +defines a decline as *releasing* the finding, so a declined finding is not still open. The rule +is stated once, from the hold's side — what keeps a cycle open — so the decline's effect is +described in exactly one place. **Why the third is the only one that needed deciding.** A scope stop is triggered by a *specific -finding*. While the duty stands unqualified, that finding is open, so the triggering pass cannot -be clean, so the scope stop wins automatically — no cell required. The cell exists only because -the duty may now be qualified. +finding*; while the duty stands unqualified that finding is open, so the triggering pass cannot +be clean and the scope stop wins automatically. The cell exists only because the duty may now be +qualified. -**The asymmetry that explains the whole history:** the two-tell stop is triggered by *statistics -about findings* — the five tells — not by a finding. Nothing is left open, the duty does not -bite, and clean can win. The scope stop's trigger **is** a finding. The reverted clause tried to -unbite that by qualifying three universal rules at once. The lattice was not being awkward; it -was correct. +**The asymmetry that explains the history:** the two-tell stop is triggered by *statistics about +findings*, not by a finding. Nothing is left open, the duty does not bite, clean can win. The +scope stop's trigger **is** a finding. The reverted clause tried to unbite that by qualifying +three universal rules at once. The lattice was correct. ### 2.1 The qualification, and how a decision on a finding is recorded -**The duty "no pass carrying a surfaced finding counts as clean" is qualified for, and only -for, a finding the user has explicitly declined.** - -Per story criterion 5, this qualification appears **at each of the three universal rules** in -both copies — the Blocker/Major-resolve duty, the surfaced-finding-stays-open rule, and the -no-clean-pass-carrying-it rule — not only at the new clause. Placing it only at the new clause -is what made the earlier attempt unshippable. - -**Both branches, stated symmetrically in one sentence.** §5 already says the loop "resumes the -moment the user says whether the set now includes it" — resumption *is* the hold ending, and it -ends whichever way the answer went. So: - -- **Declined** → the finding is released as recorded-declined and stops blocking closure for the - remainder of the cycle. It does not re-stop later passes. -- **Accepted** → the finding enters the assigned set, where its **severity governs exactly as - Mechanics already says**: an accepted Blocker or Major must resolve; an accepted Minor or Nit - is collected and never iterated. In-set Minors have never blocked a clean pass at or above the - floor, so there is no deadlock — the apparent one assumes the surfaced-finding hold outlives - the user's answer, and the resume sentence says it does not. +**The duty is qualified for, and only for, a finding the user has explicitly declined.** Per +story criterion 5, the qualification appears **at each of the three universal rules** in both +copies — not only at the new clause. Placing it only at the new clause is what made the earlier +attempt unshippable. + +**All three branches, stated symmetrically.** §5 already says the loop "resumes the moment the +user says whether the set now includes it" — resumption *is* the hold ending, whichever way the +answer went. + +- **Declined** → released as recorded-declined; stops holding the cycle for the remainder of it, + and does not re-stop later passes. +- **Accepted** → enters the assigned set, where **severity governs exactly as Mechanics already + says**: accepted Blocker or Major must resolve; accepted Minor or Nit is collected, never + iterated. In-set Minors have never blocked a clean pass at or above the floor, so there is no + deadlock — the apparent one assumed the hold outlives the user's answer, and the resume + sentence says it does not. - **Undecided** → the hold stands. Nothing closes unanswered. **Where the decision is recorded — the commit body, on rails §5 already ships.** The -human-exception machinery already solves exactly this transport problem: "an ungated change -records it in that commit; a Gate-A cycle in the spec or plan commit; a Gate-B cycle in the WIP -commit, restated by the closing amend", folded in mid-cycle by amend, carried into the squash -body, with an empty commit as a last-resort destination. **The decline reuses that transport and -is a different record type**, with its own label: +human-exception machinery already solves this transport: "an ungated change records it in that +commit; a Gate-A cycle in the spec or plan commit; a Gate-B cycle in the WIP commit, restated by +the closing amend", folded in mid-cycle by amend, carried into the squash body, empty commit as +last resort. **The decline reuses that transport as a different record type:** ``` -Declined finding: · · +Declined finding: // · · · +Defect: Reason: ``` -**The distinction from the human-exception form is stated explicitly in the shipped text, and it -is the substance of the Blocker-4 repair.** They are not the same record and must not read as -one: +**The distinction from the human-exception form is stated explicitly — this is the substance of +the Blocker-4 repair.** They are not the same record: - The **human-exception form authorizes nothing** — §5 says so in its own words, and says it is never the answer to a below-floor pass, an unclean final pass, or a `STOP and surface`, and that neither a human's assent nor the record lets an agent close or continue a cycle. -- The **decline record has §5-defined effect**: it releases one specific finding from the - surfaced-finding hold. That effect comes from the loop's own scope rule — which already routes - set membership to the user and already resumes on the answer — not from human assent - overriding a mandatory rule. Every other mandatory rule stands: the floor, the Blocker/Major - filter, the clean-final-pass requirement. - -Without that paragraph a reader meets two instructions that give opposite actions for the same -declined scope finding. - -**"Explicitly declined" is defined tightly, because the safety of the whole qualification rests -on it.** A decline is a **recorded user decision on that specific finding**, attributable and -unambiguous. It is **never** silence, never a general remark about scope, and never the agent -inferring a decline from context. The shipped text carries all three negatives; a loose reading -converts a human gate into an agent's judgement, which is the failure the rule exists to prevent. +- The **decline record has §5-defined effect**: it releases one specific finding from the hold. + That effect comes from the loop's own scope rule — which already routes set membership to the + user and already resumes on the answer — not from human assent overriding a mandatory rule. + Every other mandatory rule stands: the floor, the Blocker/Major filter, the clean-final-pass + requirement. + +**What the record can and cannot establish, said plainly.** Like the human-exception record it +sits beside, this is an **unverified assertion**: the commit body is author-written, and nothing +checks that the handle belongs to whoever decided or that a human was asked. A reader learns +that *the commit claims* the user declined this finding. It is the same standing §5 already +gives the human-exception form, and the shipped text says so rather than implying the record is +evidence. What makes the mechanism safe is not verification but **narrowness**: it releases one +named finding and nothing else. + +**"Explicitly declined" is defined tightly.** A **recorded user decision on that specific +finding**, attributable and unambiguous. **Never** silence, never a general remark about scope, +never the agent inferring a decline from context. All three negatives ship. **Identity — how a declined finding is recognized again.** Findings carry no stable identifier, -so the decline record names the finding by **pass number, slot and line position at the time of -decline, plus its verbatim location field and a short quotation of its defect field**. A later -finding is the same finding when its location and defect match; **when that is unclear it is a -new finding and the hold applies**, which costs a question and never a silent release. A finding -that is split, merged or materially reworded is new by that test. This mirrors §5's existing +so the record names pass, slot and line, plus the **verbatim location and defect fields**. +A later finding is the same finding when **location and defect both match**. **Any change to +severity, consequence or suggested fix makes it a new finding**, because each can change what +the finding asks for even where the defect reads the same — a Minor re-raised as a Blocker is +not the thing that was declined. **Where sameness is unclear it is a new finding and the hold +applies**, which costs a question and never a silent release. This mirrors §5's existing treatment of unclear set membership, which resolves toward *outside* for the same reason. **The dispositions companion stays what §5 says it is** — the advisory working copy, deletable -and rebuildable. The commit body is the record. No new artifact class, no `.gitignore` change. - -**What this makes shippable:** the reverted clause, in decline-keyed form. +and rebuildable. The commit body is the record. --- @@ -165,293 +157,330 @@ and rebuildable. The commit body is the record. No new artifact class, no `.giti > and the decision it takes differently if the text is wrong. If you cannot name an in-system > reader and a changed decision, the finding is **Minor** — collect, never iterate. -Findings about narration, about prose describing a mechanism, and about a test instrument's -internals are the **cases this usually catches**, and they are shipped as examples of the test, -not as a second rule beside it. Stating them as a categorical demotion *and* stating the test -would give two procedures that can disagree on the same finding; the test governs. +Findings about narration, mechanism prose, and test-instrument internals are the cases this +usually catches, shipped as **worked examples of the test, not as a second rule beside it**. +Stating a categorical demotion *and* the test would give two procedures that can disagree on one +finding; the test governs. **A human reader never satisfies the test.** That cost class is already priced as non-gating by §5's prose exemption — "a wrong sentence costs a confused reader, not broken behaviour". Without -this clause the test admits everything, since a future maintainer reads every file. +this the test admits everything. **The instrument carve-out runs in both directions.** An instrument finding keeps its severity when it shows the instrument **changes what the gate concludes about product behaviour** — a false green, and equally a false red, a check that blocks a valid change, or instrument logic -that would drive an unnecessary product rewrite. A false green is the most common case, not the -only one; naming it alone would demote a check that fails for wiring reasons and costs a -correct change. - -**Why not a list of demotable artifact kinds.** The field record already falsifies that form. Of -three `fic2` pass-5 findings on one parked story's acceptance criterion, two were correctly -parked and **one was correctly acted on** — "the pass-4 activation boundary — because a future -rewrite could otherwise move the duty to pass 1 while checking off every other listed -condition." Same artifact, same pass, same cluster, opposite correct answers. An enumeration -would also travel into scaffolded repos whose artifact kinds we have never seen, which is -invariant 10's stack-neutrality problem in a new place. - -**Kinship, stated in the shipped text.** This is the **finding-level analog of the path-level -prose exemption** — one principle at two granularities: text that *describes* the product versus -text that *is* the product. Naming the kinship makes each rule the other's consistency check and -gives Gate A something to check the predicate against. +that would drive an unnecessary product rewrite. Naming false green alone would demote a check +that fails for wiring reasons and costs a correct change. + +**Why not a list of demotable artifact kinds.** The field record falsifies that form. Of three +`fic2` pass-5 findings on one parked story's acceptance criterion, two were correctly parked and +**one was correctly acted on** — "the pass-4 activation boundary — because a future rewrite could +otherwise move the duty to pass 1 while checking off every other listed condition." Same +artifact, same pass, opposite correct answers. An enumeration would also travel into scaffolded +repos whose artifact kinds we have never seen — invariant 10's stack-neutrality problem in a new +place. + +**Kinship, stated in the shipped text.** The **finding-level analog of the path-level prose +exemption** — one principle at two granularities: text that *describes* the product versus text +that *is* the product. Naming it makes each rule the other's consistency check. **The boundary case, qualified rather than blanket.** Rationale prose inside a rule file — §5's "Recorded rationale" paragraphs, the why-sentences under AGENTS.md invariants — is **Minor when -no rule's application depends on it**. It is **not** categorically Minor: -`docs/prompt-standards.md:49-51` requires that "Rules carry their why", on the stated ground that -"models follow motivated rules better, and reviewers can judge whether the rule still applies" — -and invariant 11 makes that checklist binding. So rationale that a reader must consult to decide -whether or how a rule applies **is** read by an in-system reader and passes the test. What is -Minor is rationale that only explains, historically or motivationally, a rule whose application -is fully determined without it. Named explicitly because "it's in `CLAUDE.md`, agents read -`CLAUDE.md`" is the first stretch a reviewer will try, and the blanket form of this exclusion -would have contradicted a checklist item this project is required to pass. - -**Coverage-first is unchanged and stated alongside:** the reviewer reports every finding with -severity and confidence; the filter is ours, never Codex's. - -**Expected effect, stated with its limit.** The intent is that distributions like PR #23's — 16 -findings in a never-committed scratch harness, 10 in narration, 1 in a plan — demote -substantially. **How much is not predictable from the recorded counts**, because `7bbdb14`'s own -body describes harness defects that could make checks pass for wiring reasons, and those are -exactly what the two-directional carve-out keeps. `fic2`'s pass-2 meta cluster demotes only -partially: ledger rows keep their severity because rung escalation reads the recurrence count, -and story criteria keep theirs because the assigned-fix-set rule reads them. **This design -demotes less than the story's problem statement might suggest, and the amount is a prediction -rather than a measurement** — which is why the story routes the measurement to P8 instead of -asserting an outcome here. +no rule's application depends on it**, and **not** categorically Minor. +`docs/prompt-standards.md:49-51` requires that "Rules carry their why", because "models follow +motivated rules better, and reviewers can judge whether the rule still applies", and invariant 11 +makes that checklist binding. Rationale a reader must consult to decide whether or how a rule +applies **is** read by an in-system reader and passes the test. What is Minor is rationale that +only explains, historically or motivationally, a rule whose application is fully determined +without it. Named explicitly because "it's in `CLAUDE.md`, agents read `CLAUDE.md`" is the first +stretch a reviewer will try — and because the blanket form would have contradicted a checklist +item this project must pass. + +**Coverage-first unchanged:** the reviewer reports every finding with severity and confidence; +the filter is ours, never Codex's. + +**Expected effect, with its limit.** The intent is that distributions like PR #23's — 16 in a +never-committed scratch harness, 10 in narration, 1 in a plan — demote substantially. **How much +is not predictable from the recorded counts**, because `7bbdb14`'s own body describes harness +defects that could make checks pass for wiring reasons, and those are exactly what the +two-directional carve-out keeps. `fic2`'s pass-2 meta cluster demotes only partially: ledger rows +keep severity because rung escalation reads the recurrence count, story criteria because the +assigned-fix-set rule reads them. **This demotes less than the problem statement might suggest, +and the amount is a prediction rather than a measurement** — which is why the story routes the +measurement to P8 instead of asserting an outcome. --- ## 4. The pass floor (part 1) **One predicate.** `max(risk, security) == 0` → floor **1**. Everything else → **3**, unprofiled -cycles included. - -Two levels, not three: `high` gets no extra mandatory passes, taking its added rigor from lens -sets and evidence mode. Raising the high floor would worsen the cost this story exists to reduce. - -**A cycle citing several stories: unanimity.** Floor 1 **if and only if every cited story is -profiled and every one is at level 0**. Any other set — mixed levels, any unprofiled member, any -higher profile — yields 3. This is §5's own aggregation precedent for the analogous relaxation -("the cycle is skip-eligible only if **every** cited story is") applied to a new dimension, and -it is the only reading consistent with invariant 2's firing direction: the lowest-cited-floor -reading would under-review a cycle that also touches a high-risk story. - -**One value, both gates, stated in both copies.** `codex-gate.sh:119` sets a single `floor`, -consumed at `:946` for Gate B and `:966` for Gate A. A reader who does not know this will write -a rule for one gate and silently move the other. - -**Why there is no `docs-only` arm.** A diff-derived reading cannot serve Gate A at all — Gate A -runs on a spec, before any diff exists. A story-declared reading is already subsumed: intake -defines `trivial` as "no behavioural effect in the artifact's own execution context", which a -documentation change has none of. And a path-derived arm would be **wrong in this repo -specifically**: `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation, so -"docs" does not imply "changes nothing." That is §3's reachability test deciding a §4 question, -which is the argument for shipping the two parts together. - -### 4.1 The knob's lifecycle - -Without this, a floor of 1 written by one trivial cycle persists into the next high or -unprofiled cycle and silently costs two required passes. That is the gate-off lever firing *by -accident*, which is worse than by intent because nobody chose it, and it runs against invariant -2's firing direction. - -**Absence of the file is the safe state** — the hook defaults to 3 when it is missing -(`codex-gate.sh:119`), and it rejects `0` and non-numeric values (`:124-127`), which bounds -typos and not intent. - -- **Write** `.context/codex-gate.floor` at **cycle start**, derived from the complete cited-story - set by the unanimity rule above. -- **Re-derive and re-assert** it whenever the cited set or any cited profile changes mid-cycle - (§4.2), and **verify the stored value** rather than assuming the write took. -- **Remove** it as part of the **closing amend step**, so the next cycle starts from the default. - -A floor that must be re-asserted each cycle cannot silently persist into the next one, and -removal-on-close introduces no new state. +included. Two levels, not three: `high` takes its added rigor from lens sets and evidence mode. + +**Several cited stories: unanimity.** Floor 1 **if and only if every cited story is profiled and +every one is at level 0**. Any other set — mixed levels, any unprofiled member, any higher +profile — yields 3. This is §5's own aggregation precedent for the analogous relaxation ("the +cycle is skip-eligible only if **every** cited story is") applied to a new dimension, and the only +reading consistent with invariant 2's firing direction. + +**One value, both gates.** `codex-gate.sh:119` sets a single `floor`, consumed at `:946` for +Gate B and `:966` for Gate A. Stated in both copies: a reader who does not know this will write a +rule for one gate and silently move the other. + +**Why there is no `docs-only` arm.** A diff-derived reading cannot serve Gate A, which runs on a +spec before any diff exists. A story-declared reading is subsumed: intake defines `trivial` as +"no behavioural effect in the artifact's own execution context". And a path-derived arm would be +**wrong in this repo**: `docs/hardening-log.md` is a `docs/**.md` path that drives rung +escalation. That is §3's reachability test deciding a §4 question. + +### 4.1 The knob, which is a user's before it is ours + +**`.context/codex-gate.floor` is a shipped, documented user capability.** `README.md:130` lists +it under "Per-workspace knobs" — "a positive integer; moves the 3-passes-per-gate floor" — and +`docs/getting-started.md:85-86` says the same. An earlier draft had the agent writing and +deleting it unconditionally, which would silently overwrite and then destroy a deliberate user +setting. The design does not do that. + +**Two states, distinguished by a marker.** + +- **A floor file with no marker beside it is user-set.** It is **never overwritten and never + removed**, and it **is** the effective floor — the hook reads it and this design does not + contradict the hook. The cycle discloses the divergence rather than resolving it: + `floor N per workspace knob; profile derivation M per `. + This is the shape §5 already uses for a human override of a derived value — the mode override, + which may raise or lower and is logged with its reason. A workspace knob is that shape at + workspace level. +- **The agent writes only when no user value exists**: the derived floor, plus a marker file + beside it (`.context/codex-gate.floor.derived`) recording the cited-story set and the derived + value. Both are removed at cycle close. A **marker-bearing file found at cycle start is stale + agent state from a crashed or abandoned cycle** — re-derive and overwrite. The marker is agent + bookkeeping under `.context/`, the same class as pass files and resume notes; **no hook reads + it**, so this stays prompt-only. + +Absence of both files is the safe state — the hook defaults to 3 (`codex-gate.sh:119`) and +rejects `0` and non-numeric values (`:124-127`), which bounds typos and not intent. + +**Closure is not only a Gate-B amend.** One knob governs both gates, so cleanup attaches to +**each cycle type's own closing event**: the Gate-A spec loop ends at the spec commit, the +Gate-A plan loop at the plan commit, the Gate-B cycle at the closing amend. Attaching cleanup to +the Gate-B amend alone would leave a Gate-A-derived floor standing through everything that +follows. + +**Failure has a terminal action, not a silent pass.** Write, then **read back and compare**. On a +failed write, a failed read-back, a mismatch between the value read and the value derived, or a +failed removal at close: **stop and name which of the four occurred**. "Could not set the floor" +alone sends a reader retrying the wrong thing, and continuing on an unverified floor is exactly +the missed-passes outcome invariant 2 calls the dangerous direction. A **final read-back at +close** confirms removal. **Exposure, so the value is not invisible until closure.** Every pass report states the **parsed -axes, the derived floor, and the knob value actually read back**. Without this the only account -of the floor is the closing line, written by the same agent that chose it, after every pass has -already been skipped or run. +axes, the derived floor, whether a user knob is in force, and the value actually read back**. +Without this the only account of the floor is a closing line written by the same agent that +chose it, after every pass has already been run or skipped. **A stated limitation, not a guarded one.** `.context/codex-gate.floor` is one checkout-global -mutable value. Two cycles running concurrently in one checkout with different profiles would -share it, and nothing serializes them. Cycles are sequential by construction here, so this is a -**stated limitation** in the same shape as §5's existing note that concurrent calls on one slot -race — not a mechanism, and the shipped text says so rather than implying safety. +mutable value. Overlapping Gate-A and Gate-B cycles in one checkout, or cycles run by separate +sessions, would share it and nothing serializes them. §5's existing note that concurrent calls on +one slot race has the same shape. The shipped text states this rather than implying safety, and +does not claim cycles are sequential by construction — nothing enforces that. -**Provenance.** A cycle records `floor N per ` in its closing commit body — for -every cited story, so a multi-story cycle's derivation is reconstructible — making the value -auditable in history rather than only in per-clone state. +**Provenance.** Every cycle records `floor N per ` in its closing commit body, +one entry per cited story, plus the divergence form above where a user knob is in force. Every +cycle, not only a non-default one: an absent line must not be ambiguous between "default" and +"forgotten". ### 4.2 A profile that moves mid-cycle -Composed from three rules §5 already has; no new rule. - -- The floor derives from the **current** profile at each pass — §5 already requires the header - to be read fresh at each pass, never remembered or copied. -- **Passes already run keep counting**, per the existing rule. -- **Closing requires meeting the floor as currently derived.** +Composed from three rules §5 already has; no new rule. The floor derives from the **current** +profile at each pass (§5 already requires the header read fresh); **passes already run keep +counting**; **closing requires meeting the floor as currently derived**. **The consequence that must be stated, or the arithmetic reads wrong:** under a **raise**, at least one further pass is required *regardless of the floor arithmetic*, because §5 already requires the final clean pass to run under the current profile. A previously-final clean pass -stops qualifying the moment the profile moves — so even a raise that leaves the floor unchanged -(`standard` → `high`, both 3) still costs a pass. +stops qualifying the moment the profile moves — so even a raise leaving the floor unchanged +(`standard` → `high`, both 3) costs a pass. -**On lowering, recorded so nobody later reads the floor as having opened this.** A lower drops -the floor *and* the lens sets *and* the evidence mode, so a `high` cycle lowered to `trivial` -can close on one pass. That is the pre-existing profile-change path — human-confirmed in both -directions and logged. The variable floor rides that path; it does not create it. +**On lowering.** A lower drops the floor *and* the lens sets *and* the evidence mode, so a `high` +cycle lowered to `trivial` can close on one pass. That is the pre-existing profile-change path, +human-confirmed in both directions and logged. The variable floor rides it; it does not create it. --- ## 5. Q6 — the pass-4 report when prior-pass history is unavailable -**Answer: report what is computable, name what is not and why, and disclose the reduced -sensitivity.** Not a new stop condition, and not a mandatory resume note. +**Answer: report what is computable, name what is not and why, disclose the reduced sensitivity.** +Not a new stop condition, not a mandatory resume note. -The reasoning is arithmetic. Of the five tells, **three need history** — finding count rising, -Blocker count failing to fall, a require↔withdraw pair — and **two are computable from the -current pass alone**: findings clustering on the instrument, and findings clustering on prose. -So with no prior record the two-tell threshold **remains reachable** on the cluster pair. The -duty does not become inoperative; it loses sensitivity. +Of the five tells, **three need history** — finding count rising, Blocker count failing to fall, a +require↔withdraw pair — and **two are computable from the current pass alone**: clustering on the +instrument, clustering on prose. With no prior record the two-tell threshold **remains reachable** +on the cluster pair. The duty loses sensitivity; it does not become inoperative. -**History is unavailable in more shapes than total loss**, and the rule covers each: +**Four shapes, each with its own check and remedy** — symptoms alone would be prompt-standards +item 10's failure: -| Shape | Treatment | -|---|---| -| absent — fresh checkout, cleared `.context/`, another machine, cycle resumed elsewhere | the tell is uncomputable; report it as such | -| **partial** — some passes present, others missing | compute the historical tells over the passes present and **say which pass numbers are missing**; a trend over an unstated subset reads as a trend over the cycle | -| **malformed or unreadable** — a file failing the pass-acceptance checks | treated as absent for that pass, **never** as a zero-finding pass; a count read from a file that failed validation is not evidence | -| **stale** — a file at the slot from an earlier cycle | treated as absent, and its presence reported, because a foreign curve is worse than no curve | +| Shape | Check | Treatment | +|---|---|---| +| **absent** | the slot file does not exist | uncomputable; report which pass numbers are missing | +| **partial** | some pass slots present, others not | compute historical tells over the passes present and **name the missing pass numbers** — a trend over an unstated subset reads as a trend over the cycle | +| **malformed** | fails the pass-acceptance checks: terminator, count match, no non-finding lines | treat as absent for that pass, **never** as zero findings; a count from a file that failed validation is not evidence. Distinct from *unreadable* below because the remedy differs: a malformed file is a review that ran and wrote badly — the pass may be re-runnable from its `sessionId` | +| **unreadable** | the file exists but cannot be opened or decoded | treat as absent; report the OS-level cause, because permissions and a full disk need different fixes from a bad write | +| **stale** | the slot carries no cycle identity, or an identity other than this cycle's | treat as absent **and report its presence** — a foreign curve is worse than no curve | + +**Stale needs an identity to detect, and slots do not carry one.** Pass slots are numbered, not +cycle-keyed, so a pass-2 file from a previous cycle is indistinguishable from this cycle's by +content alone. Two mitigations, both prompt-only: this design's **slot discriminator convention** +(a cycle uses a per-cycle infix, as this cycle's `rle` does, following the `fic2` and `pr15` +precedent), and **§5's existing delete-and-confirm-before-each-call rule**, which already +guarantees the file you are about to write is not a previous cycle's. Neither makes stale +detectable *after the fact*; the report says so rather than implying detection. -**The disclosure is not optional, and names the cause.** The report says which tells could not be -computed, **which shape above applies**, and which pass numbers are affected — so the human sees -a degraded reading rather than a clean one. +**The disclosure is not optional and names the cause.** The report says which tells could not be +computed, **which shape applies**, and which pass numbers are affected — so the human sees a +degraded reading rather than a clean one. -**Why the alternatives are rejected**, recorded per story criterion 5: making the resume note -mandatory changes an artifact §5 explicitly calls advisory ("Nothing depends on it existing"); -treating unavailable history as its own stop condition would halt every cycle resumed on a fresh -checkout; restarting the pass-4 clock at the first *visible* pass silently lowers coverage -without saying so. +**Why the alternatives are rejected**, per story criterion 5: making the resume note mandatory +changes an artifact §5 explicitly calls advisory ("Nothing depends on it existing"); treating +unavailable history as its own stop would halt every cycle resumed on a fresh checkout; +restarting the pass-4 clock at the first *visible* pass silently lowers coverage. -**Stated risk, because this answer has a direction.** Reporting rather than stopping fails -*toward continuing* the loop, which is the direction AGENTS.md invariant 2 questions for the -hook. The mandatory disclosure is what makes the answer acceptable; without it the design would -take a different one. +**Stated risk.** Reporting rather than stopping fails *toward continuing* the loop, the direction +AGENTS.md invariant 2 questions for the hook. The mandatory disclosure is what makes it +acceptable. -### 5.1 The curve in the closing body — what it does and does not reach +### 5.1 The curve in the commit body — all three loops -The closing commit of a **Gate-B cycle** carries the per-pass finding and Blocker counts, in one -pinned greppable form following the `3cdd075` precedent: +**Each of the three loops records its own per-pass finding and Blocker counts in its own commit +body:** the **Gate-A spec loop** in the spec's commit, the **Gate-A plan loop** in the plan's +commit, the **Gate-B cycle** in the closing amend. Form pinned to the `3cdd075` precedent: ``` Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0. ``` +**Gate B alone would leave the dominant cost unmeasured.** The loops this story cites as evidence +are Gate-A loops — nineteen measured Gate-A passes on one spec. P8 without Gate-A curves cannot +measure the thing the problem statement is about. (An earlier revision narrowed this to Gate B on +a pass-1 finding, and pass 2 found the narrowing wrong; the scope is settled here rather than +oscillating.) + **The form is pinned, because an unpinned one is unparseable.** One entry per **valid** pass, in -pass order, comma-separated. A `reviewType: full` pass contributes **one entry, the sum of its -two branch files**, since the pass is the unit the floor counts. **Incomplete passes are -excluded** — they are not reviews, and §5 already says they do not count. A **valid pass with -zero findings is written as `0`**, never omitted, so position always equals pass number. - -**What it reaches, corrected.** This is the durable half **across cycles** — it survives a fresh -checkout, a cleared `.context/` and a different machine, which is what P8 and any future -resumption need. **It does not restore history within a running cycle**: the closing commit does -not exist until the cycle closes, so it is no help at pass 4 of the cycle still running. There -the §5 degraded-sensitivity answer governs. A cycle that wants mid-cycle durability may fold the -running curve into the `WIP:` body by amend — **permitted, not required.** - -**Without the curve requirement**, the economics measurement deferred to the P8 story is -answerable only for cycles whose author happened to write it down — `3cdd075` and `baa75c1` did, -`7bbdb14` recorded the pass total and no distribution — because the findings files behind those -numbers live under gitignored `.context/`. P8 would report on a self-selected subset with -nothing marking that it did. - -**Squash carry.** §5's existing squash-merge rule names only evidence entries and human-exception -records. The **decline records (§2.1), the floor provenance line (§4.1) and this curve** are -added to that list, in both copies. A record that does not survive the squash is unreachable from -`main`'s history, which is the whole reason that rule exists. +pass order, comma-separated. +- A `reviewType: full` Gate-B pass contributes **one entry, the sum of its two branch files** — + the pass is the unit the floor counts. +- **Separate `spec` and `quality` calls, and a single-branch recovery resume, are branches of one + logical pass** and likewise contribute one summed entry. The hook counts calls; the curve counts + passes, and the two are deliberately not the same number. Where they differ, the body says so: + `(N calls, M passes)`. +- **Incomplete passes are excluded** — they are not reviews, and §5 already says they do not count. +- **A valid pass with zero findings is written as `0`**, never omitted, so position equals pass + number. + +**What it reaches.** The durable half **across cycles** — surviving a fresh checkout, a cleared +`.context/`, a different machine, which is what P8 and any future resumption need. **It does not +restore history within a running cycle**: the commit does not exist until the loop closes, so it +is no help at pass 4 of the loop still running. There §5's degraded-sensitivity answer governs. A +cycle wanting mid-cycle durability may fold the running curve into the `WIP:` body by amend — +**permitted, not required.** + +**Squash carry.** §5's squash-merge rule names only evidence entries and human-exception records. +The **decline records (§2.1), the floor provenance line (§4.1) and these curves** are added to +that list, in both copies. A record that does not survive the squash is unreachable from `main`'s +history, which is why that rule exists. --- ## 6. Old-conditions accounting Required by the AGENTS.md Don't and by story criterion 6. **It covers every passage this change -rewrites, not only the floor wording** — an accounting scoped to one part, inside a section -citing that Don't, is the failure the Don't describes. +rewrites, and the accounting is performed here** — deferring the thing that constitutes +compliance while claiming compliance is the failure the Don't describes. -**Method:** for each passage below, list what the existing prose required, then mark each -requirement **kept**, **moved**, or **deliberately dropped**. The plan carries the marked list -per passage and the exact replacement wording; the spec fixes the enumeration, so no passage is -rewritten without an accounting existing for it. +### 6.1 The floor-wording inventory is generated, not hand-derived -**Every passage rewritten, by its bold lead-in — each verified present exactly once in both -copies:** +I hand-derived this table three times and got a different count each time. The inventory is now +**the output of a stated command**, so the count cannot drift from the enumeration. Run in the +repo root, against both copies: -| Passage | Rewritten by | -|---|---| -| "What a loop absorbs, and what stops it" | part 3 — the scope stop becomes an exit in a stated ordering | -| "Recognizing \"clearly stuck\"" | part 3 — becomes a suspension; clean-completion precedence moves into the ordering | -| "Surfacing does not close the cycle" | part 3 — this is the mechanism sentence §1.2 names | -| "From pass 4 onward every pass report carries three lines" | part 3 (two-tell stop as suspension) and §5 (Q6 computability and disclosure) | -| "The two rules above do not compete" | part 3 — superseded by the ordering, kept or retired explicitly | -| "**Both gates are a LOOP with a HARD FLOOR**" | part 1 — the floor statement itself | -| "The Gate-B triviality skip needs two independent conditions" | part 1 — adjacent profile-derived relaxation; checked for consistency with the floor predicate | -| "A cycle citing several stories" | part 1 — gains the floor dimension under unanimity | -| "**Severity:** Blocker (wrong/unsafe/breaks invariant)…" | part 2 — the severity definition | -| "Scope, and it is narrow" | part 3 — must distinguish the human-exception form from the decline record | -| "Recording a human exception" | part 3 — the decline record reuses this transport and must not be confused with it | -| "On squash-merge, copy every evidence entry…" | §5.1 — three new record types added to the carry | - -**Plus the floor-wording sites, which are mechanical.** Making the floor variable falsifies prose -that assumes it is 3. **Twelve sites, ten changing**, identical in both copies: five paired rows -(ten sites, of which row 5's pair stays) plus the paired sentence that follows. - -| # | `CLAUDE.md` | template | Text | Disposition | -|---|---|---|---|---| -| 1 | `:72` | `:272` | "min 3 passes per run" | **changes** — the floor statement itself | -| 2 | `:77` | `:277` | "if pass 3 still finds Blocker/Major, keep going until clean" | **changes** — at floor 1 the relevant pass is not pass 3 | -| 3 | `:236` | `:421` | "don't count it toward the 3-pass floor" | **changes** → "the floor" | -| 4 | `:403` | `:582` | "The 3-pass floor, the Blocker/Major filter…" | **changes** → "the floor" | -| 5 | `:249` | `:434` | "PR #23's Gate-B pass 3 returned all four findings at `IMPORTANT`" | **stays 3** — cites an actual historical pass, not a rule | - -**Plus the one that no search for "3" finds**, in both copies: - -> "**Below the floor nothing closes**, and a zero-finding pass remains the only exception, -> exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps looping." - -Correct under floor 3. **False under floor 1**, where pass 1 is *at* the floor and therefore -closes. It becomes "a Blocker/Major-free pass **below the floor** carrying a Minor keeps -looping." This is the site worth the most attention in the whole change: a rule that silently -inverts in exactly the configuration part 1 introduces, invisible to any grep for the digit -because it says "pass 1." +``` +grep -nE "min 3 passes|below 3|3-pass|3 passes|where the 3 come from|3-passes-per-gate" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +It returns **six sites per copy, symmetric** — `:72/:272`, `:79/:279`, `:236/:421`, `:300/:485`, +`:335/:519`, `:403/:582` — **twelve sites, all changing.** Each states a rule that a variable +floor falsifies: the floor itself; the zero-finding early exit "below 3"; the incomplete-pass +rule; Gate A's "each its own 3-pass loop"; "where the 3 come from"; and the lens-set sentence. + +**Two limitations, stated because a generated list reads as complete:** +1. **It finds digit sites only.** The site that matters most spells no digit — `:126/:322`, + "*a Blocker/Major-free **pass 1** carrying a Minor keeps looping*", correct under floor 3 and + **false under floor 1**, where pass 1 is *at* the floor and therefore closes. Two more sites, + both changing, found by hand and listed alongside. **Fourteen sites in total, all changing.** +2. **It is a floor, not coverage.** Another digit-free formulation would escape it exactly as + this one did. The hand-check is not optional. + +It correctly does **not** match `:249/:434` — "PR #23's Gate-B pass 3 returned all four findings +at `IMPORTANT`" — which cites an actual historical pass rather than stating a rule, and stays. + +### 6.2 Condition inventory for every rewritten passage + +For each passage: what its existing prose requires, and the disposition of each requirement. +The plan carries the replacement wording; the accounting is here. + +| Passage (bold lead-in, present once in both copies) | What it currently requires | Disposition | +|---|---|---| +| "**Both gates are a LOOP with a HARD FLOOR**" | min 3 per run; Blocker/Major only; counted by the hook; a TodoWrite per pass; final pass clean; the only early exit below floor is a zero-finding pass | floor value **moved** to the profile predicate; every other requirement **kept** verbatim | +| "What a loop absorbs, and what stops it" | in-set findings absorbed and acted on by severity; ancestry never decides action; assigned fix set fixed before the pass; unclear membership resolves outside; out-of-set correction stops the loop; a new structural/contract question stops it, novelty not size; stopping is not an exit from the gate | all **kept**; the scope stop is **moved** into §2's ordering as one of three suspensions, and gains the decline qualification | +| "Recognizing \"clearly stuck\"" | read the Blocker curve across passes; neither curve measures coverage; three conditions together; clean completion takes precedence; below the floor nothing closes; zero-finding pass the only exception | all **kept**; the precedence sentence is **moved** into §2's table and preserved verbatim there; the "pass 1" clause is **changed** to "below the floor" (§6.1 limitation 1) | +| "Surfacing does not close the cycle" | surface with the finding still open; resolve rule not waived; no pass credited clean; loop resumes on the user's decision | all **kept**; this is §1.2's mechanism sentence, and the decline is the single named exception to the third | +| "From pass 4 onward every pass report carries three lines" | carrier is the status report, never the Codex reply or findings file; three lines; five tells; any two mandatory | all **kept**; §5 **adds** the unavailable-history behaviour, which the passage currently leaves undefined | +| "The two rules above do not compete" | absorb decides finding scope, stuck-reading decides loop convergence; neither overrides the other | **kept**, and **moved** to reference §2's ordering rather than restating the relationship | +| "The Gate-B triviality skip needs two independent conditions" | behaviourally trivial **and** `max(risk, security)` is 0; an eligible profile never makes a behaviour-changing diff skippable; skip reason in the commit body; a skip removes the review never the evidence | all **kept**; checked for consistency with the floor predicate, which uses the same level-0 test for a different purpose — **deliberately not merged**, since one relaxes review count and the other removes review entirely | +| "A cycle citing several stories" | battery once; each profiled story satisfies its own mode; lens sets unioned; skip-eligible only if every cited story is | all **kept**; the floor **added** as a new dimension under the same unanimity shape | +| "**Severity:** Blocker … Major … Minor · Nit" | Blocker wrong/unsafe/breaks invariant; Major design flaw → rework; both must resolve; Minor and Nit collect, never iterate | all **kept**; the reachability test **added** as the classifier, with the subject list as worked examples | +| "Scope, and it is narrow" | the human-exception form supplies no permission; never the answer to a below-floor pass, unclean final pass or STOP; authorizes nothing any mandatory rule requires; not for things never owed | all **kept without exception**; §2.1 **adds** the decline as a distinct record type on the same transport and states the distinction, precisely so this passage is not weakened | +| "Recording a human exception" | the three-line form; which commit carries it; decisions after a commit closed; an empty commit is legitimate; the record is an unverified assertion | all **kept**; the decline record **reuses the transport** and adopts the same unverified-assertion honesty | +| "On squash-merge, copy every evidence entry…" | every evidence entry and human-exception record in the range copied into the squash body; nothing performs or checks the carry | **kept**; three record types **added** to the list | --- -## 7. Prerequisite and rollout +## 7. Prerequisite, rollout, and what this change falsifies + +**The template lacks the sentence §3's kinship points at.** "a wrong sentence costs a confused +reader, not broken behaviour" is in `CLAUDE.md` and **absent from the template**. **Resolution: +the template gets it**, so the kinship claim has a referent in both copies. A deliberate reduction +of the 192-line divergence at exactly one seam, because the new rule depends on it — not a general +reconciliation, which stays out of scope. + +**User-facing statements this change falsifies, and which are therefore in the fix set.** The +standing Gate-B lens — "which existing statements does this diff falsify?" — applied to shipped +documentation, where a change makes sentences wrong in files it never touches: + +| Site | What it says | Why it is false after | +|---|---|---| +| `README.md:130` | "`codex-gate.floor` — a positive integer; moves the 3-passes-per-gate floor" | the floor is no longer fixed at 3; the knob now also interacts with a derived value | +| `docs/getting-started.md:34` | "three passes minimum, final pass clean" | not at level 0 | +| `:40` | "the same 3-pass" loop for the plan | not at level 0 | +| `:53` | Gate B "three passes, final clean" | not at level 0 | +| `:84` | "Gate A's floor is unchanged at every level" | **directly contradicted** — this sentence is specifically about profile levels | +| `:86` | "moves the 3-pass floor" | same as README | -**The template lacks the sentence §3's kinship points at.** The rationale sentence — "a wrong -sentence costs a confused reader, not broken behaviour" — exists in `CLAUDE.md` and is **absent -from the `/workflow-init` template**. **Resolution: the template gets it**, so the kinship claim -has a referent in both copies. A deliberate reduction of the existing 192-line divergence at -exactly one seam, made because the new rule depends on it — not a general reconciliation, which -stays out of scope. +These are `docs/**.md` and `README.md`, so prose and Gate-B N/A, but they are **in the fix set** +and criterion-visible. `:84` is the one worth naming twice: a sentence that was true when written +and that this change makes false, in a file the change does not otherwise touch. **What the template edit does and does not reach.** Editing the inline template changes what `/workflow-init` **writes into new or re-initialized projects**. It does **not** update the -`CLAUDE.md` already sitting in a downstream project that ran `/workflow-init` earlier — those -are ordinary project files that accumulate local content, and invariant 9 forbids overwriting -them silently. So downstream repos adopt these rules by re-running `/workflow-init` and taking -the diff it offers, not by upgrading the plugin. The spec states this rather than leaving a -reader to assume a plugin bump propagates rules. +`CLAUDE.md` already sitting in a downstream project — those are ordinary project files that +accumulate local content, and invariant 9 forbids silent overwriting. Downstream repos adopt +these rules by re-running `/workflow-init` and taking the diff it offers, not by upgrading the +plugin. Stated rather than left to an assumption that a plugin bump propagates rules. **Packaging.** The change edits `plugins/dev-workflow/commands/workflow-init.md`, so invariant 12 applies: the implementation surface includes a `plugins/dev-workflow/.claude-plugin/plugin.json` **version bump** and a `CHANGELOG.md` entry. CI enforces the bump on pull requests -(`scripts/check-version-bump.sh`); the CHANGELOG entry is a convention this repo keeps for every -manifest version. +(`scripts/check-version-bump.sh`). --- @@ -461,72 +490,83 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). - **Battery** — the full `AGENTS.md` § Commands chain, green. - **A check that fails without the change.** No automated test is possible for prose, so this - takes §5's other permitted route — a **named verification**, which owes the same - counterfactual. The subject is the §6 site list, which is differential by construction: - **posed as a question about behaviour under floor 1, the pre-change text answers two sites - wrongly** — site 2 sends a reader to "pass 3" when the floor is 1, and the "pass 1 carrying a - Minor keeps looping" sentence says a pass loops where floor 1 requires it to close — **while - the post-change text answers both correctly.** That is the observation that would exist if the - claim were false, and it is observable: a reading of two identified sentences at two revisions, - not a derivation from the change itself. + takes §5's other permitted route — a **named verification**, owing the same counterfactual. The + subject is §6.1's inventory, which is differential by construction: **posed as a question about + behaviour under floor 1, the pre-change text answers wrongly at identified sites** — `:79` + states the early exit as "below 3" where the floor may be 1, and `:126` says a Blocker/Major-free + pass 1 carrying a Minor keeps looping where floor 1 requires it to close — **while the + post-change text answers correctly.** That is the observation that would exist if the claim were + false, and it is observable: a reading of identified sentences at two revisions, not a + derivation from the change itself. **The wiring must be able to produce the failure.** A verification consulting only the post-change text cannot fail and would report success because of how it was wired. Both - revisions get read, and the entry says which sentences were read at which revision. -- **A named verification of the risk path.** The risk path is the gate-off lever: a floor of 1 - in effect without a profile licensing it. The verification exercises the §4.1 lifecycle — - a floor written by a level-0 cycle must be **absent** at the start of the next cycle, and the - observation that would exist if the claim were false is a surviving `1` after a close. -- **Parity verification across every changed rule**, per story criterion 7. The two copies - already differ on 192 lines, so parity cannot be asserted from a whole-section diff. The - verification walks **each passage named in §6's first table** and compares its post-change text - across the two copies, recording each difference as deliberate-and-stated or as a defect. The - narrow severity-spelling check in `scripts/check-invariants.sh` covers one item and is not - coverage; nothing mechanical checks the rest, which is invariant 11's stated condition. - -**Instrument discipline, learned from this story's own evidence.** Two defects in the `fic2` -cycle's decision matrix were properties of the technique, not of that instance, and both apply -here: **a state's inputs must include every input the rule reads** (a matrix omitting an input -cannot distinguish the states that input separates, and still looks complete), and **a -counterfactual must distinguish ABSENT from CONTRADICTORY** (claiming a prior state contradicted -a rule it never contained reports a failure mode that state could not produce). Both survived a -full clean pass before being caught. + revisions get read, and the entry records which sentences were read at which revision. +- **A named verification of the risk path.** The risk path is the **agent-written** gate-off + lever. It exercises §4.1's lifecycle without requiring a floor-1 cycle on this branch — which + story criterion 9 forbids, and which no cycle citing this risk-`high` story could produce + anyway. The observation that would exist if the claim were false is **an agent-written floor + file or marker surviving a cycle close**; the verification checks for both after this branch's + own closes, where the derived floor is 3 and the lifecycle still runs. +- **Parity verification across every changed rule**, per story criterion 7, covering **§6.1's + fourteen sites, §6.2's twelve passages, and every rule §§2–5 newly insert** — lifecycle, + exposure, history shapes, curve format, provenance, activation. The two copies already differ on + 192 lines, so parity cannot be asserted from a whole-section diff; the verification walks each + named item and records every difference as deliberate-and-stated or as a defect. The narrow + severity-spelling check in `scripts/check-invariants.sh` covers one item and is not coverage; + nothing mechanical checks the rest, which is invariant 11's stated condition. + +**Instrument discipline, from this story's own evidence.** Two defects in the `fic2` decision +matrix were properties of the technique: **a state's inputs must include every input the rule +reads**, and **a counterfactual must distinguish ABSENT from CONTRADICTORY**. Both survived a full +clean pass before being caught. **Known open question, not resolved here.** How much instrument a one-paragraph prose rule is -worth is carried by the evidence doc as a question, not a commitment. This design does not answer -it and does not pretend the fixture-per-predicate demand is settled. +worth is carried by the evidence doc as a question, not a commitment. --- ## 9. Out of scope -Named so no part of the design absorbs them: hook code (any change under -`plugins/dev-workflow/hooks/`); gate-call observability (upstream, `mcp-codex-dev`); the -pass-counter anomaly, undiagnosed and needing hook-state inspection; the CodeRabbit -plan-metadata contradiction; the fixture-per-predicate question; any remedy to the supersession -convention; and general reconciliation of the two copies' 192-line divergence beyond the one -seam §7 names. +Hook code (any change under `plugins/dev-workflow/hooks/`); gate-call observability (upstream, +`mcp-codex-dev`); the pass-counter anomaly, undiagnosed and needing hook-state inspection; the +CodeRabbit plan-metadata contradiction; the fixture-per-predicate question; any remedy to the +supersession convention; deprecating the user-facing floor knob (a documented capability, whose +removal is Daniel's decision and unnecessary given §4.1); a hook-read marker or separate +agent-floor file (needs hook code); and general reconciliation of the two copies' 192-line +divergence beyond the one seam §7 names. --- ## 10. Risks and activation -- **When these rules bind.** They take effect from the commit that ships them. **A cycle already - in flight finishes under the rules it started with** — the floor it derived, the severity - definition it applied, the exits it knew — because re-deriving a floor mid-cycle from a rule - that did not exist when passes were banked would invalidate a count nobody could reconstruct. - This is stated in the shipped text, not left to inference. It also disposes of this design's - own case: its Gate A runs under the old rules, and nothing here is retroactive. -- **The disclosed gate-off lever is real and only partly mitigated.** §4.1's lifecycle removes - the *accidental* persistence path; the *deliberate* one remains — an agent-written value in - per-clone gitignored state, unverified against the profile. §4.1's exposure requirement and - §4's provenance line make it visible in the pass report and in history; neither is a guard. If - that is unacceptable, the answer is hook code, which is out of scope by decision — and - re-opening it is a stop-and-ask, not a silent expansion. -- **The reachability test needs judgement** at the moment §5 is trying to remove judgement. The - named-reader-and-changed-decision phrasing is what makes it decidable; if Gate A finds it - admits or excludes a case wrongly, that is a finding about this design, not about the settled - contract. -- **Q6's answer fails toward continuing the loop.** Stated in §5 with the disclosure that makes - it acceptable. +- **When these rules bind.** They take effect from the commit that ships them, and **a loop + already in flight finishes under the rules it started with** — re-deriving a floor mid-loop + from a rule that did not exist when passes were banked would invalidate a count nobody could + reconstruct. **The start marker is the loop's own first pass artifact**: a loop whose pass-1 + slot predates the shipping commit started under the old rules. That is recoverable from git + plus file mtime, and where neither is available the shipped text says to **treat the loop as + new and re-derive**, which costs passes and never skips them. This also disposes of this + design's own case: its Gate A runs under the old rules. +- **Abandonment and rollback.** Cleanup at close covers the successful path only. A loop + abandoned, or halted by a blocking profile or setup stop, **leaves its marker file behind by + design** — §4.1 treats a marker-bearing file at cycle start as stale agent state and + re-derives, so the next cycle self-heals rather than inheriting. **Rollback to prompt text that + predates this change** leaves a marker no rule mentions; it is inert (no hook reads it) and the + floor file it accompanies reverts to being read exactly as the old rules read it. +- **The gate-off lever is narrower than before but not closed, and it has more than one shape.** + §4.1's lifecycle removes the accidental-persistence path. What remains, and is disclosed: + writing a derived floor the cited set does not license; **omitting a higher-risk cited story + from the set**; **minting or editing a story profile to level 0**; and presenting an incomplete + cited-story set. The first is bounded by §4.1's read-back; the rest are bounded only by the + provenance line naming *which* stories were cited, which makes an omission visible to a reader + who checks the branch's actual work. None of this is a guard. +- **A user-set floor of 1 is not the gate-off lever, and the text keeps them distinct.** It is a + human decision on a documented knob, disclosed in the provenance line. The lever is an + *agent-written* floor without a licensing profile. Conflating them would either make a shipped + user capability read as an attack or make the real lever read as sanctioned. +- **The reachability test needs judgement** where §5 is trying to remove it. The + named-reader-and-changed-decision phrasing is what makes it decidable. +- **Q6's answer fails toward continuing the loop.** Stated in §5 with the disclosure that makes it + acceptable. - **The expected demotion is a prediction.** §3 says so; P8 measures it. If it demotes far less - than hoped, the rule is still correct and the economics claim was the thing that was wrong. + than hoped, the rule is still correct and the economics claim was what was wrong. From df4a78da4e015879b6907f3fd574657f7d9dcc42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:30:42 +0200 Subject: [PATCH 012/117] docs(story): align desired outcome 2 with the settled severity test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate-A pass 3, two findings against one paragraph. Outcome 2 still described the false-green-only instrument carve-out that criterion 5 had already moved past two commits earlier -- the same fix-the-criterion-leave-the-outcome pattern that produced the criterion 8 and criterion 5 Blockers. It now carries the consequence-keyed test and the bidirectional carve-out. It also promised a decision made "without judgement calls", while the spec says in as many words that the reachability test needs judgement at exactly the moment §5 is trying to remove it. A stated test removes arbitrariness, not judgement. The story must not promise what no prose rule delivers, and a criterion that cannot be met is worse than one that is honest about its bound. Decided by the sparring session under Daniel's 2026-08-28 delegation. Profile unchanged, so no profile-log line. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- ...8-review-loop-economics-pass-floor-story.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index c458b21..242a2dc 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -53,12 +53,18 @@ Three outcomes, each observable in the shipped prompt text. charging every cycle the same three passes per gate. A story the profile puts at level 0 stops paying a toll that buys nothing; a standard or high story keeps the floor it has today. -2. **Severity means product behaviour.** A reader of either §5 copy can decide, without - judgement calls, whether a finding about narration, about prose describing a mechanism, or - about a test instrument's internals is allowed to hold the loop open — and knows the one - case where an instrument finding still must (it demonstrates a false green on product - behaviour). Coverage-first is unchanged: the reviewer still reports everything, and the - filter stays ours. +2. **Severity means product behaviour.** A reader of either §5 copy can decide, **by a stated + test rather than by taste**, whether a finding about narration, about prose describing a + mechanism, or about a test instrument's internals is allowed to hold the loop open — the + test being whether an in-system reader takes a different decision if the text is wrong — and + knows that an instrument finding still holds the loop open whenever it shows the instrument + changes what the gate concludes about product behaviour, **in either direction**: a false + green, and equally a false red or a check that blocks a valid change. Coverage-first is + unchanged: the reviewer still reports everything, and the filter stays ours. + *(Revised 2026-08-28 after Gate-A pass 3. Two corrections: this outcome still described the + false-green-only carve-out that criterion 5 had already moved past; and "without judgement + calls" promised more than any prose rule can deliver — a stated test removes arbitrariness, + not judgement, and the spec says so plainly, so the story must not promise otherwise.)* 3. **The §5 loop rules stop being amended one clause at a time.** The six open contract questions the `fic2` cycle raised and declined to answer get one coherent answer, taken together, with Daniel's five recorded decisions as settled inputs. The alternative — From 0edafeb40198e434e010e323b8a3faab091fdf2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:34:27 +0200 Subject: [PATCH 013/117] =?UTF-8?q?docs(spec):=20revision=204=20=E2=80=94?= =?UTF-8?q?=20delete=20the=20marker=20mechanism;=20deepen=20the=20accounti?= =?UTF-8?q?ng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 3: 54 findings (0 BLOCKER, 43 MAJOR, 11 MINOR). Blockers reached zero; the Major surge was one mechanism's edge-case tax. Sixteen of those Majors were against the marker protocol revision 3 added to distinguish an agent-written floor from a user's. One of them settled it: the cheapest bypass was marker-specific -- write 1 without a marker, or delete the marker afterwards, and an agent-chosen floor is camouflaged as a user decision. A protection that makes the attack indistinguishable from the protected case is not incomplete, it is inverted. So the mechanism is deleted rather than repaired. Nothing in this design writes .context/codex-gate.floor. The floor is derived and STATED -- in every pass report and in the commit-body provenance line -- and §5's text is what binds the agent, as it always did. The knob stays what it mechanically always was: the hook's reminder threshold. Verified, not assumed -- $floor appears only inside the hook's advisory note messages, and invariant 1 makes the hook advisory (the one exit 2 in that file belongs to an embedded awk program, not the shell). The accepted cost, stated: a level-0 cycle closing at one pass draws a hook reminder reading "below floor (1/3)". That is invariant 2 working -- "a redundant warning is the accepted price" -- and a hook taught to fall silent at 1 would be the false check the same invariant calls dangerous. That deletion removed sixteen findings, the concurrency exposure on a shared mutable file, the rollback contamination, the start-marker problem, and every crash-recovery rule written in prose for an advisory file. The spec is shorter than revision 3 despite five new accounting rows. Also fixed: §1.2 claimed Blocker/Major-must-resolve is "definitional", which is the describe-what-a-gate-proves failure in my own text -- a pass raising no NEW Blocker/Major is not evidence an earlier one was resolved. §2.1's decline record now stores all five fields the sameness test reads, instead of testing on fields the record lacked; a decline binds one cycle only; and acceptance ends the hold but not the resolve duty. §5.1's curves are labelled per loop, bind both branches of a logical pass to one artifact revision, and say plainly that they are author-written and unchecked, so P8 cannot read a self-reported curve as measurement. §6.2 grew from twelve rows to seventeen, each row deepened to the conditions it had been compressing, with every generated floor site mapped to a row. §7 corrects a Gate-B misclassification I had made myself: doc edits are only N/A in a docs-only commit, and a mixed commit forfeits the exemption -- the plan chooses and states which. §7 also gains docs/coding-workflow.md:79-80, a second copy of the "Gate A's floor is the same at every level" claim missed until now. §8's risk-path verification no longer passes vacuously. Contract decision by the sparring session under Daniel's 2026-08-28 delegation. Gate A: passes 1-3 at .context/codex-reviews/gate-a-spec-rle-pass-{1,2,3}.md. Findings 27, 30, 54. Blockers 5, 2, 0. Floor 3 met by count; not clean yet. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 605 +++++++++--------- 1 file changed, 296 insertions(+), 309 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 5594841..2cd6986 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,17 +1,18 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 3, after Gate-A passes 1 (27 findings) and 2 (30) +**Date:** 2026-08-28 · **Revision:** 4, after Gate-A passes 1 (27), 2 (30), 3 (54 findings) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. -Prompt-only. No file under `plugins/dev-workflow/hooks/` changes. +Prompt-only. No file under `plugins/dev-workflow/hooks/` changes, and **no file the hook reads +is written by this design.** **Surfaces edited.** `CLAUDE.md` §5 (65–589) and its mirror inside `/workflow-init`'s inline `CLAUDE.md` template (`plugins/dev-workflow/commands/workflow-init.md`, fenced 192–778; §5 is -257–777). Plus the user-facing statements this change falsifies (§7). The two §5 copies already +257–777), plus the user-facing statements this change falsifies (§7). The two §5 copies already differ on 192 lines across 20 hunks; this design touches only the rules it changes, per story -criterion 7, and states each deliberate variance where it creates one. +criterion 7. --- @@ -21,18 +22,13 @@ criterion 7, and states each deliberate variance where it creates one. qualifying the ones before it — and nowhere does §5 say which wins when two apply at once. *Scope of that claim:* four exits **of the loop**. §5 carries other mandatory stops that are not -loop exits — a target file surviving deletion, an exhausted recovery budget, an unresolvable -profile, a blocking evidence or setup gap. Those halt the *procedure* rather than resolving the -*cycle*, they do not compete with clean completion, and this design does not reorder them. - -The missing ordering is why the `fic2` cycle could not ship two small clauses without qualifying -three rules nobody proposed changing. So "consolidated, done once instead of clause-by-clause" -means **state the ordering once**, after which most answers are readings of it rather than new -rules. +loop exits — a target surviving deletion, an exhausted recovery budget, an unresolvable profile, +a blocking evidence or setup gap. Those halt the *procedure* rather than resolving the *cycle*, +do not compete with clean completion, and are not reordered here. ### 1.1 Only one exit closes -Both facts are already in §5, in two places, and neither draws the conclusion: +Already in §5, in two places, with the conclusion never drawn: > "Stopping this way is **not an exit from the gate**: the floor, the Blocker/Major filter and > the clean-final-pass rule all stand, and the loop resumes on the revised artifact once the @@ -42,16 +38,20 @@ Both facts are already in §5, in two places, and neither draws the conclusion: > credited as clean, and the loop resumes on whatever the user decides." **Clean completion closes. The scope stop, the clearly-stuck exit and the two-tell stop -suspend.** Consequence: **suspension × suspension is not a conflict** — two at once means the -report carries both reasons. Three pairs, no ordering, one sentence. +suspend.** So **suspension × suspension is not a conflict** — two at once means the report +carries both reasons. Three pairs, one sentence, no ordering. ### 1.2 Three of the four duties are not participants - **The floor** is a quantity gating closure — "**Below the floor nothing closes**". -- **Blocker/Major must resolve** is definitional: a clean pass is Blocker/Major-free. +- **Blocker/Major must resolve** is a **precondition on closure, and it is not discharged by a + quiet pass.** A pass that raises no *new* Blocker or Major is not evidence that a previously + surfaced one was resolved; the resolution is a separate fact, and the closing report names + each surfaced Blocker/Major and how it was resolved. (An earlier revision called this + "definitional", which is the describe-what-a-gate-proves failure: the clean-pass condition + proves what the reviewer found *this* pass, not what happened to earlier findings.) - **A surfaced finding stays open** is the *mechanism* making the other exits suspensions. -- **No pass carrying a surfaced finding counts as clean** is the only participant. Every - remaining question lives there. +- **No pass carrying a surfaced finding counts as clean** is the only participant in ordering. --- @@ -59,23 +59,20 @@ report carries both reasons. Three pairs, no ordering, one sentence. | Conflict | Resolution | |---|---| -| clean completion × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim. Settled and probably unreachable — a clean pass has no regenerating Blocker/Major, so the exit's third condition fails. Kept because dropping a sentence §5 already spends is the dropped-condition failure criterion 6 exists to prevent. | +| clean completion × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim. Settled and probably unreachable — a clean pass has no regenerating Blocker/Major, so the exit's third condition fails. Kept because dropping a sentence §5 already spends is the failure criterion 6 exists to prevent. | | clean completion × two-tell stop | **Clean wins.** Daniel's recorded decision, encoded not reopened. | | clean completion × scope stop | **The scope stop outranks closure while any surfaced finding remains unresolved and undeclined.** A finding the user has explicitly declined is resolved *for this purpose* and no longer holds the cycle. | **The third cell's wording is deliberate.** An earlier draft read "clean wins only when every surfaced finding still open has been explicitly declined", which is self-contradictory: §2.1 -defines a decline as *releasing* the finding, so a declined finding is not still open. The rule -is stated once, from the hold's side — what keeps a cycle open — so the decline's effect is -described in exactly one place. +defines a decline as *releasing* the finding. The rule is stated once, from the hold's side. **Why the third is the only one that needed deciding.** A scope stop is triggered by a *specific -finding*; while the duty stands unqualified that finding is open, so the triggering pass cannot -be clean and the scope stop wins automatically. The cell exists only because the duty may now be -qualified. +finding*; while the duty stands unqualified that finding is open, so the pass cannot be clean and +the scope stop wins automatically. The cell exists only because the duty may now be qualified. **The asymmetry that explains the history:** the two-tell stop is triggered by *statistics about -findings*, not by a finding. Nothing is left open, the duty does not bite, clean can win. The +findings*, not by a finding — nothing is left open, the duty does not bite, clean can win. The scope stop's trigger **is** a finding. The reverted clause tried to unbite that by qualifying three universal rules at once. The lattice was correct. @@ -83,69 +80,71 @@ three universal rules at once. The lattice was correct. **The duty is qualified for, and only for, a finding the user has explicitly declined.** Per story criterion 5, the qualification appears **at each of the three universal rules** in both -copies — not only at the new clause. Placing it only at the new clause is what made the earlier -attempt unshippable. +copies — not only at the new clause, which is what made the earlier attempt unshippable. -**All three branches, stated symmetrically.** §5 already says the loop "resumes the moment the -user says whether the set now includes it" — resumption *is* the hold ending, whichever way the -answer went. +**All three branches, symmetrically.** §5 already says the loop "resumes the moment the user says +whether the set now includes it" — resumption *is* the hold ending, whichever way the answer went. - **Declined** → released as recorded-declined; stops holding the cycle for the remainder of it, and does not re-stop later passes. - **Accepted** → enters the assigned set, where **severity governs exactly as Mechanics already - says**: accepted Blocker or Major must resolve; accepted Minor or Nit is collected, never - iterated. In-set Minors have never blocked a clean pass at or above the floor, so there is no - deadlock — the apparent one assumed the hold outlives the user's answer, and the resume - sentence says it does not. + says**: an accepted Blocker or Major must resolve, **and the pass that surfaced it is still not + clean until it does** — acceptance ends the *hold*, not the resolve duty. An accepted Minor or + Nit is collected, never iterated, and in-set Minors have never blocked a clean pass at or above + the floor. There is no deadlock; the apparent one assumed the hold outlives the answer. - **Undecided** → the hold stands. Nothing closes unanswered. **Where the decision is recorded — the commit body, on rails §5 already ships.** The human-exception machinery already solves this transport: "an ungated change records it in that commit; a Gate-A cycle in the spec or plan commit; a Gate-B cycle in the WIP commit, restated by -the closing amend", folded in mid-cycle by amend, carried into the squash body, empty commit as -last resort. **The decline reuses that transport as a different record type:** +the closing amend", folded in mid-cycle by amend, carried into the squash body. **The decline +reuses that transport as a different record type:** ``` -Declined finding: // · · · +Declined finding: · // · · +Location: Defect: +Severity: Consequence: Fix: Reason: ``` -**The distinction from the human-exception form is stated explicitly — this is the substance of -the Blocker-4 repair.** They are not the same record: +**The record stores exactly what the sameness test reads.** An earlier revision stored location +and defect while testing sameness against severity, consequence and suggested fix — a test +reading fields the record lacks, which is the same class as the wiring failure §8 warns about. +Both now carry all five. + +**Identity.** A later finding is the same finding when **all five recorded fields match**. Any +difference — including a severity change, since a Minor re-raised as a Blocker is not the thing +that was declined — makes it a **new finding, and the hold applies**. Where sameness is unclear +it is likewise new, which costs a question and never a silent release. This mirrors §5's existing +treatment of unclear set membership. + +**A decline binds one cycle only.** The record carries a cycle identifier and **has no effect in +any later cycle**, even though the record itself persists into commit and squash history. Without +that bound, a decline recorded once would silently release the same finding in every future +cycle, which nobody decided. +**The distinction from the human-exception form is stated explicitly.** They are not the same +record: - The **human-exception form authorizes nothing** — §5 says so in its own words, and says it is - never the answer to a below-floor pass, an unclean final pass, or a `STOP and surface`, and - that neither a human's assent nor the record lets an agent close or continue a cycle. + never the answer to a below-floor pass, an unclean final pass, or a `STOP and surface`. - The **decline record has §5-defined effect**: it releases one specific finding from the hold. That effect comes from the loop's own scope rule — which already routes set membership to the user and already resumes on the answer — not from human assent overriding a mandatory rule. - Every other mandatory rule stands: the floor, the Blocker/Major filter, the clean-final-pass - requirement. + Every other mandatory rule stands. -**What the record can and cannot establish, said plainly.** Like the human-exception record it -sits beside, this is an **unverified assertion**: the commit body is author-written, and nothing -checks that the handle belongs to whoever decided or that a human was asked. A reader learns -that *the commit claims* the user declined this finding. It is the same standing §5 already -gives the human-exception form, and the shipped text says so rather than implying the record is -evidence. What makes the mechanism safe is not verification but **narrowness**: it releases one -named finding and nothing else. +**What the record establishes, said plainly.** Like the human-exception record beside it, this is +an **unverified assertion**: the commit body is author-written, and nothing checks that the handle +belongs to whoever decided. A reader learns that *the commit claims* the user declined this +finding. The shipped text says so rather than implying evidence. What makes it safe is not +verification but **narrowness** — it releases one fully-identified finding, in one cycle. **"Explicitly declined" is defined tightly.** A **recorded user decision on that specific finding**, attributable and unambiguous. **Never** silence, never a general remark about scope, never the agent inferring a decline from context. All three negatives ship. -**Identity — how a declined finding is recognized again.** Findings carry no stable identifier, -so the record names pass, slot and line, plus the **verbatim location and defect fields**. -A later finding is the same finding when **location and defect both match**. **Any change to -severity, consequence or suggested fix makes it a new finding**, because each can change what -the finding asks for even where the defect reads the same — a Minor re-raised as a Blocker is -not the thing that was declined. **Where sameness is unclear it is a new finding and the hold -applies**, which costs a question and never a silent release. This mirrors §5's existing -treatment of unclear set membership, which resolves toward *outside* for the same reason. - -**The dispositions companion stays what §5 says it is** — the advisory working copy, deletable -and rebuildable. The commit body is the record. +**The dispositions companion stays what §5 says it is** — the advisory working copy. The commit +body is the record. --- @@ -157,74 +156,68 @@ and rebuildable. The commit body is the record. > and the decision it takes differently if the text is wrong. If you cannot name an in-system > reader and a changed decision, the finding is **Minor** — collect, never iterate. -Findings about narration, mechanism prose, and test-instrument internals are the cases this -usually catches, shipped as **worked examples of the test, not as a second rule beside it**. -Stating a categorical demotion *and* the test would give two procedures that can disagree on one -finding; the test governs. +Findings about narration, mechanism prose and test-instrument internals are the cases this +usually catches, shipped as **worked examples of the test, not a second rule beside it**. Stating +a categorical demotion *and* the test would give two procedures that can disagree on one finding. **A human reader never satisfies the test.** That cost class is already priced as non-gating by -§5's prose exemption — "a wrong sentence costs a confused reader, not broken behaviour". Without -this the test admits everything. +§5's prose exemption — "a wrong sentence costs a confused reader, not broken behaviour". **The instrument carve-out runs in both directions.** An instrument finding keeps its severity when it shows the instrument **changes what the gate concludes about product behaviour** — a false green, and equally a false red, a check that blocks a valid change, or instrument logic -that would drive an unnecessary product rewrite. Naming false green alone would demote a check -that fails for wiring reasons and costs a correct change. +driving an unnecessary rewrite. Naming false green alone would demote a check that fails for +wiring reasons and costs a correct change. **Why not a list of demotable artifact kinds.** The field record falsifies that form. Of three -`fic2` pass-5 findings on one parked story's acceptance criterion, two were correctly parked and -**one was correctly acted on** — "the pass-4 activation boundary — because a future rewrite could -otherwise move the duty to pass 1 while checking off every other listed condition." Same -artifact, same pass, opposite correct answers. An enumeration would also travel into scaffolded -repos whose artifact kinds we have never seen — invariant 10's stack-neutrality problem in a new -place. +`fic2` pass-5 findings on one parked story's criterion, two were correctly parked and **one was +correctly acted on** — "the pass-4 activation boundary — because a future rewrite could otherwise +move the duty to pass 1 while checking off every other listed condition." Same artifact, same +pass, opposite correct answers. An enumeration would also travel into scaffolded repos whose +artifact kinds we have never seen — invariant 10's problem in a new place. **Kinship, stated in the shipped text.** The **finding-level analog of the path-level prose exemption** — one principle at two granularities: text that *describes* the product versus text -that *is* the product. Naming it makes each rule the other's consistency check. +that *is* the product. -**The boundary case, qualified rather than blanket.** Rationale prose inside a rule file — §5's -"Recorded rationale" paragraphs, the why-sentences under AGENTS.md invariants — is **Minor when -no rule's application depends on it**, and **not** categorically Minor. +**The boundary case, qualified rather than blanket.** Rationale prose inside a rule file is +**Minor when no rule's application depends on it**, and **not** categorically Minor. `docs/prompt-standards.md:49-51` requires that "Rules carry their why", because "models follow motivated rules better, and reviewers can judge whether the rule still applies", and invariant 11 makes that checklist binding. Rationale a reader must consult to decide whether or how a rule -applies **is** read by an in-system reader and passes the test. What is Minor is rationale that -only explains, historically or motivationally, a rule whose application is fully determined -without it. Named explicitly because "it's in `CLAUDE.md`, agents read `CLAUDE.md`" is the first -stretch a reviewer will try — and because the blanket form would have contradicted a checklist -item this project must pass. +applies **is** read by an in-system reader and passes the test. + +**This test removes arbitrariness, not judgement**, and the shipped text says so. Two readers can +still disagree about whether a named reader's decision changes; what they can no longer do is +decide by taste, because the test names what must be produced — a reader and a changed decision. +Claiming a judgement-free rule would be a promise no prose rule keeps. **Coverage-first unchanged:** the reviewer reports every finding with severity and confidence; -the filter is ours, never Codex's. +the filter is ours. **Expected effect, with its limit.** The intent is that distributions like PR #23's — 16 in a never-committed scratch harness, 10 in narration, 1 in a plan — demote substantially. **How much is not predictable from the recorded counts**, because `7bbdb14`'s own body describes harness -defects that could make checks pass for wiring reasons, and those are exactly what the -two-directional carve-out keeps. `fic2`'s pass-2 meta cluster demotes only partially: ledger rows -keep severity because rung escalation reads the recurrence count, story criteria because the -assigned-fix-set rule reads them. **This demotes less than the problem statement might suggest, -and the amount is a prediction rather than a measurement** — which is why the story routes the -measurement to P8 instead of asserting an outcome. +defects that could make checks pass for wiring reasons, which the two-directional carve-out keeps. +`fic2`'s pass-2 meta cluster demotes only partially: ledger rows keep severity because escalation +reads the recurrence count, story criteria because the assigned-fix-set rule reads them. **The +amount is a prediction rather than a measurement**, which is why the story routes it to P8. --- ## 4. The pass floor (part 1) **One predicate.** `max(risk, security) == 0` → floor **1**. Everything else → **3**, unprofiled -included. Two levels, not three: `high` takes its added rigor from lens sets and evidence mode. +included. Two levels, not three: `high` takes its rigor from lens sets and evidence mode. **Several cited stories: unanimity.** Floor 1 **if and only if every cited story is profiled and every one is at level 0**. Any other set — mixed levels, any unprofiled member, any higher -profile — yields 3. This is §5's own aggregation precedent for the analogous relaxation ("the -cycle is skip-eligible only if **every** cited story is") applied to a new dimension, and the only -reading consistent with invariant 2's firing direction. +profile — yields 3. §5's own precedent for the analogous relaxation ("skip-eligible only if +**every** cited story is"), and the only reading consistent with invariant 2's firing direction. -**One value, both gates.** `codex-gate.sh:119` sets a single `floor`, consumed at `:946` for -Gate B and `:966` for Gate A. Stated in both copies: a reader who does not know this will write a -rule for one gate and silently move the other. +**Why the text must say the floor governs both gates.** `codex-gate.sh:119` sets a single +`floor`, consumed at `:946` for Gate B and `:966` for Gate A. A reader who does not know this +will write a rule for one gate and assume the other is untouched. **Why there is no `docs-only` arm.** A diff-derived reading cannot serve Gate A, which runs on a spec before any diff exists. A story-declared reading is subsumed: intake defines `trivial` as @@ -232,67 +225,47 @@ spec before any diff exists. A story-declared reading is subsumed: intake define **wrong in this repo**: `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation. That is §3's reachability test deciding a §4 question. -### 4.1 The knob, which is a user's before it is ours - -**`.context/codex-gate.floor` is a shipped, documented user capability.** `README.md:130` lists -it under "Per-workspace knobs" — "a positive integer; moves the 3-passes-per-gate floor" — and -`docs/getting-started.md:85-86` says the same. An earlier draft had the agent writing and -deleting it unconditionally, which would silently overwrite and then destroy a deliberate user -setting. The design does not do that. - -**Two states, distinguished by a marker.** - -- **A floor file with no marker beside it is user-set.** It is **never overwritten and never - removed**, and it **is** the effective floor — the hook reads it and this design does not - contradict the hook. The cycle discloses the divergence rather than resolving it: - `floor N per workspace knob; profile derivation M per `. - This is the shape §5 already uses for a human override of a derived value — the mode override, - which may raise or lower and is logged with its reason. A workspace knob is that shape at - workspace level. -- **The agent writes only when no user value exists**: the derived floor, plus a marker file - beside it (`.context/codex-gate.floor.derived`) recording the cited-story set and the derived - value. Both are removed at cycle close. A **marker-bearing file found at cycle start is stale - agent state from a crashed or abandoned cycle** — re-derive and overwrite. The marker is agent - bookkeeping under `.context/`, the same class as pass files and resume notes; **no hook reads - it**, so this stays prompt-only. - -Absence of both files is the safe state — the hook defaults to 3 (`codex-gate.sh:119`) and -rejects `0` and non-numeric values (`:124-127`), which bounds typos and not intent. - -**Closure is not only a Gate-B amend.** One knob governs both gates, so cleanup attaches to -**each cycle type's own closing event**: the Gate-A spec loop ends at the spec commit, the -Gate-A plan loop at the plan commit, the Gate-B cycle at the closing amend. Attaching cleanup to -the Gate-B amend alone would leave a Gate-A-derived floor standing through everything that -follows. - -**Failure has a terminal action, not a silent pass.** Write, then **read back and compare**. On a -failed write, a failed read-back, a mismatch between the value read and the value derived, or a -failed removal at close: **stop and name which of the four occurred**. "Could not set the floor" -alone sends a reader retrying the wrong thing, and continuing on an unverified floor is exactly -the missed-passes outcome invariant 2 calls the dangerous direction. A **final read-back at -close** confirms removal. - -**Exposure, so the value is not invisible until closure.** Every pass report states the **parsed -axes, the derived floor, whether a user knob is in force, and the value actually read back**. -Without this the only account of the floor is a closing line written by the same agent that -chose it, after every pass has already been run or skipped. - -**A stated limitation, not a guarded one.** `.context/codex-gate.floor` is one checkout-global -mutable value. Overlapping Gate-A and Gate-B cycles in one checkout, or cycles run by separate -sessions, would share it and nothing serializes them. §5's existing note that concurrent calls on -one slot race has the same shape. The shipped text states this rather than implying safety, and -does not claim cycles are sequential by construction — nothing enforces that. - -**Provenance.** Every cycle records `floor N per ` in its closing commit body, -one entry per cited story, plus the divergence form above where a user knob is in force. Every -cycle, not only a non-default one: an absent line must not be ambiguous between "default" and -"forgotten". +### 4.1 The floor lives in the text, not in a file + +**Nothing in this design writes `.context/codex-gate.floor`.** The agent derives the floor from +the profile and **states it** — in every pass report and in the commit-body provenance line. That +is where the floor lives, and §5's text is what binds the agent, as it always was. + +**The workspace knob stays exactly what it always was: the hook's reminder threshold.** It is +never written, never removed, and never read for our derivation. Mechanically it never bound an +agent — `$floor` appears only inside the hook's advisory `note` messages (`:933`, `:946-947`, +`:956`, `:966-967`, `:973`), and the hook is advisory by invariant 1. Where the user's knob and +the derived floor diverge, the provenance line **discloses both** rather than resolving them: + +``` +floor N per ; hook reminder threshold M per workspace knob +``` + +**The consequence, accepted rather than engineered around: a level-0 cycle closing at one pass +draws a hook reminder saying "below floor (1/3)".** That reminder is **the invariant working**, +not a defect — AGENTS.md invariant 2: *"On uncertainty, fire. A missed commit (false ✓) is the +dangerous direction; **a redundant warning is the accepted price.**"* A hook taught to fall silent +at 1 would be the false ✓ the invariant names as dangerous. + +**Why an earlier revision's marker mechanism was deleted rather than repaired.** It had the agent +write the knob plus a marker distinguishing agent writes from user writes. Gate-A pass 3 returned +sixteen Majors against it — write ordering, removal ordering, cross-cycle ownership, staleness, +rollback contamination — and one that settled it: **the cheapest bypass was marker-specific.** +Write `1` without a marker, or delete the marker afterwards, and an agent-chosen floor is +camouflaged as a user decision. A protection that makes the attack indistinguishable from the +protected case is not incomplete; it is inverted. Deleting it removes every one of those findings, +the concurrency exposure on a shared mutable file, and the whole class of crash-recovery rules +written in prose for an advisory file. + +**The sanctioned lever for wanting fewer obliged passes is the profile** — proposed, human- +confirmed, logged — **or `codex-gate.off` for the reminders.** Not the floor knob, which moves +what the hook says and never what §5 obliges. ### 4.2 A profile that moves mid-cycle -Composed from three rules §5 already has; no new rule. The floor derives from the **current** -profile at each pass (§5 already requires the header read fresh); **passes already run keep -counting**; **closing requires meeting the floor as currently derived**. +Composed from three rules §5 already has; no new rule, and nothing to re-write on disk. The floor +derives from the **current** profile at each pass (§5 already requires the header read fresh); +**passes already run keep counting**; **closing requires meeting the floor as currently derived.** **The consequence that must be stated, or the arithmetic reads wrong:** under a **raise**, at least one further pass is required *regardless of the floor arithmetic*, because §5 already @@ -323,164 +296,176 @@ item 10's failure: |---|---|---| | **absent** | the slot file does not exist | uncomputable; report which pass numbers are missing | | **partial** | some pass slots present, others not | compute historical tells over the passes present and **name the missing pass numbers** — a trend over an unstated subset reads as a trend over the cycle | -| **malformed** | fails the pass-acceptance checks: terminator, count match, no non-finding lines | treat as absent for that pass, **never** as zero findings; a count from a file that failed validation is not evidence. Distinct from *unreadable* below because the remedy differs: a malformed file is a review that ran and wrote badly — the pass may be re-runnable from its `sessionId` | -| **unreadable** | the file exists but cannot be opened or decoded | treat as absent; report the OS-level cause, because permissions and a full disk need different fixes from a bad write | -| **stale** | the slot carries no cycle identity, or an identity other than this cycle's | treat as absent **and report its presence** — a foreign curve is worse than no curve | - -**Stale needs an identity to detect, and slots do not carry one.** Pass slots are numbered, not -cycle-keyed, so a pass-2 file from a previous cycle is indistinguishable from this cycle's by -content alone. Two mitigations, both prompt-only: this design's **slot discriminator convention** -(a cycle uses a per-cycle infix, as this cycle's `rle` does, following the `fic2` and `pr15` -precedent), and **§5's existing delete-and-confirm-before-each-call rule**, which already -guarantees the file you are about to write is not a previous cycle's. Neither makes stale -detectable *after the fact*; the report says so rather than implying detection. +| **malformed** | fails the pass-acceptance checks: terminator, count match, no non-finding lines | treat as absent for that pass, **never** as zero findings. Distinct from *unreadable*: a malformed file is a review that ran and wrote badly, so **if its `sessionId` is still to hand** the pass may be re-runnable — and where it is not, say so rather than implying a recovery route that no longer exists | +| **unreadable** | exists but cannot be opened or decoded | treat as absent; report the OS-level cause, since permissions and a full disk need different fixes | +| **stale** | the slot's cycle discriminator is absent or is another cycle's | treat as absent **and report its presence** — a foreign curve is worse than no curve | + +**Stale is only partly detectable, and the text says which part.** Bare numbered slots carry no +cycle identity, so a pass-2 file from a previous cycle is indistinguishable by content. Two +prompt-only mitigations: the **slot discriminator convention** — a cycle uses a per-cycle infix, +as this cycle's `rle` does, following the `fic2` and `pr15` precedent, which makes *its own* slots +identifiable — and **§5's existing delete-and-confirm-before-each-call rule**, which guarantees the +file you are about to write is not a previous cycle's. Neither makes a bare-slot file's origin +recoverable after the fact, and the report says so rather than implying detection. (The +discriminator convention also prevents the destruction this cycle caused once: deleting a bare +slot destroyed a previous cycle's findings record, which the dispositions companion happened to +survive.) **The disclosure is not optional and names the cause.** The report says which tells could not be -computed, **which shape applies**, and which pass numbers are affected — so the human sees a -degraded reading rather than a clean one. +computed, **which shape applies**, and which pass numbers are affected. **Why the alternatives are rejected**, per story criterion 5: making the resume note mandatory -changes an artifact §5 explicitly calls advisory ("Nothing depends on it existing"); treating -unavailable history as its own stop would halt every cycle resumed on a fresh checkout; -restarting the pass-4 clock at the first *visible* pass silently lowers coverage. +changes an artifact §5 explicitly calls advisory; treating unavailable history as its own stop +would halt every cycle resumed on a fresh checkout; restarting the pass-4 clock at the first +*visible* pass silently lowers coverage. **Stated risk.** Reporting rather than stopping fails *toward continuing* the loop, the direction -AGENTS.md invariant 2 questions for the hook. The mandatory disclosure is what makes it -acceptable. +invariant 2 questions. The mandatory disclosure is what makes it acceptable. ### 5.1 The curve in the commit body — all three loops -**Each of the three loops records its own per-pass finding and Blocker counts in its own commit -body:** the **Gate-A spec loop** in the spec's commit, the **Gate-A plan loop** in the plan's -commit, the **Gate-B cycle** in the closing amend. Form pinned to the `3cdd075` precedent: +**Each loop records its own per-pass finding and Blocker counts in its own commit body**, each +line **labelled with the loop it describes** so a squash body carrying several stays unambiguous: ``` -Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0. +Gate-A spec loop: Findings 27, 30, 54. Blockers 5, 2, 0. +Gate-A plan loop: Findings 8, 3. Blockers 1, 0. +Gate B: Findings 14, 24, 12. Blockers 3, 4, 0. ``` **Gate B alone would leave the dominant cost unmeasured.** The loops this story cites as evidence are Gate-A loops — nineteen measured Gate-A passes on one spec. P8 without Gate-A curves cannot -measure the thing the problem statement is about. (An earlier revision narrowed this to Gate B on -a pass-1 finding, and pass 2 found the narrowing wrong; the scope is settled here rather than -oscillating.) +measure the thing the problem statement is about. (Pass 1 narrowed this to Gate B and pass 2 found +the narrowing wrong; settled here rather than oscillating.) **The form is pinned, because an unpinned one is unparseable.** One entry per **valid** pass, in pass order, comma-separated. -- A `reviewType: full` Gate-B pass contributes **one entry, the sum of its two branch files** — - the pass is the unit the floor counts. +- A `reviewType: full` Gate-B pass contributes **one entry, the sum of its two branch files**. - **Separate `spec` and `quality` calls, and a single-branch recovery resume, are branches of one - logical pass** and likewise contribute one summed entry. The hook counts calls; the curve counts - passes, and the two are deliberately not the same number. Where they differ, the body says so: + logical pass** and likewise contribute one summed entry. +- **The curve counts logical passes; the hook counts calls.** These are deliberately different + numbers and §5 already says the counter is not evidence. Where they differ the body says so: `(N calls, M passes)`. -- **Incomplete passes are excluded** — they are not reviews, and §5 already says they do not count. +- **Both branches of one logical pass must review the same artifact revision.** If the artifact + changes between them they are not one pass, and the second branch starts a new one. +- **Incomplete passes are excluded** — §5 already says they do not count. - **A valid pass with zero findings is written as `0`**, never omitted, so position equals pass number. -**What it reaches.** The durable half **across cycles** — surviving a fresh checkout, a cleared -`.context/`, a different machine, which is what P8 and any future resumption need. **It does not -restore history within a running cycle**: the commit does not exist until the loop closes, so it -is no help at pass 4 of the loop still running. There §5's degraded-sensitivity answer governs. A -cycle wanting mid-cycle durability may fold the running curve into the `WIP:` body by amend — -**permitted, not required.** +**What it reaches, and what it is worth.** The durable half **across cycles** — surviving a fresh +checkout, a cleared `.context/`, a different machine. **It does not restore history within a +running loop**: the commit does not exist until the loop closes, so it is no help at pass 4 of the +loop still running; there §5's degraded-sensitivity answer governs. And it is **author-written and +unchecked** — nothing compares the numbers against the validated pass files, so P8 reads a +self-reported curve. That is the same standing as every other commit-body record here, it is +better than the nothing that exists today, and the shipped text says it plainly rather than +letting P8 treat it as measurement. **Squash carry.** §5's squash-merge rule names only evidence entries and human-exception records. -The **decline records (§2.1), the floor provenance line (§4.1) and these curves** are added to -that list, in both copies. A record that does not survive the squash is unreachable from `main`'s -history, which is why that rule exists. +The **decline records (§2.1), the floor provenance line (§4.1) and these labelled curves** are +added, in both copies. --- ## 6. Old-conditions accounting -Required by the AGENTS.md Don't and by story criterion 6. **It covers every passage this change -rewrites, and the accounting is performed here** — deferring the thing that constitutes -compliance while claiming compliance is the failure the Don't describes. +Required by the AGENTS.md Don't and by story criterion 6, **and performed here** — deferring the +thing that constitutes compliance while claiming compliance is the failure the Don't describes. ### 6.1 The floor-wording inventory is generated, not hand-derived -I hand-derived this table three times and got a different count each time. The inventory is now -**the output of a stated command**, so the count cannot drift from the enumeration. Run in the -repo root, against both copies: +Hand-derived three times, three different counts. It is now **the output of a stated command**: ``` grep -nE "min 3 passes|below 3|3-pass|3 passes|where the 3 come from|3-passes-per-gate" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -It returns **six sites per copy, symmetric** — `:72/:272`, `:79/:279`, `:236/:421`, `:300/:485`, -`:335/:519`, `:403/:582` — **twelve sites, all changing.** Each states a rule that a variable -floor falsifies: the floor itself; the zero-finding early exit "below 3"; the incomplete-pass -rule; Gate A's "each its own 3-pass loop"; "where the 3 come from"; and the lens-set sentence. +**Six sites per copy, symmetric** — `:72/:272`, `:79/:279`, `:236/:421`, `:300/:485`, +`:335/:519`, `:403/:582` — **twelve sites, all changing.** **Two limitations, stated because a generated list reads as complete:** -1. **It finds digit sites only.** The site that matters most spells no digit — `:126/:322`, - "*a Blocker/Major-free **pass 1** carrying a Minor keeps looping*", correct under floor 3 and - **false under floor 1**, where pass 1 is *at* the floor and therefore closes. Two more sites, - both changing, found by hand and listed alongside. **Fourteen sites in total, all changing.** -2. **It is a floor, not coverage.** Another digit-free formulation would escape it exactly as - this one did. The hand-check is not optional. +1. **Digit sites only.** The site that matters most spells no digit — `:126/:322`, "*a + Blocker/Major-free **pass 1** carrying a Minor keeps looping*", correct under floor 3 and + **false under floor 1**, where pass 1 is *at* the floor and closes. Two more sites, found by + hand. **Fourteen in total, all changing.** +2. **A floor, not coverage.** Another digit-free formulation would escape it exactly as that one + did. The hand-check is not optional. It correctly does **not** match `:249/:434` — "PR #23's Gate-B pass 3 returned all four findings -at `IMPORTANT`" — which cites an actual historical pass rather than stating a rule, and stays. - -### 6.2 Condition inventory for every rewritten passage - -For each passage: what its existing prose requires, and the disposition of each requirement. -The plan carries the replacement wording; the accounting is here. - -| Passage (bold lead-in, present once in both copies) | What it currently requires | Disposition | -|---|---|---| -| "**Both gates are a LOOP with a HARD FLOOR**" | min 3 per run; Blocker/Major only; counted by the hook; a TodoWrite per pass; final pass clean; the only early exit below floor is a zero-finding pass | floor value **moved** to the profile predicate; every other requirement **kept** verbatim | -| "What a loop absorbs, and what stops it" | in-set findings absorbed and acted on by severity; ancestry never decides action; assigned fix set fixed before the pass; unclear membership resolves outside; out-of-set correction stops the loop; a new structural/contract question stops it, novelty not size; stopping is not an exit from the gate | all **kept**; the scope stop is **moved** into §2's ordering as one of three suspensions, and gains the decline qualification | -| "Recognizing \"clearly stuck\"" | read the Blocker curve across passes; neither curve measures coverage; three conditions together; clean completion takes precedence; below the floor nothing closes; zero-finding pass the only exception | all **kept**; the precedence sentence is **moved** into §2's table and preserved verbatim there; the "pass 1" clause is **changed** to "below the floor" (§6.1 limitation 1) | -| "Surfacing does not close the cycle" | surface with the finding still open; resolve rule not waived; no pass credited clean; loop resumes on the user's decision | all **kept**; this is §1.2's mechanism sentence, and the decline is the single named exception to the third | -| "From pass 4 onward every pass report carries three lines" | carrier is the status report, never the Codex reply or findings file; three lines; five tells; any two mandatory | all **kept**; §5 **adds** the unavailable-history behaviour, which the passage currently leaves undefined | -| "The two rules above do not compete" | absorb decides finding scope, stuck-reading decides loop convergence; neither overrides the other | **kept**, and **moved** to reference §2's ordering rather than restating the relationship | -| "The Gate-B triviality skip needs two independent conditions" | behaviourally trivial **and** `max(risk, security)` is 0; an eligible profile never makes a behaviour-changing diff skippable; skip reason in the commit body; a skip removes the review never the evidence | all **kept**; checked for consistency with the floor predicate, which uses the same level-0 test for a different purpose — **deliberately not merged**, since one relaxes review count and the other removes review entirely | -| "A cycle citing several stories" | battery once; each profiled story satisfies its own mode; lens sets unioned; skip-eligible only if every cited story is | all **kept**; the floor **added** as a new dimension under the same unanimity shape | -| "**Severity:** Blocker … Major … Minor · Nit" | Blocker wrong/unsafe/breaks invariant; Major design flaw → rework; both must resolve; Minor and Nit collect, never iterate | all **kept**; the reachability test **added** as the classifier, with the subject list as worked examples | -| "Scope, and it is narrow" | the human-exception form supplies no permission; never the answer to a below-floor pass, unclean final pass or STOP; authorizes nothing any mandatory rule requires; not for things never owed | all **kept without exception**; §2.1 **adds** the decline as a distinct record type on the same transport and states the distinction, precisely so this passage is not weakened | -| "Recording a human exception" | the three-line form; which commit carries it; decisions after a commit closed; an empty commit is legitimate; the record is an unverified assertion | all **kept**; the decline record **reuses the transport** and adopts the same unverified-assertion honesty | -| "On squash-merge, copy every evidence entry…" | every evidence entry and human-exception record in the range copied into the squash body; nothing performs or checks the carry | **kept**; three record types **added** to the list | +at `IMPORTANT`" — which cites a historical pass rather than stating a rule, and stays. + +**Every site maps to a §6.2 row**, so no site is edited without an accounting: `:72` → row 1, +`:79` → row 2, `:126` → row 5, `:236` → row 3, `:300` → row 12, `:335` → row 13, `:403` → row 9. + +### 6.2 Condition inventory + +What each passage's existing prose requires, and the disposition of each requirement. Listed are +the conditions this change **touches or could drop**; the plan carries the replacement wording. + +| # | Passage | What it currently requires | Disposition | +|---|---|---|---| +| 1 | "**Both gates are a LOOP with a HARD FLOOR**" | min 3 per run; Blocker/Major only; counted by the hook; hook cannot read findings nor tell the spec run from the plan run; a satisfied count is not a clean review; a TodoWrite per pass; fix Blocker/Major after each; final pass clean; keep going until clean or clearly stuck; only early exit below floor is a zero-finding pass; don't manufacture findings; Codex advisory, validate before applying; dismissed finding gets a one-line why | floor **value moved** to the profile predicate; **every other requirement kept verbatim**, including the counter-is-not-evidence caveats, which this design leans on harder than before | +| 2 | The early-exit sentence at `:79/:279` | the only exit below the floor is a zero-finding pass | **kept**, with "below 3" **changed** to "below the floor" | +| 3 | The incomplete-pass rule at `:236/:421` | don't act on a partial list; don't count it toward the floor; don't read "no Blocker/Major visible" as clean | **kept**, "3-pass floor" **changed** to "the floor" | +| 4 | "What a loop absorbs, and what stops it" | in-set findings absorbed, acted on by severity; ancestry decides *where*, never *what you do*; ancestry grants no Minor a repair round; assigned fix set fixed **before** the pass; unclear membership resolves **outside**; an out-of-set correction stops the loop even opening no new question; a new structural/contract question stops it; **novelty not size**; **when a finding is both, novelty wins**; stopping is **not an exit** — floor, filter and clean-final-pass all stand | all **kept**; the scope stop is **moved** into §2's ordering as one of three suspensions and gains the decline qualification | +| 5 | "Recognizing \"clearly stuck\"" | read the Blocker curve **across passes**, not one total; one low count is a snapshot not a plateau; **neither curve measures coverage**; three conditions **together**, a missing one means keep going; six-plus passes is where the field saw one and is **not a threshold**; an affirmative coverage judgement must be **stated**; a known unreviewed area **forbids the exit**; Blocker/Major **regenerating** across genuine repairs; **clean completion takes precedence**; below the floor nothing closes; zero-finding pass the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | all **kept**; the precedence sentence **moved** into §2's table and preserved verbatim there; the "pass 1" clause **changed** to "below the floor" per §6.1 | +| 6 | "Surfacing does not close the cycle" | surface **with the finding still open**; the resolve rule is **not waived**; **no pass credited clean**; loop resumes on the user's decision | all **kept**. The decline is an exception to **the third condition only where the user has declined that finding** — it does not waive the resolve rule generally, and an accepted Blocker/Major still blocks clean until resolved (§2.1) | +| 7 | "From pass 4 onward every pass report carries three lines" | duty **activates at pass 4** and binds every pass after; carrier is **your status report**, never the Codex reply, never the findings file; the three line contents; all five tell definitions; **any two mandatory, not discretionary**; report the tells and hand the decision to the user; the stuck reading is **not a precondition** | all **kept**; §5 **adds** the unavailable-history behaviour, which the passage currently leaves undefined | +| 8 | "The two rules above do not compete" | absorb decides *a finding's* scope; the stuck reading decides whether *the loop* converges; neither overrides the other; a small in-set correction is not itself evidence of a plateau | **kept**, **moved** to reference §2's ordering rather than restate the relationship | +| 9 | "Lenses are different questions, not more passes" (`:403/:582`) | lens sets add questions, not passes; the 3-pass floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are **unchanged** by lenses | **kept**; "3-pass floor" **changed** to "the floor", and the unchanged-by-lenses claim now reads against a derived floor | +| 10 | "The Gate-B triviality skip needs two independent conditions" | behaviourally trivial **and** `max(risk, security)` is 0; an eligible profile never makes a behaviour-changing diff skippable; skip reason in the commit body, not the profile log; a skip removes the review **never the evidence**; profiled story runs the battery and lands its evidence entry; unprofiled records reason and battery result and nothing more | all **kept**; checked against the floor predicate, which uses the same level-0 test for a different purpose — **deliberately not merged**, since one relaxes review count and the other removes review entirely | +| 11 | "A cycle citing several stories" | battery once per cycle; each cited profiled story satisfies its own mode with its own named evidence entry; an unprofiled cited story owes no entry; lens sets **unioned**; skip-eligible only if **every** cited story is | all **kept**; the floor **added** as a new dimension under the same unanimity shape | +| 12 | "Gate A — Spec, then plan (TWO runs, each its own 3-pass loop)" (`:300/:485`) | two separate runs, each its own loop; run on the spec before `writing-plans` and the plan before executing; one broad prompt re-run each pass; the brainstorming directive opens it; coverage floor not a cage; every finding with severity and confidence; `NO FINDINGS` when clean; mechanical settle before each read pass | all **kept**; "3-pass loop" **changed** to reference the derived floor, and each of the two runs derives it independently | +| 13 | "Gate B — Code" (`:335/:519`) | tests green before commit; skip only trivial changes; check against AGENTS.md; re-review after every fix; a fix changes the diff and the hook invalidates the prior pass, **which is where the 3 come from**; the standing falsification lens; same coverage rule as Gate A | all **kept**; the "where the 3 come from" clause **changed** to derive from the floor — the *reasoning* (re-review after every fix) is what generates the number, and it survives a variable floor unchanged | +| 14 | "**Severity:** Blocker … Major … Minor · Nit" | Blocker = wrong/unsafe/breaks invariant; Major = design flaw → rework; both must resolve; Minor and Nit collect, never iterate | **kept**, and **narrowed**: the reachability test is added as the classifier, so a finding that would once have been Major on subject alone can now be Minor. That narrowing is deliberate and is the change; it is recorded here as a **changed** condition, not a kept one | +| 15 | "Scope, and it is narrow" | the form supplies no permission; never the answer to a below-floor pass, an unclean final pass, a STOP, a Gate-A/B obligation or a profile-derived evidence requirement; authorizes nothing any mandatory rule requires; mandatory is not limited to this file; not for things never owed | all **kept without exception**; §2.1 **adds** the decline as a distinct record type on the same transport and states the distinction, precisely so this passage is not weakened | +| 16 | "Recording a human exception" | the three-line form; which commit carries it; decisions made after a commit closed; an empty commit is a legitimate destination; several records accumulate; the record is an **unverified assertion** | all **kept**; the decline record **reuses the transport** and adopts the same unverified-assertion honesty | +| 17 | "On squash-merge, copy every evidence entry…" | every evidence entry and human-exception record in the range copied into the squash body; nothing performs or checks the carry; a disagreement between copies is a copying error to fix, not to choose between | **kept**; three record types **added** | --- ## 7. Prerequisite, rollout, and what this change falsifies **The template lacks the sentence §3's kinship points at.** "a wrong sentence costs a confused -reader, not broken behaviour" is in `CLAUDE.md` and **absent from the template**. **Resolution: -the template gets it**, so the kinship claim has a referent in both copies. A deliberate reduction -of the 192-line divergence at exactly one seam, because the new rule depends on it — not a general -reconciliation, which stays out of scope. +reader, not broken behaviour" is in `CLAUDE.md` and **absent from the template**. **The template +gets it**, so the kinship claim has a referent in both copies — a deliberate one-seam reduction of +the 192-line divergence, because the new rule depends on it. -**User-facing statements this change falsifies, and which are therefore in the fix set.** The -standing Gate-B lens — "which existing statements does this diff falsify?" — applied to shipped -documentation, where a change makes sentences wrong in files it never touches: +**User-facing statements this change falsifies**, from the standing lens "which existing +statements does this diff falsify?" — a change makes sentences wrong in files it never touches: | Site | What it says | Why it is false after | |---|---|---| -| `README.md:130` | "`codex-gate.floor` — a positive integer; moves the 3-passes-per-gate floor" | the floor is no longer fixed at 3; the knob now also interacts with a derived value | +| `README.md:130` | "`codex-gate.floor` — a positive integer; moves the 3-passes-per-gate floor" | the §5 floor is derived and is not 3; the knob moves the **hook's reminder threshold**, which is all it ever moved | | `docs/getting-started.md:34` | "three passes minimum, final pass clean" | not at level 0 | | `:40` | "the same 3-pass" loop for the plan | not at level 0 | | `:53` | Gate B "three passes, final clean" | not at level 0 | -| `:84` | "Gate A's floor is unchanged at every level" | **directly contradicted** — this sentence is specifically about profile levels | +| `:84` | "Gate A's floor is unchanged at every level" | **directly contradicted** — a sentence specifically about profile levels | | `:86` | "moves the 3-pass floor" | same as README | - -These are `docs/**.md` and `README.md`, so prose and Gate-B N/A, but they are **in the fix set** -and criterion-visible. `:84` is the one worth naming twice: a sentence that was true when written -and that this change makes false, in a file the change does not otherwise touch. - -**What the template edit does and does not reach.** Editing the inline template changes what -`/workflow-init` **writes into new or re-initialized projects**. It does **not** update the -`CLAUDE.md` already sitting in a downstream project — those are ordinary project files that -accumulate local content, and invariant 9 forbids silent overwriting. Downstream repos adopt -these rules by re-running `/workflow-init` and taking the diff it offers, not by upgrading the -plugin. Stated rather than left to an assumption that a plugin bump propagates rules. +| `docs/coding-workflow.md:79-80` | the axes "never subtract any: **Gate A's floor** and the baseline questions are the same at every level" | **directly contradicted**, and missed until Gate-A pass 3 — the same claim as `:84` in a second file | + +**The rewording is honest rather than cosmetic.** README and getting-started describe the knob as +what it mechanically is — the hook's reminder threshold — and name the **sanctioned lever** for +wanting fewer obliged passes: the profile, or `codex-gate.off` for the reminders. Nothing is +deprecated; the file and its behaviour are preserved. What is removed is a promise the mechanism +never kept. + +**Gate-B classification of the doc edits — corrected.** An earlier revision called these paths +"Gate-B N/A". That is wrong when they ride with the prompt changes: §5's exemption requires +**every** staged path to be explanatory documentation, and a **mixed commit forfeits it**. So +either the doc edits land in their own docs-only commit (N/A applies) or they ride with the +prompt change (full Gate B applies to the whole commit). **The plan chooses and states which**; +what is not available is calling them exempt inside a mixed commit. + +**What the template edit does and does not reach.** It changes what `/workflow-init` **writes into +new or re-initialized projects**. It does **not** update the `CLAUDE.md` already in a downstream +project — those accumulate local content and invariant 9 forbids silent overwriting. Downstream +repos adopt by re-running `/workflow-init` and taking the diff it offers. **Packaging.** The change edits `plugins/dev-workflow/commands/workflow-init.md`, so invariant 12 -applies: the implementation surface includes a `plugins/dev-workflow/.claude-plugin/plugin.json` -**version bump** and a `CHANGELOG.md` entry. CI enforces the bump on pull requests -(`scripts/check-version-bump.sh`). +applies: a `plugin.json` **version bump** and a `CHANGELOG.md` entry are in the implementation +surface. CI enforces the bump on pull requests. --- @@ -491,29 +476,34 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). - **Battery** — the full `AGENTS.md` § Commands chain, green. - **A check that fails without the change.** No automated test is possible for prose, so this takes §5's other permitted route — a **named verification**, owing the same counterfactual. The - subject is §6.1's inventory, which is differential by construction: **posed as a question about - behaviour under floor 1, the pre-change text answers wrongly at identified sites** — `:79` - states the early exit as "below 3" where the floor may be 1, and `:126` says a Blocker/Major-free - pass 1 carrying a Minor keeps looping where floor 1 requires it to close — **while the - post-change text answers correctly.** That is the observation that would exist if the claim were - false, and it is observable: a reading of identified sentences at two revisions, not a - derivation from the change itself. + subject is §6.1's inventory, differential by construction: **posed as a question about behaviour + under floor 1, the pre-change text answers wrongly at identified sites** — `:79` states the early + exit as "below 3" where the floor may be 1, and `:126` says a Blocker/Major-free pass 1 carrying + a Minor keeps looping where floor 1 requires it to close — **while the post-change text answers + correctly.** That is the observation that would exist if the claim were false. **The wiring must be able to produce the failure.** A verification consulting only the post-change text cannot fail and would report success because of how it was wired. Both revisions get read, and the entry records which sentences were read at which revision. -- **A named verification of the risk path.** The risk path is the **agent-written** gate-off - lever. It exercises §4.1's lifecycle without requiring a floor-1 cycle on this branch — which - story criterion 9 forbids, and which no cycle citing this risk-`high` story could produce - anyway. The observation that would exist if the claim were false is **an agent-written floor - file or marker surviving a cycle close**; the verification checks for both after this branch's - own closes, where the derived floor is 3 and the lifecycle still runs. +- **A named verification of the risk path.** The risk path is now narrow and specific: **the §5 + floor is derived from a cited-story set, and nothing mechanical checks the derivation.** The + verification takes this branch's own closing commits and confirms that **each provenance line's + stated floor equals the floor the cited stories' profiles actually derive**, recomputed from + those headers — the observation that would exist if the claim were false being a provenance line + whose number the profiles do not license. It also confirms that **no floor file was written**, + which under this design must hold on every path, including where none existed before. + *Two failure modes this deliberately avoids:* an absence check alone would pass vacuously if the + design never wrote a file (which it now never does), and a floor-1 demonstration is impossible on + this branch, whose cited story is risk `high` — story criterion 9 forbids manufacturing a level-0 + fixture for it. +- **A verification that a user's knob survives untouched.** An existing `codex-gate.floor` set + before a cycle starts is byte-identical after it closes, and the cycle's provenance line + discloses both values. This exercises the settled user-knob rule, which no other check reaches. - **Parity verification across every changed rule**, per story criterion 7, covering **§6.1's - fourteen sites, §6.2's twelve passages, and every rule §§2–5 newly insert** — lifecycle, - exposure, history shapes, curve format, provenance, activation. The two copies already differ on - 192 lines, so parity cannot be asserted from a whole-section diff; the verification walks each - named item and records every difference as deliberate-and-stated or as a defect. The narrow - severity-spelling check in `scripts/check-invariants.sh` covers one item and is not coverage; - nothing mechanical checks the rest, which is invariant 11's stated condition. + fourteen sites, §6.2's seventeen rows, and every rule §§2–5 newly insert.** The copies already + differ on 192 lines, so parity cannot be asserted from a whole-section diff; the verification + walks each named item and records every difference as deliberate-and-stated or as a defect. + `scripts/check-invariants.sh` covers one severity spelling and is not coverage — invariant 11's + stated condition. **Instrument discipline, from this story's own evidence.** Two defects in the `fic2` decision matrix were properties of the technique: **a state's inputs must include every input the rule @@ -527,46 +517,43 @@ worth is carried by the evidence doc as a question, not a commitment. ## 9. Out of scope -Hook code (any change under `plugins/dev-workflow/hooks/`); gate-call observability (upstream, -`mcp-codex-dev`); the pass-counter anomaly, undiagnosed and needing hook-state inspection; the -CodeRabbit plan-metadata contradiction; the fixture-per-predicate question; any remedy to the -supersession convention; deprecating the user-facing floor knob (a documented capability, whose -removal is Daniel's decision and unnecessary given §4.1); a hook-read marker or separate -agent-floor file (needs hook code); and general reconciliation of the two copies' 192-line -divergence beyond the one seam §7 names. +Hook code (anything under `plugins/dev-workflow/hooks/`); gate-call observability (upstream, +`mcp-codex-dev`); the pass-counter anomaly, undiagnosed; the CodeRabbit plan-metadata +contradiction; the fixture-per-predicate question; any remedy to the supersession convention; +deprecating or repurposing the user-facing floor knob; teaching the hook about profiles; and +general reconciliation of the two copies' 192-line divergence beyond the one seam §7 names. --- ## 10. Risks and activation -- **When these rules bind.** They take effect from the commit that ships them, and **a loop - already in flight finishes under the rules it started with** — re-deriving a floor mid-loop - from a rule that did not exist when passes were banked would invalidate a count nobody could - reconstruct. **The start marker is the loop's own first pass artifact**: a loop whose pass-1 - slot predates the shipping commit started under the old rules. That is recoverable from git - plus file mtime, and where neither is available the shipped text says to **treat the loop as - new and re-derive**, which costs passes and never skips them. This also disposes of this - design's own case: its Gate A runs under the old rules. -- **Abandonment and rollback.** Cleanup at close covers the successful path only. A loop - abandoned, or halted by a blocking profile or setup stop, **leaves its marker file behind by - design** — §4.1 treats a marker-bearing file at cycle start as stale agent state and - re-derives, so the next cycle self-heals rather than inheriting. **Rollback to prompt text that - predates this change** leaves a marker no rule mentions; it is inert (no hook reads it) and the - floor file it accompanies reverts to being read exactly as the old rules read it. -- **The gate-off lever is narrower than before but not closed, and it has more than one shape.** - §4.1's lifecycle removes the accidental-persistence path. What remains, and is disclosed: - writing a derived floor the cited set does not license; **omitting a higher-risk cited story - from the set**; **minting or editing a story profile to level 0**; and presenting an incomplete - cited-story set. The first is bounded by §4.1's read-back; the rest are bounded only by the - provenance line naming *which* stories were cited, which makes an omission visible to a reader - who checks the branch's actual work. None of this is a guard. -- **A user-set floor of 1 is not the gate-off lever, and the text keeps them distinct.** It is a - human decision on a documented knob, disclosed in the provenance line. The lever is an - *agent-written* floor without a licensing profile. Conflating them would either make a shipped - user capability read as an attack or make the real lever read as sanctioned. -- **The reachability test needs judgement** where §5 is trying to remove it. The - named-reader-and-changed-decision phrasing is what makes it decidable. +- **When these rules bind.** From the commit that ships them, and **a loop already in flight + finishes under the rules it started with** — re-deriving a floor mid-loop from a rule that did + not exist when passes were banked would invalidate a count nobody could reconstruct. **Where a + loop's starting rules cannot be established, treat it as new and re-derive**, which costs passes + and never skips them. An earlier revision proposed git timestamps plus file mtime as a start + marker; that does not survive checkout, copy or clock skew, so no marker is claimed and the + fallback carries the case. This also disposes of this design's own Gate A, which runs under the + old rules. +- **Downstream adoption has no shipping commit.** The template travels into other repositories + whose history does not contain this change, so "from the commit that ships them" is a statement + about *this* repo. Downstream, the rules bind from the `/workflow-init` run that writes them, and + the same treat-as-new fallback applies. +- **The gate-off surface, honestly enumerated.** Deleting the marker mechanism removed the + camouflage path and every crash-recovery path. What remains: **writing a provenance line the + cited set does not license; omitting a higher-risk cited story from the set; minting or editing a + story profile to level 0; presenting an incomplete cited-story set.** All four are *statement* + attacks now rather than *file* attacks — they live in the commit body, where a reader who checks + the branch's actual work can see them, and where §8's risk-path verification recomputes the + derivation. None of this is a guard, and the profile-minting path is bounded only by the + human-confirmation rule §5 already imposes on profile changes. +- **A user-set floor is not the gate-off lever, and the text keeps them distinct.** It moves what + the hook says. The lever is a *stated* floor the cited profiles do not license. +- **The reachability test needs judgement** where §5 is trying to remove it; the + named-reader-and-changed-decision phrasing removes arbitrariness, not judgement, and §3 says so. - **Q6's answer fails toward continuing the loop.** Stated in §5 with the disclosure that makes it acceptable. +- **The curve is self-reported.** §5.1 says so; P8 inherits that limit and must not present a + self-reported curve as measurement. - **The expected demotion is a prediction.** §3 says so; P8 measures it. If it demotes far less than hoped, the rule is still correct and the economics claim was what was wrong. From 8b6f667afdf9ddddb9db623808f5ccd8fbfae43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:51:05 +0200 Subject: [PATCH 014/117] docs(story): propagate the deleted floor-file mechanism into three criteria MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate-A pass 4 returned four Blockers; two were the story still describing the mechanism revision 4 deleted, which is the third occurrence this cycle of fixing the spec and leaving the story behind. Criterion 3 recorded provenance as `floor N per workspace knob; profile derivation M`, which reverses the settled meanings: the profile derivation IS the floor §5 obliges, and the workspace knob is the hook's reminder threshold. Now labelled as what each is. Criterion 4 required both shipped copies to state that the floor is agent-written into per-clone gitignored state and that writing 1 is the cheapest gate-off lever -- describing the exact mechanism revision 4 removed. Implementing the criterion would have recreated the rejected design in order to satisfy a criterion about it. The residual is now stated as what it actually is: a statement an agent can get wrong or misreport -- a floor the cited set does not license, an omitted higher-risk story, a minted level-0 profile, an incomplete cited set. §4's Hook-1 rationale still read "an agent-written floor adds no new enforcement class", which is now vacuous, and its Hook-2 entry described lowering a floor as moving against the firing direction. Both rewritten: the hook's floor was only ever a reminder threshold, §5's text is what obliges an agent, and invariant 2 is what licenses the accepted cost -- a level-0 cycle draws a reminder it does not owe, and a redundant warning is the price that sentence names. Sparring session, under Daniel's 2026-08-28 delegation. Profile unchanged, so no profile-log line. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- ...-review-loop-economics-pass-floor-story.md | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 242a2dc..35f8ead 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -145,20 +145,29 @@ deliverable rather than a convenience. survives a fresh checkout, a cleared `.context/` and a different machine. - [ ] **Every cycle's floor leaves a trace in history, default or not.** Both copies require a cycle to record `floor N per ` in its closing commit body — one entry - per cited story — and, where a workspace knob set by the user diverges from the profile - derivation, to record both: `floor N per workspace knob; profile derivation M per `. Checkable: the requirement is stated in both copies, and this story's own closing - commits carry it. + per cited story, N being the **profile-derived** floor, which is the floor §5 obliges — + and, where a user's workspace knob is set, to record it **separately and labelled as what + it is**: `floor N per ; hook reminder threshold M per workspace knob`. + Checkable: the requirement is stated in both copies, and this story's own closing commits + carry it. **Every cycle, not only a non-default one** (revised 2026-08-28 after Gate-A pass 2). An earlier wording required the line only for a non-default floor, which contradicted criterion 9 — this story is risk `high` and runs at the default 3, yet must demonstrate the provenance path. It also made an absent line ambiguous between "default floor" and "someone forgot", and left the user-knob divergence with nowhere to be disclosed. - [ ] **The gate-off residual is named in the shipped text, not merely avoided.** Both copies - state that the floor value is agent-written, lives in per-clone gitignored state that no - reviewer sees in a diff, and that nothing verifies the written value against the story - profile — and that a floor of 1 is therefore the cheapest available gate-off lever. - Checkable by reading. This is a disclosure, not a guard: no mechanism is claimed for it. + state that the §5-obliged floor is **derived and stated by the agent**, that nothing + mechanical checks the derivation against the cited profiles, and that the residual is + therefore a **statement** an agent can get wrong or misreport — by stating a floor the + cited set does not license, by omitting a higher-risk cited story, by minting or editing a + profile to level 0, or by presenting an incomplete cited set. Checkable by reading. This + is a disclosure, not a guard: no mechanism is claimed for it. + **The residual is a statement, not a file** (revised 2026-08-28 after Gate-A passes 3-4). + An earlier wording required both copies to say the floor is *agent-written into per-clone + gitignored state* and that *writing 1* is the cheapest lever — describing a mechanism the + design has since deleted, because its protection made an agent-written floor + indistinguishable from a user's. Implementing that wording would have recreated the + rejected mechanism in order to satisfy a criterion about it. - [ ] **Severity is pinned as a closed decision in both copies, keyed on consequence, with the carve-out stated at the same place.** Both state that Blocker and Major claim product behaviour, an invariant, or a contract, and both state the deciding test: **name the @@ -227,11 +236,14 @@ deliverable rather than a convenience. - `## Hook` — "**The hook always exits 0.** It is advisory; a reminder that can fail closed would make the workflow unusable whenever Codex is down or the environment is odd." The - prompt-only decision for part 1 rests on this: today's floor of 3 is already advisory, so an - agent-written floor adds no new enforcement class. + prompt-only decision for part 1 rests on this: the hook's floor was only ever a reminder + threshold, and §5's text is what obliges an agent — so deriving the floor in the text adds no + new enforcement class and removes none. - `## Hook` — "**Loose in the firing direction.** On uncertainty, fire. A missed commit - (false ✓) is the dangerous direction; a redundant warning is the accepted price." Lowering a - floor moves against this direction, which is what the residual-risk criterion discloses. + (false ✓) is the dangerous direction; a redundant warning is the accepted price." This is what + licenses the accepted cost: a level-0 cycle draws a hook reminder its derived floor does not + owe, and a redundant warning is the price named here. Teaching the hook to fall silent would + be the false ✓ the same sentence calls dangerous. - `## Prompts and scaffolding` — "**`/workflow-init`'s templates stay inline** in the command body." The mirror edit lands in the command body, never in a file read from disk. - `## Prompts and scaffolding` — "**Prompt changes pass `docs/prompt-standards.md`** — all 12 From 0c1927f48b31a98a1c65393a8fad91af4985e503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:54:53 +0200 Subject: [PATCH 015/117] docs(story): criteria state observables, not mechanisms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Level-of-description change, contract unchanged. Approved by the sparring session under Daniel's 2026-08-28 delegation; joins the accumulated story-edit list for his final-version review. Five Blocker-severity occurrences across Gate-A passes 1-4 shared one cause: the spec was revised and a criterion still described the superseded mechanism. The worst was criterion 4, which required both shipped copies to describe the floor-file mechanism revision 4 had deleted -- implementing it would have recreated the rejected design in order to satisfy a criterion about it. A criterion that restates the design is a second copy of it, and this repo's ledger already records what a second copy does. Before/after, per criterion -- the old-conditions discipline applied to the story itself. No obligation is dropped; each moves up one level of description. Provenance criterion. BEFORE: both copies require the literal line `floor N per `, plus a second literal form when a workspace knob is set. AFTER: from a closing commit body alone, a reader can determine which floor §5 obliged, which stories derived it, and that a user knob's number is the hook's reminder threshold rather than the obligation; every cycle, so absence is never ambiguous; machine-extractable because P8 reads it, with both copies pinning one form and the spec stating which. KEPT: every-cycle scope, the three facts a reader must recover, machine-extractability. MOVED: the exact byte form, to the spec. DROPPED: nothing. Gate-off residual criterion. BEFORE: both copies say the floor is agent-written into per-clone gitignored state and that writing 1 is the cheapest lever. AFTER: a reader learns the floor is produced by the agent rather than by any mechanism, that nothing checks it against the cited profiles, and by what specific routes it can be wrong -- with the enumeration stated as a floor, not a complete list -- and comes away unable to believe the residual is mitigated. KEPT: disclosure in shipped text, no-guard claim, specificity about routes. MOVED: which mechanism carries the residual, to the spec. DROPPED: nothing -- the old wording's specifics were about a deleted mechanism, so they were not obligations, they were design. Severity criterion. BEFORE: the deciding test reproduced verbatim, plus the carve-out and coverage-first. AFTER: four checkable properties -- exactly one procedure decides severity (subject cases may appear only as worked examples); the test turns on something in the system taking a different decision, not on file kind and not on a human reader; the carve-out is symmetric; coverage-first survives. KEPT: all four obligations including the both-directions carve-out and coverage-first. MOVED: the test's exact phrasing, to the spec. DROPPED: nothing. Criterion 6's placement rule -- the qualification appearing at all three universal rules -- is contract, not mechanism, and survives verbatim. Also closed §5's first two open questions, which pass 4 found still listed as open after being answered: multi-story floor by unanimity, and the mid-cycle profile rule that needed no new rule because three existing ones compose. Struck through with their answers rather than deleted, matching the third. The lesson is recorded once in docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md as field evidence for whoever next touches dev-workflow:intake -- whose template already says criteria describe observables, never implementation steps. What the record adds is five measured occurrences and the observation that the drift surfaces as Blockers in a later gate rather than as a bad-looking criterion at intake time. Profile unchanged, so no profile-log line. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- .../2026-08-16-canvas-a1-a5-dispositions.md | 30 ++++++ ...-review-loop-economics-pass-floor-story.md | 97 ++++++++----------- 2 files changed, 70 insertions(+), 57 deletions(-) diff --git a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md index 7a4a1c1..11a3d41 100644 --- a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md +++ b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md @@ -310,3 +310,33 @@ established neither whether the hash was computable at any given pass nor whethe failed to persist, and it did not read the hook at the site that computes it. Its one new lead — that the second shape's STOP arrived at a `git reset --soft`, which reaches the reset path only via `is_commit` — ties it to the item-2 row and is a lead, not a finding. + +--- + +## Field note added 2026-08-28 — criteria that restate the design are a Blocker generator + +Observed across Gate-A passes 1–4 of the review-loop-economics cycle +(`docs/superpowers/specs/2026-08-28-review-loop-economics-design.md`): **five Blocker-severity +occurrences of one defect**, where the spec was revised and the story's acceptance criteria +still described the superseded mechanism — criterion 8 demanding a floor a risk-`high` story +cannot license; criterion 5 describing artifact-kind severity after a consequence-keyed test was +settled; desired outcome 2 keeping a false-green-only carve-out; criterion 3 reversing the +settled meanings of derived floor and hook knob; and criterion 4 requiring both shipped copies to +describe a mechanism the design had deleted — where implementing the criterion would have +recreated the rejected design in order to satisfy a criterion about it. + +The cause is structural rather than carelessness: those criteria embedded **mechanism detail**, +so each had to track a design still in motion. A criterion that restates the design is a second +copy of it, and this repo's ledger already records what a second copy does — "a restatement is a +second copy that can drift". + +Remedy applied in that cycle: the criteria were rewritten to state **what must be observably +true** rather than **how**, with the bound that a criterion which cannot be made observable +without naming mechanism is one where the mechanism *is* the contract, and there it stays named. + +**Captured, not acted on beyond that cycle.** This is field evidence for whoever next touches +`dev-workflow:intake`, whose story template says acceptance criteria "describe observable +outcomes or constraints, never implementation steps" — the rule exists; what this record adds is +five measured occurrences of the failure it is meant to prevent, and the observation that the +drift shows up as *Blockers in a later gate* rather than as a bad-looking criterion at intake +time. diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 35f8ead..c3dc7aa 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -143,54 +143,34 @@ deliverable rather than a convenience. pass total and no distribution — because the findings files behind those numbers live under gitignored `.context/`. It is also the durable half of Q6: a curve in a commit body survives a fresh checkout, a cleared `.context/` and a different machine. -- [ ] **Every cycle's floor leaves a trace in history, default or not.** Both copies require a - cycle to record `floor N per ` in its closing commit body — one entry - per cited story, N being the **profile-derived** floor, which is the floor §5 obliges — - and, where a user's workspace knob is set, to record it **separately and labelled as what - it is**: `floor N per ; hook reminder threshold M per workspace knob`. - Checkable: the requirement is stated in both copies, and this story's own closing commits - carry it. - **Every cycle, not only a non-default one** (revised 2026-08-28 after Gate-A pass 2). - An earlier wording required the line only for a non-default floor, which contradicted - criterion 9 — this story is risk `high` and runs at the default 3, yet must demonstrate - the provenance path. It also made an absent line ambiguous between "default floor" and - "someone forgot", and left the user-knob divergence with nowhere to be disclosed. -- [ ] **The gate-off residual is named in the shipped text, not merely avoided.** Both copies - state that the §5-obliged floor is **derived and stated by the agent**, that nothing - mechanical checks the derivation against the cited profiles, and that the residual is - therefore a **statement** an agent can get wrong or misreport — by stating a floor the - cited set does not license, by omitting a higher-risk cited story, by minting or editing a - profile to level 0, or by presenting an incomplete cited set. Checkable by reading. This - is a disclosure, not a guard: no mechanism is claimed for it. - **The residual is a statement, not a file** (revised 2026-08-28 after Gate-A passes 3-4). - An earlier wording required both copies to say the floor is *agent-written into per-clone - gitignored state* and that *writing 1* is the cheapest lever — describing a mechanism the - design has since deleted, because its protection made an agent-written floor - indistinguishable from a user's. Implementing that wording would have recreated the - rejected mechanism in order to satisfy a criterion about it. -- [ ] **Severity is pinned as a closed decision in both copies, keyed on consequence, with the - carve-out stated at the same place.** Both state that Blocker and Major claim product - behaviour, an invariant, or a contract, and both state the deciding test: **name the - in-system reader of this text — a gate, skill, rule, escalation procedure or scaffolded - template — and the decision it takes differently if the text is wrong; if you can name - neither, the finding is Minor, collect and never iterate.** A human reader never - satisfies the test. Findings about narration, prose describing a mechanism, and test - instrument internals appear as **worked examples of that test, not as a second rule - beside it** — a copy stating a categorical demotion by subject *and* the test fails this - criterion, because two procedures can disagree on one finding. - **The instrument carve-out runs in both directions:** an instrument finding keeps its - severity when it shows the instrument changes what the gate concludes about product - behaviour — a false green, and equally a false red or a check that blocks a valid change. - Naming only false green would demote a check that fails for wiring reasons and costs a - correct change. - Both copies also still state that the reviewer reports every finding with severity and - confidence and that the filter is applied downstream by us; a copy that drops - coverage-first fails this criterion even if the severity rule is correct. - *(Revised 2026-08-28 after Gate-A pass 2 found this criterion still describing the - pre-decision form — artifact-kind demotion with a false-green-only carve-out — which no - implementation could satisfy alongside the settled contract. Encodes the settled - consequence-keyed decision and the bidirectional carve-out; sparring session, under - Daniel's 2026-08-28 delegation.)* +- [ ] **Every cycle's floor is reconstructible from history alone, default or not.** From a + cycle's closing commit body, without consulting the spec or any per-clone state, a reader + can determine: **which floor §5 obliged**, **which stories derived it**, and — where a + user's workspace knob was set — **that its number is the hook's reminder threshold and not + the obligation**. Every cycle, so an absent record is never ambiguous between "the default + applied" and "someone forgot". The record is **machine-extractable**, because the deferred + P8 measurement reads it; both copies pin one form, and the spec states which. +- [ ] **The gate-off residual is disclosed in the shipped text, not merely avoided.** A reader of + either copy learns, without inferring it: **that the floor a cycle owes is produced by the + agent rather than established by any mechanism**; **that nothing checks it against the + cited profiles**; and **by what specific routes it can therefore be wrong** — the routes + enumerated, and the enumeration stated as a floor rather than as a complete list. The text + claims **no guard**: a reader must not be able to come away believing the residual is + mitigated by anything the design ships. Checkable by reading either copy. +- [ ] **Severity is decided by one stated test in both copies, and the test is keyed on + consequence.** Four properties, each checkable by reading either copy: + **(a)** Exactly **one** procedure decides severity. A copy that states a categorical + demotion by subject *alongside* the test fails, because two procedures can disagree on one + finding; subject-based cases may appear only as worked examples of the test. + **(b)** The test turns on whether **something in the system takes a different decision** + if the text is wrong — not on what kind of file the text lives in, and not on a human + reader, whose cost §5's prose exemption already prices as non-gating. + **(c)** The instrument carve-out is **symmetric**: an instrument finding keeps its severity + whenever it shows the instrument changes what a gate concludes about product behaviour, + in **either** direction. A copy naming only a false green fails, because it would demote a + check that fails for wiring reasons and costs a correct change. + **(d)** Coverage-first survives: the reviewer still reports every finding with severity and + confidence, and the filter is ours. A copy that drops this fails even if (a)–(c) hold. - [ ] **Each of Q1–Q6 is answered or rejected in the shipped text, with a reason, and the answers do not contradict each other.** Q1 (clean-completion precedence), Q2 (a declined expansion's exit), Q3 (scope stop vs. clean completion), Q4 (whether a decline binds @@ -265,15 +245,18 @@ deliverable rather than a convenience. ## 5. Open questions -- **Which floor governs a cycle citing several stories with different profiles?** §5 already - aggregates the other dimensions explicitly — the battery runs once, each profiled story - satisfies its own mode, lens sets are unioned, skip-eligibility requires unanimity — and a - scaled floor adds a dimension with no aggregation rule. Lowest, highest, and per-story are - all defensible and they disagree. -- **Does a profile change mid-cycle move the floor for passes already run?** §5 makes the - story header the single writable copy, read fresh at each pass, and says passes run under a - lower profile keep counting toward the floor. If the floor itself is now profile-derived, a - mid-cycle raise changes the target after some passes are already banked. +- ~~**Which floor governs a cycle citing several stories with different profiles?**~~ + **Answered 2026-08-28: unanimity.** Floor 1 only if every cited story is profiled and every + one is at level 0; any other set yields 3. It follows §5's own precedent for the analogous + relaxation — "skip-eligible only if **every** cited story is" — and it is the only reading + consistent with invariant 2's firing direction, since a lowest-cited-floor rule would + under-review a cycle that also touches a high-risk story. +- ~~**Does a profile change mid-cycle move the floor for passes already run?**~~ + **Answered 2026-08-28**, and it needed no new rule — three existing ones compose. The floor + derives from the current profile at each pass; passes already run keep counting; closing + requires the floor as currently derived. The consequence worth stating is that a **raise costs + at least one further pass regardless of the arithmetic**, because §5 already requires the + final clean pass to run under the current profile. - ~~**What floor does an unprofiled cycle get?**~~ **Answered 2026-08-28** by the single-predicate decision in criterion 1: no profile means `max(risk, security)` is not 0, so the default of 3 stands. Kept rather than deleted, because the answer is only obvious From a332514229affa02307322922b60b74c899a3be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:59:02 +0200 Subject: [PATCH 016/117] =?UTF-8?q?docs(spec):=20revision=205=20=E2=80=94?= =?UTF-8?q?=20untangle=20the=20hold=20from=20the=20resolve=20duty;=20pass-?= =?UTF-8?q?4=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 4: 40 findings (4 BLOCKER, 27 MAJOR, 9 MINOR). Findings fell 54->40 after the marker deletion; Blockers rose 0->4, all of them story staleness or internal tangles rather than design regression, and no require/withdraw pair. The substantive fix. Three places in the spec disagreed about what a decline does, and two drafts of one table cell were wrong in opposite directions: the first made a declined finding both released and still open; the second deadlocked on an ACCEPTED MINOR, which is undeclined by definition and never repaired because Minors are collected and never iterated. Both mixed two different rules over two different populations. Now stated once: the HOLD exists because a finding awaits a decision and ends when any decision arrives, in either direction; the RESOLVE DUTY applies to in-set Blocker and Major findings and is untouched. The decline qualifies the hold and never the resolve duty -- a declined finding is out-of-set, so that duty never attached, and there is no exception to state. Stating one is what produced the tangle. The severity test gains a self-exclusion that closes a hole which would have shipped looking correct: the reader must consume the text in the system's OPERATION, not in reviewing it, so the gate currently finding the defect is not its own in-system reader. Without it any Gate-A finding could argue "Gate A reads this", satisfy the test, and nothing would ever demote. Two of my own overclaims removed. The marker-deletion rationale said deletion "removes the camouflage path"; it does not -- any agent can still write 1 into the gitignored knob and quiet the hook, with or without this design. What deletion removes is the design's own reliance on writing it, and with that the ambiguity: a floor file is now always a user artifact. And §10's gate-off list called itself "honestly enumerated" while being a floor -- an enumeration read as complete guarantees what it omits, which is the AGENTS.md Don't in its own words. Also: §5.1 claimed position equals pass number while excluding incomplete passes, which cannot both hold -- curves now state the pass numbers they cover, and carry the model each pass ran under per the existing coding-workflow.md:261-266 convention this change would otherwise have silently narrowed. The cycle identifier and the artifact-revision identifier are now defined rather than assumed. Provenance gains forms for the no-story and unprofiled cases. A skipped Gate B records the skip rather than a missing curve. §8's user-knob verification is conditional and records not-applicable rather than having an agent create a fixture to make it runnable, and gains a twelve-item prompt-standards conformance pass. §10's unknown-in-flight fallback is floor 3 rather than "re-derive", which could have skipped passes on the strength of not knowing when a loop started; downstream adoption now covers the skip, decline and partial-merge outcomes invariant 9 permits. §6.2 gains rows 17 and 18 -- Changing a profile, and the findings-slot paragraph. §7 gains getting-started.md:44-45. The implementation surface now names the story's own criteria. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-4 at .context/codex-reviews/gate-a-spec-rle-pass-{1..4}.md. Findings 27, 30, 54, 40. Blockers 5, 2, 0, 4. Floor 3 met by count; not clean. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 154 ++++++++++++++---- 1 file changed, 123 insertions(+), 31 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 2cd6986..60d9046 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 4, after Gate-A passes 1 (27), 2 (30), 3 (54 findings) +**Date:** 2026-08-28 · **Revision:** 5, after Gate-A passes 1 (27), 2 (30), 3 (54), 4 (40 findings) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. @@ -10,7 +10,9 @@ is written by this design.** **Surfaces edited.** `CLAUDE.md` §5 (65–589) and its mirror inside `/workflow-init`'s inline `CLAUDE.md` template (`plugins/dev-workflow/commands/workflow-init.md`, fenced 192–778; §5 is -257–777), plus the user-facing statements this change falsifies (§7). The two §5 copies already +257–777), plus the user-facing statements this change falsifies (§7) and **this story's own +acceptance criteria**, which six edits this cycle have already corrected and which the plan must +treat as part of the surface rather than as context. The two §5 copies already differ on 192 lines across 20 hunks; this design touches only the rules it changes, per story criterion 7. @@ -44,12 +46,15 @@ carries both reasons. Three pairs, one sentence, no ordering. ### 1.2 Three of the four duties are not participants - **The floor** is a quantity gating closure — "**Below the floor nothing closes**". -- **Blocker/Major must resolve** is a **precondition on closure, and it is not discharged by a - quiet pass.** A pass that raises no *new* Blocker or Major is not evidence that a previously - surfaced one was resolved; the resolution is a separate fact, and the closing report names - each surfaced Blocker/Major and how it was resolved. (An earlier revision called this - "definitional", which is the describe-what-a-gate-proves failure: the clean-pass condition - proves what the reviewer found *this* pass, not what happened to earlier findings.) +- **Blocker/Major must resolve** applies to **in-set** Blocker and Major findings, and is a + **precondition on closure not discharged by a quiet pass.** A pass that raises no *new* + Blocker or Major is not evidence that a previously surfaced one was resolved; the resolution + is a separate fact, and the closing report names each in-set Blocker/Major and how it was + resolved. (An earlier revision called this "definitional", which is the + describe-what-a-gate-proves failure: the clean-pass condition proves what the reviewer found + *this* pass, not what happened to earlier findings.) **The decline does not qualify this + duty and needs no exception in it** — a declined finding is out-of-set, so the duty never + attached. - **A surfaced finding stays open** is the *mechanism* making the other exits suspensions. - **No pass carrying a surfaced finding counts as clean** is the only participant in ordering. @@ -61,11 +66,22 @@ carries both reasons. Three pairs, one sentence, no ordering. |---|---| | clean completion × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim. Settled and probably unreachable — a clean pass has no regenerating Blocker/Major, so the exit's third condition fails. Kept because dropping a sentence §5 already spends is the failure criterion 6 exists to prevent. | | clean completion × two-tell stop | **Clean wins.** Daniel's recorded decision, encoded not reopened. | -| clean completion × scope stop | **The scope stop outranks closure while any surfaced finding remains unresolved and undeclined.** A finding the user has explicitly declined is resolved *for this purpose* and no longer holds the cycle. | - -**The third cell's wording is deliberate.** An earlier draft read "clean wins only when every -surfaced finding still open has been explicitly declined", which is self-contradictory: §2.1 -defines a decline as *releasing* the finding. The rule is stated once, from the hold's side. +| clean completion × scope stop | **The scope stop outranks closure while any surfaced finding is still awaiting the user's answer. Any answer ends the hold**, in either direction. What happens next is not this cell's business: an answered finding is governed by the ordinary rules (§2.1). | + +**The third cell's wording is deliberate, and two earlier drafts got it wrong in opposite +ways.** One read "clean wins only when every surfaced finding still open has been explicitly +declined" — self-contradictory, since a decline *releases* the finding. The next read "while any +surfaced finding remains unresolved and undeclined" — which deadlocks on an **accepted Minor**, +a finding that is undeclined by definition and never repaired, because Minors are collected and +never iterated. + +**Both drafts made the same mistake: they mixed the hold with the resolve duty.** They are +different rules over different populations. The **hold** exists because a finding is *awaiting a +decision*, and it ends when the decision arrives — which is exactly Daniel's settled +discharge-in-both-directions. The **resolve duty** applies to **in-set** Blocker and Major +findings, and it is untouched by any of this: the decline qualifies the hold and **never** the +resolve duty. A declined finding is out-of-set, so the resolve duty never attached to it in the +first place; there is no exception to state, and stating one is what produced the tangle. **Why the third is the only one that needed deciding.** A scope stop is triggered by a *specific finding*; while the duty stands unqualified that finding is open, so the pass cannot be clean and @@ -119,8 +135,12 @@ that was declined — makes it a **new finding, and the hold applies**. Where sa it is likewise new, which costs a question and never a silent release. This mirrors §5's existing treatment of unclear set membership. -**A decline binds one cycle only.** The record carries a cycle identifier and **has no effect in -any later cycle**, even though the record itself persists into commit and squash history. Without +**A decline binds one cycle only.** The cycle identifier is what the cycle is already keyed to and +what no two concurrent cycles share: **the gate and the artifact commit it reviews** — for Gate A, +the loop name plus the commit the reviewed artifact was read at; for Gate B, the `WIP:` commit's +parent. Two loops on one branch therefore never collide, and a resumed loop recovers its own +identifier from that same source rather than inventing one. The record **has no effect in any +later cycle**, even though the record itself persists into commit and squash history. Without that bound, a decline recorded once would silently release the same finding in every future cycle, which nobody decided. @@ -155,6 +175,9 @@ body is the record. > Name the gate, skill, rule, escalation procedure or scaffolded template that reads this text, > and the decision it takes differently if the text is wrong. If you cannot name an in-system > reader and a changed decision, the finding is **Minor** — collect, never iterate. +> +> **The reader must consume the text in the system's operation, not in reviewing it.** The gate +> currently finding the defect does not count as its own in-system reader. Findings about narration, mechanism prose and test-instrument internals are the cases this usually catches, shipped as **worked examples of the test, not a second rule beside it**. Stating @@ -163,6 +186,12 @@ a categorical demotion *and* the test would give two procedures that can disagre **A human reader never satisfies the test.** That cost class is already priced as non-gating by §5's prose exemption — "a wrong sentence costs a confused reader, not broken behaviour". +**And the reviewing pass never satisfies it either**, which is why the self-exclusion above is +not a detail. Without it, any Gate-A finding could argue "Gate A reads this text", satisfy the +test, and **nothing would ever demote** — the rule would ship looking correct and classify +everything as it did before. Gates remain legitimate in-system readers of rule text; what is +excluded is the pass currently raising the finding. + **The instrument carve-out runs in both directions.** An instrument finding keeps its severity when it shows the instrument **changes what the gate concludes about product behaviour** — a false green, and equally a false red, a check that blocks a valid change, or instrument logic @@ -241,6 +270,13 @@ the derived floor diverge, the provenance line **discloses both** rather than re floor N per ; hook reminder threshold M per workspace knob ``` +**Every cycle records this, default or not** — an absent line must not be ambiguous between "the +default applied" and "someone forgot" — with **one entry per cited story**, and the knob clause +present only when a knob is set. **Two cases need their own forms**, because the common one has +nowhere to put them: an artifact citing **no story** records `floor 3 (no story cited)`, and a +cited **unprofiled** story records `floor 3 per (unprofiled)`. Both keep the derivation +legible to a reader who otherwise cannot tell an unprofiled cycle from a missing line. + **The consequence, accepted rather than engineered around: a level-0 cycle closing at one pass draws a hook reminder saying "below floor (1/3)".** That reminder is **the invariant working**, not a defect — AGENTS.md invariant 2: *"On uncertainty, fire. A missed commit (false ✓) is the @@ -257,6 +293,14 @@ protected case is not incomplete; it is inverted. Deleting it removes every one the concurrency exposure on a shared mutable file, and the whole class of crash-recovery rules written in prose for an advisory file. +**What deletion does not remove, stated so the rationale does not overclaim.** Any agent can +still write `1` into the gitignored knob and quiet the hook — that path exists with or without +this design, because the file is writable and the hook reads it. What deletion removes is *this +design's own reliance on writing it*, and with that the ambiguity: under revision 4 a floor file +is **always** a user artifact, so a written `1` is a claim about the hook's reminders and never a +claim about what §5 obliged. The obligation lives in the provenance line, where a wrong number is +a false statement rather than a silent file. + **The sanctioned lever for wanting fewer obliged passes is the profile** — proposed, human- confirmed, logged — **or `codex-gate.off` for the reminders.** Not the floor knob, which moves what the hook says and never what §5 obliges. @@ -274,7 +318,9 @@ stops qualifying the moment the profile moves — so even a raise leaving the fl (`standard` → `high`, both 3) costs a pass. **On lowering.** A lower drops the floor *and* the lens sets *and* the evidence mode, so a `high` -cycle lowered to `trivial` can close on one pass. That is the pre-existing profile-change path, +cycle lowered to `trivial` can close on **one further pass after the lowering** — not on one pass +in total, since the rule that costs a raise a pass applies here too: the final clean pass must run +under the current profile, so no already-banked pass can be it. That is the pre-existing profile-change path, human-confirmed in both directions and logged. The variable floor rides it; it does not create it. --- @@ -333,6 +379,10 @@ Gate-A plan loop: Findings 8, 3. Blockers 1, 0. Gate B: Findings 14, 24, 12. Blockers 3, 4, 0. ``` +**A legitimately skipped Gate B records the skip, not a curve.** §5's triviality skip already +requires the reason in the commit body; that loop's line reads `Gate B: skipped (see skip +reason)`, so the absence is a stated fact rather than an omission P8 must guess at. + **Gate B alone would leave the dominant cost unmeasured.** The loops this story cites as evidence are Gate-A loops — nineteen measured Gate-A passes on one spec. P8 without Gate-A curves cannot measure the thing the problem statement is about. (Pass 1 narrowed this to Gate B and pass 2 found @@ -346,11 +396,22 @@ pass order, comma-separated. - **The curve counts logical passes; the hook counts calls.** These are deliberately different numbers and §5 already says the counter is not evidence. Where they differ the body says so: `(N calls, M passes)`. -- **Both branches of one logical pass must review the same artifact revision.** If the artifact - changes between them they are not one pass, and the second branch starts a new one. +- **Both branches of one logical pass must review the same artifact revision**, identified by the + commit the cycle identifier names. If the artifact changes between the branches they are **not + one pass**: the completed branch is recorded as an incomplete pass and excluded, and the second + branch begins a new pass. Ending the pass is the conservative direction — merging two revisions + would produce one entry describing two different artifacts. - **Incomplete passes are excluded** — §5 already says they do not count. -- **A valid pass with zero findings is written as `0`**, never omitted, so position equals pass - number. +- **A valid pass with zero findings is written as `0`**, never omitted. +- **Position does not equal pass number**, because incomplete passes are excluded and they + consume pass numbers. Each curve therefore states the pass numbers it covers — + `passes 1-4` or `passes 1,2,4` — so a reader never infers a mapping the exclusion rule + forbids. (An earlier revision claimed position equals pass number in the same breath as + excluding incomplete passes, which cannot both hold.) +- **The model each pass ran under is recorded beside its counts**, per the existing convention + in `docs/coding-workflow.md:261-266`: "Put the model the pass *ran under* in the pass record + beside the finding count." A curve dropping it would silently narrow a rule this change never + proposed touching. **What it reaches, and what it is worth.** The durable half **across cycles** — surviving a fresh checkout, a cleared `.context/`, a different machine. **It does not restore history within a @@ -410,7 +471,7 @@ the conditions this change **touches or could drop**; the plan carries the repla | 3 | The incomplete-pass rule at `:236/:421` | don't act on a partial list; don't count it toward the floor; don't read "no Blocker/Major visible" as clean | **kept**, "3-pass floor" **changed** to "the floor" | | 4 | "What a loop absorbs, and what stops it" | in-set findings absorbed, acted on by severity; ancestry decides *where*, never *what you do*; ancestry grants no Minor a repair round; assigned fix set fixed **before** the pass; unclear membership resolves **outside**; an out-of-set correction stops the loop even opening no new question; a new structural/contract question stops it; **novelty not size**; **when a finding is both, novelty wins**; stopping is **not an exit** — floor, filter and clean-final-pass all stand | all **kept**; the scope stop is **moved** into §2's ordering as one of three suspensions and gains the decline qualification | | 5 | "Recognizing \"clearly stuck\"" | read the Blocker curve **across passes**, not one total; one low count is a snapshot not a plateau; **neither curve measures coverage**; three conditions **together**, a missing one means keep going; six-plus passes is where the field saw one and is **not a threshold**; an affirmative coverage judgement must be **stated**; a known unreviewed area **forbids the exit**; Blocker/Major **regenerating** across genuine repairs; **clean completion takes precedence**; below the floor nothing closes; zero-finding pass the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | all **kept**; the precedence sentence **moved** into §2's table and preserved verbatim there; the "pass 1" clause **changed** to "below the floor" per §6.1 | -| 6 | "Surfacing does not close the cycle" | surface **with the finding still open**; the resolve rule is **not waived**; **no pass credited clean**; loop resumes on the user's decision | all **kept**. The decline is an exception to **the third condition only where the user has declined that finding** — it does not waive the resolve rule generally, and an accepted Blocker/Major still blocks clean until resolved (§2.1) | +| 6 | "Surfacing does not close the cycle" | surface **with the finding still open**; the resolve rule is **not waived**; **no pass credited clean**; loop resumes on the user's decision | all **kept**, and the fourth is what carries the change: the hold lasts **until the user answers**, and any answer ends it. The decline therefore touches **only** the third condition, and only for the finding decided. The resolve rule is **not waived by anything here** — a declined finding is out-of-set, so that rule never attached to it, and an accepted Blocker/Major still blocks clean until resolved (§1.2, §2.1) | | 7 | "From pass 4 onward every pass report carries three lines" | duty **activates at pass 4** and binds every pass after; carrier is **your status report**, never the Codex reply, never the findings file; the three line contents; all five tell definitions; **any two mandatory, not discretionary**; report the tells and hand the decision to the user; the stuck reading is **not a precondition** | all **kept**; §5 **adds** the unavailable-history behaviour, which the passage currently leaves undefined | | 8 | "The two rules above do not compete" | absorb decides *a finding's* scope; the stuck reading decides whether *the loop* converges; neither overrides the other; a small in-set correction is not itself evidence of a plateau | **kept**, **moved** to reference §2's ordering rather than restate the relationship | | 9 | "Lenses are different questions, not more passes" (`:403/:582`) | lens sets add questions, not passes; the 3-pass floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are **unchanged** by lenses | **kept**; "3-pass floor" **changed** to "the floor", and the unchanged-by-lenses claim now reads against a derived floor | @@ -421,7 +482,9 @@ the conditions this change **touches or could drop**; the plan carries the repla | 14 | "**Severity:** Blocker … Major … Minor · Nit" | Blocker = wrong/unsafe/breaks invariant; Major = design flaw → rework; both must resolve; Minor and Nit collect, never iterate | **kept**, and **narrowed**: the reachability test is added as the classifier, so a finding that would once have been Major on subject alone can now be Minor. That narrowing is deliberate and is the change; it is recorded here as a **changed** condition, not a kept one | | 15 | "Scope, and it is narrow" | the form supplies no permission; never the answer to a below-floor pass, an unclean final pass, a STOP, a Gate-A/B obligation or a profile-derived evidence requirement; authorizes nothing any mandatory rule requires; mandatory is not limited to this file; not for things never owed | all **kept without exception**; §2.1 **adds** the decline as a distinct record type on the same transport and states the distinction, precisely so this passage is not weakened | | 16 | "Recording a human exception" | the three-line form; which commit carries it; decisions made after a commit closed; an empty commit is a legitimate destination; several records accumulate; the record is an **unverified assertion** | all **kept**; the decline record **reuses the transport** and adopts the same unverified-assertion honesty | -| 17 | "On squash-merge, copy every evidence entry…" | every evidence entry and human-exception record in the range copied into the squash body; nothing performs or checks the carry; a disagreement between copies is a copying error to fix, not to choose between | **kept**; three record types **added** | +| 17 | "Changing a profile" | the pass proposes the complete resulting header; the human confirms, in both directions; an agent never moves it alone; on confirmation correct the header and append one profile-log line; any axis change voids every prior override; `+abuse-path` follows the current security value; passes already run under the lower profile keep counting; only the final clean pass must run under the current profile; inside an active Gate-B cycle fold the edit into the `WIP:` snapshot by amend | all **kept**; §4.2 **adds** the floor's behaviour under a moving profile, which composes from the last two requirements and introduces no new rule | +| 18 | The findings-slot naming paragraph (`` is `gate-a-spec-pass-

`, …) | the three slot names; one finding per line; the severity token set; delete every target and confirm gone before each call; one pass at a time, since the slot name has no invocation-unique component and two concurrent calls on one slot race | all **kept**; §5 **adds** the per-cycle discriminator as a *convention* on top, not a change to the grammar — the three names remain as specified, and a discriminated slot is one of them with an infix, which existing practice already contains (`gate-b-spec-pr15-pass-1`) | +| 19 | "On squash-merge, copy every evidence entry…" | every evidence entry and human-exception record in the range copied into the squash body; nothing performs or checks the carry; a disagreement between copies is a copying error to fix, not to choose between | **kept**; three record types **added** | --- @@ -443,6 +506,7 @@ statements does this diff falsify?" — a change makes sentences wrong in files | `:53` | Gate B "three passes, final clean" | not at level 0 | | `:84` | "Gate A's floor is unchanged at every level" | **directly contradicted** — a sentence specifically about profile levels | | `:86` | "moves the 3-pass floor" | same as README | +| `docs/getting-started.md:44-45` | "If the Gate-A floor wasn't met, the hook says so right when execution starts" | the hook reports against **its own** threshold, so at level 0 it can say the floor was not met when nothing further is owed — the reminder is right to fire and the sentence is wrong about what it means | | `docs/coding-workflow.md:79-80` | the axes "never subtract any: **Gate A's floor** and the baseline questions are the same at every level" | **directly contradicted**, and missed until Gate-A pass 3 — the same claim as `:84` in a second file | **The rewording is honest rather than cosmetic.** README and getting-started describe the knob as @@ -495,15 +559,33 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). design never wrote a file (which it now never does), and a floor-1 demonstration is impossible on this branch, whose cited story is risk `high` — story criterion 9 forbids manufacturing a level-0 fixture for it. -- **A verification that a user's knob survives untouched.** An existing `codex-gate.floor` set - before a cycle starts is byte-identical after it closes, and the cycle's provenance line - discloses both values. This exercises the settled user-knob rule, which no other check reaches. +- **A verification that a user's knob survives untouched — conditional, and honest when it does + not apply.** Where a `codex-gate.floor` exists before a cycle starts, it is byte-identical after + it closes and the provenance line discloses both values. **Where none exists, the verification + is recorded as not-applicable with that reason** — an agent must not create one to make a check + runnable, which would be a fixture supplying its own input, the wiring failure this same section + warns about. The design's stronger claim covers the gap: **no floor file is written on any + path**, which §8's risk-path verification checks unconditionally. - **Parity verification across every changed rule**, per story criterion 7, covering **§6.1's - fourteen sites, §6.2's seventeen rows, and every rule §§2–5 newly insert.** The copies already + fourteen sites, §6.2's nineteen rows, and every rule §§2–5 newly insert.** The copies already differ on 192 lines, so parity cannot be asserted from a whole-section diff; the verification walks each named item and records every difference as deliberate-and-stated or as a defect. `scripts/check-invariants.sh` covers one severity spelling and is not coverage — invariant 11's stated condition. +- **A fresh twelve-item `docs/prompt-standards.md` pass over every changed prompt region.** + Invariant 11 makes all twelve binding on any skill, command, agent definition, hook message or + scaffolded template, and this change edits two prompt copies plus user-facing docs. Naming the + absence of a mechanical checker is not the same as doing the reading; the entry records which + regions were read against which items, and item 7 — "no contradictions with CLAUDE.md / + AGENTS.md" — is the one this change is most able to fail, since it rewrites CLAUDE.md itself. + +**When this evidence is produced and revalidated.** §5 requires the evidence entry to be +revalidated before every Gate-B re-review and before the cycle-closing amend, because a fix +changes the diff even when the profile sits still. Two of the verifications above read **this +branch's closing commits**, which do not exist until the cycle closes — so they are produced +against the `WIP:` snapshot during the cycle, and **re-read against the final amended commit +before closing**. If the amend changes what they observed, the clean pass no longer covers what is +being committed: fix, re-review, close on the entry that pass validated. **Instrument discipline, from this story's own evidence.** Two defects in the `fic2` decision matrix were properties of the technique: **a state's inputs must include every input the rule @@ -530,16 +612,26 @@ general reconciliation of the two copies' 192-line divergence beyond the one sea - **When these rules bind.** From the commit that ships them, and **a loop already in flight finishes under the rules it started with** — re-deriving a floor mid-loop from a rule that did not exist when passes were banked would invalidate a count nobody could reconstruct. **Where a - loop's starting rules cannot be established, treat it as new and re-derive**, which costs passes - and never skips them. An earlier revision proposed git timestamps plus file mtime as a start + loop's starting rules cannot be established, it takes floor 3** — not a re-derivation, which + could hand a level-0 loop a floor of 1 and *skip* passes on the strength of not knowing when it + started. The conservative value is the point; "re-derive" was the wrong instruction and did not + do what the sentence beside it claimed. An earlier revision proposed git timestamps plus file mtime as a start marker; that does not survive checkout, copy or clock skew, so no marker is claimed and the fallback carries the case. This also disposes of this design's own Gate A, which runs under the old rules. - **Downstream adoption has no shipping commit.** The template travels into other repositories whose history does not contain this change, so "from the commit that ships them" is a statement - about *this* repo. Downstream, the rules bind from the `/workflow-init` run that writes them, and - the same treat-as-new fallback applies. -- **The gate-off surface, honestly enumerated.** Deleting the marker mechanism removed the + about *this* repo. Downstream, the rules bind from the `/workflow-init` run that **actually writes them**, which is + not every run: invariant 9 makes the command idempotent and non-overwriting, so a run may report + the file unchanged, or show a diff and be declined, or merge only part of it. **The rules bind + only over the text a project's `CLAUDE.md` actually contains**, a project may therefore sit on a + partial adoption indefinitely, and nothing detects that — the same floor-3 fallback applies + wherever the text does not settle the question. +- **The gate-off surface — the routes known today, not a complete list.** An enumeration read as + complete guarantees the routes it omits, which is the AGENTS.md Don't in its own words; this + one is a floor. Beyond the four below, the pre-existing routes are unchanged and not created + here: falsifying evidence entries, misreporting a battery, silencing reminders with + `codex-gate.off`, or simply not running a pass and reporting that it ran. Deleting the marker mechanism removed the camouflage path and every crash-recovery path. What remains: **writing a provenance line the cited set does not license; omitting a higher-risk cited story from the set; minting or editing a story profile to level 0; presenting an incomplete cited-story set.** All four are *statement* From facb63687f0b55c0fff3716c153066dcc50ae732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:19:07 +0200 Subject: [PATCH 017/117] =?UTF-8?q?docs(spec):=20revision=206=20=E2=80=94?= =?UTF-8?q?=20correct=20two=20false=20claims=20I=20made=20about=20the=20ho?= =?UTF-8?q?ok?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 5: 33 findings (0 BLOCKER, 30 MAJOR, 2 MINOR, 1 NIT). Findings falling 54 -> 40 -> 33; Blockers 4 -> 0; no tell present. Two findings were about assertions I made while citing my own verification, and both were wrong -- the same describe-a-mechanism-without-checking-it failure this design warns about: The spec opened "no file the hook reads is written by this design". False. The hook greps CLAUDE.md at :94 for the §5 heading to build the citation every reminder prints. That is now stated as a constraint the plan must honour: the §5 heading must keep matching ^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review, or every hook message silently degrades to the generic fallback. And §4.1 claimed $floor "appears only inside the hook's advisory note messages". Mechanically false -- it appears in control flow at :946 and :966. The correct and narrower claim: that control flow only selects WHICH advisory message fires, and the hook exits 0 on every branch, so no value of the knob ever made it block or made an agent owe a pass. Also corrected, all mine: §4's both-gates rationale still cited the hook's shared $floor as the reason, which stopped holding once the obliged floor left that file -- the reason is that both loops cite the same stories. The self-exclusion excluded "the gate currently finding the defect" rather than the reviewing pass, which would have excluded gates as legitimate readers of rule text. The provenance line attributed a set-derived floor to each cited story individually, which misattributes a floor of 3 to level-0 stories that did not cause it -- now one floor plus the set that produced it. The three-rule qualification set no longer cohered once §1.2 scoped the resolve duty to in-set findings; the three are now named exactly, with the resolve duty explicitly NOT among them. The decline is restricted to findings surfaced by a scope stop, since an in-set Blocker was never awaiting a set-membership answer and a decline there would be a general waiver. The reader enumeration is now illustrative rather than closed, per invariant 10, since these rules ship into projects whose readers we have never seen. The test is stated as deciding one boundary only -- Minor-or-below versus keeps-its-severity -- never Blocker versus Major, which Mechanics still decides. The curve example now closes legitimately and shows the pass-number mapping, the incomplete-pass exclusion and the per-pass model. The revalidation step no longer asks to re-read a commit that does not exist yet. The no-write check now says what it cannot detect. The unknown-start fallback covers all four parts rather than the floor alone. And the downstream partial-adoption tension with §2's coupling argument is stated rather than resolved, because nothing in prompt text can prevent a human accepting half. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-5 at .context/codex-reviews/gate-a-spec-rle-pass-{1..5}.md. Findings 27, 30, 54, 40, 33. Blockers 5, 2, 0, 4, 0. Floor 3 met; not clean. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 173 ++++++++++++++---- 1 file changed, 136 insertions(+), 37 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 60d9046..168881f 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,12 +1,21 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 5, after Gate-A passes 1 (27), 2 (30), 3 (54), 4 (40 findings) +**Date:** 2026-08-28 · **Revision:** 6, after Gate-A passes 1 (27), 2 (30), 3 (54), 4 (40), 5 (33) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. -Prompt-only. No file under `plugins/dev-workflow/hooks/` changes, and **no file the hook reads -is written by this design.** +Prompt-only. No file under `plugins/dev-workflow/hooks/` changes, and **no hook *state* file is +written by this design** — in particular not `.context/codex-gate.floor`. + +**The hook does read one file this change edits, and that is a constraint the plan must honour.** +`codex-gate.sh:94` greps `CLAUDE.md` for its §5 heading with +`^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review` to build the citation every +reminder prints (`:109`, falling back to "this project's review policy"). So **the §5 heading must +keep matching that pattern**: renaming or renumbering the section silently degrades every hook +message to the generic fallback. An earlier revision claimed no file the hook reads is written +here, which was false — the same describe-a-mechanism-without-checking-it failure this design +warns about, in text that cited its own verification. **Surfaces edited.** `CLAUDE.md` §5 (65–589) and its mirror inside `/workflow-init`'s inline `CLAUDE.md` template (`plugins/dev-workflow/commands/workflow-init.md`, fenced 192–778; §5 is @@ -50,7 +59,9 @@ carries both reasons. Three pairs, one sentence, no ordering. **precondition on closure not discharged by a quiet pass.** A pass that raises no *new* Blocker or Major is not evidence that a previously surfaced one was resolved; the resolution is a separate fact, and the closing report names each in-set Blocker/Major and how it was - resolved. (An earlier revision called this "definitional", which is the + resolved — **read from the per-pass findings files, which are the durable inventory**, not from + recall. Where those files are unavailable (§5's shapes), the report says which passes it could + not read rather than presenting a partial inventory as complete. (An earlier revision called this "definitional", which is the describe-what-a-gate-proves failure: the clean-pass condition proves what the reviewer found *this* pass, not what happened to earlier findings.) **The decline does not qualify this duty and needs no exception in it** — a declined finding is out-of-set, so the duty never @@ -94,9 +105,25 @@ three universal rules at once. The lattice was correct. ### 2.1 The qualification, and how a decision on a finding is recorded -**The duty is qualified for, and only for, a finding the user has explicitly declined.** Per -story criterion 5, the qualification appears **at each of the three universal rules** in both -copies — not only at the new clause, which is what made the earlier attempt unshippable. +**The duty is qualified for, and only for, a finding the user has explicitly declined.** The +qualification is stated **at each of the three rules that would otherwise contradict it**, in both +copies — not only at the new clause, which is what made the earlier attempt unshippable. Naming +them exactly, because an earlier revision named a set that no longer cohered once §1.2 put the +resolve duty out of reach: +1. **"a surfaced finding stays open"** — a declined finding does not stay open. +2. **"no pass carrying a surfaced finding counts as clean"** — the duty this qualifies. +3. **"the loop resumes on whatever the user decides"** — resumption is the hold *ending*, which + this makes explicit rather than leaving to inference. +The **Blocker/Major-resolve duty is deliberately not in that set**: §1.2 scopes it to in-set +findings, so a decline never reaches it and a qualification there would be inert text implying an +exception that does not exist. + +**What can be declined, and what cannot.** A decline answers **one question only: whether a +surfaced finding enters the assigned fix set.** It is available only for a finding surfaced by a +scope stop — one out-of-set, or one opening a new structural or contract question. **An in-set +Blocker or Major cannot be declined**: it was never awaiting a set-membership answer, so there is +no hold to release, and treating a decline as available there would convert the mechanism into a +general waiver — exactly what §5's "Scope, and it is narrow" forbids. **All three branches, symmetrically.** §5 already says the loop "resumes the moment the user says whether the set now includes it" — resumption *is* the hold ending, whichever way the answer went. @@ -110,8 +137,14 @@ whether the set now includes it" — resumption *is* the hold ending, whichever the floor. There is no deadlock; the apparent one assumed the hold outlives the answer. - **Undecided** → the hold stands. Nothing closes unanswered. -**Where the decision is recorded — the commit body, on rails §5 already ships.** The -human-exception machinery already solves this transport: "an ungated change records it in that +**Where the decision is recorded — the commit body, on rails §5 already ships.** With one timing +fact stated rather than assumed: **the commit that will carry the record does not exist while the +loop is running.** A Gate-A spec loop's record lands in the spec commit, which is written when the +loop closes; a Gate-B cycle's lands in the `WIP:` body by amend and is restated by the closing +amend. During the loop the decision lives in the working record — the dispositions companion, which +§5 already calls the advisory working copy — and **becomes the record when the commit is written**. +Nothing about the decline's effect waits for the commit; what the commit provides is durability +past the session. The human-exception machinery already solves this transport the same way: "an ungated change records it in that commit; a Gate-A cycle in the spec or plan commit; a Gate-B cycle in the WIP commit, restated by the closing amend", folded in mid-cycle by amend, carried into the squash body. **The decline reuses that transport as a different record type:** @@ -172,12 +205,21 @@ body is the record. **The decision procedure is the rule. The subject list is illustration.** -> Name the gate, skill, rule, escalation procedure or scaffolded template that reads this text, -> and the decision it takes differently if the text is wrong. If you cannot name an in-system +> Name **what in the system reads this text** — any artifact, procedure or automation whose +> behaviour depends on it — and the decision it takes differently if the text is wrong. In this +> repo that is typically a gate, a skill, a rule, an escalation procedure or a scaffolded +> template; **that list is illustrative, not the set of allowed readers**, because this rule ships +> into projects whose readers we have never seen (invariant 10). If you cannot name an in-system > reader and a changed decision, the finding is **Minor** — collect, never iterate. > -> **The reader must consume the text in the system's operation, not in reviewing it.** The gate -> currently finding the defect does not count as its own in-system reader. +> **The reader must consume the text in the system's operation, not in reviewing it.** The +> **review pass raising this finding** is not an in-system reader of the text it is reviewing. + +**The test decides one boundary only: Minor-or-below versus keeps-its-severity.** Which of Blocker +or Major a kept finding takes is decided exactly as Mechanics already decides it — Blocker for +wrong, unsafe or invariant-breaking, Major for a design flaw requiring rework. The test never +promotes and never chooses between the two; saying so keeps a reader from treating it as a +replacement for definitions it does not touch. Findings about narration, mechanism prose and test-instrument internals are the cases this usually catches, shipped as **worked examples of the test, not a second rule beside it**. Stating @@ -189,8 +231,10 @@ a categorical demotion *and* the test would give two procedures that can disagre **And the reviewing pass never satisfies it either**, which is why the self-exclusion above is not a detail. Without it, any Gate-A finding could argue "Gate A reads this text", satisfy the test, and **nothing would ever demote** — the rule would ship looking correct and classify -everything as it did before. Gates remain legitimate in-system readers of rule text; what is -excluded is the pass currently raising the finding. +everything as it did before. **Gates remain legitimate in-system readers of rule text**: a rule a +*future* Gate-B call will apply is read in the system's operation and passes the test. What is +excluded is narrower than "a gate" — it is the single pass currently raising the finding, reading +the text in order to review it. **The instrument carve-out runs in both directions.** An instrument finding keeps its severity when it shows the instrument **changes what the gate concludes about product behaviour** — a @@ -244,9 +288,13 @@ every one is at level 0**. Any other set — mixed levels, any unprofiled member profile — yields 3. §5's own precedent for the analogous relaxation ("skip-eligible only if **every** cited story is"), and the only reading consistent with invariant 2's firing direction. -**Why the text must say the floor governs both gates.** `codex-gate.sh:119` sets a single -`floor`, consumed at `:946` for Gate B and `:966` for Gate A. A reader who does not know this -will write a rule for one gate and assume the other is untouched. +**Why the text must say the floor governs both gates.** Because **the derived floor is a property +of the cycle's cited stories, and both loops cite the same stories** — so a level-0 story relaxes +the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle alike. A reader who does not see +this stated will write a rule for one gate and assume the other is untouched. (The hook's single +`$floor` at `:119`, compared at `:946` and `:966`, is a *separate* fact about the reminder +threshold. An earlier revision offered it as the reason, which stopped holding once the obliged +floor left that file — the reason is the shared cited-story set.) **Why there is no `docs-only` arm.** A diff-derived reading cannot serve Gate A, which runs on a spec before any diff exists. A story-declared reading is subsumed: intake defines `trivial` as @@ -262,8 +310,12 @@ is where the floor lives, and §5's text is what binds the agent, as it always w **The workspace knob stays exactly what it always was: the hook's reminder threshold.** It is never written, never removed, and never read for our derivation. Mechanically it never bound an -agent — `$floor` appears only inside the hook's advisory `note` messages (`:933`, `:946-947`, -`:956`, `:966-967`, `:973`), and the hook is advisory by invariant 1. Where the user's knob and +agent, and the precise claim is narrower than an earlier revision's: `$floor` **does** appear in +control flow — `[ "$passes" -lt "$floor" ]` at `:946` and `[ "$passesA" -lt "$floor" ]` at `:966` — +but that control flow only **selects which advisory message fires** (`:933`, `:947`, `:956`, +`:967`, `:973`). The hook exits 0 on every branch (invariant 1), so no value of the knob has ever +made it block or made an agent owe a pass. "Appears only inside note messages" was mechanically +wrong and is corrected here. Where the user's knob and the derived floor diverge, the provenance line **discloses both** rather than resolving them: ``` @@ -271,8 +323,18 @@ floor N per ; hook reminder threshold M per workspace knob ``` **Every cycle records this, default or not** — an absent line must not be ambiguous between "the -default applied" and "someone forgot" — with **one entry per cited story**, and the knob clause -present only when a knob is set. **Two cases need their own forms**, because the common one has +default applied" and "someone forgot". + +**The floor is a property of the cited *set*, not of any one story**, because unanimity makes it +so: one `high` story among four level-0 ones yields 3 for the cycle, and `floor 3 per ` +would misattribute that to stories that did not cause it. The line therefore states **one floor, +then the set that produced it**, with each story's level — `floor 3 per {A (level 0), B (high)}`. +A single-story cycle is the degenerate case of the same form. + +The knob clause is present **whenever the file exists**, not only when its value differs: a +present-but-equal knob is still a fact about the workspace, and tying disclosure to divergence +would make an absent clause ambiguous between "no knob" and "a knob that agreed". **Two cases need +their own forms**, because the common one has nowhere to put them: an artifact citing **no story** records `floor 3 (no story cited)`, and a cited **unprofiled** story records `floor 3 per (unprofiled)`. Both keep the derivation legible to a reader who otherwise cannot tell an unprofiled cycle from a missing line. @@ -342,7 +404,7 @@ item 10's failure: |---|---|---| | **absent** | the slot file does not exist | uncomputable; report which pass numbers are missing | | **partial** | some pass slots present, others not | compute historical tells over the passes present and **name the missing pass numbers** — a trend over an unstated subset reads as a trend over the cycle | -| **malformed** | fails the pass-acceptance checks: terminator, count match, no non-finding lines | treat as absent for that pass, **never** as zero findings. Distinct from *unreadable*: a malformed file is a review that ran and wrote badly, so **if its `sessionId` is still to hand** the pass may be re-runnable — and where it is not, say so rather than implying a recovery route that no longer exists | +| **malformed** | fails any pass-acceptance check — terminator exactly `END OF FINDINGS ( total)`, count matching the finding lines, nothing but finding lines, and **each line structurally well formed**: right field count, no empty or whitespace-only severity field. §5's reader tolerates the severity *token*'s casing and nothing else; every structural failure is INCOMPLETE | treat as absent for that pass, **never** as zero findings. Distinct from *unreadable*: a malformed file is a review that ran and wrote badly, so **if its `sessionId` is still to hand** the pass may be re-runnable — and where it is not, say so rather than implying a recovery route that no longer exists | | **unreadable** | exists but cannot be opened or decoded | treat as absent; report the OS-level cause, since permissions and a full disk need different fixes | | **stale** | the slot's cycle discriminator is absent or is another cycle's | treat as absent **and report its presence** — a foreign curve is worse than no curve | @@ -351,7 +413,16 @@ cycle identity, so a pass-2 file from a previous cycle is indistinguishable by c prompt-only mitigations: the **slot discriminator convention** — a cycle uses a per-cycle infix, as this cycle's `rle` does, following the `fic2` and `pr15` precedent, which makes *its own* slots identifiable — and **§5's existing delete-and-confirm-before-each-call rule**, which guarantees the -file you are about to write is not a previous cycle's. Neither makes a bare-slot file's origin +file you are about to write is not a previous cycle's. + +**The discriminator is a change to the slot grammar, not an addition beside it**, and §5 must say +so: today the grammar is exactly `gate-a-spec-pass-

`, `gate-a-plan-pass-

` and +`gate-b--pass-

`, and an infixed name satisfies none of them. The shipped text +widens each to admit an optional per-cycle infix — `gate-a-spec--pass-

` — and says the +infix is **recommended whenever a bare slot is already occupied**, which is the situation that +destroyed a previous cycle's findings file in this very cycle. Calling it a convention on top of +an unchanged grammar, as an earlier revision did, would leave every reader with two rules and no +way to tell which governs. Neither makes a bare-slot file's origin recoverable after the fact, and the report says so rather than implying detection. (The discriminator convention also prevents the destruction this cycle caused once: deleting a bare slot destroyed a previous cycle's findings record, which the dispositions companion happened to @@ -374,11 +445,17 @@ invariant 2 questions. The mandatory disclosure is what makes it acceptable. line **labelled with the loop it describes** so a squash body carrying several stays unambiguous: ``` -Gate-A spec loop: Findings 27, 30, 54. Blockers 5, 2, 0. -Gate-A plan loop: Findings 8, 3. Blockers 1, 0. -Gate B: Findings 14, 24, 12. Blockers 3, 4, 0. +Gate-A spec loop (passes 1-3, opus-5): Findings 27, 30, 0. Blockers 5, 2, 0. +Gate-A plan loop (passes 1,2,4, opus-5; pass 3 incomplete): Findings 8, 3, 0. Blockers 1, 0, 0. +Gate B (passes 1-3; 1-2 opus-5, 3 gpt-5-codex): Findings 14, 24, 0. Blockers 3, 4, 0. ``` +Every loop shown closes legitimately: three valid passes at floor 3, each ending on a +zero-finding pass. The plan loop's `passes 1,2,4` makes the exclusion rule visible — pass 3 was +incomplete, so it is absent from the counts and named in the mapping. **Where one logical pass was +assembled from calls under different models, each contributing model is listed for that pass**, +since a `spec` and a `quality` call need not have run under the same model. + **A legitimately skipped Gate B records the skip, not a curve.** §5's triviality skip already requires the reason in the commit body; that loop's line reads `Gate B: skipped (see skip reason)`, so the absence is a stated fact rather than an omission P8 must guess at. @@ -553,8 +630,12 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). verification takes this branch's own closing commits and confirms that **each provenance line's stated floor equals the floor the cited stories' profiles actually derive**, recomputed from those headers — the observation that would exist if the claim were false being a provenance line - whose number the profiles do not license. It also confirms that **no floor file was written**, - which under this design must hold on every path, including where none existed before. + whose number the profiles do not license. It also confirms that **no floor file is present at close where none was present at start**. + What that establishes, stated rather than implied: it detects a *persisting* write; it **cannot** + detect a transient one created and removed inside the cycle, because nothing observes the file + between the two checks. No rule here writes it, so a transient write would be a deviation from + the design rather than a permitted path — the check bounds the observable consequence, not the + behaviour. *Two failure modes this deliberately avoids:* an absence check alone would pass vacuously if the design never wrote a file (which it now never does), and a floor-1 demonstration is impossible on this branch, whose cited story is risk `high` — story criterion 9 forbids manufacturing a level-0 @@ -567,7 +648,9 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). warns about. The design's stronger claim covers the gap: **no floor file is written on any path**, which §8's risk-path verification checks unconditionally. - **Parity verification across every changed rule**, per story criterion 7, covering **§6.1's - fourteen sites, §6.2's nineteen rows, and every rule §§2–5 newly insert.** The copies already + fourteen sites, §6.2's nineteen rows, and every rule §§2–5 newly insert — including the §10 + residual disclosure and the §4.1 provenance forms, which story criteria require in **both** + shipped copies and which a §5-scoped parity walk would otherwise miss.** The copies already differ on 192 lines, so parity cannot be asserted from a whole-section diff; the verification walks each named item and records every difference as deliberate-and-stated or as a defect. `scripts/check-invariants.sh` covers one severity spelling and is not coverage — invariant 11's @@ -576,15 +659,20 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). Invariant 11 makes all twelve binding on any skill, command, agent definition, hook message or scaffolded template, and this change edits two prompt copies plus user-facing docs. Naming the absence of a mechanical checker is not the same as doing the reading; the entry records which - regions were read against which items, and item 7 — "no contradictions with CLAUDE.md / - AGENTS.md" — is the one this change is most able to fail, since it rewrites CLAUDE.md itself. + regions were read against which items. **Item 7 — "no contradictions with CLAUDE.md / + AGENTS.md" — is read against the whole resulting prompt, not only the changed regions**, because + a contradiction is a relation between an edited passage and an unedited one, and a diff-scoped + reading cannot see the second half of it. That is also the item this change is most able to + fail, since it rewrites `CLAUDE.md` itself. **When this evidence is produced and revalidated.** §5 requires the evidence entry to be revalidated before every Gate-B re-review and before the cycle-closing amend, because a fix changes the diff even when the profile sits still. Two of the verifications above read **this branch's closing commits**, which do not exist until the cycle closes — so they are produced -against the `WIP:` snapshot during the cycle, and **re-read against the final amended commit -before closing**. If the amend changes what they observed, the clean pass no longer covers what is +against the `WIP:` snapshot during the cycle, and **re-read against the content the close will +carry — the staged tree plus the message about to be written — rather than against a commit that +does not exist yet.** The amend *is* the closing act, so "re-read the final commit before closing" +is circular; what is checkable beforehand is exactly what the amend will commit. If the amend changes what they observed, the clean pass no longer covers what is being committed: fix, re-review, close on the entry that pass validated. **Instrument discipline, from this story's own evidence.** Two defects in the `fic2` decision @@ -611,8 +699,13 @@ general reconciliation of the two copies' 192-line divergence beyond the one sea - **When these rules bind.** From the commit that ships them, and **a loop already in flight finishes under the rules it started with** — re-deriving a floor mid-loop from a rule that did - not exist when passes were banked would invalidate a count nobody could reconstruct. **Where a - loop's starting rules cannot be established, it takes floor 3** — not a re-derivation, which + not exist when passes were banked would invalidate a count nobody could reconstruct. **Where a loop's starting rules cannot be established, it + takes the stricter reading of every rule this change touches, not only the floor**: floor 3; + severity classified without the demotion; the ordering's suspensions treated as binding; decline + records treated as absent, so no hold is released; and the curve duty treated as owed. A fallback + scoped to the floor alone would leave the other three parts silently unresolved, which is the + same partial-rewrite failure the old-conditions Don't describes. Concretely, the floor part is + **floor 3** — not a re-derivation, which could hand a level-0 loop a floor of 1 and *skip* passes on the strength of not knowing when it started. The conservative value is the point; "re-derive" was the wrong instruction and did not do what the sentence beside it claimed. An earlier revision proposed git timestamps plus file mtime as a start @@ -625,7 +718,13 @@ general reconciliation of the two copies' 192-line divergence beyond the one sea not every run: invariant 9 makes the command idempotent and non-overwriting, so a run may report the file unchanged, or show a diff and be declined, or merge only part of it. **The rules bind only over the text a project's `CLAUDE.md` actually contains**, a project may therefore sit on a - partial adoption indefinitely, and nothing detects that — the same floor-3 fallback applies + partial adoption indefinitely, and nothing detects that. **This is in tension with §2's argument + that the three parts are coupled**, and the tension is real rather than resolved here: a + downstream project that takes the severity rule without the precedence ordering gets a loop whose + exits are unordered and whose severities have moved, which is a state this design never + evaluated. What can be done in prompt text is done — the scaffolded copy ships the three together + and `/workflow-init` presents them as one diff — and what cannot is said: nothing prevents a + human from accepting half — the same floor-3 fallback applies wherever the text does not settle the question. - **The gate-off surface — the routes known today, not a complete list.** An enumeration read as complete guarantees the routes it omits, which is the AGENTS.md Don't in its own words; this From ed9629f1292ef9e9c5beea4d2935ee6c40c0c1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:20:11 +0200 Subject: [PATCH 018/117] docs(story): cover four spec obligations no criterion observed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate-A pass 5 checked spec-vs-story in BOTH directions for the first time, and the reverse direction found four gaps: obligations the spec carries and the settled decisions require, which no acceptance criterion made checkable. These predate the de-mechanization rather than being caused by it -- moving criteria up a level made the holes visible. Criterion 2 observed only per-pass finding and Blocker counts, not the pass-number mapping the exclusion rule makes necessary, the per-pass model an existing convention already requires beside a finding count, or what a legitimately skipped loop records. Criterion 5 did not require the reviewing-pass exclusion, which is the property that makes the severity test demote anything at all: without it any review finding can name the review itself as the in-system reader. Added as property (e), with the bound that gates remain legitimate readers of rule text they will later apply. No criterion observed the settled prohibition on writing the user's floor knob, though "never written, never removed" is the whole basis on which the marker mechanism was deleted. Added to the residual criterion, with the byte-identical observation this branch can actually make. And nothing covered activation at all -- when the rules start binding, what a loop does when its starting rules cannot be established, or what a downstream project gets when the scaffolder writes nothing, is declined, or is merged in part. That is now its own criterion, requiring the fallback to cover every part this change touches rather than the floor alone. Ten criteria. Level of description unchanged from the previous edit; these add coverage of settled obligations rather than new obligations. Sparring session, under Daniel's 2026-08-28 delegation. Profile unchanged, so no profile-log line. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- ...-review-loop-economics-pass-floor-story.md | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index c3dc7aa..f9fb9b0 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -129,9 +129,12 @@ deliverable rather than a convenience. plan loop** (in the plan's commit body) and the **Gate-B cycle** (in the closing amend) to record that loop's per-pass finding and Blocker counts, in one pinned greppable form, following the `3cdd075` precedent - (`Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0.`). Checkable: the - requirement is stated in both copies, and this story's own commits carry it for each loop - that ran. + (`Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0.`). The form is complete + enough that a reader can tell **which pass each number belongs to** — incomplete passes are + excluded and they consume pass numbers — and **which model each pass ran under**, which an + existing convention already requires beside a finding count. A **legitimately skipped** loop + records the skip rather than leaving a silent gap. Checkable: the requirement is stated in + both copies, and this story's own commits carry it for each loop that ran. **All three, not Gate B alone** (revised 2026-08-28 after Gate-A pass 2). A Gate-B-only requirement would leave the *dominant* cost unmeasured: the loops this story cites as evidence are Gate-A loops — nineteen measured Gate-A passes on one spec, and this story's @@ -157,6 +160,10 @@ deliverable rather than a convenience. enumerated, and the enumeration stated as a floor rather than as a complete list. The text claims **no guard**: a reader must not be able to come away believing the residual is mitigated by anything the design ships. Checkable by reading either copy. + **And both copies oblige the agent to leave the user's floor knob alone** — never written, + never removed by any rule this change ships; where one exists a cycle discloses it rather + than acting on it. Observable on this branch: a knob present before a cycle is byte-identical + after it. - [ ] **Severity is decided by one stated test in both copies, and the test is keyed on consequence.** Four properties, each checkable by reading either copy: **(a)** Exactly **one** procedure decides severity. A copy that states a categorical @@ -171,6 +178,11 @@ deliverable rather than a convenience. check that fails for wiring reasons and costs a correct change. **(d)** Coverage-first survives: the reviewer still reports every finding with severity and confidence, and the filter is ours. A copy that drops this fails even if (a)–(c) hold. + **(e)** **The pass raising a finding is not an in-system reader of the text it is + reviewing.** Without this the test demotes nothing — any review finding could name the + review itself as the reader — so a copy stating (a)–(d) and omitting (e) fails. Gates must + remain legitimate readers of rule text they will later apply; what is excluded is the + reviewing pass, not gates. - [ ] **Each of Q1–Q6 is answered or rejected in the shipped text, with a reason, and the answers do not contradict each other.** Q1 (clean-completion precedence), Q2 (a declined expansion's exit), Q3 (scope stop vs. clean completion), Q4 (whether a decline binds @@ -182,6 +194,13 @@ deliverable rather than a convenience. no pass carrying it counts as clean — the qualification appears **at each of those three rules** in both copies, not only at the new clause. Q2 was reverted last cycle precisely because that did not happen. +- [ ] **The shipped text says when it starts binding, and what an adopter gets when it does not + fully arrive.** Both copies state: that a loop already running finishes under the rules it + started with; what a loop does when its starting rules cannot be established, covering + **every part this change touches and not the floor alone**; and that a downstream project + adopts by re-running the scaffolder, which may write nothing, be declined, or be merged in + part — with the consequence of a partial adoption stated rather than assumed away. + Checkable by reading either copy. - [ ] **Every condition of the replaced prose is accounted for.** The change lists what each replaced §5 passage required and marks each requirement kept, moved, or deliberately dropped, per the AGENTS.md Don't quoted in §4. Checkable: the accounting exists and From 78ebfd6075e2e7816e423cbcc9ab5855d7d399f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:12:32 +0200 Subject: [PATCH 019/117] docs(story): de-mechanize the last two criteria; cover unanimity and decline durability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate-A pass 6 returned three Blockers; two were the fifth and sixth occurrences of criteria-restating-design. Criteria 1 and 6 were the last two sites of the class and are now de-mechanized like 3, 4 and 5. Level-of-description change, contract unchanged. Before/after per criterion: Criterion 1. BEFORE: "this one value governs Gate A and Gate B alike, which is not decoration: codex-gate.sh:119 sets a single floor, consumed at :946 for Gate B and :966 for Gate A." That rationale stopped holding at revision 6 -- those comparisons drive the hook's reminder threshold, not the obliged floor, so the criterion encoded a coupling the design had rejected. AFTER: one derived value governs every loop of the cycle -- Gate-A spec, Gate-A plan, Gate B -- and each copy says why: those loops cite the same stories. KEPT: both-gates scope, the requirement that the text say it rather than leave it to inference. MOVED: the hook's line numbers, to the spec. DROPPED: the false rationale, deliberately. Criterion 6. BEFORE: the qualification appears "at each of those three rules", naming the Blocker/Major-resolve duty, the surfaced-finding-open rule and the no-clean rule. The design later established that a decline never qualifies the resolve duty, so the criterion demanded a qualification the spec forbids. AFTER: the qualification is stated at every rule it modifies, in both copies, and at no rule it does not -- both halves falsifiable, and which rules those are is the design's to determine. KEPT: the placement obligation that made Q2 unshippable last cycle. ADDED: the converse, since a qualification at an unmodified rule implies an exception that does not exist. DROPPED: the enumeration. If a seventh occurrence of this class appears after this, the class fix has failed and that goes in the three lines. Two coverage gaps also closed, from the same pass: no criterion required the settled multi-story unanimity rule, and none required the decline to be durably recorded, identifiable, attributable, squash-surviving and cycle-bounded -- though that durability is the whole basis on which the commit body was chosen over the advisory dispositions file. The three announce-then-idle occurrences are recorded once in the dispositions file as field evidence, with the remedy adopted. Sparring session, under Daniel's 2026-08-28 delegation; he chose CONTINUE on the mandatory two-tell stop, and was told the stop-here option was not lawful under §5 with open Blockers. No rule was bent. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- .../2026-08-16-canvas-a1-a5-dispositions.md | 8 +++++ ...-review-loop-economics-pass-floor-story.md | 30 +++++++++++++------ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md index 11a3d41..64c81f0 100644 --- a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md +++ b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md @@ -340,3 +340,11 @@ outcomes or constraints, never implementation steps" — the rule exists; what t five measured occurrences of the failure it is meant to prevent, and the observation that the drift shows up as *Blockers in a later gate* rather than as a bad-looking criterion at intake time. + +**Also observed, 2026-08-28, same cycle:** three occurrences of an agent ending a turn on an +announcement — "writing the spec now", "running pass 6" — with the named tool call never issued. +Each cost a round-trip and one cost ~90 minutes of wall clock before a peer session noticed. No +error, no timeout, nothing in flight: the announcement simply replaced the act. Remedy adopted for +the remainder of that cycle: during an active gate cycle a turn ends with the tool call actually +issued, a message sent, or an explicit statement that something is blocking — and noticing the +turn ending with the call not in flight *is* that statement. diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index f9fb9b0..6cd3a05 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -112,9 +112,14 @@ deliverable rather than a convenience. mandatory pass floor of **1** where `max(risk, security)` is 0 and **3** otherwise — unprofiled cycles included, which keep today's 3. Two levels, not three: `high` gets no extra mandatory passes, taking its added rigor from lens sets and evidence mode instead. - Each copy also states that this one value governs **Gate A and Gate B alike**, which is - not decoration: `codex-gate.sh:119` sets a single `floor`, consumed at `:946` for Gate B - and at `:966` for Gate A. + **A cycle citing several stories reaches the reduced floor only unanimously** — every + cited story profiled, every one at level 0 — so a single higher-profile or unprofiled + member returns the cycle to 3. Stated in both copies, and falsifiable: a copy silent on + the multi-story case leaves the cheapest wrong reading available. + Each copy also states that **one derived value governs every loop of the cycle** — the + Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle alike — and says *why*: those + loops cite the same stories, so the value they derive is the same. A copy that states the + floor for one gate and leaves the others to inference fails this criterion. **One predicate, not two** (Daniel, 2026-08-28). An earlier draft added a `docs-only` arm; it is dropped because it does no work and, read path-wise, does the wrong work. A diff-derived reading cannot serve Gate A at all — Gate A runs on a spec, before any @@ -183,17 +188,24 @@ deliverable rather than a convenience. review itself as the reader — so a copy stating (a)–(d) and omitting (e) fails. Gates must remain legitimate readers of rule text they will later apply; what is excluded is the reviewing pass, not gates. + **(f)** *(record, not severity)* A user's decision on a surfaced finding is **durably + recorded where history keeps it** — carrying enough of the finding to identify it again, + naming who decided and when, surviving into a squash, and **bounded to the cycle it was + taken in**. A copy that lets the decision live only in per-clone working state, or that + lets one decision release the same finding in later cycles, fails. - [ ] **Each of Q1–Q6 is answered or rejected in the shipped text, with a reason, and the answers do not contradict each other.** Q1 (clean-completion precedence), Q2 (a declined expansion's exit), Q3 (scope stop vs. clean completion), Q4 (whether a decline binds later passes in the same cycle), Q5 (whether the three universal rules may carry an in-set qualification), Q6 (what the pass-4 report does when prior-pass history is - unavailable) — as stated in `docs/field-reports/2026-08-26-fic2-cycle-evidence.md`. Where - an answer qualifies one of the three universal rules — the Blocker/Major-resolve duty, - the rule that a surfaced finding stays open with resolution unwaived, and the rule that - no pass carrying it counts as clean — the qualification appears **at each of those three - rules** in both copies, not only at the new clause. Q2 was reverted last cycle precisely - because that did not happen. + unavailable) — as stated in `docs/field-reports/2026-08-26-fic2-cycle-evidence.md`. + **Where an answer qualifies a standing rule, the qualification is stated at every rule it + modifies, in both copies — and at no rule it does not modify.** Both halves are + falsifiable by reading: a rule the answer changes but does not mention leaves two + instructions disagreeing, which is why Q2 was reverted last cycle; a rule the answer does + *not* change but mentions anyway implies an exception that does not exist, which is its + own defect. Which rules those are is the design's to determine and the reviewer's to + check against it. - [ ] **The shipped text says when it starts binding, and what an adopter gets when it does not fully arrive.** Both copies state: that a loop already running finishes under the rules it started with; what a loop does when its starting rules cannot be established, covering From b8bfe15e658520e4f3798f74fb7e160a63472235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:14:26 +0200 Subject: [PATCH 020/117] =?UTF-8?q?docs(spec):=20revision=207=20=E2=80=94?= =?UTF-8?q?=20the=20qualification=20is=20the=20hold=20rule,=20stated=20in?= =?UTF-8?q?=20full?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 6: 34 findings (3 BLOCKER, 23 MAJOR, 6 MINOR, 2 NIT). Two tells present, mandatory stop-and-surface taken, Daniel chose CONTINUE. The Blocker that was mine to fix. §2.1 had written the qualification as "qualified for, and only for, a declined finding" -- a PARTIAL COPY of what was settled. It left an accepted Minor ending the hold while the no-clean rule stayed unqualified, so closure was governed by two rules that disagreed. The full rule is now stated as Daniel decided it: a surfaced finding holds closure while it awaits an answer; any answer ends the hold, either direction; after the answer the ordinary rules govern and the hold plays no further part. The three modified rules are named, and the Blocker/Major-resolve duty is explicitly NOT among them, since a qualification there would imply an exception that does not exist. The other two Blockers were story-side and are fixed in 78ebfd6. Twenty-three Majors folded. The load-bearing ones: the accepted branch no longer reads as re-scoring a pass that already happened -- a pass's cleanliness is a fact about what it found and never changes; what a later answer changes is whether the CYCLE may close. The slot grammar is now one rule rather than a convention layered on an unchanged one, with the infix REQUIRED where the bare slot is occupied, a refuse-rather-than-overwrite rule, and a uniqueness constraint -- the collision this cycle actually caused. A bare slot is unattributable rather than stale, which the earlier wording conflated. The reader-tolerance claim had dropped the existing rule that an unrecognized non-empty severity token reads as MAJOR. The severity test sets a ceiling, not a floor, so a trivial finding stays a Nit. The reader test now names the ACT that consumes the text rather than the artifact, since a rule or a template does not read anything. §10 admits that one gate-off route -- a stated floor the cited set does not license -- is created by this design rather than pre-existing. Row 1 keeps "counted by the hook" while sharpening it: the hook counts calls, the floor counts logical passes. Row 13 no longer implies re-review-after-fix generates the number 3. The revalidation check states what it cannot establish, since the index can change between the read and the commit. The knob clause has a form for a file present but unusable. Provenance now has exactly one pinned form, the single-story case being its one-element instance. The commit-timing paragraph names Gate B as the exception §5 already made it, via the WIP commit. And §10 separates what this branch must COMMIT from which rules its Gate-A loop was reviewed under -- recording is an act of the commit, not a rule the passes were judged by. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-6 at .context/codex-reviews/gate-a-spec-rle-pass-{1..6}.md. Findings 27, 30, 54, 40, 33, 34. Blockers 5, 2, 0, 4, 0, 3. Floor met; not clean. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 129 ++++++++++++------ 1 file changed, 89 insertions(+), 40 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 168881f..b955d71 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 6, after Gate-A passes 1 (27), 2 (30), 3 (54), 4 (40), 5 (33) +**Date:** 2026-08-28 · **Revision:** 7, after Gate-A passes 1-6 (27, 30, 54, 40, 33, 34 findings) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. @@ -105,18 +105,31 @@ three universal rules at once. The lattice was correct. ### 2.1 The qualification, and how a decision on a finding is recorded -**The duty is qualified for, and only for, a finding the user has explicitly declined.** The -qualification is stated **at each of the three rules that would otherwise contradict it**, in both -copies — not only at the new clause, which is what made the earlier attempt unshippable. Naming -them exactly, because an earlier revision named a set that no longer cohered once §1.2 put the -resolve duty out of reach: -1. **"a surfaced finding stays open"** — a declined finding does not stay open. -2. **"no pass carrying a surfaced finding counts as clean"** — the duty this qualifies. -3. **"the loop resumes on whatever the user decides"** — resumption is the hold *ending*, which - this makes explicit rather than leaving to inference. -The **Blocker/Major-resolve duty is deliberately not in that set**: §1.2 scopes it to in-set -findings, so a decline never reaches it and a qualification there would be inert text implying an -exception that does not exist. +**The qualification is the hold rule itself, stated fully rather than as a decline-only +carve-out.** An earlier revision wrote it as "qualified for, and only for, a declined finding", +which is a **partial copy** of what was settled: it left an *accepted* Minor ending the hold while +the no-clean rule stayed unqualified, so closure was governed by two rules that disagreed. The +full rule, which is what Daniel decided: + +> **A surfaced finding holds closure while it awaits the user's answer. Any answer ends the hold, +> in either direction. After the answer the ordinary rules govern, and the hold plays no further +> part.** + +Ordinary rules after the answer: an **accepted Blocker or Major** must resolve, and until it does +the cycle does not close — by the resolve duty, not by the hold. An **accepted Minor or Nit** is +collected and never iterated, and blocks nothing. A **declined** finding is out-of-set, so neither +duty attaches to it. + +**Stated at every rule it modifies, in both copies, and at no rule it does not** — the criterion's +two halves: +1. **"a surfaced finding stays open"** — modified: the finding stops being open when answered. +2. **"no pass carrying a surfaced finding counts as clean"** — modified: it means *unanswered* + findings; an answered one is governed by its severity. +3. **"the loop resumes on whatever the user decides"** — modified: resumption is the hold + *ending*, made explicit rather than left to inference. +The **Blocker/Major-resolve duty is not modified and carries no qualification**. §1.2 scopes it to +in-set findings; a declined finding never enters that set, and an accepted one is subject to it in +full. Writing a qualification there would imply an exception that does not exist. **What can be declined, and what cannot.** A decline answers **one question only: whether a surfaced finding enters the assigned fix set.** It is available only for a finding surfaced by a @@ -131,17 +144,21 @@ whether the set now includes it" — resumption *is* the hold ending, whichever - **Declined** → released as recorded-declined; stops holding the cycle for the remainder of it, and does not re-stop later passes. - **Accepted** → enters the assigned set, where **severity governs exactly as Mechanics already - says**: an accepted Blocker or Major must resolve, **and the pass that surfaced it is still not - clean until it does** — acceptance ends the *hold*, not the resolve duty. An accepted Minor or - Nit is collected, never iterated, and in-set Minors have never blocked a clean pass at or above - the floor. There is no deadlock; the apparent one assumed the hold outlives the answer. + says**. An accepted Blocker or Major must resolve; until it does, **no pass closes the cycle** — + and note this is a statement about *closure*, not about re-scoring a pass that already happened. + A pass's cleanliness is a fact about what that pass found and never changes afterwards; what a + later answer changes is whether the cycle may close. An accepted Minor or Nit is collected, + never iterated, and blocks nothing. There is no deadlock; the apparent one assumed the hold + outlives the answer. - **Undecided** → the hold stands. Nothing closes unanswered. **Where the decision is recorded — the commit body, on rails §5 already ships.** With one timing fact stated rather than assumed: **the commit that will carry the record does not exist while the loop is running.** A Gate-A spec loop's record lands in the spec commit, which is written when the -loop closes; a Gate-B cycle's lands in the `WIP:` body by amend and is restated by the closing -amend. During the loop the decision lives in the working record — the dispositions companion, which +loop closes. **A Gate-B cycle is the exception, and it is an exception because §5 already made +it one**: the `WIP:` commit exists precisely so a cycle has a commit while it is still running, so +a Gate-B decline lands there by amend immediately and is restated by the closing amend. The +timing claim above is therefore about Gate A only. During the loop the decision lives in the working record — the dispositions companion, which §5 already calls the advisory working copy — and **becomes the record when the commit is written**. Nothing about the decline's effect waits for the commit; what the commit provides is durability past the session. The human-exception machinery already solves this transport the same way: "an ungated change records it in that @@ -205,12 +222,16 @@ body is the record. **The decision procedure is the rule. The subject list is illustration.** -> Name **what in the system reads this text** — any artifact, procedure or automation whose -> behaviour depends on it — and the decision it takes differently if the text is wrong. In this +> Name **what in the system consumes this text** — a gate call, a skill run, an agent following a +> rule, an escalation procedure being applied, a template being scaffolded; whatever *acts* on it +> — and the decision that act takes differently if the text is wrong. (An artifact does not read; +> something reads it and then does something. Naming the act, not the file, is what makes the +> answer checkable.) In this > repo that is typically a gate, a skill, a rule, an escalation procedure or a scaffolded > template; **that list is illustrative, not the set of allowed readers**, because this rule ships > into projects whose readers we have never seen (invariant 10). If you cannot name an in-system -> reader and a changed decision, the finding is **Minor** — collect, never iterate. +> reader and a changed decision, the finding is **Minor or below** — collect, never iterate. A +> finding that is trivial on its own terms stays a Nit; the test sets a ceiling, not a floor. > > **The reader must consume the text in the system's operation, not in reviewing it.** The > **review pass raising this finding** is not an in-system reader of the text it is reviewing. @@ -319,9 +340,13 @@ wrong and is corrected here. Where the user's knob and the derived floor diverge, the provenance line **discloses both** rather than resolving them: ``` -floor N per ; hook reminder threshold M per workspace knob +floor N per { (level L), …}; hook reminder threshold M per workspace knob ``` +That is the **only** form. An earlier revision showed a single-story shorthand beside a +set-derived form and left two pinned-looking patterns for one line; a single-story cycle is the +one-element case of the form above, written the same way, so nothing has to choose between them. + **Every cycle records this, default or not** — an absent line must not be ambiguous between "the default applied" and "someone forgot". @@ -333,7 +358,11 @@ A single-story cycle is the degenerate case of the same form. The knob clause is present **whenever the file exists**, not only when its value differs: a present-but-equal knob is still a fact about the workspace, and tying disclosure to divergence -would make an absent clause ambiguous between "no knob" and "a knob that agreed". **Two cases need +would make an absent clause ambiguous between "no knob" and "a knob that agreed". **Where the file +exists but carries no usable value** — empty, non-numeric, zero, negative, or unreadable — the +clause records that rather than a number: `hook reminder threshold: file present, value unusable +(hook default 3 applies)`. The hook already ignores such a value (`codex-gate.sh:124-127`), so the +disclosure describes what the hook will actually do, which is the whole point of recording it. **Two cases need their own forms**, because the common one has nowhere to put them: an artifact citing **no story** records `floor 3 (no story cited)`, and a cited **unprofiled** story records `floor 3 per (unprofiled)`. Both keep the derivation @@ -402,10 +431,10 @@ item 10's failure: | Shape | Check | Treatment | |---|---|---| -| **absent** | the slot file does not exist | uncomputable; report which pass numbers are missing | +| **absent** | the slot file does not exist | uncomputable; report which pass numbers are missing. **No recovery**: a pass that left no file cannot be reconstructed, and re-running it would produce a different pass, not that one | | **partial** | some pass slots present, others not | compute historical tells over the passes present and **name the missing pass numbers** — a trend over an unstated subset reads as a trend over the cycle | -| **malformed** | fails any pass-acceptance check — terminator exactly `END OF FINDINGS ( total)`, count matching the finding lines, nothing but finding lines, and **each line structurally well formed**: right field count, no empty or whitespace-only severity field. §5's reader tolerates the severity *token*'s casing and nothing else; every structural failure is INCOMPLETE | treat as absent for that pass, **never** as zero findings. Distinct from *unreadable*: a malformed file is a review that ran and wrote badly, so **if its `sessionId` is still to hand** the pass may be re-runnable — and where it is not, say so rather than implying a recovery route that no longer exists | -| **unreadable** | exists but cannot be opened or decoded | treat as absent; report the OS-level cause, since permissions and a full disk need different fixes | +| **malformed** | fails any pass-acceptance check — terminator exactly `END OF FINDINGS ( total)`, count matching the finding lines, nothing but finding lines, and **each line structurally well formed**: right field count, no empty or whitespace-only severity field. §5's reader tolerates two things about the severity field and nothing else — its casing, and a non-empty unrecognized token, which reads as `MAJOR`; every *structural* failure is INCOMPLETE | treat as absent for that pass, **never** as zero findings. Distinct from *unreadable*: a malformed file is a review that ran and wrote badly, so **if its `sessionId` is still to hand** the pass may be re-runnable — and where it is not, say so rather than implying a recovery route that no longer exists | +| **unreadable** | exists but cannot be opened or decoded | treat as absent; report the OS-level cause, since permissions and a full disk need different fixes. **Recovery is environmental** — fix the cause and re-read; the file may be intact | | **stale** | the slot's cycle discriminator is absent or is another cycle's | treat as absent **and report its presence** — a foreign curve is worse than no curve | **Stale is only partly detectable, and the text says which part.** Bare numbered slots carry no @@ -417,12 +446,22 @@ file you are about to write is not a previous cycle's. **The discriminator is a change to the slot grammar, not an addition beside it**, and §5 must say so: today the grammar is exactly `gate-a-spec-pass-

`, `gate-a-plan-pass-

` and -`gate-b--pass-

`, and an infixed name satisfies none of them. The shipped text -widens each to admit an optional per-cycle infix — `gate-a-spec--pass-

` — and says the -infix is **recommended whenever a bare slot is already occupied**, which is the situation that -destroyed a previous cycle's findings file in this very cycle. Calling it a convention on top of -an unchanged grammar, as an earlier revision did, would leave every reader with two rules and no -way to tell which governs. Neither makes a bare-slot file's origin +`gate-b--pass-

`, and an infixed name satisfies none of them. **The shipped text replaces each grammar with one that +admits an optional per-cycle infix** — `gate-a-spec[-]-pass-

` and its two siblings — so +there is exactly one rule and bare slots stay valid. Three properties come with it: +- **The infix is required, not recommended, when the bare slot is occupied.** That is the case + that destroyed a previous cycle's findings file in this very cycle: the delete-before-write rule + removed a bare slot belonging to a closed cycle. +- **Refuse rather than overwrite.** Where the target the cycle would write is occupied by a file + the cycle did not write, the pass stops and names the collision. §5 already says a target + surviving deletion is a stop; this extends it to a target that should not be deleted at all. +- **The infix identifies a cycle, not a story or a branch**, and two cycles must not share one. + A repeated infix reproduces exactly the collision it exists to prevent, so it is derived from + the cycle identifier §2.1 already defines. + +**Absent infix does not mean stale.** Bare slots remain valid, so a bare file is *unattributable* +rather than foreign — the stale row above treats it as absent because its origin cannot be +established, not because it is known to belong elsewhere. Neither makes a bare-slot file's origin recoverable after the fact, and the report says so rather than implying detection. (The discriminator convention also prevents the destruction this cycle caused once: deleting a bare slot destroyed a previous cycle's findings record, which the dispositions companion happened to @@ -543,7 +582,7 @@ the conditions this change **touches or could drop**; the plan carries the repla | # | Passage | What it currently requires | Disposition | |---|---|---|---| -| 1 | "**Both gates are a LOOP with a HARD FLOOR**" | min 3 per run; Blocker/Major only; counted by the hook; hook cannot read findings nor tell the spec run from the plan run; a satisfied count is not a clean review; a TodoWrite per pass; fix Blocker/Major after each; final pass clean; keep going until clean or clearly stuck; only early exit below floor is a zero-finding pass; don't manufacture findings; Codex advisory, validate before applying; dismissed finding gets a one-line why | floor **value moved** to the profile predicate; **every other requirement kept verbatim**, including the counter-is-not-evidence caveats, which this design leans on harder than before | +| 1 | "**Both gates are a LOOP with a HARD FLOOR**" | min 3 per run; Blocker/Major only; counted by the hook — a rule §5.1 does not contradict but does sharpen, since the hook counts *calls* while the floor counts *logical passes*, and §5 already says the counter is not evidence; hook cannot read findings nor tell the spec run from the plan run; a satisfied count is not a clean review; a TodoWrite per pass; fix Blocker/Major after each; final pass clean; keep going until clean or clearly stuck; only early exit below floor is a zero-finding pass; don't manufacture findings; Codex advisory, validate before applying; dismissed finding gets a one-line why | floor **value moved** to the profile predicate; **every other requirement kept verbatim**, including the counter-is-not-evidence caveats, which this design leans on harder than before | | 2 | The early-exit sentence at `:79/:279` | the only exit below the floor is a zero-finding pass | **kept**, with "below 3" **changed** to "below the floor" | | 3 | The incomplete-pass rule at `:236/:421` | don't act on a partial list; don't count it toward the floor; don't read "no Blocker/Major visible" as clean | **kept**, "3-pass floor" **changed** to "the floor" | | 4 | "What a loop absorbs, and what stops it" | in-set findings absorbed, acted on by severity; ancestry decides *where*, never *what you do*; ancestry grants no Minor a repair round; assigned fix set fixed **before** the pass; unclear membership resolves **outside**; an out-of-set correction stops the loop even opening no new question; a new structural/contract question stops it; **novelty not size**; **when a finding is both, novelty wins**; stopping is **not an exit** — floor, filter and clean-final-pass all stand | all **kept**; the scope stop is **moved** into §2's ordering as one of three suspensions and gains the decline qualification | @@ -555,7 +594,7 @@ the conditions this change **touches or could drop**; the plan carries the repla | 10 | "The Gate-B triviality skip needs two independent conditions" | behaviourally trivial **and** `max(risk, security)` is 0; an eligible profile never makes a behaviour-changing diff skippable; skip reason in the commit body, not the profile log; a skip removes the review **never the evidence**; profiled story runs the battery and lands its evidence entry; unprofiled records reason and battery result and nothing more | all **kept**; checked against the floor predicate, which uses the same level-0 test for a different purpose — **deliberately not merged**, since one relaxes review count and the other removes review entirely | | 11 | "A cycle citing several stories" | battery once per cycle; each cited profiled story satisfies its own mode with its own named evidence entry; an unprofiled cited story owes no entry; lens sets **unioned**; skip-eligible only if **every** cited story is | all **kept**; the floor **added** as a new dimension under the same unanimity shape | | 12 | "Gate A — Spec, then plan (TWO runs, each its own 3-pass loop)" (`:300/:485`) | two separate runs, each its own loop; run on the spec before `writing-plans` and the plan before executing; one broad prompt re-run each pass; the brainstorming directive opens it; coverage floor not a cage; every finding with severity and confidence; `NO FINDINGS` when clean; mechanical settle before each read pass | all **kept**; "3-pass loop" **changed** to reference the derived floor, and each of the two runs derives it independently | -| 13 | "Gate B — Code" (`:335/:519`) | tests green before commit; skip only trivial changes; check against AGENTS.md; re-review after every fix; a fix changes the diff and the hook invalidates the prior pass, **which is where the 3 come from**; the standing falsification lens; same coverage rule as Gate A | all **kept**; the "where the 3 come from" clause **changed** to derive from the floor — the *reasoning* (re-review after every fix) is what generates the number, and it survives a variable floor unchanged | +| 13 | "Gate B — Code" (`:335/:519`) | tests green before commit; skip only trivial changes; check against AGENTS.md; re-review after every fix; a fix changes the diff and the hook invalidates the prior pass, **which is where the 3 come from**; the standing falsification lens; same coverage rule as Gate A | all **kept**; the "where the 3 come from" clause **changed**. Precisely: re-review-after-every-fix generates however many passes repairs require, which is a *lower bound of one per fix* and not the number 3; the floor is a separate minimum. Both survive a variable floor, and the passage stops implying that one produces the other | | 14 | "**Severity:** Blocker … Major … Minor · Nit" | Blocker = wrong/unsafe/breaks invariant; Major = design flaw → rework; both must resolve; Minor and Nit collect, never iterate | **kept**, and **narrowed**: the reachability test is added as the classifier, so a finding that would once have been Major on subject alone can now be Minor. That narrowing is deliberate and is the change; it is recorded here as a **changed** condition, not a kept one | | 15 | "Scope, and it is narrow" | the form supplies no permission; never the answer to a below-floor pass, an unclean final pass, a STOP, a Gate-A/B obligation or a profile-derived evidence requirement; authorizes nothing any mandatory rule requires; mandatory is not limited to this file; not for things never owed | all **kept without exception**; §2.1 **adds** the decline as a distinct record type on the same transport and states the distinction, precisely so this passage is not weakened | | 16 | "Recording a human exception" | the three-line form; which commit carries it; decisions made after a commit closed; an empty commit is a legitimate destination; several records accumulate; the record is an **unverified assertion** | all **kept**; the decline record **reuses the transport** and adopts the same unverified-assertion honesty | @@ -584,6 +623,7 @@ statements does this diff falsify?" — a change makes sentences wrong in files | `:84` | "Gate A's floor is unchanged at every level" | **directly contradicted** — a sentence specifically about profile levels | | `:86` | "moves the 3-pass floor" | same as README | | `docs/getting-started.md:44-45` | "If the Gate-A floor wasn't met, the hook says so right when execution starts" | the hook reports against **its own** threshold, so at level 0 it can say the floor was not met when nothing further is owed — the reminder is right to fire and the sentence is wrong about what it means | +| `docs/getting-started.md:58-59` | the real commit replaces the WIP on `✓ Codex Gate B satisfied (3/3 cycle, 3 on current fingerprint)` | the trigger is a **clean final pass at the derived floor**, which at level 0 is `1/1`; a reader waiting for `3/3` waits for a message that will not come | | `docs/coding-workflow.md:79-80` | the axes "never subtract any: **Gate A's floor** and the baseline questions are the same at every level" | **directly contradicted**, and missed until Gate-A pass 3 — the same claim as `:84` in a second file | **The rewording is honest rather than cosmetic.** README and getting-started describe the knob as @@ -672,7 +712,11 @@ branch's closing commits**, which do not exist until the cycle closes — so the against the `WIP:` snapshot during the cycle, and **re-read against the content the close will carry — the staged tree plus the message about to be written — rather than against a commit that does not exist yet.** The amend *is* the closing act, so "re-read the final commit before closing" -is circular; what is checkable beforehand is exactly what the amend will commit. If the amend changes what they observed, the clean pass no longer covers what is +is circular. **What that check establishes is bounded and the text says so:** the index can change +between the read and the commit, and `git commit -a` or a path argument commits content the index +did not hold, so the observation is of *the intended content at the moment it was read*, not a +guarantee about the resulting commit. Narrowing the gap is what the hook's own content fingerprint +already does; this check does not duplicate it and does not claim to. If the amend changes what they observed, the clean pass no longer covers what is being committed: fix, re-review, close on the entry that pass validated. **Instrument discipline, from this story's own evidence.** Two defects in the `fic2` decision @@ -710,8 +754,12 @@ general reconciliation of the two copies' 192-line divergence beyond the one sea started. The conservative value is the point; "re-derive" was the wrong instruction and did not do what the sentence beside it claimed. An earlier revision proposed git timestamps plus file mtime as a start marker; that does not survive checkout, copy or clock skew, so no marker is claimed and the - fallback carries the case. This also disposes of this design's own Gate A, which runs under the - old rules. + fallback carries the case. This also disposes of this design's own Gate A, which runs under the old rules — **with one + consequence worth stating, since two story criteria require this branch to demonstrate the + provenance line and the curve.** Those are obligations on *what this branch commits*, not claims + about which rules its Gate-A loop was reviewed under. A loop reviewed under the old rules can + still record its curve and its floor, because recording is an act of the commit rather than a + rule the passes were judged by. Nothing here is retroactive and nothing here is exempt. - **Downstream adoption has no shipping commit.** The template travels into other repositories whose history does not contain this change, so "from the commit that ships them" is a statement about *this* repo. Downstream, the rules bind from the `/workflow-init` run that **actually writes them**, which is @@ -728,8 +776,9 @@ general reconciliation of the two copies' 192-line divergence beyond the one sea wherever the text does not settle the question. - **The gate-off surface — the routes known today, not a complete list.** An enumeration read as complete guarantees the routes it omits, which is the AGENTS.md Don't in its own words; this - one is a floor. Beyond the four below, the pre-existing routes are unchanged and not created - here: falsifying evidence entries, misreporting a battery, silencing reminders with + one is a floor. **One route below is created by this design and is named as such**: a stated + floor the cited set does not license did not exist before, because before this change there was + no derived floor to state. The others are pre-existing and unchanged: falsifying evidence entries, misreporting a battery, silencing reminders with `codex-gate.off`, or simply not running a pass and reporting that it ran. Deleting the marker mechanism removed the camouflage path and every crash-recovery path. What remains: **writing a provenance line the cited set does not license; omitting a higher-risk cited story from the set; minting or editing a From 93712ba66baa82670546f1c0ebbe3aede6382baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 18:45:16 +0200 Subject: [PATCH 021/117] =?UTF-8?q?docs(spec):=20revision=208=20=E2=80=94?= =?UTF-8?q?=20split=20the=20condition=20inventory=20out;=20fix=20five=20de?= =?UTF-8?q?sign=20Blockers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 7: 32 findings, 9 BLOCKER, 20 MAJOR. Second mandatory two-tell stop (Blockers 3->9; instrument clustering 41%). Daniel chose SPLIT. §6.2 was the generator. Four of the nine Blockers were rows in that table contradicting the design they existed to account for -- including one where I fixed §5's slot-grammar rule in revision 7 and left the row describing it saying the opposite. A nineteen-row restatement of nineteen CLAUDE.md passages is a second copy, and it drifts every time the design moves. That is the third site of one defect class in this cycle: the story's criteria (five Blockers), then the spec's own accounting table (four more). Now split as Daniel specified: the spec keeps the METHOD and the passage list -- the stable, checkable half -- and the row-by-row kept/moved/dropped dispositions move to their own artifact, produced ONCE against frozen final text and gated before any replacement text is written. The pass-2 tension is recorded rather than resolved by hindsight: pass 2 demanded the inventory be in the spec when the alternative was deferral-to-plan, which was correct; the frozen-text artifact is a third option neither pass had, and it preserves what pass 2 required -- the accounting exists and is reviewed before approval. What moves is when it is produced, not whether. The five design Blockers, all real: The hold rule contradicted its own accepted branch -- the list said an answer makes a pass carrying the finding clean while the branch said cleanliness never changes. Resolved by naming what the rule gates: closure of the CYCLE, never a pass's cleanliness. The surfacing pass is not clean and never becomes clean; closure needs a subsequent clean pass, as it always did. The shipped hook message says a cycle MUST reach the hook's threshold, which at level 0 contradicts a legitimate one-pass close -- and "redundant warning" gave an agent no rule for choosing between two instructions, at the new floor's main success path. Both copies now state the precedence: the derived floor controls closure, the hook ratio is a reminder that controls nothing, and a below-threshold reminder is noted and disregarded once the derived floor and ordinary rules are met. The hook's own text is NOT changed -- it lives in codex-gate.sh:933 and hook code is out of scope -- so the residual is named rather than implied. The provenance line had grown four pinned-looking forms. Now one grammar with explicit tokens for story-set entries, no-story, unprofiled, and knob absent/numeric/unusable, with one example per variant, and levels written as the numeral max(risk, security) yields rather than an axis word. The cycle identifier was derived from a gate plus a commit, which sibling worktrees and restarted loops can share, and whose WIP parent identifies the base rather than the reviewed artifact. It is now an immutable nonce generated at cycle start and recorded in history, with the reviewed commit tracked separately because the two answer different questions. And a Gate-B decline amended into the WIP body had no rule requiring the amend to carry the WIP prefix -- so recording a decline could have read to the hook as the cycle closing, resetting counters and discarding accumulated passes. The form is pinned, existing body records must be carried forward, and the non-WIP amend is reserved for final closure. The prediction is recorded verbatim in the dispositions file so pass 8 can be scored against it rather than remembered: roughly 7 of 29 Blocker/Major and 4 of 9 Blockers should go with §6.2. If it does not fall materially, the generator is elsewhere and the whole-artifact split is next. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-7 at .context/codex-reviews/gate-a-spec-rle-pass-{1..7}.md. Findings 27, 30, 54, 40, 33, 34, 32. Blockers 5, 2, 0, 4, 0, 3, 9. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- .../2026-08-16-canvas-a1-a5-dispositions.md | 17 ++ ...2026-08-28-review-loop-economics-design.md | 175 +++++++++++++----- 2 files changed, 149 insertions(+), 43 deletions(-) diff --git a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md index 64c81f0..8845586 100644 --- a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md +++ b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md @@ -348,3 +348,20 @@ error, no timeout, nothing in flight: the announcement simply replaced the act. the remainder of that cycle: during an active gate cycle a turn ends with the tool call actually issued, a message sent, or an explicit statement that something is blocking — and noticing the turn ending with the call not in flight *is* that statement. + +**Prediction recorded 2026-08-28, before the pass that tests it.** Gate-A pass 7 of the +review-loop-economics cycle found that the spec's own nineteen-row condition-inventory table (§6.2) +had become the loop's largest finding source: **7 of 29 Blocker/Major, including 4 of 9 Blockers**, +every one a row contradicting the design it existed to account for. The table was split out — +method and passage list stay in the spec, the row-by-row dispositions move to an artifact produced +once against frozen text and gated before implementation. + +Verbatim prediction, so it can be scored rather than remembered: **pass 8 should lose roughly 7 of +29 Blocker/Major and 4 of 9 Blockers to the §6.2 removal. If pass 8 does not fall materially, the +generator is elsewhere and the whole-artifact split becomes the live candidate.** + +Worth keeping either way: this is the third site of one defect class in a single cycle — a +restatement that must track a moving original. It appeared in the story's acceptance criteria +(five Blocker occurrences), then inside the spec's own accounting table (four more). The lesson is +not "write the table more carefully"; it is that a second copy of a moving thing drifts, and the +remedy is to produce it once against something that has stopped moving. diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index b955d71..0d28ed7 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 7, after Gate-A passes 1-6 (27, 30, 54, 40, 33, 34 findings) +**Date:** 2026-08-28 · **Revision:** 8, after Gate-A passes 1-7 (27, 30, 54, 40, 33, 34, 32) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. @@ -122,9 +122,14 @@ duty attaches to it. **Stated at every rule it modifies, in both copies, and at no rule it does not** — the criterion's two halves: -1. **"a surfaced finding stays open"** — modified: the finding stops being open when answered. -2. **"no pass carrying a surfaced finding counts as clean"** — modified: it means *unanswered* - findings; an answered one is governed by its severity. +1. **"a surfaced finding stays open"** — modified: the finding stops *awaiting a decision* when + answered. It does not thereby become resolved; an accepted Blocker or Major is open until + repaired. +2. **"no pass carrying a surfaced finding counts as clean"** — modified in **what it gates, not + in what it says about any pass.** It gates **closure of the cycle** while a finding is + unanswered. **The pass that surfaced the finding is not clean and never becomes clean** — a + pass's cleanliness is a fact about what that pass found, and no later event rewrites it. + Closure requires a *subsequent* clean pass at or above the floor, as it always did. 3. **"the loop resumes on whatever the user decides"** — modified: resumption is the hold *ending*, made explicit rather than left to inference. The **Blocker/Major-resolve duty is not modified and carries no qualification**. §1.2 scopes it to @@ -158,7 +163,15 @@ loop is running.** A Gate-A spec loop's record lands in the spec commit, which i loop closes. **A Gate-B cycle is the exception, and it is an exception because §5 already made it one**: the `WIP:` commit exists precisely so a cycle has a commit while it is still running, so a Gate-B decline lands there by amend immediately and is restated by the closing amend. The -timing claim above is therefore about Gate A only. During the loop the decision lives in the working record — the dispositions companion, which +timing claim above is therefore about Gate A only. + +**That mid-cycle amend must visibly keep the `WIP:` prefix in its own `-m`.** The hook recognizes +a WIP commit from the command's message argument, so an amend that omits it **reads as the cycle +closing**: counters reset and the accumulated passes are discarded, while the agent believed it +was only recording a decline. The shipped text therefore pins the form — +`git commit --amend -m "WIP: "` — requires every existing body record to be carried +forward rather than replaced, and reserves the **non-`WIP:` amend for final closure alone**. This +is a hazard §5's Mechanics already names, reached by a new route. During the loop the decision lives in the working record — the dispositions companion, which §5 already calls the advisory working copy — and **becomes the record when the commit is written**. Nothing about the decline's effect waits for the commit; what the commit provides is durability past the session. The human-exception machinery already solves this transport the same way: "an ungated change records it in that @@ -185,11 +198,20 @@ that was declined — makes it a **new finding, and the hold applies**. Where sa it is likewise new, which costs a question and never a silent release. This mirrors §5's existing treatment of unclear set membership. -**A decline binds one cycle only.** The cycle identifier is what the cycle is already keyed to and -what no two concurrent cycles share: **the gate and the artifact commit it reviews** — for Gate A, -the loop name plus the commit the reviewed artifact was read at; for Gate B, the `WIP:` commit's -parent. Two loops on one branch therefore never collide, and a resumed loop recovers its own -identifier from that same source rather than inventing one. The record **has no effect in any +**A decline binds one cycle only, and what binds it is a nonce.** Deriving the identifier from the +gate plus a commit — an earlier revision's answer — does not work: sibling worktrees and a +restarted loop can share a loop name and a base commit, and a `WIP:` parent identifies the *base* +rather than the artifact reviewed. So: +- **The cycle nonce** is generated once at cycle start, immutable for the cycle's life, and + **recorded in history** — in the first commit body the cycle writes, and in every record it + carries thereafter. Any collision-resistant value serves; it must be safe as a slot infix, so it + is restricted to `[a-z0-9]{4,16}`. +- **The reviewed artifact is tracked separately.** The nonce answers "which cycle is this"; a + commit or tree id answers "did both branches of this pass see the same thing" (§5.1). + Conflating them left the earlier definition both non-unique and unable to do §5.1's job. +- **A resumed cycle recovers its nonce from the recorded history**, never by re-deriving it. A + cycle that cannot recover one has no identity and starts a new cycle — which costs passes rather + than silently inheriting a decline. The record **has no effect in any later cycle**, even though the record itself persists into commit and squash history. Without that bound, a decline recorded once would silently release the same finding in every future cycle, which nobody decided. @@ -339,13 +361,34 @@ made it block or made an agent owe a pass. "Appears only inside note messages" w wrong and is corrected here. Where the user's knob and the derived floor diverge, the provenance line **discloses both** rather than resolving them: +**One grammar, every case inside it.** Earlier revisions grew a separate form per case — a +single-story shorthand, a no-story form, an unprofiled form, an unusable-knob form — four +pinned-looking patterns for one line, unparseable by the P8 reader that must consume it. + +``` +floor per ; hook reminder threshold + + := "none" the artifact cites no story + | "{" ("," )* "}" + := " (level " <0|1|2> ")" a profiled story + | " (unprofiled)" a cited story with no profile + := "absent" no codex-gate.floor file + | a usable value + | "unusable" file present; empty, non-numeric, or < 1 +``` + +Every case is one production, so a mixed profiled-and-unprofiled set has a canonical form rather +than falling between patterns. One example per variant: + ``` -floor N per { (level L), …}; hook reminder threshold M per workspace knob +floor 1 per {docs/superpowers/stories/A-story.md (level 0)}; hook reminder threshold absent +floor 3 per {A-story.md (level 0), B-story.md (level 2)}; hook reminder threshold 3 +floor 3 per {A-story.md (level 0), C-story.md (unprofiled)}; hook reminder threshold 1 +floor 3 per none; hook reminder threshold unusable ``` -That is the **only** form. An earlier revision showed a single-story shorthand beside a -set-derived form and left two pinned-looking patterns for one line; a single-story cycle is the -one-element case of the form above, written the same way, so nothing has to choose between them. +Levels are the numeral `max(risk, security)` yields — `0`, `1`, `2` — never an axis word like +`high`, which an earlier revision mixed in and which does not identify the level a reader needs. **Every cycle records this, default or not** — an absent line must not be ambiguous between "the default applied" and "someone forgot". @@ -368,6 +411,25 @@ nowhere to put them: an artifact citing **no story** records `floor 3 (no story cited **unprofiled** story records `floor 3 per (unprofiled)`. Both keep the derivation legible to a reader who otherwise cannot tell an unprofiled cycle from a missing line. +**Both copies state the precedence explicitly, because a reminder is not an instruction and the +difference has to be actionable.** The order, shipped as text: + +> **The derived floor controls whether the cycle may close. The hook's ratio is a reminder +> threshold and controls nothing.** Where the derived floor and the ordinary closure rules are +> satisfied, a below-threshold hook reminder is **noted in the pass report and disregarded** — it +> is not a pass the cycle owes. + +Without that, a level-0 cycle meets its obligation and is then told by a shipped message that it +"MUST reach a minimum of 3 passes", and the agent has two instructions and no rule for choosing — +which is prompt-standards item 7's contradiction case at the new floor's *main success path*. + +**The hook's own message text is not changed here**, because it lives in +`plugins/dev-workflow/hooks/codex-gate.sh:933` and hook code is out of scope by decision. That +leaves a **known residual, named rather than implied**: the message will keep saying "MUST" at a +threshold the cycle does not owe. What makes it tolerable is the precedence rule above plus +invariant 1 — the hook is advisory and exits 0 regardless — not the reminder being harmless on +its own. + **The consequence, accepted rather than engineered around: a level-0 cycle closing at one pass draws a hook reminder saying "below floor (1/3)".** That reminder is **the invariant working**, not a defect — AGENTS.md invariant 2: *"On uncertainty, fire. A missed commit (false ✓) is the @@ -575,34 +637,61 @@ at `IMPORTANT`" — which cites a historical pass rather than stating a rule, an **Every site maps to a §6.2 row**, so no site is edited without an accounting: `:72` → row 1, `:79` → row 2, `:126` → row 5, `:236` → row 3, `:300` → row 12, `:335` → row 13, `:403` → row 9. -### 6.2 Condition inventory - -What each passage's existing prose requires, and the disposition of each requirement. Listed are -the conditions this change **touches or could drop**; the plan carries the replacement wording. - -| # | Passage | What it currently requires | Disposition | -|---|---|---|---| -| 1 | "**Both gates are a LOOP with a HARD FLOOR**" | min 3 per run; Blocker/Major only; counted by the hook — a rule §5.1 does not contradict but does sharpen, since the hook counts *calls* while the floor counts *logical passes*, and §5 already says the counter is not evidence; hook cannot read findings nor tell the spec run from the plan run; a satisfied count is not a clean review; a TodoWrite per pass; fix Blocker/Major after each; final pass clean; keep going until clean or clearly stuck; only early exit below floor is a zero-finding pass; don't manufacture findings; Codex advisory, validate before applying; dismissed finding gets a one-line why | floor **value moved** to the profile predicate; **every other requirement kept verbatim**, including the counter-is-not-evidence caveats, which this design leans on harder than before | -| 2 | The early-exit sentence at `:79/:279` | the only exit below the floor is a zero-finding pass | **kept**, with "below 3" **changed** to "below the floor" | -| 3 | The incomplete-pass rule at `:236/:421` | don't act on a partial list; don't count it toward the floor; don't read "no Blocker/Major visible" as clean | **kept**, "3-pass floor" **changed** to "the floor" | -| 4 | "What a loop absorbs, and what stops it" | in-set findings absorbed, acted on by severity; ancestry decides *where*, never *what you do*; ancestry grants no Minor a repair round; assigned fix set fixed **before** the pass; unclear membership resolves **outside**; an out-of-set correction stops the loop even opening no new question; a new structural/contract question stops it; **novelty not size**; **when a finding is both, novelty wins**; stopping is **not an exit** — floor, filter and clean-final-pass all stand | all **kept**; the scope stop is **moved** into §2's ordering as one of three suspensions and gains the decline qualification | -| 5 | "Recognizing \"clearly stuck\"" | read the Blocker curve **across passes**, not one total; one low count is a snapshot not a plateau; **neither curve measures coverage**; three conditions **together**, a missing one means keep going; six-plus passes is where the field saw one and is **not a threshold**; an affirmative coverage judgement must be **stated**; a known unreviewed area **forbids the exit**; Blocker/Major **regenerating** across genuine repairs; **clean completion takes precedence**; below the floor nothing closes; zero-finding pass the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | all **kept**; the precedence sentence **moved** into §2's table and preserved verbatim there; the "pass 1" clause **changed** to "below the floor" per §6.1 | -| 6 | "Surfacing does not close the cycle" | surface **with the finding still open**; the resolve rule is **not waived**; **no pass credited clean**; loop resumes on the user's decision | all **kept**, and the fourth is what carries the change: the hold lasts **until the user answers**, and any answer ends it. The decline therefore touches **only** the third condition, and only for the finding decided. The resolve rule is **not waived by anything here** — a declined finding is out-of-set, so that rule never attached to it, and an accepted Blocker/Major still blocks clean until resolved (§1.2, §2.1) | -| 7 | "From pass 4 onward every pass report carries three lines" | duty **activates at pass 4** and binds every pass after; carrier is **your status report**, never the Codex reply, never the findings file; the three line contents; all five tell definitions; **any two mandatory, not discretionary**; report the tells and hand the decision to the user; the stuck reading is **not a precondition** | all **kept**; §5 **adds** the unavailable-history behaviour, which the passage currently leaves undefined | -| 8 | "The two rules above do not compete" | absorb decides *a finding's* scope; the stuck reading decides whether *the loop* converges; neither overrides the other; a small in-set correction is not itself evidence of a plateau | **kept**, **moved** to reference §2's ordering rather than restate the relationship | -| 9 | "Lenses are different questions, not more passes" (`:403/:582`) | lens sets add questions, not passes; the 3-pass floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are **unchanged** by lenses | **kept**; "3-pass floor" **changed** to "the floor", and the unchanged-by-lenses claim now reads against a derived floor | -| 10 | "The Gate-B triviality skip needs two independent conditions" | behaviourally trivial **and** `max(risk, security)` is 0; an eligible profile never makes a behaviour-changing diff skippable; skip reason in the commit body, not the profile log; a skip removes the review **never the evidence**; profiled story runs the battery and lands its evidence entry; unprofiled records reason and battery result and nothing more | all **kept**; checked against the floor predicate, which uses the same level-0 test for a different purpose — **deliberately not merged**, since one relaxes review count and the other removes review entirely | -| 11 | "A cycle citing several stories" | battery once per cycle; each cited profiled story satisfies its own mode with its own named evidence entry; an unprofiled cited story owes no entry; lens sets **unioned**; skip-eligible only if **every** cited story is | all **kept**; the floor **added** as a new dimension under the same unanimity shape | -| 12 | "Gate A — Spec, then plan (TWO runs, each its own 3-pass loop)" (`:300/:485`) | two separate runs, each its own loop; run on the spec before `writing-plans` and the plan before executing; one broad prompt re-run each pass; the brainstorming directive opens it; coverage floor not a cage; every finding with severity and confidence; `NO FINDINGS` when clean; mechanical settle before each read pass | all **kept**; "3-pass loop" **changed** to reference the derived floor, and each of the two runs derives it independently | -| 13 | "Gate B — Code" (`:335/:519`) | tests green before commit; skip only trivial changes; check against AGENTS.md; re-review after every fix; a fix changes the diff and the hook invalidates the prior pass, **which is where the 3 come from**; the standing falsification lens; same coverage rule as Gate A | all **kept**; the "where the 3 come from" clause **changed**. Precisely: re-review-after-every-fix generates however many passes repairs require, which is a *lower bound of one per fix* and not the number 3; the floor is a separate minimum. Both survive a variable floor, and the passage stops implying that one produces the other | -| 14 | "**Severity:** Blocker … Major … Minor · Nit" | Blocker = wrong/unsafe/breaks invariant; Major = design flaw → rework; both must resolve; Minor and Nit collect, never iterate | **kept**, and **narrowed**: the reachability test is added as the classifier, so a finding that would once have been Major on subject alone can now be Minor. That narrowing is deliberate and is the change; it is recorded here as a **changed** condition, not a kept one | -| 15 | "Scope, and it is narrow" | the form supplies no permission; never the answer to a below-floor pass, an unclean final pass, a STOP, a Gate-A/B obligation or a profile-derived evidence requirement; authorizes nothing any mandatory rule requires; mandatory is not limited to this file; not for things never owed | all **kept without exception**; §2.1 **adds** the decline as a distinct record type on the same transport and states the distinction, precisely so this passage is not weakened | -| 16 | "Recording a human exception" | the three-line form; which commit carries it; decisions made after a commit closed; an empty commit is a legitimate destination; several records accumulate; the record is an **unverified assertion** | all **kept**; the decline record **reuses the transport** and adopts the same unverified-assertion honesty | -| 17 | "Changing a profile" | the pass proposes the complete resulting header; the human confirms, in both directions; an agent never moves it alone; on confirmation correct the header and append one profile-log line; any axis change voids every prior override; `+abuse-path` follows the current security value; passes already run under the lower profile keep counting; only the final clean pass must run under the current profile; inside an active Gate-B cycle fold the edit into the `WIP:` snapshot by amend | all **kept**; §4.2 **adds** the floor's behaviour under a moving profile, which composes from the last two requirements and introduces no new rule | -| 18 | The findings-slot naming paragraph (`` is `gate-a-spec-pass-

`, …) | the three slot names; one finding per line; the severity token set; delete every target and confirm gone before each call; one pass at a time, since the slot name has no invocation-unique component and two concurrent calls on one slot race | all **kept**; §5 **adds** the per-cycle discriminator as a *convention* on top, not a change to the grammar — the three names remain as specified, and a discriminated slot is one of them with an infix, which existing practice already contains (`gate-b-spec-pr15-pass-1`) | -| 19 | "On squash-merge, copy every evidence entry…" | every evidence entry and human-exception record in the range copied into the squash body; nothing performs or checks the carry; a disagreement between copies is a copying error to fix, not to choose between | **kept**; three record types **added** | - ---- +### 6.2 The accounting method, and where the dispositions live + +**Split out of this spec after Gate-A pass 7** (Daniel, 2026-08-28). The row-by-row inventory +lived here through revisions 2–7 and became the largest single source of findings in the loop: +**7 of 29 Blocker/Major at pass 7, including 4 of 9 Blockers** — every one a row contradicting the +design it existed to account for, because a nineteen-row restatement of nineteen `CLAUDE.md` +passages is a second copy, and it drifts each time the design moves. That is the same defect this +change already removed from the story's acceptance criteria, reproduced inside the spec. + +**The pass-2 tension, recorded honestly rather than resolved by hindsight.** Gate-A pass 2 raised a +Blocker demanding the inventory live *in this spec* rather than being deferred to the plan — +correct, because deferring the thing that constitutes compliance while claiming compliance is the +failure the AGENTS.md Don't describes. **The frozen-text artifact is a third option neither pass +had on the table**, and it preserves what pass 2 actually required: the accounting exists, and is +reviewed, *before the replacement text is approved*. What changes is when it is produced, not +whether. + +**Method — this is the stable half and it stays here.** For each passage below: list what its +existing prose requires, then mark each requirement **kept**, **moved**, or **deliberately +dropped**. A requirement that is neither kept nor explicitly dropped is a dropped condition, which +is exactly what the Don't exists to catch, and no passage may be rewritten without its accounting. + +**Where the dispositions are produced and gated.** In one artifact, +`docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md`, written **once against +the frozen final text of this spec** — not maintained across revisions — and **reviewed before any +replacement text is written.** The plan names that point explicitly as a gate it does not pass +without. Producing it against moving text is what generated the findings this split removes. + +**The passages this change rewrites — the list is the checkable part.** Each is present exactly +once in both §5 copies: + +| # | Passage | Rewritten by | +|---|---|---| +| 1 | "**Both gates are a LOOP with a HARD FLOOR**" | part 1 — the floor statement itself | +| 2 | The early-exit sentence at `:79/:279` | part 1 — the zero-finding early exit | +| 3 | The incomplete-pass rule at `:236/:421` | part 1 — the incomplete-pass rule | +| 4 | "What a loop absorbs, and what stops it" | part 3 — the scope stop becomes an ordered suspension | +| 5 | "Recognizing \"clearly stuck\"" | part 3 — becomes a suspension; clean-completion precedence moves into the ordering | +| 6 | "Surfacing does not close the cycle" | part 3 — the hold mechanism §1.2 names | +| 7 | "From pass 4 onward every pass report carries three lines" | part 3 and §5 — two-tell stop, and Q6's unavailable-history behaviour | +| 8 | "The two rules above do not compete" | part 3 — superseded by the ordering, kept or retired explicitly | +| 9 | "Lenses are different questions, not more passes" (`:403/:582`) | part 1 — the lens-set sentence naming the floor | +| 10 | "The Gate-B triviality skip needs two independent conditions" | part 1 — adjacent profile-derived relaxation, checked for consistency | +| 11 | "A cycle citing several stories" | part 1 — gains the floor dimension under unanimity | +| 12 | "Gate A — Spec, then plan (TWO runs, each its own 3-pass loop)" (`:300/:485`) | part 1 — Gate A's per-run loop | +| 13 | "Gate B — Code" (`:335/:519`) | part 1 — Gate B's "where the 3 come from" | +| 14 | "**Severity:** Blocker … Major … Minor · Nit" | part 2 — the severity definition | +| 15 | "Scope, and it is narrow" | part 3 — must distinguish the human-exception form from the decline record | +| 16 | "Recording a human exception" | part 3 — the decline reuses this transport | +| 17 | "Changing a profile" | §4.2 — the floor under a moving profile | +| 18 | The findings-slot naming paragraph (`` is `gate-a-spec-pass-

`, …) | §5 — the slot grammar gains an optional per-cycle infix | +| 19 | "On squash-merge, copy every evidence entry…" | §5.1 — three record types added to the carry | + +**Nineteen passages.** The plan's conditions artifact carries one dispositioned entry per row +and is incomplete without all nineteen. ## 7. Prerequisite, rollout, and what this change falsifies @@ -688,7 +777,7 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). warns about. The design's stronger claim covers the gap: **no floor file is written on any path**, which §8's risk-path verification checks unconditionally. - **Parity verification across every changed rule**, per story criterion 7, covering **§6.1's - fourteen sites, §6.2's nineteen rows, and every rule §§2–5 newly insert — including the §10 + fourteen sites, §6.2's nineteen passages, and every rule §§2–5 newly insert — including the §10 residual disclosure and the §4.1 provenance forms, which story criteria require in **both** shipped copies and which a §5-scoped parity walk would otherwise miss.** The copies already differ on 192 lines, so parity cannot be asserted from a whole-section diff; the verification From 787dd3d04d98dab50295fa61f62366194d6f6844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Fri, 28 Aug 2026 19:01:56 +0200 Subject: [PATCH 022/117] =?UTF-8?q?docs(spec):=20revision=209=20=E2=80=94?= =?UTF-8?q?=20pass-8=20Blockers;=20the=20split's=20price,=20stated?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 8: 33 findings, 4 BLOCKER, 28 Blocker/Major. One tell present (findings 32->33), below the mandatory two. The prediction scored. §6.2 Blocker/Major fell 7 -> 2 and Blockers fell 9 -> 4, both as predicted. Total Blocker/Major did not move: 29 -> 28. Reported to the sparring session with that ambiguity intact rather than resolved in the split's favour. Four Blockers, three of them consequences of revision 8's own additions: §6.2's passage list omitted two passages revision 8 itself rewrote -- the delete-every-target rule, which now gains a refuse-on-collision case, and the baseSha/WIP/closing-amend block, which the WIP-amend rule and the new body records change. Rows 20 and 21 added, and the price of the split is now stated rather than discovered: once the dispositions are deferred, the passage list is the SOLE guard against a dropped condition, and the conditions artifact cannot catch what the list never named. The list is re-checked whenever the design adds a rule. The single provenance grammar was contradicted by prose forms left behind when it was introduced -- a no-story form, a colon-separated unusable clause, and an example using a bare axis word where the grammar requires a level numeral. Those are now marked superseded rather than sitting beside the grammar as apparent alternatives: every case is a production, and a form that does not parse is wrong. The pinned mid-cycle amend command was self-defeating: `-m "WIP: "` replaces the whole message, so recording one decline would erase the nonce and every earlier decline -- destroying the durability the record exists for. The spec now pins the required PROPERTY -- message begins WIP:, body contains every record it contained before plus the new one -- and notes which command forms satisfy it. And §1.2's closing inventory could be discharged by naming unreadable passes. §5's degraded-sensitivity answer governs tell computation, which is a reporting duty; it does not reach a precondition on closure. A cycle that cannot establish that every in-set Blocker and Major was resolved does not close -- "we cannot tell" and "it was resolved" are different states and only one permits closing. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-8 at .context/codex-reviews/gate-a-spec-rle-pass-{1..8}.md. Findings 27, 30, 54, 40, 33, 34, 32, 33. Blockers 5, 2, 0, 4, 0, 3, 9, 4. Blocker/Major 24, 22, 43, 31, 30, 26, 29, 28. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 71 ++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 0d28ed7..b35c294 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 8, after Gate-A passes 1-7 (27, 30, 54, 40, 33, 34, 32) +**Date:** 2026-08-28 · **Revision:** 9, after Gate-A passes 1-8 (27, 30, 54, 40, 33, 34, 32, 33) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. @@ -60,8 +60,15 @@ carries both reasons. Three pairs, one sentence, no ordering. Blocker or Major is not evidence that a previously surfaced one was resolved; the resolution is a separate fact, and the closing report names each in-set Blocker/Major and how it was resolved — **read from the per-pass findings files, which are the durable inventory**, not from - recall. Where those files are unavailable (§5's shapes), the report says which passes it could - not read rather than presenting a partial inventory as complete. (An earlier revision called this "definitional", which is the + recall. + **Where those files are unavailable (§5's shapes), naming the unreadable passes is a disclosure, + not a discharge.** §5's degraded-sensitivity answer governs the *tell computation*, which is a + reporting duty; it does not reach this duty, which is a **precondition on closure**. A cycle that + cannot establish that every in-set Blocker and Major was resolved **does not close** — it stops + and surfaces, exactly as it would for any unresolved Blocker, because "we cannot tell" and "it + was resolved" are different states and only one of them permits closing. That is the loose + firing direction applied where it belongs: the expensive outcome is a cycle closing over an + unresolved Blocker nobody could see. (An earlier revision called this "definitional", which is the describe-what-a-gate-proves failure: the clean-pass condition proves what the reviewer found *this* pass, not what happened to earlier findings.) **The decline does not qualify this duty and needs no exception in it** — a declined finding is out-of-set, so the duty never @@ -169,8 +176,14 @@ timing claim above is therefore about Gate A only. a WIP commit from the command's message argument, so an amend that omits it **reads as the cycle closing**: counters reset and the accumulated passes are discarded, while the agent believed it was only recording a decline. The shipped text therefore pins the form — -`git commit --amend -m "WIP: "` — requires every existing body record to be carried -forward rather than replaced, and reserves the **non-`WIP:` amend for final closure alone**. This +the amend must **retain the `WIP:` prefix in the message the command supplies** and **preserve the +existing body**. Those two together rule out the obvious `-m "WIP: "`, which replaces the +*whole* message and would erase the nonce and every prior decline — destroying the durability the +record exists for. The shipped text pins the property, not one command line: the resulting commit +message must begin `WIP:` and must contain every record the previous message contained, plus the +new one. (`--amend` with an edited full message, or `-F` with the previous body plus the addition, +both satisfy it; `-m` with a bare subject does not.) The **non-`WIP:` amend is reserved for final +closure alone**. This is a hazard §5's Mechanics already names, reached by a new route. During the loop the decision lives in the working record — the dispositions companion, which §5 already calls the advisory working copy — and **becomes the record when the commit is written**. Nothing about the decline's effect waits for the commit; what the commit provides is durability @@ -393,23 +406,21 @@ Levels are the numeral `max(risk, security)` yields — `0`, `1`, `2` — never **Every cycle records this, default or not** — an absent line must not be ambiguous between "the default applied" and "someone forgot". -**The floor is a property of the cited *set*, not of any one story**, because unanimity makes it -so: one `high` story among four level-0 ones yields 3 for the cycle, and `floor 3 per ` -would misattribute that to stories that did not cause it. The line therefore states **one floor, -then the set that produced it**, with each story's level — `floor 3 per {A (level 0), B (high)}`. -A single-story cycle is the degenerate case of the same form. - -The knob clause is present **whenever the file exists**, not only when its value differs: a -present-but-equal knob is still a fact about the workspace, and tying disclosure to divergence -would make an absent clause ambiguous between "no knob" and "a knob that agreed". **Where the file -exists but carries no usable value** — empty, non-numeric, zero, negative, or unreadable — the -clause records that rather than a number: `hook reminder threshold: file present, value unusable -(hook default 3 applies)`. The hook already ignores such a value (`codex-gate.sh:124-127`), so the -disclosure describes what the hook will actually do, which is the whole point of recording it. **Two cases need -their own forms**, because the common one has -nowhere to put them: an artifact citing **no story** records `floor 3 (no story cited)`, and a -cited **unprofiled** story records `floor 3 per (unprofiled)`. Both keep the derivation -legible to a reader who otherwise cannot tell an unprofiled cycle from a missing line. +**Why the set and not the story.** Unanimity makes the floor a property of the cited *set*: one +level-2 story among four level-0 ones yields 3 for the cycle, so an entry per story would +misattribute that 3 to stories that did not cause it. `` therefore carries the whole +set with each member's level, and a single-story cycle is its one-element case. + +**Why the knob clause is always present.** A present-but-equal knob is still a fact about the +workspace, and tying disclosure to divergence would make an absent clause ambiguous between "no +knob" and "a knob that agreed". `` has a production for each state, including `unusable` — +empty, non-numeric, or below 1 — which the hook already ignores (`codex-gate.sh:124-127`), so the +line describes what the hook will actually do. + +**The grammar above is the whole specification of this line.** Earlier revisions left prose forms +beside it — `floor 3 (no story cited)`, a colon-separated unusable clause, a bare axis word like +`(high)` where the grammar requires a level numeral — and those are **superseded, not alternatives**: +every case is a production above, and any form that does not parse under it is wrong. **Both copies state the precedence explicitly, because a reminder is not an instruction and the difference has to be actionable.** The order, shipped as text: @@ -689,9 +700,19 @@ once in both §5 copies: | 17 | "Changing a profile" | §4.2 — the floor under a moving profile | | 18 | The findings-slot naming paragraph (`` is `gate-a-spec-pass-

`, …) | §5 — the slot grammar gains an optional per-cycle infix | | 19 | "On squash-merge, copy every evidence entry…" | §5.1 — three record types added to the carry | +| 20 | "Before each call, delete every target file and confirm it is gone" (`:199/:386`) | §5 — the slot grammar gains an infix, and the rule gains a **refuse-on-collision** case where the target belongs to another cycle | +| 21 | The `baseSha` / WIP / closing-amend block (`:500-517`, template `:679-696`) | §2.1 — the mid-cycle amend must retain the `WIP:` prefix and carry forward existing body records; §4.1 and §5.1 add records the closing body must contain | + +**Twenty-one passages.** The conditions artifact carries one dispositioned entry per row and is +incomplete without all twenty-one. -**Nineteen passages.** The plan's conditions artifact carries one dispositioned entry per row -and is incomplete without all nineteen. +**Rows 20 and 21 were missing until Gate-A pass 8**, which is worth recording rather than quietly +repairing: revision 8 added the rules that rewrite them — the refuse-on-collision case and the +WIP-amend rule — and did not extend the list. **That is the price of this split, stated plainly: +once the dispositions are deferred, the passage list is the sole guard against a dropped +condition, and the conditions artifact cannot catch what the list never named.** The list must +therefore be re-checked whenever the design adds a rule, and the plan's gate reads it against the +final text rather than trusting it. ## 7. Prerequisite, rollout, and what this change falsifies @@ -777,7 +798,7 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). warns about. The design's stronger claim covers the gap: **no floor file is written on any path**, which §8's risk-path verification checks unconditionally. - **Parity verification across every changed rule**, per story criterion 7, covering **§6.1's - fourteen sites, §6.2's nineteen passages, and every rule §§2–5 newly insert — including the §10 + fourteen sites, §6.2's twenty-one passages, and every rule §§2–5 newly insert — including the §10 residual disclosure and the §4.1 provenance forms, which story criteria require in **both** shipped copies and which a §5-scoped parity walk would otherwise miss.** The copies already differ on 192 lines, so parity cannot be asserted from a whole-section diff; the verification From e8e5cfa11e6fa6c779268b245e2aded712845f43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 10:12:24 +0200 Subject: [PATCH 023/117] =?UTF-8?q?docs(spec):=20revision=2010=20=E2=80=94?= =?UTF-8?q?=20slim=20to=20contract=20level;=20detail=20descends=20to=20the?= =?UTF-8?q?=20plan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Level-of-detail change, contract unchanged. 909 -> 546 lines. Daniel's decision on the fired escalation, after eight passes held Blocker/Major flat at 26-31 while roughly three-quarters of each pass's findings were consequences of the previous revision's own additions. MOVED TO THE PLAN, each to be reviewed in the plan's own Gate A against this spec once stable -- nothing escapes review by moving, only its timing changes: the provenance line's concrete grammar, productions and examples; the curve's concrete format and worked example; command lines for the mid-cycle amend; the per-shape diagnostic checks in §5; per-site replacement wordings for §6.1's fourteen sites and §6.2's twenty-one passages; and the conditions artifact's production procedure. KEPT HERE: every settled decision with its reason; every rule stated as a REQUIRED PROPERTY rather than a format; the precedence table; the hold rule; the reachability test and its exclusions; the floor predicate, unanimity, and the hook-precedence rule; Q6's answer with the rejected alternatives and their reasons; the accounting method, the passage list, and the split's price; the falsified-statement inventory; the evidence plan's shape and counterfactuals; scope; and the risks with their stated limits. DROPPED, not moved: narration of what earlier revisions of this document got wrong. Where such an error produced a rule the rule is here; the history lives in commit bodies and the dispositions file. NO DECISION MOVED. Where slimming would have required deciding something, the rule stayed. Stating rules as properties rather than formats is what made the pass-8 amend Blocker visible in the first place -- the pinned `-m` command contradicted the preservation requirement it sat beside, which a property statement cannot do. Three pass-8 Blockers resolved as part of the restructuring: §1's closure precondition -- "cannot tell" is not "was resolved", so a cycle that cannot establish resolution does not close; the provenance prose-forms contradiction, dissolved by keeping only required properties here; and §6.2's completeness rule, with rows 20 and 21 added and the split's price stated -- once dispositions are deferred, the passage list is the sole guard against a dropped condition. Note on size: the target was roughly a third and the result is 60%. Further cuts would have to drop either rules or the rationale prompt-standards item 6 makes binding, and the guard forbids both. Mirror claims verified mechanically before this commit, for the coverage statement the closing pass owes: template fenced at 192-778 with §5 at 257-777; the prose-exemption rationale confirmed ABSENT from the template (the §7 prerequisite); all twenty-one §6.2 passages present exactly once in the template; rows 20 and 21's line citations confirmed in both copies. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-8 at .context/codex-reviews/gate-a-spec-rle-pass-{1..8}.md. Findings 27, 30, 54, 40, 33, 34, 32, 33. Blockers 5, 2, 0, 4, 0, 3, 9, 4. Blocker/Major 24, 22, 43, 31, 30, 26, 29, 28. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 1263 ++++++----------- 1 file changed, 450 insertions(+), 813 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index b35c294..319cebc 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,80 +1,63 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 9, after Gate-A passes 1-8 (27, 30, 54, 40, 33, 34, 32, 33) +**Date:** 2026-08-28 · **Revision:** 10 (restructured), after Gate-A passes 1-8 +(27, 30, 54, 40, 33, 34, 32, 33 findings) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. -Prompt-only. No file under `plugins/dev-workflow/hooks/` changes, and **no hook *state* file is -written by this design** — in particular not `.context/codex-gate.floor`. - -**The hook does read one file this change edits, and that is a constraint the plan must honour.** -`codex-gate.sh:94` greps `CLAUDE.md` for its §5 heading with -`^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review` to build the citation every -reminder prints (`:109`, falling back to "this project's review policy"). So **the §5 heading must -keep matching that pattern**: renaming or renumbering the section silently degrades every hook -message to the generic fallback. An earlier revision claimed no file the hook reads is written -here, which was false — the same describe-a-mechanism-without-checking-it failure this design -warns about, in text that cited its own verification. - -**Surfaces edited.** `CLAUDE.md` §5 (65–589) and its mirror inside `/workflow-init`'s inline -`CLAUDE.md` template (`plugins/dev-workflow/commands/workflow-init.md`, fenced 192–778; §5 is -257–777), plus the user-facing statements this change falsifies (§7) and **this story's own -acceptance criteria**, which six edits this cycle have already corrected and which the plan must -treat as part of the surface rather than as context. The two §5 copies already -differ on 192 lines across 20 hunks; this design touches only the rules it changes, per story -criterion 7. +**What this document is, after revision 10.** Contract level only: settled decisions with their +reasons, rules stated as required properties, named interfaces, and scope. **Exact replacement +wordings, concrete formats, command lines and step-by-step procedures live in the plan**, where +they get their own Gate A against this spec once it is stable. Nothing escapes review by moving; +what changes is when it is reviewed. Revision 10 is a level-of-detail change and moves no +decision — §11 lists what went where. + +Prompt-only. **No file under `plugins/dev-workflow/hooks/` changes, and no hook *state* file is +written.** One file the hook *reads* is edited: `codex-gate.sh:94` greps `CLAUDE.md` for the §5 +heading to build every reminder's citation, so **the §5 heading must keep matching +`^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`** or the citation degrades to a +generic fallback. + +**Surfaces.** `CLAUDE.md` §5 (65–589); its mirror in `/workflow-init`'s inline template +(`plugins/dev-workflow/commands/workflow-init.md`, fenced 192–778, §5 at 257–777); the +user-facing statements §7 lists; and this story's own acceptance criteria. The two §5 copies +already differ on 192 lines; only the rules this change touches are brought to parity. --- -## 1. The finding this design is built on - -**The loop has four exits and four standing duties**, each in its own bolded paragraph, each -qualifying the ones before it — and nowhere does §5 say which wins when two apply at once. - -*Scope of that claim:* four exits **of the loop**. §5 carries other mandatory stops that are not -loop exits — a target surviving deletion, an exhausted recovery budget, an unresolvable profile, -a blocking evidence or setup gap. Those halt the *procedure* rather than resolving the *cycle*, -do not compete with clean completion, and are not reordered here. - -### 1.1 Only one exit closes - -Already in §5, in two places, with the conclusion never drawn: - -> "Stopping this way is **not an exit from the gate**: the floor, the Blocker/Major filter and -> the clean-final-pass rule all stand, and the loop resumes on the revised artifact once the -> question is answered." - -> "You surface *with the finding still open* — the resolve rule is not waived, no pass is -> credited as clean, and the loop resumes on whatever the user decides." - -**Clean completion closes. The scope stop, the clearly-stuck exit and the two-tell stop -suspend.** So **suspension × suspension is not a conflict** — two at once means the report -carries both reasons. Three pairs, one sentence, no ordering. - -### 1.2 Three of the four duties are not participants - -- **The floor** is a quantity gating closure — "**Below the floor nothing closes**". -- **Blocker/Major must resolve** applies to **in-set** Blocker and Major findings, and is a - **precondition on closure not discharged by a quiet pass.** A pass that raises no *new* - Blocker or Major is not evidence that a previously surfaced one was resolved; the resolution - is a separate fact, and the closing report names each in-set Blocker/Major and how it was - resolved — **read from the per-pass findings files, which are the durable inventory**, not from - recall. - **Where those files are unavailable (§5's shapes), naming the unreadable passes is a disclosure, - not a discharge.** §5's degraded-sensitivity answer governs the *tell computation*, which is a - reporting duty; it does not reach this duty, which is a **precondition on closure**. A cycle that - cannot establish that every in-set Blocker and Major was resolved **does not close** — it stops - and surfaces, exactly as it would for any unresolved Blocker, because "we cannot tell" and "it - was resolved" are different states and only one of them permits closing. That is the loose - firing direction applied where it belongs: the expensive outcome is a cycle closing over an - unresolved Blocker nobody could see. (An earlier revision called this "definitional", which is the - describe-what-a-gate-proves failure: the clean-pass condition proves what the reviewer found - *this* pass, not what happened to earlier findings.) **The decline does not qualify this - duty and needs no exception in it** — a declined finding is out-of-set, so the duty never - attached. -- **A surfaced finding stays open** is the *mechanism* making the other exits suspensions. -- **No pass carrying a surfaced finding counts as clean** is the only participant in ordering. +## 1. The finding this rests on + +**The loop has four exits and four standing duties, and §5 never says which wins when two apply.** +That is why the `fic2` cycle could not ship two clauses without qualifying three rules nobody +proposed changing. Consolidation therefore means **stating the ordering once**, after which most +answers are readings of it. + +*Scope:* four exits **of the loop**. §5's other mandatory stops — a target surviving deletion, an +exhausted recovery budget, an unresolvable profile, a blocking evidence gap — halt the *procedure* +rather than resolving the *cycle*, and are not reordered. + +**Only one exit closes**, which §5 already says twice without drawing the conclusion: + +> "Stopping this way is **not an exit from the gate**: the floor, the Blocker/Major filter and the +> clean-final-pass rule all stand, and the loop resumes…" + +> "You surface *with the finding still open* — the resolve rule is not waived, no pass is credited +> as clean, and the loop resumes on whatever the user decides." + +Clean completion **closes**. The scope stop, the clearly-stuck exit and the two-tell stop +**suspend**. So suspension × suspension is not a conflict: two at once means the report carries +both reasons. + +**Three of the four duties are not participants in ordering.** The floor is a quantity gating +closure. **Blocker/Major-must-resolve applies to in-set findings and is a precondition on closure +not discharged by a quiet pass** — a pass raising no *new* Blocker/Major is not evidence an +earlier one was resolved, so the closing report inventories each in-set Blocker/Major and its +resolution from the per-pass findings files. **Where those files are unavailable, that is a +disclosure, not a discharge: a cycle that cannot establish resolution does not close.** "Cannot +tell" and "was resolved" are different states and only one permits closing. "A surfaced finding +stays open" is the mechanism making the other exits suspensions. **"No pass carrying a surfaced +finding counts as clean" is the only participant.** --- @@ -82,828 +65,482 @@ carries both reasons. Three pairs, one sentence, no ordering. | Conflict | Resolution | |---|---| -| clean completion × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim. Settled and probably unreachable — a clean pass has no regenerating Blocker/Major, so the exit's third condition fails. Kept because dropping a sentence §5 already spends is the failure criterion 6 exists to prevent. | -| clean completion × two-tell stop | **Clean wins.** Daniel's recorded decision, encoded not reopened. | -| clean completion × scope stop | **The scope stop outranks closure while any surfaced finding is still awaiting the user's answer. Any answer ends the hold**, in either direction. What happens next is not this cell's business: an answered finding is governed by the ordinary rules (§2.1). | - -**The third cell's wording is deliberate, and two earlier drafts got it wrong in opposite -ways.** One read "clean wins only when every surfaced finding still open has been explicitly -declined" — self-contradictory, since a decline *releases* the finding. The next read "while any -surfaced finding remains unresolved and undeclined" — which deadlocks on an **accepted Minor**, -a finding that is undeclined by definition and never repaired, because Minors are collected and -never iterated. - -**Both drafts made the same mistake: they mixed the hold with the resolve duty.** They are -different rules over different populations. The **hold** exists because a finding is *awaiting a -decision*, and it ends when the decision arrives — which is exactly Daniel's settled -discharge-in-both-directions. The **resolve duty** applies to **in-set** Blocker and Major -findings, and it is untouched by any of this: the decline qualifies the hold and **never** the -resolve duty. A declined finding is out-of-set, so the resolve duty never attached to it in the -first place; there is no exception to state, and stating one is what produced the tangle. - -**Why the third is the only one that needed deciding.** A scope stop is triggered by a *specific -finding*; while the duty stands unqualified that finding is open, so the pass cannot be clean and -the scope stop wins automatically. The cell exists only because the duty may now be qualified. - -**The asymmetry that explains the history:** the two-tell stop is triggered by *statistics about -findings*, not by a finding — nothing is left open, the duty does not bite, clean can win. The -scope stop's trigger **is** a finding. The reverted clause tried to unbite that by qualifying -three universal rules at once. The lattice was correct. - -### 2.1 The qualification, and how a decision on a finding is recorded - -**The qualification is the hold rule itself, stated fully rather than as a decline-only -carve-out.** An earlier revision wrote it as "qualified for, and only for, a declined finding", -which is a **partial copy** of what was settled: it left an *accepted* Minor ending the hold while -the no-clean rule stayed unqualified, so closure was governed by two rules that disagreed. The -full rule, which is what Daniel decided: +| clean × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim. Settled and probably unreachable — a clean pass has no regenerating Blocker/Major — and kept because dropping a sentence §5 spends is the failure criterion 6 exists to prevent. | +| clean × two-tell stop | **Clean wins.** Daniel's recorded decision, encoded not reopened. | +| clean × scope stop | **The scope stop outranks closure while any surfaced finding is still awaiting the user's answer. Any answer ends the hold**, either direction. What follows is §2.1's business, not this cell's. | -> **A surfaced finding holds closure while it awaits the user's answer. Any answer ends the hold, -> in either direction. After the answer the ordinary rules govern, and the hold plays no further -> part.** - -Ordinary rules after the answer: an **accepted Blocker or Major** must resolve, and until it does -the cycle does not close — by the resolve duty, not by the hold. An **accepted Minor or Nit** is -collected and never iterated, and blocks nothing. A **declined** finding is out-of-set, so neither -duty attaches to it. - -**Stated at every rule it modifies, in both copies, and at no rule it does not** — the criterion's -two halves: -1. **"a surfaced finding stays open"** — modified: the finding stops *awaiting a decision* when - answered. It does not thereby become resolved; an accepted Blocker or Major is open until - repaired. -2. **"no pass carrying a surfaced finding counts as clean"** — modified in **what it gates, not - in what it says about any pass.** It gates **closure of the cycle** while a finding is - unanswered. **The pass that surfaced the finding is not clean and never becomes clean** — a - pass's cleanliness is a fact about what that pass found, and no later event rewrites it. - Closure requires a *subsequent* clean pass at or above the floor, as it always did. -3. **"the loop resumes on whatever the user decides"** — modified: resumption is the hold - *ending*, made explicit rather than left to inference. -The **Blocker/Major-resolve duty is not modified and carries no qualification**. §1.2 scopes it to -in-set findings; a declined finding never enters that set, and an accepted one is subject to it in -full. Writing a qualification there would imply an exception that does not exist. - -**What can be declined, and what cannot.** A decline answers **one question only: whether a -surfaced finding enters the assigned fix set.** It is available only for a finding surfaced by a -scope stop — one out-of-set, or one opening a new structural or contract question. **An in-set -Blocker or Major cannot be declined**: it was never awaiting a set-membership answer, so there is -no hold to release, and treating a decline as available there would convert the mechanism into a -general waiver — exactly what §5's "Scope, and it is narrow" forbids. - -**All three branches, symmetrically.** §5 already says the loop "resumes the moment the user says -whether the set now includes it" — resumption *is* the hold ending, whichever way the answer went. - -- **Declined** → released as recorded-declined; stops holding the cycle for the remainder of it, - and does not re-stop later passes. -- **Accepted** → enters the assigned set, where **severity governs exactly as Mechanics already - says**. An accepted Blocker or Major must resolve; until it does, **no pass closes the cycle** — - and note this is a statement about *closure*, not about re-scoring a pass that already happened. - A pass's cleanliness is a fact about what that pass found and never changes afterwards; what a - later answer changes is whether the cycle may close. An accepted Minor or Nit is collected, - never iterated, and blocks nothing. There is no deadlock; the apparent one assumed the hold - outlives the answer. -- **Undecided** → the hold stands. Nothing closes unanswered. - -**Where the decision is recorded — the commit body, on rails §5 already ships.** With one timing -fact stated rather than assumed: **the commit that will carry the record does not exist while the -loop is running.** A Gate-A spec loop's record lands in the spec commit, which is written when the -loop closes. **A Gate-B cycle is the exception, and it is an exception because §5 already made -it one**: the `WIP:` commit exists precisely so a cycle has a commit while it is still running, so -a Gate-B decline lands there by amend immediately and is restated by the closing amend. The -timing claim above is therefore about Gate A only. - -**That mid-cycle amend must visibly keep the `WIP:` prefix in its own `-m`.** The hook recognizes -a WIP commit from the command's message argument, so an amend that omits it **reads as the cycle -closing**: counters reset and the accumulated passes are discarded, while the agent believed it -was only recording a decline. The shipped text therefore pins the form — -the amend must **retain the `WIP:` prefix in the message the command supplies** and **preserve the -existing body**. Those two together rule out the obvious `-m "WIP: "`, which replaces the -*whole* message and would erase the nonce and every prior decline — destroying the durability the -record exists for. The shipped text pins the property, not one command line: the resulting commit -message must begin `WIP:` and must contain every record the previous message contained, plus the -new one. (`--amend` with an edited full message, or `-F` with the previous body plus the addition, -both satisfy it; `-m` with a bare subject does not.) The **non-`WIP:` amend is reserved for final -closure alone**. This -is a hazard §5's Mechanics already names, reached by a new route. During the loop the decision lives in the working record — the dispositions companion, which -§5 already calls the advisory working copy — and **becomes the record when the commit is written**. -Nothing about the decline's effect waits for the commit; what the commit provides is durability -past the session. The human-exception machinery already solves this transport the same way: "an ungated change records it in that -commit; a Gate-A cycle in the spec or plan commit; a Gate-B cycle in the WIP commit, restated by -the closing amend", folded in mid-cycle by amend, carried into the squash body. **The decline -reuses that transport as a different record type:** +**Why only the third needed deciding.** A scope stop is triggered by a *specific finding*; while +the duty stands unqualified that finding is open, so the pass cannot be clean and the scope stop +wins automatically. **The asymmetry:** the two-tell stop is triggered by *statistics about +findings*, so nothing is left open and clean can win. The scope stop's trigger **is** a finding. -``` -Declined finding: · // · · -Location: -Defect: -Severity: Consequence: Fix: -Reason: -``` +### 2.1 The hold, the decline, and the record + +**The rule, stated in full rather than as a decline-only carve-out:** -**The record stores exactly what the sameness test reads.** An earlier revision stored location -and defect while testing sameness against severity, consequence and suggested fix — a test -reading fields the record lacks, which is the same class as the wiring failure §8 warns about. -Both now carry all five. - -**Identity.** A later finding is the same finding when **all five recorded fields match**. Any -difference — including a severity change, since a Minor re-raised as a Blocker is not the thing -that was declined — makes it a **new finding, and the hold applies**. Where sameness is unclear -it is likewise new, which costs a question and never a silent release. This mirrors §5's existing -treatment of unclear set membership. - -**A decline binds one cycle only, and what binds it is a nonce.** Deriving the identifier from the -gate plus a commit — an earlier revision's answer — does not work: sibling worktrees and a -restarted loop can share a loop name and a base commit, and a `WIP:` parent identifies the *base* -rather than the artifact reviewed. So: -- **The cycle nonce** is generated once at cycle start, immutable for the cycle's life, and - **recorded in history** — in the first commit body the cycle writes, and in every record it - carries thereafter. Any collision-resistant value serves; it must be safe as a slot infix, so it - is restricted to `[a-z0-9]{4,16}`. -- **The reviewed artifact is tracked separately.** The nonce answers "which cycle is this"; a - commit or tree id answers "did both branches of this pass see the same thing" (§5.1). - Conflating them left the earlier definition both non-unique and unable to do §5.1's job. -- **A resumed cycle recovers its nonce from the recorded history**, never by re-deriving it. A - cycle that cannot recover one has no identity and starts a new cycle — which costs passes rather - than silently inheriting a decline. The record **has no effect in any -later cycle**, even though the record itself persists into commit and squash history. Without -that bound, a decline recorded once would silently release the same finding in every future -cycle, which nobody decided. - -**The distinction from the human-exception form is stated explicitly.** They are not the same -record: -- The **human-exception form authorizes nothing** — §5 says so in its own words, and says it is - never the answer to a below-floor pass, an unclean final pass, or a `STOP and surface`. -- The **decline record has §5-defined effect**: it releases one specific finding from the hold. - That effect comes from the loop's own scope rule — which already routes set membership to the - user and already resumes on the answer — not from human assent overriding a mandatory rule. - Every other mandatory rule stands. - -**What the record establishes, said plainly.** Like the human-exception record beside it, this is -an **unverified assertion**: the commit body is author-written, and nothing checks that the handle -belongs to whoever decided. A reader learns that *the commit claims* the user declined this -finding. The shipped text says so rather than implying evidence. What makes it safe is not -verification but **narrowness** — it releases one fully-identified finding, in one cycle. - -**"Explicitly declined" is defined tightly.** A **recorded user decision on that specific -finding**, attributable and unambiguous. **Never** silence, never a general remark about scope, -never the agent inferring a decline from context. All three negatives ship. - -**The dispositions companion stays what §5 says it is** — the advisory working copy. The commit -body is the record. +> **A surfaced finding holds closure while it awaits the user's answer. Any answer ends the hold, +> in either direction. After the answer the ordinary rules govern and the hold plays no part.** + +- **Declined** → out-of-set; neither duty attaches; does not re-stop later passes in that cycle. +- **Accepted** → in-set; **severity governs as Mechanics already says.** An accepted Blocker or + Major must resolve and until it does **the cycle does not close** — a statement about closure, + never about re-scoring a pass. **A pass's cleanliness is a fact about what that pass found and + is never rewritten**; closure needs a *subsequent* clean pass at or above the floor. + An accepted Minor or Nit is collected, never iterated, and blocks nothing. +- **Undecided** → the hold stands. + +**What can be declined.** Only a finding surfaced by a **scope stop** — out-of-set, or opening a +new structural or contract question. **An in-set Blocker or Major cannot be declined**: it was +never awaiting a membership answer, and treating it as declinable would make this a general +waiver, which "Scope, and it is narrow" forbids. + +**Stated at every rule it modifies, in both copies, and at no rule it does not.** It modifies +"a surfaced finding stays open" (stops *awaiting a decision*, not thereby resolved), "no pass +carrying a surfaced finding counts as clean" (gates **closure** while unanswered), and "the loop +resumes on whatever the user decides" (resumption *is* the hold ending). **The +Blocker/Major-resolve duty is not modified and carries no qualification** — §1 scopes it to in-set +findings, so a decline never reaches it, and a qualification there would imply an exception that +does not exist. + +**"Explicitly declined"** is a **recorded user decision on that specific finding**, attributable +and unambiguous. Never silence, never a general remark about scope, never the agent inferring one. + +**Required properties of the record** — the plan fixes the format: +- Lives in the **commit body**, reusing the human-exception transport (Gate-A loops: the spec or + plan commit, written at close; Gate B: the `WIP:` body by amend, restated by the closing amend). + During a loop the decision lives in the advisory working record and *becomes* the record at + commit; the decline's effect never waits for the commit. +- Carries enough of the finding to **re-identify it**: location, defect, severity, consequence and + suggested fix. **The record stores exactly what the sameness test reads** — a test reading + fields the record lacks is the wiring failure §8 warns about. Sameness requires all five to + match; **any difference, including severity, makes it a new finding and the hold applies**, as + does any genuine uncertainty. +- Names **who decided and when**, and survives the squash carry. +- **Bound to one cycle** by the cycle nonce below. A decline has no effect in any later cycle. +- **Reads as an unverified assertion**, exactly like the human-exception record beside it: nothing + checks that the handle belongs to whoever decided. What makes it safe is **narrowness** — one + fully-identified finding, one cycle — not verification. The shipped text says so. + +**The cycle nonce.** Generated once at cycle start, immutable, recorded in history, and restricted +so it is safe as a slot infix. **Deriving it from a gate plus a commit does not work**: sibling +worktrees and restarted loops share loop names and base commits, and a `WIP:` parent identifies +the base rather than the artifact reviewed. **The reviewed commit or tree id is tracked +separately**, because "which cycle is this" and "did both branches see the same thing" are +different questions. A cycle that cannot recover its nonce has no identity and starts a new cycle. + +**The mid-cycle amend must preserve what it amends.** Required properties: the resulting message +**begins `WIP:`** — the hook recognizes a WIP commit from the message the command supplies, and an +amend that loses the prefix reads as the cycle *closing*, resetting counters and discarding +accumulated passes — and **contains every record the previous message contained**, plus the new +one. The **non-`WIP:` amend is reserved for final closure alone.** --- ## 3. Severity semantics (part 2) -**The decision procedure is the rule. The subject list is illustration.** - -> Name **what in the system consumes this text** — a gate call, a skill run, an agent following a -> rule, an escalation procedure being applied, a template being scaffolded; whatever *acts* on it -> — and the decision that act takes differently if the text is wrong. (An artifact does not read; -> something reads it and then does something. Naming the act, not the file, is what makes the -> answer checkable.) In this -> repo that is typically a gate, a skill, a rule, an escalation procedure or a scaffolded -> template; **that list is illustrative, not the set of allowed readers**, because this rule ships -> into projects whose readers we have never seen (invariant 10). If you cannot name an in-system -> reader and a changed decision, the finding is **Minor or below** — collect, never iterate. A -> finding that is trivial on its own terms stays a Nit; the test sets a ceiling, not a floor. -> -> **The reader must consume the text in the system's operation, not in reviewing it.** The -> **review pass raising this finding** is not an in-system reader of the text it is reviewing. - -**The test decides one boundary only: Minor-or-below versus keeps-its-severity.** Which of Blocker -or Major a kept finding takes is decided exactly as Mechanics already decides it — Blocker for -wrong, unsafe or invariant-breaking, Major for a design flaw requiring rework. The test never -promotes and never chooses between the two; saying so keeps a reader from treating it as a -replacement for definitions it does not touch. - -Findings about narration, mechanism prose and test-instrument internals are the cases this -usually catches, shipped as **worked examples of the test, not a second rule beside it**. Stating -a categorical demotion *and* the test would give two procedures that can disagree on one finding. - -**A human reader never satisfies the test.** That cost class is already priced as non-gating by -§5's prose exemption — "a wrong sentence costs a confused reader, not broken behaviour". - -**And the reviewing pass never satisfies it either**, which is why the self-exclusion above is -not a detail. Without it, any Gate-A finding could argue "Gate A reads this text", satisfy the -test, and **nothing would ever demote** — the rule would ship looking correct and classify -everything as it did before. **Gates remain legitimate in-system readers of rule text**: a rule a -*future* Gate-B call will apply is read in the system's operation and passes the test. What is -excluded is narrower than "a gate" — it is the single pass currently raising the finding, reading -the text in order to review it. - -**The instrument carve-out runs in both directions.** An instrument finding keeps its severity -when it shows the instrument **changes what the gate concludes about product behaviour** — a -false green, and equally a false red, a check that blocks a valid change, or instrument logic -driving an unnecessary rewrite. Naming false green alone would demote a check that fails for -wiring reasons and costs a correct change. - -**Why not a list of demotable artifact kinds.** The field record falsifies that form. Of three -`fic2` pass-5 findings on one parked story's criterion, two were correctly parked and **one was -correctly acted on** — "the pass-4 activation boundary — because a future rewrite could otherwise -move the duty to pass 1 while checking off every other listed condition." Same artifact, same -pass, opposite correct answers. An enumeration would also travel into scaffolded repos whose -artifact kinds we have never seen — invariant 10's problem in a new place. - -**Kinship, stated in the shipped text.** The **finding-level analog of the path-level prose -exemption** — one principle at two granularities: text that *describes* the product versus text -that *is* the product. - -**The boundary case, qualified rather than blanket.** Rationale prose inside a rule file is -**Minor when no rule's application depends on it**, and **not** categorically Minor. -`docs/prompt-standards.md:49-51` requires that "Rules carry their why", because "models follow -motivated rules better, and reviewers can judge whether the rule still applies", and invariant 11 -makes that checklist binding. Rationale a reader must consult to decide whether or how a rule -applies **is** read by an in-system reader and passes the test. - -**This test removes arbitrariness, not judgement**, and the shipped text says so. Two readers can -still disagree about whether a named reader's decision changes; what they can no longer do is -decide by taste, because the test names what must be produced — a reader and a changed decision. -Claiming a judgement-free rule would be a promise no prose rule keeps. - -**Coverage-first unchanged:** the reviewer reports every finding with severity and confidence; -the filter is ours. - -**Expected effect, with its limit.** The intent is that distributions like PR #23's — 16 in a -never-committed scratch harness, 10 in narration, 1 in a plan — demote substantially. **How much -is not predictable from the recorded counts**, because `7bbdb14`'s own body describes harness -defects that could make checks pass for wiring reasons, which the two-directional carve-out keeps. -`fic2`'s pass-2 meta cluster demotes only partially: ledger rows keep severity because escalation -reads the recurrence count, story criteria because the assigned-fix-set rule reads them. **The -amount is a prediction rather than a measurement**, which is why the story routes it to P8. +**One procedure decides severity, and the subject list is illustration, not a second rule.** + +> Name **what in the system consumes this text** — whatever *acts* on it — and the decision that +> act takes differently if the text is wrong. If you can name neither, the finding is **Minor or +> below**; collect, never iterate. + +- **The reader must consume the text in the system's *operation*, not in reviewing it. The review + pass raising the finding is not an in-system reader of the text it reviews.** Without this the + test demotes nothing, since any review finding could name the review itself. **Gates remain + legitimate readers** of rule text they will later apply. +- **A human reader never satisfies the test** — §5's prose exemption already prices that cost as + non-gating: "a wrong sentence costs a confused reader, not broken behaviour". +- **The list of reader kinds is illustrative, not closed.** This ships into projects whose readers + we have never seen (invariant 10). +- **The test sets a ceiling, not a floor**, and **never chooses between Blocker and Major** — + Mechanics still decides that. +- **The instrument carve-out is symmetric**: an instrument finding keeps its severity whenever it + shows the instrument changes what a gate concludes about product behaviour — a false green, and + equally a false red or a check blocking a valid change. +- **Rationale prose is Minor only when no rule's application depends on it**, not categorically. + `docs/prompt-standards.md:49-51` requires that "Rules carry their why", because "models follow + motivated rules better", and invariant 11 makes that binding — so rationale a reader must + consult to apply a rule passes the test. +- **This removes arbitrariness, not judgement**, and the shipped text says so. +- **Coverage-first is unchanged**: the reviewer reports every finding with severity and + confidence; the filter is ours. + +**Kinship, stated in the shipped text:** this is the **finding-level analog of the path-level +prose exemption** — one principle at two granularities, text that *describes* the product versus +text that *is* the product. Each becomes the other's consistency check. + +**Why not artifact kinds.** The field record falsifies that form: of three `fic2` pass-5 findings +on one story's acceptance criterion, two were correctly parked and **one correctly acted on**. +Same artifact, same pass, opposite correct answers. + +**Expected effect, with its limit.** PR #23-class distributions should demote substantially. **How +much is not predictable** — `7bbdb14`'s own body describes harness defects that could make checks +pass for wiring reasons, which the symmetric carve-out keeps. `fic2`'s meta cluster demotes only +partially: ledger rows and story criteria keep their severity because escalation and the +assigned-fix-set rule read them. **The amount is a prediction, not a measurement**, which is why +the story routes it to P8. --- ## 4. The pass floor (part 1) -**One predicate.** `max(risk, security) == 0` → floor **1**. Everything else → **3**, unprofiled +**One predicate.** `max(risk, security) == 0` → floor **1**; everything else → **3**, unprofiled included. Two levels, not three: `high` takes its rigor from lens sets and evidence mode. -**Several cited stories: unanimity.** Floor 1 **if and only if every cited story is profiled and -every one is at level 0**. Any other set — mixed levels, any unprofiled member, any higher -profile — yields 3. §5's own precedent for the analogous relaxation ("skip-eligible only if -**every** cited story is"), and the only reading consistent with invariant 2's firing direction. - -**Why the text must say the floor governs both gates.** Because **the derived floor is a property -of the cycle's cited stories, and both loops cite the same stories** — so a level-0 story relaxes -the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle alike. A reader who does not see -this stated will write a rule for one gate and assume the other is untouched. (The hook's single -`$floor` at `:119`, compared at `:946` and `:966`, is a *separate* fact about the reminder -threshold. An earlier revision offered it as the reason, which stopped holding once the obliged -floor left that file — the reason is the shared cited-story set.) - -**Why there is no `docs-only` arm.** A diff-derived reading cannot serve Gate A, which runs on a -spec before any diff exists. A story-declared reading is subsumed: intake defines `trivial` as -"no behavioural effect in the artifact's own execution context". And a path-derived arm would be -**wrong in this repo**: `docs/hardening-log.md` is a `docs/**.md` path that drives rung -escalation. That is §3's reachability test deciding a §4 question. - -### 4.1 The floor lives in the text, not in a file - -**Nothing in this design writes `.context/codex-gate.floor`.** The agent derives the floor from -the profile and **states it** — in every pass report and in the commit-body provenance line. That -is where the floor lives, and §5's text is what binds the agent, as it always was. - -**The workspace knob stays exactly what it always was: the hook's reminder threshold.** It is -never written, never removed, and never read for our derivation. Mechanically it never bound an -agent, and the precise claim is narrower than an earlier revision's: `$floor` **does** appear in -control flow — `[ "$passes" -lt "$floor" ]` at `:946` and `[ "$passesA" -lt "$floor" ]` at `:966` — -but that control flow only **selects which advisory message fires** (`:933`, `:947`, `:956`, -`:967`, `:973`). The hook exits 0 on every branch (invariant 1), so no value of the knob has ever -made it block or made an agent owe a pass. "Appears only inside note messages" was mechanically -wrong and is corrected here. Where the user's knob and -the derived floor diverge, the provenance line **discloses both** rather than resolving them: - -**One grammar, every case inside it.** Earlier revisions grew a separate form per case — a -single-story shorthand, a no-story form, an unprofiled form, an unusable-knob form — four -pinned-looking patterns for one line, unparseable by the P8 reader that must consume it. - -``` -floor per ; hook reminder threshold - - := "none" the artifact cites no story - | "{" ("," )* "}" - := " (level " <0|1|2> ")" a profiled story - | " (unprofiled)" a cited story with no profile - := "absent" no codex-gate.floor file - | a usable value - | "unusable" file present; empty, non-numeric, or < 1 -``` - -Every case is one production, so a mixed profiled-and-unprofiled set has a canonical form rather -than falling between patterns. One example per variant: - -``` -floor 1 per {docs/superpowers/stories/A-story.md (level 0)}; hook reminder threshold absent -floor 3 per {A-story.md (level 0), B-story.md (level 2)}; hook reminder threshold 3 -floor 3 per {A-story.md (level 0), C-story.md (unprofiled)}; hook reminder threshold 1 -floor 3 per none; hook reminder threshold unusable -``` +**Unanimity across a cited set.** Floor 1 **if and only if every cited story is profiled and every +one is at level 0**. This follows §5's own precedent — "skip-eligible only if **every** cited +story is" — and is the only reading consistent with invariant 2's firing direction. -Levels are the numeral `max(risk, security)` yields — `0`, `1`, `2` — never an axis word like -`high`, which an earlier revision mixed in and which does not identify the level a reader needs. +**One derived value governs every loop of the cycle** — Gate-A spec, Gate-A plan, Gate B — because +those loops cite the same stories. Both copies say so and say why. -**Every cycle records this, default or not** — an absent line must not be ambiguous between "the -default applied" and "someone forgot". +**No `docs-only` arm.** A diff-derived reading cannot serve Gate A, which runs before a diff +exists; a story-declared one is subsumed, since intake defines `trivial` as no behavioural effect; +and path-derived would be **wrong here**, because `docs/hardening-log.md` is a `docs/**.md` path +that drives rung escalation. -**Why the set and not the story.** Unanimity makes the floor a property of the cited *set*: one -level-2 story among four level-0 ones yields 3 for the cycle, so an entry per story would -misattribute that 3 to stories that did not cause it. `` therefore carries the whole -set with each member's level, and a single-story cycle is its one-element case. +### 4.1 The floor lives in the text, not in a file -**Why the knob clause is always present.** A present-but-equal knob is still a fact about the -workspace, and tying disclosure to divergence would make an absent clause ambiguous between "no -knob" and "a knob that agreed". `` has a production for each state, including `unusable` — -empty, non-numeric, or below 1 — which the hook already ignores (`codex-gate.sh:124-127`), so the -line describes what the hook will actually do. +**Nothing here writes `.context/codex-gate.floor`.** The floor is derived and **stated** — in every +pass report and in the commit-body provenance line — and §5's text is what binds an agent. -**The grammar above is the whole specification of this line.** Earlier revisions left prose forms -beside it — `floor 3 (no story cited)`, a colon-separated unusable clause, a bare axis word like -`(high)` where the grammar requires a level numeral — and those are **superseded, not alternatives**: -every case is a production above, and any form that does not parse under it is wrong. +**The knob stays the user's, and is the hook's reminder threshold.** Never written, never removed, +never read for the derivation. It never bound an agent: `$floor` does appear in control flow +(`:946`, `:966`), but that flow only selects **which advisory message fires**, and the hook exits 0 +on every branch (invariant 1). -**Both copies state the precedence explicitly, because a reminder is not an instruction and the -difference has to be actionable.** The order, shipped as text: +**Precedence, shipped as text, because a reminder is not an instruction:** > **The derived floor controls whether the cycle may close. The hook's ratio is a reminder > threshold and controls nothing.** Where the derived floor and the ordinary closure rules are -> satisfied, a below-threshold hook reminder is **noted in the pass report and disregarded** — it -> is not a pass the cycle owes. - -Without that, a level-0 cycle meets its obligation and is then told by a shipped message that it -"MUST reach a minimum of 3 passes", and the agent has two instructions and no rule for choosing — -which is prompt-standards item 7's contradiction case at the new floor's *main success path*. - -**The hook's own message text is not changed here**, because it lives in -`plugins/dev-workflow/hooks/codex-gate.sh:933` and hook code is out of scope by decision. That -leaves a **known residual, named rather than implied**: the message will keep saying "MUST" at a -threshold the cycle does not owe. What makes it tolerable is the precedence rule above plus -invariant 1 — the hook is advisory and exits 0 regardless — not the reminder being harmless on -its own. - -**The consequence, accepted rather than engineered around: a level-0 cycle closing at one pass -draws a hook reminder saying "below floor (1/3)".** That reminder is **the invariant working**, -not a defect — AGENTS.md invariant 2: *"On uncertainty, fire. A missed commit (false ✓) is the -dangerous direction; **a redundant warning is the accepted price.**"* A hook taught to fall silent -at 1 would be the false ✓ the invariant names as dangerous. - -**Why an earlier revision's marker mechanism was deleted rather than repaired.** It had the agent -write the knob plus a marker distinguishing agent writes from user writes. Gate-A pass 3 returned -sixteen Majors against it — write ordering, removal ordering, cross-cycle ownership, staleness, -rollback contamination — and one that settled it: **the cheapest bypass was marker-specific.** -Write `1` without a marker, or delete the marker afterwards, and an agent-chosen floor is -camouflaged as a user decision. A protection that makes the attack indistinguishable from the -protected case is not incomplete; it is inverted. Deleting it removes every one of those findings, -the concurrency exposure on a shared mutable file, and the whole class of crash-recovery rules -written in prose for an advisory file. - -**What deletion does not remove, stated so the rationale does not overclaim.** Any agent can -still write `1` into the gitignored knob and quiet the hook — that path exists with or without -this design, because the file is writable and the hook reads it. What deletion removes is *this -design's own reliance on writing it*, and with that the ambiguity: under revision 4 a floor file -is **always** a user artifact, so a written `1` is a claim about the hook's reminders and never a -claim about what §5 obliged. The obligation lives in the provenance line, where a wrong number is -a false statement rather than a silent file. - -**The sanctioned lever for wanting fewer obliged passes is the profile** — proposed, human- -confirmed, logged — **or `codex-gate.off` for the reminders.** Not the floor knob, which moves -what the hook says and never what §5 obliges. +> satisfied, a below-threshold reminder is **noted in the pass report and disregarded.** + +**Named residual:** the hook's own message says a cycle "MUST reach a minimum" at its threshold +(`codex-gate.sh:933`), which at level 0 contradicts a legitimate one-pass close. **Hook text is out +of scope by decision**, so this is disclosed, not fixed; what makes it tolerable is the precedence +rule plus invariant 1. + +**Why the earlier marker mechanism was deleted rather than repaired.** It had the agent write the +knob plus a marker distinguishing agent writes from user writes; Gate-A pass 3 returned sixteen +Majors against it and one that settled it — **the cheapest bypass was marker-specific**, so a +protection made the attack indistinguishable from the protected case. **What deletion does not +remove:** anyone can still write the gitignored knob and quiet the hook. What it removes is this +design's reliance on writing it, and with that the ambiguity — a floor file is now **always** a +user artifact. + +**Required properties of the provenance line** — the plan fixes the grammar: +- **Every cycle records it**, default or not, so an absent line is never ambiguous between "the + default applied" and "someone forgot". +- It states **one floor and the cited set that produced it, with each member's level as a + numeral** — not an entry per story, since unanimity makes the floor a property of the set. +- It distinguishes a **cited story with no profile** from **no story cited**. +- The **knob is recorded whenever the file exists**, including when present but unusable, since + the hook ignores such a value and the line describes what the hook will do. +- It is **machine-extractable**, because the deferred P8 measurement reads it, and **one form + covers every case** — a second pinned form for a special case is what made earlier revisions + unparseable. ### 4.2 A profile that moves mid-cycle -Composed from three rules §5 already has; no new rule, and nothing to re-write on disk. The floor -derives from the **current** profile at each pass (§5 already requires the header read fresh); -**passes already run keep counting**; **closing requires meeting the floor as currently derived.** - -**The consequence that must be stated, or the arithmetic reads wrong:** under a **raise**, at -least one further pass is required *regardless of the floor arithmetic*, because §5 already -requires the final clean pass to run under the current profile. A previously-final clean pass -stops qualifying the moment the profile moves — so even a raise leaving the floor unchanged -(`standard` → `high`, both 3) costs a pass. +Three existing rules compose; no new rule. The floor derives from the **current** profile at each +pass; **passes already run keep counting**; **closing requires the floor as currently derived.** -**On lowering.** A lower drops the floor *and* the lens sets *and* the evidence mode, so a `high` -cycle lowered to `trivial` can close on **one further pass after the lowering** — not on one pass -in total, since the rule that costs a raise a pass applies here too: the final clean pass must run -under the current profile, so no already-banked pass can be it. That is the pre-existing profile-change path, -human-confirmed in both directions and logged. The variable floor rides it; it does not create it. +**The consequence that must be stated:** a **raise costs at least one further pass regardless of +the arithmetic**, because §5 already requires the final clean pass to run under the current +profile — so even a raise leaving the floor unchanged costs a pass. **A lowering** drops the floor, +the lens sets and the evidence mode together, so a `high` cycle lowered to `trivial` can close on +**one further pass after the lowering**. That is the pre-existing profile-change path, +human-confirmed and logged; the variable floor rides it and does not create it. --- ## 5. Q6 — the pass-4 report when prior-pass history is unavailable -**Answer: report what is computable, name what is not and why, disclose the reduced sensitivity.** -Not a new stop condition, not a mandatory resume note. +**Report what is computable, name what is not and why, and disclose the reduced sensitivity.** Not +a new stop condition, not a mandatory resume note. + +Three of the five tells need history; **two are computable from the current pass alone**, so the +two-tell threshold **remains reachable**. The duty loses sensitivity; it does not become +inoperative. + +**Five shapes, each with its own check and remedy** — the plan carries the check specifics: +**absent** (no file; unrecoverable, since re-running produces a different pass); **partial** (some +slots present — compute over what exists and **name the missing pass numbers**, since a trend over +an unstated subset reads as a trend over the cycle); **malformed** (fails any pass-acceptance +check — treated as absent, **never** as zero findings, and re-runnable only while its `sessionId` +is still to hand); **unreadable** (environmental, and the OS cause is reported because permissions +and a full disk need different fixes); **stale** (another cycle's, or unattributable). + +**Stale is only partly detectable and the text says which part.** Bare numbered slots carry no +cycle identity, so a bare file is *unattributable* rather than known-foreign. Two prompt-only +mitigations: the **per-cycle slot infix**, which makes a cycle's own slots identifiable, and §5's +existing delete-and-confirm rule. Neither recovers a bare file's origin after the fact. + +**The disclosure is not optional and names the cause**, the shape, and the affected passes. + +**Rejected, with reasons:** a mandatory resume note changes an artifact §5 calls advisory; +unavailable history as its own stop would halt every cycle resumed on a fresh checkout; restarting +the pass-4 clock at the first visible pass silently lowers coverage. + +**Stated risk:** this fails *toward continuing* the loop, the direction invariant 2 questions. The +mandatory disclosure is what makes it acceptable. + +### 5.1 The per-pass curve + +**Each of the three loops records its own per-pass finding and Blocker counts in its own commit +body**, labelled with the loop it describes. **Gate B alone would leave the dominant cost +unmeasured** — the loops this story cites as evidence are Gate-A loops. + +**Required properties** — the plan fixes the format: +- One entry per **valid** pass, in pass order; **incomplete passes are excluded**, and because + they consume pass numbers the record **states which pass numbers it covers**. A valid + zero-finding pass is recorded as zero, never omitted. +- A `full` Gate-B pass, and separate `spec`/`quality` calls, and a single-branch recovery, are + **branches of one logical pass** contributing one summed entry. **The curve counts logical + passes; the hook counts calls**, and where they differ the body says so. +- **Both branches of one logical pass must have reviewed the same artifact revision**, identified + by the tracked reviewed commit. If it changed between them they are not one pass: the completed + branch is an incomplete pass and the second begins a new one. +- **The model each pass ran under is recorded**, per the existing convention at + `docs/coding-workflow.md:261-266`, with each contributing model listed where a pass was + assembled from calls under different models. +- A **legitimately skipped** loop records the skip rather than a silent gap. + +**What it reaches.** Durable **across cycles** — surviving a fresh checkout, a cleared `.context/`, +another machine. **Not within a running loop**: the commit does not exist until the loop closes, so +§5's degraded-sensitivity answer governs there. And it is **author-written and unchecked** — +nothing compares it against the validated pass files, so **P8 reads a self-reported curve** and the +text says so rather than letting it be treated as measurement. + +**Squash carry.** §5's rule names only evidence entries and human-exception records; the **decline +records, the provenance line and these curves** are added, in both copies. -Of the five tells, **three need history** — finding count rising, Blocker count failing to fall, a -require↔withdraw pair — and **two are computable from the current pass alone**: clustering on the -instrument, clustering on prose. With no prior record the two-tell threshold **remains reachable** -on the cluster pair. The duty loses sensitivity; it does not become inoperative. +--- -**Four shapes, each with its own check and remedy** — symptoms alone would be prompt-standards -item 10's failure: +## 6. Old-conditions accounting -| Shape | Check | Treatment | -|---|---|---| -| **absent** | the slot file does not exist | uncomputable; report which pass numbers are missing. **No recovery**: a pass that left no file cannot be reconstructed, and re-running it would produce a different pass, not that one | -| **partial** | some pass slots present, others not | compute historical tells over the passes present and **name the missing pass numbers** — a trend over an unstated subset reads as a trend over the cycle | -| **malformed** | fails any pass-acceptance check — terminator exactly `END OF FINDINGS ( total)`, count matching the finding lines, nothing but finding lines, and **each line structurally well formed**: right field count, no empty or whitespace-only severity field. §5's reader tolerates two things about the severity field and nothing else — its casing, and a non-empty unrecognized token, which reads as `MAJOR`; every *structural* failure is INCOMPLETE | treat as absent for that pass, **never** as zero findings. Distinct from *unreadable*: a malformed file is a review that ran and wrote badly, so **if its `sessionId` is still to hand** the pass may be re-runnable — and where it is not, say so rather than implying a recovery route that no longer exists | -| **unreadable** | exists but cannot be opened or decoded | treat as absent; report the OS-level cause, since permissions and a full disk need different fixes. **Recovery is environmental** — fix the cause and re-read; the file may be intact | -| **stale** | the slot's cycle discriminator is absent or is another cycle's | treat as absent **and report its presence** — a foreign curve is worse than no curve | - -**Stale is only partly detectable, and the text says which part.** Bare numbered slots carry no -cycle identity, so a pass-2 file from a previous cycle is indistinguishable by content. Two -prompt-only mitigations: the **slot discriminator convention** — a cycle uses a per-cycle infix, -as this cycle's `rle` does, following the `fic2` and `pr15` precedent, which makes *its own* slots -identifiable — and **§5's existing delete-and-confirm-before-each-call rule**, which guarantees the -file you are about to write is not a previous cycle's. - -**The discriminator is a change to the slot grammar, not an addition beside it**, and §5 must say -so: today the grammar is exactly `gate-a-spec-pass-

`, `gate-a-plan-pass-

` and -`gate-b--pass-

`, and an infixed name satisfies none of them. **The shipped text replaces each grammar with one that -admits an optional per-cycle infix** — `gate-a-spec[-]-pass-

` and its two siblings — so -there is exactly one rule and bare slots stay valid. Three properties come with it: -- **The infix is required, not recommended, when the bare slot is occupied.** That is the case - that destroyed a previous cycle's findings file in this very cycle: the delete-before-write rule - removed a bare slot belonging to a closed cycle. -- **Refuse rather than overwrite.** Where the target the cycle would write is occupied by a file - the cycle did not write, the pass stops and names the collision. §5 already says a target - surviving deletion is a stop; this extends it to a target that should not be deleted at all. -- **The infix identifies a cycle, not a story or a branch**, and two cycles must not share one. - A repeated infix reproduces exactly the collision it exists to prevent, so it is derived from - the cycle identifier §2.1 already defines. - -**Absent infix does not mean stale.** Bare slots remain valid, so a bare file is *unattributable* -rather than foreign — the stale row above treats it as absent because its origin cannot be -established, not because it is known to belong elsewhere. Neither makes a bare-slot file's origin -recoverable after the fact, and the report says so rather than implying detection. (The -discriminator convention also prevents the destruction this cycle caused once: deleting a bare -slot destroyed a previous cycle's findings record, which the dispositions companion happened to -survive.) - -**The disclosure is not optional and names the cause.** The report says which tells could not be -computed, **which shape applies**, and which pass numbers are affected. - -**Why the alternatives are rejected**, per story criterion 5: making the resume note mandatory -changes an artifact §5 explicitly calls advisory; treating unavailable history as its own stop -would halt every cycle resumed on a fresh checkout; restarting the pass-4 clock at the first -*visible* pass silently lowers coverage. - -**Stated risk.** Reporting rather than stopping fails *toward continuing* the loop, the direction -invariant 2 questions. The mandatory disclosure is what makes it acceptable. - -### 5.1 The curve in the commit body — all three loops - -**Each loop records its own per-pass finding and Blocker counts in its own commit body**, each -line **labelled with the loop it describes** so a squash body carrying several stays unambiguous: +Required by the AGENTS.md Don't and story criterion 6. -``` -Gate-A spec loop (passes 1-3, opus-5): Findings 27, 30, 0. Blockers 5, 2, 0. -Gate-A plan loop (passes 1,2,4, opus-5; pass 3 incomplete): Findings 8, 3, 0. Blockers 1, 0, 0. -Gate B (passes 1-3; 1-2 opus-5, 3 gpt-5-codex): Findings 14, 24, 0. Blockers 3, 4, 0. -``` +**Method — the stable half, and it stays here.** For each passage: list what its existing prose +requires, then mark each requirement **kept**, **moved**, or **deliberately dropped**. A +requirement neither kept nor explicitly dropped is a dropped condition. **No passage is rewritten +without its accounting.** -Every loop shown closes legitimately: three valid passes at floor 3, each ending on a -zero-finding pass. The plan loop's `passes 1,2,4` makes the exclusion rule visible — pass 3 was -incomplete, so it is absent from the counts and named in the mapping. **Where one logical pass was -assembled from calls under different models, each contributing model is listed for that pass**, -since a `spec` and a `quality` call need not have run under the same model. - -**A legitimately skipped Gate B records the skip, not a curve.** §5's triviality skip already -requires the reason in the commit body; that loop's line reads `Gate B: skipped (see skip -reason)`, so the absence is a stated fact rather than an omission P8 must guess at. - -**Gate B alone would leave the dominant cost unmeasured.** The loops this story cites as evidence -are Gate-A loops — nineteen measured Gate-A passes on one spec. P8 without Gate-A curves cannot -measure the thing the problem statement is about. (Pass 1 narrowed this to Gate B and pass 2 found -the narrowing wrong; settled here rather than oscillating.) - -**The form is pinned, because an unpinned one is unparseable.** One entry per **valid** pass, in -pass order, comma-separated. -- A `reviewType: full` Gate-B pass contributes **one entry, the sum of its two branch files**. -- **Separate `spec` and `quality` calls, and a single-branch recovery resume, are branches of one - logical pass** and likewise contribute one summed entry. -- **The curve counts logical passes; the hook counts calls.** These are deliberately different - numbers and §5 already says the counter is not evidence. Where they differ the body says so: - `(N calls, M passes)`. -- **Both branches of one logical pass must review the same artifact revision**, identified by the - commit the cycle identifier names. If the artifact changes between the branches they are **not - one pass**: the completed branch is recorded as an incomplete pass and excluded, and the second - branch begins a new pass. Ending the pass is the conservative direction — merging two revisions - would produce one entry describing two different artifacts. -- **Incomplete passes are excluded** — §5 already says they do not count. -- **A valid pass with zero findings is written as `0`**, never omitted. -- **Position does not equal pass number**, because incomplete passes are excluded and they - consume pass numbers. Each curve therefore states the pass numbers it covers — - `passes 1-4` or `passes 1,2,4` — so a reader never infers a mapping the exclusion rule - forbids. (An earlier revision claimed position equals pass number in the same breath as - excluding incomplete passes, which cannot both hold.) -- **The model each pass ran under is recorded beside its counts**, per the existing convention - in `docs/coding-workflow.md:261-266`: "Put the model the pass *ran under* in the pass record - beside the finding count." A curve dropping it would silently narrow a rule this change never - proposed touching. - -**What it reaches, and what it is worth.** The durable half **across cycles** — surviving a fresh -checkout, a cleared `.context/`, a different machine. **It does not restore history within a -running loop**: the commit does not exist until the loop closes, so it is no help at pass 4 of the -loop still running; there §5's degraded-sensitivity answer governs. And it is **author-written and -unchecked** — nothing compares the numbers against the validated pass files, so P8 reads a -self-reported curve. That is the same standing as every other commit-body record here, it is -better than the nothing that exists today, and the shipped text says it plainly rather than -letting P8 treat it as measurement. - -**Squash carry.** §5's squash-merge rule names only evidence entries and human-exception records. -The **decline records (§2.1), the floor provenance line (§4.1) and these labelled curves** are -added, in both copies. - ---- +**Where the dispositions are produced and gated.** In one artifact, +`docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md`, written **once against +the frozen final text** and **reviewed before any replacement text is written**. The plan names +that gate explicitly. -## 6. Old-conditions accounting +**The pass-2 tension, recorded rather than resolved by hindsight.** Gate-A pass 2 raised a Blocker +demanding the inventory live *in this spec* rather than being deferred to the plan — correct, since +deferring what constitutes compliance while claiming compliance is the failure the Don't describes. +**The frozen-text artifact is a third option neither pass had**, and it preserves what pass 2 +required: the accounting exists and is reviewed *before the replacement is approved*. What moved is +when it is produced. -Required by the AGENTS.md Don't and by story criterion 6, **and performed here** — deferring the -thing that constitutes compliance while claiming compliance is the failure the Don't describes. +**The price of the split, stated.** Once dispositions are deferred, **the passage list is the sole +guard against a dropped condition**, and the conditions artifact cannot catch what the list never +named. Rows 20 and 21 were missing until pass 8 because revision 8 added rules rewriting them +without extending the list. **The list is re-checked whenever the design adds a rule**, and the +plan's gate reads it against the final text rather than trusting it. -### 6.1 The floor-wording inventory is generated, not hand-derived +### 6.1 Floor-wording sites are generated, not hand-derived -Hand-derived three times, three different counts. It is now **the output of a stated command**: +Hand-derived three times, three different counts. The inventory is the output of a stated command: ``` grep -nE "min 3 passes|below 3|3-pass|3 passes|where the 3 come from|3-passes-per-gate" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Six sites per copy, symmetric** — `:72/:272`, `:79/:279`, `:236/:421`, `:300/:485`, -`:335/:519`, `:403/:582` — **twelve sites, all changing.** - -**Two limitations, stated because a generated list reads as complete:** -1. **Digit sites only.** The site that matters most spells no digit — `:126/:322`, "*a - Blocker/Major-free **pass 1** carrying a Minor keeps looping*", correct under floor 3 and - **false under floor 1**, where pass 1 is *at* the floor and closes. Two more sites, found by - hand. **Fourteen in total, all changing.** -2. **A floor, not coverage.** Another digit-free formulation would escape it exactly as that one - did. The hand-check is not optional. - -It correctly does **not** match `:249/:434` — "PR #23's Gate-B pass 3 returned all four findings -at `IMPORTANT`" — which cites a historical pass rather than stating a rule, and stays. - -**Every site maps to a §6.2 row**, so no site is edited without an accounting: `:72` → row 1, -`:79` → row 2, `:126` → row 5, `:236` → row 3, `:300` → row 12, `:335` → row 13, `:403` → row 9. - -### 6.2 The accounting method, and where the dispositions live - -**Split out of this spec after Gate-A pass 7** (Daniel, 2026-08-28). The row-by-row inventory -lived here through revisions 2–7 and became the largest single source of findings in the loop: -**7 of 29 Blocker/Major at pass 7, including 4 of 9 Blockers** — every one a row contradicting the -design it existed to account for, because a nineteen-row restatement of nineteen `CLAUDE.md` -passages is a second copy, and it drifts each time the design moves. That is the same defect this -change already removed from the story's acceptance criteria, reproduced inside the spec. - -**The pass-2 tension, recorded honestly rather than resolved by hindsight.** Gate-A pass 2 raised a -Blocker demanding the inventory live *in this spec* rather than being deferred to the plan — -correct, because deferring the thing that constitutes compliance while claiming compliance is the -failure the AGENTS.md Don't describes. **The frozen-text artifact is a third option neither pass -had on the table**, and it preserves what pass 2 actually required: the accounting exists, and is -reviewed, *before the replacement text is approved*. What changes is when it is produced, not -whether. - -**Method — this is the stable half and it stays here.** For each passage below: list what its -existing prose requires, then mark each requirement **kept**, **moved**, or **deliberately -dropped**. A requirement that is neither kept nor explicitly dropped is a dropped condition, which -is exactly what the Don't exists to catch, and no passage may be rewritten without its accounting. +**Six sites per copy, symmetric — twelve, all changing.** Two limitations, stated because a +generated list reads as complete: **it finds digit sites only**, and the site that matters most +spells none — `:126/:322`, "*a Blocker/Major-free **pass 1** carrying a Minor keeps looping*", +correct under floor 3 and **false under floor 1** where pass 1 is *at* the floor. **Fourteen sites +in total, all changing.** And **it is a floor, not coverage**: another digit-free formulation would +escape it. It correctly does not match `:249/:434`, which cites a historical pass rather than +stating a rule. -**Where the dispositions are produced and gated.** In one artifact, -`docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md`, written **once against -the frozen final text of this spec** — not maintained across revisions — and **reviewed before any -replacement text is written.** The plan names that point explicitly as a gate it does not pass -without. Producing it against moving text is what generated the findings this split removes. +### 6.2 The passages this change rewrites -**The passages this change rewrites — the list is the checkable part.** Each is present exactly -once in both §5 copies: +Each present exactly once in both copies. **Twenty-one; the conditions artifact is incomplete +without all of them.** | # | Passage | Rewritten by | |---|---|---| -| 1 | "**Both gates are a LOOP with a HARD FLOOR**" | part 1 — the floor statement itself | -| 2 | The early-exit sentence at `:79/:279` | part 1 — the zero-finding early exit | -| 3 | The incomplete-pass rule at `:236/:421` | part 1 — the incomplete-pass rule | +| 1 | "Both gates are a LOOP with a HARD FLOOR" | part 1 — the floor statement | +| 2 | The early-exit sentence (`:79/:279`) | part 1 — the zero-finding exit | +| 3 | The incomplete-pass rule (`:236/:421`) | part 1 | | 4 | "What a loop absorbs, and what stops it" | part 3 — the scope stop becomes an ordered suspension | -| 5 | "Recognizing \"clearly stuck\"" | part 3 — becomes a suspension; clean-completion precedence moves into the ordering | -| 6 | "Surfacing does not close the cycle" | part 3 — the hold mechanism §1.2 names | -| 7 | "From pass 4 onward every pass report carries three lines" | part 3 and §5 — two-tell stop, and Q6's unavailable-history behaviour | +| 5 | "Recognizing \"clearly stuck\"" | part 3 — becomes a suspension; clean precedence moves to the ordering | +| 6 | "Surfacing does not close the cycle" | part 3 — the hold mechanism | +| 7 | "From pass 4 onward…" | part 3 and §5 — two-tell stop, and Q6's answer | | 8 | "The two rules above do not compete" | part 3 — superseded by the ordering, kept or retired explicitly | -| 9 | "Lenses are different questions, not more passes" (`:403/:582`) | part 1 — the lens-set sentence naming the floor | -| 10 | "The Gate-B triviality skip needs two independent conditions" | part 1 — adjacent profile-derived relaxation, checked for consistency | -| 11 | "A cycle citing several stories" | part 1 — gains the floor dimension under unanimity | -| 12 | "Gate A — Spec, then plan (TWO runs, each its own 3-pass loop)" (`:300/:485`) | part 1 — Gate A's per-run loop | -| 13 | "Gate B — Code" (`:335/:519`) | part 1 — Gate B's "where the 3 come from" | -| 14 | "**Severity:** Blocker … Major … Minor · Nit" | part 2 — the severity definition | -| 15 | "Scope, and it is narrow" | part 3 — must distinguish the human-exception form from the decline record | +| 9 | "Lenses are different questions…" (`:403/:582`) | part 1 | +| 10 | "The Gate-B triviality skip…" | part 1 — adjacent relaxation, checked for consistency | +| 11 | "A cycle citing several stories" | part 1 — the floor dimension under unanimity | +| 12 | "Gate A — Spec, then plan…" (`:300/:485`) | part 1 | +| 13 | "Gate B — Code" (`:335/:519`) | part 1 | +| 14 | "**Severity:** Blocker … Nit" | part 2 | +| 15 | "Scope, and it is narrow" | part 3 — distinguishing the human-exception form from the decline | | 16 | "Recording a human exception" | part 3 — the decline reuses this transport | -| 17 | "Changing a profile" | §4.2 — the floor under a moving profile | -| 18 | The findings-slot naming paragraph (`` is `gate-a-spec-pass-

`, …) | §5 — the slot grammar gains an optional per-cycle infix | -| 19 | "On squash-merge, copy every evidence entry…" | §5.1 — three record types added to the carry | -| 20 | "Before each call, delete every target file and confirm it is gone" (`:199/:386`) | §5 — the slot grammar gains an infix, and the rule gains a **refuse-on-collision** case where the target belongs to another cycle | -| 21 | The `baseSha` / WIP / closing-amend block (`:500-517`, template `:679-696`) | §2.1 — the mid-cycle amend must retain the `WIP:` prefix and carry forward existing body records; §4.1 and §5.1 add records the closing body must contain | - -**Twenty-one passages.** The conditions artifact carries one dispositioned entry per row and is -incomplete without all twenty-one. - -**Rows 20 and 21 were missing until Gate-A pass 8**, which is worth recording rather than quietly -repairing: revision 8 added the rules that rewrite them — the refuse-on-collision case and the -WIP-amend rule — and did not extend the list. **That is the price of this split, stated plainly: -once the dispositions are deferred, the passage list is the sole guard against a dropped -condition, and the conditions artifact cannot catch what the list never named.** The list must -therefore be re-checked whenever the design adds a rule, and the plan's gate reads it against the -final text rather than trusting it. +| 17 | "Changing a profile" | §4.2 | +| 18 | The findings-slot naming paragraph | §5 — the grammar gains an optional per-cycle infix | +| 19 | "On squash-merge, copy every evidence entry…" | §5.1 — three record types added | +| 20 | "Before each call, delete every target file…" (`:199/:386`) | §5 — the infix, and a **refuse-on-collision** case where the target belongs to another cycle | +| 21 | The `baseSha`/WIP/closing-amend block (`:500-517`, template `:679-696`) | §2.1 — the WIP-amend properties; §4.1 and §5.1 add records the closing body must carry | + +**Row 18's change is a replacement, not an addition beside it.** Today's grammar admits three exact +names; an infixed name satisfies none. The shipped text replaces each with one admitting an +optional per-cycle infix, so there is **one rule** and bare slots stay valid — with the infix +**required** where the bare slot is occupied, a **refuse-rather-than-overwrite** rule when the +target belongs to another cycle, and **uniqueness** from the §2.1 nonce. + +--- ## 7. Prerequisite, rollout, and what this change falsifies -**The template lacks the sentence §3's kinship points at.** "a wrong sentence costs a confused -reader, not broken behaviour" is in `CLAUDE.md` and **absent from the template**. **The template -gets it**, so the kinship claim has a referent in both copies — a deliberate one-seam reduction of -the 192-line divergence, because the new rule depends on it. +**The template lacks the sentence §3's kinship points at** — "a wrong sentence costs a confused +reader, not broken behaviour" is in `CLAUDE.md` and absent from the template. **The template gets +it**, a deliberate one-seam reduction of the 192-line divergence because the new rule depends on +it, not a general reconciliation. -**User-facing statements this change falsifies**, from the standing lens "which existing -statements does this diff falsify?" — a change makes sentences wrong in files it never touches: +**Statements this change falsifies**, from the standing lens — a change makes sentences wrong in +files it never touches: -| Site | What it says | Why it is false after | -|---|---|---| -| `README.md:130` | "`codex-gate.floor` — a positive integer; moves the 3-passes-per-gate floor" | the §5 floor is derived and is not 3; the knob moves the **hook's reminder threshold**, which is all it ever moved | -| `docs/getting-started.md:34` | "three passes minimum, final pass clean" | not at level 0 | -| `:40` | "the same 3-pass" loop for the plan | not at level 0 | -| `:53` | Gate B "three passes, final clean" | not at level 0 | -| `:84` | "Gate A's floor is unchanged at every level" | **directly contradicted** — a sentence specifically about profile levels | -| `:86` | "moves the 3-pass floor" | same as README | -| `docs/getting-started.md:44-45` | "If the Gate-A floor wasn't met, the hook says so right when execution starts" | the hook reports against **its own** threshold, so at level 0 it can say the floor was not met when nothing further is owed — the reminder is right to fire and the sentence is wrong about what it means | -| `docs/getting-started.md:58-59` | the real commit replaces the WIP on `✓ Codex Gate B satisfied (3/3 cycle, 3 on current fingerprint)` | the trigger is a **clean final pass at the derived floor**, which at level 0 is `1/1`; a reader waiting for `3/3` waits for a message that will not come | -| `docs/coding-workflow.md:79-80` | the axes "never subtract any: **Gate A's floor** and the baseline questions are the same at every level" | **directly contradicted**, and missed until Gate-A pass 3 — the same claim as `:84` in a second file | - -**The rewording is honest rather than cosmetic.** README and getting-started describe the knob as -what it mechanically is — the hook's reminder threshold — and name the **sanctioned lever** for -wanting fewer obliged passes: the profile, or `codex-gate.off` for the reminders. Nothing is -deprecated; the file and its behaviour are preserved. What is removed is a promise the mechanism -never kept. - -**Gate-B classification of the doc edits — corrected.** An earlier revision called these paths -"Gate-B N/A". That is wrong when they ride with the prompt changes: §5's exemption requires -**every** staged path to be explanatory documentation, and a **mixed commit forfeits it**. So -either the doc edits land in their own docs-only commit (N/A applies) or they ride with the -prompt change (full Gate B applies to the whole commit). **The plan chooses and states which**; -what is not available is calling them exempt inside a mixed commit. - -**What the template edit does and does not reach.** It changes what `/workflow-init` **writes into -new or re-initialized projects**. It does **not** update the `CLAUDE.md` already in a downstream -project — those accumulate local content and invariant 9 forbids silent overwriting. Downstream -repos adopt by re-running `/workflow-init` and taking the diff it offers. - -**Packaging.** The change edits `plugins/dev-workflow/commands/workflow-init.md`, so invariant 12 -applies: a `plugin.json` **version bump** and a `CHANGELOG.md` entry are in the implementation -surface. CI enforces the bump on pull requests. +| Site | Why it is false after | +|---|---| +| `README.md:130` | the §5 floor is derived and not 3; the knob moves the **hook's reminder threshold**, which is all it moved | +| `docs/getting-started.md:34, :40, :53` | three-pass minimums, not true at level 0 | +| `:44-45` | says the hook reports when the Gate-A floor is unmet — it reports against **its own** threshold, so at level 0 it fires when nothing is owed | +| `:58-59` | waits for `✓ … (3/3 …)` before the closing amend; at level 0 that message never comes | +| `:86` | "moves the 3-pass floor" | +| `docs/coding-workflow.md:79-80` | "Gate A's floor and the baseline questions are the same at every level" — **directly contradicted**, the same claim as `:84` in a second file | + +**The rewording is honest, not cosmetic**: README and getting-started describe the knob as what it +mechanically is, and name the **sanctioned lever** for wanting fewer obliged passes — the profile, +or `codex-gate.off` for reminders. Nothing is deprecated. + +**Gate-B classification of the doc edits.** §5's exemption requires **every** staged path to be +explanatory documentation, and **a mixed commit forfeits it**. So the doc edits either land in +their own docs-only commit (N/A applies) or ride with the prompt change (full Gate B applies to the +whole commit). **The plan chooses and states which.** + +**What the template edit reaches.** It changes what `/workflow-init` writes into **new or +re-initialized** projects. It does **not** update a downstream project's existing `CLAUDE.md` — +invariant 9 forbids silent overwriting. Adoption is by re-running the scaffolder. + +**Packaging.** Editing `plugins/dev-workflow/commands/workflow-init.md` triggers invariant 12: a +`plugin.json` version bump and a `CHANGELOG.md` entry are in the implementation surface. --- ## 8. Evidence plan -Mode `battery+check+verification` (no `+abuse-path`; security is `none`). +Mode `battery+check+verification` (no `+abuse-path`). - **Battery** — the full `AGENTS.md` § Commands chain, green. -- **A check that fails without the change.** No automated test is possible for prose, so this - takes §5's other permitted route — a **named verification**, owing the same counterfactual. The - subject is §6.1's inventory, differential by construction: **posed as a question about behaviour - under floor 1, the pre-change text answers wrongly at identified sites** — `:79` states the early - exit as "below 3" where the floor may be 1, and `:126` says a Blocker/Major-free pass 1 carrying - a Minor keeps looping where floor 1 requires it to close — **while the post-change text answers - correctly.** That is the observation that would exist if the claim were false. - **The wiring must be able to produce the failure.** A verification consulting only the - post-change text cannot fail and would report success because of how it was wired. Both - revisions get read, and the entry records which sentences were read at which revision. -- **A named verification of the risk path.** The risk path is now narrow and specific: **the §5 - floor is derived from a cited-story set, and nothing mechanical checks the derivation.** The - verification takes this branch's own closing commits and confirms that **each provenance line's - stated floor equals the floor the cited stories' profiles actually derive**, recomputed from - those headers — the observation that would exist if the claim were false being a provenance line - whose number the profiles do not license. It also confirms that **no floor file is present at close where none was present at start**. - What that establishes, stated rather than implied: it detects a *persisting* write; it **cannot** - detect a transient one created and removed inside the cycle, because nothing observes the file - between the two checks. No rule here writes it, so a transient write would be a deviation from - the design rather than a permitted path — the check bounds the observable consequence, not the - behaviour. - *Two failure modes this deliberately avoids:* an absence check alone would pass vacuously if the - design never wrote a file (which it now never does), and a floor-1 demonstration is impossible on - this branch, whose cited story is risk `high` — story criterion 9 forbids manufacturing a level-0 - fixture for it. -- **A verification that a user's knob survives untouched — conditional, and honest when it does - not apply.** Where a `codex-gate.floor` exists before a cycle starts, it is byte-identical after - it closes and the provenance line discloses both values. **Where none exists, the verification - is recorded as not-applicable with that reason** — an agent must not create one to make a check - runnable, which would be a fixture supplying its own input, the wiring failure this same section - warns about. The design's stronger claim covers the gap: **no floor file is written on any - path**, which §8's risk-path verification checks unconditionally. -- **Parity verification across every changed rule**, per story criterion 7, covering **§6.1's - fourteen sites, §6.2's twenty-one passages, and every rule §§2–5 newly insert — including the §10 - residual disclosure and the §4.1 provenance forms, which story criteria require in **both** - shipped copies and which a §5-scoped parity walk would otherwise miss.** The copies already - differ on 192 lines, so parity cannot be asserted from a whole-section diff; the verification - walks each named item and records every difference as deliberate-and-stated or as a defect. - `scripts/check-invariants.sh` covers one severity spelling and is not coverage — invariant 11's - stated condition. -- **A fresh twelve-item `docs/prompt-standards.md` pass over every changed prompt region.** - Invariant 11 makes all twelve binding on any skill, command, agent definition, hook message or - scaffolded template, and this change edits two prompt copies plus user-facing docs. Naming the - absence of a mechanical checker is not the same as doing the reading; the entry records which - regions were read against which items. **Item 7 — "no contradictions with CLAUDE.md / - AGENTS.md" — is read against the whole resulting prompt, not only the changed regions**, because - a contradiction is a relation between an edited passage and an unedited one, and a diff-scoped - reading cannot see the second half of it. That is also the item this change is most able to - fail, since it rewrites `CLAUDE.md` itself. - -**When this evidence is produced and revalidated.** §5 requires the evidence entry to be -revalidated before every Gate-B re-review and before the cycle-closing amend, because a fix -changes the diff even when the profile sits still. Two of the verifications above read **this -branch's closing commits**, which do not exist until the cycle closes — so they are produced -against the `WIP:` snapshot during the cycle, and **re-read against the content the close will -carry — the staged tree plus the message about to be written — rather than against a commit that -does not exist yet.** The amend *is* the closing act, so "re-read the final commit before closing" -is circular. **What that check establishes is bounded and the text says so:** the index can change -between the read and the commit, and `git commit -a` or a path argument commits content the index -did not hold, so the observation is of *the intended content at the moment it was read*, not a -guarantee about the resulting commit. Narrowing the gap is what the hook's own content fingerprint -already does; this check does not duplicate it and does not claim to. If the amend changes what they observed, the clean pass no longer covers what is -being committed: fix, re-review, close on the entry that pass validated. - -**Instrument discipline, from this story's own evidence.** Two defects in the `fic2` decision -matrix were properties of the technique: **a state's inputs must include every input the rule -reads**, and **a counterfactual must distinguish ABSENT from CONTRADICTORY**. Both survived a full -clean pass before being caught. - -**Known open question, not resolved here.** How much instrument a one-paragraph prose rule is -worth is carried by the evidence doc as a question, not a commitment. +- **A check that fails without the change.** No automated test is possible for prose, so this takes + §5's other permitted route — a **named verification** owing the same counterfactual. Subject: + §6.1's site inventory, differential by construction — **posed as a question about behaviour under + floor 1, the pre-change text answers wrongly at identified sites** (`:79` states the exit as + "below 3"; `:126` says a Blocker/Major-free pass 1 carrying a Minor keeps looping, where floor 1 + requires it to close) **while the post-change text answers correctly.** **Both revisions get + read** — a verification consulting only the post-change text cannot fail and would report success + because of how it was wired. +- **A named verification of the risk path.** The risk path is that **the floor is derived by the + agent and nothing mechanical checks the derivation**. It recomputes each provenance line's floor + from the cited stories' headers; the observation that would exist if the claim were false is a + line whose number the profiles do not license. It also confirms **no floor file is present at + close where none was present at start** — which detects a *persisting* write and **cannot** detect + a transient one, stated rather than implied. +- **A conditional verification that a user's knob survives untouched** — byte-identical across a + cycle where one exists; **recorded not-applicable with its reason where none does.** An agent must + not create one to make a check runnable, which would be a fixture supplying its own input. +- **Parity across every changed rule**, per story criterion 7 — §6.1's fourteen sites, §6.2's + twenty-one passages, and every rule §§2–5 insert, including §10's residual disclosure and §4.1's + provenance properties, which the story requires in **both** copies. The copies already differ on + 192 lines, so parity cannot be asserted from a whole-section diff. +- **A fresh twelve-item `docs/prompt-standards.md` pass** over every changed prompt region. **Item + 7 is read against the whole resulting prompt**, not only changed regions — a contradiction is a + relation between an edited passage and an unedited one. + +**When produced and revalidated.** §5 requires revalidation before every re-review and before the +closing amend. Verifications reading closing commits are produced against the `WIP:` snapshot and +**re-read against the content the close will carry** — the amend *is* the closing act, so re-reading +"the final commit" is circular. **What that establishes is bounded**: the index can change between +the read and the commit. + +**Instrument discipline, from this story's own evidence.** Two `fic2` defects were properties of +the technique: **a state's inputs must include every input the rule reads**, and **a counterfactual +must distinguish ABSENT from CONTRADICTORY**. Both survived a full clean pass before being caught. --- ## 9. Out of scope -Hook code (anything under `plugins/dev-workflow/hooks/`); gate-call observability (upstream, -`mcp-codex-dev`); the pass-counter anomaly, undiagnosed; the CodeRabbit plan-metadata +Hook code (anything under `plugins/dev-workflow/hooks/`, including the reminder's own wording); +gate-call observability (upstream); the pass-counter anomaly; the CodeRabbit plan-metadata contradiction; the fixture-per-predicate question; any remedy to the supersession convention; deprecating or repurposing the user-facing floor knob; teaching the hook about profiles; and -general reconciliation of the two copies' 192-line divergence beyond the one seam §7 names. +general reconciliation of the 192-line divergence beyond §7's one seam. --- ## 10. Risks and activation -- **When these rules bind.** From the commit that ships them, and **a loop already in flight - finishes under the rules it started with** — re-deriving a floor mid-loop from a rule that did - not exist when passes were banked would invalidate a count nobody could reconstruct. **Where a loop's starting rules cannot be established, it - takes the stricter reading of every rule this change touches, not only the floor**: floor 3; - severity classified without the demotion; the ordering's suspensions treated as binding; decline - records treated as absent, so no hold is released; and the curve duty treated as owed. A fallback - scoped to the floor alone would leave the other three parts silently unresolved, which is the - same partial-rewrite failure the old-conditions Don't describes. Concretely, the floor part is - **floor 3** — not a re-derivation, which - could hand a level-0 loop a floor of 1 and *skip* passes on the strength of not knowing when it - started. The conservative value is the point; "re-derive" was the wrong instruction and did not - do what the sentence beside it claimed. An earlier revision proposed git timestamps plus file mtime as a start - marker; that does not survive checkout, copy or clock skew, so no marker is claimed and the - fallback carries the case. This also disposes of this design's own Gate A, which runs under the old rules — **with one - consequence worth stating, since two story criteria require this branch to demonstrate the - provenance line and the curve.** Those are obligations on *what this branch commits*, not claims - about which rules its Gate-A loop was reviewed under. A loop reviewed under the old rules can - still record its curve and its floor, because recording is an act of the commit rather than a - rule the passes were judged by. Nothing here is retroactive and nothing here is exempt. -- **Downstream adoption has no shipping commit.** The template travels into other repositories - whose history does not contain this change, so "from the commit that ships them" is a statement - about *this* repo. Downstream, the rules bind from the `/workflow-init` run that **actually writes them**, which is - not every run: invariant 9 makes the command idempotent and non-overwriting, so a run may report - the file unchanged, or show a diff and be declined, or merge only part of it. **The rules bind - only over the text a project's `CLAUDE.md` actually contains**, a project may therefore sit on a - partial adoption indefinitely, and nothing detects that. **This is in tension with §2's argument - that the three parts are coupled**, and the tension is real rather than resolved here: a - downstream project that takes the severity rule without the precedence ordering gets a loop whose - exits are unordered and whose severities have moved, which is a state this design never - evaluated. What can be done in prompt text is done — the scaffolded copy ships the three together - and `/workflow-init` presents them as one diff — and what cannot is said: nothing prevents a - human from accepting half — the same floor-3 fallback applies - wherever the text does not settle the question. -- **The gate-off surface — the routes known today, not a complete list.** An enumeration read as - complete guarantees the routes it omits, which is the AGENTS.md Don't in its own words; this - one is a floor. **One route below is created by this design and is named as such**: a stated - floor the cited set does not license did not exist before, because before this change there was - no derived floor to state. The others are pre-existing and unchanged: falsifying evidence entries, misreporting a battery, silencing reminders with - `codex-gate.off`, or simply not running a pass and reporting that it ran. Deleting the marker mechanism removed the - camouflage path and every crash-recovery path. What remains: **writing a provenance line the - cited set does not license; omitting a higher-risk cited story from the set; minting or editing a - story profile to level 0; presenting an incomplete cited-story set.** All four are *statement* - attacks now rather than *file* attacks — they live in the commit body, where a reader who checks - the branch's actual work can see them, and where §8's risk-path verification recomputes the - derivation. None of this is a guard, and the profile-minting path is bounded only by the - human-confirmation rule §5 already imposes on profile changes. -- **A user-set floor is not the gate-off lever, and the text keeps them distinct.** It moves what - the hook says. The lever is a *stated* floor the cited profiles do not license. -- **The reachability test needs judgement** where §5 is trying to remove it; the - named-reader-and-changed-decision phrasing removes arbitrariness, not judgement, and §3 says so. -- **Q6's answer fails toward continuing the loop.** Stated in §5 with the disclosure that makes it - acceptable. -- **The curve is self-reported.** §5.1 says so; P8 inherits that limit and must not present a - self-reported curve as measurement. -- **The expected demotion is a prediction.** §3 says so; P8 measures it. If it demotes far less - than hoped, the rule is still correct and the economics claim was what was wrong. +- **When these rules bind.** From the commit that ships them, and **a loop already running finishes + under the rules it started with** — re-deriving a floor mid-loop from a rule that did not exist + when passes were banked would invalidate a count nobody could reconstruct. **Where a loop's + starting rules cannot be established it takes the stricter reading of every part this change + touches**, floor 3 included — not a re-derivation, which could hand a level-0 loop a floor of 1 + and *skip* passes on the strength of not knowing when it started. +- **Downstream has no shipping commit.** The template travels into repositories whose history does + not contain this change, so adoption binds from the `/workflow-init` run that **actually writes** + the text — which invariant 9 permits to write nothing, be declined, or be merged in part. **The + rules bind only over the text a project's `CLAUDE.md` contains**, and a partial adoption can + persist undetected. **This is in tension with §1's coupling argument and the tension is real**: a + project taking the severity rule without the ordering gets a loop this design never evaluated. + What prompt text can do is done; what it cannot is said. +- **The gate-off surface — routes known today, not a complete list**, since an enumeration read as + complete guarantees what it omits. **One route is created here and is named as such**: a stated + floor the cited set does not license, which could not exist before there was a derived floor to + state. Pre-existing and unchanged: omitting a higher-risk cited story, minting or editing a + profile to level 0, presenting an incomplete set, falsifying evidence entries, silencing + reminders, or not running a pass and reporting that it ran. **None of this is a guard**, and the + profile-minting path is bounded only by §5's existing human-confirmation rule. +- **A user-set floor is not the gate-off lever**, and the text keeps them distinct: it moves what + the hook says; the lever is a *stated* floor the profiles do not license. +- **The reachability test needs judgement** where §5 is trying to remove it; the phrasing removes + arbitrariness, not judgement, and §3 says so. +- **Q6's answer fails toward continuing the loop**, with the disclosure that makes it acceptable. +- **The curve is self-reported**; P8 inherits that limit. +- **The expected demotion is a prediction**; P8 measures it. If it demotes far less than hoped, the + rule is still correct and the economics claim was what was wrong. + +--- + +## 11. What revision 10 moved, and what it did not + +**Level-of-detail change. Contract unchanged.** No decision moved to the plan; where slimming a +section would have required deciding something, the rule stayed here. + +**Moved to the plan** — each reviewed in the plan's own Gate A against this spec: the provenance +line's concrete grammar, productions and examples (§4.1 keeps its required properties); the curve's +concrete format and worked example (§5.1 keeps its properties); command lines for the mid-cycle +amend (§2.1 keeps the required properties, which is what made the earlier pinned `-m` form's defect +visible); the per-shape diagnostic checks in §5; per-site replacement wordings for §6.1's fourteen +sites and §6.2's twenty-one passages; and the conditions artifact's production procedure (§6 keeps +the method and the gate). + +**Kept here, deliberately:** every settled decision with its reason; every rule stated as a +required property; the precedence table; the hold rule; the reachability test and its exclusions; +the floor predicate, unanimity and the hook-precedence rule; Q6's answer and the rejected +alternatives with reasons; the accounting method, the passage list and the split's price; the +falsified-statement inventory; the evidence plan's shape and counterfactuals; scope; and the risks +with their stated limits. + +**Dropped, not moved:** narration of what earlier revisions of *this document* got wrong. Where +such an error produced a rule, the rule is here and the history is in the commit bodies and in +`docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md`, which is where a reader looking for +the lesson should be sent. From c513094dadeba6c39b9a41b5b457b49970995f77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 10:28:14 +0200 Subject: [PATCH 024/117] =?UTF-8?q?docs:=20revision=2011=20=E2=80=94=20rep?= =?UTF-8?q?air=20what=20the=20restructuring=20compressed=20away?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 9: 28 findings, 2 BLOCKER, 22 Blocker/Major -- the lowest of the loop on all three counts. Zero tells. The size prediction ("well below 20 Blocker/Major") was NOT met at 22, though it fell 28 -> 22, the largest drop since the pass-3 spike. The restructuring claim was wrong and this records where. Revision 10 said "level of detail change, contract unchanged". Pass 9 was asked to judge that claim directly and found TWO BLOCKERS AND SIX MAJORS where compression had weakened or dropped a normative detail rather than relocating it. A guard that only asks "did a decision move?" misses the case where a rule survives in outline and loses its force. The worst was the severity test. Revision 9 required BOTH an in-system reader and a changed decision; revision 10 compressed it to "if you can name neither", which demotes only when both are absent and lets a finding keep severity when one exists. That inverts the central test of part 2. The second: the provenance grammar and the curve format went to the plan, though both are DURABLE INTERFACES SOMETHING OTHER THAN A HUMAN PARSES, and the story expressly requires the spec to state the form. Both are back, and §11 now names the test that separates detail from contract here: does anything but a person parse it. Six more restored: the nonce's collision-resistance and [a-z0-9]{4,16} constraint, and its presence in every record rather than somewhere in history; the decline's distinctness from the human-exception record, which differ in force -- one authorizes nothing, the other has defined effect; nonce recovery for an interrupted Gate-A loop, whose commit does not exist while it runs; which two tells are computable from the current pass; and the unknown-start fallback's mapping for severity, suspensions, declines and the curve rather than "the stricter reading". Also fixed: "absent" history no longer reads as overriding §5's recovery attempt; the slot infix is required whenever more than one cycle could write the slot, not only after a collision, since two concurrent new cycles both find it free; the squash carry gains a skipped loop's skip record; the conditions artifact's gate now has a reviewer, an acceptance condition and a failure consequence; a cited SET changing mid-cycle is covered, with adding a story treated as a raise; and the claim that narrowness makes an unauthenticated decline record "safe" is corrected -- narrowness bounds blast radius, and a fabricated decline still releases a real hold with nothing detecting it. New: a rollback risk. In this repo a bad rule reverts like any commit; downstream there is no revert, since a project's CLAUDE.md is its own file. Cheap to stop shipping, slow to un-ship. Story: two criteria added for obligations nothing observed -- every falsified user-facing sentence corrected in the same change, and the manifest version and CHANGELOG updated -- plus the floor criterion now requires each pass report to state its derived floor, so the value is visible while passes are still being spent. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-9. Findings 27, 30, 54, 40, 33, 34, 32, 33, 28. Blockers 5, 2, 0, 4, 0, 3, 9, 4, 2. Blocker/Major 24, 22, 43, 31, 30, 26, 29, 28, 22. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 172 ++++++++++++++---- ...-review-loop-economics-pass-floor-story.md | 8 + 2 files changed, 145 insertions(+), 35 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 319cebc..1a9379b 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,7 +1,7 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 10 (restructured), after Gate-A passes 1-8 -(27, 30, 54, 40, 33, 34, 32, 33 findings) +**Date:** 2026-08-28 · **Revision:** 11, after Gate-A passes 1-9 +(27, 30, 54, 40, 33, 34, 32, 33, 28 findings) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. @@ -107,7 +107,11 @@ and unambiguous. Never silence, never a general remark about scope, never the ag **Required properties of the record** — the plan fixes the format: - Lives in the **commit body**, reusing the human-exception transport (Gate-A loops: the spec or - plan commit, written at close; Gate B: the `WIP:` body by amend, restated by the closing amend). + plan commit, written at close; Gate B: the `WIP:` body by amend, restated by the closing amend) + — **as a distinct record type carrying its own label**, never merged into or mistaken for a + human-exception record. The two differ in force and the shipped text says so: the + human-exception form **authorizes nothing**, while a decline has §5-defined effect on one named + finding. A reader who cannot tell them apart has the wrong rule for both. During a loop the decision lives in the advisory working record and *becomes* the record at commit; the decline's effect never waits for the commit. - Carries enough of the finding to **re-identify it**: location, defect, severity, consequence and @@ -118,15 +122,23 @@ and unambiguous. Never silence, never a general remark about scope, never the ag - Names **who decided and when**, and survives the squash carry. - **Bound to one cycle** by the cycle nonce below. A decline has no effect in any later cycle. - **Reads as an unverified assertion**, exactly like the human-exception record beside it: nothing - checks that the handle belongs to whoever decided. What makes it safe is **narrowness** — one - fully-identified finding, one cycle — not verification. The shipped text says so. - -**The cycle nonce.** Generated once at cycle start, immutable, recorded in history, and restricted -so it is safe as a slot infix. **Deriving it from a gate plus a commit does not work**: sibling + checks that the handle belongs to whoever decided. **Narrowness bounds what a false record can do — one fully-identified finding, one cycle — and + that is not the same as making it safe.** A fabricated decline still releases a real hold on a + real finding, and nothing detects it. The shipped text says exactly that, because "narrow" read + as "safe" is the overclaim this repo logs most often. + +**The cycle nonce.** Generated once at cycle start, immutable, **collision-resistant**, and +matching `[a-z0-9]{4,16}` so it is safe as a slot infix and as a path component. **It appears in +every record the cycle writes**, not merely somewhere in history — a record without it cannot be +attributed to a cycle, which is the whole function. **Deriving it from a gate plus a commit does not work**: sibling worktrees and restarted loops share loop names and base commits, and a `WIP:` parent identifies the base rather than the artifact reviewed. **The reviewed commit or tree id is tracked separately**, because "which cycle is this" and "did both branches see the same thing" are -different questions. A cycle that cannot recover its nonce has no identity and starts a new cycle. +different questions. **Recovery has two sources, because a Gate-A loop's commit does not exist while it runs:** during +the loop the nonce lives in the advisory working record beside the findings files, and it becomes +history at the loop's commit. A cycle that can recover it from **either** keeps its identity; a +cycle that can recover it from **neither** has no identity and starts a new cycle — which costs +passes rather than silently inheriting a decline. **The mid-cycle amend must preserve what it amends.** Required properties: the resulting message **begins `WIP:`** — the hook recognizes a WIP commit from the message the command supplies, and an @@ -141,8 +153,9 @@ one. The **non-`WIP:` amend is reserved for final closure alone.** **One procedure decides severity, and the subject list is illustration, not a second rule.** > Name **what in the system consumes this text** — whatever *acts* on it — and the decision that -> act takes differently if the text is wrong. If you can name neither, the finding is **Minor or -> below**; collect, never iterate. +> act takes differently if the text is wrong. **Both are required.** If you cannot name **both** +> — the act, and the decision it takes differently — the finding is **Minor or below**; collect, +> never iterate. - **The reader must consume the text in the system's *operation*, not in reviewing it. The review pass raising the finding is not an in-system reader of the text it reviews.** Without this the @@ -215,6 +228,12 @@ on every branch (invariant 1). > threshold and controls nothing.** Where the derived floor and the ordinary closure rules are > satisfied, a below-threshold reminder is **noted in the pass report and disregarded.** +**The first eligible floor-1 cycle is P8's first checkpoint.** The story cannot demonstrate floor 1 +on this branch — it is risk `high` — so the demonstration is deferred: **the first post-merge cycle +whose cited set licenses floor 1 must carry the floor-1 provenance line, and the P8 measurement +reads it.** That is what makes the reduced floor observable at all, and it is why the provenance +grammar is pinned here rather than downstream. + **Named residual:** the hook's own message says a cycle "MUST reach a minimum" at its threshold (`codex-gate.sh:933`), which at level 0 contradicts a legitimate one-pass close. **Hook text is out of scope by decision**, so this is disclosed, not fixed; what makes it tolerable is the precedence @@ -228,7 +247,32 @@ remove:** anyone can still write the gitignored knob and quiet the hook. What it design's reliance on writing it, and with that the ambiguity — a floor file is now **always** a user artifact. -**Required properties of the provenance line** — the plan fixes the grammar: +**The provenance line's grammar is pinned here, not in the plan.** It is a **durable interface a +later reader consumes** — story criterion 3 requires one form and says the spec states which — so +unlike a replacement wording it cannot be settled downstream without leaving P8's input to the +plan. Revision 10 moved it and was wrong to; that is the one place the slimming crossed from +detail into contract, and it is the reason the restructuring guard exists. + +``` +floor per ; hook reminder threshold + + := "none" the artifact cites no story + | "{" ("," )* "}" + := " (level " <0|1|2> ")" a profiled story + | " (unprofiled)" a cited story with no profile + := "absent" | | "unusable" +``` + +Paths are repo-relative and contain no `,`, `{`, `}` or `;`; a path that would is written in +double quotes with `\"` escaping. **The same reasoning fixes the curve's form in §5.1**: both are +read by something other than a human. + +**The residual disclosure this implies ships in both copies**, in the words the story requires: +that the floor a cycle owes is **produced by the agent**, that **nothing checks it** against the +cited profiles, and by what routes it can therefore be wrong (§10). A copy carrying the grammar +without the disclosure would present a parseable number as a verified one. + +**Required properties the grammar exists to satisfy:** - **Every cycle records it**, default or not, so an absent line is never ambiguous between "the default applied" and "someone forgot". - It states **one floor and the cited set that produced it, with each member's level as a @@ -245,6 +289,12 @@ user artifact. Three existing rules compose; no new rule. The floor derives from the **current** profile at each pass; **passes already run keep counting**; **closing requires the floor as currently derived.** +**The cited *set* can move too, not only a profile's values** — a story added, removed or +corrected mid-cycle — and the same three rules cover it: the set is re-read at each pass, the +floor is re-derived from it under unanimity, and closure requires the floor the current set +yields. **Adding a story is a raise for this purpose** and carries the same one-further-pass +consequence. + **The consequence that must be stated:** a **raise costs at least one further pass regardless of the arithmetic**, because §5 already requires the final clean pass to run under the current profile — so even a raise leaving the floor unchanged costs a pass. **A lowering** drops the floor, @@ -259,12 +309,16 @@ human-confirmed and logged; the variable floor rides it and does not create it. **Report what is computable, name what is not and why, and disclose the reduced sensitivity.** Not a new stop condition, not a mandatory resume note. -Three of the five tells need history; **two are computable from the current pass alone**, so the -two-tell threshold **remains reachable**. The duty loses sensitivity; it does not become +Three of the five tells need history — the finding count rising, the Blocker count failing to +fall, and a require↔withdraw pair. **Two are computable from the current pass alone: findings +clustering on the instrument, and findings clustering on prose about either.** So the two-tell +threshold **remains reachable** on that pair. The duty loses sensitivity; it does not become inoperative. **Five shapes, each with its own check and remedy** — the plan carries the check specifics: -**absent** (no file; unrecoverable, since re-running produces a different pass); **partial** (some +**absent** (no file — and *for the trend* it is unrecoverable, since a fresh run produces a +different pass rather than that one; this does not touch §5's single shared recovery attempt, +which applies to the pass being run now, not to reconstructing an earlier one); **partial** (some slots present — compute over what exists and **name the missing pass numbers**, since a trend over an unstated subset reads as a trend over the cycle); **malformed** (fails any pass-acceptance check — treated as absent, **never** as zero findings, and re-runnable only while its `sessionId` @@ -291,7 +345,19 @@ mandatory disclosure is what makes it acceptable. body**, labelled with the loop it describes. **Gate B alone would leave the dominant cost unmeasured** — the loops this story cites as evidence are Gate-A loops. -**Required properties** — the plan fixes the format: +**The form is pinned here**, for the same reason as the provenance line: P8 reads it, so leaving +it to the plan would leave a durable interface undecided. + +``` + (passes , ): Findings , , …. Blockers , , …. + := "Gate-A spec loop" | "Gate-A plan loop" | "Gate B" + := a comma-and-range list of the pass numbers covered, e.g. "1-3" or "1,2,4" + := one model name, or "pass

" entries where they differ +``` + +A skipped loop writes `: skipped (see skip reason)` and no counts. + +**Required properties the form exists to satisfy:** - One entry per **valid** pass, in pass order; **incomplete passes are excluded**, and because they consume pass numbers the record **states which pass numbers it covers**. A valid zero-finding pass is recorded as zero, never omitted. @@ -313,7 +379,9 @@ nothing compares it against the validated pass files, so **P8 reads a self-repor text says so rather than letting it be treated as measurement. **Squash carry.** §5's rule names only evidence entries and human-exception records; the **decline -records, the provenance line and these curves** are added, in both copies. +records, the provenance line, these curves and a skipped loop's skip record** are added, in both +copies. A skip record that does not survive the squash leaves an unexplained gap in exactly the +history P8 reads. --- @@ -328,8 +396,11 @@ without its accounting.** **Where the dispositions are produced and gated.** In one artifact, `docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md`, written **once against -the frozen final text** and **reviewed before any replacement text is written**. The plan names -that gate explicitly. +the frozen final text** and **reviewed before any replacement text is written**. The gate is a +gate, so it has the parts a gate has: it is **a Gate-A review of that artifact** under this +story's profile, its **acceptance condition is a clean pass at the derived floor** like any other, +and **failure means no replacement text is written** rather than a note and a continuation. The +plan names where in its sequence that falls. **The pass-2 tension, recorded rather than resolved by hindsight.** Gate-A pass 2 raised a Blocker demanding the inventory live *in this spec* rather than being deferred to the plan — correct, since @@ -393,8 +464,12 @@ without all of them.** **Row 18's change is a replacement, not an addition beside it.** Today's grammar admits three exact names; an infixed name satisfies none. The shipped text replaces each with one admitting an optional per-cycle infix, so there is **one rule** and bare slots stay valid — with the infix -**required** where the bare slot is occupied, a **refuse-rather-than-overwrite** rule when the -target belongs to another cycle, and **uniqueness** from the §2.1 nonce. +**required whenever more than one cycle could write that slot** — which includes a bare slot +already occupied *and* the case two new cycles start concurrently, where neither finds an occupied +slot and both would take the bare name. In practice: **a cycle that has a nonce uses it**, so the +bare form is reserved for the single-cycle case it already serves. Plus a +**refuse-rather-than-overwrite** rule when the target belongs to another cycle, and **uniqueness** +from the §2.1 nonce. --- @@ -489,12 +564,19 @@ general reconciliation of the 192-line divergence beyond §7's one seam. ## 10. Risks and activation +**Everything in this section that is a rule rather than a note appears in both shipped copies**, +per the story's activation criterion: when the rules bind, the unknown-start fallback with its +named parts, and the partial-adoption consequence. The risks that are *observations about this +design* rather than instructions to a future agent stay here. + - **When these rules bind.** From the commit that ships them, and **a loop already running finishes under the rules it started with** — re-deriving a floor mid-loop from a rule that did not exist - when passes were banked would invalidate a count nobody could reconstruct. **Where a loop's - starting rules cannot be established it takes the stricter reading of every part this change - touches**, floor 3 included — not a re-derivation, which could hand a level-0 loop a floor of 1 - and *skip* passes on the strength of not knowing when it started. + when passes were banked would invalidate a count nobody could reconstruct. **Where a loop's starting rules cannot be established it takes the stricter reading of every part + this change touches**, named rather than left to interpretation: **floor 3**; **severity + classified without the demotion**, so nothing is collected that would otherwise iterate; **every + suspension treated as binding**; **decline records treated as absent**, so no hold is released; + and **the curve duty treated as owed**. Not a re-derivation, which could hand a level-0 loop a + floor of 1 and *skip* passes on the strength of not knowing when it started. - **Downstream has no shipping commit.** The template travels into repositories whose history does not contain this change, so adoption binds from the `/workflow-init` run that **actually writes** the text — which invariant 9 permits to write nothing, be declined, or be merged in part. **The @@ -509,6 +591,13 @@ general reconciliation of the 192-line divergence beyond §7's one seam. profile to level 0, presenting an incomplete set, falsifying evidence entries, silencing reminders, or not running a pass and reporting that it ran. **None of this is a guard**, and the profile-minting path is bounded only by §5's existing human-confirmation rule. +- **Rollback, once a rule has been adopted.** In this repo a bad rule is reverted like any other + commit, and the §10 activation rule then applies to loops in flight. **Downstream there is no + revert**: a project's `CLAUDE.md` is its own file, so withdrawing a rule means shipping a + corrected template and waiting for each project to re-run the scaffolder and accept the diff — + the same partial-adoption path, with the same absence of detection. **A rule that turns out + wrong is therefore cheap to stop shipping and slow to un-ship**, which is an argument for the + gates rather than a gap this design can close. - **A user-set floor is not the gate-off lever**, and the text keeps them distinct: it moves what the hook says; the lever is a *stated* floor the profiles do not license. - **The reachability test needs judgement** where §5 is trying to remove it; the phrasing removes @@ -522,16 +611,29 @@ general reconciliation of the 192-line divergence beyond §7's one seam. ## 11. What revision 10 moved, and what it did not -**Level-of-detail change. Contract unchanged.** No decision moved to the plan; where slimming a -section would have required deciding something, the rule stayed here. - -**Moved to the plan** — each reviewed in the plan's own Gate A against this spec: the provenance -line's concrete grammar, productions and examples (§4.1 keeps its required properties); the curve's -concrete format and worked example (§5.1 keeps its properties); command lines for the mid-cycle -amend (§2.1 keeps the required properties, which is what made the earlier pinned `-m` form's defect -visible); the per-shape diagnostic checks in §5; per-site replacement wordings for §6.1's fourteen -sites and §6.2's twenty-one passages; and the conditions artifact's production procedure (§6 keeps -the method and the gate). +**Mostly a level-of-detail change — and revision 11 records where that claim was wrong.** Gate-A +pass 9 reviewed the restructuring against exactly this claim and found **two Blockers and six +Majors where compression had weakened or dropped a normative detail**, not merely relocated it. +The clearest: the severity test had become "if you can name neither", which demotes only when +*both* a reader and a changed decision are absent — inverting a rule that requires both. The +provenance grammar and the curve format had gone to the plan although both are **durable +interfaces a later reader consumes** and the story requires the spec to state them. + +All eight are repaired above, and the lesson is recorded rather than smoothed over: **a +restructuring guard that only asks "did a decision move?" misses the case where a rule survives in +outline and loses its force.** The check that caught it was asking the reviewer to judge the claim +directly. + +**Moved to the plan** — each reviewed in the plan's own Gate A against this spec: command lines +for the mid-cycle amend (§2.1 keeps the required properties, which is what made the earlier pinned +`-m` form's defect visible); the per-shape diagnostic check specifics in §5; per-site replacement +wordings for §6.1's fourteen sites and §6.2's twenty-one passages; and the conditions artifact's +production procedure (§6 keeps the method and the gate's acceptance condition). + +**Returned to the spec after pass 9:** the provenance grammar and the curve form. Both are read by +something other than a human, so leaving them downstream would leave P8's input undecided — the +test that separates detail from contract here is **"does anything but a person parse it"**, not +length. **Kept here, deliberately:** every settled decision with its reason; every rule stated as a required property; the precedence table; the hold rule; the reachability test and its exclusions; diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 6cd3a05..3940a28 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -116,6 +116,8 @@ deliverable rather than a convenience. cited story profiled, every one at level 0 — so a single higher-profile or unprofiled member returns the cycle to 3. Stated in both copies, and falsifiable: a copy silent on the multi-story case leaves the cheapest wrong reading available. + **Every pass report states the floor it derived and the axes it read**, so the value is + visible while passes are still being spent rather than only in the closing commit. Each copy also states that **one derived value governs every loop of the cycle** — the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle alike — and says *why*: those loops cite the same stories, so the value they derive is the same. A copy that states the @@ -213,6 +215,12 @@ deliverable rather than a convenience. adopts by re-running the scaffolder, which may write nothing, be declined, or be merged in part — with the consequence of a partial adoption stated rather than assumed away. Checkable by reading either copy. +- [ ] **The change leaves no shipped sentence contradicting it, and the package it ships in is + valid.** Every user-facing statement this change falsifies is corrected in the same change — + the inventory is in the spec and a reviewer can check each cited line — and the plugin + manifest's version and `CHANGELOG.md` are updated, which CI enforces on pull requests. Both + are checkable after the fact: no corrected sentence still asserts a fixed three-pass floor, + and the manifest version differs from its value on the base ref. - [ ] **Every condition of the replaced prose is accounted for.** The change lists what each replaced §5 passage required and marks each requirement kept, moved, or deliberately dropped, per the AGENTS.md Don't quoted in §4. Checkable: the accounting exists and From 32bf49dcc8091b225133b110e4004c7907bd488d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 10:48:22 +0200 Subject: [PATCH 025/117] docs(field-report): record the fourth announce-then-idle occurrence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fourth occurrence 2026-08-29, spotted by Daniel watching the terminal rather than by a peer check. The pattern is now specific enough to name: the announcement sentence lands at the END of an outbound message and the call never follows. Remedy tightened accordingly -- issue the gate call FIRST, then write the report, and never write "running pass N" unless the call is in flight. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md index 8845586..42006be 100644 --- a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md +++ b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md @@ -343,7 +343,7 @@ time. **Also observed, 2026-08-28, same cycle:** three occurrences of an agent ending a turn on an announcement — "writing the spec now", "running pass 6" — with the named tool call never issued. -Each cost a round-trip and one cost ~90 minutes of wall clock before a peer session noticed. No +A fourth followed on 2026-08-29 ("running pass 10"), spotted by Daniel watching the terminal. Each cost a round-trip and one cost ~90 minutes of wall clock before a peer session noticed. No error, no timeout, nothing in flight: the announcement simply replaced the act. Remedy adopted for the remainder of that cycle: during an active gate cycle a turn ends with the tool call actually issued, a message sent, or an explicit statement that something is blocking — and noticing the From 52611e89cca15cce2a2657ea921b84eac825cc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 11:03:52 +0200 Subject: [PATCH 026/117] =?UTF-8?q?docs:=20revision=2012=20=E2=80=94=20a?= =?UTF-8?q?=20closure=20deadlock,=20and=20the=20nonce=20the=20formats=20co?= =?UTF-8?q?uld=20not=20carry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 10: 27 findings, 2 BLOCKER, 23 Blocker/Major. One tell present (Blockers flat at 2, which is failing to fall), below the mandatory two. The deadlock. §1 said a cycle that cannot establish resolution does not close; §5 said unavailable history is not a stop condition and an absent file is unrecoverable. Both are right about their own subject -- §5's answer governs the tell computation, a reporting duty, while §1's is a precondition on closure -- and together they left a resumed cycle with lost history unable to close and with no terminal path. It now has one: such a cycle STOPS AND SURFACES to the human, naming which passes it could not read and which findings it cannot account for, and the human's answer resumes it. What is forbidden is closing silently over an inventory nobody could check, not the cycle existing. The nonce was mandatory in every record and absent from every pinned format. It now leads both grammars, which is what makes "appears in every record" something the formats can satisfy. The passage list gained rows 22 and 23 -- the evidence entry and the optional companions -- because the nonce rule reaches records the list never named. Twenty-three passages. Also: collision-resistance is now operational rather than aspirational (at least 8 characters from a random source, never derived from a name, timestamp or commit, each of which collides exactly where sibling cycles do); nonce recovery has a rule for disagreement and for multiple candidates, both resolving to "no identity, start fresh"; the advisory working record is a cycle record too, and carries the nonce and the collision rule; the MODELS production is complete, with undetermined written rather than guessed; the provenance grammar has productions for N and for quoted paths, plus one worked instance per variant; the residual names all three hook lines that render a ratio, not just :933; the unknown-start fallback states that its list is the whole of it and that a user knob above 3 is not lowered by it; a revert is itself a shipping commit, so a loop crossing it lands in the unknown-start case deliberately; removing a cited story lowers the floor and releases nothing, since an accepted finding entered by the user's answer and not by the story that raised it; the conditions artifact's review owes its own curve, because a gate exempt from the rules it enforces is the gate-off path in miniature, and a finding there feeds back rather than being absorbed. And a correction to §1: the findings files establish the INVENTORY of in-set Blocker and Major findings, not the resolutions, which they do not contain. Each resolution is established from what the cycle did -- the diff, the later pass that no longer raises it, or a recorded decline. Reading a resolution out of a findings file is reading something never written there. Story: the pass report must state the axes and their source stories, not only the floor; criterion 5(b) now requires BOTH halves of the test to be nameable and says a copy demoting only when both are absent inverts the rule; the decline criterion requires the record to be legible as its own kind; §4 gains invariants 9 and 10, both of which the spec relies on; and the demonstration string is now required to be an instance of the pinned form rather than a shorthand that cannot parse. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-10. Findings 27, 30, 54, 40, 33, 34, 32, 33, 28, 27. Blockers 5, 2, 0, 4, 0, 3, 9, 4, 2, 2. Blocker/Major 24, 22, 43, 31, 30, 26, 29, 28, 22, 23. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 136 ++++++++++++++---- ...-review-loop-economics-pass-floor-story.md | 27 +++- 2 files changed, 127 insertions(+), 36 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 1a9379b..5271afd 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,15 +1,17 @@ # Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design -**Date:** 2026-08-28 · **Revision:** 11, after Gate-A passes 1-9 -(27, 30, 54, 40, 33, 34, 32, 33, 28 findings) +**Date:** 2026-08-28 · **Revision:** 12, after Gate-A passes 1-10 +(27, 30, 54, 40, 33, 34, 32, 33, 28, 27 findings) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. **What this document is, after revision 10.** Contract level only: settled decisions with their -reasons, rules stated as required properties, named interfaces, and scope. **Exact replacement -wordings, concrete formats, command lines and step-by-step procedures live in the plan**, where -they get their own Gate A against this spec once it is stable. Nothing escapes review by moving; +reasons, rules stated as required properties, named interfaces, and scope. **Exact replacement wordings, +command lines and step-by-step procedures live in the plan**, where they get their own Gate A +against this spec once it is stable. **Two formats are the exception and are pinned here**: the +provenance line (§4.1) and the per-pass curve (§5.1), because **something other than a person +parses them** — that is the test separating detail from contract here, not length. Nothing escapes review by moving; what changes is when it is reviewed. Revision 10 is a level-of-detail change and moves no decision — §11 lists what went where. @@ -53,9 +55,22 @@ both reasons. closure. **Blocker/Major-must-resolve applies to in-set findings and is a precondition on closure not discharged by a quiet pass** — a pass raising no *new* Blocker/Major is not evidence an earlier one was resolved, so the closing report inventories each in-set Blocker/Major and its -resolution from the per-pass findings files. **Where those files are unavailable, that is a -disclosure, not a discharge: a cycle that cannot establish resolution does not close.** "Cannot -tell" and "was resolved" are different states and only one permits closing. "A surfaced finding +resolution. **The findings files establish the *inventory* — which in-set Blocker and Major +findings exist — and not the resolutions, which they do not contain.** Each resolution is +established from what the cycle actually did: the diff, the later pass that no longer raises it, +or a recorded decline. Reading a resolution out of a findings file is reading something that was +never written there. **Where those files are unavailable, that is a +disclosure, not a discharge: a cycle that cannot establish resolution does not close on its own.** +"Cannot tell" and "was resolved" are different states and only one permits closing. + +**And it does not loop forever either, which needs saying because §5 declines to make unavailable +history a stop condition.** Those two rules govern different things — §5's answer is about the +*tell computation*, a reporting duty, while this is a *precondition on closure* — and together +they would leave a resumed cycle with lost history unable to close and with no terminal path. So +the path is named: **a cycle that cannot establish resolution stops and surfaces to the human**, +reporting which passes it could not read and which findings it therefore cannot account for. The +human's answer resumes it, as any other surfaced stop does. What is forbidden is closing *silently* +over an inventory nobody could check — not the cycle continuing to exist. "A surfaced finding stays open" is the mechanism making the other exits suspensions. **"No pass carrying a surfaced finding counts as clean" is the only participant.** @@ -127,8 +142,11 @@ and unambiguous. Never silence, never a general remark about scope, never the ag real finding, and nothing detects it. The shipped text says exactly that, because "narrow" read as "safe" is the overclaim this repo logs most often. -**The cycle nonce.** Generated once at cycle start, immutable, **collision-resistant**, and -matching `[a-z0-9]{4,16}` so it is safe as a slot infix and as a path component. **It appears in +**The cycle nonce.** Generated once at cycle start, immutable, and **collision-resistant in an +operational sense rather than an aspirational one: at least 8 characters drawn uniformly from +`[a-z0-9]`, from a source of randomness** — never derived from a name, a timestamp or a commit, +each of which collides exactly where sibling cycles do. It matches `[a-z0-9]{8,16}`, so it is safe +as a slot infix and as a path component. **It appears in every record the cycle writes**, not merely somewhere in history — a record without it cannot be attributed to a cycle, which is the whole function. **Deriving it from a gate plus a commit does not work**: sibling worktrees and restarted loops share loop names and base commits, and a `WIP:` parent identifies @@ -138,7 +156,14 @@ different questions. **Recovery has two sources, because a Gate-A loop's commit the loop the nonce lives in the advisory working record beside the findings files, and it becomes history at the loop's commit. A cycle that can recover it from **either** keeps its identity; a cycle that can recover it from **neither** has no identity and starts a new cycle — which costs -passes rather than silently inheriting a decline. +passes rather than silently inheriting a decline. **Where the sources disagree, or more than one +candidate nonce is present, the cycle likewise has no identity**: guessing which is current is how +a decline leaks between cycles, and the conservative reading costs passes instead. + +**The advisory working record is a cycle record too.** It carries the nonce like any other, and +§5's per-cycle infix and refuse-on-collision rule apply to it as they do to findings slots — a file +two cycles can both write is the same collision the infix exists to prevent, one artifact along. +§5's optional-companion rules are otherwise unchanged, and the commit body remains *the* record. **The mid-cycle amend must preserve what it amends.** Required properties: the resulting message **begins `WIP:`** — the hook recognizes a WIP commit from the message the command supplies, and an @@ -234,8 +259,10 @@ whose cited set licenses floor 1 must carry the floor-1 provenance line, and the reads it.** That is what makes the reduced floor observable at all, and it is why the provenance grammar is pinned here rather than downstream. -**Named residual:** the hook's own message says a cycle "MUST reach a minimum" at its threshold -(`codex-gate.sh:933`), which at level 0 contradicts a legitimate one-pass close. **Hook text is out +**Named residual:** the hook's messages state its own threshold as an obligation — "MUST reach a +minimum" (`codex-gate.sh:933`), the Gate-B floor line (`:947`) and the Gate-A floor line (`:967`), +each rendering a ratio against `$floor` — all of which at level 0 report a shortfall against a +number the cycle does not owe. **Hook text is out of scope by decision**, so this is disclosed, not fixed; what makes it tolerable is the precedence rule plus invariant 1. @@ -254,17 +281,36 @@ plan. Revision 10 moved it and was wrong to; that is the one place the slimming detail into contract, and it is the reason the restructuring guard exists. ``` -floor per ; hook reminder threshold +cycle ; floor per ; hook reminder threshold + := [a-z0-9]{8,16} the cycle nonce (§2.1) + := the derived floor := "none" the artifact cites no story | "{" ("," )* "}" - := " (level " <0|1|2> ")" a profiled story - | " (unprofiled)" a cited story with no profile + := " (level " <0|1|2> ")" a profiled story + | " (unprofiled)" a cited story with no profile + := | := "absent" | | "unusable" ``` -Paths are repo-relative and contain no `,`, `{`, `}` or `;`; a path that would is written in -double quotes with `\"` escaping. **The same reasoning fixes the curve's form in §5.1**: both are +A `` is repo-relative and contains none of `,` `{` `}` `;` `"` or whitespace; any other +path is a `` — double-quoted, with `\` and `"` backslash-escaped and no other escape +recognized. **The nonce leads every pinned form**, which is what lets §2.1's rule that it appears +in *every* record be something the formats can satisfy. + +One instance per variant, all parsing under the grammar above: + +``` +cycle k7m2q9xa; floor 1 per {docs/superpowers/stories/A-story.md (level 0)}; hook reminder threshold absent +cycle k7m2q9xa; floor 3 per {A-story.md (level 0), B-story.md (level 2)}; hook reminder threshold 3 +cycle k7m2q9xa; floor 3 per {A-story.md (level 0), C-story.md (unprofiled)}; hook reminder threshold 1 +cycle k7m2q9xa; floor 3 per none; hook reminder threshold unusable +cycle k7m2q9xa; floor 3 per {"docs/stories/odd, name.md" (level 2)}; hook reminder threshold absent +``` + +**Everything that quotes this line elsewhere quotes an instance of the grammar.** A shorthand in a +story criterion, a commit body or a report is either a valid instance or it is wrong — there is no +informal variant, because the only reader that matters parses rather than reads. **The same reasoning fixes the curve's form in §5.1**: both are read by something other than a human. **The residual disclosure this implies ships in both copies**, in the words the story requires: @@ -293,7 +339,10 @@ pass; **passes already run keep counting**; **closing requires the floor as curr corrected mid-cycle — and the same three rules cover it: the set is re-read at each pass, the floor is re-derived from it under unanimity, and closure requires the floor the current set yields. **Adding a story is a raise for this purpose** and carries the same one-further-pass -consequence. +consequence. **Removing one lowers the floor and releases nothing else**: an accepted in-set +Blocker or Major stays in the set and must still resolve, because it entered by the user's answer +and not by the story that first raised it. A set change moves the floor; it is not a route to +discharge findings. **The consequence that must be stated:** a **raise costs at least one further pass regardless of the arithmetic**, because §5 already requires the final clean pass to run under the current @@ -352,9 +401,17 @@ it to the plan would leave a durable interface undecided. (passes , ): Findings , , …. Blockers , , …. := "Gate-A spec loop" | "Gate-A plan loop" | "Gate B" := a comma-and-range list of the pass numbers covered, e.g. "1-3" or "1,2,4" - := one model name, or "pass

" entries where they differ + := | ("; " )* + := "pass "

" " ("+" )* + := [A-Za-z0-9._-]+ as reported by the call, never recalled ``` +One bare `` means every covered pass ran under it. `` entries are +semicolon-separated and must cover **every** pass the `` names; a pass assembled from calls +under different models joins them with `+`. **A model that cannot be determined is written +`undetermined`**, never omitted and never guessed — `docs/coding-workflow.md` already requires +recording it that way where neither config level names one. + A skipped loop writes `: skipped (see skip reason)` and no counts. **Required properties the form exists to satisfy:** @@ -399,7 +456,13 @@ without its accounting.** the frozen final text** and **reviewed before any replacement text is written**. The gate is a gate, so it has the parts a gate has: it is **a Gate-A review of that artifact** under this story's profile, its **acceptance condition is a clean pass at the derived floor** like any other, -and **failure means no replacement text is written** rather than a note and a continuation. The +**it owes its own labelled curve** in the commit that carries the artifact — a gate exempt from the +rules it enforces would be the gate-off path in miniature — +and **failure means no replacement text is written** rather than a note and a continuation. +**A finding that changes the passage list, a disposition or the spec itself feeds back rather than +being absorbed**: the artifact is regenerated against the corrected text and re-reviewed, and where +the finding changes *this spec*, the spec's own Gate A reopens on the changed rule. The artifact is +downstream of the spec, so it cannot silently amend it. The plan names where in its sequence that falls. **The pass-2 tension, recorded rather than resolved by hindsight.** Gate-A pass 2 raised a Blocker @@ -412,7 +475,7 @@ when it is produced. **The price of the split, stated.** Once dispositions are deferred, **the passage list is the sole guard against a dropped condition**, and the conditions artifact cannot catch what the list never named. Rows 20 and 21 were missing until pass 8 because revision 8 added rules rewriting them -without extending the list. **The list is re-checked whenever the design adds a rule**, and the +without extending the list. **The list is re-checked whenever the design adds a rule** — pass 10 added rows 22 and 23 for exactly that reason, the nonce rule having reached records the list never named —, and the plan's gate reads it against the final text rather than trusting it. ### 6.1 Floor-wording sites are generated, not hand-derived @@ -434,7 +497,7 @@ stating a rule. ### 6.2 The passages this change rewrites -Each present exactly once in both copies. **Twenty-one; the conditions artifact is incomplete +Each present exactly once in both copies. **Twenty-three; the conditions artifact is incomplete without all of them.** | # | Passage | Rewritten by | @@ -460,6 +523,8 @@ without all of them.** | 19 | "On squash-merge, copy every evidence entry…" | §5.1 — three record types added | | 20 | "Before each call, delete every target file…" (`:199/:386`) | §5 — the infix, and a **refuse-on-collision** case where the target belongs to another cycle | | 21 | The `baseSha`/WIP/closing-amend block (`:500-517`, template `:679-696`) | §2.1 — the WIP-amend properties; §4.1 and §5.1 add records the closing body must carry | +| 22 | "The evidence entry lives in the commit body" | §2.1 — the nonce rule reaches every record a cycle writes, this one included | +| 23 | "Optional companions, from field practice" (dispositions file, cycle-stable resume note) | §2.1 — both are records a cycle writes, so both carry the nonce; §5 also gives the dispositions file a defined role as the working record before a commit exists | **Row 18's change is a replacement, not an addition beside it.** Today's grammar admits three exact names; an infixed name satisfies none. The shipped text replaces each with one admitting an @@ -533,7 +598,7 @@ Mode `battery+check+verification` (no `+abuse-path`). cycle where one exists; **recorded not-applicable with its reason where none does.** An agent must not create one to make a check runnable, which would be a fixture supplying its own input. - **Parity across every changed rule**, per story criterion 7 — §6.1's fourteen sites, §6.2's - twenty-one passages, and every rule §§2–5 insert, including §10's residual disclosure and §4.1's + twenty-three passages, and every rule §§2–5 insert, including §10's residual disclosure and §4.1's provenance properties, which the story requires in **both** copies. The copies already differ on 192 lines, so parity cannot be asserted from a whole-section diff. - **A fresh twelve-item `docs/prompt-standards.md` pass** over every changed prompt region. **Item @@ -575,7 +640,12 @@ design* rather than instructions to a future agent stay here. this change touches**, named rather than left to interpretation: **floor 3**; **severity classified without the demotion**, so nothing is collected that would otherwise iterate; **every suspension treated as binding**; **decline records treated as absent**, so no hold is released; - and **the curve duty treated as owed**. Not a re-derivation, which could hand a level-0 loop a + and **the curve duty treated as owed**. **That list is the whole of it** — the parts this change + touches are the floor, severity, the ordering's exits, the decline, and the curve, and each has an + entry above; a part not named here is a part this change did not touch. **A user knob set above 3 + is not lowered by the fallback**: the knob moves the hook's reminder threshold while the fallback + sets the *obliged* floor, so a workspace asking for louder reminders keeps them. Not a + re-derivation, which could hand a level-0 loop a floor of 1 and *skip* passes on the strength of not knowing when it started. - **Downstream has no shipping commit.** The template travels into repositories whose history does not contain this change, so adoption binds from the `/workflow-init` run that **actually writes** @@ -587,12 +657,18 @@ design* rather than instructions to a future agent stay here. - **The gate-off surface — routes known today, not a complete list**, since an enumeration read as complete guarantees what it omits. **One route is created here and is named as such**: a stated floor the cited set does not license, which could not exist before there was a derived floor to - state. Pre-existing and unchanged: omitting a higher-risk cited story, minting or editing a - profile to level 0, presenting an incomplete set, falsifying evidence entries, silencing - reminders, or not running a pass and reporting that it ran. **None of this is a guard**, and the + state. **A second is created here**: a **fabricated decline record**, which releases a real hold on a + real finding and which nothing authenticates — §2.1 says exactly that, and omitting it here while + admitting it there would be an enumeration contradicting its own source. Pre-existing and + unchanged: omitting a higher-risk cited story, minting or editing a profile to level 0, + presenting an incomplete set, falsifying evidence entries, silencing reminders, or not running a + pass and reporting that it ran. **None of this is a guard**, and the profile-minting path is bounded only by §5's existing human-confirmation rule. - **Rollback, once a rule has been adopted.** In this repo a bad rule is reverted like any other - commit, and the §10 activation rule then applies to loops in flight. **Downstream there is no + commit — **and the revert is itself a shipping commit for the *old* rules**, so a loop in flight + across it lands in the unknown-start case rather than cleanly under either version. That is + intended: the fallback's stricter reading is the right answer for a loop whose rules changed + underneath it, in either direction. **Downstream there is no revert**: a project's `CLAUDE.md` is its own file, so withdrawing a rule means shipping a corrected template and waiting for each project to re-run the scaffolder and accept the diff — the same partial-adoption path, with the same absence of detection. **A rule that turns out @@ -627,7 +703,7 @@ directly. **Moved to the plan** — each reviewed in the plan's own Gate A against this spec: command lines for the mid-cycle amend (§2.1 keeps the required properties, which is what made the earlier pinned `-m` form's defect visible); the per-shape diagnostic check specifics in §5; per-site replacement -wordings for §6.1's fourteen sites and §6.2's twenty-one passages; and the conditions artifact's +wordings for §6.1's fourteen sites and §6.2's twenty-three passages; and the conditions artifact's production procedure (§6 keeps the method and the gate's acceptance condition). **Returned to the spec after pass 9:** the provenance grammar and the curve form. Both are read by diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 3940a28..c429be4 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -116,7 +116,8 @@ deliverable rather than a convenience. cited story profiled, every one at level 0 — so a single higher-profile or unprofiled member returns the cycle to 3. Stated in both copies, and falsifiable: a copy silent on the multi-story case leaves the cheapest wrong reading available. - **Every pass report states the floor it derived and the axes it read**, so the value is + **Every pass report states the floor it derived, the risk and security axes it read, and + which cited stories it read them from**, so the value is visible while passes are still being spent rather than only in the closing commit. Each copy also states that **one derived value governs every loop of the cycle** — the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle alike — and says *why*: those @@ -176,9 +177,11 @@ deliverable rather than a convenience. **(a)** Exactly **one** procedure decides severity. A copy that states a categorical demotion by subject *alongside* the test fails, because two procedures can disagree on one finding; subject-based cases may appear only as worked examples of the test. - **(b)** The test turns on whether **something in the system takes a different decision** - if the text is wrong — not on what kind of file the text lives in, and not on a human - reader, whose cost §5's prose exemption already prices as non-gating. + **(b)** The test requires **both** halves to be nameable: **what in the system consumes the + text**, and **the decision it takes differently** if the text is wrong. Failing to name + either makes the finding Minor-or-below. It does not turn on what kind of file the text + lives in, nor on a human reader, whose cost §5's prose exemption already prices as + non-gating. A copy demoting only when *both* are absent inverts the rule. **(c)** The instrument carve-out is **symmetric**: an instrument finding keeps its severity whenever it shows the instrument changes what a gate concludes about product behaviour, in **either** direction. A copy naming only a false green fails, because it would demote a @@ -191,7 +194,10 @@ deliverable rather than a convenience. remain legitimate readers of rule text they will later apply; what is excluded is the reviewing pass, not gates. **(f)** *(record, not severity)* A user's decision on a surfaced finding is **durably - recorded where history keeps it** — carrying enough of the finding to identify it again, + recorded where history keeps it, and legible as its own kind of record** — never mergeable + with or mistakable for a human-exception record, since the two differ in force: one + authorizes nothing, the other releases a named finding's hold. Carrying enough of the + finding to identify it again, naming who decided and when, surviving into a squash, and **bounded to the cycle it was taken in**. A copy that lets the decision live only in per-clone working state, or that lets one decision release the same finding in later cycles, fails. @@ -232,7 +238,8 @@ deliverable rather than a convenience. deliberate and stated as such. Checkable by diffing the two regions. - [ ] **The provenance path is demonstrated end to end on this branch, at the floor this branch actually licenses.** This story is risk `high`, so every cycle citing it owes - floor **3** — and its closing commit body carries `floor 3 per `, showing the + floor **3** — and its closing commit body carries a provenance line **in the one pinned form**, + naming the cycle, that floor, and this story at its level, showing the derivation and the provenance line working at a real value. **The floor-1 demonstration is not on this branch, deliberately.** It was in an earlier draft and was unsatisfiable: a criterion demanding a floor-1 cycle here contradicts this @@ -263,6 +270,14 @@ deliverable rather than a convenience. licenses the accepted cost: a level-0 cycle draws a hook reminder its derived floor does not owe, and a redundant warning is the price named here. Teaching the hook to fall silent would be the false ✓ the same sentence calls dangerous. +- `## Prompts and scaffolding` — "**`/workflow-init` never overwrites silently.** Idempotent: + missing → write; identical → report unchanged; present and different → show the diff and ask." + This is why a downstream project adopts by re-running the scaffolder and can sit on a partial + adoption — the spec's rollout section rests on it. +- `## Prompts and scaffolding` — "**The base taxonomy stays stack-neutral.** Project vocabulary… + goes only in that project's `docs/hardening-taxonomy.md`… Otherwise one project leaks into every + other." The severity test ships into projects whose reader kinds we have never seen, which is + why its list of readers is illustrative rather than closed. - `## Prompts and scaffolding` — "**`/workflow-init`'s templates stay inline** in the command body." The mirror edit lands in the command body, never in a file read from disk. - `## Prompts and scaffolding` — "**Prompt changes pass `docs/prompt-standards.md`** — all 12 From b885bfc9ab8a211c8124f491ec2f09e2b6a921c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 12:41:14 +0200 Subject: [PATCH 027/117] docs(story): narrow to parts 1+2; split the loop-rule consolidation out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Daniel's decision on the third mandatory Gate-A stop (2026-08-29), carried by per-part attribution: over three consecutive passes the floor and severity parts produced 4/1, 8/1 and 7/1 Blocker/Major while the loop-rule sections produced 10, 11 and 14 and rising, each repair creating an interaction the next pass found. The parent story now ships the pass floor and severity semantics. Its §6 sizing note is CORRECTED rather than quietly replaced, because the old reasoning was right about what it addressed: "splitting would reproduce the clause-by-clause churn" was about the CONTRACT QUESTIONS, which were genuinely entangled -- moving one moved the others -- and which are now all settled and recorded. What did not converge is interaction density among shipped rules, a different problem whose standard remedy is exactly the split the first reasoning refused. The same story can correctly refuse a split and then correctly take one. Parts 1 and 2 stay together and the story says why: part 2's reachability test is what settles a part-1 question -- a path-derived docs-only arm would be wrong here because docs/hardening-log.md is a docs/**.md path that drives rung escalation. Splitting those two would separate a rule from the argument that decides it. Moved out: desired outcome 3, the Q1-Q6 criterion, and the decline-record sub-criterion. Everything else stays with parts 1+2 -- the curve, provenance and activation criteria are read by the shipped observables, not by the loop rules. The successor story carries TEN settled decisions as inputs marked decided-and-paid-for, with the instruction that its design begins from them and reopens none: only clean completion closes; clean outranks the two-tell stop and the clearly-stuck exit; any answer ends the hold in either direction; a decline never qualifies the resolve duty; a decline is scope-stop only; it binds one cycle; what "explicitly declined" means; the commit-body transport as a distinct record type; and Q6's computability answer. Plus two implementation facts the parent paid for -- a pass's cleanliness is never rewritten, and findings files give the inventory and not the resolutions. Its criteria include one the parent cycle earned the hard way: no path may leave a cycle unable to close and unable to stop. The parent shipped a stop whose only answer resumed a cycle that immediately stopped again. The successor's profile is PROPOSED, not confirmed, and the story says so in a standing note -- per §5 the human confirms it, and nothing executes until then. Sparring session, under Daniel's 2026-08-28 delegation; the split itself was Daniel's direct decision. Gate B: N/A -- both staged paths are docs/**.md under CLAUDE.md §5's prose exemption. --- ...-review-loop-economics-pass-floor-story.md | 62 ++++----- ...026-08-29-loop-rule-consolidation-story.md | 121 ++++++++++++++++++ 2 files changed, 149 insertions(+), 34 deletions(-) create mode 100644 docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index c429be4..4e15a30 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -1,4 +1,4 @@ -# Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Story +# Review-loop economics: pass floor and severity semantics — Story **Date:** 2026-08-28 · **Size:** story **Risk:** high · **Security:** none · **Validation:** battery+check+verification @@ -65,16 +65,19 @@ Three outcomes, each observable in the shipped prompt text. false-green-only carve-out that criterion 5 had already moved past; and "without judgement calls" promised more than any prose rule can deliver — a stated test removes arbitrariness, not judgement, and the spec says so plainly, so the story must not promise otherwise.)* -3. **The §5 loop rules stop being amended one clause at a time.** The six open contract - questions the `fic2` cycle raised and declined to answer get one coherent answer, taken - together, with Daniel's five recorded decisions as settled inputs. The alternative — - answering them as they surface — is what produced two stop-and-surfaces and a revert - inside a single cycle. +**Scope narrowed 2026-08-29 — the loop-rule consolidation moved to a successor story.** A third +outcome once sat here: answering the six open §5 contract questions in one coherent pass. +**Those answers are all decided and recorded**; what did not converge was writing them down. Gate-A +attribution over three consecutive passes measured it — parts 1 and 2 produced 4/1, 8/1 and 7/1 +Blocker/Major while the loop-rule sections produced 10, 11 and 14 and rising, each repair creating +an interaction the next pass found. See +`docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md`, which carries every +recorded decision as a settled input rather than reopening any of them. -**Why now, and why together.** Parts 1 and 2 each change what a loop is allowed to do; part -3 is the accumulated debt of changing that a clause at a time. Shipping them separately would -reproduce the churn this story exists to end, which is why the consolidation is the -deliverable rather than a convenience. +**Why parts 1 and 2 still ship together.** They are not merely adjacent: part 2's reachability test +is what decides a part-1 question — a path-derived `docs-only` arm would be wrong here because +`docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation. Splitting *those two* +would separate a rule from the argument that settles it. **Named out of scope**, so no criterion below absorbs them: @@ -193,27 +196,7 @@ deliverable rather than a convenience. review itself as the reader — so a copy stating (a)–(d) and omitting (e) fails. Gates must remain legitimate readers of rule text they will later apply; what is excluded is the reviewing pass, not gates. - **(f)** *(record, not severity)* A user's decision on a surfaced finding is **durably - recorded where history keeps it, and legible as its own kind of record** — never mergeable - with or mistakable for a human-exception record, since the two differ in force: one - authorizes nothing, the other releases a named finding's hold. Carrying enough of the - finding to identify it again, - naming who decided and when, surviving into a squash, and **bounded to the cycle it was - taken in**. A copy that lets the decision live only in per-clone working state, or that - lets one decision release the same finding in later cycles, fails. -- [ ] **Each of Q1–Q6 is answered or rejected in the shipped text, with a reason, and the - answers do not contradict each other.** Q1 (clean-completion precedence), Q2 (a declined - expansion's exit), Q3 (scope stop vs. clean completion), Q4 (whether a decline binds - later passes in the same cycle), Q5 (whether the three universal rules may carry an - in-set qualification), Q6 (what the pass-4 report does when prior-pass history is - unavailable) — as stated in `docs/field-reports/2026-08-26-fic2-cycle-evidence.md`. - **Where an answer qualifies a standing rule, the qualification is stated at every rule it - modifies, in both copies — and at no rule it does not modify.** Both halves are - falsifiable by reading: a rule the answer changes but does not mention leaves two - instructions disagreeing, which is why Q2 was reverted last cycle; a rule the answer does - *not* change but mentions anyway implies an exception that does not exist, which is its - own defect. Which rules those are is the design's to determine and the reviewer's to - check against it. + - [ ] **The shipped text says when it starts binding, and what an adopter gets when it does not fully arrive.** Both copies state: that a loop already running finishes under the rules it started with; what a loop does when its starting rules cannot be established, covering @@ -318,6 +301,17 @@ deliverable rather than a convenience. ## 6. Suggested size -`story` — three parts, but one coherent change to one subsystem's rules in two mirrored -copies. Part 3's value *is* being done once; splitting it would reproduce the clause-by-clause -churn the story exists to end. +`story` — two coupled parts, one change to one subsystem's rules in two mirrored copies. + +**This note said the opposite until 2026-08-29, and the correction is worth keeping.** It read: +"Part 3's value *is* being done once; splitting it would reproduce the clause-by-clause churn the +story exists to end." **That reasoning was about the contract questions**, which were genuinely +entangled — answering one moved the others — and it was right about them. They are now all settled +and recorded, so the entanglement it described has been paid for. + +**What did not converge was a different problem with a different remedy.** Eleven Gate-A passes +never brought Blocker/Major below 22, and the last three attributed 10, 11 and 14 of them to the +loop-rule sections while parts 1 and 2 held at 4/1, 8/1 and 7/1. Each repair to one loop rule +created an interaction the next pass found — density among shipped rules, not entanglement among +open questions. Splitting is the standard remedy for the first and the failure mode for the second, +which is why the same story can correctly refuse a split and then correctly take one. diff --git a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md new file mode 100644 index 0000000..513caf4 --- /dev/null +++ b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md @@ -0,0 +1,121 @@ +# §5 loop-rule consolidation: exits, duties, and the decline — Story + +**Date:** 2026-08-29 · **Size:** story +**Risk:** *(proposed)* high · **Security:** *(proposed)* none · **Validation:** *(proposed)* battery+check+verification + +> **DRAFT — the profile above is proposed, not confirmed.** Per §5 a profile is confirmed by the +> human, and until it is this story is not executable. Nothing depends on it yet: the work it +> describes is split out of a cycle that is still running, and the successor starts when someone +> picks it up. The proposal's reasons are in §5. + +## 1. Problem statement + +**§5's loop has four exits and four standing duties, and nowhere says which wins when two apply at +once.** Clean completion, the scope stop, the clearly-stuck exit and the two-tell stop; the floor, +the Blocker/Major-resolve duty, the rule that a surfaced finding stays open, and the rule that no +pass carrying one counts as clean. Every one is stated in its own paragraph, each qualifying the +ones before it, and the ordering exists only in a reader's head. + +**That gap has already cost a cycle.** The `fic2` cycle could not ship two small clauses without +qualifying three rules nobody had proposed changing, and reverted — correctly. The revert is the +evidence: two clauses met an unordered lattice and the lattice pushed back. +(`docs/field-reports/2026-08-26-fic2-cycle-evidence.md`.) + +**Why this is a story rather than a paragraph.** The answers are not the hard part — they are all +settled and listed in §4 below. **Writing them down is.** Eleven Gate-A passes on the parent +story's spec never brought Blocker/Major below 22, and the last three attributed **10, 11 and 14** +of them to exactly these rules while the floor and severity parts held at 4/1, 8/1 and 7/1. Each +repair to one loop rule created an interaction the next pass found. **The subject here is +interaction density among rules that all bear on one decision — may this cycle close — and it needs +its own artifact and its own review budget.** + +**Parent:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md`, which +ships the pass floor and severity semantics and whose §6 records why the split was refused first +and then taken. + +## 2. Desired outcome + +**One stated ordering, written once, that a reader can apply without inferring it** — after which +the individual rules reference the ordering instead of qualifying each other. Specifically, a reader +of either §5 copy can answer, without judgement: which exits *close* a cycle and which merely +*suspend* it; what happens when two apply at once; which duties participate in that ordering and +which are preconditions; and what a user's answer on a surfaced finding does in **both** directions. + +**Out of scope**, named so nothing absorbs them: +- **The pass floor and severity semantics** — the parent story ships those, and this story treats + them as given rather than adjusting them. +- **Reopening any decision in §4.** They are settled and paid for; the design starts from them. +- **Hook code** (anything under `plugins/dev-workflow/hooks/`), unchanged from the parent. +- **The pass-counter anomaly**, the CodeRabbit plan-metadata contradiction, and the + fixture-per-predicate question — all still parked. + +## 3. Acceptance criteria + +- [ ] **The ordering is stated once, in both copies, and covers every reachable conflict.** A + reader can determine which exits close and which suspend, and what happens when two apply + together, without inferring it from paragraphs that qualify one another. Conflicts that + **cannot** co-occur are named as such with the reason, rather than given invented rules — an + ordering that legislates for unreachable states rebuilds the broken instrument the `fic2` + record already documents. +- [ ] **Every duty is classified, and each classification is visible.** For each of the four + standing duties, both copies say whether it participates in the ordering or is a + precondition on closure — and a duty that is a precondition says what it gates and what + discharges it. +- [ ] **A user's answer on a surfaced finding has one rule covering both directions**, and the + rules it modifies carry the qualification **at each rule it modifies and at no rule it does + not**. Both halves are falsifiable by reading: a modified rule that does not mention it + leaves two instructions disagreeing; an unmodified rule that mentions it implies an exception + that does not exist. +- [ ] **No path leaves a cycle unable to close and unable to stop.** Every terminal condition the + change introduces has an answer that changes the cycle's state. Checkable by walking each + stop the shipped text names and asking what the next state is — the parent cycle shipped a + stop whose only answer resumed a cycle that immediately stopped again, and that is the + failure this criterion exists to catch. +- [ ] **Every condition of the replaced prose is accounted for** — for each rewritten passage, what + it required, each requirement marked kept, moved or deliberately dropped, per the AGENTS.md + Don't. A requirement neither kept nor explicitly dropped is a dropped condition. +- [ ] **The two copies stay in parity** on every rule this story changes, deliberate wording + differences stated as such. + +## 4. Settled inputs — decided, paid for, and not to be reopened + +Each was confirmed by Daniel during the parent cycle and is recorded in that cycle's commit bodies +and in `docs/field-reports/2026-08-26-fic2-cycle-evidence.md`. **The design begins from these.** + +| # | Decision | +|---|---| +| 1 | **Only clean completion closes a cycle.** The scope stop, the clearly-stuck exit and the two-tell stop **suspend** — they surface and the loop resumes. Two suspensions at once compose; the report carries both reasons. | +| 2 | **Clean completion outranks the two-tell stop** (Q1). | +| 3 | **Clean completion outranks the clearly-stuck exit** — §5 already says so and the sentence is preserved verbatim. | +| 4 | **A surfaced finding holds closure while it awaits the user's answer; any answer ends the hold, in either direction** (C3/Q5, and the accept branch). After the answer the ordinary rules govern. | +| 5 | **A decline releases the hold and never qualifies the Blocker/Major-resolve duty**, which applies to in-set findings a decline never reaches. | +| 6 | **A decline is available only for a finding surfaced by a scope stop.** An in-set Blocker or Major cannot be declined; treating it as declinable would make this a general waiver. | +| 7 | **A decline binds for the remainder of its cycle and has no effect in any later one** (Q4). | +| 8 | **"Explicitly declined" is a recorded user decision on that specific finding**, attributable and unambiguous — never silence, never a general remark about scope, never inferred. | +| 9 | **The decline is recorded in the commit body**, reusing the human-exception transport as a **distinct record type** — the two differ in force, since the human-exception form authorizes nothing. | +| 10 | **Q6:** when prior-pass history is unavailable, the pass report states what is computable, names what is not and why, and discloses the reduced sensitivity. Not a new stop condition, not a mandatory resume note. | + +**Two implementation facts the parent cycle established, carried so they are not rediscovered:** a +pass's cleanliness is a fact about what that pass found and is **never rewritten** — what a later +answer changes is whether the *cycle* may close; and the findings files establish the **inventory** +of in-set findings, not their resolutions, which they do not contain. + +## 5. Open questions + +- **The profile.** Proposed `high / none / battery+check+verification`, on the same reasoning the + parent used: the surface is the review gate itself, and a wrong rule mis-steers every future + cycle. **No named `high` trigger matches literally**, so this is a judgement call under intake's + "surfaces, not words", and the human decides it. The parent's experience is evidence for rather + than against: eleven passes and three mandatory stops on this material. +- **How much of the ordering is new text versus reference.** §5 already contains the two sentences + from which "only clean completion closes" follows; whether the ordering is stated fresh or + assembled from what is there changes the old-conditions accounting and the parity surface. +- **Whether the cycle nonce belongs here.** The parent introduced it to bind a decline to one + cycle, and it now also serves slot naming and record attribution. If the parent ships it, this + story consumes it; if not, this story needs it. + +## 6. Suggested size + +`story` — one coherent change to one subsystem's rules in two mirrored copies. Not `chore`: it +answers a structural question and rewrites standing duties. Not `epic-needs-splitting`: the +decisions are already made, which is what made the parent's version expensive. From 8899bc92c98b1d734ced47e6deafd95884ce9c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 12:43:27 +0200 Subject: [PATCH 028/117] =?UTF-8?q?docs(spec):=20revision=2013=20=E2=80=94?= =?UTF-8?q?=20reduce=20to=20parts=201+2;=20part=203=20follows=20its=20stor?= =?UTF-8?q?y=20out?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 724 -> 573 lines. Daniel's decision on the third mandatory Gate-A stop. Removed with part 3: the exits-and-duties analysis and precedence table, the hold and the decline and its record, Q6's unavailable-history answer, and the eleven §5 passages only those rules rewrite. The successor story's accounting owes them. The two pass-11 Blockers followed part 3 out, which is where both belonged: the "recorded decline" that had crept into §1's resolution list was a waiver path into the mandatory resolve duty, and the lost-history stop had no state-changing answer -- a deadlock fix that deadlocked one level up. Neither has residue inside parts 1+2; the reduced spec makes no claim about resolution inventories or unavailable history. Kept, by a test rather than by feel: DOES ANYTHING BUT A PERSON PARSE IT, and does a shipped criterion read it. The provenance line and the per-pass curve both pass -- P8 reads them -- so their grammars stay pinned here rather than descending to the plan. The CYCLE NONCE stays for the same reason, since both records carry it, which also settles the successor story's open question about where the nonce lives. Two seams handled rather than left: the squash-carry passage and the unknown-start fallback are both touched by this change and by the successor, so each now says what the other adds -- extending a list is safe where replacing it would not be, and the second change must not drop what the first added. §10 rewritten as the record of what moved and why, carrying the numbers: eleven passes never below 22 Blocker/Major, and the last three attributing 4/1, 8/1, 7/1 to parts 1+2 against 10, 11, 14 rising for the loop rules. It also keeps the most transferable thing this cycle produced: a restructuring guard that asks only "did a decision move?" misses the case where a rule survives in outline and loses its force. Pass 9 found eight of those in one revision. Sparring session, under Daniel's 2026-08-28 delegation; the split was Daniel's direct decision. Gate A: passes 1-11 at .context/codex-reviews/gate-a-spec-rle-pass-{1..11}.md. Findings 27, 30, 54, 40, 33, 34, 32, 33, 28, 27, 38. Blockers 5, 2, 0, 4, 0, 3, 9, 4, 2, 2, 2. Blocker/Major 24, 22, 43, 31, 30, 26, 29, 28, 22, 23, 30. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 385 ++++++------------ 1 file changed, 117 insertions(+), 268 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 5271afd..a6d4a1d 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,7 +1,7 @@ -# Review-loop economics: pass floor, severity semantics, and the §5 loop-rule consolidation — Design +# Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-28 · **Revision:** 12, after Gate-A passes 1-10 -(27, 30, 54, 40, 33, 34, 32, 33, 28, 27 findings) +**Date:** 2026-08-29 · **Revision:** 13 (reduced to parts 1+2), after Gate-A passes 1-11 +(27, 30, 54, 40, 33, 34, 32, 33, 28, 27, 38 findings) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. @@ -10,10 +10,10 @@ validation `battery+check+verification`, no `+abuse-path`. reasons, rules stated as required properties, named interfaces, and scope. **Exact replacement wordings, command lines and step-by-step procedures live in the plan**, where they get their own Gate A against this spec once it is stable. **Two formats are the exception and are pinned here**: the -provenance line (§4.1) and the per-pass curve (§5.1), because **something other than a person +provenance line (§3.1) and the per-pass curve (§4), because **something other than a person parses them** — that is the test separating detail from contract here, not length. Nothing escapes review by moving; what changes is when it is reviewed. Revision 10 is a level-of-detail change and moves no -decision — §11 lists what went where. +decision — §10 lists what went where. Prompt-only. **No file under `plugins/dev-workflow/hooks/` changes, and no hook *state* file is written.** One file the hook *reads* is edited: `codex-gate.sh:94` greps `CLAUDE.md` for the §5 @@ -28,152 +28,49 @@ already differ on 192 lines; only the rules this change touches are brought to p --- -## 1. The finding this rests on - -**The loop has four exits and four standing duties, and §5 never says which wins when two apply.** -That is why the `fic2` cycle could not ship two clauses without qualifying three rules nobody -proposed changing. Consolidation therefore means **stating the ordering once**, after which most -answers are readings of it. - -*Scope:* four exits **of the loop**. §5's other mandatory stops — a target surviving deletion, an -exhausted recovery budget, an unresolvable profile, a blocking evidence gap — halt the *procedure* -rather than resolving the *cycle*, and are not reordered. - -**Only one exit closes**, which §5 already says twice without drawing the conclusion: - -> "Stopping this way is **not an exit from the gate**: the floor, the Blocker/Major filter and the -> clean-final-pass rule all stand, and the loop resumes…" - -> "You surface *with the finding still open* — the resolve rule is not waived, no pass is credited -> as clean, and the loop resumes on whatever the user decides." - -Clean completion **closes**. The scope stop, the clearly-stuck exit and the two-tell stop -**suspend**. So suspension × suspension is not a conflict: two at once means the report carries -both reasons. - -**Three of the four duties are not participants in ordering.** The floor is a quantity gating -closure. **Blocker/Major-must-resolve applies to in-set findings and is a precondition on closure -not discharged by a quiet pass** — a pass raising no *new* Blocker/Major is not evidence an -earlier one was resolved, so the closing report inventories each in-set Blocker/Major and its -resolution. **The findings files establish the *inventory* — which in-set Blocker and Major -findings exist — and not the resolutions, which they do not contain.** Each resolution is -established from what the cycle actually did: the diff, the later pass that no longer raises it, -or a recorded decline. Reading a resolution out of a findings file is reading something that was -never written there. **Where those files are unavailable, that is a -disclosure, not a discharge: a cycle that cannot establish resolution does not close on its own.** -"Cannot tell" and "was resolved" are different states and only one permits closing. - -**And it does not loop forever either, which needs saying because §5 declines to make unavailable -history a stop condition.** Those two rules govern different things — §5's answer is about the -*tell computation*, a reporting duty, while this is a *precondition on closure* — and together -they would leave a resumed cycle with lost history unable to close and with no terminal path. So -the path is named: **a cycle that cannot establish resolution stops and surfaces to the human**, -reporting which passes it could not read and which findings it therefore cannot account for. The -human's answer resumes it, as any other surfaced stop does. What is forbidden is closing *silently* -over an inventory nobody could check — not the cycle continuing to exist. "A surfaced finding -stays open" is the mechanism making the other exits suspensions. **"No pass carrying a surfaced -finding counts as clean" is the only participant.** +## 1. What these two parts change, and why they ship together ---- +**Part 1** makes the mandatory pass floor a function of the story profile instead of a flat 3. +**Part 2** decides finding severity by whether something in the system takes a different decision, +instead of by what kind of file the text lives in. -## 2. The precedence structure (part 3) +**They are coupled by an argument, not by convenience.** Part 2's reachability test is what settles +a part-1 question: a path-derived `docs-only` arm for the floor would be **wrong in this repo**, +because `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation — "docs" does +not imply "changes nothing". Splitting these two would separate a rule from the argument that +decides it. -| Conflict | Resolution | -|---|---| -| clean × clearly-stuck | **Clean wins.** §5 already says so; preserved verbatim. Settled and probably unreachable — a clean pass has no regenerating Blocker/Major — and kept because dropping a sentence §5 spends is the failure criterion 6 exists to prevent. | -| clean × two-tell stop | **Clean wins.** Daniel's recorded decision, encoded not reopened. | -| clean × scope stop | **The scope stop outranks closure while any surfaced finding is still awaiting the user's answer. Any answer ends the hold**, either direction. What follows is §2.1's business, not this cell's. | - -**Why only the third needed deciding.** A scope stop is triggered by a *specific finding*; while -the duty stands unqualified that finding is open, so the pass cannot be clean and the scope stop -wins automatically. **The asymmetry:** the two-tell stop is triggered by *statistics about -findings*, so nothing is left open and clean can win. The scope stop's trigger **is** a finding. - -### 2.1 The hold, the decline, and the record - -**The rule, stated in full rather than as a decline-only carve-out:** - -> **A surfaced finding holds closure while it awaits the user's answer. Any answer ends the hold, -> in either direction. After the answer the ordinary rules govern and the hold plays no part.** - -- **Declined** → out-of-set; neither duty attaches; does not re-stop later passes in that cycle. -- **Accepted** → in-set; **severity governs as Mechanics already says.** An accepted Blocker or - Major must resolve and until it does **the cycle does not close** — a statement about closure, - never about re-scoring a pass. **A pass's cleanliness is a fact about what that pass found and - is never rewritten**; closure needs a *subsequent* clean pass at or above the floor. - An accepted Minor or Nit is collected, never iterated, and blocks nothing. -- **Undecided** → the hold stands. - -**What can be declined.** Only a finding surfaced by a **scope stop** — out-of-set, or opening a -new structural or contract question. **An in-set Blocker or Major cannot be declined**: it was -never awaiting a membership answer, and treating it as declinable would make this a general -waiver, which "Scope, and it is narrow" forbids. - -**Stated at every rule it modifies, in both copies, and at no rule it does not.** It modifies -"a surfaced finding stays open" (stops *awaiting a decision*, not thereby resolved), "no pass -carrying a surfaced finding counts as clean" (gates **closure** while unanswered), and "the loop -resumes on whatever the user decides" (resumption *is* the hold ending). **The -Blocker/Major-resolve duty is not modified and carries no qualification** — §1 scopes it to in-set -findings, so a decline never reaches it, and a qualification there would imply an exception that -does not exist. - -**"Explicitly declined"** is a **recorded user decision on that specific finding**, attributable -and unambiguous. Never silence, never a general remark about scope, never the agent inferring one. - -**Required properties of the record** — the plan fixes the format: -- Lives in the **commit body**, reusing the human-exception transport (Gate-A loops: the spec or - plan commit, written at close; Gate B: the `WIP:` body by amend, restated by the closing amend) - — **as a distinct record type carrying its own label**, never merged into or mistaken for a - human-exception record. The two differ in force and the shipped text says so: the - human-exception form **authorizes nothing**, while a decline has §5-defined effect on one named - finding. A reader who cannot tell them apart has the wrong rule for both. - During a loop the decision lives in the advisory working record and *becomes* the record at - commit; the decline's effect never waits for the commit. -- Carries enough of the finding to **re-identify it**: location, defect, severity, consequence and - suggested fix. **The record stores exactly what the sameness test reads** — a test reading - fields the record lacks is the wiring failure §8 warns about. Sameness requires all five to - match; **any difference, including severity, makes it a new finding and the hold applies**, as - does any genuine uncertainty. -- Names **who decided and when**, and survives the squash carry. -- **Bound to one cycle** by the cycle nonce below. A decline has no effect in any later cycle. -- **Reads as an unverified assertion**, exactly like the human-exception record beside it: nothing - checks that the handle belongs to whoever decided. **Narrowness bounds what a false record can do — one fully-identified finding, one cycle — and - that is not the same as making it safe.** A fabricated decline still releases a real hold on a - real finding, and nothing detects it. The shipped text says exactly that, because "narrow" read - as "safe" is the overclaim this repo logs most often. - -**The cycle nonce.** Generated once at cycle start, immutable, and **collision-resistant in an -operational sense rather than an aspirational one: at least 8 characters drawn uniformly from -`[a-z0-9]`, from a source of randomness** — never derived from a name, a timestamp or a commit, -each of which collides exactly where sibling cycles do. It matches `[a-z0-9]{8,16}`, so it is safe -as a slot infix and as a path component. **It appears in -every record the cycle writes**, not merely somewhere in history — a record without it cannot be -attributed to a cycle, which is the whole function. **Deriving it from a gate plus a commit does not work**: sibling -worktrees and restarted loops share loop names and base commits, and a `WIP:` parent identifies -the base rather than the artifact reviewed. **The reviewed commit or tree id is tracked -separately**, because "which cycle is this" and "did both branches see the same thing" are -different questions. **Recovery has two sources, because a Gate-A loop's commit does not exist while it runs:** during +**The §5 loop-rule consolidation is no longer in this spec.** It moved to +`docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md` after Gate-A attribution +showed it generating 10, 11 and 14 Blocker/Major over three passes while these two parts held at +4/1, 8/1 and 7/1. Every decision that cycle bought is carried there as a settled input. **Nothing +here depends on that work landing first**, which is what made the split available: the floor and +severity rules bind on their own. + +### 1.1 The cycle nonce + +Both records these parts ship — the provenance line (§3.1) and the per-pass curve (§4) — are read +by something other than a person, and a record that cannot be attributed to a cycle is not usable +by the measurement that reads it. So each carries a **cycle nonce**. + +Generated once at cycle start, immutable, and **collision-resistant in an operational sense rather +than an aspirational one: at least 8 characters drawn uniformly from `[a-z0-9]`, from a source of +randomness** — never derived from a name, a timestamp or a commit, each of which collides exactly +where sibling cycles do. It matches `[a-z0-9]{8,16}`, so it is also safe as a slot infix and a path +component. **It appears in every record the cycle writes.** + +**Recovery has two sources, because a Gate-A loop's commit does not exist while it runs:** during the loop the nonce lives in the advisory working record beside the findings files, and it becomes -history at the loop's commit. A cycle that can recover it from **either** keeps its identity; a -cycle that can recover it from **neither** has no identity and starts a new cycle — which costs -passes rather than silently inheriting a decline. **Where the sources disagree, or more than one -candidate nonce is present, the cycle likewise has no identity**: guessing which is current is how -a decline leaks between cycles, and the conservative reading costs passes instead. - -**The advisory working record is a cycle record too.** It carries the nonce like any other, and -§5's per-cycle infix and refuse-on-collision rule apply to it as they do to findings slots — a file -two cycles can both write is the same collision the infix exists to prevent, one artifact along. -§5's optional-companion rules are otherwise unchanged, and the commit body remains *the* record. - -**The mid-cycle amend must preserve what it amends.** Required properties: the resulting message -**begins `WIP:`** — the hook recognizes a WIP commit from the message the command supplies, and an -amend that loses the prefix reads as the cycle *closing*, resetting counters and discarding -accumulated passes — and **contains every record the previous message contained**, plus the new -one. The **non-`WIP:` amend is reserved for final closure alone.** +history at the loop's commit. A cycle recovering it from **either** keeps its identity. **A cycle +that can recover it from neither — or whose sources disagree, or which finds more than one +candidate — has no identity and starts a new cycle**, which costs passes rather than letting one +cycle's records be read as another's. ---- +**The advisory working record is a cycle record too**: it carries the nonce, and §5's per-cycle +infix and refuse-on-collision rule apply to it as they do to findings slots. §5's +optional-companion rules are otherwise unchanged. -## 3. Severity semantics (part 2) +## 2. Severity semantics (part 2) **One procedure decides severity, and the subject list is illustration, not a second rule.** @@ -220,7 +117,7 @@ the story routes it to P8. --- -## 4. The pass floor (part 1) +## 3. The pass floor (part 1) **One predicate.** `max(risk, security) == 0` → floor **1**; everything else → **3**, unprofiled included. Two levels, not three: `high` takes its rigor from lens sets and evidence mode. @@ -237,7 +134,7 @@ exists; a story-declared one is subsumed, since intake defines `trivial` as no b and path-derived would be **wrong here**, because `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation. -### 4.1 The floor lives in the text, not in a file +### 3.1 The floor lives in the text, not in a file **Nothing here writes `.context/codex-gate.floor`.** The floor is derived and **stated** — in every pass report and in the commit-body provenance line — and §5's text is what binds an agent. @@ -283,7 +180,7 @@ detail into contract, and it is the reason the restructuring guard exists. ``` cycle ; floor per ; hook reminder threshold - := [a-z0-9]{8,16} the cycle nonce (§2.1) + := [a-z0-9]{8,16} the cycle nonce (§1.1) := the derived floor := "none" the artifact cites no story | "{" ("," )* "}" @@ -295,7 +192,7 @@ cycle ; floor per ; hook reminder threshold A `` is repo-relative and contains none of `,` `{` `}` `;` `"` or whitespace; any other path is a `` — double-quoted, with `\` and `"` backslash-escaped and no other escape -recognized. **The nonce leads every pinned form**, which is what lets §2.1's rule that it appears +recognized. **The nonce leads every pinned form**, which is what lets §1.1's rule that it appears in *every* record be something the formats can satisfy. One instance per variant, all parsing under the grammar above: @@ -330,7 +227,7 @@ without the disclosure would present a parseable number as a verified one. covers every case** — a second pinned form for a special case is what made earlier revisions unparseable. -### 4.2 A profile that moves mid-cycle +### 3.2 A profile that moves mid-cycle Three existing rules compose; no new rule. The floor derives from the **current** profile at each pass; **passes already run keep counting**; **closing requires the floor as currently derived.** @@ -353,42 +250,7 @@ human-confirmed and logged; the variable floor rides it and does not create it. --- -## 5. Q6 — the pass-4 report when prior-pass history is unavailable - -**Report what is computable, name what is not and why, and disclose the reduced sensitivity.** Not -a new stop condition, not a mandatory resume note. - -Three of the five tells need history — the finding count rising, the Blocker count failing to -fall, and a require↔withdraw pair. **Two are computable from the current pass alone: findings -clustering on the instrument, and findings clustering on prose about either.** So the two-tell -threshold **remains reachable** on that pair. The duty loses sensitivity; it does not become -inoperative. - -**Five shapes, each with its own check and remedy** — the plan carries the check specifics: -**absent** (no file — and *for the trend* it is unrecoverable, since a fresh run produces a -different pass rather than that one; this does not touch §5's single shared recovery attempt, -which applies to the pass being run now, not to reconstructing an earlier one); **partial** (some -slots present — compute over what exists and **name the missing pass numbers**, since a trend over -an unstated subset reads as a trend over the cycle); **malformed** (fails any pass-acceptance -check — treated as absent, **never** as zero findings, and re-runnable only while its `sessionId` -is still to hand); **unreadable** (environmental, and the OS cause is reported because permissions -and a full disk need different fixes); **stale** (another cycle's, or unattributable). - -**Stale is only partly detectable and the text says which part.** Bare numbered slots carry no -cycle identity, so a bare file is *unattributable* rather than known-foreign. Two prompt-only -mitigations: the **per-cycle slot infix**, which makes a cycle's own slots identifiable, and §5's -existing delete-and-confirm rule. Neither recovers a bare file's origin after the fact. - -**The disclosure is not optional and names the cause**, the shape, and the affected passes. - -**Rejected, with reasons:** a mandatory resume note changes an artifact §5 calls advisory; -unavailable history as its own stop would halt every cycle resumed on a fresh checkout; restarting -the pass-4 clock at the first visible pass silently lowers coverage. - -**Stated risk:** this fails *toward continuing* the loop, the direction invariant 2 questions. The -mandatory disclosure is what makes it acceptable. - -### 5.1 The per-pass curve +## 4. The per-pass curve **Each of the three loops records its own per-pass finding and Blocker counts in its own commit body**, labelled with the loop it describes. **Gate B alone would leave the dominant cost @@ -431,18 +293,19 @@ A skipped loop writes `: skipped (see skip reason)` and no counts. **What it reaches.** Durable **across cycles** — surviving a fresh checkout, a cleared `.context/`, another machine. **Not within a running loop**: the commit does not exist until the loop closes, so -§5's degraded-sensitivity answer governs there. And it is **author-written and unchecked** — +nothing here fills that gap. And it is **author-written and unchecked** — nothing compares it against the validated pass files, so **P8 reads a self-reported curve** and the text says so rather than letting it be treated as measurement. -**Squash carry.** §5's rule names only evidence entries and human-exception records; the **decline -records, the provenance line, these curves and a skipped loop's skip record** are added, in both -copies. A skip record that does not survive the squash leaves an unexplained gap in exactly the +**Squash carry.** §5's rule names only evidence entries and human-exception records; the +**provenance line, these curves and a skipped loop's skip record** are added, in both copies. +(The successor story adds the decline record to the same list; the two changes touch one passage +and the second must not drop what the first added.) A skip record that does not survive the squash leaves an unexplained gap in exactly the history P8 reads. --- -## 6. Old-conditions accounting +## 5. Old-conditions accounting Required by the AGENTS.md Don't and story criterion 6. @@ -478,7 +341,7 @@ named. Rows 20 and 21 were missing until pass 8 because revision 8 added rules r without extending the list. **The list is re-checked whenever the design adds a rule** — pass 10 added rows 22 and 23 for exactly that reason, the nonce rule having reached records the list never named —, and the plan's gate reads it against the final text rather than trusting it. -### 6.1 Floor-wording sites are generated, not hand-derived +### 5.1 Floor-wording sites are generated, not hand-derived Hand-derived three times, three different counts. The inventory is the output of a stated command: @@ -495,36 +358,27 @@ in total, all changing.** And **it is a floor, not coverage**: another digit-fre escape it. It correctly does not match `:249/:434`, which cites a historical pass rather than stating a rule. -### 6.2 The passages this change rewrites +### 5.2 The passages this change rewrites -Each present exactly once in both copies. **Twenty-three; the conditions artifact is incomplete -without all of them.** +Each present exactly once in both copies. **Twelve; the conditions artifact is incomplete without +all of them.** Eleven further passages went to the successor story with part 3 — the loop's exits +and duties, the human-exception and squash blocks it rewrites, and the WIP/amend block — and its +accounting owes them. | # | Passage | Rewritten by | |---|---|---| | 1 | "Both gates are a LOOP with a HARD FLOOR" | part 1 — the floor statement | | 2 | The early-exit sentence (`:79/:279`) | part 1 — the zero-finding exit | | 3 | The incomplete-pass rule (`:236/:421`) | part 1 | -| 4 | "What a loop absorbs, and what stops it" | part 3 — the scope stop becomes an ordered suspension | -| 5 | "Recognizing \"clearly stuck\"" | part 3 — becomes a suspension; clean precedence moves to the ordering | -| 6 | "Surfacing does not close the cycle" | part 3 — the hold mechanism | -| 7 | "From pass 4 onward…" | part 3 and §5 — two-tell stop, and Q6's answer | -| 8 | "The two rules above do not compete" | part 3 — superseded by the ordering, kept or retired explicitly | -| 9 | "Lenses are different questions…" (`:403/:582`) | part 1 | -| 10 | "The Gate-B triviality skip…" | part 1 — adjacent relaxation, checked for consistency | -| 11 | "A cycle citing several stories" | part 1 — the floor dimension under unanimity | -| 12 | "Gate A — Spec, then plan…" (`:300/:485`) | part 1 | -| 13 | "Gate B — Code" (`:335/:519`) | part 1 | -| 14 | "**Severity:** Blocker … Nit" | part 2 | -| 15 | "Scope, and it is narrow" | part 3 — distinguishing the human-exception form from the decline | -| 16 | "Recording a human exception" | part 3 — the decline reuses this transport | -| 17 | "Changing a profile" | §4.2 | -| 18 | The findings-slot naming paragraph | §5 — the grammar gains an optional per-cycle infix | -| 19 | "On squash-merge, copy every evidence entry…" | §5.1 — three record types added | -| 20 | "Before each call, delete every target file…" (`:199/:386`) | §5 — the infix, and a **refuse-on-collision** case where the target belongs to another cycle | -| 21 | The `baseSha`/WIP/closing-amend block (`:500-517`, template `:679-696`) | §2.1 — the WIP-amend properties; §4.1 and §5.1 add records the closing body must carry | -| 22 | "The evidence entry lives in the commit body" | §2.1 — the nonce rule reaches every record a cycle writes, this one included | -| 23 | "Optional companions, from field practice" (dispositions file, cycle-stable resume note) | §2.1 — both are records a cycle writes, so both carry the nonce; §5 also gives the dispositions file a defined role as the working record before a commit exists | +| 4 | "Lenses are different questions…" (`:403/:582`) | part 1 | +| 5 | "The Gate-B triviality skip…" | part 1 — adjacent relaxation, checked for consistency | +| 6 | "A cycle citing several stories" | part 1 — the floor dimension under unanimity | +| 7 | "Gate A — Spec, then plan…" (`:300/:485`) | part 1 | +| 8 | "Gate B — Code" (`:335/:519`) | part 1 | +| 9 | "**Severity:** Blocker … Nit" | part 2 | +| 10 | "Changing a profile" | §4.2 | +| 11 | The findings-slot naming paragraph | §5 — the grammar gains an optional per-cycle infix | +| 12 | "Before each call, delete every target file…" (`:199/:386`) | §5 — the infix, and a **refuse-on-collision** case where the target belongs to another cycle | **Row 18's change is a replacement, not an addition beside it.** Today's grammar admits three exact names; an infixed name satisfies none. The shipped text replaces each with one admitting an @@ -534,11 +388,11 @@ already occupied *and* the case two new cycles start concurrently, where neither slot and both would take the bare name. In practice: **a cycle that has a nonce uses it**, so the bare form is reserved for the single-cycle case it already serves. Plus a **refuse-rather-than-overwrite** rule when the target belongs to another cycle, and **uniqueness** -from the §2.1 nonce. +from the §1.1 nonce. --- -## 7. Prerequisite, rollout, and what this change falsifies +## 6. Prerequisite, rollout, and what this change falsifies **The template lacks the sentence §3's kinship points at** — "a wrong sentence costs a confused reader, not broken behaviour" is in `CLAUDE.md` and absent from the template. **The template gets @@ -575,14 +429,14 @@ invariant 9 forbids silent overwriting. Adoption is by re-running the scaffolder --- -## 8. Evidence plan +## 7. Evidence plan Mode `battery+check+verification` (no `+abuse-path`). - **Battery** — the full `AGENTS.md` § Commands chain, green. - **A check that fails without the change.** No automated test is possible for prose, so this takes §5's other permitted route — a **named verification** owing the same counterfactual. Subject: - §6.1's site inventory, differential by construction — **posed as a question about behaviour under + §5.1's site inventory, differential by construction — **posed as a question about behaviour under floor 1, the pre-change text answers wrongly at identified sites** (`:79` states the exit as "below 3"; `:126` says a Blocker/Major-free pass 1 carrying a Minor keeps looping, where floor 1 requires it to close) **while the post-change text answers correctly.** **Both revisions get @@ -597,8 +451,8 @@ Mode `battery+check+verification` (no `+abuse-path`). - **A conditional verification that a user's knob survives untouched** — byte-identical across a cycle where one exists; **recorded not-applicable with its reason where none does.** An agent must not create one to make a check runnable, which would be a fixture supplying its own input. -- **Parity across every changed rule**, per story criterion 7 — §6.1's fourteen sites, §6.2's - twenty-three passages, and every rule §§2–5 insert, including §10's residual disclosure and §4.1's +- **Parity across every changed rule**, per story criterion 7 — §5.1's fourteen sites, §5.2's twelve + passages, and every rule §§2–4 insert, including §9's residual disclosure and §3.1's provenance properties, which the story requires in **both** copies. The copies already differ on 192 lines, so parity cannot be asserted from a whole-section diff. - **A fresh twelve-item `docs/prompt-standards.md` pass** over every changed prompt region. **Item @@ -617,7 +471,7 @@ must distinguish ABSENT from CONTRADICTORY**. Both survived a full clean pass be --- -## 9. Out of scope +## 8. Out of scope Hook code (anything under `plugins/dev-workflow/hooks/`, including the reminder's own wording); gate-call observability (upstream); the pass-counter anomaly; the CodeRabbit plan-metadata @@ -627,7 +481,7 @@ general reconciliation of the 192-line divergence beyond §7's one seam. --- -## 10. Risks and activation +## 9. Risks and activation **Everything in this section that is a rule rather than a note appears in both shipped copies**, per the story's activation criterion: when the rules bind, the unknown-start fallback with its @@ -639,10 +493,10 @@ design* rather than instructions to a future agent stay here. when passes were banked would invalidate a count nobody could reconstruct. **Where a loop's starting rules cannot be established it takes the stricter reading of every part this change touches**, named rather than left to interpretation: **floor 3**; **severity classified without the demotion**, so nothing is collected that would otherwise iterate; **every - suspension treated as binding**; **decline records treated as absent**, so no hold is released; and **the curve duty treated as owed**. **That list is the whole of it** — the parts this change - touches are the floor, severity, the ordering's exits, the decline, and the curve, and each has an - entry above; a part not named here is a part this change did not touch. **A user knob set above 3 + touches are the floor, severity and the curve, and each has an entry above; a part not named here + is a part this change did not touch. (The successor story extends this fallback to the loop rules + it ships; extending a list is safe where replacing it would not be.) **A user knob set above 3 is not lowered by the fallback**: the knob moves the hook's reminder threshold while the fallback sets the *obliged* floor, so a workspace asking for louder reminders keeps them. Not a re-derivation, which could hand a level-0 loop a @@ -657,10 +511,7 @@ design* rather than instructions to a future agent stay here. - **The gate-off surface — routes known today, not a complete list**, since an enumeration read as complete guarantees what it omits. **One route is created here and is named as such**: a stated floor the cited set does not license, which could not exist before there was a derived floor to - state. **A second is created here**: a **fabricated decline record**, which releases a real hold on a - real finding and which nothing authenticates — §2.1 says exactly that, and omitting it here while - admitting it there would be an enumeration contradicting its own source. Pre-existing and - unchanged: omitting a higher-risk cited story, minting or editing a profile to level 0, + state. Pre-existing and unchanged: omitting a higher-risk cited story, minting or editing a profile to level 0, presenting an incomplete set, falsifying evidence entries, silencing reminders, or not running a pass and reporting that it ran. **None of this is a guard**, and the profile-minting path is bounded only by §5's existing human-confirmation rule. @@ -678,47 +529,45 @@ design* rather than instructions to a future agent stay here. the hook says; the lever is a *stated* floor the profiles do not license. - **The reachability test needs judgement** where §5 is trying to remove it; the phrasing removes arbitrariness, not judgement, and §3 says so. -- **Q6's answer fails toward continuing the loop**, with the disclosure that makes it acceptable. - **The curve is self-reported**; P8 inherits that limit. - **The expected demotion is a prediction**; P8 measures it. If it demotes far less than hoped, the rule is still correct and the economics claim was what was wrong. --- -## 11. What revision 10 moved, and what it did not - -**Mostly a level-of-detail change — and revision 11 records where that claim was wrong.** Gate-A -pass 9 reviewed the restructuring against exactly this claim and found **two Blockers and six -Majors where compression had weakened or dropped a normative detail**, not merely relocated it. -The clearest: the severity test had become "if you can name neither", which demotes only when -*both* a reader and a changed decision are absent — inverting a rule that requires both. The -provenance grammar and the curve format had gone to the plan although both are **durable -interfaces a later reader consumes** and the story requires the spec to state them. - -All eight are repaired above, and the lesson is recorded rather than smoothed over: **a -restructuring guard that only asks "did a decision move?" misses the case where a rule survives in -outline and loses its force.** The check that caught it was asking the reviewer to judge the claim -directly. - -**Moved to the plan** — each reviewed in the plan's own Gate A against this spec: command lines -for the mid-cycle amend (§2.1 keeps the required properties, which is what made the earlier pinned -`-m` form's defect visible); the per-shape diagnostic check specifics in §5; per-site replacement -wordings for §6.1's fourteen sites and §6.2's twenty-three passages; and the conditions artifact's -production procedure (§6 keeps the method and the gate's acceptance condition). - -**Returned to the spec after pass 9:** the provenance grammar and the curve form. Both are read by -something other than a human, so leaving them downstream would leave P8's input undecided — the -test that separates detail from contract here is **"does anything but a person parse it"**, not -length. - -**Kept here, deliberately:** every settled decision with its reason; every rule stated as a -required property; the precedence table; the hold rule; the reachability test and its exclusions; -the floor predicate, unanimity and the hook-precedence rule; Q6's answer and the rejected -alternatives with reasons; the accounting method, the passage list and the split's price; the -falsified-statement inventory; the evidence plan's shape and counterfactuals; scope; and the risks -with their stated limits. - -**Dropped, not moved:** narration of what earlier revisions of *this document* got wrong. Where -such an error produced a rule, the rule is here and the history is in the commit bodies and in -`docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md`, which is where a reader looking for -the lesson should be sent. +## 10. What this spec keeps and what moved out + +**Revision 13 reduced this spec to parts 1 and 2.** Part 3 — the §5 loop-rule consolidation — moved +to `docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md` with every decision the +parent cycle bought, carried there as settled inputs rather than reopened. + +**Why, in numbers.** Eleven Gate-A passes never brought Blocker/Major below 22. Attributed over the +last three: parts 1 and 2 produced **4/1, 8/1 and 7/1**; the loop-rule sections produced **10, 11 +and 14**, rising, with each repair creating an interaction the next pass found. Two of pass 11's +Blockers were generated by the immediately preceding repair, one of them a deadlock fix that +deadlocked one level up. + +**Moved out with part 3:** the exits-and-duties analysis and the precedence table; the hold, the +decline and its record; Q6's unavailable-history answer; and the eleven §5 passages only those rules +rewrite — the loop-absorb paragraph, the clearly-stuck and surfacing paragraphs, the pass-4 report, +the two-rules paragraph, the human-exception and scope-narrow blocks, and the `baseSha`/WIP/amend +block. The successor's accounting owes those. + +**Kept here, and the test that decided each:** *does anything but a person parse it, and does a +shipped criterion read it?* The **provenance line** and the **per-pass curve** both pass — P8 reads +them — so their grammars stay pinned rather than descending to the plan. The **cycle nonce** stays +because both records carry it; that also settles the successor story's open question about where the +nonce lives. The floor predicate, unanimity, the hook-precedence rule, the severity test with its +exclusions, the accounting method and passage list, the falsified-statement inventory, the evidence +plan, scope and the risks all stay. + +**Earlier revisions moved detail to the plan** — exact replacement wordings, the scaffolder's edit +procedures, the per-site texts for §5.1's fourteen sites and §5.2's twelve passages, and the +conditions artifact's production procedure. **Two of those moves were wrong and were reversed**: +the provenance grammar and the curve form, which pass 9 caught. **A restructuring guard that asks +only "did a decision move?" misses the case where a rule survives in outline and loses its force** — +pass 9 found eight of those in one revision. That sentence is the most transferable thing this +cycle produced. + +**Nothing here waits on the successor.** The floor and severity rules bind on their own, which is +what made the split available rather than merely desirable. From 46072fed127bab540880c1b47d1770f831a853f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:02:56 +0200 Subject: [PATCH 029/117] =?UTF-8?q?docs:=20revision=2014=20=E2=80=94=20the?= =?UTF-8?q?=20split's=20accounting=20errors,=20and=20the=20P8=20handoff=20?= =?UTF-8?q?accepted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 12: 24 findings, 5 BLOCKER, 17 Blocker/Major -- BELOW 22 FOR THE FIRST TIME IN TWELVE PASSES. The split prediction ("order of 8-10 or less") was not met at 17, but the floor the loop had held since pass 1 finally broke. All five Blockers were split-accounting errors rather than design defects, which is what a split should produce and did. The important one: six passages were assigned wholly to the successor that THIS change also rewrites -- including the clearly-stuck paragraph, whose "pass 1 carrying a Minor" sentence §5.1 identifies as false under floor 1. Assigning it away would have dropped the floor change's own condition into a story whose scope excludes the floor. The rule is now stated: A PASSAGE BOTH CHANGES REWRITE APPEARS IN BOTH ACCOUNTINGS, each covering its own change. That is not duplication -- the Don't asks what THIS change does to a passage's conditions, and two changes owe two answers. Eighteen passages, not twelve. The nonce stayed with the parent but its consequences had not: it leads the provenance grammar and did not lead the curve or the skipped-loop form, though the rule says every record carries it. Fixed in all three. The curve grammar was a grammar in name only -- ,

and undefined, no ordering or overlap rule, and a production that rejects provider-qualified identifiers like moonshotai/kimi-k3 which this repo already records. Now fully defined, with required to have exactly as many entries as enumerates. Also: the unknown-start fallback's list was syntactically broken and claimed a completeness it did not have; the rollback and activation rules gave two different answers for a loop spanning a revert, and the precedence is now stated (activation wins where the start is determinable, the fallback covers only where it is not); cited-set changes are no longer categorically raises or lowerings, since adding a level-0 story to an all-level-0 set moves nothing; the knob's `unusable` state splits into unreadable, empty, non-numeric and out-of-range, because a permission problem is not a typo and one token names a symptom rather than a cause; and the pass report must state the axes and their source stories, not just the floor, which is what makes the derivation checkable while passes are still being spent. Three seams closed rather than left to the successor to discover: the successor now names the squash-carry and unknown-start passages it EXTENDS rather than replaces, and the clearly-stuck paragraph both stories owe; its open question about the nonce is answered; and its settled-inputs table gains the decline record's five-field identity and its unverified-assertion standing, which the parent paid for and the successor would otherwise have re-derived. And the P8 story now ACCEPTS the handoff instead of merely being named by it: a criterion for the review-loop question, the two pinned forms it reads, the floor-1 checkpoint, and the requirement to report that the curves are author-written and unchecked. Its standing note that "whether closing bodies should be required to carry the curve is not decided here" is struck through -- the parent decided it -- with the subset problem it raised closed for cycles under the new rules and open for every cycle before them. TEMPLATE MIRROR CHECK, done here because twelve passes have not done it: fence and §5 bounds confirmed at 192/257/778; the prose-exemption rationale confirmed ABSENT from the template, which is §6's prerequisite and not a defect; all eighteen §5.2 passages present exactly once; the six floor sites present; the digit-free pass-1 sentence at :322. The coverage statement the close owes can now rest on this. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-12. Findings 27,30,54,40,33,34,32,33,28,27,38,24. Blockers 5,2,0,4,0,3,9,4,2,2,2,5. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17. Gate B: N/A -- all staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 100 +++++++++++++----- ...4-passive-metrics-over-the-ledger-story.md | 21 +++- ...026-08-29-loop-rule-consolidation-story.md | 25 ++++- 3 files changed, 111 insertions(+), 35 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index a6d4a1d..f762194 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,7 +1,7 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 13 (reduced to parts 1+2), after Gate-A passes 1-11 -(27, 30, 54, 40, 33, 34, 32, 33, 28, 27, 38 findings) +**Date:** 2026-08-29 · **Revision:** 14, after Gate-A passes 1-12 +(27, 30, 54, 40, 33, 34, 32, 33, 28, 27, 38, 24 findings) **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile (read from that header, not copied):** risk `high` · security `none` · validation `battery+check+verification`, no `+abuse-path`. @@ -139,6 +139,12 @@ that drives rung escalation. **Nothing here writes `.context/codex-gate.floor`.** The floor is derived and **stated** — in every pass report and in the commit-body provenance line — and §5's text is what binds an agent. +**What a pass report states**, since the story requires it and the floor is otherwise invisible +until the cycle closes: the **derived floor**, the **risk and security values it read**, and the +**cited stories it read them from**. A report giving the number alone leaves a reader unable to +check the derivation while passes are still being spent, which is the only time checking it is +cheap. + **The knob stays the user's, and is the hook's reminder threshold.** Never written, never removed, never read for the derivation. It never bound an agent: `$floor` does appear in control flow (`:946`, `:966`), but that flow only selects **which advisory message fires**, and the hook exits 0 @@ -187,7 +193,8 @@ cycle ; floor per ; hook reminder threshold := " (level " <0|1|2> ")" a profiled story | " (unprofiled)" a cited story with no profile := | - := "absent" | | "unusable" + := "absent" | | "unusable(" ")" + := "unreadable" | "empty" | "non-numeric" | "out-of-range" ``` A `` is repo-relative and contains none of `,` `{` `}` `;` `"` or whitespace; any other @@ -201,7 +208,7 @@ One instance per variant, all parsing under the grammar above: cycle k7m2q9xa; floor 1 per {docs/superpowers/stories/A-story.md (level 0)}; hook reminder threshold absent cycle k7m2q9xa; floor 3 per {A-story.md (level 0), B-story.md (level 2)}; hook reminder threshold 3 cycle k7m2q9xa; floor 3 per {A-story.md (level 0), C-story.md (unprofiled)}; hook reminder threshold 1 -cycle k7m2q9xa; floor 3 per none; hook reminder threshold unusable +cycle k7m2q9xa; floor 3 per none; hook reminder threshold unusable(non-numeric) cycle k7m2q9xa; floor 3 per {"docs/stories/odd, name.md" (level 2)}; hook reminder threshold absent ``` @@ -222,7 +229,11 @@ without the disclosure would present a parseable number as a verified one. numeral** — not an entry per story, since unanimity makes the floor a property of the set. - It distinguishes a **cited story with no profile** from **no story cited**. - The **knob is recorded whenever the file exists**, including when present but unusable, since - the hook ignores such a value and the line describes what the hook will do. + the hook ignores such a value and the line describes what the hook will do. **The unusable cases + are distinguished rather than merged**: unreadable, empty, non-numeric and out-of-range need + different fixes — a permission problem is not a typo — and a single `unusable` token would tell a + reader only that something is wrong, which prompt-standards item 10 treats as naming a symptom + instead of a cause. - It is **machine-extractable**, because the deferred P8 measurement reads it, and **one form covers every case** — a second pinned form for a special case is what made earlier revisions unparseable. @@ -235,8 +246,11 @@ pass; **passes already run keep counting**; **closing requires the floor as curr **The cited *set* can move too, not only a profile's values** — a story added, removed or corrected mid-cycle — and the same three rules cover it: the set is re-read at each pass, the floor is re-derived from it under unanimity, and closure requires the floor the current set -yields. **Adding a story is a raise for this purpose** and carries the same one-further-pass -consequence. **Removing one lowers the floor and releases nothing else**: an accepted in-set +yields. **Adding or removing a story changes the derived floor only if it changes the +unanimity verdict** — adding a level-0 story to an all-level-0 set moves nothing, and removing one +non-zero story from a set with two leaves the floor at 3. Where the verdict does move, **upward is +a raise** and carries the one-further-pass consequence; **downward lowers the floor and releases +nothing else**: an accepted in-set Blocker or Major stays in the set and must still resolve, because it entered by the user's answer and not by the story that first raised it. A set change moves the floor; it is not a route to discharge findings. @@ -260,21 +274,34 @@ unmeasured** — the loops this story cites as evidence are Gate-A loops. it to the plan would leave a durable interface undecided. ``` - (passes , ): Findings , , …. Blockers , , …. - := "Gate-A spec loop" | "Gate-A plan loop" | "Gate B" - := a comma-and-range list of the pass numbers covered, e.g. "1-3" or "1,2,4" +cycle ; (passes , ): Findings . Blockers . + + := [a-z0-9]{8,16} the cycle nonce (§1.1) + := "Gate-A spec loop" | "Gate-A plan loop" | "Gate B" + := ("," )* strictly ascending, non-overlapping + :=

|

"-"

the second greater than the first +

:= [1-9][0-9]* a pass number + := ("," )* one per pass in , same order + := 0 | [1-9][0-9]* a finding or Blocker count := | ("; " )* := "pass "

" " ("+" )* - := [A-Za-z0-9._-]+ as reported by the call, never recalled + := [^ ;:,()]+ | "undetermined" verbatim as the call reported it ``` +`` has exactly as many entries as `` enumerates, so a reader can map each number to +its pass without inference. `` is deliberately permissive about punctuation because +provider-qualified identifiers like `moonshotai/kimi-k3` are already in this repo's records; what +it excludes is the grammar's own delimiters. + One bare `` means every covered pass ran under it. `` entries are semicolon-separated and must cover **every** pass the `` names; a pass assembled from calls under different models joins them with `+`. **A model that cannot be determined is written `undetermined`**, never omitted and never guessed — `docs/coding-workflow.md` already requires recording it that way where neither config level names one. -A skipped loop writes `: skipped (see skip reason)` and no counts. +A skipped loop writes `cycle ; : skipped (see skip reason)` and no counts — the +nonce leads **every** form, skipped included, or a skip cannot be attributed to the cycle that +took it. **Required properties the form exists to satisfy:** - One entry per **valid** pass, in pass order; **incomplete passes are excluded**, and because @@ -360,10 +387,16 @@ stating a rule. ### 5.2 The passages this change rewrites -Each present exactly once in both copies. **Twelve; the conditions artifact is incomplete without -all of them.** Eleven further passages went to the successor story with part 3 — the loop's exits -and duties, the human-exception and squash blocks it rewrites, and the WIP/amend block — and its -accounting owes them. +Each present exactly once in both copies. **Eighteen; the conditions artifact is incomplete without +all of them.** + +**A passage both changes rewrite appears in both accountings**, each covering its own change. That +is not duplication: the AGENTS.md Don't asks what *this* change does to a passage's conditions, and +two changes to one passage owe two answers. Getting this wrong is how a condition is dropped — +Gate-A pass 12 found six passages assigned wholly to the successor that **this** change also +rewrites, including the clearly-stuck paragraph, whose "pass 1 carrying a Minor" sentence §5.1 +identifies as false under floor 1. Assigning it away would have dropped the floor change's own +condition into a story whose scope excludes the floor. | # | Passage | Rewritten by | |---|---|---| @@ -379,6 +412,12 @@ accounting owes them. | 10 | "Changing a profile" | §4.2 | | 11 | The findings-slot naming paragraph | §5 — the grammar gains an optional per-cycle infix | | 12 | "Before each call, delete every target file…" (`:199/:386`) | §5 — the infix, and a **refuse-on-collision** case where the target belongs to another cycle | +| 13 | "Recognizing \"clearly stuck\"" | **part 1** — its "pass 1 carrying a Minor" sentence is false under floor 1 (§5.1). *The successor also rewrites this passage for the ordering; both accountings owe it.* | +| 14 | "Recording a human exception" | §1.1 — the nonce appears in every cycle record, this one included | +| 15 | "The evidence entry lives in the commit body" | §1.1 — same | +| 16 | "Optional companions, from field practice" | §1.1 — the nonce, plus the working record's role and the collision rule; §5's optional-companion *status* is unchanged | +| 17 | "On squash-merge, copy every evidence entry…" | §4 — the provenance line, curves and skip records added to the carry. *The successor adds the decline record; both accountings owe it.* | +| 18 | The `baseSha`/WIP/closing-amend block (`:500-517`, template `:679-696`) | §4 — the closing body must carry the provenance line and curve. *The successor adds the WIP-amend properties; both accountings owe it.* | **Row 18's change is a replacement, not an addition beside it.** Today's grammar admits three exact names; an infixed name satisfies none. The shipped text replaces each with one admitting an @@ -451,8 +490,9 @@ Mode `battery+check+verification` (no `+abuse-path`). - **A conditional verification that a user's knob survives untouched** — byte-identical across a cycle where one exists; **recorded not-applicable with its reason where none does.** An agent must not create one to make a check runnable, which would be a fixture supplying its own input. -- **Parity across every changed rule**, per story criterion 7 — §5.1's fourteen sites, §5.2's twelve - passages, and every rule §§2–4 insert, including §9's residual disclosure and §3.1's +- **Parity across every changed rule**, per story criterion 7 — §5.1's fourteen sites, §5.2's eighteen + passages, and every rule §§1.1–4 insert — the nonce's generation, recovery, record and + collision duties included — plus §9's residual disclosure and §3.1's provenance properties, which the story requires in **both** copies. The copies already differ on 192 lines, so parity cannot be asserted from a whole-section diff. - **A fresh twelve-item `docs/prompt-standards.md` pass** over every changed prompt region. **Item @@ -493,9 +533,11 @@ design* rather than instructions to a future agent stay here. when passes were banked would invalidate a count nobody could reconstruct. **Where a loop's starting rules cannot be established it takes the stricter reading of every part this change touches**, named rather than left to interpretation: **floor 3**; **severity classified without the demotion**, so nothing is collected that would otherwise iterate; **every - and **the curve duty treated as owed**. **That list is the whole of it** — the parts this change - touches are the floor, severity and the curve, and each has an entry above; a part not named here - is a part this change did not touch. (The successor story extends this fallback to the loop rules + and **the curve duty treated as owed**. **That list covers this change's rules; it is not a list of everything a + cycle owes.** The parts this change touches are the floor, severity, the curve, and the cycle + nonce with the record and collision duties that follow from it — and each is treated at its + strictest: the nonce is required, recovery ambiguity resolves to a new cycle, and the + collision-refusal rule applies. A part not named here is a part this change did not touch. (The successor story extends this fallback to the loop rules it ships; extending a list is safe where replacing it would not be.) **A user knob set above 3 is not lowered by the fallback**: the knob moves the hook's reminder threshold while the fallback sets the *obliged* floor, so a workspace asking for louder reminders keeps them. Not a @@ -506,7 +548,8 @@ design* rather than instructions to a future agent stay here. the text — which invariant 9 permits to write nothing, be declined, or be merged in part. **The rules bind only over the text a project's `CLAUDE.md` contains**, and a partial adoption can persist undetected. **This is in tension with §1's coupling argument and the tension is real**: a - project taking the severity rule without the ordering gets a loop this design never evaluated. + project taking the floor rule without the severity test gets a floor whose `docs-only` question + §1 says the severity test settles. What prompt text can do is done; what it cannot is said. - **The gate-off surface — routes known today, not a complete list**, since an enumeration read as complete guarantees what it omits. **One route is created here and is named as such**: a stated @@ -516,10 +559,13 @@ design* rather than instructions to a future agent stay here. pass and reporting that it ran. **None of this is a guard**, and the profile-minting path is bounded only by §5's existing human-confirmation rule. - **Rollback, once a rule has been adopted.** In this repo a bad rule is reverted like any other - commit — **and the revert is itself a shipping commit for the *old* rules**, so a loop in flight - across it lands in the unknown-start case rather than cleanly under either version. That is - intended: the fallback's stricter reading is the right answer for a loop whose rules changed - underneath it, in either direction. **Downstream there is no + commit — **and the revert is itself a shipping commit for the *old* rules.** A loop in flight across it + therefore has two answers available and they disagree: the activation rule says a loop finishes + under the rules it started with, while the fallback says an indeterminate start takes the + stricter reading. **The activation rule wins where the start is determinable** — a loop whose + own first pass artifact predates the revert finishes under the rules it began with, which is + what that rule is for. The fallback applies only where the start cannot be established. Stating + the precedence is the point; without it a revert makes every in-flight loop ambiguous. **Downstream there is no revert**: a project's `CLAUDE.md` is its own file, so withdrawing a rule means shipping a corrected template and waiting for each project to re-run the scaffolder and accept the diff — the same partial-adoption path, with the same absence of detection. **A rule that turns out @@ -562,7 +608,7 @@ exclusions, the accounting method and passage list, the falsified-statement inve plan, scope and the risks all stay. **Earlier revisions moved detail to the plan** — exact replacement wordings, the scaffolder's edit -procedures, the per-site texts for §5.1's fourteen sites and §5.2's twelve passages, and the +procedures, the per-site texts for §5.1's fourteen sites and §5.2's eighteen passages, and the conditions artifact's production procedure. **Two of those moves were wrong and were reversed**: the provenance grammar and the curve form, which pass 9 caught. **A restructuring guard that asks only "did a decision move?" misses the case where a rule survives in outline and loses its force** — 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 index dc89293..0c274ec 100644 --- 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 @@ -31,7 +31,7 @@ From `todos.md`, "**P8 — passive metrics, read-only over the ledger and git.** | 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 | -### Second question routed here, added 2026-08-28 +### Second question routed here, added 2026-08-28 (updated 2026-08-29: the forms it reads are now pinned) `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` defers its economic measurement to this story rather than to a fresh backlog row. The question it hands @@ -49,10 +49,11 @@ condition is doing real work: The findings files those numbers come from live under `.context/`, which is gitignored and per-clone. So this question is answerable from git only for cycles whose author wrote the curve down. -- **Whether closing bodies should be *required* to carry the curve is not decided here**, and - it is not this story's to decide — it is a §5 rule, and it belongs to whoever owns that text. - Recorded so the gap is visible rather than discovered later by an analysis that quietly - reports on the subset of cycles that happened to be legible. +- ~~**Whether closing bodies should be *required* to carry the curve is not decided here.**~~ + **Decided 2026-08-29 by the parent story**, which makes the curve and the provenance line + required and pins both forms. The subset problem this bullet raised is therefore closed for + cycles run under those rules and remains for every cycle before them — an analysis spanning both + says which side of that line each cycle falls on. Neither point changes this story's scope: still read-only, still no instrumentation, still nothing written back. @@ -73,6 +74,16 @@ from the file rather than from recall — without the analysis writing anything 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. +- [ ] **The review-loop question routed here is answerable, or its gap is named.** The + review-loop-economics story defers its economic measurement to this one: after roughly three + profiled cycles under the new rules, read their pass counts and finding distributions against + the `fic2` baseline. That analysis reads **two pinned commit-body forms** — the provenance + line and the per-pass curve, both specified in + `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` — and its **first + checkpoint** is the first post-merge cycle whose cited set licenses floor 1, which must carry + the floor-1 provenance line. Whatever this analysis reports from those forms says plainly that + **the curves are author-written and unchecked**: nothing compares them against the validated + pass files, so they are self-reported and not measurement. ## 4. Affected AGENTS.md invariants diff --git a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md index 513caf4..45ceda0 100644 --- a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md +++ b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md @@ -93,6 +93,8 @@ and in `docs/field-reports/2026-08-26-fic2-cycle-evidence.md`. **The design begi | 7 | **A decline binds for the remainder of its cycle and has no effect in any later one** (Q4). | | 8 | **"Explicitly declined" is a recorded user decision on that specific finding**, attributable and unambiguous — never silence, never a general remark about scope, never inferred. | | 9 | **The decline is recorded in the commit body**, reusing the human-exception transport as a **distinct record type** — the two differ in force, since the human-exception form authorizes nothing. | +| 9b | **The record stores exactly what the sameness test reads: location, defect, severity, consequence and suggested fix.** A test reading fields the record lacks is a wiring failure. Sameness requires all five to match; **any difference — including severity, since a Minor re-raised as a Blocker is not the thing that was declined — makes it a new finding and the hold applies**, as does any genuine uncertainty. | +| 9c | **The record reads as an unverified assertion**, like the human-exception record beside it: nothing checks that the handle belongs to whoever decided. **Narrowness bounds what a false record can do — one fully-identified finding, one cycle — and that is not the same as making it safe**; a fabricated decline still releases a real hold and nothing detects it. | | 10 | **Q6:** when prior-pass history is unavailable, the pass report states what is computable, names what is not and why, and discloses the reduced sensitivity. Not a new stop condition, not a mandatory resume note. | **Two implementation facts the parent cycle established, carried so they are not rediscovered:** a @@ -100,6 +102,23 @@ pass's cleanliness is a fact about what that pass found and is **never rewritten answer changes is whether the *cycle* may close; and the findings files establish the **inventory** of in-set findings, not their resolutions, which they do not contain. +### Two passages this story shares with the parent + +Both changes rewrite these, and **this story must extend rather than replace what the parent added** +— the parent's accounting already covers its own half: + +- **The squash-carry rule.** The parent adds the provenance line, the per-pass curves and a skipped + loop's skip record to what a squash must carry. **This story adds the decline record**, and a + rewrite that drops the parent's three would silently unship them. +- **The unknown-start fallback** (what a loop does when its starting rules cannot be established). + The parent covers the floor, severity, the curve and the nonce duties at their strictest. **This + story extends the same list to the loop rules it ships** — suspensions binding, decline records + treated as absent so no hold is released. Extending is safe; replacing is not. + +A third, for the same reason: the **clearly-stuck paragraph** is rewritten by both — by the parent +because its "pass 1 carrying a Minor" sentence is false under a floor of 1, and by this story +because the exit becomes a suspension in the ordering. **Both accountings owe it.** + ## 5. Open questions - **The profile.** Proposed `high / none / battery+check+verification`, on the same reasoning the @@ -110,9 +129,9 @@ of in-set findings, not their resolutions, which they do not contain. - **How much of the ordering is new text versus reference.** §5 already contains the two sentences from which "only clean completion closes" follows; whether the ordering is stated fresh or assembled from what is there changes the old-conditions accounting and the parity surface. -- **Whether the cycle nonce belongs here.** The parent introduced it to bind a decline to one - cycle, and it now also serves slot naming and record attribution. If the parent ships it, this - story consumes it; if not, this story needs it. +*(Resolved 2026-08-29: the cycle nonce stays with the parent, which ships it because both of the +records parts 1+2 produce carry it. **This story consumes it and does not define it** — it uses the +nonce to bind a decline to one cycle, per settled decision 7.)* ## 6. Suggested size From 8bc04d61331e82fbd332a70a9ea5dfb1ca954bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:30:29 +0200 Subject: [PATCH 030/117] =?UTF-8?q?docs(spec):=20revision=2015=20=E2=80=94?= =?UTF-8?q?=20rules=20only,=20604=20->=20332=20lines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Daniel's decision on the fourth mandatory Gate-A stop: radical slim, then a clean pass. He declined the close-unclean proposal -- the clean-final-pass rule stands, applied to an artifact sized for it. TWO REAL BLOCKERS FIXED FIRST, defects under every option: The spec treated the three loops as loops of one cycle. CLAUDE.md:218-219 is explicit that they are THREE CYCLES -- "Gate A runs separate spec and plan loops, so those are two cycles; Gate B is one cycle." So: three nonces, one provenance line per cycle, and the shared-derivation argument restated on its true basis -- they share a floor because they derive from the same CITED-STORY SET, not because they are one cycle. The old reason was false and would have shipped. And the predicate's fall-through: "everything else -> 3" swallowed a case §5 handles differently. A present-but-unresolvable profile STOPS AND SURFACES, and that rule stands; the predicate applies only to profiles that resolve and to artifacts citing no story. Reading an unresolvable profile as 3 would have converted an existing stop condition into a silent default. THE SLIM. Test applied per element, in order: does a shipped criterion read it? does anything but a person parse it? is it a decision? Three noes and it left. MOVED TO THE PLAN: the old-conditions passage list and its apparatus (the method stays, one paragraph, and the plan executes it); all grammar productions and concrete record forms (required properties stay, productions go); the falsified-statement site list (the obligation stays); per-site wordings; the generated grep and its site inventory; and the instrument-discipline procedure. DROPPED, not moved: everything whose subject was this document rather than the workflow -- the account of what earlier revisions moved, why the marker mechanism was deleted, the pass-2 tension narrative, the split's price as narrative rather than rule, prediction ledgers, and cross-reference apparatus. Where such a passage had produced a rule, the rule stayed and the history is in commit bodies and the dispositions file. KEPT: every decision with the rationale that is itself contract -- the reachability test's exclusions, the disclosure obligations, the coupling argument for shipping parts 1+2 together. Required properties for the provenance line and the curve, because a shipped criterion reads them and P8 parses them. The nonce. The accounting method. Activation, fallback, rollback precedence, and the gate-off disclosure. Also recorded in the dispositions file: pass 13 produced the loop's FIRST WRONG FINDING in thirteen passes -- a claimed syntax break that revision 14 had already replaced, dismissed with grep evidence. Roughly one bad finding in ~380. That ratio argues for validating before applying rather than against it. Sparring session, under Daniel's 2026-08-28 delegation; the slim was Daniel's direct decision. Gate A: passes 1-13. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- .../2026-08-16-canvas-a1-a5-dispositions.md | 11 + ...2026-08-28-review-loop-economics-design.md | 795 ++++++------------ 2 files changed, 265 insertions(+), 541 deletions(-) diff --git a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md index 42006be..96c129f 100644 --- a/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md +++ b/docs/field-reports/2026-08-16-canvas-a1-a5-dispositions.md @@ -365,3 +365,14 @@ restatement that must track a moving original. It appeared in the story's accept (five Blocker occurrences), then inside the spec's own accounting table (four more). The lesson is not "write the table more carefully"; it is that a second copy of a moving thing drifts, and the remedy is to produce it once against something that has stopped moving. + + +**The loop's first wrong finding, 2026-08-29.** Gate-A pass 13 of the review-loop-economics cycle +returned a BLOCKER claiming a spec sentence was "syntactically incomplete at *every and the curve +duty treated as owed*". `grep 'every and'` on the reviewed file returns nothing — the break existed +in revision 13 and revision 14 had replaced the sentence. **Dismissed with that evidence.** + +Worth recording because it is the **first outright wrong finding in thirteen passes** of that +loop — roughly 380 findings. That ratio is the argument for validating before applying rather than +against it: the discipline cost thirteen passes' worth of checking and caught one, and the one it +caught would otherwise have driven an edit to text that was already correct. diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index f762194..643e846 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,619 +1,332 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 14, after Gate-A passes 1-12 -(27, 30, 54, 40, 33, 34, 32, 33, 28, 27, 38, 24 findings) +**Date:** 2026-08-29 · **Revision:** 15 (rules only) · **Gate-A passes 1-13** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` -**Profile (read from that header, not copied):** risk `high` · security `none` · -validation `battery+check+verification`, no `+abuse-path`. - -**What this document is, after revision 10.** Contract level only: settled decisions with their -reasons, rules stated as required properties, named interfaces, and scope. **Exact replacement wordings, -command lines and step-by-step procedures live in the plan**, where they get their own Gate A -against this spec once it is stable. **Two formats are the exception and are pinned here**: the -provenance line (§3.1) and the per-pass curve (§4), because **something other than a person -parses them** — that is the test separating detail from contract here, not length. Nothing escapes review by moving; -what changes is when it is reviewed. Revision 10 is a level-of-detail change and moves no -decision — §10 lists what went where. - -Prompt-only. **No file under `plugins/dev-workflow/hooks/` changes, and no hook *state* file is -written.** One file the hook *reads* is edited: `codex-gate.sh:94` greps `CLAUDE.md` for the §5 -heading to build every reminder's citation, so **the §5 heading must keep matching -`^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`** or the citation degrades to a -generic fallback. - -**Surfaces.** `CLAUDE.md` §5 (65–589); its mirror in `/workflow-init`'s inline template -(`plugins/dev-workflow/commands/workflow-init.md`, fenced 192–778, §5 at 257–777); the -user-facing statements §7 lists; and this story's own acceptance criteria. The two §5 copies -already differ on 192 lines; only the rules this change touches are brought to parity. +**Profile:** read from that header, never from here. + +Prompt-only, in two mirrored copies: `CLAUDE.md` §5 and `/workflow-init`'s inline template. +**No file under `plugins/dev-workflow/hooks/` changes, and no hook state file is written.** + +**This document states rules and decisions.** Concrete formats, per-site wordings, the +old-conditions passage list, and every procedure live in the plan, reviewed there against this +spec. Two things stay pinned as *required properties* because a shipped criterion reads them and a +program parses them: the provenance line (§2.3) and the per-pass curve (§4). + +**One constraint on the edit itself:** `codex-gate.sh:94` greps `CLAUDE.md` for the §5 heading to +build every reminder's citation, so **the heading must keep matching +`^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`**. --- -## 1. What these two parts change, and why they ship together +## 1. Why these two parts ship together + +Part 1 makes the mandatory pass floor a function of the story profile. Part 2 decides finding +severity by whether something in the system takes a different decision. + +**They are coupled by an argument.** Part 2's test settles a part-1 question: a path-derived +`docs-only` arm for the floor would be **wrong here**, because `docs/hardening-log.md` is a +`docs/**.md` path that drives rung escalation. Splitting these two would separate a rule from the +argument that decides it. + +The §5 loop-rule consolidation is **not** in this spec; it moved to +`docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md`. Nothing here waits on it. + +--- + +## 2. The pass floor (part 1) + +**One predicate.** `max(risk, security) == 0` → floor **1**. Every resolvable profile above that, +and an **absent** profile, → **3**. Two levels, not three: `high` takes its rigor from lens sets +and evidence mode. + +**A present but unresolvable profile is not "everything else".** §5 already requires it to **stop +and surface the cause**, and that rule stands unchanged — the predicate applies only to profiles +that resolve, and to artifacts citing no story. Reading an unresolvable profile as 3 would convert +an existing stop condition into a silent default. -**Part 1** makes the mandatory pass floor a function of the story profile instead of a flat 3. -**Part 2** decides finding severity by whether something in the system takes a different decision, -instead of by what kind of file the text lives in. +**Unanimity across a cited set.** Floor 1 **if and only if every cited story is profiled and every +one is at level 0**. Any other set yields 3. This follows §5's own precedent — "skip-eligible only +if **every** cited story is" — and is the only reading consistent with invariant 2's firing +direction. + +**One derived value governs the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle.** Not +because they are one cycle — §5 is explicit that they are **three separate cycles** — but because +they derive from **the same cited-story set**. Both copies state the rule and this reason. + +### 2.1 The floor lives in the text -**They are coupled by an argument, not by convenience.** Part 2's reachability test is what settles -a part-1 question: a path-derived `docs-only` arm for the floor would be **wrong in this repo**, -because `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation — "docs" does -not imply "changes nothing". Splitting these two would separate a rule from the argument that -decides it. +**Nothing here writes `.context/codex-gate.floor`.** The floor is derived and **stated**; §5's text +is what binds an agent. -**The §5 loop-rule consolidation is no longer in this spec.** It moved to -`docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md` after Gate-A attribution -showed it generating 10, 11 and 14 Blocker/Major over three passes while these two parts held at -4/1, 8/1 and 7/1. Every decision that cycle bought is carried there as a settled input. **Nothing -here depends on that work landing first**, which is what made the split available: the floor and -severity rules bind on their own. +**The knob stays the user's** — never written, never removed, never read for the derivation. It is +the **hook's reminder threshold** and never bound an agent: `$floor` appears in control flow, but +that flow only selects which advisory message fires, and the hook exits 0 on every branch +(invariant 1). -### 1.1 The cycle nonce +**Precedence, shipped as text:** -Both records these parts ship — the provenance line (§3.1) and the per-pass curve (§4) — are read -by something other than a person, and a record that cannot be attributed to a cycle is not usable -by the measurement that reads it. So each carries a **cycle nonce**. +> **The derived floor controls whether a cycle may close. The hook's ratio is a reminder threshold +> and controls nothing.** Where the derived floor and the ordinary closure rules are satisfied, a +> below-threshold reminder is **noted in the pass report and disregarded.** -Generated once at cycle start, immutable, and **collision-resistant in an operational sense rather -than an aspirational one: at least 8 characters drawn uniformly from `[a-z0-9]`, from a source of -randomness** — never derived from a name, a timestamp or a commit, each of which collides exactly -where sibling cycles do. It matches `[a-z0-9]{8,16}`, so it is also safe as a slot infix and a path -component. **It appears in every record the cycle writes.** +**Named residual, disclosed in both copies:** the hook's messages state its own threshold as an +obligation, so at level 0 they report a shortfall the cycle does not owe. **Hook text is out of +scope by decision**; the precedence rule plus invariant 1 are what make it tolerable, not the +reminder being harmless. -**Recovery has two sources, because a Gate-A loop's commit does not exist while it runs:** during -the loop the nonce lives in the advisory working record beside the findings files, and it becomes -history at the loop's commit. A cycle recovering it from **either** keeps its identity. **A cycle -that can recover it from neither — or whose sources disagree, or which finds more than one -candidate — has no identity and starts a new cycle**, which costs passes rather than letting one -cycle's records be read as another's. +### 2.2 What a pass report states -**The advisory working record is a cycle record too**: it carries the nonce, and §5's per-cycle -infix and refuse-on-collision rule apply to it as they do to findings slots. §5's -optional-companion rules are otherwise unchanged. +The **derived floor**, the **risk and security values read**, and the **cited stories they were +read from**. A report giving the number alone leaves a reader unable to check the derivation while +passes are still being spent, which is the only time checking it is cheap. -## 2. Severity semantics (part 2) +### 2.3 The provenance line — required properties -**One procedure decides severity, and the subject list is illustration, not a second rule.** +One line per cycle, in its closing commit body. The plan fixes the grammar; these properties are +the contract: + +- **Every cycle records it**, default floor or not, so an absent line is never ambiguous between + "the default applied" and "someone forgot". **Three cycles means three lines**, one per cycle. +- It carries that cycle's **nonce** (§5), the **derived floor**, and **the cited set that produced + it with each member's level as a numeral** — one floor and one set, not an entry per story, + since unanimity makes the floor a property of the set. +- It distinguishes **a cited story with no profile** from **no story cited**. +- It records the **workspace knob whenever the file exists**, including when present but unusable, + **naming the cause** — unreadable, empty, non-numeric, out-of-range — because those need + different fixes and one token names a symptom rather than a cause. +- It is **machine-extractable and has one form covering every case**, because the deferred P8 + measurement parses it. + +### 2.4 A profile or cited set that moves mid-cycle + +Three existing rules compose; no new rule. The floor derives from the **current** profile at each +pass; **passes already run keep counting**; **closing requires the floor as currently derived.** + +**A raise costs at least one further pass regardless of the arithmetic**, because §5 already +requires the final clean pass to run under the current profile — so even a raise leaving the floor +unchanged costs a pass. **A lowering** drops the floor, the lens sets and the evidence mode +together, so it closes on **one further pass after the lowering**. That is the pre-existing +profile-change path; the variable floor rides it. + +**A cited-set change moves the floor only if it changes the unanimity verdict** — adding a level-0 +story to an all-level-0 set moves nothing. Where the verdict moves, upward is a raise with the +same one-further-pass consequence; downward lowers the floor and **releases nothing else**. + +--- + +## 3. Severity semantics (part 2) + +**One procedure decides severity. The subject list is illustration, not a second rule.** > Name **what in the system consumes this text** — whatever *acts* on it — and the decision that -> act takes differently if the text is wrong. **Both are required.** If you cannot name **both** -> — the act, and the decision it takes differently — the finding is **Minor or below**; collect, -> never iterate. +> act takes differently if the text is wrong. **Both are required.** If you cannot name both, the +> finding is **Minor or below**; collect, never iterate. + +The exclusions are contract, not commentary: - **The reader must consume the text in the system's *operation*, not in reviewing it. The review pass raising the finding is not an in-system reader of the text it reviews.** Without this the - test demotes nothing, since any review finding could name the review itself. **Gates remain - legitimate readers** of rule text they will later apply. + test demotes nothing. **Gates remain legitimate readers** of rule text they will later apply. - **A human reader never satisfies the test** — §5's prose exemption already prices that cost as - non-gating: "a wrong sentence costs a confused reader, not broken behaviour". -- **The list of reader kinds is illustrative, not closed.** This ships into projects whose readers - we have never seen (invariant 10). + non-gating. +- **The list of reader kinds is illustrative, not closed**, because this ships into projects whose + readers we have never seen (invariant 10). - **The test sets a ceiling, not a floor**, and **never chooses between Blocker and Major** — Mechanics still decides that. - **The instrument carve-out is symmetric**: an instrument finding keeps its severity whenever it shows the instrument changes what a gate concludes about product behaviour — a false green, and equally a false red or a check blocking a valid change. - **Rationale prose is Minor only when no rule's application depends on it**, not categorically. - `docs/prompt-standards.md:49-51` requires that "Rules carry their why", because "models follow - motivated rules better", and invariant 11 makes that binding — so rationale a reader must - consult to apply a rule passes the test. + `docs/prompt-standards.md` requires that rules carry their why, because models follow motivated + rules better, and invariant 11 makes that binding — so rationale a reader must consult to apply + a rule passes the test. - **This removes arbitrariness, not judgement**, and the shipped text says so. -- **Coverage-first is unchanged**: the reviewer reports every finding with severity and - confidence; the filter is ours. - -**Kinship, stated in the shipped text:** this is the **finding-level analog of the path-level -prose exemption** — one principle at two granularities, text that *describes* the product versus -text that *is* the product. Each becomes the other's consistency check. +- **Coverage-first is unchanged**: the reviewer reports every finding with severity and confidence; + the filter is ours. -**Why not artifact kinds.** The field record falsifies that form: of three `fic2` pass-5 findings -on one story's acceptance criterion, two were correctly parked and **one correctly acted on**. -Same artifact, same pass, opposite correct answers. +**Kinship, stated in the shipped text:** the **finding-level analog of the path-level prose +exemption** — one principle at two granularities, text that *describes* the product versus text +that *is* the product. -**Expected effect, with its limit.** PR #23-class distributions should demote substantially. **How -much is not predictable** — `7bbdb14`'s own body describes harness defects that could make checks -pass for wiring reasons, which the symmetric carve-out keeps. `fic2`'s meta cluster demotes only -partially: ledger rows and story criteria keep their severity because escalation and the -assigned-fix-set rule read them. **The amount is a prediction, not a measurement**, which is why -the story routes it to P8. +**Expected effect, with its limit, disclosed rather than claimed.** Distributions like PR #23's +should demote substantially, but **how much is not predictable** — that commit's own body describes +harness defects the symmetric carve-out keeps. Ledger rows and story criteria keep their severity +because escalation and the assigned-fix-set rule read them. **The amount is a prediction, not a +measurement**, which is why the story routes it to P8. --- -## 3. The pass floor (part 1) - -**One predicate.** `max(risk, security) == 0` → floor **1**; everything else → **3**, unprofiled -included. Two levels, not three: `high` takes its rigor from lens sets and evidence mode. - -**Unanimity across a cited set.** Floor 1 **if and only if every cited story is profiled and every -one is at level 0**. This follows §5's own precedent — "skip-eligible only if **every** cited -story is" — and is the only reading consistent with invariant 2's firing direction. - -**One derived value governs every loop of the cycle** — Gate-A spec, Gate-A plan, Gate B — because -those loops cite the same stories. Both copies say so and say why. - -**No `docs-only` arm.** A diff-derived reading cannot serve Gate A, which runs before a diff -exists; a story-declared one is subsumed, since intake defines `trivial` as no behavioural effect; -and path-derived would be **wrong here**, because `docs/hardening-log.md` is a `docs/**.md` path -that drives rung escalation. - -### 3.1 The floor lives in the text, not in a file - -**Nothing here writes `.context/codex-gate.floor`.** The floor is derived and **stated** — in every -pass report and in the commit-body provenance line — and §5's text is what binds an agent. - -**What a pass report states**, since the story requires it and the floor is otherwise invisible -until the cycle closes: the **derived floor**, the **risk and security values it read**, and the -**cited stories it read them from**. A report giving the number alone leaves a reader unable to -check the derivation while passes are still being spent, which is the only time checking it is -cheap. - -**The knob stays the user's, and is the hook's reminder threshold.** Never written, never removed, -never read for the derivation. It never bound an agent: `$floor` does appear in control flow -(`:946`, `:966`), but that flow only selects **which advisory message fires**, and the hook exits 0 -on every branch (invariant 1). - -**Precedence, shipped as text, because a reminder is not an instruction:** - -> **The derived floor controls whether the cycle may close. The hook's ratio is a reminder -> threshold and controls nothing.** Where the derived floor and the ordinary closure rules are -> satisfied, a below-threshold reminder is **noted in the pass report and disregarded.** - -**The first eligible floor-1 cycle is P8's first checkpoint.** The story cannot demonstrate floor 1 -on this branch — it is risk `high` — so the demonstration is deferred: **the first post-merge cycle -whose cited set licenses floor 1 must carry the floor-1 provenance line, and the P8 measurement -reads it.** That is what makes the reduced floor observable at all, and it is why the provenance -grammar is pinned here rather than downstream. - -**Named residual:** the hook's messages state its own threshold as an obligation — "MUST reach a -minimum" (`codex-gate.sh:933`), the Gate-B floor line (`:947`) and the Gate-A floor line (`:967`), -each rendering a ratio against `$floor` — all of which at level 0 report a shortfall against a -number the cycle does not owe. **Hook text is out -of scope by decision**, so this is disclosed, not fixed; what makes it tolerable is the precedence -rule plus invariant 1. - -**Why the earlier marker mechanism was deleted rather than repaired.** It had the agent write the -knob plus a marker distinguishing agent writes from user writes; Gate-A pass 3 returned sixteen -Majors against it and one that settled it — **the cheapest bypass was marker-specific**, so a -protection made the attack indistinguishable from the protected case. **What deletion does not -remove:** anyone can still write the gitignored knob and quiet the hook. What it removes is this -design's reliance on writing it, and with that the ambiguity — a floor file is now **always** a -user artifact. - -**The provenance line's grammar is pinned here, not in the plan.** It is a **durable interface a -later reader consumes** — story criterion 3 requires one form and says the spec states which — so -unlike a replacement wording it cannot be settled downstream without leaving P8's input to the -plan. Revision 10 moved it and was wrong to; that is the one place the slimming crossed from -detail into contract, and it is the reason the restructuring guard exists. - -``` -cycle ; floor per ; hook reminder threshold - - := [a-z0-9]{8,16} the cycle nonce (§1.1) - := the derived floor - := "none" the artifact cites no story - | "{" ("," )* "}" - := " (level " <0|1|2> ")" a profiled story - | " (unprofiled)" a cited story with no profile - := | - := "absent" | | "unusable(" ")" - := "unreadable" | "empty" | "non-numeric" | "out-of-range" -``` - -A `` is repo-relative and contains none of `,` `{` `}` `;` `"` or whitespace; any other -path is a `` — double-quoted, with `\` and `"` backslash-escaped and no other escape -recognized. **The nonce leads every pinned form**, which is what lets §1.1's rule that it appears -in *every* record be something the formats can satisfy. - -One instance per variant, all parsing under the grammar above: - -``` -cycle k7m2q9xa; floor 1 per {docs/superpowers/stories/A-story.md (level 0)}; hook reminder threshold absent -cycle k7m2q9xa; floor 3 per {A-story.md (level 0), B-story.md (level 2)}; hook reminder threshold 3 -cycle k7m2q9xa; floor 3 per {A-story.md (level 0), C-story.md (unprofiled)}; hook reminder threshold 1 -cycle k7m2q9xa; floor 3 per none; hook reminder threshold unusable(non-numeric) -cycle k7m2q9xa; floor 3 per {"docs/stories/odd, name.md" (level 2)}; hook reminder threshold absent -``` - -**Everything that quotes this line elsewhere quotes an instance of the grammar.** A shorthand in a -story criterion, a commit body or a report is either a valid instance or it is wrong — there is no -informal variant, because the only reader that matters parses rather than reads. **The same reasoning fixes the curve's form in §5.1**: both are -read by something other than a human. - -**The residual disclosure this implies ships in both copies**, in the words the story requires: -that the floor a cycle owes is **produced by the agent**, that **nothing checks it** against the -cited profiles, and by what routes it can therefore be wrong (§10). A copy carrying the grammar -without the disclosure would present a parseable number as a verified one. - -**Required properties the grammar exists to satisfy:** -- **Every cycle records it**, default or not, so an absent line is never ambiguous between "the - default applied" and "someone forgot". -- It states **one floor and the cited set that produced it, with each member's level as a - numeral** — not an entry per story, since unanimity makes the floor a property of the set. -- It distinguishes a **cited story with no profile** from **no story cited**. -- The **knob is recorded whenever the file exists**, including when present but unusable, since - the hook ignores such a value and the line describes what the hook will do. **The unusable cases - are distinguished rather than merged**: unreadable, empty, non-numeric and out-of-range need - different fixes — a permission problem is not a typo — and a single `unusable` token would tell a - reader only that something is wrong, which prompt-standards item 10 treats as naming a symptom - instead of a cause. -- It is **machine-extractable**, because the deferred P8 measurement reads it, and **one form - covers every case** — a second pinned form for a special case is what made earlier revisions - unparseable. - -### 3.2 A profile that moves mid-cycle - -Three existing rules compose; no new rule. The floor derives from the **current** profile at each -pass; **passes already run keep counting**; **closing requires the floor as currently derived.** +## 4. The per-pass curve — required properties -**The cited *set* can move too, not only a profile's values** — a story added, removed or -corrected mid-cycle — and the same three rules cover it: the set is re-read at each pass, the -floor is re-derived from it under unanimity, and closure requires the floor the current set -yields. **Adding or removing a story changes the derived floor only if it changes the -unanimity verdict** — adding a level-0 story to an all-level-0 set moves nothing, and removing one -non-zero story from a set with two leaves the floor at 3. Where the verdict does move, **upward is -a raise** and carries the one-further-pass consequence; **downward lowers the floor and releases -nothing else**: an accepted in-set -Blocker or Major stays in the set and must still resolve, because it entered by the user's answer -and not by the story that first raised it. A set change moves the floor; it is not a route to -discharge findings. - -**The consequence that must be stated:** a **raise costs at least one further pass regardless of -the arithmetic**, because §5 already requires the final clean pass to run under the current -profile — so even a raise leaving the floor unchanged costs a pass. **A lowering** drops the floor, -the lens sets and the evidence mode together, so a `high` cycle lowered to `trivial` can close on -**one further pass after the lowering**. That is the pre-existing profile-change path, -human-confirmed and logged; the variable floor rides it and does not create it. +Each of the three cycles records its own per-pass finding and Blocker counts in its own commit +body, labelled with the cycle it describes. **Gate B alone would leave the dominant cost +unmeasured** — the loops this story cites as evidence are Gate-A loops. The plan fixes the format; +these are the contract: ---- - -## 4. The per-pass curve - -**Each of the three loops records its own per-pass finding and Blocker counts in its own commit -body**, labelled with the loop it describes. **Gate B alone would leave the dominant cost -unmeasured** — the loops this story cites as evidence are Gate-A loops. - -**The form is pinned here**, for the same reason as the provenance line: P8 reads it, so leaving -it to the plan would leave a durable interface undecided. - -``` -cycle ; (passes , ): Findings . Blockers . - - := [a-z0-9]{8,16} the cycle nonce (§1.1) - := "Gate-A spec loop" | "Gate-A plan loop" | "Gate B" - := ("," )* strictly ascending, non-overlapping - :=

|

"-"

the second greater than the first -

:= [1-9][0-9]* a pass number - := ("," )* one per pass in , same order - := 0 | [1-9][0-9]* a finding or Blocker count - := | ("; " )* - := "pass "

" " ("+" )* - := [^ ;:,()]+ | "undetermined" verbatim as the call reported it -``` - -`` has exactly as many entries as `` enumerates, so a reader can map each number to -its pass without inference. `` is deliberately permissive about punctuation because -provider-qualified identifiers like `moonshotai/kimi-k3` are already in this repo's records; what -it excludes is the grammar's own delimiters. - -One bare `` means every covered pass ran under it. `` entries are -semicolon-separated and must cover **every** pass the `` names; a pass assembled from calls -under different models joins them with `+`. **A model that cannot be determined is written -`undetermined`**, never omitted and never guessed — `docs/coding-workflow.md` already requires -recording it that way where neither config level names one. - -A skipped loop writes `cycle ; : skipped (see skip reason)` and no counts — the -nonce leads **every** form, skipped included, or a skip cannot be attributed to the cycle that -took it. - -**Required properties the form exists to satisfy:** -- One entry per **valid** pass, in pass order; **incomplete passes are excluded**, and because - they consume pass numbers the record **states which pass numbers it covers**. A valid - zero-finding pass is recorded as zero, never omitted. -- A `full` Gate-B pass, and separate `spec`/`quality` calls, and a single-branch recovery, are +- Carries that cycle's **nonce**, so a curve can be attributed to the cycle that produced it. +- **One entry per valid pass**, and because incomplete passes are excluded and consume pass + numbers, the record **states which pass numbers it covers**. A valid zero-finding pass is + recorded as zero, never omitted. +- A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are **branches of one logical pass** contributing one summed entry. **The curve counts logical passes; the hook counts calls**, and where they differ the body says so. -- **Both branches of one logical pass must have reviewed the same artifact revision**, identified - by the tracked reviewed commit. If it changed between them they are not one pass: the completed - branch is an incomplete pass and the second begins a new one. -- **The model each pass ran under is recorded**, per the existing convention at - `docs/coding-workflow.md:261-266`, with each contributing model listed where a pass was - assembled from calls under different models. -- A **legitimately skipped** loop records the skip rather than a silent gap. - -**What it reaches.** Durable **across cycles** — surviving a fresh checkout, a cleared `.context/`, -another machine. **Not within a running loop**: the commit does not exist until the loop closes, so -nothing here fills that gap. And it is **author-written and unchecked** — -nothing compares it against the validated pass files, so **P8 reads a self-reported curve** and the -text says so rather than letting it be treated as measurement. +- **Both branches of one logical pass must have reviewed the same artifact revision.** If it + changed between them they are not one pass. +- **The model each pass ran under is recorded**, per the existing convention in + `docs/coding-workflow.md`, written `undetermined` where it cannot be determined rather than + guessed, and admitting provider-qualified identifiers. +- A **legitimately skipped** cycle records the skip rather than a silent gap. + +**What it is worth, stated rather than implied.** Durable **across cycles**. **Not within a running +cycle** — the commit does not exist until the cycle closes. And **author-written and unchecked**: +nothing compares it against the validated pass files, so **P8 reads a self-reported curve** and +must not present it as measurement. **Squash carry.** §5's rule names only evidence entries and human-exception records; the -**provenance line, these curves and a skipped loop's skip record** are added, in both copies. -(The successor story adds the decline record to the same list; the two changes touch one passage -and the second must not drop what the first added.) A skip record that does not survive the squash leaves an unexplained gap in exactly the -history P8 reads. +**provenance lines, the curves and a skipped cycle's skip record** are added. *(The successor story +adds the decline record to the same passage; extending is required, replacing would unship these.)* --- -## 5. Old-conditions accounting - -Required by the AGENTS.md Don't and story criterion 6. - -**Method — the stable half, and it stays here.** For each passage: list what its existing prose -requires, then mark each requirement **kept**, **moved**, or **deliberately dropped**. A -requirement neither kept nor explicitly dropped is a dropped condition. **No passage is rewritten -without its accounting.** - -**Where the dispositions are produced and gated.** In one artifact, -`docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md`, written **once against -the frozen final text** and **reviewed before any replacement text is written**. The gate is a -gate, so it has the parts a gate has: it is **a Gate-A review of that artifact** under this -story's profile, its **acceptance condition is a clean pass at the derived floor** like any other, -**it owes its own labelled curve** in the commit that carries the artifact — a gate exempt from the -rules it enforces would be the gate-off path in miniature — -and **failure means no replacement text is written** rather than a note and a continuation. -**A finding that changes the passage list, a disposition or the spec itself feeds back rather than -being absorbed**: the artifact is regenerated against the corrected text and re-reviewed, and where -the finding changes *this spec*, the spec's own Gate A reopens on the changed rule. The artifact is -downstream of the spec, so it cannot silently amend it. The -plan names where in its sequence that falls. - -**The pass-2 tension, recorded rather than resolved by hindsight.** Gate-A pass 2 raised a Blocker -demanding the inventory live *in this spec* rather than being deferred to the plan — correct, since -deferring what constitutes compliance while claiming compliance is the failure the Don't describes. -**The frozen-text artifact is a third option neither pass had**, and it preserves what pass 2 -required: the accounting exists and is reviewed *before the replacement is approved*. What moved is -when it is produced. - -**The price of the split, stated.** Once dispositions are deferred, **the passage list is the sole -guard against a dropped condition**, and the conditions artifact cannot catch what the list never -named. Rows 20 and 21 were missing until pass 8 because revision 8 added rules rewriting them -without extending the list. **The list is re-checked whenever the design adds a rule** — pass 10 added rows 22 and 23 for exactly that reason, the nonce rule having reached records the list never named —, and the -plan's gate reads it against the final text rather than trusting it. - -### 5.1 Floor-wording sites are generated, not hand-derived - -Hand-derived three times, three different counts. The inventory is the output of a stated command: - -``` -grep -nE "min 3 passes|below 3|3-pass|3 passes|where the 3 come from|3-passes-per-gate" \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -``` - -**Six sites per copy, symmetric — twelve, all changing.** Two limitations, stated because a -generated list reads as complete: **it finds digit sites only**, and the site that matters most -spells none — `:126/:322`, "*a Blocker/Major-free **pass 1** carrying a Minor keeps looping*", -correct under floor 3 and **false under floor 1** where pass 1 is *at* the floor. **Fourteen sites -in total, all changing.** And **it is a floor, not coverage**: another digit-free formulation would -escape it. It correctly does not match `:249/:434`, which cites a historical pass rather than -stating a rule. - -### 5.2 The passages this change rewrites - -Each present exactly once in both copies. **Eighteen; the conditions artifact is incomplete without -all of them.** - -**A passage both changes rewrite appears in both accountings**, each covering its own change. That -is not duplication: the AGENTS.md Don't asks what *this* change does to a passage's conditions, and -two changes to one passage owe two answers. Getting this wrong is how a condition is dropped — -Gate-A pass 12 found six passages assigned wholly to the successor that **this** change also -rewrites, including the clearly-stuck paragraph, whose "pass 1 carrying a Minor" sentence §5.1 -identifies as false under floor 1. Assigning it away would have dropped the floor change's own -condition into a story whose scope excludes the floor. - -| # | Passage | Rewritten by | -|---|---|---| -| 1 | "Both gates are a LOOP with a HARD FLOOR" | part 1 — the floor statement | -| 2 | The early-exit sentence (`:79/:279`) | part 1 — the zero-finding exit | -| 3 | The incomplete-pass rule (`:236/:421`) | part 1 | -| 4 | "Lenses are different questions…" (`:403/:582`) | part 1 | -| 5 | "The Gate-B triviality skip…" | part 1 — adjacent relaxation, checked for consistency | -| 6 | "A cycle citing several stories" | part 1 — the floor dimension under unanimity | -| 7 | "Gate A — Spec, then plan…" (`:300/:485`) | part 1 | -| 8 | "Gate B — Code" (`:335/:519`) | part 1 | -| 9 | "**Severity:** Blocker … Nit" | part 2 | -| 10 | "Changing a profile" | §4.2 | -| 11 | The findings-slot naming paragraph | §5 — the grammar gains an optional per-cycle infix | -| 12 | "Before each call, delete every target file…" (`:199/:386`) | §5 — the infix, and a **refuse-on-collision** case where the target belongs to another cycle | -| 13 | "Recognizing \"clearly stuck\"" | **part 1** — its "pass 1 carrying a Minor" sentence is false under floor 1 (§5.1). *The successor also rewrites this passage for the ordering; both accountings owe it.* | -| 14 | "Recording a human exception" | §1.1 — the nonce appears in every cycle record, this one included | -| 15 | "The evidence entry lives in the commit body" | §1.1 — same | -| 16 | "Optional companions, from field practice" | §1.1 — the nonce, plus the working record's role and the collision rule; §5's optional-companion *status* is unchanged | -| 17 | "On squash-merge, copy every evidence entry…" | §4 — the provenance line, curves and skip records added to the carry. *The successor adds the decline record; both accountings owe it.* | -| 18 | The `baseSha`/WIP/closing-amend block (`:500-517`, template `:679-696`) | §4 — the closing body must carry the provenance line and curve. *The successor adds the WIP-amend properties; both accountings owe it.* | - -**Row 18's change is a replacement, not an addition beside it.** Today's grammar admits three exact -names; an infixed name satisfies none. The shipped text replaces each with one admitting an -optional per-cycle infix, so there is **one rule** and bare slots stay valid — with the infix -**required whenever more than one cycle could write that slot** — which includes a bare slot -already occupied *and* the case two new cycles start concurrently, where neither finds an occupied -slot and both would take the bare name. In practice: **a cycle that has a nonce uses it**, so the -bare form is reserved for the single-cycle case it already serves. Plus a -**refuse-rather-than-overwrite** rule when the target belongs to another cycle, and **uniqueness** -from the §1.1 nonce. +## 5. The cycle nonce + +Both shipped records carry it, and a record that cannot be attributed to a cycle is unusable by the +measurement that reads it. **§5 defines three cycles — the Gate-A spec loop, the Gate-A plan loop +and the Gate-B cycle — so a run of all three produces three nonces, not one.** + +- Generated once at cycle start, immutable, and **collision-resistant operationally: at least 8 + characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — never derived from a + name, a timestamp or a commit, each of which collides exactly where sibling cycles do. +- Constrained so it is **safe as a slot infix and a path component**. +- **It appears in every record the cycle writes.** +- **Recovery has two sources**, because a Gate-A cycle's commit does not exist while it runs: the + advisory working record during the cycle, and history at its commit. Recovering from **either** + keeps identity. **Neither, or sources that disagree, or more than one candidate → no identity, + start a new cycle** — which costs passes rather than letting one cycle's records read as + another's. +- **The advisory working record is a cycle record too**: it carries the nonce, and §5's per-cycle + infix and refuse-on-collision rules apply to it. §5's optional-companion status is unchanged. --- -## 6. Prerequisite, rollout, and what this change falsifies +## 6. Old-conditions accounting + +**Method.** For each passage this change rewrites: list what its existing prose requires, then mark +each requirement **kept**, **moved**, or **deliberately dropped**. A requirement neither kept nor +explicitly dropped is a dropped condition. **No passage is rewritten without its accounting.** + +**A passage that both this change and the successor rewrite appears in both accountings**, each +covering its own change — two changes to one passage owe two answers. + +**Where it is produced and gated.** In one artifact, written **once against the frozen final text** +and **reviewed before any replacement text is written**, as a Gate-A review under this story's +profile with a clean pass as its acceptance condition and no replacement text on failure. A finding +there **feeds back** rather than being absorbed. **The plan carries the passage list and executes +this method against it** — and because the dispositions are deferred, **that list is the sole guard +against a dropped condition**, so it is re-checked whenever the design adds a rule. -**The template lacks the sentence §3's kinship points at** — "a wrong sentence costs a confused -reader, not broken behaviour" is in `CLAUDE.md` and absent from the template. **The template gets -it**, a deliberate one-seam reduction of the 192-line divergence because the new rule depends on -it, not a general reconciliation. +**The two copies are accounted for separately** where their text differs, since they already +diverge substantially and one accounting cannot cover both. -**Statements this change falsifies**, from the standing lens — a change makes sentences wrong in -files it never touches: +--- + +## 7. Rollout, and what this change falsifies -| Site | Why it is false after | -|---|---| -| `README.md:130` | the §5 floor is derived and not 3; the knob moves the **hook's reminder threshold**, which is all it moved | -| `docs/getting-started.md:34, :40, :53` | three-pass minimums, not true at level 0 | -| `:44-45` | says the hook reports when the Gate-A floor is unmet — it reports against **its own** threshold, so at level 0 it fires when nothing is owed | -| `:58-59` | waits for `✓ … (3/3 …)` before the closing amend; at level 0 that message never comes | -| `:86` | "moves the 3-pass floor" | -| `docs/coding-workflow.md:79-80` | "Gate A's floor and the baseline questions are the same at every level" — **directly contradicted**, the same claim as `:84` in a second file | +**The template lacks the sentence §3's kinship points at** — the prose-exemption rationale is in +`CLAUDE.md` and absent from the template. **The template gets it**: a one-seam reduction of the +divergence, because the new rule depends on it, not a general reconciliation. -**The rewording is honest, not cosmetic**: README and getting-started describe the knob as what it -mechanically is, and name the **sanctioned lever** for wanting fewer obliged passes — the profile, -or `codex-gate.off` for reminders. Nothing is deprecated. +**Statements this change falsifies must be corrected in the same change** — in `README.md`, +`docs/getting-started.md` and `docs/coding-workflow.md`, wherever they assert a fixed three-pass +floor, describe the knob as moving the §5 floor, say the hook reports an unmet Gate-A floor, or +wait for a `3/3` message before closing. The plan carries the site list. **The rewording describes +the knob as what it mechanically is** and names the sanctioned lever for fewer obliged passes — the +profile, or `codex-gate.off` for reminders. Nothing is deprecated. **Gate-B classification of the doc edits.** §5's exemption requires **every** staged path to be explanatory documentation, and **a mixed commit forfeits it**. So the doc edits either land in -their own docs-only commit (N/A applies) or ride with the prompt change (full Gate B applies to the -whole commit). **The plan chooses and states which.** +their own docs-only commit or ride with the prompt change and take full Gate B. **The plan chooses +and states which.** **What the template edit reaches.** It changes what `/workflow-init` writes into **new or re-initialized** projects. It does **not** update a downstream project's existing `CLAUDE.md` — invariant 9 forbids silent overwriting. Adoption is by re-running the scaffolder. -**Packaging.** Editing `plugins/dev-workflow/commands/workflow-init.md` triggers invariant 12: a -`plugin.json` version bump and a `CHANGELOG.md` entry are in the implementation surface. +**Packaging.** Editing the template triggers invariant 12: a `plugin.json` version bump and a +`CHANGELOG.md` entry are in the implementation surface. --- -## 7. Evidence plan +## 8. Evidence plan -Mode `battery+check+verification` (no `+abuse-path`). +Mode `battery+check+verification` (no `+abuse-path`; security is `none`). - **Battery** — the full `AGENTS.md` § Commands chain, green. - **A check that fails without the change.** No automated test is possible for prose, so this takes - §5's other permitted route — a **named verification** owing the same counterfactual. Subject: - §5.1's site inventory, differential by construction — **posed as a question about behaviour under - floor 1, the pre-change text answers wrongly at identified sites** (`:79` states the exit as - "below 3"; `:126` says a Blocker/Major-free pass 1 carrying a Minor keeps looping, where floor 1 - requires it to close) **while the post-change text answers correctly.** **Both revisions get - read** — a verification consulting only the post-change text cannot fail and would report success - because of how it was wired. -- **A named verification of the risk path.** The risk path is that **the floor is derived by the - agent and nothing mechanical checks the derivation**. It recomputes each provenance line's floor - from the cited stories' headers; the observation that would exist if the claim were false is a - line whose number the profiles do not license. It also confirms **no floor file is present at - close where none was present at start** — which detects a *persisting* write and **cannot** detect - a transient one, stated rather than implied. + §5's other permitted route — a **named verification** owing the same counterfactual. Posed as a + question about behaviour under floor 1, **the pre-change text answers wrongly at identified + sites** and the post-change text answers correctly. **Both revisions get read**: a verification + consulting only the post-change text cannot fail and would report success because of how it was + wired. +- **A named verification of the risk path** — that the floor is derived by the agent and nothing + mechanical checks it. It recomputes each provenance line's floor from the cited stories' headers; + the observation that would exist if the claim were false is a line whose number the profiles do + not license. It also confirms **no floor file is present at close where none was present at + start**, which detects a *persisting* write and **cannot** detect a transient one. - **A conditional verification that a user's knob survives untouched** — byte-identical across a - cycle where one exists; **recorded not-applicable with its reason where none does.** An agent must - not create one to make a check runnable, which would be a fixture supplying its own input. -- **Parity across every changed rule**, per story criterion 7 — §5.1's fourteen sites, §5.2's eighteen - passages, and every rule §§1.1–4 insert — the nonce's generation, recovery, record and - collision duties included — plus §9's residual disclosure and §3.1's - provenance properties, which the story requires in **both** copies. The copies already differ on - 192 lines, so parity cannot be asserted from a whole-section diff. -- **A fresh twelve-item `docs/prompt-standards.md` pass** over every changed prompt region. **Item - 7 is read against the whole resulting prompt**, not only changed regions — a contradiction is a - relation between an edited passage and an unedited one. - -**When produced and revalidated.** §5 requires revalidation before every re-review and before the -closing amend. Verifications reading closing commits are produced against the `WIP:` snapshot and -**re-read against the content the close will carry** — the amend *is* the closing act, so re-reading -"the final commit" is circular. **What that establishes is bounded**: the index can change between -the read and the commit. - -**Instrument discipline, from this story's own evidence.** Two `fic2` defects were properties of -the technique: **a state's inputs must include every input the rule reads**, and **a counterfactual -must distinguish ABSENT from CONTRADICTORY**. Both survived a full clean pass before being caught. + cycle where one exists; **recorded not-applicable with its reason where none does**, since + creating one would be a fixture supplying its own input. +- **Parity across every changed rule**, in both copies, since they already diverge and parity + cannot be asserted from a whole-section diff. +- **A fresh twelve-item `docs/prompt-standards.md` pass** over every changed prompt region, with + **item 7 read against the whole resulting prompt** — a contradiction is a relation between an + edited passage and an unedited one. + +**Revalidation.** §5 requires it before every re-review and before the closing amend. Verifications +reading closing commits are produced against the `WIP:` snapshot and **re-read against the content +the close will carry** — the amend *is* the closing act. **What that establishes is bounded**: the +index can change between the read and the commit. --- -## 8. Out of scope +## 9. Out of scope -Hook code (anything under `plugins/dev-workflow/hooks/`, including the reminder's own wording); -gate-call observability (upstream); the pass-counter anomaly; the CodeRabbit plan-metadata -contradiction; the fixture-per-predicate question; any remedy to the supersession convention; -deprecating or repurposing the user-facing floor knob; teaching the hook about profiles; and -general reconciliation of the 192-line divergence beyond §7's one seam. +Hook code, including the reminder's own wording; gate-call observability; the pass-counter anomaly; +the CodeRabbit plan-metadata contradiction; the fixture-per-predicate question; any remedy to the +supersession convention; deprecating or repurposing the user-facing floor knob; teaching the hook +about profiles; the §5 loop-rule consolidation and everything its successor story owns; and general +reconciliation of the two copies' divergence beyond §7's one seam. --- -## 9. Risks and activation - -**Everything in this section that is a rule rather than a note appears in both shipped copies**, -per the story's activation criterion: when the rules bind, the unknown-start fallback with its -named parts, and the partial-adoption consequence. The risks that are *observations about this -design* rather than instructions to a future agent stay here. - -- **When these rules bind.** From the commit that ships them, and **a loop already running finishes - under the rules it started with** — re-deriving a floor mid-loop from a rule that did not exist - when passes were banked would invalidate a count nobody could reconstruct. **Where a loop's starting rules cannot be established it takes the stricter reading of every part - this change touches**, named rather than left to interpretation: **floor 3**; **severity - classified without the demotion**, so nothing is collected that would otherwise iterate; **every - and **the curve duty treated as owed**. **That list covers this change's rules; it is not a list of everything a - cycle owes.** The parts this change touches are the floor, severity, the curve, and the cycle - nonce with the record and collision duties that follow from it — and each is treated at its - strictest: the nonce is required, recovery ambiguity resolves to a new cycle, and the - collision-refusal rule applies. A part not named here is a part this change did not touch. (The successor story extends this fallback to the loop rules - it ships; extending a list is safe where replacing it would not be.) **A user knob set above 3 - is not lowered by the fallback**: the knob moves the hook's reminder threshold while the fallback - sets the *obliged* floor, so a workspace asking for louder reminders keeps them. Not a - re-derivation, which could hand a level-0 loop a - floor of 1 and *skip* passes on the strength of not knowing when it started. -- **Downstream has no shipping commit.** The template travels into repositories whose history does - not contain this change, so adoption binds from the `/workflow-init` run that **actually writes** - the text — which invariant 9 permits to write nothing, be declined, or be merged in part. **The - rules bind only over the text a project's `CLAUDE.md` contains**, and a partial adoption can - persist undetected. **This is in tension with §1's coupling argument and the tension is real**: a - project taking the floor rule without the severity test gets a floor whose `docs-only` question - §1 says the severity test settles. - What prompt text can do is done; what it cannot is said. +## 10. Risks and activation + +- **When these rules bind.** From the commit that ships them, and **a cycle already running + finishes under the rules it started with**. **Where a cycle's starting rules cannot be + established it takes the stricter reading of every part this change touches** — floor 3, severity + classified without the demotion, the curve duty owed, and the nonce duties at their strictest. Not + a re-derivation, which could hand a level-0 cycle a floor of 1 and *skip* passes on the strength + of not knowing when it started. A user knob set above 3 is not lowered by this fallback. *(The + successor extends this list to the rules it ships; extending is safe, replacing is not.)* +- **A revert is itself a shipping commit for the old rules.** **The activation rule wins where the + start is determinable**; the fallback covers only where it is not. Without that precedence a + revert makes every in-flight cycle ambiguous. +- **Downstream has no shipping commit.** Adoption binds from the `/workflow-init` run that + **actually writes** the text, which invariant 9 permits to write nothing, be declined, or be + merged in part. **The rules bind only over the text a project's `CLAUDE.md` contains**, and a + partial adoption can persist undetected. **This is in tension with §1's coupling argument and the + tension is real**: a project taking the floor rule without the severity test gets a floor whose + `docs-only` question §1 says the severity test settles. What prompt text can do is done; what it + cannot is said. - **The gate-off surface — routes known today, not a complete list**, since an enumeration read as - complete guarantees what it omits. **One route is created here and is named as such**: a stated - floor the cited set does not license, which could not exist before there was a derived floor to - state. Pre-existing and unchanged: omitting a higher-risk cited story, minting or editing a profile to level 0, + complete guarantees what it omits. **One route is created here**: a stated floor the cited set + does not license, which could not exist before there was a derived floor to state. Pre-existing + and unchanged: omitting a higher-risk cited story, minting or editing a profile to level 0, presenting an incomplete set, falsifying evidence entries, silencing reminders, or not running a - pass and reporting that it ran. **None of this is a guard**, and the - profile-minting path is bounded only by §5's existing human-confirmation rule. -- **Rollback, once a rule has been adopted.** In this repo a bad rule is reverted like any other - commit — **and the revert is itself a shipping commit for the *old* rules.** A loop in flight across it - therefore has two answers available and they disagree: the activation rule says a loop finishes - under the rules it started with, while the fallback says an indeterminate start takes the - stricter reading. **The activation rule wins where the start is determinable** — a loop whose - own first pass artifact predates the revert finishes under the rules it began with, which is - what that rule is for. The fallback applies only where the start cannot be established. Stating - the precedence is the point; without it a revert makes every in-flight loop ambiguous. **Downstream there is no - revert**: a project's `CLAUDE.md` is its own file, so withdrawing a rule means shipping a - corrected template and waiting for each project to re-run the scaffolder and accept the diff — - the same partial-adoption path, with the same absence of detection. **A rule that turns out - wrong is therefore cheap to stop shipping and slow to un-ship**, which is an argument for the - gates rather than a gap this design can close. -- **A user-set floor is not the gate-off lever**, and the text keeps them distinct: it moves what - the hook says; the lever is a *stated* floor the profiles do not license. -- **The reachability test needs judgement** where §5 is trying to remove it; the phrasing removes - arbitrariness, not judgement, and §3 says so. + pass and reporting that it ran. **None of this is a guard.** +- **A user-set floor is not the gate-off lever**: it moves what the hook says. The lever is a + *stated* floor the profiles do not license. +- **The reachability test needs judgement** where §5 is trying to remove it; §3 says so. - **The curve is self-reported**; P8 inherits that limit. - **The expected demotion is a prediction**; P8 measures it. If it demotes far less than hoped, the rule is still correct and the economics claim was what was wrong. - ---- - -## 10. What this spec keeps and what moved out - -**Revision 13 reduced this spec to parts 1 and 2.** Part 3 — the §5 loop-rule consolidation — moved -to `docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md` with every decision the -parent cycle bought, carried there as settled inputs rather than reopened. - -**Why, in numbers.** Eleven Gate-A passes never brought Blocker/Major below 22. Attributed over the -last three: parts 1 and 2 produced **4/1, 8/1 and 7/1**; the loop-rule sections produced **10, 11 -and 14**, rising, with each repair creating an interaction the next pass found. Two of pass 11's -Blockers were generated by the immediately preceding repair, one of them a deadlock fix that -deadlocked one level up. - -**Moved out with part 3:** the exits-and-duties analysis and the precedence table; the hold, the -decline and its record; Q6's unavailable-history answer; and the eleven §5 passages only those rules -rewrite — the loop-absorb paragraph, the clearly-stuck and surfacing paragraphs, the pass-4 report, -the two-rules paragraph, the human-exception and scope-narrow blocks, and the `baseSha`/WIP/amend -block. The successor's accounting owes those. - -**Kept here, and the test that decided each:** *does anything but a person parse it, and does a -shipped criterion read it?* The **provenance line** and the **per-pass curve** both pass — P8 reads -them — so their grammars stay pinned rather than descending to the plan. The **cycle nonce** stays -because both records carry it; that also settles the successor story's open question about where the -nonce lives. The floor predicate, unanimity, the hook-precedence rule, the severity test with its -exclusions, the accounting method and passage list, the falsified-statement inventory, the evidence -plan, scope and the risks all stay. - -**Earlier revisions moved detail to the plan** — exact replacement wordings, the scaffolder's edit -procedures, the per-site texts for §5.1's fourteen sites and §5.2's eighteen passages, and the -conditions artifact's production procedure. **Two of those moves were wrong and were reversed**: -the provenance grammar and the curve form, which pass 9 caught. **A restructuring guard that asks -only "did a decision move?" misses the case where a rule survives in outline and loses its force** — -pass 9 found eight of those in one revision. That sentence is the most transferable thing this -cycle produced. - -**Nothing here waits on the successor.** The floor and severity rules bind on their own, which is -what made the split available rather than merely desirable. From 770ce63a08cb03c634e5b34f110acd1054b6d5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:50:10 +0200 Subject: [PATCH 031/117] =?UTF-8?q?docs:=20revision=2016=20=E2=80=94=20pas?= =?UTF-8?q?s=2014's=20twelve=20Majors;=20ZERO=20Blockers=20for=20the=20fir?= =?UTF-8?q?st=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 14: 18 findings, 0 BLOCKER, 12 Blocker/Major -- the best pass of the loop by a wide margin, and the first with no Blocker since pass 5. Zero tells. Blocker/Major across fourteen passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12. The slim prediction was "under 10 B/M"; 12 is close, and far from the >=20 that would have meant the artifact class rather than its size was the problem. The finding I have now made twice: revision 15 moved both pinned grammars to the plan, and pass 9 had ALREADY settled that they stay -- the parent story requires one form and says this spec states it, and P8 parses it. Both are back, with the test that settles such cases recorded beside them: does anything but a person parse it. Slimming is not a licence to re-answer a question a prior pass closed. Also restored as properties rather than formats: revision identity for the two-branch pass rule (which thing is compared is a decision, only its encoding is a format), and the slot rules the working-record sentence had been referring to as if §5 already contained them -- it does not, which made that reference point at nothing. A cycle with a nonce uses it in every shared slot; a target another cycle owns is refused rather than overwritten. Those exist because a bare slot WAS overwritten during this cycle, destroying a previous cycle's findings file. Unanimity is now scoped on all four conditions -- non-empty, profiled, resolvable, level 0 -- because "every cited story" is vacuously true of an empty set, and because an unresolvable member stops and surfaces rather than yielding 3. Cited-set changes: the final clean pass must run against the current set WHENEVER MEMBERSHIP CHANGES, not only when the number moves -- adding a high-risk story to a set already at 3 leaves the floor alone while adding that story's lenses, evidence and review scope, which an earlier pass did not cover. And removing a story recomputes obligations from the current set but NEVER discharges an accepted in-set Blocker or Major, because the user's acceptance put it in the fix set, not the citation. The nonce gained what it lacked: what makes a value a CANDIDATE for recovery (this cycle's type, this cycle's artifact, still open), retirement of the working record at closure so it cannot become one later, and terminal behaviour when generation fails -- bounded retry, then stop and surface, with no deterministic fallback, since a derived value collides exactly where sibling cycles do. The conditions artifact's review is now part of the Gate-A plan cycle rather than a fourth cycle, which the three-cycle topology would otherwise contradict. And the evidence plan now requires this branch's own three closing bodies to be reconstructed into the pinned forms -- two story criteria require them to demonstrate the provenance line and the curve, and the spec and plan cycles here closed before those forms existed. A demonstration the branch does not contain is not a demonstration. Story: criterion 1 no longer calls the three cycles loops of one cycle, and explicitly fails a copy giving the shared-cycle reason, which is false. New criterion for the nonce, which nothing observed. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-14. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12. Blockers 5,2,0,4,0,3,9,4,2,2,2,5,9,0. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 119 ++++++++++++++---- ...-review-loop-economics-pass-floor-story.md | 16 ++- 2 files changed, 110 insertions(+), 25 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 643e846..a1223af 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 15 (rules only) · **Gate-A passes 1-13** +**Date:** 2026-08-29 · **Revision:** 16 (rules only) · **Gate-A passes 1-14** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -44,8 +44,11 @@ and surface the cause**, and that rule stands unchanged — the predicate applie that resolve, and to artifacts citing no story. Reading an unresolvable profile as 3 would convert an existing stop condition into a silent default. -**Unanimity across a cited set.** Floor 1 **if and only if every cited story is profiled and every -one is at level 0**. Any other set yields 3. This follows §5's own precedent — "skip-eligible only +**Unanimity across a cited set.** Floor 1 **if and only if the cited set is non-empty and every +member is profiled, resolvable, and at level 0** — all four conditions, because "every cited story" +is vacuously true of an empty set. **No story cited, or any cited story unprofiled, yields 3. Any +cited story whose profile is present but unresolvable stops and surfaces**, which is the §5 rule +above and not a floor of 3. This follows §5's own precedent — "skip-eligible only if **every** cited story is" — and is the only reading consistent with invariant 2's firing direction. @@ -82,8 +85,26 @@ passes are still being spent, which is the only time checking it is cheap. ### 2.3 The provenance line — required properties -One line per cycle, in its closing commit body. The plan fixes the grammar; these properties are -the contract: +One line per cycle, in its closing commit body. **The grammar is pinned here, not in the plan** — +the parent story requires one form and says this spec states it, and P8 parses it. (Revision 15 +moved it out and was wrong to; pass 9 had already settled this, and the test it settled on is +whether anything but a person parses it.) + +``` +cycle ; floor per ; hook reminder threshold + + := [a-z0-9]{8,16} + := [1-9][0-9]* + := "none" | "{" ("," )* "}" + := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" + := | bare excludes , { } ; " and whitespace; + quoted is double-quoted with \ and " escaped + := "absent" | [1-9][0-9]* | "unusable(" ")" + := "unreadable" | "empty" | "non-numeric" | "out-of-range" +``` + +Everything that quotes this line elsewhere quotes an instance of it; there is no informal variant. +The properties the grammar exists to satisfy: - **Every cycle records it**, default floor or not, so an absent line is never ambiguous between "the default applied" and "someone forgot". **Three cycles means three lines**, one per cycle. @@ -108,9 +129,16 @@ unchanged costs a pass. **A lowering** drops the floor, the lens sets and the ev together, so it closes on **one further pass after the lowering**. That is the pre-existing profile-change path; the variable floor rides it. -**A cited-set change moves the floor only if it changes the unanimity verdict** — adding a level-0 -story to an all-level-0 set moves nothing. Where the verdict moves, upward is a raise with the -same one-further-pass consequence; downward lowers the floor and **releases nothing else**. +**The cited set is re-read at each pass, and the final clean pass runs against the current set** — +**whenever its membership changes, not only when the floor number moves.** Adding a high-risk story +to a set already at floor 3 leaves the number alone while adding that story's lens set, its +evidence obligations and its review scope; a pass run before it joined did not cover them. + +Where the change moves the **number**: upward is a raise with the same one-further-pass +consequence; downward lowers the floor. **What a removal discharges, precisely:** obligations are +recomputed from the current set, so removing a story does remove *that story's* lenses and evidence +duty. It **never discharges an accepted in-set Blocker or Major** — the user's acceptance put that +finding in the fix set, not the citation, so removing the citation does not take it out. --- @@ -160,8 +188,25 @@ measurement**, which is why the story routes it to P8. Each of the three cycles records its own per-pass finding and Blocker counts in its own commit body, labelled with the cycle it describes. **Gate B alone would leave the dominant cost -unmeasured** — the loops this story cites as evidence are Gate-A loops. The plan fixes the format; -these are the contract: +unmeasured** — the loops this story cites as evidence are Gate-A loops. **Pinned here for the same +reason as the provenance line:** + +``` +cycle ; (passes , ): Findings . Blockers . + + := "Gate-A spec" | "Gate-A plan" | "Gate B" + := ("," )* strictly ascending, non-overlapping + :=

|

"-"

+

:= [1-9][0-9]* + := ("," )* exactly as many entries as enumerates + := 0 | [1-9][0-9]* + := | ("; " )* + := "pass "

" " ("+" )* + := [^ ;:,()]+ | "undetermined" verbatim as the call reported it +``` + +A skipped cycle writes `cycle ; : skipped (see skip reason)` and no counts. The +properties the form exists to satisfy: - Carries that cycle's **nonce**, so a curve can be attributed to the cycle that produced it. - **One entry per valid pass**, and because incomplete passes are excluded and consume pass @@ -170,8 +215,12 @@ these are the contract: - A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are **branches of one logical pass** contributing one summed entry. **The curve counts logical passes; the hook counts calls**, and where they differ the body says so. -- **Both branches of one logical pass must have reviewed the same artifact revision.** If it - changed between them they are not one pass. +- **Both branches of one logical pass must have reviewed the same artifact revision**, identified + by the **tracked reviewed commit** — the plan fixes how it is encoded, but which thing is + compared is a decision, not a format. **If it changed between them they are not one pass**: the + completed branch is recorded as an incomplete pass and excluded, and the later branch begins a + new one. Ending the pass is the conservative direction; merging two revisions would produce one + entry describing two different artifacts. - **The model each pass ran under is recorded**, per the existing convention in `docs/coding-workflow.md`, written `undetermined` where it cannot be determined rather than guessed, and admitting provider-qualified identifiers. @@ -199,13 +248,33 @@ and the Gate-B cycle — so a run of all three produces three nonces, not one.** name, a timestamp or a commit, each of which collides exactly where sibling cycles do. - Constrained so it is **safe as a slot infix and a path component**. - **It appears in every record the cycle writes.** +- **A nonce is a *candidate* for recovery only if it is keyed to this cycle's type (Gate-A spec, + Gate-A plan, or Gate B) and this cycle's artifact, and that cycle is still open.** History + normally holds many closed cycles' nonces and they are not candidates; a working record left by a + closed cycle is not one either, and **the working record is retired at closure** so it cannot + become one later. - **Recovery has two sources**, because a Gate-A cycle's commit does not exist while it runs: the - advisory working record during the cycle, and history at its commit. Recovering from **either** - keeps identity. **Neither, or sources that disagree, or more than one candidate → no identity, - start a new cycle** — which costs passes rather than letting one cycle's records read as - another's. -- **The advisory working record is a cycle record too**: it carries the nonce, and §5's per-cycle - infix and refuse-on-collision rules apply to it. §5's optional-companion status is unchanged. + advisory working record during the cycle, and history at its commit. Recovering a single + candidate from **either** keeps identity. **No candidate, disagreeing sources, or more than one + candidate → no identity, start a new cycle** — which costs passes rather than letting one cycle's + records read as another's. +- **A cycle does not start without a valid, unique nonce.** Where generation fails — randomness + unavailable, an invalid value, or a collision with an open cycle — retry within a bounded policy + the plan fixes, then **stop and surface**. **No deterministic fallback**, since a derived value + collides exactly where sibling cycles do, which is the property the nonce exists to avoid. +- **The advisory working record is a cycle record too**: it carries the nonce, and the slot rules + below apply to it as they do to findings slots. §5's optional-companion *status* is unchanged. + +**Slot rules this change adds** — the plan fixes the spelling, these are the properties. §5's +current slot grammar admits three exact names and no per-cycle component, which is why this is an +addition rather than a reference: +- **A cycle that has a nonce uses it in every slot more than one cycle could write.** The bare name + is reserved for the legacy single-cycle case it already serves. +- **A target owned by another nonce is refused, not overwritten**, and the refusal names the + collision. §5 already stops on a target that survives deletion; this extends that to a target + that must not be deleted at all. +- These rules exist because a bare slot was in fact overwritten during this cycle, destroying a + previous cycle's findings file. --- @@ -219,9 +288,11 @@ explicitly dropped is a dropped condition. **No passage is rewritten without its covering its own change — two changes to one passage owe two answers. **Where it is produced and gated.** In one artifact, written **once against the frozen final text** -and **reviewed before any replacement text is written**, as a Gate-A review under this story's -profile with a clean pass as its acceptance condition and no replacement text on failure. A finding -there **feeds back** rather than being absorbed. **The plan carries the passage list and executes +and **reviewed before any replacement text is written**. **That review is part of the Gate-A plan +cycle, not a fourth cycle** — the topology stays at three, and the artifact is an input the plan +cycle reviews alongside the plan. Its acceptance is that plan cycle's clean pass; **no replacement +text is written while it is outstanding**, and a finding against it **feeds back** — the artifact +is regenerated against corrected text — rather than being absorbed. **The plan carries the passage list and executes this method against it** — and because the dispositions are deferred, **that list is the sole guard against a dropped condition**, so it is re-checked whenever the design adds a rule. @@ -282,6 +353,12 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). **item 7 read against the whole resulting prompt** — a contradiction is a relation between an edited passage and an unedited one. +- **This branch's own three closing bodies carry the final forms**, since two story criteria + require them to demonstrate the provenance line and the curve, and the spec and plan cycles here + closed before those forms existed. **The plan carries the obligation to reconstruct those bodies + into the pinned forms**, and the verification confirms **all three** before closure — a + demonstration the branch does not actually contain is not a demonstration. + **Revalidation.** §5 requires it before every re-review and before the closing amend. Verifications reading closing commits are produced against the `WIP:` snapshot and **re-read against the content the close will carry** — the amend *is* the closing act. **What that establishes is bounded**: the diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 4e15a30..d53db60 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -122,10 +122,11 @@ would separate a rule from the argument that settles it. **Every pass report states the floor it derived, the risk and security axes it read, and which cited stories it read them from**, so the value is visible while passes are still being spent rather than only in the closing commit. - Each copy also states that **one derived value governs every loop of the cycle** — the - Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle alike — and says *why*: those - loops cite the same stories, so the value they derive is the same. A copy that states the - floor for one gate and leaves the others to inference fails this criterion. + Each copy also states that **one derived value governs all three cycles** — the Gate-A spec + cycle, the Gate-A plan cycle and the Gate-B cycle, which §5 defines as **three separate + cycles** — and says *why*: they derive from **the same cited-story set**, not from being one + cycle. A copy that states the floor for one gate and leaves the others to inference fails + this criterion, and so does one that gives the shared-cycle reason, which is false. **One predicate, not two** (Daniel, 2026-08-28). An earlier draft added a `docs-only` arm; it is dropped because it does no work and, read path-wise, does the wrong work. A diff-derived reading cannot serve Gate A at all — Gate A runs on a spec, before any @@ -197,6 +198,13 @@ would separate a rule from the argument that settles it. remain legitimate readers of rule text they will later apply; what is excluded is the reviewing pass, not gates. +- [ ] **A cycle's records can be told apart from another cycle's.** Both copies require each + cycle to hold an identifier created at its start, unique among open cycles, and present in + every record that cycle writes — **one per cycle, so a run of all three holds three**. They + state how it is recovered by a cycle resumed after an interruption, what happens when it + cannot be recovered unambiguously, that a cycle does not start without one, and that a slot + another cycle owns is refused rather than overwritten. Checkable by reading either copy, and + observable on this branch: each of its three closing bodies carries its own. - [ ] **The shipped text says when it starts binding, and what an adopter gets when it does not fully arrive.** Both copies state: that a loop already running finishes under the rules it started with; what a loop does when its starting rules cannot be established, covering From 6491e9dc5f09e3f7f18115fc24a2aa99971802a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:06:36 +0200 Subject: [PATCH 032/117] =?UTF-8?q?docs:=20revision=2017=20=E2=80=94=20pas?= =?UTF-8?q?s=2015's=20nine=20Majors;=20second=20consecutive=20zero-Blocker?= =?UTF-8?q?=20pass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 15: 13 findings, 0 BLOCKER, 9 Blocker/Major. The slim prediction ("under 10 B/M") is MET. Blocker/Major across fifteen passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9. Blockers 5,2,0,4,0,3,9,4,2,2,2,5,9,0,0. Zero tells. One finding corrected a claim I made about my own branch. §8 said the spec and plan cycles "closed before those forms existed" and required their bodies to be reconstructed. Neither is true: the Gate-A spec cycle has not closed -- it is still in Gate A -- and no plan artifact or plan cycle exists in branch history at all. So no reconstruction is needed and none is claimed; all three cycles write their closing bodies natively in the pinned forms. Checking git log before asserting what a branch contains would have caught it. The two grammars gained what a grammar needs to be one. `` excluded the delimiters but not `+`, which is itself the contributing-model separator, and had no quoted form for a reported identifier containing one; both fixed. And `` never required its keys to be exactly the passes `` expands to, each once, ascending -- a per-pass model list that omits or repeats a pass is malformed rather than partially informative -- nor that every model contributing to a split pass is listed, since recording one of two is the same loss as recording none. A new verification: nothing in the evidence plan ever PARSED the two pinned interfaces. This branch's three instances cannot exercise quoted paths, each unusable-knob cause, gapped ranges, split-model passes, a skipped cycle, or the cardinality rule. The check now reads constructed strings -- valid ones that must parse, invalid ones that must be rejected -- and records which features each exercises. A grammar nothing ever parsed is a format claim, not a format. The prompt-standards obligation was scoped to changed regions with only item 7 read whole; invariant 11 binds each changed prompt artifact AS A COMPLETE PROMPT, so the changed regions are the reason the pass is owed, not its scope. That pass will surface a pre-existing absence -- neither resulting prompt carries a "Target model:" line, which item 1 requires, and workflow-init names one only for the outer command whose bytes are not scaffolded. The spec says explicitly to ROUTE that rather than fix it here: its remedy touches whole-file properties rather than §5, and expanding into it silently is the scope failure this project logs. Also: a profile lowering does not always move the floor either -- level 2 to level 1 leaves it at 3 -- so the one-further-pass consequence attaches to the profile or set CHANGING, not to the number moving. Story: criterion 1 now observes the set-membership rule and that removing a citation cannot discharge an accepted in-set finding; criterion 2 observes when separate calls are one logical pass and what a revision mismatch does. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-15. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 48 ++++++++++++++----- ...-review-loop-economics-pass-floor-story.md | 12 ++++- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index a1223af..57de072 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 16 (rules only) · **Gate-A passes 1-14** +**Date:** 2026-08-29 · **Revision:** 17 (rules only) · **Gate-A passes 1-15** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -134,8 +134,11 @@ profile-change path; the variable floor rides it. to a set already at floor 3 leaves the number alone while adding that story's lens set, its evidence obligations and its review scope; a pass run before it joined did not cover them. -Where the change moves the **number**: upward is a raise with the same one-further-pass -consequence; downward lowers the floor. **What a removal discharges, precisely:** obligations are +**A profile change moves the number only sometimes, in both directions**: level 2 → level 1 leaves +the floor at 3, exactly as level 0 → level 0 leaves it at 1. The one-further-pass consequence +attaches to the **profile or set changing**, not to the number moving — the final clean pass must +run under the current profile and against the current set regardless. Where the number does move, +upward is a raise and downward a lowering. **What a removal discharges, precisely:** obligations are recomputed from the current set, so removing a story does remove *that story's* lenses and evidence duty. It **never discharges an accepted in-set Blocker or Major** — the user's acceptance put that finding in the fix set, not the citation, so removing the citation does not take it out. @@ -202,9 +205,17 @@ cycle ; (passes , ): Findings . Blockers := 0 | [1-9][0-9]* := | ("; " )* := "pass "

" " ("+" )* - := [^ ;:,()]+ | "undetermined" verbatim as the call reported it + := | | "undetermined" + := [^ ;:,()+"]+ excludes the grammar's own delimiters, "+" included + := '"' ... '"' for a reported identifier containing any of them, + with \ and " escaped ``` +**`` keys must be exactly the passes `` expands to, each once, ascending** — a +per-pass model list that omits or repeats a pass is malformed, not partially informative. **Every +model contributing to a split logical pass is listed** for that pass, joined by `+`; recording one +of two contributing models is the same loss as recording none. + A skipped cycle writes `cycle ; : skipped (see skip reason)` and no counts. The properties the form exists to satisfy: @@ -347,17 +358,32 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). - **A conditional verification that a user's knob survives untouched** — byte-identical across a cycle where one exists; **recorded not-applicable with its reason where none does**, since creating one would be a fixture supplying its own input. +- **A parse check over both pinned grammars.** The branch's own instances cannot exercise them: + three lines cannot cover quoted paths, each unusable-knob cause, gapped pass ranges, split-model + passes, a skipped cycle, or the cardinality rule that `` matches ``. **The check + reads a set of constructed strings — valid ones that must parse and invalid ones that must be + rejected** — and records which grammar features each exercises. A grammar nothing ever parsed is + a format claim, not a format. - **Parity across every changed rule**, in both copies, since they already diverge and parity cannot be asserted from a whole-section diff. -- **A fresh twelve-item `docs/prompt-standards.md` pass** over every changed prompt region, with - **item 7 read against the whole resulting prompt** — a contradiction is a relation between an - edited passage and an unedited one. +- **A fresh twelve-item `docs/prompt-standards.md` pass.** Invariant 11 binds **each changed + prompt artifact as a complete prompt**, not the diff — so the items are read against the + resulting `CLAUDE.md` and the resulting scaffolded template, with the changed regions as the + reason the pass is owed rather than its scope. Item 7's whole-artifact reading is the clearest + case, not the only one. + **One consequence to expect, and it is not this change's to absorb:** neither resulting prompt + currently carries a `Target model:` line, which item 1 requires — `workflow-init.md` names one + for the outer command and those bytes are not scaffolded. That is a **pre-existing absence this + pass will surface**, whose remedy touches whole-file properties rather than §5. **Route it rather + than fixing it here**; the pass's job is to find it, and expanding into it silently is the scope + failure this project logs. - **This branch's own three closing bodies carry the final forms**, since two story criteria - require them to demonstrate the provenance line and the curve, and the spec and plan cycles here - closed before those forms existed. **The plan carries the obligation to reconstruct those bodies - into the pinned forms**, and the verification confirms **all three** before closure — a - demonstration the branch does not actually contain is not a demonstration. + require it. **No reconstruction is needed and none is claimed:** the Gate-A spec cycle has not + closed — it is still in Gate A as this is written — and the Gate-A plan cycle has not started, so + both write their closing bodies natively in the pinned forms. Only the Gate-B cycle's body is + written later, likewise natively. **The verification confirms all three before closure**, because + a demonstration the branch does not actually contain is not a demonstration. **Revalidation.** §5 requires it before every re-review and before the closing amend. Verifications reading closing commits are produced against the `WIP:` snapshot and **re-read against the content diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index d53db60..27d87af 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -127,6 +127,11 @@ would separate a rule from the argument that settles it. cycles** — and says *why*: they derive from **the same cited-story set**, not from being one cycle. A copy that states the floor for one gate and leaves the others to inference fails this criterion, and so does one that gives the shared-cycle reason, which is false. + Both copies also state that **a change to the cited set requires the final clean pass to run + against the current set even when the floor number does not move** — a new member brings its + lenses and evidence duties with it — and that **removing a citation never discharges an + already accepted in-set Blocker or Major**, since acceptance and not the citation put it in + the fix set. **One predicate, not two** (Daniel, 2026-08-28). An earlier draft added a `docs-only` arm; it is dropped because it does no work and, read path-wise, does the wrong work. A diff-derived reading cannot serve Gate A at all — Gate A runs on a spec, before any @@ -145,8 +150,11 @@ would separate a rule from the argument that settles it. enough that a reader can tell **which pass each number belongs to** — incomplete passes are excluded and they consume pass numbers — and **which model each pass ran under**, which an existing convention already requires beside a finding count. A **legitimately skipped** loop - records the skip rather than leaving a silent gap. Checkable: the requirement is stated in - both copies, and this story's own commits carry it for each loop that ran. + records the skip rather than leaving a silent gap. Both copies also state when separate calls + are **one logical pass**: only when they reviewed the **same tracked revision** — and that a + revision mismatch **ends the first as an incomplete pass** before the later one starts a new + pass, rather than merging two revisions into a single entry. Checkable: the requirement is + stated in both copies, and this story's own commits carry it for each cycle that ran. **All three, not Gate B alone** (revised 2026-08-28 after Gate-A pass 2). A Gate-B-only requirement would leave the *dominant* cost unmeasured: the loops this story cites as evidence are Gate-A loops — nineteen measured Gate-A passes on one spec, and this story's From e364fbce108991650f3c5e579ba105662b6bfb9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:20:42 +0200 Subject: [PATCH 033/117] =?UTF-8?q?docs:=20revision=2018=20=E2=80=94=20pas?= =?UTF-8?q?s=2016's=20four=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 16: 4 findings, 1 BLOCKER, 4 Blocker/Major. Blocker/Major across sixteen passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4. Zero tells. The Blocker was a contradiction I wrote in one paragraph. §8 said invariant 11 binds each resulting prompt as a complete artifact -- and then knowingly routed a known item-1 failure as out of scope. Those cannot both stand: a change cannot require each resulting prompt to pass all twelve items and leave one false by decision. Both resulting prompts get a `Target model:` line. It is one line per copy in a file this change already edits, and the invariant that makes the pass binding is the same one that makes the line required. My "route it" instinct was over-caution wearing scope discipline's clothes. The nonce field this branch cannot supply natively. The Gate-A spec cycle running now began before the nonce rule existed; its slot discriminator is short and deterministic, so it is not a nonce, and minting one at this point would be late-created provenance dressed as a cycle record. The activation rule already governs this: a cycle already running finishes under the rules it started with. So this cycle records the field as `pre-rule` and says so, and the FIRST CYCLE STARTED AFTER THESE RULES SHIP carries a real one -- which the verification confirms, rather than pretending this branch demonstrates something it cannot. §2.4 contradicted itself across two paragraphs -- the first still said a lowering drops the floor and closes after one further pass, while the repair below said changes can leave the floor unchanged and the consequence attaches to the change rather than the number. Now stated once: ANY profile change costs at least one further pass, either direction, floor moved or not, because the final clean pass must run under the current profile. And that further pass must be clean with every other closure duty satisfied -- it is one more pass, not a licence to close on the next one, which the old wording could be read to allow. Story criterion 1 still said "3 otherwise" and named only higher-profile or unprofiled members, so it could be read to accept the silent fallback the spec explicitly forbids. It now states the unresolvable-stop directly and fails a copy that reads an unresolvable profile as 3. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-16. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 39 ++++++++++++------- ...-review-loop-economics-pass-floor-story.md | 8 +++- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 57de072..0300a77 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 17 (rules only) · **Gate-A passes 1-15** +**Date:** 2026-08-29 · **Revision:** 18 (rules only) · **Gate-A passes 1-16** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -123,11 +123,12 @@ The properties the grammar exists to satisfy: Three existing rules compose; no new rule. The floor derives from the **current** profile at each pass; **passes already run keep counting**; **closing requires the floor as currently derived.** -**A raise costs at least one further pass regardless of the arithmetic**, because §5 already -requires the final clean pass to run under the current profile — so even a raise leaving the floor -unchanged costs a pass. **A lowering** drops the floor, the lens sets and the evidence mode -together, so it closes on **one further pass after the lowering**. That is the pre-existing -profile-change path; the variable floor rides it. +**Any profile change costs at least one further pass**, in either direction and whether or not the +floor number moves, because §5 already requires the **final clean pass** to run under the current +profile — so no already-banked pass can be it. **That further pass must be clean and every other +closure duty must be satisfied**; it is one more pass, not a licence to close on the next one. A +lowering additionally drops the lens sets and the evidence mode along with any change in the floor. +That is the pre-existing profile-change path; the variable floor rides it. **The cited set is re-read at each pass, and the final clean pass runs against the current set** — **whenever its membership changes, not only when the floor number moves.** Adding a high-risk story @@ -371,19 +372,27 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). resulting `CLAUDE.md` and the resulting scaffolded template, with the changed regions as the reason the pass is owed rather than its scope. Item 7's whole-artifact reading is the clearest case, not the only one. - **One consequence to expect, and it is not this change's to absorb:** neither resulting prompt - currently carries a `Target model:` line, which item 1 requires — `workflow-init.md` names one - for the outer command and those bytes are not scaffolded. That is a **pre-existing absence this - pass will surface**, whose remedy touches whole-file properties rather than §5. **Route it rather - than fixing it here**; the pass's job is to find it, and expanding into it silently is the scope - failure this project logs. + **One known failure, fixed rather than routed.** Neither resulting prompt carries a + `Target model:` line, which item 1 requires — `workflow-init.md` names one for the outer command + and those bytes are not scaffolded. **Both resulting prompts get one.** An earlier revision + proposed routing it as out of scope, which cannot stand beside the sentence above: a change + cannot require each resulting prompt to pass all twelve items and knowingly leave one false. It + is one line per copy, in a file this change already edits, and the invariant that makes the pass + binding is the same invariant that makes the line required. - **This branch's own three closing bodies carry the final forms**, since two story criteria require it. **No reconstruction is needed and none is claimed:** the Gate-A spec cycle has not closed — it is still in Gate A as this is written — and the Gate-A plan cycle has not started, so - both write their closing bodies natively in the pinned forms. Only the Gate-B cycle's body is - written later, likewise natively. **The verification confirms all three before closure**, because - a demonstration the branch does not actually contain is not a demonstration. + both write their closing bodies natively in the pinned forms. + **The nonce is the one field this branch's spec cycle cannot supply natively, and the activation + rule already governs it.** That cycle began before the nonce rule existed; its slot discriminator + is short and deterministic, so it is not a nonce, and minting one now would be late-created + provenance dressed as a cycle record. **A cycle already running finishes under the rules it + started with** (§10), so this one records its provenance line and curve with the nonce field + marked `pre-rule` and says so. **The first cycle started after these rules ship carries a real + one**, and the verification confirms that rather than pretending this branch demonstrates it. + **The verification confirms all three bodies before closure**, because a demonstration the branch + does not contain is not a demonstration. **Revalidation.** §5 requires it before every re-review and before the closing amend. Verifications reading closing commits are produced against the `WIP:` snapshot and **re-read against the content diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 27d87af..97d2e04 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -112,8 +112,12 @@ would separate a rule from the argument that settles it. - [ ] **The floor scales by profile, on one predicate, and both copies say which gates it moves.** `CLAUDE.md` §5 and the `/workflow-init` inline template mirror each state a - mandatory pass floor of **1** where `max(risk, security)` is 0 and **3** otherwise — - unprofiled cycles included, which keep today's 3. Two levels, not three: `high` gets no + mandatory pass floor of **1** only where the cited set is non-empty and every member is + profiled, resolvable and at level 0; **3** where no story is cited or any member is + unprofiled; and — stated, not left to a fallback — **a member whose profile is present but + unresolvable stops and surfaces**, per §5's existing rule, rather than yielding 3. A copy + that reads an unresolvable profile as 3 fails this criterion, because it converts a stop + condition into a silent default. Two levels, not three: `high` gets no extra mandatory passes, taking its added rigor from lens sets and evidence mode instead. **A cycle citing several stories reaches the reduced floor only unanimously** — every cited story profiled, every one at level 0 — so a single higher-profile or unprofiled From 8640302ad3172954196c1e9bf74507974ebe689b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:32:36 +0200 Subject: [PATCH 034/117] =?UTF-8?q?docs:=20revision=2019=20=E2=80=94=20pas?= =?UTF-8?q?s=2017's=20six=20findings;=20the=20pre-rule=20cycle=20field=20m?= =?UTF-8?q?ade=20grammatical?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 17: 6 findings, 1 BLOCKER, 4 Blocker/Major, 2 NIT. Blocker/Major across seventeen passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4. The Blocker was revision 18's own repair breaking a rule two sections away. I introduced `pre-rule` as the nonce value for a cycle that predates the nonce rule -- and `pre-rule` contains a hyphen, so it cannot match `[a-z0-9]{8,16}`, while §2.3 claims one machine-extractable form covers every case. A magic string beside a grammar is not covered by it. Now a PRODUCTION: `` is either `cycle ` or the reserved `cycle none (pre-rule)`, so the one-form claim holds and a parser needs no special case. And the pre-rule treatment was scoped wrongly. §8 called only the running spec cycle pre-rule and the later plan and Gate-B cycles native -- but §10 binds the rules at the IMPLEMENTATION commit, and all three of this branch's cycles begin before that. So all three write `cycle none (pre-rule)`, the branch demonstrates every field of both forms except the nonce, and the first post-ship cycle demonstrates that. Claiming otherwise would claim a demonstration the branch cannot contain -- which is the same error revision 18 corrected one paragraph earlier and reintroduced two paragraphs later. A lowering does not categorically drop lenses and evidence mode together: a mode-only override changes evidence while leaving axis-derived lenses alone, and security high to standard keeps the security lens set while changing what evidence is owed. Naming them as a package was wrong in both directions. The rule is that every derived obligation is recomputed from the current profile. Story: the mid-cycle answer and criterion 1 now carry what the spec requires -- ANY profile change costs a further clean pass, either direction, floor moved or not -- where both had recorded only the raise case. Two count mismatches fixed: "three outcomes" enumerating two after the split, and "four properties" enumerating five including the load-bearing reviewing-pass exclusion. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-17. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 33 +++++++++++++------ ...-review-loop-economics-pass-floor-story.md | 17 ++++++---- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 0300a77..1bfd55e 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 18 (rules only) · **Gate-A passes 1-16** +**Date:** 2026-08-29 · **Revision:** 19 (rules only) · **Gate-A passes 1-17** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -91,8 +91,9 @@ moved it out and was wrong to; pass 9 had already settled this, and the test it whether anything but a person parses it.) ``` -cycle ; floor per ; hook reminder threshold +; floor per ; hook reminder threshold + := "cycle " | "cycle none (pre-rule)" := [a-z0-9]{8,16} := [1-9][0-9]* := "none" | "{" ("," )* "}" @@ -126,8 +127,11 @@ pass; **passes already run keep counting**; **closing requires the floor as curr **Any profile change costs at least one further pass**, in either direction and whether or not the floor number moves, because §5 already requires the **final clean pass** to run under the current profile — so no already-banked pass can be it. **That further pass must be clean and every other -closure duty must be satisfied**; it is one more pass, not a licence to close on the next one. A -lowering additionally drops the lens sets and the evidence mode along with any change in the floor. +closure duty must be satisfied**; it is one more pass, not a licence to close on the next one. **What a lowering drops is whatever the changed values drop, not a fixed pair**: a mode-only +override changes the evidence obligations while leaving the axis-derived lens sets alone, and +security `high` → `standard` keeps the security lens set while changing what evidence is owed. The +rule is that **every derived obligation is recomputed from the current profile**; naming lenses and +mode as a package was wrong in both directions. That is the pre-existing profile-change path; the variable floor rides it. **The cited set is re-read at each pass, and the final clean pass runs against the current set** — @@ -196,7 +200,7 @@ unmeasured** — the loops this story cites as evidence are Gate-A loops. **Pinn reason as the provenance line:** ``` -cycle ; (passes , ): Findings . Blockers . +; (passes , ): Findings . Blockers . := "Gate-A spec" | "Gate-A plan" | "Gate B" := ("," )* strictly ascending, non-overlapping @@ -217,7 +221,10 @@ per-pass model list that omits or repeats a pass is malformed, not partially inf model contributing to a split logical pass is listed** for that pass, joined by `+`; recording one of two contributing models is the same loss as recording none. -A skipped cycle writes `cycle ; : skipped (see skip reason)` and no counts. The +A skipped cycle writes `; : skipped (see skip reason)` and no counts. +`cycle none (pre-rule)` is the **only** admissible alternative to a nonce, reserved for a cycle +that began before the nonce rule shipped (§8) — it is a production of the grammar rather than a +magic string beside it, so the one-form claim holds and a parser needs no special case. The properties the form exists to satisfy: - Carries that cycle's **nonce**, so a curve can be attributed to the cycle that produced it. @@ -384,12 +391,18 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). require it. **No reconstruction is needed and none is claimed:** the Gate-A spec cycle has not closed — it is still in Gate A as this is written — and the Gate-A plan cycle has not started, so both write their closing bodies natively in the pinned forms. - **The nonce is the one field this branch's spec cycle cannot supply natively, and the activation - rule already governs it.** That cycle began before the nonce rule existed; its slot discriminator + **Which of this branch's cycles can supply a nonce natively follows from when the rules bind, and + §10 fixes that at the implementation commit — not at this spec's close.** So the Gate-A plan + cycle and the Gate-B cycle also begin before the rules ship, and all three are pre-rule cycles by + §10's own rule. **All three therefore write `cycle none (pre-rule)`**, and the branch demonstrates + every field of both forms except the nonce, which the first post-ship cycle demonstrates. Claiming + otherwise would be claiming a demonstration the branch cannot contain. + **The nonce is the one field this branch cannot supply natively, and the activation rule governs + it.** That cycle began before the nonce rule existed; its slot discriminator is short and deterministic, so it is not a nonce, and minting one now would be late-created provenance dressed as a cycle record. **A cycle already running finishes under the rules it - started with** (§10), so this one records its provenance line and curve with the nonce field - marked `pre-rule` and says so. **The first cycle started after these rules ship carries a real + started with** (§10), so this one records its provenance line and curve with the cycle field written + `cycle none (pre-rule)`, which the grammar admits as a production. **The first cycle started after these rules ship carries a real one**, and the verification confirms that rather than pretending this branch demonstrates it. **The verification confirms all three bodies before closure**, because a demonstration the branch does not contain is not a demonstration. diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 97d2e04..a2f5703 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -47,7 +47,7 @@ this story repairs. ## 2. Desired outcome -Three outcomes, each observable in the shipped prompt text. +Two outcomes, each observable in the shipped prompt text. 1. **A cycle's mandatory pass count reflects what the story is worth reviewing**, instead of charging every cycle the same three passes per gate. A story the profile puts at level 0 @@ -131,8 +131,9 @@ would separate a rule from the argument that settles it. cycles** — and says *why*: they derive from **the same cited-story set**, not from being one cycle. A copy that states the floor for one gate and leaves the others to inference fails this criterion, and so does one that gives the shared-cycle reason, which is false. - Both copies also state that **a change to the cited set requires the final clean pass to run - against the current set even when the floor number does not move** — a new member brings its + Both copies also state that **a change to the cited set *or to any cited profile* requires + the final clean pass to run against the current set and under the current profile, even when + the floor number does not move** — a new member brings its lenses and evidence duties with it — and that **removing a citation never discharges an already accepted in-set Blocker or Major**, since acceptance and not the citation put it in the fix set. @@ -189,7 +190,8 @@ would separate a rule from the argument that settles it. than acting on it. Observable on this branch: a knob present before a cycle is byte-identical after it. - [ ] **Severity is decided by one stated test in both copies, and the test is keyed on - consequence.** Four properties, each checkable by reading either copy: + consequence.** + Five properties, each checkable by reading either copy. **(a)** Exactly **one** procedure decides severity. A copy that states a categorical demotion by subject *alongside* the test fails, because two procedures can disagree on one finding; subject-based cases may appear only as worked examples of the test. @@ -311,9 +313,10 @@ would separate a rule from the argument that settles it. - ~~**Does a profile change mid-cycle move the floor for passes already run?**~~ **Answered 2026-08-28**, and it needed no new rule — three existing ones compose. The floor derives from the current profile at each pass; passes already run keep counting; closing - requires the floor as currently derived. The consequence worth stating is that a **raise costs - at least one further pass regardless of the arithmetic**, because §5 already requires the - final clean pass to run under the current profile. + requires the floor as currently derived. The consequence worth stating is that **any profile + change costs at least one further clean pass, in either direction and whether or not the floor + number moves**, because §5 already requires the final clean pass to run under the current + profile — so no already-banked pass can be it. - ~~**What floor does an unprofiled cycle get?**~~ **Answered 2026-08-28** by the single-predicate decision in criterion 1: no profile means `max(risk, security)` is not 0, so the default of 3 stands. Kept rather than deleted, because the answer is only obvious From df84dfadde051dde24151687855067c902236c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:44:28 +0200 Subject: [PATCH 035/117] =?UTF-8?q?docs:=20revision=2020=20=E2=80=94=20the?= =?UTF-8?q?=20pre-rule=20exception=20made=20semantic,=20not=20just=20gramm?= =?UTF-8?q?atical?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 18: 2 findings, 0 BLOCKER, 1 Blocker/Major. Blocker/Major across eighteen passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1. Zero tells. The one Major was precise and correct. Revision 19 made `cycle none (pre-rule)` a grammatical production, which fixed the parser's problem and left the SEMANTIC contract untouched: the required properties still said every provenance record and every curve carries its cycle nonce, §5 still said every record carries it, and two story criteria still required this branch's three closing bodies to carry distinct identifiers -- while §8 and §10 require all three to use the non-identifying pre-rule field. A grammar that admits an exception the prose forbids is worse than one that admits neither. Now stated once, in the properties themselves: the cycle field carries the nonce for any cycle started AFTER these rules ship, and `none (pre-rule)` only for one that began before. A pre-rule record is NOT cycle-attributable, and the text says so rather than implying the field always identifies something. The exception is bounded and self-terminating -- it reaches only cycles already running when the rules land, and no later cycle can enter the state. And the branch-demonstration claims now say what they actually prove: every field of both pinned forms EXCEPT the identifier, which no cycle here can supply. The identifier is verified at a named later checkpoint -- the first cycle started after the implementation commit -- rather than being counted as satisfied. Recording that as a checkpoint instead of a met criterion is the difference between a demonstration and a claim, which is the distinction this whole cycle has been paying for. Two stale cross-references fixed: the story cited "Section A" and an unqualified "§8", neither of which exists in the story -- both were pointing at design sections from an earlier structure. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-18. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 25 ++++++++++++++----- ...-review-loop-economics-pass-floor-story.md | 14 ++++++++--- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 1bfd55e..511ef3c 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 19 (rules only) · **Gate-A passes 1-17** +**Date:** 2026-08-29 · **Revision:** 20 (rules only) · **Gate-A passes 1-18** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -109,7 +109,9 @@ The properties the grammar exists to satisfy: - **Every cycle records it**, default floor or not, so an absent line is never ambiguous between "the default applied" and "someone forgot". **Three cycles means three lines**, one per cycle. -- It carries that cycle's **nonce** (§5), the **derived floor**, and **the cited set that produced +- It carries that cycle's **cycle field** — **the nonce (§5) for any cycle started after these + rules ship, and `none (pre-rule)` only for a cycle that began before them** — the **derived + floor**, and **the cited set that produced it with each member's level as a numeral** — one floor and one set, not an entry per story, since unanimity makes the floor a property of the set. - It distinguishes **a cited story with no profile** from **no story cited**. @@ -227,7 +229,10 @@ that began before the nonce rule shipped (§8) — it is a production of the gra magic string beside it, so the one-form claim holds and a parser needs no special case. The properties the form exists to satisfy: -- Carries that cycle's **nonce**, so a curve can be attributed to the cycle that produced it. +- Carries that cycle's **cycle field**, so a curve can be attributed to the cycle that produced + it — **the nonce for a post-rule cycle, `none (pre-rule)` for one that began before the rules + shipped.** A pre-rule record is **not attributable to a cycle**, and the text says so rather than + implying the field always identifies one. - **One entry per valid pass**, and because incomplete passes are excluded and consume pass numbers, the record **states which pass numbers it covers**. A valid zero-finding pass is recorded as zero, never omitted. @@ -266,7 +271,11 @@ and the Gate-B cycle — so a run of all three produces three nonces, not one.** characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — never derived from a name, a timestamp or a commit, each of which collides exactly where sibling cycles do. - Constrained so it is **safe as a slot infix and a path component**. -- **It appears in every record the cycle writes.** +- **It appears in every record the cycle writes** — for every cycle started after these rules + ship. **A pre-rule cycle has no nonce and cannot acquire one**, so its records carry + `none (pre-rule)` in the cycle field and are, by construction, not cycle-attributable. That is a + bounded, self-terminating exception: it applies only to cycles already running when the rules + land, and no later cycle can enter the state. - **A nonce is a *candidate* for recovery only if it is keyed to this cycle's type (Gate-A spec, Gate-A plan, or Gate B) and this cycle's artifact, and that cycle is still open.** History normally holds many closed cycles' nonces and they are not candidates; a working record left by a @@ -404,8 +413,12 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). started with** (§10), so this one records its provenance line and curve with the cycle field written `cycle none (pre-rule)`, which the grammar admits as a production. **The first cycle started after these rules ship carries a real one**, and the verification confirms that rather than pretending this branch demonstrates it. - **The verification confirms all three bodies before closure**, because a demonstration the branch - does not contain is not a demonstration. + **The verification confirms all three bodies before closure**, and states exactly what they + prove: **every field of both pinned forms except the nonce**, which no cycle on this branch can + supply. **The nonce is verified at a named later checkpoint** — the first cycle started after the + implementation commit, whose provenance line and curve must carry a real one and whose records + must be attributable to it. Recording that as a checkpoint rather than as a satisfied criterion + is the difference between a demonstration and a claim. **Revalidation.** §5 requires it before every re-review and before the closing amend. Verifications reading closing commits are produced against the `WIP:` snapshot and **re-read against the content diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index a2f5703..b06294e 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -144,7 +144,7 @@ would separate a rule from the argument that settles it. "no behavioural effect in the artifact's own execution context", which a documentation change has none of. And a path-derived arm would be **wrong in this repo specifically** — `docs/hardening-log.md` is a `docs/**.md` path that drives rung escalation, so "docs" - does not imply "changes nothing". That is Section A's reachability test deciding a floor + does not imply "changes nothing". That is the design's severity test (part 2) deciding a floor question, which is why the two parts belong in one change. - [ ] **Every cycle's closing commit body carries its per-pass shape — all three cycle types.** Both copies require the **Gate-A spec loop** (in the spec's commit body), the **Gate-A @@ -159,7 +159,9 @@ would separate a rule from the argument that settles it. are **one logical pass**: only when they reviewed the **same tracked revision** — and that a revision mismatch **ends the first as an incomplete pass** before the later one starts a new pass, rather than merging two revisions into a single entry. Checkable: the requirement is - stated in both copies, and this story's own commits carry it for each cycle that ran. + stated in both copies, and this story's own commits carry it for each cycle that ran — + **every field of the pinned form except the identifier, which no cycle on this branch can + supply** (see the identifier criterion). **All three, not Gate B alone** (revised 2026-08-28 after Gate-A pass 2). A Gate-B-only requirement would leave the *dominant* cost unmeasured: the loops this story cites as evidence are Gate-A loops — nineteen measured Gate-A passes on one spec, and this story's @@ -218,7 +220,11 @@ would separate a rule from the argument that settles it. state how it is recovered by a cycle resumed after an interruption, what happens when it cannot be recovered unambiguously, that a cycle does not start without one, and that a slot another cycle owns is refused rather than overwritten. Checkable by reading either copy, and - observable on this branch: each of its three closing bodies carries its own. + **What this branch can and cannot demonstrate, stated rather than assumed:** its three cycles + all began before these rules ship, so each closing body carries the reserved pre-rule cycle + field and **none of them demonstrates a real identifier**. The branch demonstrates the field's + presence and grammar; **a named later checkpoint — the first cycle started after the + implementation commit — demonstrates a real one and the attribution it buys.** - [ ] **The shipped text says when it starts binding, and what an adopter gets when it does not fully arrive.** Both copies state: that a loop already running finishes under the rules it started with; what a loop does when its starting rules cannot be established, covering @@ -261,7 +267,7 @@ would separate a rule from the argument that settles it. *(Revised 2026-08-28 after Gate-A pass 1 found the original unsatisfiable — sparring session, under Daniel's 2026-08-28 delegation; flagged to Daniel for final-version review because criterion 8 was his explicit choice. The falsifiability he chose is preserved by - the §8 differential verification, the provenance line now, and the P8 checkpoint later.)* + the design's §8 differential verification, the provenance line now, and the P8 checkpoint later.)* ## 4. Affected AGENTS.md invariants From 9bda16836e727672703c57df41523a1ccab689ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:57:43 +0200 Subject: [PATCH 036/117] =?UTF-8?q?docs:=20revision=2021=20=E2=80=94=20the?= =?UTF-8?q?=20curve=20now=20measures=20what=20it=20is=20kept=20for?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 19: 7 findings, 0 BLOCKER, 4 Blocker/Major. Blocker/Major across nineteen passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4. The finding worth the pass: THE CURVE COULD NOT ANSWER THE QUESTION IT EXISTS TO ANSWER. P8 is meant to measure how much consequence-keyed severity demotes findings -- which moves the Blocker/Major line, not the total -- while the pinned form recorded only Findings and Blockers. An instrument that cannot measure its own subject is the false-green class this spec's own severity rule keeps as gating. Majors are now recorded alongside. Subject categories are deliberately NOT: they are a judgement per finding rather than a count, and the findings files carry the material for anyone who wants them. The pre-rule exception was qualified in three places and left categorical in two. §5's own section still said both records carry a nonce and a run of three produces three nonces; story criterion 6 still required every cycle to hold an identifier. Both now say what is true: post-rule cycles carry nonces, a pre-rule cycle has none and cannot acquire one, and the exception is bounded and self-terminating because no later cycle can enter that state. The verification checkpoint said "the first cycle started after the implementation commit", which names no cycle type and no artifact and has no total ordering when siblings start concurrently. It is now the Gate-A spec cycle of the next story whose spec is written after the implementation commit lands, and that cycle's provenance line and curve must carry the SAME nonce, differing from any other cycle's. Both grammars had prose placeholders where productions belonged -- ``, `` and an ellipsis in ``. Closed, with the escape language stated and the unrepresentable case given a behaviour: a path containing a control character stops and surfaces rather than being emitted, and a model identifier containing one is written `undetermined` with the raw value in prose. A one-line record has to say what it does with something that cannot be one line. Plus a Minor and three Nits: criterion 8 claimed the user-facing site inventory lives in the spec, which delegates it to the plan; a historical note still called the provenance criterion "criterion 8" after renumbering; an invariant note still said "part 3" after the split; and the successor announced two shared passages and then added a third. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-19. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4. Gate B: N/A -- all staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 40 +++++++++++++------ ...-review-loop-economics-pass-floor-story.md | 15 ++++--- ...026-08-29-loop-rule-consolidation-story.md | 2 +- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 511ef3c..f1d2e62 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 20 (rules only) · **Gate-A passes 1-18** +**Date:** 2026-08-29 · **Revision:** 21 (rules only) · **Gate-A passes 1-19** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -98,8 +98,12 @@ whether anything but a person parses it.) := [1-9][0-9]* := "none" | "{" ("," )* "}" := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" - := | bare excludes , { } ; " and whitespace; - quoted is double-quoted with \ and " escaped + := | + := [A-Za-z0-9._/-]+ contains no delimiter, quote or whitespace + := a double-quoted string whose only escapes are \" and \\ ; + a path containing a newline or other control + character is NOT representable — the cycle stops + and surfaces rather than emitting one := "absent" | [1-9][0-9]* | "unusable(" ")" := "unreadable" | "empty" | "non-numeric" | "out-of-range" ``` @@ -202,7 +206,7 @@ unmeasured** — the loops this story cites as evidence are Gate-A loops. **Pinn reason as the provenance line:** ``` -; (passes , ): Findings . Blockers . +; (passes , ): Findings . Blockers . Majors . := "Gate-A spec" | "Gate-A plan" | "Gate B" := ("," )* strictly ascending, non-overlapping @@ -214,8 +218,9 @@ reason as the provenance line:** := "pass "

" " ("+" )* := | | "undetermined" := [^ ;:,()+"]+ excludes the grammar's own delimiters, "+" included - := '"' ... '"' for a reported identifier containing any of them, - with \ and " escaped + := a double-quoted string, same two escapes as ; + a reported identifier containing a control character + is written `undetermined`, with the raw value in prose ``` **`` keys must be exactly the passes `` expands to, each once, ascending** — a @@ -229,6 +234,12 @@ that began before the nonce rule shipped (§8) — it is a production of the gra magic string beside it, so the one-form claim holds and a parser needs no special case. The properties the form exists to satisfy: +- **Records Majors as well as Findings and Blockers.** The question this curve is kept for is + whether consequence-keyed severity demotes findings — which moves the Blocker/Major line, not the + total. A curve of totals and Blockers alone cannot answer it, so the instrument would not measure + the thing it exists to measure. **Subject categories are deliberately not recorded**: they are a + judgement per finding rather than a count, and the findings files carry the material for anyone + who wants them. - Carries that cycle's **cycle field**, so a curve can be attributed to the cycle that produced it — **the nonce for a post-rule cycle, `none (pre-rule)` for one that began before the rules shipped.** A pre-rule record is **not attributable to a cycle**, and the text says so rather than @@ -263,9 +274,12 @@ adds the decline record to the same passage; extending is required, replacing wo ## 5. The cycle nonce -Both shipped records carry it, and a record that cannot be attributed to a cycle is unusable by the -measurement that reads it. **§5 defines three cycles — the Gate-A spec loop, the Gate-A plan loop -and the Gate-B cycle — so a run of all three produces three nonces, not one.** +Both shipped records carry a **cycle field**, because a record that cannot be attributed to a cycle +is unusable by the measurement that reads it. **§5 defines three cycles — the Gate-A spec loop, the +Gate-A plan loop and the Gate-B cycle — so a run of all three produces three cycle fields, and for +post-rule cycles three distinct nonces.** A **pre-rule** cycle has no nonce (§8); its field is the +reserved `none (pre-rule)` and its records are not cycle-attributable. Everything below describes +**post-rule cycles**, which is every cycle started after the implementation commit. - Generated once at cycle start, immutable, and **collision-resistant operationally: at least 8 characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — never derived from a @@ -415,9 +429,11 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). one**, and the verification confirms that rather than pretending this branch demonstrates it. **The verification confirms all three bodies before closure**, and states exactly what they prove: **every field of both pinned forms except the nonce**, which no cycle on this branch can - supply. **The nonce is verified at a named later checkpoint** — the first cycle started after the - implementation commit, whose provenance line and curve must carry a real one and whose records - must be attributable to it. Recording that as a checkpoint rather than as a satisfied criterion + supply. **The nonce is verified at a named later checkpoint**: the **Gate-A spec cycle of the next story + whose spec is written after the implementation commit lands**. Naming a cycle type and an + artifact rather than "the first cycle" matters because siblings can start concurrently and "first" + has no total ordering across them. That cycle's provenance line and curve must carry a real nonce, + the two must carry the **same** one, and it must differ from any other cycle's. Recording that as a checkpoint rather than as a satisfied criterion is the difference between a demonstration and a claim. **Revalidation.** §5 requires it before every re-review and before the closing amend. Verifications diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index b06294e..6ec8ca3 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -214,9 +214,12 @@ would separate a rule from the argument that settles it. remain legitimate readers of rule text they will later apply; what is excluded is the reviewing pass, not gates. -- [ ] **A cycle's records can be told apart from another cycle's.** Both copies require each - cycle to hold an identifier created at its start, unique among open cycles, and present in - every record that cycle writes — **one per cycle, so a run of all three holds three**. They +- [ ] **A cycle's records can be told apart from another cycle's.** Both copies require **each + cycle started after these rules ship** to hold an identifier created at its start, unique + among open cycles, and present in every record that cycle writes — **one per cycle, so a run + of all three holds three**. Both copies also state the **bounded, self-terminating exception**: + a cycle already running when the rules land has no identifier, cannot acquire one, and writes + a reserved value that says so — and no later cycle can enter that state. They state how it is recovered by a cycle resumed after an interruption, what happens when it cannot be recovered unambiguously, that a cycle does not start without one, and that a slot another cycle owns is refused rather than overwritten. Checkable by reading either copy, and @@ -234,7 +237,7 @@ would separate a rule from the argument that settles it. Checkable by reading either copy. - [ ] **The change leaves no shipped sentence contradicting it, and the package it ships in is valid.** Every user-facing statement this change falsifies is corrected in the same change — - the inventory is in the spec and a reviewer can check each cited line — and the plugin + a reviewer can check each corrected line against the site list the plan carries — and the plugin manifest's version and `CHANGELOG.md` are updated, which CI enforces on pull requests. Both are checkable after the fact: no corrected sentence still asserts a fixed three-pass floor, and the manifest version differs from its value on the base ref. @@ -266,7 +269,7 @@ would separate a rule from the argument that settles it. measured afterwards by the follow-up named in §2. *(Revised 2026-08-28 after Gate-A pass 1 found the original unsatisfiable — sparring session, under Daniel's 2026-08-28 delegation; flagged to Daniel for final-version review - because criterion 8 was his explicit choice. The falsifiability he chose is preserved by + because the provenance-demonstration criterion was his explicit choice. The falsifiability he chose is preserved by the design's §8 differential verification, the provenance line now, and the P8 checkpoint later.)* ## 4. Affected AGENTS.md invariants @@ -301,7 +304,7 @@ would separate a rule from the argument that settles it. `plugins/dev-workflow/`, so this fires. - `## 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." This is the governing constraint on part 3 and the basis of its + deliberately dropped." This is the governing constraint on the accounting and the basis of its criterion. - `## Don'ts` — "**Never describe what a gate proves without checking what it actually compares.**… for every sentence about a gate, name the exact comparison the code performs, diff --git a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md index 45ceda0..b11f99d 100644 --- a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md +++ b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md @@ -102,7 +102,7 @@ pass's cleanliness is a fact about what that pass found and is **never rewritten answer changes is whether the *cycle* may close; and the findings files establish the **inventory** of in-set findings, not their resolutions, which they do not contain. -### Two passages this story shares with the parent +### Three passages this story shares with the parent Both changes rewrite these, and **this story must extend rather than replace what the parent added** — the parent's accounting already covers its own half: From 87583f3efcc47f7029ff2b61e17e1ab965c0bb2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:11:29 +0200 Subject: [PATCH 037/117] =?UTF-8?q?docs:=20revision=2022=20=E2=80=94=20the?= =?UTF-8?q?=20baseline=20cannot=20bear=20the=20demotion=20comparison,=20an?= =?UTF-8?q?d=20says=20so?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 20: 6 findings, 0 BLOCKER, 4 Blocker/Major. Blocker/Major across twenty passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4. Three consecutive zero-Blocker passes. Zero tells. The finding that mattered: adding Major counts to the curve made FORWARD comparison possible and did not make the BASELINE bear it. The fic2 closing commit records only totals and Blockers, the committed field report supplies Majors for some passes and not others, and the per-finding subject material lives only in gitignored findings files. So the baseline supports a total-volume comparison and nothing finer, and any demotion claim rests on post-rule cycles compared with each other. The spec now says that, because a later reader would otherwise compute a demotion figure the baseline cannot support -- which is the overclaim class this repo logs most often, arriving through an instrument rather than through prose. A wording contradiction resolved rather than papered over: the spec called P8 "the measurement" in one place and forbade it from presenting its curves as measurement in another. What P8 produces is bounded by what it reads -- a self-reported curve nothing validates against the findings files -- so it reports a comparison of recorded values with that limit stated. The prompt-standards pass covered the resulting CLAUDE.md and the resulting scaffolded template and omitted workflow-init.md itself, though this change edits that prompt and the spec's own text cites its separate target-model declaration. Added as a third artifact. Two story criteria were still describing the previous revision: the checkpoint as "the first cycle started after the implementation commit" rather than the named cycle type and artifact, and the branch's pre-rule provenance line as naming its cycle when the reserved value by construction does not. Both corrected -- what the branch demonstrates is the field's presence and grammar, not attribution. And two pointers sent a reader to §8 for the activation rule, which is §10's. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-20. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 39 ++++++++++++------- ...-review-loop-economics-pass-floor-story.md | 10 +++-- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index f1d2e62..6ee8e2a 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 21 (rules only) · **Gate-A passes 1-19** +**Date:** 2026-08-29 · **Revision:** 22 (rules only) · **Gate-A passes 1-20** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -234,12 +234,19 @@ that began before the nonce rule shipped (§8) — it is a production of the gra magic string beside it, so the one-form claim holds and a parser needs no special case. The properties the form exists to satisfy: -- **Records Majors as well as Findings and Blockers.** The question this curve is kept for is - whether consequence-keyed severity demotes findings — which moves the Blocker/Major line, not the - total. A curve of totals and Blockers alone cannot answer it, so the instrument would not measure - the thing it exists to measure. **Subject categories are deliberately not recorded**: they are a - judgement per finding rather than a count, and the findings files carry the material for anyone - who wants them. +- **Records Majors as well as Findings and Blockers**, because the question this curve is kept for + is whether consequence-keyed severity demotes findings — which moves the Blocker/Major line, not + the total. **Subject categories are deliberately not recorded**: they are a judgement per finding + rather than a count, and the findings files carry the material for anyone who wants it. + +**What the curve makes answerable, and what it does not — because the pre-rule baseline is thinner +than the forward record.** Going forward, post-rule cycles carry Findings, Blockers and Majors per +pass, so **demotion is comparable across them**. **The `fic2` baseline does not support that +comparison**: its closing commit records only totals and Blockers, the committed field report +supplies Majors for some passes and not others, and the per-finding subject material lives only in +gitignored findings files. So the baseline supports a **total-volume** comparison and nothing +finer, and any demotion claim rests on **post-rule cycles compared with each other**. Saying this +here keeps a later reader from computing a demotion figure the baseline cannot bear. - Carries that cycle's **cycle field**, so a curve can be attributed to the cycle that produced it — **the nonce for a post-rule cycle, `none (pre-rule)` for one that began before the rules shipped.** A pre-rule record is **not attributable to a cycle**, and the text says so rather than @@ -277,8 +284,9 @@ adds the decline record to the same passage; extending is required, replacing wo Both shipped records carry a **cycle field**, because a record that cannot be attributed to a cycle is unusable by the measurement that reads it. **§5 defines three cycles — the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle — so a run of all three produces three cycle fields, and for -post-rule cycles three distinct nonces.** A **pre-rule** cycle has no nonce (§8); its field is the -reserved `none (pre-rule)` and its records are not cycle-attributable. Everything below describes +post-rule cycles three distinct nonces.** A **pre-rule** cycle has no nonce — §10's activation rule is what makes a cycle pre-rule; its field is the +reserved `none (pre-rule)` and its records are not cycle-attributable; the activation rule that +creates that case is §10's. Everything below describes **post-rule cycles**, which is every cycle started after the implementation commit. - Generated once at cycle start, immutable, and **collision-resistant operationally: at least 8 @@ -398,8 +406,10 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). - **Parity across every changed rule**, in both copies, since they already diverge and parity cannot be asserted from a whole-section diff. - **A fresh twelve-item `docs/prompt-standards.md` pass.** Invariant 11 binds **each changed - prompt artifact as a complete prompt**, not the diff — so the items are read against the - resulting `CLAUDE.md` and the resulting scaffolded template, with the changed regions as the + prompt artifact as a complete prompt**, not the diff — so the items are read against **each changed prompt artifact**: the resulting root `CLAUDE.md`, + the resulting scaffolded template, **and `plugins/dev-workflow/commands/workflow-init.md` as the + outer command prompt** — which this change edits and which carries its own target-model + declaration separate from the template it writes, with the changed regions as the reason the pass is owed rather than its scope. Item 7's whole-artifact reading is the clearest case, not the only one. **One known failure, fixed rather than routed.** Neither resulting prompt carries a @@ -482,5 +492,8 @@ reconciliation of the two copies' divergence beyond §7's one seam. *stated* floor the profiles do not license. - **The reachability test needs judgement** where §5 is trying to remove it; §3 says so. - **The curve is self-reported**; P8 inherits that limit. -- **The expected demotion is a prediction**; P8 measures it. If it demotes far less than hoped, the - rule is still correct and the economics claim was what was wrong. +- **The expected demotion is a prediction**, and P8 is where it is checked. **What P8 can produce + is bounded by what it reads**: a self-reported curve nothing validates against the findings files. + So P8 reports a **comparison of recorded values** with that limit stated, not a measurement in any + stronger sense. If the recorded values show far less demotion than hoped, the rule is still + correct and the economics claim was what was wrong. diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 6ec8ca3..b4178c9 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -161,7 +161,9 @@ would separate a rule from the argument that settles it. pass, rather than merging two revisions into a single entry. Checkable: the requirement is stated in both copies, and this story's own commits carry it for each cycle that ran — **every field of the pinned form except the identifier, which no cycle on this branch can - supply** (see the identifier criterion). + supply.** Those records carry the reserved pre-rule value, which by construction **does not + name a cycle**; what the branch demonstrates is the field's presence and grammar, not + attribution (see the identifier criterion). **All three, not Gate B alone** (revised 2026-08-28 after Gate-A pass 2). A Gate-B-only requirement would leave the *dominant* cost unmeasured: the loops this story cites as evidence are Gate-A loops — nineteen measured Gate-A passes on one spec, and this story's @@ -226,8 +228,10 @@ would separate a rule from the argument that settles it. **What this branch can and cannot demonstrate, stated rather than assumed:** its three cycles all began before these rules ship, so each closing body carries the reserved pre-rule cycle field and **none of them demonstrates a real identifier**. The branch demonstrates the field's - presence and grammar; **a named later checkpoint — the first cycle started after the - implementation commit — demonstrates a real one and the attribution it buys.** + presence and grammar; **a named later checkpoint demonstrates a real one and the attribution it + buys: the Gate-A spec cycle of the next story whose spec is written after the implementation + commit lands.** Naming a cycle type and an artifact matters because siblings can start + concurrently and "first" does not order them. - [ ] **The shipped text says when it starts binding, and what an adopter gets when it does not fully arrive.** Both copies state: that a loop already running finishes under the rules it started with; what a loop does when its starting rules cannot be established, covering From 7db381c2ca8d877964317ec03077fb39ff89144a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:29:08 +0200 Subject: [PATCH 038/117] =?UTF-8?q?docs:=20revision=2023=20=E2=80=94=20com?= =?UTF-8?q?parable=20mixes=20are=20not=20measured=20demotion,=20and=20a=20?= =?UTF-8?q?prerequisite=20that=20was=20not=20owed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 21: 9 findings, 0 BLOCKER, 3 Blocker/Major. Blocker/Major across twenty-one passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3. Four consecutive zero-Blocker passes. The Major that matters is an overclaim I made while correcting an overclaim. Revision 22 said forward post-rule cycles make DEMOTION comparable. They do not: demotion is what happens to ONE FINDING under two classifications, and nothing in the durable record classifies a finding both ways. Comparing severity mixes across cycles that reviewed different artifacts is evidence about the population, not about the rule. So the spec now says what P8 can actually report -- a change in recorded mixes, with the confound named -- and that a demotion figure would need a paired classification the record does not carry. And a prerequisite this change did not owe. Since revision 1 the spec claimed the scaffolded template lacks the rationale the severity kinship points at, and required adding it. The template already carries the principle -- "Those describe the product rather than being it, so they carry no gate at all" -- and the kinship rests on that describe-versus-be distinction, not on CLAUDE.md's longer cost clause. The copies differ in how they phrase the COST, which the kinship does not depend on. So no seam is opened and the divergence is untouched beyond the rules this change actually edits. Twenty-one passes carried that unexamined because every check for it grepped one exact phrase rather than the claim. Also corrected: my own statement of the fic2 baseline's limits was itself too strong -- the baseline carries the complete per-pass totals AND Blocker series, lacking only Majors for some passes, so it supports more comparison than I said. The unknown-start fallback listed four parts and this change touches five; the provenance-line duty was the one missing. `` accepted control characters two lines above the rule saying such identifiers are unrepresentable. `` permitted the same path twice with conflicting levels. Story: the branch's provenance demonstration now says what it does not do -- name its cycle. Successor: it cited the fic2 field report as the record of the settled decisions, but that document is where the QUESTIONS were parked and states Q3-Q5 as unanswered; the commit bodies are the record. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-21. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3. Gate B: N/A -- all staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 40 +++++++++++++------ ...-review-loop-economics-pass-floor-story.md | 6 ++- ...026-08-29-loop-rule-consolidation-story.md | 12 ++++-- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 6ee8e2a..955ccd0 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 22 (rules only) · **Gate-A passes 1-20** +**Date:** 2026-08-29 · **Revision:** 23 (rules only) · **Gate-A passes 1-21** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -97,6 +97,8 @@ whether anything but a person parses it.) := [a-z0-9]{8,16} := [1-9][0-9]* := "none" | "{" ("," )* "}" + each appears at most once; a repeated path, + with or without conflicting levels, is malformed := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" := | := [A-Za-z0-9._/-]+ contains no delimiter, quote or whitespace @@ -217,7 +219,9 @@ reason as the provenance line:** := | ("; " )* := "pass "

" " ("+" )* := | | "undetermined" - := [^ ;:,()+"]+ excludes the grammar's own delimiters, "+" included + := [!-~]{1,} minus ; : , ( ) + " and space + printable ASCII only; a control character makes the + identifier unrepresentable, handled below := a double-quoted string, same two escapes as ; a reported identifier containing a control character is written `undetermined`, with the raw value in prose @@ -230,7 +234,7 @@ of two contributing models is the same loss as recording none. A skipped cycle writes `; : skipped (see skip reason)` and no counts. `cycle none (pre-rule)` is the **only** admissible alternative to a nonce, reserved for a cycle -that began before the nonce rule shipped (§8) — it is a production of the grammar rather than a +that began before the nonce rule shipped (§10 decides which cycles those are) — it is a production of the grammar rather than a magic string beside it, so the one-form claim holds and a parser needs no special case. The properties the form exists to satisfy: @@ -240,12 +244,17 @@ properties the form exists to satisfy: rather than a count, and the findings files carry the material for anyone who wants it. **What the curve makes answerable, and what it does not — because the pre-rule baseline is thinner -than the forward record.** Going forward, post-rule cycles carry Findings, Blockers and Majors per -pass, so **demotion is comparable across them**. **The `fic2` baseline does not support that -comparison**: its closing commit records only totals and Blockers, the committed field report -supplies Majors for some passes and not others, and the per-finding subject material lives only in -gitignored findings files. So the baseline supports a **total-volume** comparison and nothing -finer, and any demotion claim rests on **post-rule cycles compared with each other**. Saying this +than the forward record.** Going forward, post-rule cycles carry Findings, Blockers and Majors per pass, so **their recorded +severity mixes are comparable**. **That is not the same as measuring demotion**, and the difference +matters: demotion is what happens to *one finding* under two classifications, and nothing here +records a finding classified both ways. Comparing mixes across cycles that reviewed different +artifacts is evidence about the population, not about the rule. **A demotion figure would need a +paired classification the durable record does not carry**, so what P8 can report is a change in +recorded mixes with the confound named. **The `fic2` baseline is thinner in one specific respect**: its closing commit and the committed +field report both carry the complete per-pass **totals and Blocker series**, but **Majors only for +some passes**, and no per-finding subject material outside gitignored files. So the baseline +supports **total-volume and Blocker comparison across all its passes** and a **Major comparison +only over the passes that recorded them** — which the report must say when it uses them. Saying this here keeps a later reader from computing a demotion figure the baseline cannot bear. - Carries that cycle's **cycle field**, so a curve can be attributed to the cycle that produced it — **the nonce for a post-rule cycle, `none (pre-rule)` for one that began before the rules @@ -353,9 +362,13 @@ diverge substantially and one accounting cannot cover both. ## 7. Rollout, and what this change falsifies -**The template lacks the sentence §3's kinship points at** — the prose-exemption rationale is in -`CLAUDE.md` and absent from the template. **The template gets it**: a one-seam reduction of the -divergence, because the new rule depends on it, not a general reconciliation. +**No prerequisite is owed here, contrary to earlier revisions.** They claimed the template lacked +the rationale §3's kinship points at and required adding it. **The template already carries the +principle** — "Those describe the product rather than being it, so they carry no gate at all" — +and the kinship claim rests on that describe-versus-be distinction, not on `CLAUDE.md`'s longer +cost clause. What the copies differ in is the *phrasing of the cost*, which the kinship does not +depend on. **So no seam is opened**, and the divergence between the copies is untouched by this +change beyond the rules it actually edits. **Statements this change falsifies must be corrected in the same change** — in `README.md`, `docs/getting-started.md` and `docs/coding-workflow.md`, wherever they assert a fixed three-pass @@ -468,7 +481,8 @@ reconciliation of the two copies' divergence beyond §7's one seam. - **When these rules bind.** From the commit that ships them, and **a cycle already running finishes under the rules it started with**. **Where a cycle's starting rules cannot be established it takes the stricter reading of every part this change touches** — floor 3, severity - classified without the demotion, the curve duty owed, and the nonce duties at their strictest. Not + classified without the demotion, **the provenance-line duty owed**, the curve duty owed, and the + nonce duties at their strictest — five parts, matching the five this change touches. Not a re-derivation, which could hand a level-0 cycle a floor of 1 and *skip* passes on the strength of not knowing when it started. A user knob set above 3 is not lowered by this fallback. *(The successor extends this list to the rules it ships; extending is safe, replacing is not.)* diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index b4178c9..cc096ca 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -257,8 +257,10 @@ would separate a rule from the argument that settles it. - [ ] **The provenance path is demonstrated end to end on this branch, at the floor this branch actually licenses.** This story is risk `high`, so every cycle citing it owes floor **3** — and its closing commit body carries a provenance line **in the one pinned form**, - naming the cycle, that floor, and this story at its level, showing the - derivation and the provenance line working at a real value. + naming the cycle, that floor, and this story at its level, showing the derivation and the + provenance line working at a real value. **What that line does not do is name its cycle** — + every cycle on this branch predates the identifier rule, so its cycle field carries the + reserved pre-rule value, which by construction attributes nothing. **The floor-1 demonstration is not on this branch, deliberately.** It was in an earlier draft and was unsatisfiable: a criterion demanding a floor-1 cycle here contradicts this story's own profile, and the only way to satisfy it as written would have been to mint a diff --git a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md index b11f99d..ff29946 100644 --- a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md +++ b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md @@ -79,8 +79,12 @@ which are preconditions; and what a user's answer on a surfaced finding does in ## 4. Settled inputs — decided, paid for, and not to be reopened -Each was confirmed by Daniel during the parent cycle and is recorded in that cycle's commit bodies -and in `docs/field-reports/2026-08-26-fic2-cycle-evidence.md`. **The design begins from these.** +Each was confirmed by Daniel during the parent cycle. **The durable record is that cycle's commit +bodies**, which carry every one of them. `docs/field-reports/2026-08-26-fic2-cycle-evidence.md` is +where the *questions* were parked, not where they were answered — it states Q3–Q5 as unanswered and +gives Q6 only as candidate answers, because it was written before the answers existed. Citing it as +the source of the decisions would send a reader to a document that predates them. **The design +begins from the table below.** | # | Decision | |---|---| @@ -111,8 +115,8 @@ Both changes rewrite these, and **this story must extend rather than replace wha loop's skip record to what a squash must carry. **This story adds the decline record**, and a rewrite that drops the parent's three would silently unship them. - **The unknown-start fallback** (what a loop does when its starting rules cannot be established). - The parent covers the floor, severity, the curve and the nonce duties at their strictest. **This - story extends the same list to the loop rules it ships** — suspensions binding, decline records + The parent covers the floor, severity, the curve and the nonce duties at their strictest. **This story extends + the same list to the loop rules it ships** — suspensions binding, decline records treated as absent so no hold is released. Extending is safe; replacing is not. A third, for the same reason: the **clearly-stuck paragraph** is rewritten by both — by the parent From 68cca70b145d86af53b4fd8bfb56ed4da9634911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:42:56 +0200 Subject: [PATCH 039/117] =?UTF-8?q?docs:=20revision=2024=20=E2=80=94=20pro?= =?UTF-8?q?pagate=20the=20demotion=20correction=20to=20every=20site=20that?= =?UTF-8?q?=20made=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 22: 4 findings, 0 BLOCKER, 3 Blocker/Major. Blocker/Major across twenty-two passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3. Five consecutive zero-Blocker passes. Zero tells. All three Majors were one correction that landed in one place and not the others -- the loop's own signature failure, arriving one last time. Revision 23 established in §4 that comparable severity MIXES are not measured demotion, and left §3 routing "the amount" to P8, §10 saying P8 checks the expected demotion, the parent story calling it an economic measurement, and the P8 story asking for a demotion figure. This time I enumerated every site carrying the claim across all four artifacts before editing, rather than fixing where the finding pointed. What every copy now says: P8 reports a change in RECORDED SEVERITY MIXES with two confounds named -- the curves are self-reported and unvalidated, and the cycles compared reviewed DIFFERENT ARTIFACTS, so a difference is evidence about the population as much as about the rule. No demotion figure is derivable, because that needs one finding classified under both rules and nothing records that. The inference from a shifted mix is the reader's to make with the confound in view, not P8's to assert. Criterion 11 contradicted itself within three lines -- requiring the pre-rule provenance line to name its cycle and then saying it names nothing. What the branch shows is the line's derivation, grammar and every other field; attribution is demonstrated at the named checkpoint. The successor's inherited fallback list still had four parts after the parent went to five; the provenance-line duty was again the one missing. And §9 still excluded reconciliation "beyond §7's one seam" after §7 withdrew that seam -- this change now touches the copies' divergence not at all. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-22. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3. Gate B: N/A -- all staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 25 +++++++++++-------- ...4-passive-metrics-over-the-ledger-story.md | 11 +++++--- ...-review-loop-economics-pass-floor-story.md | 13 ++++++---- ...026-08-29-loop-rule-consolidation-story.md | 3 ++- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 955ccd0..b696573 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 23 (rules only) · **Gate-A passes 1-21** +**Date:** 2026-08-29 · **Revision:** 24 (rules only) · **Gate-A passes 1-22** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -195,8 +195,10 @@ that *is* the product. **Expected effect, with its limit, disclosed rather than claimed.** Distributions like PR #23's should demote substantially, but **how much is not predictable** — that commit's own body describes harness defects the symmetric carve-out keeps. Ledger rows and story criteria keep their severity -because escalation and the assigned-fix-set rule read them. **The amount is a prediction, not a -measurement**, which is why the story routes it to P8. +because escalation and the assigned-fix-set rule read them. **The amount is a prediction, and it stays one**: §4 explains why the durable record cannot +measure demotion — no finding is classified both ways — so what the story routes to P8 is a +comparison of recorded severity mixes across cycles, with its confound named, not a measurement of +this rule's effect. --- @@ -471,8 +473,8 @@ index can change between the read and the commit. Hook code, including the reminder's own wording; gate-call observability; the pass-counter anomaly; the CodeRabbit plan-metadata contradiction; the fixture-per-predicate question; any remedy to the supersession convention; deprecating or repurposing the user-facing floor knob; teaching the hook -about profiles; the §5 loop-rule consolidation and everything its successor story owns; and general -reconciliation of the two copies' divergence beyond §7's one seam. +about profiles; the §5 loop-rule consolidation and everything its successor story owns; and general reconciliation of the two copies' divergence, +which this change does not touch at all (§7 withdrew the seam an earlier revision claimed to need). --- @@ -506,8 +508,11 @@ reconciliation of the two copies' divergence beyond §7's one seam. *stated* floor the profiles do not license. - **The reachability test needs judgement** where §5 is trying to remove it; §3 says so. - **The curve is self-reported**; P8 inherits that limit. -- **The expected demotion is a prediction**, and P8 is where it is checked. **What P8 can produce - is bounded by what it reads**: a self-reported curve nothing validates against the findings files. - So P8 reports a **comparison of recorded values** with that limit stated, not a measurement in any - stronger sense. If the recorded values show far less demotion than hoped, the rule is still - correct and the economics claim was what was wrong. +- **The expected demotion is a prediction, and it is not one this design makes checkable.** P8 + reads a **self-reported curve** nothing validates against the findings files, and those curves + come from **cycles reviewing different artifacts**, so a difference between them is evidence + about the population as much as about the rule. **What P8 can report is a change in recorded + severity mixes, with both limits named.** A demotion figure would need one finding classified + under both rules, which nothing here produces. If the recorded mixes shift far less than hoped, + the rule may still be correct and the economics claim was what was wrong — and that inference is + the reader's to make with the confound in view, not P8's to assert. 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 index 0c274ec..4bfb5d9 100644 --- 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 @@ -75,9 +75,14 @@ from the file rather than from recall — without the analysis writing anything - [ ] What the analysis cannot answer from the ledger alone is stated, rather than left for a reader to infer from what it does answer. - [ ] **The review-loop question routed here is answerable, or its gap is named.** The - review-loop-economics story defers its economic measurement to this one: after roughly three - profiled cycles under the new rules, read their pass counts and finding distributions against - the `fic2` baseline. That analysis reads **two pinned commit-body forms** — the provenance + review-loop-economics story defers to this one a **comparison, not a measurement of its + rule's effect**: after roughly three profiled cycles under the new rules, compare their + recorded severity mixes with the `fic2` baseline — which carries complete per-pass totals and + Blockers and **Majors for only some passes**. **Two confounds must be named wherever a figure + is reported**: the curves are self-reported and unvalidated, and the cycles being compared + reviewed **different artifacts**, so a difference is evidence about the population as much as + about the rule. **No demotion figure is derivable** — that would need one finding classified + under both rules, which nothing records. That analysis reads **two pinned commit-body forms** — the provenance line and the per-pass curve, both specified in `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` — and its **first checkpoint** is the first post-merge cycle whose cited set licenses floor 1, which must carry diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index cc096ca..dd00776 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -95,7 +95,8 @@ would separate a rule from the argument that settles it. worth. The evidence doc carries it as an open question, not a commitment. - **Any remedy to the supersession convention** the worked example above illustrates. Parked as a `todos.md` candidate. -- **The economic measurement itself** — whether loops actually got cheaper. It cannot be +- **Whether loops actually got cheaper** — and the honest form of that question, which is narrower + than "measure the rule's effect". It cannot be observed inside this cycle: the severity rule's effect appears only across several cycles run under it, and claiming otherwise from a single cycle would be the fabricated-evidence failure §5 names. It is **deferred to a named vehicle rather than to a new backlog row** — @@ -170,7 +171,7 @@ would separate a rule from the argument that settles it. own Gate-A run — so P8 without Gate-A curves cannot measure the thing the problem statement is about. **Why it is in scope** (approved as a scope addition, Daniel, 2026-08-28): the deferred - economics measurement routed to P8 is otherwise answerable only for cycles whose author + economics comparison routed to P8 is otherwise answerable only for cycles whose author happened to write the curve down — `3cdd075` and `baa75c1` did, `7bbdb14` recorded the pass total and no distribution — because the findings files behind those numbers live under gitignored `.context/`. It is also the durable half of Q6: a curve in a commit body @@ -258,9 +259,11 @@ would separate a rule from the argument that settles it. branch actually licenses.** This story is risk `high`, so every cycle citing it owes floor **3** — and its closing commit body carries a provenance line **in the one pinned form**, naming the cycle, that floor, and this story at its level, showing the derivation and the - provenance line working at a real value. **What that line does not do is name its cycle** — - every cycle on this branch predates the identifier rule, so its cycle field carries the - reserved pre-rule value, which by construction attributes nothing. + provenance line working at a real value. **The one field it cannot demonstrate is the cycle + identifier**: every cycle on this branch predates the identifier rule, so each carries the + reserved pre-rule value, which by construction attributes nothing. What this branch shows is + the line's derivation, its grammar and every other field; attribution is demonstrated at the + checkpoint named in the identifier criterion. **The floor-1 demonstration is not on this branch, deliberately.** It was in an earlier draft and was unsatisfiable: a criterion demanding a floor-1 cycle here contradicts this story's own profile, and the only way to satisfy it as written would have been to mint a diff --git a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md index ff29946..ce26909 100644 --- a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md +++ b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md @@ -115,7 +115,8 @@ Both changes rewrite these, and **this story must extend rather than replace wha loop's skip record to what a squash must carry. **This story adds the decline record**, and a rewrite that drops the parent's three would silently unship them. - **The unknown-start fallback** (what a loop does when its starting rules cannot be established). - The parent covers the floor, severity, the curve and the nonce duties at their strictest. **This story extends + The parent covers five parts at their strictest — floor, severity, the provenance line, the curve, + and the nonce duties. **This story extends the same list to the loop rules it ships** — suspensions binding, decline records treated as absent so no hold is released. Extending is safe; replacing is not. From 89fb693b24ffd6d47c05a46d5e86667684e67f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:54:28 +0200 Subject: [PATCH 040/117] =?UTF-8?q?docs:=20revision=2025=20=E2=80=94=20the?= =?UTF-8?q?=20same=20correction,=20this=20time=20everywhere=20it=20lives?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 23: 2 findings, 0 BLOCKER, 2 Blocker/Major. Blocker/Major across twenty-three passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2. Six consecutive zero-Blocker passes. Both findings were the same two I "fixed" last revision. I patched where each finding pointed, said in the commit body that I had enumerated every site first, and had not: pass 23 found the claim still standing in the spec's own curve properties, in the story's scope note, in two of its criteria, and in the P8 story's opening. Saying you searched is not searching, which is this cycle's recurring lesson arriving one more time. This revision read every affected region in full before editing and rewrote all seven together. What every artifact now says: the curve records Majors because the severity rule moves the Blocker/Major line rather than the total, so a record of totals alone could not show even a change in the MIX -- and a change in the mix is all that is derivable, because no finding is ever classified under both rules. No demotion figure exists. What P8 reports is a comparison of recorded mixes across cycles that reviewed different artifacts, with that confound and the self-reported-curve confound both named. Criterion 11 still opened by saying the provenance line names the cycle and then denied it three lines later; the naming clause is gone rather than qualified. "Unmeasured" became "unrecorded" where the point was that a Gate-B-only rule would leave the larger half of the record missing, not that measurement was possible. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-23. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2. Gate B: N/A -- all staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 9 ++-- ...4-passive-metrics-over-the-ledger-story.md | 15 ++++--- ...-review-loop-economics-pass-floor-story.md | 41 ++++++++++--------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index b696573..182b5fc 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 24 (rules only) · **Gate-A passes 1-22** +**Date:** 2026-08-29 · **Revision:** 25 (rules only) · **Gate-A passes 1-23** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -240,9 +240,10 @@ that began before the nonce rule shipped (§10 decides which cycles those are) magic string beside it, so the one-form claim holds and a parser needs no special case. The properties the form exists to satisfy: -- **Records Majors as well as Findings and Blockers**, because the question this curve is kept for - is whether consequence-keyed severity demotes findings — which moves the Blocker/Major line, not - the total. **Subject categories are deliberately not recorded**: they are a judgement per finding +- **Records Majors as well as Findings and Blockers**, because the severity rule moves the + Blocker/Major line rather than the total, so a record of totals and Blockers alone could not show + even a change in the mix. (What that record supports, and what it cannot, is stated below — it is + a comparison, not a measurement of the rule's effect.) **Subject categories are deliberately not recorded**: they are a judgement per finding rather than a count, and the findings files carry the material for anyone who wants it. **What the curve makes answerable, and what it does not — because the pre-rule baseline is thinner 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 index 4bfb5d9..927bc71 100644 --- 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 @@ -33,12 +33,15 @@ From `todos.md`, "**P8 — passive metrics, read-only over the ledger and git.** ### Second question routed here, added 2026-08-28 (updated 2026-08-29: the forms it reads are now pinned) -`docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` defers its -economic measurement to this story rather than to a fresh backlog row. The question it hands -over: **after roughly three profiled cycles run under the new floor and severity rules, do their -pass counts and finding distributions differ from the `fic2` baseline curve of -14 · 24 · 12 · 3 · 6 · 6 · 2** (`docs/field-reports/2026-08-26-fic2-cycle-evidence.md`)? That -story cannot answer it inside its own cycle, and a single cycle would not answer it in any case. +`docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` defers to this story, +rather than to a fresh backlog row, a **comparison** — not a measurement of its rule's effect. The +question it hands over: **after roughly three profiled cycles run under the new floor and severity +rules, do their recorded severity mixes differ from the `fic2` baseline curve of +14 · 24 · 12 · 3 · 6 · 6 · 2** (`docs/field-reports/2026-08-26-fic2-cycle-evidence.md`)? That story +cannot answer it inside its own cycle, and a single cycle would not answer it in any case. **Nor +can any number of cycles yield a demotion figure**: that needs one finding classified under both +rules, which nothing records — see acceptance criterion 5 for the two confounds any reported figure +must carry. Two things this hand-off does not silently assume, because the row's "the data already exists" condition is doing real work: diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index dd00776..5e22e57 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -95,15 +95,17 @@ would separate a rule from the argument that settles it. worth. The evidence doc carries it as an open question, not a commitment. - **Any remedy to the supersession convention** the worked example above illustrates. Parked as a `todos.md` candidate. -- **Whether loops actually got cheaper** — and the honest form of that question, which is narrower - than "measure the rule's effect". It cannot be - observed inside this cycle: the severity rule's effect appears only across several cycles - run under it, and claiming otherwise from a single cycle would be the fabricated-evidence - failure §5 names. It is **deferred to a named vehicle rather than to a new backlog row** — +- **Whether loops actually got cheaper.** The honest form of that question is narrower than + "measure the rule's effect", and the design says why: no finding is ever classified under both + the old and new rules, so **no demotion figure is derivable** and what a later reader can compare + is *recorded severity mixes across cycles that reviewed different artifacts* — evidence about the + population as much as about the rule. It certainly cannot be observed inside this cycle, and + claiming otherwise from one cycle would be the fabricated-evidence failure §5 names. It is + **deferred to a named vehicle rather than to a new backlog row** — `docs/superpowers/stories/2026-08-04-passive-metrics-over-the-ledger-story.md` (the P8 passive-metrics story, `todos.md:546`), annotated with this trigger: after roughly three - profiled cycles under the new rules, read their pass counts and finding distributions - against the `fic2` baseline curve of 14 · 24 · 12 · 3 · 6 · 6 · 2 + profiled cycles under the new rules, compare their recorded severity mixes against the `fic2` + baseline curve of 14 · 24 · 12 · 3 · 6 · 6 · 2, with both confounds named (`docs/field-reports/2026-08-26-fic2-cycle-evidence.md`). A named vehicle rather than a fresh row because loose deferred items rot here — the pass-counter anomaly, the fixture-per-predicate question and the durations row's missing control run are all still @@ -166,10 +168,10 @@ would separate a rule from the argument that settles it. name a cycle**; what the branch demonstrates is the field's presence and grammar, not attribution (see the identifier criterion). **All three, not Gate B alone** (revised 2026-08-28 after Gate-A pass 2). A Gate-B-only - requirement would leave the *dominant* cost unmeasured: the loops this story cites as + requirement would leave the *dominant* cost unrecorded: the loops this story cites as evidence are Gate-A loops — nineteen measured Gate-A passes on one spec, and this story's - own Gate-A run — so P8 without Gate-A curves cannot measure the thing the problem - statement is about. + own Gate-A run — so P8 without Gate-A curves would be comparing the smaller half of what the + problem statement is about. **Why it is in scope** (approved as a scope addition, Daniel, 2026-08-28): the deferred economics comparison routed to P8 is otherwise answerable only for cycles whose author happened to write the curve down — `3cdd075` and `baa75c1` did, `7bbdb14` recorded the @@ -258,24 +260,25 @@ would separate a rule from the argument that settles it. - [ ] **The provenance path is demonstrated end to end on this branch, at the floor this branch actually licenses.** This story is risk `high`, so every cycle citing it owes floor **3** — and its closing commit body carries a provenance line **in the one pinned form**, - naming the cycle, that floor, and this story at its level, showing the derivation and the - provenance line working at a real value. **The one field it cannot demonstrate is the cycle - identifier**: every cycle on this branch predates the identifier rule, so each carries the - reserved pre-rule value, which by construction attributes nothing. What this branch shows is - the line's derivation, its grammar and every other field; attribution is demonstrated at the - checkpoint named in the identifier criterion. + recording that floor and this story at its level, so the derivation and the line are shown + working at a real value. **The cycle field is the one it cannot demonstrate**: every cycle on + this branch predates the identifier rule, so each carries the reserved pre-rule value, which + by construction attributes nothing. This branch shows the line's derivation, its grammar and + every other field; **attribution is demonstrated at the checkpoint named in the identifier + criterion**, not here. **The floor-1 demonstration is not on this branch, deliberately.** It was in an earlier draft and was unsatisfiable: a criterion demanding a floor-1 cycle here contradicts this story's own profile, and the only way to satisfy it as written would have been to mint a level-0 micro-story for the purpose — a fixture built to make a criterion pass, which is the fabricated-evidence class §5 names. Instead the floor-1 case becomes the **first - checkpoint of the P8 measurement**: the first post-merge cycle whose cited-story set - licenses floor 1 must carry the floor-1 provenance line, and P8 reads it. + checkpoint of the P8 comparison**: the first post-merge cycle whose cited-story set licenses + floor 1 must carry the floor-1 provenance line, and P8 reads it. **What is demonstrated here stays bounded, and the wording forecloses reading it wider:** the derivation, the knob and the provenance path work. It is not evidence that review loops became cheaper. Reading a working mechanism as an improved outcome is the overclaim class AGENTS.md names as this repo's most persistent defect, and the economics are - measured afterwards by the follow-up named in §2. + compared afterwards by the follow-up named in §2 — compared, because §2 explains why no + measurement of the rule's effect is derivable from what the record carries. *(Revised 2026-08-28 after Gate-A pass 1 found the original unsatisfiable — sparring session, under Daniel's 2026-08-28 delegation; flagged to Daniel for final-version review because the provenance-demonstration criterion was his explicit choice. The falsifiability he chose is preserved by From 93d5d8f8cd1b192f93762364330b144be3d12b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:11:55 +0200 Subject: [PATCH 041/117] =?UTF-8?q?docs:=20revision=2026=20=E2=80=94=20a?= =?UTF-8?q?=20misstated=20commit,=20and=20a=20raw=20value=20a=20commit=20c?= =?UTF-8?q?annot=20carry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 24: 7 findings, 0 BLOCKER, 2 Blocker/Major, 5 MINOR. Blocker/Major across twenty-four passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2. Seven consecutive zero-Blocker passes. A factual error about a commit I had already cited many times. §4 said 3cdd075 and the field report "both carry the complete per-pass totals and Blocker series, but Majors only for some passes". 3cdd075 carries NO Major series at all -- only Findings and Blockers -- and the Majors come from the field report, for passes 1-5 of 7. Verified by reading both. So the baseline supports total-volume and Blocker comparison across all seven passes, a Major comparison over five, and nothing finer, and the report must say so when it uses the Major series. And a rule that cannot be obeyed. The grammar said a model identifier containing a control character is written `undetermined` with the raw value recorded in prose -- but a commit message cannot safely carry one, and NUL cannot appear in it at all. The body now records where the value came from and which bytes were rejected, described rather than embedded. Criterion 11 claimed the provenance path is demonstrated "end to end" while deferring attribution in its own next sentence, and the knob clause is not-applicable on a branch with no user knob. It now says what it is: demonstrated as far as this branch can, with BOTH out-of-reach fields named rather than one. Four consistency repairs: §4's opening and the curve criterion summarized the record as Findings and Blockers although the grammar mandates Majors and the comparison depends on them; §5's rationale said an unattributable record is unusable, which would disqualify the pre-rule fic2 baseline the design relies on -- attribution is what spares a LATER reader from knowing which cycle a record came from, not what makes a record usable; §3 required "its confound" where two are named; and the checkpoint demanded a nonce differing from any other cycle's where the rule can only require uniqueness among cycles open when it was generated. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-24. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 40 ++++++++++++------- ...-review-loop-economics-pass-floor-story.md | 11 +++-- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 182b5fc..8a47e7e 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 25 (rules only) · **Gate-A passes 1-23** +**Date:** 2026-08-29 · **Revision:** 26 (rules only) · **Gate-A passes 1-24** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -197,16 +197,18 @@ should demote substantially, but **how much is not predictable** — that commit harness defects the symmetric carve-out keeps. Ledger rows and story criteria keep their severity because escalation and the assigned-fix-set rule read them. **The amount is a prediction, and it stays one**: §4 explains why the durable record cannot measure demotion — no finding is classified both ways — so what the story routes to P8 is a -comparison of recorded severity mixes across cycles, with its confound named, not a measurement of -this rule's effect. +comparison of recorded severity mixes across cycles, with **both** confounds named (the curves are +self-reported and unvalidated; the cycles compared reviewed different artifacts), not a measurement +of this rule's effect. --- ## 4. The per-pass curve — required properties Each of the three cycles records its own per-pass finding and Blocker counts in its own commit -body, labelled with the cycle it describes. **Gate B alone would leave the dominant cost -unmeasured** — the loops this story cites as evidence are Gate-A loops. **Pinned here for the same +body, labelled with the cycle it describes — **Findings, Blockers and Majors per pass**, since the +severity rule moves the Blocker/Major line. **Gate B alone would leave the dominant cost +unrecorded** — the loops this story cites as evidence are Gate-A loops. **Pinned here for the same reason as the provenance line:** ``` @@ -225,8 +227,13 @@ reason as the provenance line:** printable ASCII only; a control character makes the identifier unrepresentable, handled below := a double-quoted string, same two escapes as ; - a reported identifier containing a control character - is written `undetermined`, with the raw value in prose + a reported identifier containing a control character is + written `undetermined` — and the raw value is NOT + reproduced anywhere in the body, since a commit message + cannot safely carry one (NUL cannot appear at all). + What the body records instead is where the value came + from and which bytes were rejected, described rather + than embedded ``` **`` keys must be exactly the passes `` expands to, each once, ascending** — a @@ -253,11 +260,12 @@ matters: demotion is what happens to *one finding* under two classifications, an records a finding classified both ways. Comparing mixes across cycles that reviewed different artifacts is evidence about the population, not about the rule. **A demotion figure would need a paired classification the durable record does not carry**, so what P8 can report is a change in -recorded mixes with the confound named. **The `fic2` baseline is thinner in one specific respect**: its closing commit and the committed -field report both carry the complete per-pass **totals and Blocker series**, but **Majors only for -some passes**, and no per-finding subject material outside gitignored files. So the baseline -supports **total-volume and Blocker comparison across all its passes** and a **Major comparison -only over the passes that recorded them** — which the report must say when it uses them. Saying this +recorded mixes with the confound named. **The `fic2` baseline is thinner, and precisely how matters.** Its closing commit `3cdd075` +carries the complete per-pass **totals and Blockers and no Major series at all**; the committed +field report adds **Majors for passes 1–5 only**, and neither carries per-finding subject material +outside gitignored files. So the baseline supports **total-volume and Blocker comparison across all +seven passes**, a **Major comparison over five of them**, and nothing finer — which the report must +state whenever it uses the Major series. Saying this here keeps a later reader from computing a demotion figure the baseline cannot bear. - Carries that cycle's **cycle field**, so a curve can be attributed to the cycle that produced it — **the nonce for a post-rule cycle, `none (pre-rule)` for one that began before the rules @@ -294,7 +302,10 @@ adds the decline record to the same passage; extending is required, replacing wo ## 5. The cycle nonce Both shipped records carry a **cycle field**, because a record that cannot be attributed to a cycle -is unusable by the measurement that reads it. **§5 defines three cycles — the Gate-A spec loop, the +cannot be told apart from another cycle's when several are read together. **That is a limitation, +not a disqualification** — the `fic2` baseline is entirely pre-rule and is used precisely because a +human knows which cycle it came from. What attribution buys is that a *later* reader does not have +to. **§5 defines three cycles — the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle — so a run of all three produces three cycle fields, and for post-rule cycles three distinct nonces.** A **pre-rule** cycle has no nonce — §10's activation rule is what makes a cycle pre-rule; its field is the reserved `none (pre-rule)` and its records are not cycle-attributable; the activation rule that @@ -459,7 +470,8 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). whose spec is written after the implementation commit lands**. Naming a cycle type and an artifact rather than "the first cycle" matters because siblings can start concurrently and "first" has no total ordering across them. That cycle's provenance line and curve must carry a real nonce, - the two must carry the **same** one, and it must differ from any other cycle's. Recording that as a checkpoint rather than as a satisfied criterion + the two must carry the **same** one, and it must differ from that of **any cycle open at the time + it was generated** — which is the uniqueness the rule actually requires, and all it can check. Recording that as a checkpoint rather than as a satisfied criterion is the difference between a demonstration and a claim. **Revalidation.** §5 requires it before every re-review and before the closing amend. Verifications diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 5e22e57..44a2e87 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -152,7 +152,7 @@ would separate a rule from the argument that settles it. - [ ] **Every cycle's closing commit body carries its per-pass shape — all three cycle types.** Both copies require the **Gate-A spec loop** (in the spec's commit body), the **Gate-A plan loop** (in the plan's commit body) and the **Gate-B cycle** (in the closing amend) to - record that loop's per-pass finding and Blocker counts, in one pinned greppable form, + record that loop's per-pass **Findings, Blockers and Majors**, in one pinned greppable form, following the `3cdd075` precedent (`Findings 14, 24, 12, 3, 6, 6, 2. Blockers 3, 4, 0, 0, 0, 0, 0.`). The form is complete enough that a reader can tell **which pass each number belongs to** — incomplete passes are @@ -257,8 +257,11 @@ would separate a rule from the argument that settles it. - [ ] **The two copies stay in parity.** Every rule this story changes reads the same in `CLAUDE.md` §5 and in the `/workflow-init` template, except where a wording difference is deliberate and stated as such. Checkable by diffing the two regions. -- [ ] **The provenance path is demonstrated end to end on this branch, at the floor this - branch actually licenses.** This story is risk `high`, so every cycle citing it owes +- [ ] **The provenance path is demonstrated on this branch as far as this branch can demonstrate + it, at the floor it actually licenses.** Not "end to end": two fields are out of reach here + and the criterion names both rather than claiming coverage it lacks — the **cycle identifier** + (every cycle here predates the rule) and the **user-knob clause** (which is + not-applicable where no knob exists, per the design's conditional verification). This story is risk `high`, so every cycle citing it owes floor **3** — and its closing commit body carries a provenance line **in the one pinned form**, recording that floor and this story at its level, so the derivation and the line are shown working at a real value. **The cycle field is the one it cannot demonstrate**: every cycle on @@ -274,7 +277,7 @@ would separate a rule from the argument that settles it. checkpoint of the P8 comparison**: the first post-merge cycle whose cited-story set licenses floor 1 must carry the floor-1 provenance line, and P8 reads it. **What is demonstrated here stays bounded, and the wording forecloses reading it wider:** - the derivation, the knob and the provenance path work. It is not evidence that review + the derivation and the provenance path work. It is not evidence that review loops became cheaper. Reading a working mechanism as an improved outcome is the overclaim class AGENTS.md names as this repo's most persistent defect, and the economics are compared afterwards by the follow-up named in §2 — compared, because §2 explains why no From db39bceb2f4573883d998fe6e9c5aa8c547797ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:25:05 +0200 Subject: [PATCH 042/117] =?UTF-8?q?docs:=20revision=2027=20=E2=80=94=20a?= =?UTF-8?q?=20requirement=20that=20breaks=20CI,=20and=20a=20retracted=20in?= =?UTF-8?q?ference=20I=20reproduced?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 25: 5 findings, 0 BLOCKER, 3 Blocker/Major. Blocker/Major across twenty-five passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3. Eight consecutive zero-Blocker passes. Zero tells. THE CHANGE NOW HAS EXACTLY ONE OPEN QUESTION, and it is one I created two revisions ago by fixing something too confidently. Revision 18 required both resulting prompts to carry a `Target model:` line, over my earlier instinct to route it. Adding it to the SCAFFOLDED TEMPLATE breaks CI: scripts/check-invariants.sh counts `^Target model:` per claiming file and fails on anything but exactly one, workflow-init.md already has one at :9, and the template lives inside that same file. Indenting it inside the fence would indent it in every scaffolded CLAUDE.md. Three ways out, none of them this spec's to choose: widen the checker to count per scaffolded artifact rather than per file; spell the template's declaration so the checker does not match it, which weakens the check; or record the scaffolded CLAUDE.md as exempt from item 1 with the reason. Each touches scripts/check-invariants.sh or the invariant's reach, and no revision put either in scope. The plan does not proceed until it is decided. Root CLAUDE.md is probably not implicated at all -- invariant 11's list is skills, commands, agent definitions, hook messages and scaffolded templates. And I reproduced a retracted claim in my own problem statement. The story said "the substance converged and the number did not", which is exactly the inference docs/hardening-log.md retracted at entries 78, 84, 85 and 87, and which CLAUDE.md contradicts in the same paragraph the story cites -- neither curve measures coverage, so a low count can sit beside an unreviewed subsystem. The measurement is the numbers; the convergence reading is not part of it. This is the same defect I corrected in the brief that commissioned this story, arriving in the story itself. Three more: the branch demonstrates every field of both forms except TWO, not one -- the cycle identifier and the knob clause's non-absent form, which cannot be shown in a workspace with no knob; my absolute claim that neither historical source carries per-finding subject material is false of the field report, which classifies the pass-5 findings and quotes one verbatim; and both quoted productions admitted the empty string where a path or model identifier is required. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-25. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 40 ++++++++++++------- ...-review-loop-economics-pass-floor-story.md | 15 ++++--- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 8a47e7e..6885711 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 26 (rules only) · **Gate-A passes 1-24** +**Date:** 2026-08-29 · **Revision:** 27 (rules only) · **Gate-A passes 1-25** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -102,7 +102,7 @@ whether anything but a person parses it.) := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" := | := [A-Za-z0-9._/-]+ contains no delimiter, quote or whitespace - := a double-quoted string whose only escapes are \" and \\ ; + := a double-quoted string, non-empty, whose only escapes are \" and \\ ; a path containing a newline or other control character is NOT representable — the cycle stops and surfaces rather than emitting one @@ -226,7 +226,7 @@ reason as the provenance line:** := [!-~]{1,} minus ; : , ( ) + " and space printable ASCII only; a control character makes the identifier unrepresentable, handled below - := a double-quoted string, same two escapes as ; + := a non-empty double-quoted string, same two escapes as ; a reported identifier containing a control character is written `undetermined` — and the raw value is NOT reproduced anywhere in the body, since a commit message @@ -262,8 +262,9 @@ artifacts is evidence about the population, not about the rule. **A demotion fig paired classification the durable record does not carry**, so what P8 can report is a change in recorded mixes with the confound named. **The `fic2` baseline is thinner, and precisely how matters.** Its closing commit `3cdd075` carries the complete per-pass **totals and Blockers and no Major series at all**; the committed -field report adds **Majors for passes 1–5 only**, and neither carries per-finding subject material -outside gitignored files. So the baseline supports **total-volume and Blocker comparison across all +field report adds **Majors for passes 1–5 only**, and the field report carries **some** per-finding +subject material — it classifies the four pass-5 findings by subject, quotes one verbatim, and +gives aggregate pass-2 clusters — but not a per-finding classification across all seven passes. So the baseline supports **total-volume and Blocker comparison across all seven passes**, a **Major comparison over five of them**, and nothing finer — which the report must state whenever it uses the Major series. Saying this here keeps a later reader from computing a demotion figure the baseline cannot bear. @@ -439,13 +440,22 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). declaration separate from the template it writes, with the changed regions as the reason the pass is owed rather than its scope. Item 7's whole-artifact reading is the clearest case, not the only one. - **One known failure, fixed rather than routed.** Neither resulting prompt carries a - `Target model:` line, which item 1 requires — `workflow-init.md` names one for the outer command - and those bytes are not scaffolded. **Both resulting prompts get one.** An earlier revision - proposed routing it as out of scope, which cannot stand beside the sentence above: a change - cannot require each resulting prompt to pass all twelve items and knowingly leave one false. It - is one line per copy, in a file this change already edits, and the invariant that makes the pass - binding is the same invariant that makes the line required. + **One known failure, and it collides with a shipped check — this is the change's one open + question.** The **scaffolded template** is a "scaffolded template" in invariant 11's own list, so + item 1 binds it and it carries no `Target model:` line. But the template lives inside + `workflow-init.md`, whose own declaration sits at column 1 (`:9`), and + `scripts/check-invariants.sh` counts `^Target model:` in each claiming file and **fails on + anything but exactly one**. Adding the template's line makes two and breaks CI; indenting it + inside the fence would indent it in every scaffolded file. + **Three ways out, none of them this spec's to choose:** widen the checker to count declarations + per *scaffolded artifact* rather than per file; give the template a differently-spelled + declaration the checker does not match, which weakens the check's purpose; or record that the + scaffolded `CLAUDE.md` is exempt from item 1 with the reason. **The plan does not proceed on this + until it is decided**, and the decision touches `scripts/check-invariants.sh`, which no earlier + revision put in scope. + *(Root `CLAUDE.md` is a separate question and probably not one: invariant 11's list is skills, + commands, agent definitions, hook messages and scaffolded templates, and the root file is none of + those.)* - **This branch's own three closing bodies carry the final forms**, since two story criteria require it. **No reconstruction is needed and none is claimed:** the Gate-A spec cycle has not @@ -465,8 +475,10 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). `cycle none (pre-rule)`, which the grammar admits as a production. **The first cycle started after these rules ship carries a real one**, and the verification confirms that rather than pretending this branch demonstrates it. **The verification confirms all three bodies before closure**, and states exactly what they - prove: **every field of both pinned forms except the nonce**, which no cycle on this branch can - supply. **The nonce is verified at a named later checkpoint**: the **Gate-A spec cycle of the next story + prove: **every field of both pinned forms except two** — the **cycle identifier**, which no cycle + on this branch can supply, and the **knob clause's non-absent form**, which cannot be shown where + no user knob exists (the conditional verification above records that as not-applicable rather + than as satisfied). **The nonce is verified at a named later checkpoint**: the **Gate-A spec cycle of the next story whose spec is written after the implementation commit lands**. Naming a cycle type and an artifact rather than "the first cycle" matters because siblings can start concurrently and "first" has no total ordering across them. That cycle's provenance line and curve must carry a real nonce, diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 44a2e87..c3ba16f 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -14,7 +14,11 @@ Four measurements, each citable: - **Loops do not converge on count.** In the kit's heaviest consumer, one design spec past 2800 lines ran nineteen measured Gate-A passes; findings fell from 43 into a 2–19 range after pass 6 and never reached zero, while Blockers fell from 11 to 0–1 from pass 7 on - (`CLAUDE.md:157-159`). The substance converged and the number did not. + (`CLAUDE.md:157-159`). **What that shows is that the count did not converge — and not that the + substance did.** Reading a falling Blocker curve as convergence is an inference + `docs/hardening-log.md` retracted at entries 78, 84, 85 and 87, and `CLAUDE.md` says in the same + paragraph that neither curve measures coverage, so a low count can sit beside an unreviewed + subsystem. The measurement is the numbers; the convergence reading is not part of it. - **Severity lands on the instrument, not the product.** PR #23's closing commit records that "of 27 Blocker/Major findings, 16 were in the never-committed scratch harness, 10 in the design spec's narration, 1 in the plan" (`7bbdb14`, quoted at `todos.md:268-270`) — @@ -260,14 +264,15 @@ would separate a rule from the argument that settles it. - [ ] **The provenance path is demonstrated on this branch as far as this branch can demonstrate it, at the floor it actually licenses.** Not "end to end": two fields are out of reach here and the criterion names both rather than claiming coverage it lacks — the **cycle identifier** - (every cycle here predates the rule) and the **user-knob clause** (which is - not-applicable where no knob exists, per the design's conditional verification). This story is risk `high`, so every cycle citing it owes + (every cycle here predates the rule) and the **user-knob clause's non-absent form** (this + workspace has no knob, so the design's conditional verification records not-applicable rather + than satisfied). Every other field is shown. This story is risk `high`, so every cycle citing it owes floor **3** — and its closing commit body carries a provenance line **in the one pinned form**, recording that floor and this story at its level, so the derivation and the line are shown working at a real value. **The cycle field is the one it cannot demonstrate**: every cycle on this branch predates the identifier rule, so each carries the reserved pre-rule value, which - by construction attributes nothing. This branch shows the line's derivation, its grammar and - every other field; **attribution is demonstrated at the checkpoint named in the identifier + by construction attributes nothing. This branch shows the line's derivation, its grammar and its + remaining fields; **attribution is demonstrated at the checkpoint named in the identifier criterion**, not here. **The floor-1 demonstration is not on this branch, deliberately.** It was in an earlier draft and was unsatisfiable: a criterion demanding a floor-1 cycle here contradicts this From 754fe97b41e0fb61176310772a513df82fa8fa3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:27:04 +0200 Subject: [PATCH 043/117] =?UTF-8?q?docs:=20revision=2028=20=E2=80=94=20ite?= =?UTF-8?q?m=201=20answered=20n/a=20with=20reason;=20the=20last=20open=20q?= =?UTF-8?q?uestion=20closes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Target-model collision is decided (2026-08-29): item 1 is INAPPLICABLE to the scaffolded CLAUDE.md, recorded as a reasoned n/a. No checker edit, no dodge-spelling, no invariant narrowed. The decisive reason is truthfulness, and it reframes the collision. The scaffolded file is model-agnostic BY DESIGN -- its executing model is whatever the end user runs, unknowable when the template is written -- so a `Target model:` line written into it would be FALSE in every repo it lands in. Pass 16's Blocker said a change cannot knowingly leave an item false; the mirror of that rule is that a standard cannot require writing a falsehood. An n/a recorded with its reason ANSWERS item 1 rather than skipping it, which is the pattern AGENTS.md's own commands table already uses for typecheck. Three consequences, all clean: scripts/check-invariants.sh is untouched and stays out of scope, which option 1 would have violated; workflow-init.md keeps its single declaration for the outer command; and the implementation records the status in one sentence beside the template and OUTSIDE the fence, so it never scaffolds -- placed where a future template editor will meet it rather than in a spec they may never open. The story's conformance criterion now says prompt conformance is judged item by item and an item may be satisfied OR recorded n/a with its reason. What fails is an item left unanswered or answered falsely, not one answered "not applicable, because". That is encoding a precedent this repo already practices, not softening a standard. And the two form fields this branch cannot demonstrate are recorded as undemonstrable-here with reasons rather than as gaps or as satisfied -- the same honesty pattern, applied to evidence instead of to a checklist. Sparring session, under Daniel's 2026-08-28 delegation; flagged for his final-version review with the other story edits. Gate A: passes 1-25. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 37 +++++++++---------- ...-review-loop-economics-pass-floor-story.md | 4 ++ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 6885711..d0e5708 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 27 (rules only) · **Gate-A passes 1-25** +**Date:** 2026-08-29 · **Revision:** 28 (rules only) · **Gate-A passes 1-26** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -440,22 +440,19 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). declaration separate from the template it writes, with the changed regions as the reason the pass is owed rather than its scope. Item 7's whole-artifact reading is the clearest case, not the only one. - **One known failure, and it collides with a shipped check — this is the change's one open - question.** The **scaffolded template** is a "scaffolded template" in invariant 11's own list, so - item 1 binds it and it carries no `Target model:` line. But the template lives inside - `workflow-init.md`, whose own declaration sits at column 1 (`:9`), and - `scripts/check-invariants.sh` counts `^Target model:` in each claiming file and **fails on - anything but exactly one**. Adding the template's line makes two and breaks CI; indenting it - inside the fence would indent it in every scaffolded file. - **Three ways out, none of them this spec's to choose:** widen the checker to count declarations - per *scaffolded artifact* rather than per file; give the template a differently-spelled - declaration the checker does not match, which weakens the check's purpose; or record that the - scaffolded `CLAUDE.md` is exempt from item 1 with the reason. **The plan does not proceed on this - until it is decided**, and the decision touches `scripts/check-invariants.sh`, which no earlier - revision put in scope. - *(Root `CLAUDE.md` is a separate question and probably not one: invariant 11's list is skills, - commands, agent definitions, hook messages and scaffolded templates, and the root file is none of - those.)* + **Item 1 is inapplicable to the scaffolded `CLAUDE.md`, recorded as a reasoned n/a** (decided + 2026-08-29). The item asks a prompt to name its executing model. **The scaffolded file is + model-agnostic by design**: its executing model is whatever the end user runs, unknowable when + the template is written, so a `Target model:` line written into it would be **false in every + repo it lands in**. A standard cannot require writing a falsehood, and **an n/a recorded with its + reason answers the item rather than skipping it** — the pattern `AGENTS.md`'s own commands table + already uses ("typecheck: n/a — no typed sources"). The scaffolded artifact's class is *project + `CLAUDE.md`*, the same class as this repo's root file, which carries no declaration and correctly + so. + **Nothing else moves for this:** `scripts/check-invariants.sh` is untouched and stays out of + scope, `workflow-init.md` keeps its single declaration for the outer command, and the template + adds none. **The implementation records the status where a future template editor meets it** — one + sentence in `workflow-init.md` beside the template and outside the fence, so it never scaffolds. - **This branch's own three closing bodies carry the final forms**, since two story criteria require it. **No reconstruction is needed and none is claimed:** the Gate-A spec cycle has not @@ -474,8 +471,10 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). started with** (§10), so this one records its provenance line and curve with the cycle field written `cycle none (pre-rule)`, which the grammar admits as a production. **The first cycle started after these rules ship carries a real one**, and the verification confirms that rather than pretending this branch demonstrates it. - **The verification confirms all three bodies before closure**, and states exactly what they - prove: **every field of both pinned forms except two** — the **cycle identifier**, which no cycle + **The verification confirms all three bodies before closure**, and **records the two fields it + cannot demonstrate as undemonstrable-here with their reasons** rather than as gaps or as + satisfied — the same honesty as the item-1 n/a above. It states exactly what they prove: **every + field of both pinned forms except two** — the **cycle identifier**, which no cycle on this branch can supply, and the **knob clause's non-absent form**, which cannot be shown where no user knob exists (the conditional verification above records that as not-applicable rather than as satisfied). **The nonce is verified at a named later checkpoint**: the **Gate-A spec cycle of the next story diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index c3ba16f..08ac0a1 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -252,6 +252,10 @@ would separate a rule from the argument that settles it. manifest's version and `CHANGELOG.md` are updated, which CI enforces on pull requests. Both are checkable after the fact: no corrected sentence still asserts a fixed three-pass floor, and the manifest version differs from its value on the base ref. + **Prompt conformance is judged item by item, and an item may be satisfied *or recorded n/a + with its reason*** — the answer `AGENTS.md`'s own commands table already gives where an item + does not apply. What fails this criterion is an item left unanswered, or answered falsely; + not one answered "not applicable, because…". - [ ] **Every condition of the replaced prose is accounted for.** The change lists what each replaced §5 passage required and marks each requirement kept, moved, or deliberately dropped, per the AGENTS.md Don't quoted in §4. Checkable: the accounting exists and From 96b5ea2c56321f1f04766d568e9cf5805071cdb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:36:29 +0200 Subject: [PATCH 044/117] =?UTF-8?q?docs:=20revision=2029=20=E2=80=94=20sco?= =?UTF-8?q?pe=20the=20n/a,=20and=20two=20grammar=20ambiguities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 26: 4 findings, 0 BLOCKER, 3 Blocker/Major. Blocker/Major across twenty-six passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3. Nine consecutive zero-Blocker passes. The item-1 n/a was stated without a fence around it. The spec's supporting sentence endorsed the root CLAUDE.md carrying no declaration, and the story's criterion said ANY checklist item may be recorded n/a with a reason. Both read wider than what was decided. Now: the n/a is scoped to the scaffolded CLAUDE.md and to item 1, every other item still binds that artifact, and every item still binds the other two changed prompts. The root file is outside the reasoning entirely -- invariant 11 never named project CLAUDE.md files, so item 1 never bound it and this decision neither excuses nor endorses anything there. And the story's door is narrowed to what was actually argued: n/a requires a reason that ESTABLISHES INAPPLICABILITY -- the scaffolded file is model-agnostic, so the line would be false -- and "we would rather not" is not such a reason. Two grammar ambiguities. The nonce production caps at 16 while the generation rule said only "at least 8", so the two disagreed about what is legal; the rule now states 8 to 16 with the reason for each bound. And `undetermined` -- the reserved token for a model that could not be determined -- was also matched by ``, so nothing distinguished an unknown reviewer from a real model literally named that; the bare production now excludes it and a real model so named is written quoted. And one place still said the branch demonstrates every field except the nonce, five lines above the passage correctly naming two. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-26. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 25 +++++++++++++------ ...-review-loop-economics-pass-floor-story.md | 10 +++++--- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index d0e5708..66bbc87 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 28 (rules only) · **Gate-A passes 1-26** +**Date:** 2026-08-29 · **Revision:** 29 (rules only) · **Gate-A passes 1-26** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -223,7 +223,10 @@ reason as the provenance line:** := | ("; " )* := "pass "

" " ("+" )* := | | "undetermined" - := [!-~]{1,} minus ; : , ( ) + " and space + "undetermined" means the model could not be determined; + a real model so named is written as + := [!-~]{1,} minus ; : , ( ) + " and space, and not the + literal "undetermined", which is reserved printable ASCII only; a control character makes the identifier unrepresentable, handled below := a non-empty double-quoted string, same two escapes as ; @@ -313,8 +316,10 @@ reserved `none (pre-rule)` and its records are not cycle-attributable; the activ creates that case is §10's. Everything below describes **post-rule cycles**, which is every cycle started after the implementation commit. -- Generated once at cycle start, immutable, and **collision-resistant operationally: at least 8 - characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — never derived from a +- Generated once at cycle start, immutable, and **collision-resistant operationally: 8 to 16 + characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — the same bound the + grammar pins, 8 being where collision resistance starts and 16 where the field stops being a + usable infix — — never derived from a name, a timestamp or a commit, each of which collides exactly where sibling cycles do. - Constrained so it is **safe as a slot infix and a path component**. - **It appears in every record the cycle writes** — for every cycle started after these rules @@ -446,9 +451,11 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). the template is written, so a `Target model:` line written into it would be **false in every repo it lands in**. A standard cannot require writing a falsehood, and **an n/a recorded with its reason answers the item rather than skipping it** — the pattern `AGENTS.md`'s own commands table - already uses ("typecheck: n/a — no typed sources"). The scaffolded artifact's class is *project - `CLAUDE.md`*, the same class as this repo's root file, which carries no declaration and correctly - so. + already uses ("typecheck: n/a — no typed sources"). **This n/a is scoped to the scaffolded `CLAUDE.md` and to item 1**, and reaches nothing else: + every other item still binds it, and every item still binds the other two changed prompt + artifacts. The root `CLAUDE.md` is outside this reasoning entirely — invariant 11's list does not + name project `CLAUDE.md` files, so item 1 never bound it and this decision neither excuses nor + endorses anything about it. **Nothing else moves for this:** `scripts/check-invariants.sh` is untouched and stays out of scope, `workflow-init.md` keeps its single declaration for the outer command, and the template adds none. **The implementation records the status where a future template editor meets it** — one @@ -462,7 +469,9 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). §10 fixes that at the implementation commit — not at this spec's close.** So the Gate-A plan cycle and the Gate-B cycle also begin before the rules ship, and all three are pre-rule cycles by §10's own rule. **All three therefore write `cycle none (pre-rule)`**, and the branch demonstrates - every field of both forms except the nonce, which the first post-ship cycle demonstrates. Claiming + every field of both forms except the two it cannot — the cycle identifier, which the first + post-ship cycle demonstrates, and the knob clause's non-absent form, which needs a workspace that + has a knob. Claiming otherwise would be claiming a demonstration the branch cannot contain. **The nonce is the one field this branch cannot supply natively, and the activation rule governs it.** That cycle began before the nonce rule existed; its slot discriminator diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 08ac0a1..3b27a49 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -252,10 +252,12 @@ would separate a rule from the argument that settles it. manifest's version and `CHANGELOG.md` are updated, which CI enforces on pull requests. Both are checkable after the fact: no corrected sentence still asserts a fixed three-pass floor, and the manifest version differs from its value on the base ref. - **Prompt conformance is judged item by item, and an item may be satisfied *or recorded n/a - with its reason*** — the answer `AGENTS.md`'s own commands table already gives where an item - does not apply. What fails this criterion is an item left unanswered, or answered falsely; - not one answered "not applicable, because…". + **Prompt conformance is judged item by item.** An item is satisfied, **or recorded n/a with + a reason that shows the item cannot truthfully be met** — the answer `AGENTS.md`'s own + commands table already gives where an item does not apply. That is a narrow door, not a + general one: **the reason has to establish inapplicability**, and "we would rather not" is + not such a reason. What fails this criterion is an item left unanswered, answered falsely, or + answered n/a without a reason that holds. - [ ] **Every condition of the replaced prose is accounted for.** The change lists what each replaced §5 passage required and marks each requirement kept, moved, or deliberately dropped, per the AGENTS.md Don't quoted in §4. Checkable: the accounting exists and From 17d40372410462486137a74c479a10a32eb761c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:53:58 +0200 Subject: [PATCH 045/117] =?UTF-8?q?docs:=20revision=2030=20=E2=80=94=20the?= =?UTF-8?q?=20floor=20replaces=20a=20number,=20not=20the=20rules=20around?= =?UTF-8?q?=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 27: 6 findings, 0 BLOCKER, 5 Blocker/Major. Blocker/Major across twenty-seven passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5. Ten consecutive zero-Blocker passes. The one that would have narrowed §5 by accident: the precedence rule said the derived floor "controls whether a cycle may close", which reads as making the floor the only route. §5 has two others and this change touches neither -- a zero-finding pass still exits below the floor, and a legitimate Gate-B skip still removes the review entirely. The rule now says the floor replaces the NUMBER, not the rules around it. A change that quietly deletes two exits while claiming to adjust one is exactly the dropped-condition failure the accounting exists to catch, arriving in the sentence rather than the table. An overclaim about a gate, in the class this repo logs most: both spec and story said the manifest version and CHANGELOG entry are "enforced by CI" under invariant 12. Verified: neither invariant 12 nor scripts/check-version-bump.sh mentions the changelog at all -- the checker compares only the version string against the base ref. Only the bump is enforced; the changelog is convention, and the story's criterion is what carries it. "It appears in every record the cycle writes" never said which records. Now named: the provenance line, the curve (or a skip record standing in for one), and -- because §5's slot and companion rules already key them to a cycle -- the findings slots and the advisory working record. Not the evidence entry or a human-exception record, which this change neither introduces nor keys, and which are the successor's to consider. The item-1 n/a is scoped once more: it reaches one artifact and one item, every other item binds that artifact, and root CLAUDE.md is explicitly NOT ruled on -- whether invariant 11 reaches project CLAUDE.md files is a separate question this change neither raises nor answers. The story now records that this change makes exactly one n/a, rather than describing a general door. And the story's answered multi-story question still said "any other set yields 3", which swallowed the present-but-unresolvable case that stops and surfaces. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-27. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 39 +++++++++++++------ ...-review-loop-economics-pass-floor-story.md | 26 +++++++------ 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 66bbc87..45d20be 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 29 (rules only) · **Gate-A passes 1-26** +**Date:** 2026-08-29 · **Revision:** 30 (rules only) · **Gate-A passes 1-27** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -68,9 +68,15 @@ that flow only selects which advisory message fires, and the hook exits 0 on eve **Precedence, shipped as text:** -> **The derived floor controls whether a cycle may close. The hook's ratio is a reminder threshold -> and controls nothing.** Where the derived floor and the ordinary closure rules are satisfied, a -> below-threshold reminder is **noted in the pass report and disregarded.** +> **The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder threshold +> that controls nothing.** Where the cycle's own closure rules are satisfied, a below-threshold +> reminder is **noted in the pass report and disregarded.** +> +> **"The floor" here replaces the number, not the rules around it.** §5's existing exits are +> untouched: a **zero-finding pass** still exits below the floor, and a **legitimate Gate-B skip** +> still removes the review entirely, recording its skip and — per §4 — a skip line in place of a +> curve. This change makes the floor a function of the profile; it does not make meeting the floor +> the only way a cycle can close. **Named residual, disclosed in both copies:** the hook's messages state its own threshold as an obligation, so at level 0 they report a shortfall the cycle does not owe. **Hook text is out of @@ -319,11 +325,16 @@ creates that case is §10's. Everything below describes - Generated once at cycle start, immutable, and **collision-resistant operationally: 8 to 16 characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — the same bound the grammar pins, 8 being where collision resistance starts and 16 where the field stops being a - usable infix — — never derived from a + usable infix — never derived from a name, a timestamp or a commit, each of which collides exactly where sibling cycles do. - Constrained so it is **safe as a slot infix and a path component**. - **It appears in every record the cycle writes** — for every cycle started after these rules - ship. **A pre-rule cycle has no nonce and cannot acquire one**, so its records carry + ship — and **the record set is named rather than left open**: the **provenance line**, the + **per-pass curve** (including a skip record standing in for one), and, because §5's slot and + companion rules already key them to a cycle, the cycle's **findings slots** and its **advisory + working record**. It is not required in records this change neither introduces nor keys to a + cycle — the evidence entry and a human-exception record among them — which are the successor's + to consider if it needs them. **A pre-rule cycle has no nonce and cannot acquire one**, so its records carry `none (pre-rule)` in the cycle field and are, by construction, not cycle-attributable. That is a bounded, self-terminating exception: it applies only to cycles already running when the rules land, and no later cycle can enter the state. @@ -406,8 +417,12 @@ and states which.** re-initialized** projects. It does **not** update a downstream project's existing `CLAUDE.md` — invariant 9 forbids silent overwriting. Adoption is by re-running the scaffolder. -**Packaging.** Editing the template triggers invariant 12: a `plugin.json` version bump and a -`CHANGELOG.md` entry are in the implementation surface. +**Packaging.** Editing the template triggers invariant 12: a `plugin.json` **version bump**, which +`scripts/check-version-bump.sh` enforces on pull requests. A **`CHANGELOG.md` entry** is also in the +implementation surface, as this repo's convention for every manifest version — **but nothing +enforces it**: neither invariant 12 nor the checker mentions the changelog, and the checker verifies +only that the version string differs from the base ref's. Saying CI enforces both would be a claim +about a gate that does not make it. --- @@ -452,10 +467,10 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). repo it lands in**. A standard cannot require writing a falsehood, and **an n/a recorded with its reason answers the item rather than skipping it** — the pattern `AGENTS.md`'s own commands table already uses ("typecheck: n/a — no typed sources"). **This n/a is scoped to the scaffolded `CLAUDE.md` and to item 1**, and reaches nothing else: - every other item still binds it, and every item still binds the other two changed prompt - artifacts. The root `CLAUDE.md` is outside this reasoning entirely — invariant 11's list does not - name project `CLAUDE.md` files, so item 1 never bound it and this decision neither excuses nor - endorses anything about it. + every other item still binds that artifact, and every item still binds + `plugins/dev-workflow/commands/workflow-init.md` as the outer command prompt. **Root `CLAUDE.md` + is not part of this decision and is not being ruled on** — whether invariant 11 reaches project + `CLAUDE.md` files at all is a separate question this change neither raises nor answers. **Nothing else moves for this:** `scripts/check-invariants.sh` is untouched and stays out of scope, `workflow-init.md` keeps its single declaration for the outer command, and the template adds none. **The implementation records the status where a future template editor meets it** — one diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 3b27a49..53140cd 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -249,15 +249,17 @@ would separate a rule from the argument that settles it. - [ ] **The change leaves no shipped sentence contradicting it, and the package it ships in is valid.** Every user-facing statement this change falsifies is corrected in the same change — a reviewer can check each corrected line against the site list the plan carries — and the plugin - manifest's version and `CHANGELOG.md` are updated, which CI enforces on pull requests. Both - are checkable after the fact: no corrected sentence still asserts a fixed three-pass floor, - and the manifest version differs from its value on the base ref. - **Prompt conformance is judged item by item.** An item is satisfied, **or recorded n/a with - a reason that shows the item cannot truthfully be met** — the answer `AGENTS.md`'s own - commands table already gives where an item does not apply. That is a narrow door, not a - general one: **the reason has to establish inapplicability**, and "we would rather not" is - not such a reason. What fails this criterion is an item left unanswered, answered falsely, or - answered n/a without a reason that holds. + manifest's version and `CHANGELOG.md` are updated. Both are checkable after the fact: no + corrected sentence still asserts a fixed three-pass floor, and the manifest version differs + from its value on the base ref. **Only the version bump is CI-enforced** — the changelog + entry is this repo's convention and nothing checks it, so this criterion is what carries it. + **Prompt conformance is judged item by item, and this change records exactly one n/a**: + item 1 for the scaffolded `CLAUDE.md`, because that artifact is model-agnostic by design and + a target-model line would be false in every repo it lands in. **Every other item binds it, + and every item binds the outer command prompt.** An n/a is admissible only where the reason + **establishes that the item cannot truthfully be met** — the answer `AGENTS.md`'s commands + table already gives; "we would rather not" is not such a reason. What fails this criterion is + an item left unanswered, answered falsely, or answered n/a on a reason that does not hold. - [ ] **Every condition of the replaced prose is accounted for.** The change lists what each replaced §5 passage required and marks each requirement kept, moved, or deliberately dropped, per the AGENTS.md Don't quoted in §4. Checkable: the accounting exists and @@ -340,8 +342,10 @@ would separate a rule from the argument that settles it. ## 5. Open questions - ~~**Which floor governs a cycle citing several stories with different profiles?**~~ - **Answered 2026-08-28: unanimity.** Floor 1 only if every cited story is profiled and every - one is at level 0; any other set yields 3. It follows §5's own precedent for the analogous + **Answered 2026-08-28: unanimity.** Floor 1 only if the cited set is non-empty and every member + is profiled, resolvable and at level 0. A set with no story or any unprofiled member yields 3; + **a set containing a present-but-unresolvable profile stops and surfaces** rather than yielding + anything. It follows §5's own precedent for the analogous relaxation — "skip-eligible only if **every** cited story is" — and it is the only reading consistent with invariant 2's firing direction, since a lowest-cited-floor rule would under-review a cycle that also touches a high-risk story. From 78dd825b94ca7ef95cd43dc980862e4882e0869a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:11:15 +0200 Subject: [PATCH 046/117] =?UTF-8?q?docs:=20revision=2031=20=E2=80=94=20a?= =?UTF-8?q?=20checkpoint=20that=20names=20itself,=20and=20a=20scope=20I=20?= =?UTF-8?q?contradicted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 28: 6 findings, 0 BLOCKER, 4 Blocker/Major. Blocker/Major across twenty-eight passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4. Eleven consecutive zero-Blocker passes. Zero tells. Scoping the item-1 n/a last revision created a contradiction two sections away. §8 still required a complete twelve-item pass over root CLAUDE.md while §8 also said whether invariant 11 reaches that file is not ruled on -- so root item 1 had no disposition and could not get one. Resolved by reading invariant 11's list literally: the checklist runs against the scaffolded template and the outer command prompt, both of which the list names. Root CLAUDE.md is not in the list, this change does not run the checklist against it, and takes no position on whether it should be -- which is why no item of it needs a disposition. The nonce checkpoint could not be named in advance and I kept trying. "The Gate-A spec cycle of the next story" fails twice over: concurrent siblings have no total ordering, and a cycle running after a revert would be under the restored rules and owe no nonce at all. The obligation is now RECORDED at implementation and discharged by the first cycle that both starts under these rules and closes, with whoever closes it performing the check. Self-identifying rather than pre-assigned, which removes the ordering problem instead of restating it. Also: §2.4's closure statement was still categorical after §2.1 established that the floor replaces the number and not the other exits -- a zero-finding pass and a legitimate skip are unaffected by a moving profile because neither turns on the count. Story criterion 6 still said the identifier appears in "every record", without the named set the spec now defines. The story's scope note still called part 1 "policy over the knob", which the design specifically abandoned -- the floor is derived and stated, and the knob is neither read for the derivation nor written. And the AGENTS.md example was misquoted: its typecheck row reads "n/a -- no typed sources (shell + markdown)". Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-28. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 31 ++++++++++++------- ...-review-loop-economics-pass-floor-story.md | 18 ++++++----- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 45d20be..acd8e4a 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 30 (rules only) · **Gate-A passes 1-27** +**Date:** 2026-08-29 · **Revision:** 31 (rules only) · **Gate-A passes 1-28** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -136,7 +136,9 @@ The properties the grammar exists to satisfy: ### 2.4 A profile or cited set that moves mid-cycle Three existing rules compose; no new rule. The floor derives from the **current** profile at each -pass; **passes already run keep counting**; **closing requires the floor as currently derived.** +pass; **passes already run keep counting**; **closing requires the floor as currently derived** — where the floor is what +governs, which §2.1 notes is not every close: a zero-finding pass and a legitimate skip are +unaffected by a moving profile because neither turns on the count. **Any profile change costs at least one further pass**, in either direction and whether or not the floor number moves, because §5 already requires the **final clean pass** to run under the current @@ -454,10 +456,13 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). - **Parity across every changed rule**, in both copies, since they already diverge and parity cannot be asserted from a whole-section diff. - **A fresh twelve-item `docs/prompt-standards.md` pass.** Invariant 11 binds **each changed - prompt artifact as a complete prompt**, not the diff — so the items are read against **each changed prompt artifact**: the resulting root `CLAUDE.md`, - the resulting scaffolded template, **and `plugins/dev-workflow/commands/workflow-init.md` as the - outer command prompt** — which this change edits and which carries its own target-model - declaration separate from the template it writes, with the changed regions as the + prompt artifact as a complete prompt**, not the diff — so the items are read against **each changed prompt artifact invariant 11 names**: the resulting + **scaffolded template** and **`plugins/dev-workflow/commands/workflow-init.md`** as the outer + command prompt, which carries its own target-model declaration separate from the template it + writes. **Root `CLAUDE.md` is not in that list** — invariant 11 enumerates skills, commands, agent + definitions, hook messages and scaffolded templates — so this change does not run the checklist + against it and takes no position on whether it should be run; that question is untouched here, + which is why no item of it needs a disposition, with the changed regions as the reason the pass is owed rather than its scope. Item 7's whole-artifact reading is the clearest case, not the only one. **Item 1 is inapplicable to the scaffolded `CLAUDE.md`, recorded as a reasoned n/a** (decided @@ -466,7 +471,7 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). the template is written, so a `Target model:` line written into it would be **false in every repo it lands in**. A standard cannot require writing a falsehood, and **an n/a recorded with its reason answers the item rather than skipping it** — the pattern `AGENTS.md`'s own commands table - already uses ("typecheck: n/a — no typed sources"). **This n/a is scoped to the scaffolded `CLAUDE.md` and to item 1**, and reaches nothing else: + already uses (its typecheck row reads "n/a — no typed sources (shell + markdown)"). **This n/a is scoped to the scaffolded `CLAUDE.md` and to item 1**, and reaches nothing else: every other item still binds that artifact, and every item still binds `plugins/dev-workflow/commands/workflow-init.md` as the outer command prompt. **Root `CLAUDE.md` is not part of this decision and is not being ruled on** — whether invariant 11 reaches project @@ -501,10 +506,14 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). field of both pinned forms except two** — the **cycle identifier**, which no cycle on this branch can supply, and the **knob clause's non-absent form**, which cannot be shown where no user knob exists (the conditional verification above records that as not-applicable rather - than as satisfied). **The nonce is verified at a named later checkpoint**: the **Gate-A spec cycle of the next story - whose spec is written after the implementation commit lands**. Naming a cycle type and an - artifact rather than "the first cycle" matters because siblings can start concurrently and "first" - has no total ordering across them. That cycle's provenance line and curve must carry a real nonce, + than as satisfied). **The nonce is verified by an obligation placed on a future cycle, not by naming one in advance.** + Selecting "the next" cycle cannot work: concurrent siblings have no total ordering, and a cycle + running after a revert would be under the restored rules and owe no nonce at all. Instead the + implementation commit **records the obligation**, and **the first cycle that both starts under + these rules and closes discharges it** — its provenance line and curve must carry a real nonce, + the same one in both, distinct from any cycle open when it was generated. Whoever closes that + cycle performs the check and records that the obligation is discharged. The obligation is + self-identifying rather than pre-assigned, which is what removes the ordering problem. That cycle's provenance line and curve must carry a real nonce, the two must carry the **same** one, and it must differ from that of **any cycle open at the time it was generated** — which is the uniqueness the rule actually requires, and all it can check. Recording that as a checkpoint rather than as a satisfied criterion is the difference between a demonstration and a claim. diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 53140cd..b965e05 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -86,8 +86,9 @@ would separate a rule from the argument that settles it. **Named out of scope**, so no criterion below absorbs them: - **Hook code.** No change to any file under `plugins/dev-workflow/hooks/`. Part 1 is - prompt-only policy over the already-shipped `.context/codex-gate.floor` knob (Daniel, - 2026-08-27). If design concludes prompt-only cannot hold, that is a stop-and-ask, not a + prompt-only: the floor is derived from the profile and **stated in the text**, and the + already-shipped `.context/codex-gate.floor` knob is neither read for that derivation nor written + (Daniel, 2026-08-27; refined as the design settled). If design concludes prompt-only cannot hold, that is a stop-and-ask, not a silent expansion. - **Gate-call observability** — upstream in `mcp-codex-dev`, a different repo. - **The pass-counter anomaly.** During `fic2`, seven validated passes were reported by the @@ -225,8 +226,9 @@ would separate a rule from the argument that settles it. - [ ] **A cycle's records can be told apart from another cycle's.** Both copies require **each cycle started after these rules ship** to hold an identifier created at its start, unique - among open cycles, and present in every record that cycle writes — **one per cycle, so a run - of all three holds three**. Both copies also state the **bounded, self-terminating exception**: + among cycles open when it was generated, and present in **each record the shipped text names + as carrying it** — a named set rather than an open one, so a reader can tell whether a given + record is in scope — **one per cycle, so a run of all three holds three**. Both copies also state the **bounded, self-terminating exception**: a cycle already running when the rules land has no identifier, cannot acquire one, and writes a reserved value that says so — and no later cycle can enter that state. They state how it is recovered by a cycle resumed after an interruption, what happens when it @@ -235,10 +237,10 @@ would separate a rule from the argument that settles it. **What this branch can and cannot demonstrate, stated rather than assumed:** its three cycles all began before these rules ship, so each closing body carries the reserved pre-rule cycle field and **none of them demonstrates a real identifier**. The branch demonstrates the field's - presence and grammar; **a named later checkpoint demonstrates a real one and the attribution it - buys: the Gate-A spec cycle of the next story whose spec is written after the implementation - commit lands.** Naming a cycle type and an artifact matters because siblings can start - concurrently and "first" does not order them. + presence and grammar; **a future cycle demonstrates a real one and the attribution it buys** — + the obligation is recorded at implementation and discharged by the first cycle that both + starts under these rules and closes, rather than assigned in advance to a cycle that + concurrent siblings and a possible revert make impossible to name. - [ ] **The shipped text says when it starts binding, and what an adopter gets when it does not fully arrive.** Both copies state: that a loop already running finishes under the rules it started with; what a loop does when its starting rules cannot be established, covering From e56fd28b6482b8f7c785eb976f7576bf8c5a481d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:25:33 +0200 Subject: [PATCH 047/117] =?UTF-8?q?docs:=20revision=2032=20=E2=80=94=20the?= =?UTF-8?q?=20skip=20is=20not=20a=20cycle=20owing=20a=20gate,=20and=20"any?= =?UTF-8?q?"=20beats=20"the=20first"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 29: 3 findings, 0 BLOCKER, 2 Blocker/Major. Blocker/Major across twenty-nine passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2. Twelve consecutive zero-Blocker passes. Zero tells. §2.4 preserved the Gate-B skip in its opening and then contradicted it in every rule below: a cited set lowering to unanimously level 0 during an active cycle can make an already-behaviourally-trivial diff legitimately skippable, at which point the further-pass duties have nothing to attach to. The section now states its precondition once -- every rule in it presupposes a cycle that still owes a gate -- and says what governs instead when the skip applies: the skip's own obligations, its reason in the commit body and its skip record in place of a curve. And the nonce obligation still said "the first cycle", which I had introduced precisely to escape an ordering problem and which reintroduces it: concurrent cycles share no ordering and no atomic discharge state, so two closers could each see no prior discharge or each defer to the other. It is now ANY qualifying cycle, with duplicate discharge explicitly harmless and allowed. The obligation is satisfied by the first record of it that exists, and a second changes nothing. A rule needing exactly one discharger would need coordination this design does not have -- which is the honest reason, not a preference. The story's identifier criterion said "a named set" without naming it; it now lists the four members and states the exclusion, matching the spec. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-29. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 27 +++++++++++++------ ...-review-loop-economics-pass-floor-story.md | 15 ++++++----- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index acd8e4a..f85de14 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 31 (rules only) · **Gate-A passes 1-28** +**Date:** 2026-08-29 · **Revision:** 32 (rules only) · **Gate-A passes 1-29** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -140,8 +140,15 @@ pass; **passes already run keep counting**; **closing requires the floor as curr governs, which §2.1 notes is not every close: a zero-finding pass and a legitimate skip are unaffected by a moving profile because neither turns on the count. -**Any profile change costs at least one further pass**, in either direction and whether or not the -floor number moves, because §5 already requires the **final clean pass** to run under the current +**Every rule in this section presupposes a cycle that still owes a gate.** A change that makes a +Gate-B cycle **legitimately skippable** — the diff behaviourally trivial *and* the cited set +unanimously level 0 — removes the review rather than adjusting its count, and the further-pass +duties below do not apply to a cycle that is no longer running one. That is §5's existing skip, +which this change leaves untouched; the skip's own obligations (its reason in the commit body, its +skip record in place of a curve) are what govern instead. + +Otherwise: **any profile change costs at least one further pass**, in either direction and whether +or not the floor number moves, because §5 already requires the **final clean pass** to run under the current profile — so no already-banked pass can be it. **That further pass must be clean and every other closure duty must be satisfied**; it is one more pass, not a licence to close on the next one. **What a lowering drops is whatever the changed values drop, not a fixed pair**: a mode-only override changes the evidence obligations while leaving the axis-derived lens sets alone, and @@ -509,11 +516,15 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). than as satisfied). **The nonce is verified by an obligation placed on a future cycle, not by naming one in advance.** Selecting "the next" cycle cannot work: concurrent siblings have no total ordering, and a cycle running after a revert would be under the restored rules and owe no nonce at all. Instead the - implementation commit **records the obligation**, and **the first cycle that both starts under - these rules and closes discharges it** — its provenance line and curve must carry a real nonce, - the same one in both, distinct from any cycle open when it was generated. Whoever closes that - cycle performs the check and records that the obligation is discharged. The obligation is - self-identifying rather than pre-assigned, which is what removes the ordering problem. That cycle's provenance line and curve must carry a real nonce, + implementation commit **records the obligation**, and **any cycle that both starts under these + rules and closes discharges it** — its provenance line and curve must carry a real nonce, the + same one in both, distinct from any cycle open when it was generated. Whoever closes such a cycle + performs the check and records the discharge. + **"Any", deliberately, not "the first":** concurrent cycles share no ordering and no atomic + discharge state, so two closers could each see no prior discharge or each defer to the other. + **A duplicate discharge is harmless and explicitly allowed** — the obligation is satisfied by the + first record of it that exists, and a second changes nothing. A rule needing exactly one + discharger would need coordination this design does not have. That cycle's provenance line and curve must carry a real nonce, the two must carry the **same** one, and it must differ from that of **any cycle open at the time it was generated** — which is the uniqueness the rule actually requires, and all it can check. Recording that as a checkpoint rather than as a satisfied criterion is the difference between a demonstration and a claim. diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index b965e05..f01e95c 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -226,9 +226,11 @@ would separate a rule from the argument that settles it. - [ ] **A cycle's records can be told apart from another cycle's.** Both copies require **each cycle started after these rules ship** to hold an identifier created at its start, unique - among cycles open when it was generated, and present in **each record the shipped text names - as carrying it** — a named set rather than an open one, so a reader can tell whether a given - record is in scope — **one per cycle, so a run of all three holds three**. Both copies also state the **bounded, self-terminating exception**: + among cycles open when it was generated, and present in **a named set of records** — the + provenance line, the per-pass curve (or the skip record standing in for one), the cycle's + findings slots, and its advisory working record — **and not required in records this change + neither introduces nor keys to a cycle**, so a reader can tell whether a given record is in + scope — **one per cycle, so a run of all three holds three**. Both copies also state the **bounded, self-terminating exception**: a cycle already running when the rules land has no identifier, cannot acquire one, and writes a reserved value that says so — and no later cycle can enter that state. They state how it is recovered by a cycle resumed after an interruption, what happens when it @@ -238,9 +240,10 @@ would separate a rule from the argument that settles it. all began before these rules ship, so each closing body carries the reserved pre-rule cycle field and **none of them demonstrates a real identifier**. The branch demonstrates the field's presence and grammar; **a future cycle demonstrates a real one and the attribution it buys** — - the obligation is recorded at implementation and discharged by the first cycle that both - starts under these rules and closes, rather than assigned in advance to a cycle that - concurrent siblings and a possible revert make impossible to name. + the obligation is recorded at implementation and discharged by **any** cycle that both starts + under these rules and closes — not assigned in advance to a cycle that concurrent siblings and + a possible revert make impossible to name, and not to "the first", which concurrent closers + cannot agree on. A duplicate discharge is harmless and allowed. - [ ] **The shipped text says when it starts binding, and what an adopter gets when it does not fully arrive.** Both copies state: that a loop already running finishes under the rules it started with; what a loop does when its starting rules cannot be established, covering From d5ee48cbe776ea83fa68bf2d8e676301d8f1acfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:41:00 +0200 Subject: [PATCH 048/117] =?UTF-8?q?docs:=20revision=2033=20=E2=80=94=20sto?= =?UTF-8?q?p=20restating=20=C2=A75's=20skip=20rule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 30: 3 findings, 2 BLOCKER, 3 Blocker/Major. All three were caused by my revision-32 repair, and they share one cause: fixing pass 29's finding, I restated §5's triviality skip in order to say the further-pass duties do not apply to it -- and the restatement was wrong three ways. It defined skip eligibility as a behaviourally trivial diff plus a unanimously level-0 cited set, omitting §5's judgment-based eligibility for an unprofiled story and therefore mixed sets whose members are each eligible. It said the skip reason and skip record "are what govern instead", omitting the battery and the per-profile evidence duties §5 requires of a skipped cycle. And the blanket precondition swept in the rule that removing a citation never discharges an accepted in-set Blocker or Major -- which holds regardless, since acceptance put the finding in the fix set and the citation did not. The correct move was not a better summary. It was not to summarise at all: "summarising an existing decision procedure is how its conditions get dropped" is the AGENTS.md Don't in one line, and I had just done it. The section now says the further-pass duties are about pass counts and reach only a cycle still running a gate; where the skip applies, §5's own rules govern, unchanged and unrestated. This change adds exactly one obligation to a skipped cycle -- the skip record in place of a curve -- and two things stay outside the scope entirely: the provenance line, owed by every cycle, and an accepted in-set Blocker or Major, which nothing here discharges. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-30. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2,3. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index f85de14..7f194bf 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 32 (rules only) · **Gate-A passes 1-29** +**Date:** 2026-08-29 · **Revision:** 33 (rules only) · **Gate-A passes 1-30** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -140,14 +140,18 @@ pass; **passes already run keep counting**; **closing requires the floor as curr governs, which §2.1 notes is not every close: a zero-finding pass and a legitimate skip are unaffected by a moving profile because neither turns on the count. -**Every rule in this section presupposes a cycle that still owes a gate.** A change that makes a -Gate-B cycle **legitimately skippable** — the diff behaviourally trivial *and* the cited set -unanimously level 0 — removes the review rather than adjusting its count, and the further-pass -duties below do not apply to a cycle that is no longer running one. That is §5's existing skip, -which this change leaves untouched; the skip's own obligations (its reason in the commit body, its -skip record in place of a curve) are what govern instead. +**The further-pass duties below are about pass counts, so they reach only a cycle that is still +running a gate.** Where §5's triviality skip applies, there is no pass count to adjust and these +duties are silent — **§5's own skip rules govern, unchanged and not restated here**, because +summarising an existing decision procedure is how its conditions get dropped. This change adds +exactly one obligation to a skipped cycle, stated in §4: a skip record in place of a curve. -Otherwise: **any profile change costs at least one further pass**, in either direction and whether +**Two things are outside that scope and are not suspended by a skip.** The **provenance line** is +owed by every cycle, skipped or not (§2.3). And an **accepted in-set Blocker or Major stays in the +fix set**: acceptance put it there, not the citation, so neither removing a story nor becoming +skip-eligible discharges it. + +**Any profile change costs at least one further pass**, in either direction and whether or not the floor number moves, because §5 already requires the **final clean pass** to run under the current profile — so no already-banked pass can be it. **That further pass must be clean and every other closure duty must be satisfied**; it is one more pass, not a licence to close on the next one. **What a lowering drops is whatever the changed values drop, not a fixed pair**: a mode-only From ef2125f6c999795781f0eb9ba86a4790e7760a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:53:26 +0200 Subject: [PATCH 049/117] =?UTF-8?q?docs:=20revision=2034=20=E2=80=94=20rem?= =?UTF-8?q?ove=20the=20last=20two=20exit=20summaries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 31: 1 finding, 1 BLOCKER. Blocker/Major across thirty-one passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2,3,1. Revision 33 removed one restatement of §5's exits and left two others, one of them false. §2.4 said a legitimate skip is "unaffected by a moving profile" -- but §5 makes profiled skip eligibility depend on the current profile and cited set, so a moving profile is exactly what can change it. It also contradicted the next paragraph, which delegates the skip to §5 without restating it. Both summaries are gone. §2.1 now says only that the derived value replaces the pass-count number and that every other §5 closure rule stands as written and is not restated here. §2.4 says its rules are pass-count rules that apply while §5 says a gate is running and are silent otherwise, with what §5 says about when a gate runs left to §5. The single skip-specific addition this change makes is named where it lives, in §4. Three revisions to stop summarising one rule. Each attempt fixed the summary instead of deleting it, and each new summary dropped a different condition -- the unprofiled eligibility case, then the battery and evidence duties, then the dependence on the current profile. The AGENTS.md Don't is not advice about carefulness; a summary of a decision procedure is a second copy, and this is what a second copy does. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-31. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2,3,1. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- ...2026-08-28-review-loop-economics-design.md | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 7f194bf..7f88607 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 33 (rules only) · **Gate-A passes 1-30** +**Date:** 2026-08-29 · **Revision:** 34 (rules only) · **Gate-A passes 1-31** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -72,11 +72,9 @@ that flow only selects which advisory message fires, and the hook exits 0 on eve > that controls nothing.** Where the cycle's own closure rules are satisfied, a below-threshold > reminder is **noted in the pass report and disregarded.** > -> **"The floor" here replaces the number, not the rules around it.** §5's existing exits are -> untouched: a **zero-finding pass** still exits below the floor, and a **legitimate Gate-B skip** -> still removes the review entirely, recording its skip and — per §4 — a skip line in place of a -> curve. This change makes the floor a function of the profile; it does not make meeting the floor -> the only way a cycle can close. +> **This replaces the pass-count number and nothing else.** Every other rule §5 states about how a +> cycle closes stands as written, and none of them is restated here — a summary is where their +> conditions would get dropped. **Named residual, disclosed in both copies:** the hook's messages state its own threshold as an obligation, so at level 0 they report a shortfall the cycle does not owe. **Hook text is out of @@ -136,15 +134,12 @@ The properties the grammar exists to satisfy: ### 2.4 A profile or cited set that moves mid-cycle Three existing rules compose; no new rule. The floor derives from the **current** profile at each -pass; **passes already run keep counting**; **closing requires the floor as currently derived** — where the floor is what -governs, which §2.1 notes is not every close: a zero-finding pass and a legitimate skip are -unaffected by a moving profile because neither turns on the count. - -**The further-pass duties below are about pass counts, so they reach only a cycle that is still -running a gate.** Where §5's triviality skip applies, there is no pass count to adjust and these -duties are silent — **§5's own skip rules govern, unchanged and not restated here**, because -summarising an existing decision procedure is how its conditions get dropped. This change adds -exactly one obligation to a skipped cycle, stated in §4: a skip record in place of a curve. +pass; **passes already run keep counting**; **closing requires the floor as currently derived.** + +**These are pass-count rules, so they apply while §5 says a gate is running** and are silent +otherwise. What §5 says about when a gate runs — including how a moving profile or cited set bears +on that — is §5's, unchanged and deliberately not summarised here. **This change's only +skip-specific addition is in §4**: a skip record in place of a curve. **Two things are outside that scope and are not suspended by a skip.** The **provenance line** is owed by every cycle, skipped or not (§2.3). And an **accepted in-set Blocker or Major stays in the From cb23ee79adea24425d2591bb4b23f95a1251d986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:09:37 +0200 Subject: [PATCH 050/117] =?UTF-8?q?docs:=20revision=2035=20=E2=80=94=20zer?= =?UTF-8?q?o=20and=20unknown=20are=20different=20facts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 32: 2 findings, 1 BLOCKER, 2 Blocker/Major. Blocker/Major across thirty-two passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2,3,1,2. The curve required a numeric count for every valid pass and had no way to say a count is unrecoverable. That state is reachable: §5's recovery turns on the NONCE, not on the findings files, so a resumed cycle can keep its identity while its pass files are gone -- it knows a pass happened and not what it found. The grammar had `undetermined` for an unknown model and nothing for an unknown count, so the only writable value was `0`. Now `?`, with the rule that P8 EXCLUDES such a pass from comparison rather than reading it as zero, and says how many it excluded. Zero and unknown are different facts, and a record that cannot tell them apart understates every curve containing one -- which would have biased the comparison the curve exists for, in the direction that flatters the change. And story criterion 1 carried the profile-change duty without the scope the spec added last revision: these are pass-count rules and reach only a cycle §5 says is still running a gate. The criterion now says so and refers to §5 for whether the gate runs, keeping the accepted-in-set clause separate since that one holds regardless. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-32. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2,3,1,2. Gate B: N/A -- both staged paths are docs/**.md under §5's prose exemption. --- .../specs/2026-08-28-review-loop-economics-design.md | 10 ++++++++-- ...026-08-28-review-loop-economics-pass-floor-story.md | 7 ++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 7f88607..b904575 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 34 (rules only) · **Gate-A passes 1-31** +**Date:** 2026-08-29 · **Revision:** 35 (rules only) · **Gate-A passes 1-32** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -233,7 +233,7 @@ reason as the provenance line:** :=

|

"-"

:= [1-9][0-9]* := ("," )* exactly as many entries as enumerates - := 0 | [1-9][0-9]* + := 0 | [1-9][0-9]* | "?" "?" = the count is unrecoverable for that pass := | ("; " )* := "pass "

" " ("+" )* := | | "undetermined" @@ -292,6 +292,12 @@ here keeps a later reader from computing a demotion figure the baseline cannot b - **One entry per valid pass**, and because incomplete passes are excluded and consume pass numbers, the record **states which pass numbers it covers**. A valid zero-finding pass is recorded as zero, never omitted. +- **A count that cannot be recovered is written `?`, never guessed and never written as `0`.** A + cycle can keep its identity while its pass files are gone — §5's recovery turns on the nonce, not + on the findings files — so a resumed cycle may know a pass happened and not what it found. + **P8 excludes a `?` pass from any comparison** rather than reading it as zero, and says how many + it excluded. Zero and unknown are different facts, and a record that cannot tell them apart + understates every curve containing one. - A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are **branches of one logical pass** contributing one summed entry. **The curve counts logical passes; the hook counts calls**, and where they differ the body says so. diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index f01e95c..7091aa8 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -139,9 +139,10 @@ would separate a rule from the argument that settles it. cycles** — and says *why*: they derive from **the same cited-story set**, not from being one cycle. A copy that states the floor for one gate and leaves the others to inference fails this criterion, and so does one that gives the shared-cycle reason, which is false. - Both copies also state that **a change to the cited set *or to any cited profile* requires - the final clean pass to run against the current set and under the current profile, even when - the floor number does not move** — a new member brings its + Both copies also state that — **for a cycle §5 says is still running a gate**, the scope the + spec sets and which §5 alone decides — **a change to the cited set *or to any cited profile* + requires the final clean pass to run against the current set and under the current profile, + even when the floor number does not move** — a new member brings its lenses and evidence duties with it — and that **removing a citation never discharges an already accepted in-set Blocker or Major**, since acceptance and not the citation put it in the fix set. From 6f6bc8e4549c3673f30b36782c59796805cf57a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:21:38 +0200 Subject: [PATCH 051/117] =?UTF-8?q?docs:=20revision=2036=20=E2=80=94=20unk?= =?UTF-8?q?nown=20is=20per=20series,=20and=20so=20is=20its=20exclusion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 33: 1 finding, 0 BLOCKER, 1 MAJOR. Blocker/Major across thirty-three passes: 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2,3,1,2,1. Revision 35 introduced `?` for an unrecoverable count and then over-applied it: one unknown value excluded the whole pass from every comparison. But a pass whose Findings are unknown may still have known Blocker and Major counts, and those remain usable. Discarding all three because one is missing throws away good data -- and unevenly, since which series survives is not the same across cycles, so the loss would bias comparisons rather than merely shrink them. Now: `?` is per series and so is its exclusion. P8 drops a `?` value from the comparisons that read that series, keeps the pass's other series, and reports exclusions per series rather than per pass. Mirrored in the parent's curve criterion and in P8's own criterion, so the deferred consumer has the rule rather than inheriting a gap. Sparring session, under Daniel's 2026-08-28 delegation. Gate A: passes 1-33. Blocker/Major 24,22,43,31,30,26,29,28,22,23,30,17,28,12,9,4,4,1,4,4,3,3,2,2,3,3,5,4,2,3,1,2,1. Gate B: N/A -- all staged paths are docs/**.md under §5's prose exemption. --- .../2026-08-28-review-loop-economics-design.md | 15 ++++++++++----- ...08-04-passive-metrics-over-the-ledger-story.md | 5 ++++- ...8-28-review-loop-economics-pass-floor-story.md | 5 ++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index b904575..db17ac5 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -1,6 +1,6 @@ # Review-loop economics: pass floor and severity semantics — Design -**Date:** 2026-08-29 · **Revision:** 35 (rules only) · **Gate-A passes 1-32** +**Date:** 2026-08-29 · **Revision:** 36 (rules only) · **Gate-A passes 1-33** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` **Profile:** read from that header, never from here. @@ -294,10 +294,15 @@ here keeps a later reader from computing a demotion figure the baseline cannot b recorded as zero, never omitted. - **A count that cannot be recovered is written `?`, never guessed and never written as `0`.** A cycle can keep its identity while its pass files are gone — §5's recovery turns on the nonce, not - on the findings files — so a resumed cycle may know a pass happened and not what it found. - **P8 excludes a `?` pass from any comparison** rather than reading it as zero, and says how many - it excluded. Zero and unknown are different facts, and a record that cannot tell them apart - understates every curve containing one. + on the findings files — so a resumed cycle may know a pass happened and not what it found. Zero + and unknown are different facts, and a record that cannot tell them apart understates every curve + containing one. + **`?` is per series, and so is its exclusion.** A pass whose Findings are unknown may still have + a known Blocker and Major count, and those remain usable. **P8 excludes a `?` value from the + comparisons that read that series and keeps the pass's other series**, reporting exclusions + **per series** rather than per pass. Discarding a whole pass because one of its three numbers is + unknown would throw away good data — and would do so unevenly, since the series most often lost + is not the same across cycles. - A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are **branches of one logical pass** contributing one summed entry. **The curve counts logical passes; the hook counts calls**, and where they differ the body says so. 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 index 927bc71..f3d6fe7 100644 --- 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 @@ -85,7 +85,10 @@ from the file rather than from recall — without the analysis writing anything is reported**: the curves are self-reported and unvalidated, and the cycles being compared reviewed **different artifacts**, so a difference is evidence about the population as much as about the rule. **No demotion figure is derivable** — that would need one finding classified - under both rules, which nothing records. That analysis reads **two pinned commit-body forms** — the provenance + under both rules, which nothing records. **An unknown count is excluded per series, not per + pass**: a curve may record one series as unknown while the others are intact, and whatever is + reported states how many values were excluded from each series rather than how many passes + were dropped. That analysis reads **two pinned commit-body forms** — the provenance line and the per-pass curve, both specified in `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` — and its **first checkpoint** is the first post-merge cycle whose cited set licenses floor 1, which must carry diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 7091aa8..9148a47 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -164,7 +164,10 @@ would separate a rule from the argument that settles it. enough that a reader can tell **which pass each number belongs to** — incomplete passes are excluded and they consume pass numbers — and **which model each pass ran under**, which an existing convention already requires beside a finding count. A **legitimately skipped** loop - records the skip rather than leaving a silent gap. Both copies also state when separate calls + records the skip rather than leaving a silent gap. **A count that cannot be recovered is + recorded as unknown rather than as zero, per series** — a pass may have one series lost and + the others intact — and a reader excludes an unknown value only from the comparisons that + read it. Both copies also state when separate calls are **one logical pass**: only when they reviewed the **same tracked revision** — and that a revision mismatch **ends the first as an incomplete pass** before the later one starts a new pass, rather than merging two revisions into a single entry. Checkable: the requirement is From 59cb7f01a8cf96ebb57a0fb1e1c89ea160cbda21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:32:40 +0200 Subject: [PATCH 052/117] docs(field-report): preserve the Gate-A rle cycle record before slot reuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thirty-four validated findings files under gitignored .context/ are this cycle's only durable evidence and P8's material. This file carries the curve, extracted mechanically rather than recalled, the two structural decisions that moved it, the three mandatory stops, and the findings worth keeping. The load-bearing one: a restructuring guard asking "did a decision move?" misses the case where a rule survives in outline and loses its force -- pass 9 found eight in one revision. Also recorded: running a check is not reading its output, and a grep for the phrasing you expect is not a check either. Includes the coverage statement the close owes, with the template-mirror claims re-verified against the final revision and the known limit stated. Gate B: N/A -- one staged path, docs/**.md under CLAUDE.md §5's prose exemption. --- .../2026-08-29-gate-a-rle-cycle-evidence.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 docs/field-reports/2026-08-29-gate-a-rle-cycle-evidence.md diff --git a/docs/field-reports/2026-08-29-gate-a-rle-cycle-evidence.md b/docs/field-reports/2026-08-29-gate-a-rle-cycle-evidence.md new file mode 100644 index 0000000..db51332 --- /dev/null +++ b/docs/field-reports/2026-08-29-gate-a-rle-cycle-evidence.md @@ -0,0 +1,81 @@ +# Gate-A spec cycle `rle` — the record, preserved + +The Gate-A spec cycle for +`docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` ran **34 passes** and closed +clean. Its 34 validated findings files live under `.context/codex-reviews/`, which is gitignored, +so **this file is the durable record** — written before any slot reuse or `.context/` clear, the +same reason `2026-08-26-fic2-cycle-evidence.md` exists. + +Every number below was extracted mechanically from those files +(`grep -cE '^(BLOCKER|MAJOR|MINOR|NIT) \|'` and `grep -c '^BLOCKER'` per pass), not recalled. + +## The curve + +``` +Findings 27, 30, 54, 40, 33, 34, 32, 33, 28, 27, 38, 24, 32, 18, 13, 4, 6, 2, 7, 6, 9, 4, 2, 7, 5, 4, 6, 6, 3, 3, 1, 2, 1, 1 +Blockers 5, 2, 0, 4, 0, 3, 9, 4, 2, 2, 2, 5, 9, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 0, 0 +Majors 19, 20, 43, 27, 30, 23, 20, 24, 20, 21, 28, 12, 19, 12, 9, 3, 3, 1, 4, 4, 3, 3, 2, 2, 3, 3, 5, 4, 2, 1, 0, 1, 1, 0 +``` + +Blocker/Major never fell below 22 for the first eleven passes. **Two structural decisions moved +it, and nothing else did.** + +| Pass | Event | Blocker/Major after | +|---|---|---| +| 1–11 | ordinary repair rounds | 22–43, never below 22 | +| 12 | **part 3 split to a successor story** | 17 | +| 13 | (split accounting errors) | 28 | +| 14 | **spec slimmed 604 → 332 lines, rules only** | 12 | +| 15–34 | ordinary repair rounds | 9, then 1–5 throughout | + +**Three mandatory two-tell stops** fired, at passes 6, 11 and 13, each surfaced to the human, and +the second and third produced the two structural decisions above. A fourth stop at pass 7 was +discretionary — the tells were readable before the duty activates at pass 4. + +## What the passes actually cost, by cause + +Of roughly 380 findings, the recurring generators were: + +1. **A restatement that must track a moving original.** Three sites in one cycle: the story's + acceptance criteria (five Blocker occurrences), the spec's own condition-inventory table (four + more), and — three revisions running — a summary of §5's triviality skip, where each attempt + corrected the summary instead of deleting it and each new summary dropped a different condition. + **The remedy is never a better summary.** +2. **A correction landing in one place and not the others.** The demotion-versus-comparison + correction took four revisions to propagate across four artifacts; twice I wrote in a commit + body that I had enumerated every site and had not. +3. **A repair generating the next defect.** Most Blockers from pass 10 onward traced to the + immediately preceding revision's own fix. + +## Findings worth keeping + +- **A restructuring guard that asks "did a decision move?" misses the case where a rule survives + in outline and loses its force.** Pass 9 found eight of those in one revision — including a + severity test compressed to "if you can name neither", which inverts a rule requiring both. +- **Running a check is not reading its output.** Twice I ran a grep, wrote a sentence its own + output contradicted, and cited the verification. A grep for the phrasing you expect is not a + check either — my search for compression losses returned zero because the reviewer had phrased + them differently. +- **An instrument that cannot measure its own subject.** The per-pass curve recorded Findings and + Blockers while existing to show whether severity moves the Blocker/Major line. +- **Comparable is not measured.** Even with Majors recorded, no finding is ever classified under + both rules, so no demotion figure is derivable — only a comparison of recorded mixes across + cycles that reviewed different artifacts. +- **The loop's first wrong finding arrived at pass 13**, in roughly 380. It claimed a syntax break + a prior revision had already replaced; dismissed with grep evidence. One bad finding in ~380 is + the argument *for* validating before applying, not against it. + +## Closing state + +Pass 34 returned **zero Blocker/Major** and one MINOR, collected per §5's clean-final-pass rule: +§4's rationale claims the lost series differs across cycles, which nothing establishes. Recorded +here as the cycle's one outstanding Minor. + +**Coverage statement.** The `/workflow-init` mirror's actual text was checked by hand against every +claim the spec makes about it, re-run against the final revision: the template block is fenced at +192–778 with §5 at 257–777; the describe-versus-be principle the severity kinship rests on **is +present** in the template (an earlier revision's claim that it was absent was withdrawn); the +hook's §5-heading regex still matches `CLAUDE.md`; and `workflow-init.md` retains exactly one +`Target model:` declaration, which the item-1 n/a decision preserves. **Known limit:** the two +copies diverge on ~192 lines overall, and only the rules this change touches were compared — +general reconciliation was out of scope throughout. From 14700947bc0fe0bfa771827c57e03771126a3737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:36:44 +0200 Subject: [PATCH 053/117] docs(plan): implementation plan for the review-loop economics change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Twelve tasks, sixty-one steps, against the frozen spec (revision 36, Gate A closed clean at pass 34). Task 0 is the old-conditions dispositions artifact and its gate -- it runs first and nothing else proceeds until its review passes, which is what satisfies the AGENTS.md Don't rather than a promise to satisfy it later. Each task carries the edit AND the check that would catch it wrong, with the check run BEFORE the edit so its failing state is observed. For a prose change that check is a grep or a reading, not a unit test; the differential named verification in Task 10 reads BOTH revisions and says so, because one that consults only the post-change text cannot fail. Three constraints the tasks encode rather than assume: the §5 heading must keep matching the hook's citation regex; workflow-init.md must keep exactly one Target-model declaration, which is why Task 8's note sits outside the fence; and Task 7 commits alone, because §5's prose exemption needs every staged path to be documentation and a mixed commit forfeits it. One gap stated rather than hidden: Task 2's replacement wordings are proposals, not transcriptions -- the spec pins the rules, not the sentences -- and the plan's own Gate A is what reviews them. Gate A: not yet run on this plan. This commit is the artifact it reviews. Gate B: N/A -- one staged path, docs/**.md under §5's prose exemption. --- .../plans/2026-08-29-review-loop-economics.md | 706 ++++++++++++++++++ 1 file changed, 706 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-29-review-loop-economics.md diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics.md b/docs/superpowers/plans/2026-08-29-review-loop-economics.md new file mode 100644 index 0000000..af4d2ea --- /dev/null +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics.md @@ -0,0 +1,706 @@ +# Review-loop economics (pass floor + severity semantics) — 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:** Make the mandatory Gate-A/Gate-B pass floor a function of the story profile, and decide +finding severity by whether something in the system takes a different decision. + +**Architecture:** Two mirrored prose edits — `CLAUDE.md` §5 and the inline `CLAUDE.md` template in +`/workflow-init` — plus the user-facing sentences those edits falsify, plus packaging. No code +changes anywhere; **nothing under `plugins/dev-workflow/hooks/` is touched and no hook state file +is written.** + +**Tech Stack:** Markdown prompts, POSIX shell for verification, `git` for the evidence records. + +**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36, closed +clean at Gate-A pass 34). **Read it alongside this plan** — every task argues from it. + +**Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` — +`risk high · security none · battery+check+verification`. **Read the profile from that header, not +from here.** + +--- + +## Global Constraints + +Copied verbatim from the spec. Every task's requirements implicitly include these. + +- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes, and no hook state file is + written — in particular not `.context/codex-gate.floor`. +- **The §5 heading must keep matching `^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`.** + `codex-gate.sh:94` greps `CLAUDE.md` for it to build the citation every reminder prints; renaming + or renumbering the section degrades every message to a generic fallback. +- **`plugins/dev-workflow/commands/workflow-init.md` must keep exactly one `^Target model:` line.** + `scripts/check-invariants.sh` fails on any other count. The template adds none — see Task 8. +- **Every rule this change adds lands in BOTH copies**, `CLAUDE.md` §5 and the template's §5, + except where a difference is deliberate and stated. +- **§5's other closure rules are never restated**, only referred to. Three revisions of the spec + were spent learning this: each summary of the triviality skip dropped a different condition. +- **Gate B for this branch's implementation commits:** §5's prose exemption needs *every* staged + path to be explanatory documentation, and **a mixed commit forfeits it**. Tasks 1–6 and 8–9 stage + prompt or non-`.md` paths and therefore **fire full Gate B**. Task 7 stages only `docs/**.md` and + `README.md` and is **N/A** — provided it is committed alone, which its steps require. + +--- + +## File Structure + +| File | Responsibility in this change | +|---|---| +| `CLAUDE.md` §5 (65–589) | the live rules | +| `plugins/dev-workflow/commands/workflow-init.md` §5 (257–777, fenced 192–778) | the scaffolded mirror | +| `plugins/dev-workflow/commands/workflow-init.md` (outside the fence) | the item-1 n/a note | +| `README.md`, `docs/getting-started.md`, `docs/coding-workflow.md` | sentences this change falsifies | +| `plugins/dev-workflow/.claude-plugin/plugin.json`, `CHANGELOG.md` | packaging | +| `docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md` | the old-conditions dispositions (Task 0) | + +**Line numbers in this plan were read on 2026-08-29 and drift as edits land.** Every task locates +its site by **quoted text**, not by number; the numbers are navigation aids only. + +--- + +## Task 0: The conditions artifact, and its gate + +**This task runs FIRST and nothing else proceeds until its review passes.** The AGENTS.md Don't — +"never replace a decision procedure without accounting for its old conditions" — is satisfied by +this artifact existing and being reviewed *before* any replacement text is written. + +**Files:** +- Create: `docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md` + +**Interfaces:** +- Consumes: the spec's §5.2 passage list (18 rows) and §5.1 site inventory. +- Produces: a dispositioned entry per passage that Tasks 1–5 must not contradict. + +- [ ] **Step 1: Regenerate the site inventory rather than copying it** + +```bash +grep -nE "min 3 passes|below 3|3-pass|3 passes|where the 3 come from|3-passes-per-gate" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Expected: **12 lines, six per copy.** If the count differs, the spec's inventory is stale and that +is a finding for the spec, not something to paper over here. + +- [ ] **Step 2: Confirm the two digit-free sites the grep cannot find** + +```bash +grep -n 'pass 1 carrying a Minor' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Expected: exactly one hit per copy. **Fourteen sites in total.** + +- [ ] **Step 3: Write one dispositioned entry per passage** + +For each of the spec's eighteen passages, in a table with these columns: *passage* (quoted by its +bold lead-in), *what its existing prose requires* (enumerated, one requirement per line), and +*disposition* — **kept**, **moved** (say where), or **deliberately dropped** (say why). A +requirement neither kept nor explicitly dropped is a dropped condition and fails this task. + +- [ ] **Step 4: Verify completeness mechanically** + +```bash +sed -n '/^| # | Passage/,/^$/p' docs/superpowers/specs/2026-08-28-review-loop-economics-design.md \ + | grep -c '^| [0-9]' +grep -c '^| ' docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md +``` + +Expected: the artifact has an entry for every passage the spec lists. A missing row is the failure +this artifact exists to prevent. + +- [ ] **Step 5: Commit** + +```bash +git add docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md +git commit -m "docs(spec): the old-conditions dispositions for the review-loop-economics change" +``` + +- [ ] **Step 6: GATE — this artifact is reviewed inside the Gate-A plan cycle** + +It is an input that cycle reviews alongside this plan, at the derived floor, with a clean pass as +its acceptance. **No replacement text is written while it is outstanding.** A finding against it +feeds back — regenerate against corrected text — rather than being absorbed. + +--- + +## Task 1: The floor predicate + +**Files:** +- Modify: `CLAUDE.md` — the paragraph opening `**Both gates are a LOOP with a HARD FLOOR:` +- Modify: `plugins/dev-workflow/commands/workflow-init.md` — the same paragraph in the template + +**Interfaces:** +- Consumes: Task 0's dispositions for that passage. +- Produces: the phrase `max(risk, security)` in both copies, which Task 2's sites refer back to. + +- [ ] **Step 1: Write the check, and watch it fail** + +```bash +# Both copies must state the predicate, unanimity, and the three-cycle scope. +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'max(risk, security)' +done +``` + +Expected now: `0` and `0`. **Run it before editing** — a check first seen passing has proved +nothing about the change. + +- [ ] **Step 2: Replace the floor sentence in `CLAUDE.md`** + +Current text opens: `**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major` + +Replace `min 3 passes per run` with the derived floor, keeping **every other requirement in that +paragraph verbatim** (the TodoWrite per pass, fix-after-each, the clean final pass, the +zero-findings early exit, Codex-is-advisory, the one-line dismissal reason): + +```markdown +**Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run (Blocker/Major +only), derived from the story profile — `max(risk, security) == 0` gives **1**, and every +resolvable profile above that, or an artifact citing no story, gives **3**. A cited story whose +profile is present but unresolvable **stops and surfaces** under the rule below rather than +falling through to 3. A cycle citing several stories reaches 1 **only if the cited set is +non-empty and every member is profiled, resolvable and at level 0**; any other set gives 3. +**One derived value governs all three cycles** — the Gate-A spec cycle, the Gate-A plan cycle and +the Gate-B cycle — because they derive from the same cited-story set, not because they are one +cycle. The floor is counted by the hook,** +``` + +- [ ] **Step 3: Make the identical edit in the template** + +Same paragraph inside the fenced block. **Byte-identical** to Step 2's replacement. + +- [ ] **Step 4: Re-run the check** + +Expected: `1` and `1`. + +- [ ] **Step 5: Verify no other requirement in the paragraph was lost** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + tr '\n' ' ' < "$f" | tr -s ' ' | grep -c "don't manufacture findings to pad" +done +``` + +Expected: `1` and `1`. That clause is the tail of the same paragraph and its survival is the cheap +proxy for "the replacement did not swallow its neighbours." + +- [ ] **Step 6: Commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit -m "feat(gates): derive the pass floor from the story profile" +``` + +**Gate B applies to this commit** — both paths are prompts. + +--- + +## Task 2: The fourteen floor-wording sites + +Every site states a rule that a variable floor falsifies. **The one that matters most spells no +digit**: `a Blocker/Major-free pass 1 carrying a Minor keeps looping` is correct at floor 3 and +**false at floor 1**, where pass 1 is *at* the floor and therefore closes. + +**Files:** `CLAUDE.md` and the template, at the seven sites each. + +**Interfaces:** +- Consumes: Task 1's `max(risk, security)` predicate. +- Produces: nothing later tasks depend on. + +- [ ] **Step 1: Write the check, and watch it fail** + +```bash +grep -cE "min 3 passes|below 3|3-pass|3 passes|where the 3 come from" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -c 'pass 1 carrying a Minor' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Expected now: nonzero counts. After Task 2 both must be **0**, except the historical citation — +see Step 4. + +- [ ] **Step 2: Apply the seven replacements in each copy** + +| Quoted current text | Replacement | +|---|---| +| `below 3 is a pass with **zero** findings` | `below the floor is a pass with **zero** findings` | +| `a Blocker/Major-free pass 1 carrying a Minor keeps looping` | `a Blocker/Major-free pass **below the floor** carrying a Minor keeps looping` | +| `don't count it toward the 3-pass floor` | `don't count it toward the floor` | +| `each its own 3-pass loop` | `each its own loop at the derived floor` | +| `which is where the 3 come from` | `which is where the floor's lower bound comes from` | +| `The 3-pass floor, the Blocker/Major` | `The floor, the Blocker/Major` | +| `if pass 3 still finds Blocker/Major` | `if the pass at the floor still finds Blocker/Major` | + +- [ ] **Step 3: Re-run the check** + +Expected: `0` for both greps in both files. + +- [ ] **Step 4: Confirm the historical citation was NOT changed** + +```bash +grep -c "PR #23's Gate-B pass 3 returned all four findings" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Expected: `1` and `1`. That sentence cites an actual historical pass, not a rule, and **must keep +saying 3**. Changing it would falsify a record. + +- [ ] **Step 5: Commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit -m "fix(gates): reword every rule that assumed a floor of three" +``` + +--- + +## Task 3: Severity semantics + +**Files:** the `**Severity:**` line in Mechanics, both copies. + +**Interfaces:** +- Consumes: nothing. +- Produces: the reachability test, which Task 6's parity walk checks. + +- [ ] **Step 1: Write the check, and watch it fail** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'consumes this text' +done +``` + +Expected now: `0` and `0`. + +- [ ] **Step 2: Extend the Severity line in `CLAUDE.md`** + +Keep the four existing definitions verbatim and append the classifier: + +```markdown +- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → + rework) → both must resolve. Minor · Nit → collect, never iterate. + **Which side of that line a finding falls on is decided by one test, not by what kind of file + the text lives in:** name **what in the system consumes this text** — whatever *acts* on it — + and the decision that act takes differently if the text is wrong. **Both are required**; if you + cannot name both, the finding is **Minor or below**. The reader must consume the text in the + system's *operation*, not in reviewing it — **the review pass raising the finding is not an + in-system reader of the text it reviews**, or the test would demote nothing, though gates remain + legitimate readers of rule text they will later apply. A human reader never satisfies it: that + cost is already priced as non-gating by the prose exemption. The list of reader kinds is + illustrative, not closed. **The test sets a ceiling and never chooses between Blocker and + Major** — the definitions above still do that. Findings about narration, prose describing a + mechanism, and test-instrument internals are the cases this usually catches, as worked examples + rather than a second rule. **An instrument finding keeps its severity whenever it shows the + instrument changes what a gate concludes about product behaviour, in either direction** — a + false green, and equally a false red or a check blocking a valid change. **Rationale prose is + Minor only when no rule's application depends on it**, not categorically: `docs/prompt-standards.md` + requires that rules carry their why, so rationale a reader must consult to apply a rule passes + the test. This removes arbitrariness, not judgement. **This is the finding-level analog of the + path-level prose exemption** — one principle at two granularities, text that *describes* the + product versus text that *is* the product. Coverage-first is unchanged: the reviewer reports + every finding with severity and confidence; the filter is ours. +``` + +- [ ] **Step 3: Make the identical edit in the template** + +- [ ] **Step 4: Re-run the check** + +Expected: `1` and `1`. + +- [ ] **Step 5: Verify the four definitions survived** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + grep -c 'Blocker (wrong/unsafe/breaks invariant)' "$f" +done +``` + +Expected: `1` and `1`. + +- [ ] **Step 6: Commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit -m "feat(gates): decide severity by consequence, not by artifact kind" +``` + +--- + +## Task 4: The two pinned records and the cycle nonce + +**Files:** both copies — Mechanics' closing-commit area and the findings-slot paragraph. + +**Interfaces:** +- Consumes: Task 1's derived floor (the provenance line reports it). +- Produces: ``, which Task 5's squash-carry rule names. + +- [ ] **Step 1: Write the check, and watch it fail** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f"; grep -c 'cycle none (pre-rule)' "$f" +done +``` + +Expected now: `0` and `0`. + +- [ ] **Step 2: Add both grammars and the nonce rules to `CLAUDE.md`** + +Copy §2.3, §4 and §5 of the spec's grammars **verbatim** — they are already pinned there and +retyping is how a production drifts. Add the surrounding rules: every cycle records a provenance +line; the curve records Findings, Blockers and Majors per valid pass with the pass numbers it +covers; an unrecoverable count is `?`, excluded **per series** and not per pass; a skipped cycle +records a skip line in place of a curve; the nonce is 8–16 chars of `[a-z0-9]` from randomness, +appears in the provenance line, the curve, the cycle's findings slots and its advisory working +record, and a cycle that cannot recover a single candidate starts fresh. + +- [ ] **Step 3: Widen the findings-slot grammar in both copies** + +The three slot names gain an optional per-cycle infix — `gate-a-spec[-]-pass-

` and its +two siblings — with the infix **required whenever more than one cycle could write that slot**, and +a target owned by another cycle **refused rather than overwritten**. + +- [ ] **Step 4: Make the identical edits in the template** + +- [ ] **Step 5: Re-run the check, and parse the grammars** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f"; grep -c 'cycle none (pre-rule)' "$f" +done +``` + +Expected: `1` and `1`. + +Then hand-parse the spec's five provenance examples and its curve example against the productions +as written in the shipped copies. **A grammar nothing ever parsed is a format claim, not a +format.** Record which features each example exercised: quoted path, each unusable-knob cause, +gapped ranges, split-model pass, skipped cycle, `?` count, ``/`` cardinality. + +- [ ] **Step 6: Commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit -m "feat(gates): pin the provenance line, the per-pass curve and the cycle nonce" +``` + +--- + +## Task 5: The squash carry + +**Files:** the `On squash-merge, copy every evidence entry` paragraph, both copies. + +**Interfaces:** +- Consumes: Task 4's record types. +- Produces: nothing later tasks depend on. + +- [ ] **Step 1: Write the check, and watch it fail** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'provenance line, the per-pass curves' +done +``` + +Expected now: `0` and `0`. + +- [ ] **Step 2: Extend the list in both copies** + +Add **the provenance line, the per-pass curves, and a skipped cycle's skip record** to what a +squash must carry. **Extend the sentence — do not rewrite it.** The successor story adds the +decline record to this same passage later, and a rewrite there would drop what this adds. + +- [ ] **Step 3: Re-run the check** + +Expected: `1` and `1`. + +- [ ] **Step 4: Verify the existing two survived** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'every evidence entry and every human-exception record' +done +``` + +Expected: `1` and `1`. + +- [ ] **Step 5: Commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit -m "feat(gates): carry the provenance line, curves and skip records through a squash" +``` + +--- + +## Task 6: Parity, and the twelve-item conformance pass + +No new text — this is the verification story criterion 7 and invariant 11 require. + +- [ ] **Step 1: Walk every changed rule across both copies** + +For each rule Tasks 1–5 added, extract it from both files and diff. Record every difference as +**deliberate-and-stated** or as a **defect**. The copies already diverge on ~192 lines overall, so +**a whole-section diff proves nothing** — walk the named rules. + +- [ ] **Step 2: Run the twelve-item pass** + +Against **each changed prompt artifact as a complete prompt**: the resulting scaffolded template +and `plugins/dev-workflow/commands/workflow-init.md`. Item 7 — no contradictions with +`CLAUDE.md`/`AGENTS.md` — is read against the whole resulting prompt, since a contradiction is a +relation between an edited passage and an unedited one. **Root `CLAUDE.md` is outside invariant +11's list and is not part of this pass.** + +- [ ] **Step 3: Record item 1's n/a for the scaffolded template** + +Satisfied-or-n/a-with-reason, per the story's conformance criterion. The reason: the scaffolded +file is model-agnostic by design, so a target-model line would be false in every repo it lands in. + +- [ ] **Step 4: Commit the record** + +```bash +git add docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md +git commit -m "docs(spec): record the parity walk and the twelve-item conformance pass" +``` + +--- + +## Task 7: The falsified user-facing sentences + +**Commit these alone.** Every staged path here is `docs/**.md` or `README.md`, so §5's prose +exemption applies and Gate B is N/A — **but only if nothing else is staged.** A mixed commit +forfeits the exemption. + +**Files:** `README.md`, `docs/getting-started.md`, `docs/coding-workflow.md`. + +- [ ] **Step 1: Write the check, and watch it fail** + +```bash +grep -nE 'three passes minimum|3-pass floor|3-passes-per-gate|floor is unchanged at every level|Gate A.s floor and' \ + README.md docs/getting-started.md docs/coding-workflow.md +``` + +Expected now: several hits. After this task: **zero**. + +- [ ] **Step 2: Correct each sentence** + +| Site | Current | Correction | +|---|---|---| +| `README.md:130` | "a positive integer; moves the 3-passes-per-gate floor" | "a positive integer; moves the **hook's reminder threshold**. It does not change the floor §5 obliges, which is derived from the story profile." | +| `getting-started.md:34` | "three passes minimum, final pass clean" | "the floor its profile derives, final pass clean" | +| `:40` | "the same 3-pass loop runs" | "the same loop runs at the derived floor" | +| `:44` | "If the Gate-A floor wasn't met, the hook says so" | "If the hook's own threshold wasn't met it says so — which at a derived floor of 1 can fire when nothing further is owed" | +| `:53` | "three passes, final clean" | "the derived floor, final clean" | +| `:58` | waits for `✓ … (3/3 …)` | show the ratio as the derived floor, not a literal 3/3 | +| `:84` | "Gate A's floor is unchanged at every level" | "Gate A's floor is derived from the profile like Gate B's; what the axes never subtract is the baseline questions" | +| `:86` | "moves the 3-pass floor" | "moves the hook's reminder threshold" | +| `coding-workflow.md:79-80` | "they never subtract any: Gate A's floor and the baseline questions are the same at every level" | "they never subtract any baseline question; the floor itself is derived from the profile" | + +- [ ] **Step 3: Re-run the check** + +Expected: zero hits. + +- [ ] **Step 4: Commit, alone** + +```bash +git add README.md docs/getting-started.md docs/coding-workflow.md +git diff --cached --name-only # confirm ONLY these three +git commit -m "docs: correct every sentence the derived floor falsifies" +``` + +**Gate B: N/A** — every staged path is explanatory documentation. Verify the staged set before +committing; that verification is what earns the exemption. + +--- + +## Task 8: The item-1 note beside the template + +**Files:** `plugins/dev-workflow/commands/workflow-init.md`, **outside** the fenced template block. + +- [ ] **Step 1: Write the check, and watch it fail** + +```bash +grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md +``` + +Expected: `1` — and it must **still be 1** after this task. `scripts/check-invariants.sh` fails on +any other count. + +- [ ] **Step 2: Add the note immediately before the fence** + +```markdown +> **Prompt-standards item 1 for the scaffolded `CLAUDE.md`: n/a, and why.** The file this template +> writes is model-agnostic by design — its executing model is whatever the reader of that project +> runs — so a `Target model:` line would be false in every repo it lands in. Recorded as a reasoned +> n/a rather than skipped. This note is deliberately outside the fence so it never scaffolds, and +> deliberately not a `Target model:` line, which would make this file's declaration count 2 and +> fail `scripts/check-invariants.sh`. +``` + +- [ ] **Step 3: Re-run the check** + +```bash +grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md +sh scripts/check-invariants.sh && echo INVARIANTS-OK +``` + +Expected: `1`, and the checker exits 0. + +- [ ] **Step 4: Confirm the note is outside the fence** + +```bash +awk 'NR>=186 && NR<=200' plugins/dev-workflow/commands/workflow-init.md +``` + +Expected: the note appears **before** the ```` ````markdown ```` line. Inside it, it would scaffold +into every user's `CLAUDE.md`. + +- [ ] **Step 5: Commit** + +```bash +git add plugins/dev-workflow/commands/workflow-init.md +git commit -m "docs(workflow-init): record item 1's n/a for the scaffolded CLAUDE.md" +``` + +--- + +## Task 9: Packaging + +- [ ] **Step 1: Write the check, and watch it fail** + +```bash +sh scripts/check-version-bump.sh main && echo BUMP-OK || echo BUMP-MISSING +``` + +Expected before the bump: the checker reports the plugin changed without a version change. +**Run it before bumping** — it is the only step here whose failure state is observable. + +- [ ] **Step 2: Bump the manifest** + +`plugins/dev-workflow/.claude-plugin/plugin.json`: `0.10.0` → `0.11.0`. Minor, because this +changes shipped rules without breaking a documented interface. + +- [ ] **Step 3: Add the CHANGELOG entry** + +Newest first, describing the floor derivation, the severity test, the two pinned records and the +nonce. **Nothing enforces this** — neither invariant 12 nor the checker mentions the changelog — +so the story's criterion is what carries it. + +- [ ] **Step 4: Re-run the check and the full battery** + +```bash +sh scripts/check-version-bump.sh main && echo BUMP-OK +``` + +Then the whole `AGENTS.md` § Commands chain, which must exit 0. + +- [ ] **Step 5: Commit** + +```bash +git add plugins/dev-workflow/.claude-plugin/plugin.json plugins/dev-workflow/CHANGELOG.md +git commit -m "chore(dev-workflow): 0.11.0 — profile-derived floor and consequence-keyed severity" +``` + +--- + +## Task 10: The evidence pack + +The mode is `battery+check+verification`. This task produces what the closing commit body cites. + +- [ ] **Step 1: The battery** + +The full `AGENTS.md` § Commands chain, green, with the assertion counts recorded. + +- [ ] **Step 2: The differential named verification — both revisions read** + +No automated test is possible for prose, so this takes §5's other permitted route. **Pose one +question about behaviour under a derived floor of 1** and answer it against both revisions: + +> *At floor 1, does a Blocker/Major-free pass 1 carrying a Minor close or keep looping?* + +- Against the **pre-change** text (`git show :CLAUDE.md`): the sentence says it **keeps + looping** — wrong, since pass 1 is at the floor. +- Against the **post-change** text: it says a pass **below the floor** keeps looping — correct. + +Ask the same question of `below 3` versus `below the floor`. **Record which revision was read for +each answer.** A verification that consults only the post-change text cannot fail and would report +success because of how it was wired — that is the failure mode this step exists to avoid, and +naming it is part of the entry. + +- [ ] **Step 3: The risk-path verification** + +The risk is that **the floor is derived by the agent and nothing mechanical checks it**. Recompute +each provenance line's floor from the cited stories' profile headers. The observation that would +exist if the claim were false is **a provenance line whose number the profiles do not license**. +Also confirm **no floor file is present at close where none was present at start** — which detects +a persisting write and **cannot** detect a transient one. + +- [ ] **Step 4: The user-knob verification, conditionally** + +If a `.context/codex-gate.floor` exists before a cycle, it is byte-identical after. **If none +exists, record not-applicable with that reason.** Do not create one — a fixture supplying its own +input proves nothing. + +- [ ] **Step 5: Write the evidence entry** + +Naming the story path and each verification's observation. It is **revalidated before every Gate-B +re-review and before the closing amend**, against the content the close will carry rather than a +commit that does not exist yet. + +--- + +## Task 11: The three closing bodies + +Two story criteria require this branch to demonstrate the pinned forms. + +- [ ] **Step 1: Write the check** + +```bash +git log --format='%b' -3 | grep -cE '^cycle (none \(pre-rule\)|[a-z0-9]{8,16});' +``` + +- [ ] **Step 2: Write each cycle's closing body** + +The Gate-A spec cycle, the Gate-A plan cycle and the Gate-B cycle each carry a provenance line and +a curve, in the pinned forms. **All three use `cycle none (pre-rule)`**: every cycle on this branch +began before these rules ship, so none has a nonce and minting one would be late-created +provenance. The branch demonstrates **every field of both forms except two** — the cycle +identifier, and the knob clause's non-absent form, which needs a workspace that has a knob. Both +are recorded as **undemonstrable-here with their reasons**, not as gaps and not as satisfied. + +- [ ] **Step 3: Record the nonce obligation** + +The implementation commit records that **any cycle that both starts under these rules and closes** +discharges the nonce demonstration; duplicate discharge is harmless and allowed. + +- [ ] **Step 4: Re-run the check** + +Expected: three matching lines. + +--- + +## Self-Review + +**Spec coverage.** §2 floor predicate → Task 1. §2.1 knob and precedence → Task 1. §2.2 pass report +→ Task 1. §2.3 provenance → Task 4. §2.4 mid-cycle → Task 1. §3 severity → Task 3. §4 curve → +Task 4. §5 nonce → Task 4. §6 accounting → Task 0. §7 rollout, falsified sentences, packaging → +Tasks 7, 8, 9. §8 evidence → Task 10. §9 scope → the Global Constraints. §10 risks → carried into +the shipped text by Tasks 1 and 4. + +**Placeholders.** None: every replacement is quoted, every check is runnable, no step says "handle +appropriately". + +**Type consistency.** ``, ``, ``, ``, ``, ``, +`` are used identically in Tasks 4 and 11 and are defined in the spec, which travels with +this plan. + +**One known gap, stated rather than hidden.** Task 2's replacement wordings are proposals, not +transcriptions — the spec pins the *rules*, not the sentences. The Gate-A plan cycle reviews them, +which is exactly what that cycle is for. From 5c00f8c356238d41b9c91ba851d74e5b01533199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:55:01 +0200 Subject: [PATCH 054/117] docs(plan): rewrite the review-loop-economics plan against revision 36 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first version drew 20 Blocker/Major at Gate-A pass 1 from four habits, each structurally prevented here rather than repaired: - anchors typed from memory that did not exist in the file -> every anchor is pasted `grep -n` output, and line-broken anchors are quoted as the file holds them (the `pass 1` sentence continues onto the next line); - references to spec contents that revision 15 removed -> the nineteen-passage list lives in the plan, recovered mechanically from `git show 46072fe:` and reconciled against revision 36, which adds the pass-report passage; - checks written to look like TDD rather than to fail -> every check's failing output is recorded inline, and Task 1's is scoped to the floor paragraph because the naive form already passes on the Profiles section; - a plan for a §5 change that did not follow §5 -> Tasks 1-5, 8 and 9 are ONE Gate-B cycle with one `WIP:` snapshot and a closing amend, not nine ordinary commits that would have destroyed the executor's own cycle. Also: Task 0 no longer waits on itself (the conditions artifact is reviewed by this plan's Gate-A passes); Gate-B classification is fixed per path (Tasks 0, 7 and 8 are N/A and commit alone); Task 9 runs check-version-bump after the WIP commit exists, since it compares commits and ignores the worktree; and the coverage gap is closed with concrete wordings for the pass-report fields, the knob-preservation rule, the gate-off disclosure and the nonce retry policy. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- .../plans/2026-08-29-review-loop-economics.md | 852 +++++++++--------- 1 file changed, 447 insertions(+), 405 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics.md b/docs/superpowers/plans/2026-08-29-review-loop-economics.md index af4d2ea..fb2d402 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics.md @@ -4,179 +4,273 @@ > (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use > checkbox (`- [ ]`) syntax for tracking. -**Goal:** Make the mandatory Gate-A/Gate-B pass floor a function of the story profile, and decide -finding severity by whether something in the system takes a different decision. +**Goal:** Make the mandatory pass floor a function of the story profile, and decide finding +severity by whether something in the system takes a different decision. -**Architecture:** Two mirrored prose edits — `CLAUDE.md` §5 and the inline `CLAUDE.md` template in -`/workflow-init` — plus the user-facing sentences those edits falsify, plus packaging. No code -changes anywhere; **nothing under `plugins/dev-workflow/hooks/` is touched and no hook state file -is written.** +**Architecture:** Two mirrored prose edits — `CLAUDE.md` §5 and the inline template in +`/workflow-init` — plus the user-facing sentences they falsify, plus packaging. **No code. Nothing +under `plugins/dev-workflow/hooks/` is touched and no hook state file is written.** -**Tech Stack:** Markdown prompts, POSIX shell for verification, `git` for the evidence records. +**Tech Stack:** Markdown prompts; POSIX shell for every check; `git` for the evidence records. -**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36, closed -clean at Gate-A pass 34). **Read it alongside this plan** — every task argues from it. +**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36, Gate A +closed clean at pass 34). **Read it alongside this plan.** **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` — -`risk high · security none · battery+check+verification`. **Read the profile from that header, not -from here.** +`risk high · security none · battery+check+verification`. **Read the profile from that header.** + +**This is a rewrite.** The first version drew 20 Blocker/Major at Gate-A pass 1 from four habits: +typed anchors that did not exist, references to spec contents that no longer existed, checks that +could not fail, and — worst — ordinary commits inside a Gate-B cycle, in a plan whose subject is +§5. Every one is structurally prevented below: **anchors are pasted from `grep -n`, the passage +list lives here because the spec no longer holds it, every check's failing output is recorded, and +the whole prompt change is one Gate-B cycle with one `WIP:` snapshot.** --- ## Global Constraints -Copied verbatim from the spec. Every task's requirements implicitly include these. +Verbatim from the spec. Every task's requirements implicitly include these. -- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes, and no hook state file is - written — in particular not `.context/codex-gate.floor`. +- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes; no hook state file is + written, in particular not `.context/codex-gate.floor`. - **The §5 heading must keep matching `^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`.** - `codex-gate.sh:94` greps `CLAUDE.md` for it to build the citation every reminder prints; renaming - or renumbering the section degrades every message to a generic fallback. + `codex-gate.sh:94` greps `CLAUDE.md` for it to build the citation every reminder prints. - **`plugins/dev-workflow/commands/workflow-init.md` must keep exactly one `^Target model:` line.** - `scripts/check-invariants.sh` fails on any other count. The template adds none — see Task 8. -- **Every rule this change adds lands in BOTH copies**, `CLAUDE.md` §5 and the template's §5, - except where a difference is deliberate and stated. -- **§5's other closure rules are never restated**, only referred to. Three revisions of the spec - were spent learning this: each summary of the triviality skip dropped a different condition. -- **Gate B for this branch's implementation commits:** §5's prose exemption needs *every* staged - path to be explanatory documentation, and **a mixed commit forfeits it**. Tasks 1–6 and 8–9 stage - prompt or non-`.md` paths and therefore **fire full Gate B**. Task 7 stages only `docs/**.md` and - `README.md` and is **N/A** — provided it is committed alone, which its steps require. + `scripts/check-invariants.sh` fails on any other count. Verified now: **1**. +- **Every rule lands in BOTH copies**, except where a difference is deliberate and stated. +- **§5's other closure rules are never restated, only referred to.** Three spec revisions were + spent on this: each summary of the triviality skip dropped a different condition. + +### The commit protocol — read this before Task 1 + +**Tasks 1–5, 8 and 9 are ONE Gate-B cycle, not seven.** They edit the same two prompt files plus +packaging, and §5 gates the *cycle*, not each edit. So: + +1. **Task 1 opens the cycle** with a commit whose message begins `WIP:`. Every later task in the + cycle **amends that commit**, keeping the `WIP:` prefix and carrying the body forward. +2. **`mcp__codex__review` runs against that WIP commit**, `baseSha` = its parent. +3. **Re-review after every fix.** A fix changes the diff and invalidates the prior pass. +4. **The cycle closes with `git commit --amend -m ""`** — the first message without + `WIP:` — carrying the evidence entry, the provenance line and the curve. + +**An ordinary `git commit` inside the cycle reads to the hook as the cycle closing and resets the +counters.** The first version of this plan instructed exactly that, seven times. + +**Task 7 is outside the cycle** and commits normally: every path it stages is `docs/**.md` or +`README.md`, so §5's prose exemption applies and Gate B is N/A — **but only if nothing else is +staged**, since a mixed commit forfeits it. **Task 0 is also outside** and is likewise N/A +(`docs/superpowers/**.md` only). --- ## File Structure -| File | Responsibility in this change | -|---|---| -| `CLAUDE.md` §5 (65–589) | the live rules | -| `plugins/dev-workflow/commands/workflow-init.md` §5 (257–777, fenced 192–778) | the scaffolded mirror | -| `plugins/dev-workflow/commands/workflow-init.md` (outside the fence) | the item-1 n/a note | -| `README.md`, `docs/getting-started.md`, `docs/coding-workflow.md` | sentences this change falsifies | -| `plugins/dev-workflow/.claude-plugin/plugin.json`, `CHANGELOG.md` | packaging | -| `docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md` | the old-conditions dispositions (Task 0) | - -**Line numbers in this plan were read on 2026-08-29 and drift as edits land.** Every task locates -its site by **quoted text**, not by number; the numbers are navigation aids only. +| File | Responsibility | Gate B | +|---|---|---| +| `CLAUDE.md` §5 | the live rules | full (in the cycle) | +| `plugins/dev-workflow/commands/workflow-init.md` §5 | the scaffolded mirror | full (in the cycle) | +| same file, outside the fence | the item-1 n/a note | full (in the cycle) | +| `plugins/dev-workflow/.claude-plugin/plugin.json`, `CHANGELOG.md` | packaging | full (in the cycle) | +| `README.md`, `docs/getting-started.md`, `docs/coding-workflow.md` | falsified sentences | **N/A**, committed alone | +| `docs/superpowers/specs/…-conditions.md` | old-conditions dispositions | **N/A** | --- -## Task 0: The conditions artifact, and its gate +## Task 0: The conditions artifact -**This task runs FIRST and nothing else proceeds until its review passes.** The AGENTS.md Don't — -"never replace a decision procedure without accounting for its old conditions" — is satisfied by -this artifact existing and being reviewed *before* any replacement text is written. +**Not a gate that needs its own output to start.** This artifact is written as part of this plan +revision and is **reviewed by the plan's own Gate-A passes alongside the plan**; its acceptance is +that cycle's clean pass. Nothing waits on a separate approval. -**Files:** -- Create: `docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md` +**Files:** Create `docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md` -**Interfaces:** -- Consumes: the spec's §5.2 passage list (18 rows) and §5.1 site inventory. -- Produces: a dispositioned entry per passage that Tasks 1–5 must not contradict. +### The nineteen passages — provenance stated -- [ ] **Step 1: Regenerate the site inventory rather than copying it** +The spec no longer carries this list: revision 15 slimmed it out and put it here. It was recovered +mechanically, not from memory: ```bash -grep -nE "min 3 passes|below 3|3-pass|3 passes|where the 3 come from|3-passes-per-gate" \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git show 46072fe:docs/superpowers/specs/2026-08-28-review-loop-economics-design.md \ + | grep -E '^\| [0-9]+ \| ' ``` -Expected: **12 lines, six per copy.** If the count differs, the spec's inventory is stale and that -is a finding for the spec, not something to paper over here. +That revision claimed "Eighteen" and its table holds 18 rows — **claim and content agree**, so the +recovery is sound. **Reconciled against revision 36, it is nineteen**: revision 36's §2.2 requires +the pass report to state the derived floor, the axes read and their source stories, which rewrites +the `From pass 4 onward every pass report carries three lines` paragraph — present once in each +copy, and absent from the recovered list because it went to the successor story during the split +and came back when §2.2 was added. + +| # | Passage (quoted by its bold lead-in) | +|---|---| +| 1 | `**Both gates are a LOOP with a HARD FLOOR` | +| 2 | the early-exit sentence — `below 3 is a pass with **zero** findings` | +| 3 | the incomplete-pass rule — `don't count it toward the 3-pass floor` | +| 4 | `Lenses are **different questions, not more passes.**` | +| 5 | `**The Gate-B triviality skip needs two independent conditions**` | +| 6 | `**A cycle citing several stories**` | +| 7 | `- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).**` | +| 8 | `- **Gate B — Code.**` | +| 9 | `- **Severity:** Blocker (wrong/unsafe/breaks invariant)` | +| 10 | `**Changing a profile:**` | +| 11 | the findings-slot naming paragraph (`` is `gate-a-spec-pass-

`, …) | +| 12 | `**Before each call, delete every target file and confirm it is gone.**` | +| 13 | `**Recognizing "clearly stuck"` | +| 14 | `**Recording a human exception.**` | +| 15 | `**The evidence entry lives in the commit body**` | +| 16 | `**Optional companions, from field practice.**` | +| 17 | `**On squash-merge, copy every evidence entry` | +| 18 | the `baseSha` / WIP / closing-amend block | +| 19 | `**From pass 4 onward every pass report carries three lines.**` | + +- [ ] **Step 1: Confirm all nineteen exist exactly once in both copies** + +```bash +while IFS= read -r p; do + a=$(grep -cF "$p" CLAUDE.md) + b=$(grep -cF "$p" plugins/dev-workflow/commands/workflow-init.md) + [ "$a" = 1 ] && [ "$b" = 1 ] || printf 'MISMATCH %s/%s: %s\n' "$a" "$b" "$p" +done <<'PATS' +**Both gates are a LOOP with a HARD FLOOR +below 3 is a pass with +don't count it toward the 3-pass floor +Lenses are **different questions, not more passes.** +The Gate-B triviality skip needs two independent conditions +A cycle citing several stories +Gate A — Spec, then plan (TWO runs, each its own 3-pass loop) +Gate B — Code. +**Severity:** Blocker (wrong/unsafe/breaks invariant) +Changing a profile: +gate-a-spec-pass-

+Before each call, delete every target file and confirm it is gone. +Recognizing "clearly stuck" +Recording a human exception. +The evidence entry lives in the commit body +Optional companions, from field practice. +On squash-merge, copy every evidence entry +**`baseSha`:** against main = merge-base with main +From pass 4 onward every pass report carries three lines +PATS +``` + +Expected: **no output.** Any MISMATCH means the list is stale and that is a finding, not something +to route around. + +- [ ] **Step 2: Regenerate the floor-site inventory** + +```bash +grep -nE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md | wc -l +``` -- [ ] **Step 2: Confirm the two digit-free sites the grep cannot find** +**Demonstrated now: 14** — seven per copy. That is the inventory, and it already includes the +digit-free `pass 1` site because the pattern `if pass 3 still` and the `carrying a Minor` sentence +both fall inside it. Confirm the digit-free one separately: ```bash -grep -n 'pass 1 carrying a Minor' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -n 'carrying a Minor keeps' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -Expected: exactly one hit per copy. **Fourteen sites in total.** +**Demonstrated now:** `CLAUDE.md:126` and `workflow-init.md:322`. - [ ] **Step 3: Write one dispositioned entry per passage** -For each of the spec's eighteen passages, in a table with these columns: *passage* (quoted by its -bold lead-in), *what its existing prose requires* (enumerated, one requirement per line), and -*disposition* — **kept**, **moved** (say where), or **deliberately dropped** (say why). A -requirement neither kept nor explicitly dropped is a dropped condition and fails this task. +Columns: *passage*, *what its existing prose requires* (one requirement per line, read from the +file, not recalled), *disposition* — **kept** / **moved** (say where) / **deliberately dropped** +(say why). A requirement neither kept nor explicitly dropped is a dropped condition and fails this +task. -- [ ] **Step 4: Verify completeness mechanically** +- [ ] **Step 4: Verify entry count against the list** ```bash -sed -n '/^| # | Passage/,/^$/p' docs/superpowers/specs/2026-08-28-review-loop-economics-design.md \ - | grep -c '^| [0-9]' -grep -c '^| ' docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md +grep -c '^| [0-9]* |' docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md ``` -Expected: the artifact has an entry for every passage the spec lists. A missing row is the failure -this artifact exists to prevent. +Expected: **19**. -- [ ] **Step 5: Commit** +- [ ] **Step 5: Commit — ordinary commit, Gate B N/A** ```bash git add docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md -git commit -m "docs(spec): the old-conditions dispositions for the review-loop-economics change" +git diff --cached --name-only # confirm ONLY this path +git commit -m "docs(spec): old-conditions dispositions for the review-loop economics change" ``` -- [ ] **Step 6: GATE — this artifact is reviewed inside the Gate-A plan cycle** - -It is an input that cycle reviews alongside this plan, at the derived floor, with a clean pass as -its acceptance. **No replacement text is written while it is outstanding.** A finding against it -feeds back — regenerate against corrected text — rather than being absorbed. - --- -## Task 1: The floor predicate +## Task 1: Open the Gate-B cycle, and derive the floor -**Files:** -- Modify: `CLAUDE.md` — the paragraph opening `**Both gates are a LOOP with a HARD FLOOR:` -- Modify: `plugins/dev-workflow/commands/workflow-init.md` — the same paragraph in the template +**Files:** `CLAUDE.md:72`, `plugins/dev-workflow/commands/workflow-init.md:272` **Interfaces:** -- Consumes: Task 0's dispositions for that passage. -- Produces: the phrase `max(risk, security)` in both copies, which Task 2's sites refer back to. +- Produces: `max(risk, security)` inside the floor paragraph, which Task 2's sites refer back to. -- [ ] **Step 1: Write the check, and watch it fail** +- [ ] **Step 1: Run the check and see it fail** ```bash -# Both copies must state the predicate, unanimity, and the three-cycle scope. for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do printf '%s: ' "$f" - tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'max(risk, security)' + sed -n '/Both gates are a LOOP with a HARD FLOOR/,/^$/p' "$f" | grep -c 'max(risk, security)' done ``` -Expected now: `0` and `0`. **Run it before editing** — a check first seen passing has proved -nothing about the change. +**Demonstrated failing output, 2026-08-29:** +``` +CLAUDE.md: 0 +plugins/dev-workflow/commands/workflow-init.md: 0 +``` -- [ ] **Step 2: Replace the floor sentence in `CLAUDE.md`** +**The `sed` scoping is load-bearing.** An unscoped `grep -c 'max(risk, security)'` returns **1** +for both files today, because the Profiles section already contains the phrase — the check would +pass before the edit and prove nothing. That was a real finding against the first version of this +plan. -Current text opens: `**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major` +- [ ] **Step 2: Replace the floor clause in both copies** -Replace `min 3 passes per run` with the derived floor, keeping **every other requirement in that -paragraph verbatim** (the TodoWrite per pass, fix-after-each, the clean final pass, the -zero-findings early exit, Codex-is-advisory, the one-line dismissal reason): +Anchor, pasted from `grep -n` (identical in both files): +``` +72:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major +272:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major +``` + +Replace `min 3 passes per run` with the derived floor, **keeping every other requirement in that +paragraph verbatim**: ```markdown **Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run (Blocker/Major -only), derived from the story profile — `max(risk, security) == 0` gives **1**, and every -resolvable profile above that, or an artifact citing no story, gives **3**. A cited story whose -profile is present but unresolvable **stops and surfaces** under the rule below rather than -falling through to 3. A cycle citing several stories reaches 1 **only if the cited set is -non-empty and every member is profiled, resolvable and at level 0**; any other set gives 3. -**One derived value governs all three cycles** — the Gate-A spec cycle, the Gate-A plan cycle and -the Gate-B cycle — because they derive from the same cited-story set, not because they are one -cycle. The floor is counted by the hook,** +only), derived from the story profile — `max(risk, security) == 0` gives **1**; every resolvable +profile above that, and an artifact citing no story, gives **3**. A cited story whose profile is +present but unresolvable **stops and surfaces** under the existing rule rather than falling +through to 3. A cycle citing several stories reaches 1 **only if the cited set is non-empty and +every member is profiled, resolvable and at level 0**; any other set gives 3. **One derived value +governs all three cycles** — the Gate-A spec cycle, the Gate-A plan cycle and the Gate-B cycle — +because they derive from the same cited-story set, not because they are one cycle. ``` -- [ ] **Step 3: Make the identical edit in the template** +- [ ] **Step 3: Add the pass-report and residual rules to both copies** -Same paragraph inside the fenced block. **Byte-identical** to Step 2's replacement. +Closing the coverage gap the first version left. Append to the same paragraph: + +```markdown +**Every pass report states the derived floor, the risk and security values read, and the cited +stories they were read from** — the number alone leaves a reader unable to check the derivation +while passes are still being spent. **The derived floor is the count a cycle owes; the hook's +ratio is a reminder threshold and controls nothing**, so where the cycle's own closure rules are +satisfied a below-threshold reminder is noted in the pass report and disregarded. This replaces +the pass-count number and nothing else; every other rule here about how a cycle closes stands as +written. **The floor is produced by the agent and nothing checks it** — not against the cited +profiles, not anywhere — so a stated floor the cited set does not license, an omitted higher-risk +story, a minted level-0 profile or an incomplete cited set are all routes to fewer passes, and +naming them is a disclosure rather than a guard. **The workspace knob is never written, never +removed and never read for this derivation**; it remains the hook's reminder threshold. +``` - [ ] **Step 4: Re-run the check** Expected: `1` and `1`. -- [ ] **Step 5: Verify no other requirement in the paragraph was lost** +- [ ] **Step 5: Verify the paragraph's tail survived** ```bash for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do @@ -185,242 +279,210 @@ for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do done ``` -Expected: `1` and `1`. That clause is the tail of the same paragraph and its survival is the cheap -proxy for "the replacement did not swallow its neighbours." +Expected: `1` and `1`. **Demonstrated present now**, so this check catches a replacement that +swallowed its neighbours. -- [ ] **Step 6: Commit** +- [ ] **Step 6: OPEN THE CYCLE with a WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit -m "feat(gates): derive the pass floor from the story profile" +git commit -m "WIP: review-loop economics — floor, severity, records" ``` -**Gate B applies to this commit** — both paths are prompts. +**This message must begin `WIP:`.** Every later task in this cycle amends it. Do not run an +ordinary commit again until Task 9's close. --- ## Task 2: The fourteen floor-wording sites -Every site states a rule that a variable floor falsifies. **The one that matters most spells no -digit**: `a Blocker/Major-free pass 1 carrying a Minor keeps looping` is correct at floor 3 and -**false at floor 1**, where pass 1 is *at* the floor and therefore closes. - -**Files:** `CLAUDE.md` and the template, at the seven sites each. +**Interfaces:** consumes Task 1's predicate; produces nothing. -**Interfaces:** -- Consumes: Task 1's `max(risk, security)` predicate. -- Produces: nothing later tasks depend on. - -- [ ] **Step 1: Write the check, and watch it fail** +- [ ] **Step 1: Run the check and see it fail** ```bash -grep -cE "min 3 passes|below 3|3-pass|3 passes|where the 3 come from" \ +grep -cE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -c 'pass 1 carrying a Minor' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -Expected now: nonzero counts. After Task 2 both must be **0**, except the historical citation — -see Step 4. +**Demonstrated output, 2026-08-29:** `CLAUDE.md:7` and `workflow-init.md:7`. After this task both +must be **0**. -- [ ] **Step 2: Apply the seven replacements in each copy** +- [ ] **Step 2: Apply seven replacements per copy** -| Quoted current text | Replacement | -|---|---| -| `below 3 is a pass with **zero** findings` | `below the floor is a pass with **zero** findings` | -| `a Blocker/Major-free pass 1 carrying a Minor keeps looping` | `a Blocker/Major-free pass **below the floor** carrying a Minor keeps looping` | -| `don't count it toward the 3-pass floor` | `don't count it toward the floor` | -| `each its own 3-pass loop` | `each its own loop at the derived floor` | -| `which is where the 3 come from` | `which is where the floor's lower bound comes from` | -| `The 3-pass floor, the Blocker/Major` | `The floor, the Blocker/Major` | -| `if pass 3 still finds Blocker/Major` | `if the pass at the floor still finds Blocker/Major` | +Anchors pasted from `grep -n`; **CLAUDE.md line : template line**. + +| Anchor (verbatim, as the file holds it) | Lines | Replacement | +|---|---|---| +| `final pass must be clean — if pass 3 still finds Blocker/Major, keep going until` | 77 : 277 | `…— if the pass at the floor still finds Blocker/Major, keep going until` | +| `below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is` | 79 : 279 | `below the floor is a pass with **zero** findings; …` | +| `the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps` | 126 : 322 | `…; a Blocker/Major-free pass **below the floor** carrying a Minor keeps` | +| `act on the partial list, don't count it toward the 3-pass floor, and don't read "no` | 236 : 421 | `…don't count it toward the floor, and don't read "no` | +| `- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the` | 300 : 485 | `- **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the` | +| ` invalidates the prior pass, which is where the 3 come from.` | 335 : 519 | ` invalidates the prior pass, which is where the floor's lower bound comes from.` | +| `Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major` | 403 : 582 | `Lenses are **different questions, not more passes.** The floor, the Blocker/Major` | + +**The `pass 1` sentence continues onto the next line** — `looping.` sits at 127/323. Match the +fragment above, not a reconstructed whole sentence. That mismatch broke the first version. - [ ] **Step 3: Re-run the check** -Expected: `0` for both greps in both files. +Expected: `0` and `0`. -- [ ] **Step 4: Confirm the historical citation was NOT changed** +- [ ] **Step 4: Confirm the historical citation was NOT touched** ```bash grep -c "PR #23's Gate-B pass 3 returned all four findings" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -Expected: `1` and `1`. That sentence cites an actual historical pass, not a rule, and **must keep -saying 3**. Changing it would falsify a record. +Expected `1` and `1`. **Demonstrated present now.** It cites an actual pass, not a rule, and must +keep saying 3 — changing it would falsify a record. -- [ ] **Step 5: Commit** +- [ ] **Step 5: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit -m "fix(gates): reword every rule that assumed a floor of three" +git commit --amend --no-edit ``` +The message keeps its `WIP:` prefix. **No ordinary commit.** + --- ## Task 3: Severity semantics -**Files:** the `**Severity:**` line in Mechanics, both copies. +**Files:** `CLAUDE.md:495`, `plugins/dev-workflow/commands/workflow-init.md:674` -**Interfaces:** -- Consumes: nothing. -- Produces: the reachability test, which Task 6's parity walk checks. - -- [ ] **Step 1: Write the check, and watch it fail** +- [ ] **Step 1: Run the check and see it fail** ```bash for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f" - tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'consumes this text' + printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'consumes this text' done ``` -Expected now: `0` and `0`. +**Demonstrated failing output, 2026-08-29:** `0` and `0`. -- [ ] **Step 2: Extend the Severity line in `CLAUDE.md`** +- [ ] **Step 2: Extend the Severity entry in both copies** -Keep the four existing definitions verbatim and append the classifier: +Anchor, pasted from `grep -nF`: +``` +CLAUDE.md:495:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → +plugins/dev-workflow/commands/workflow-init.md:674:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → +``` -```markdown -- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → - rework) → both must resolve. Minor · Nit → collect, never iterate. - **Which side of that line a finding falls on is decided by one test, not by what kind of file - the text lives in:** name **what in the system consumes this text** — whatever *acts* on it — - and the decision that act takes differently if the text is wrong. **Both are required**; if you - cannot name both, the finding is **Minor or below**. The reader must consume the text in the - system's *operation*, not in reviewing it — **the review pass raising the finding is not an - in-system reader of the text it reviews**, or the test would demote nothing, though gates remain - legitimate readers of rule text they will later apply. A human reader never satisfies it: that - cost is already priced as non-gating by the prose exemption. The list of reader kinds is - illustrative, not closed. **The test sets a ceiling and never chooses between Blocker and - Major** — the definitions above still do that. Findings about narration, prose describing a - mechanism, and test-instrument internals are the cases this usually catches, as worked examples - rather than a second rule. **An instrument finding keeps its severity whenever it shows the - instrument changes what a gate concludes about product behaviour, in either direction** — a - false green, and equally a false red or a check blocking a valid change. **Rationale prose is - Minor only when no rule's application depends on it**, not categorically: `docs/prompt-standards.md` - requires that rules carry their why, so rationale a reader must consult to apply a rule passes - the test. This removes arbitrariness, not judgement. **This is the finding-level analog of the - path-level prose exemption** — one principle at two granularities, text that *describes* the - product versus text that *is* the product. Coverage-first is unchanged: the reviewer reports - every finding with severity and confidence; the filter is ours. -``` - -- [ ] **Step 3: Make the identical edit in the template** +Keep the four definitions verbatim; append the classifier from spec §3 — the reachability test +with both halves required, the reviewing-pass exclusion, the human-reader exclusion, the +illustrative reader list, the ceiling-not-floor rule, the symmetric instrument carve-out, the +qualified rationale case, the kinship sentence, and coverage-first. -- [ ] **Step 4: Re-run the check** +- [ ] **Step 3: Re-run the check** Expected: `1` and `1`. -- [ ] **Step 5: Verify the four definitions survived** +- [ ] **Step 4: Verify the four definitions survived** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f" - grep -c 'Blocker (wrong/unsafe/breaks invariant)' "$f" -done +grep -cF '**Severity:** Blocker (wrong/unsafe/breaks invariant)' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -Expected: `1` and `1`. +Expected `1` and `1`. -- [ ] **Step 6: Commit** +- [ ] **Step 5: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit -m "feat(gates): decide severity by consequence, not by artifact kind" +git commit --amend --no-edit ``` --- ## Task 4: The two pinned records and the cycle nonce -**Files:** both copies — Mechanics' closing-commit area and the findings-slot paragraph. - -**Interfaces:** -- Consumes: Task 1's derived floor (the provenance line reports it). -- Produces: ``, which Task 5's squash-carry rule names. - -- [ ] **Step 1: Write the check, and watch it fail** +- [ ] **Step 1: Run the check and see it fail** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f"; grep -c 'cycle none (pre-rule)' "$f" -done +grep -c 'cycle none (pre-rule)' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -Expected now: `0` and `0`. +**Demonstrated failing output, 2026-08-29:** `0` and `0`. -- [ ] **Step 2: Add both grammars and the nonce rules to `CLAUDE.md`** +- [ ] **Step 2: Copy both grammars from the spec** -Copy §2.3, §4 and §5 of the spec's grammars **verbatim** — they are already pinned there and -retyping is how a production drifts. Add the surrounding rules: every cycle records a provenance -line; the curve records Findings, Blockers and Majors per valid pass with the pass numbers it -covers; an unrecoverable count is `?`, excluded **per series** and not per pass; a skipped cycle -records a skip line in place of a curve; the nonce is 8–16 chars of `[a-z0-9]` from randomness, -appears in the provenance line, the curve, the cycle's findings slots and its advisory working -record, and a cycle that cannot recover a single candidate starts fresh. +**From spec §2.3 (provenance) and §4 (curve) only** — §5 of the spec is the nonce prose and holds +no grammar. Copy the fenced blocks **verbatim**; retyping is how a production drifts. -- [ ] **Step 3: Widen the findings-slot grammar in both copies** +- [ ] **Step 3: Add the surrounding rules** -The three slot names gain an optional per-cycle infix — `gate-a-spec[-]-pass-

` and its -two siblings — with the infix **required whenever more than one cycle could write that slot**, and -a target owned by another cycle **refused rather than overwritten**. +Every cycle records a provenance line. The curve records Findings, Blockers and Majors per valid +pass with the pass numbers covered; an unrecoverable count is `?`, excluded **per series** and not +per pass; a skipped cycle records a skip line in place of a curve. -- [ ] **Step 4: Make the identical edits in the template** +**The nonce**, with the generation policy the spec delegates here: 8–16 chars of `[a-z0-9]` from a +source of randomness, never derived from a name, timestamp or commit. **On failure — randomness +unavailable, an invalid value, or a collision with an open cycle — retry at most three times, then +stop and surface. No deterministic fallback.** It appears in the provenance line, the curve, the +cycle's findings slots and its advisory working record. A cycle that cannot recover exactly one +candidate starts fresh. -- [ ] **Step 5: Re-run the check, and parse the grammars** +- [ ] **Step 4: Widen the findings-slot grammar in both copies** + +Anchor: the paragraph containing `gate-a-spec-pass-

`. The three names gain an optional per-cycle +infix, **required whenever more than one cycle could write that slot**, with a target owned by +another cycle **refused rather than overwritten**. + +- [ ] **Step 5: Re-run the check, then parse the grammars** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f"; grep -c 'cycle none (pre-rule)' "$f" -done +grep -c 'cycle none (pre-rule)' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -Expected: `1` and `1`. +Expected `1` and `1`. -Then hand-parse the spec's five provenance examples and its curve example against the productions -as written in the shipped copies. **A grammar nothing ever parsed is a format claim, not a -format.** Record which features each example exercised: quoted path, each unusable-knob cause, -gapped ranges, split-model pass, skipped cycle, `?` count, ``/`` cardinality. +Then **construct** instances and parse them against the productions **as they now read in the +shipped copies** — the spec carries no worked examples to reuse. Cover: a quoted path; each +`unusable()` value; a gapped `` like `1,2,4`; a split-model pass; a skipped cycle; a +`?` count; and one instance that must be **rejected** (`` shorter than ``). Record +which feature each exercised. **A grammar nothing ever parsed is a format claim, not a format.** -- [ ] **Step 6: Commit** +- [ ] **Step 6: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit -m "feat(gates): pin the provenance line, the per-pass curve and the cycle nonce" +git commit --amend --no-edit ``` --- ## Task 5: The squash carry -**Files:** the `On squash-merge, copy every evidence entry` paragraph, both copies. +**Files:** `CLAUDE.md:519`, `plugins/dev-workflow/commands/workflow-init.md:698` -**Interfaces:** -- Consumes: Task 4's record types. -- Produces: nothing later tasks depend on. - -- [ ] **Step 1: Write the check, and watch it fail** +- [ ] **Step 1: Run the check and see it fail** ```bash for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f" - tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'provenance line, the per-pass curves' + printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'the per-pass curves' done ``` -Expected now: `0` and `0`. - -- [ ] **Step 2: Extend the list in both copies** +**Demonstrated failing output, 2026-08-29:** `0` and `0`. -Add **the provenance line, the per-pass curves, and a skipped cycle's skip record** to what a -squash must carry. **Extend the sentence — do not rewrite it.** The successor story adds the -decline record to this same passage later, and a rewrite there would drop what this adds. +- [ ] **Step 2: Extend the list — do not rewrite the sentence** -- [ ] **Step 3: Re-run the check** +Anchor, pasted from `grep -nF` (the whole rule is one long line): +``` +CLAUDE.md:519: **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — …** +plugins/dev-workflow/commands/workflow-init.md:698: (identical) +``` -Expected: `1` and `1`. +Add **the provenance line, the per-pass curves, and a skipped cycle's skip record**. The successor +story adds the decline record to this same passage later; a rewrite there would drop what this +adds, which is why this is an extension. -- [ ] **Step 4: Verify the existing two survived** +- [ ] **Step 3: Re-run the check, and confirm the original two survived** ```bash for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do @@ -429,278 +491,258 @@ for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do done ``` -Expected: `1` and `1`. +Expected `1` and `1` for both checks. -- [ ] **Step 5: Commit** +- [ ] **Step 4: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit -m "feat(gates): carry the provenance line, curves and skip records through a squash" +git commit --amend --no-edit ``` --- -## Task 6: Parity, and the twelve-item conformance pass +## Task 6: The item-1 note -No new text — this is the verification story criterion 7 and invariant 11 require. +**Runs before Task 7's conformance pass**, because that pass certifies this file and must see its +final state. The first version had them the other way round. -- [ ] **Step 1: Walk every changed rule across both copies** +**Files:** `plugins/dev-workflow/commands/workflow-init.md`, **outside** the fenced block. + +- [ ] **Step 1: Record the count that must not change** -For each rule Tasks 1–5 added, extract it from both files and diff. Record every difference as -**deliberate-and-stated** or as a **defect**. The copies already diverge on ~192 lines overall, so -**a whole-section diff proves nothing** — walk the named rules. +```bash +grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md +``` -- [ ] **Step 2: Run the twelve-item pass** +**Demonstrated now: 1.** It must still be 1 afterwards — `scripts/check-invariants.sh` fails on +any other count, and adding a second declaration is exactly what a naive fix would do. -Against **each changed prompt artifact as a complete prompt**: the resulting scaffolded template -and `plugins/dev-workflow/commands/workflow-init.md`. Item 7 — no contradictions with -`CLAUDE.md`/`AGENTS.md` — is read against the whole resulting prompt, since a contradiction is a -relation between an edited passage and an unedited one. **Root `CLAUDE.md` is outside invariant -11's list and is not part of this pass.** +- [ ] **Step 2: Add the note immediately before the fence** -- [ ] **Step 3: Record item 1's n/a for the scaffolded template** +The fence opens at `192:````markdown`. Place the note **before** that line: -Satisfied-or-n/a-with-reason, per the story's conformance criterion. The reason: the scaffolded -file is model-agnostic by design, so a target-model line would be false in every repo it lands in. +```markdown +> **Prompt-standards item 1 for the scaffolded `CLAUDE.md`: n/a, and why.** The file this template +> writes is model-agnostic by design — its executing model is whatever the reader of that project +> runs — so a `Target model:` line inside it would be false in every repo it lands in. Recorded as +> a reasoned n/a rather than skipped: the item is answered. This note sits **outside the fence** so +> it never scaffolds, and is deliberately **not** a `Target model:` line, which would make this +> file's declaration count 2 and fail `scripts/check-invariants.sh`. +``` -- [ ] **Step 4: Commit the record** +- [ ] **Step 3: Verify the count and the placement** ```bash -git add docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md -git commit -m "docs(spec): record the parity walk and the twelve-item conformance pass" +grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md +awk 'NR>=185 && NR<=200' plugins/dev-workflow/commands/workflow-init.md +sh scripts/check-invariants.sh && echo INVARIANTS-OK ``` ---- +Expected: `1`; the note visible **before** the ```` ````markdown ```` line; checker exits 0. -## Task 7: The falsified user-facing sentences +- [ ] **Step 4: Amend the WIP commit** -**Commit these alone.** Every staged path here is `docs/**.md` or `README.md`, so §5's prose -exemption applies and Gate B is N/A — **but only if nothing else is staged.** A mixed commit -forfeits the exemption. +```bash +git add plugins/dev-workflow/commands/workflow-init.md +git commit --amend --no-edit +``` -**Files:** `README.md`, `docs/getting-started.md`, `docs/coding-workflow.md`. +--- -- [ ] **Step 1: Write the check, and watch it fail** +## Task 7: Parity and the twelve-item conformance pass -```bash -grep -nE 'three passes minimum|3-pass floor|3-passes-per-gate|floor is unchanged at every level|Gate A.s floor and' \ - README.md docs/getting-started.md docs/coding-workflow.md -``` +No new text. This is the verification story criterion 7 and invariant 11 require. -Expected now: several hits. After this task: **zero**. +- [ ] **Step 1: Walk every changed rule across both copies** -- [ ] **Step 2: Correct each sentence** +For each rule Tasks 1–5 added, extract it from both files and diff. Record each difference as +**deliberate-and-stated** or **defect**. The copies already diverge on ~192 lines overall, so a +whole-section diff proves nothing — walk the named rules. -| Site | Current | Correction | -|---|---|---| -| `README.md:130` | "a positive integer; moves the 3-passes-per-gate floor" | "a positive integer; moves the **hook's reminder threshold**. It does not change the floor §5 obliges, which is derived from the story profile." | -| `getting-started.md:34` | "three passes minimum, final pass clean" | "the floor its profile derives, final pass clean" | -| `:40` | "the same 3-pass loop runs" | "the same loop runs at the derived floor" | -| `:44` | "If the Gate-A floor wasn't met, the hook says so" | "If the hook's own threshold wasn't met it says so — which at a derived floor of 1 can fire when nothing further is owed" | -| `:53` | "three passes, final clean" | "the derived floor, final clean" | -| `:58` | waits for `✓ … (3/3 …)` | show the ratio as the derived floor, not a literal 3/3 | -| `:84` | "Gate A's floor is unchanged at every level" | "Gate A's floor is derived from the profile like Gate B's; what the axes never subtract is the baseline questions" | -| `:86` | "moves the 3-pass floor" | "moves the hook's reminder threshold" | -| `coding-workflow.md:79-80` | "they never subtract any: Gate A's floor and the baseline questions are the same at every level" | "they never subtract any baseline question; the floor itself is derived from the profile" | +- [ ] **Step 2: Run the twelve items against each changed prompt artifact** -- [ ] **Step 3: Re-run the check** +The resulting scaffolded template, and `plugins/dev-workflow/commands/workflow-init.md` as the +outer command prompt. **Item 7 is read against the whole resulting artifact**, not the diff. +**Root `CLAUDE.md` is outside invariant 11's list and is not part of this pass.** -Expected: zero hits. +- [ ] **Step 3: Record item 1's n/a for the scaffolded template**, with the reason from Task 6. -- [ ] **Step 4: Commit, alone** +- [ ] **Step 4: Append the results to the conditions artifact and commit — Gate B N/A** ```bash -git add README.md docs/getting-started.md docs/coding-workflow.md -git diff --cached --name-only # confirm ONLY these three -git commit -m "docs: correct every sentence the derived floor falsifies" +git add docs/superpowers/specs/2026-08-28-review-loop-economics-conditions.md +git diff --cached --name-only # confirm ONLY this path +git commit -m "docs(spec): parity walk and twelve-item conformance results" ``` -**Gate B: N/A** — every staged path is explanatory documentation. Verify the staged set before -committing; that verification is what earns the exemption. +**This is an ordinary commit and it is correct here** — the staged path is `docs/superpowers/**.md` +and the WIP cycle's counters are unaffected by a commit that touches none of its files. Amend the +WIP commit again in Task 8 before reviewing. --- -## Task 8: The item-1 note beside the template +## Task 8: The falsified user-facing sentences — committed alone -**Files:** `plugins/dev-workflow/commands/workflow-init.md`, **outside** the fenced template block. - -- [ ] **Step 1: Write the check, and watch it fail** +- [ ] **Step 1: Run the check and see it fail** ```bash -grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md +grep -cE "3-passes-per-gate|three passes minimum|same 3-pass loop|Gate-A floor wasn't met|three passes, final clean|3/3 cycle|floor is unchanged at every level|moves the 3-pass floor|Gate A's floor and" \ + README.md docs/getting-started.md docs/coding-workflow.md ``` -Expected: `1` — and it must **still be 1** after this task. `scripts/check-invariants.sh` fails on -any other count. +**Demonstrated output, 2026-08-29:** `README.md:1`, `docs/coding-workflow.md:1`, +`docs/getting-started.md:7` — **nine hits, matching the nine sites below.** The first version's +grep found only five and its site list said nine. -- [ ] **Step 2: Add the note immediately before the fence** +- [ ] **Step 2: Correct each site** -```markdown -> **Prompt-standards item 1 for the scaffolded `CLAUDE.md`: n/a, and why.** The file this template -> writes is model-agnostic by design — its executing model is whatever the reader of that project -> runs — so a `Target model:` line would be false in every repo it lands in. Recorded as a reasoned -> n/a rather than skipped. This note is deliberately outside the fence so it never scaffolds, and -> deliberately not a `Target model:` line, which would make this file's declaration count 2 and -> fail `scripts/check-invariants.sh`. -``` +Anchors pasted from `grep -nF`: -- [ ] **Step 3: Re-run the check** +| Anchor | Correction | +|---|---| +| `README.md:130:` `\| `codex-gate.floor` \| a positive integer; moves the 3-passes-per-gate floor. \|` | `… moves the **hook's reminder threshold**. It does not change the floor §5 obliges, which is derived from the story profile.` | +| `getting-started.md:34:three passes minimum, final pass clean — the one early exit is a pass that comes` | `the floor its profile derives, final pass clean — the one early exit is a pass that comes` | +| `:40:task-by-task plan (each task starts with a failing test); the same 3-pass loop runs` | `…; the same loop runs at the derived floor` | +| `:44:progress claims backed by test runs. If the Gate-A floor wasn't met, the hook says` | `… If the hook's own threshold wasn't met, it says` | +| `:53:` `` `mcp__codex__review` the same way: three passes, final clean. Verification is by`` | `… the same way: the derived floor, final clean. Verification is by` | +| `:58:` `` `✓ Codex Gate B satisfied (3/3 cycle, 3 on current fingerprint)`, the real commit replaces`` | `` `✓ Codex Gate B satisfied (N/N cycle, N on current fingerprint)` — N being the derived floor — the real commit replaces `` | +| `:84:is still owed and Gate A's floor is unchanged at every level. The caution bias is` | `is still owed; Gate A's floor is derived from the profile like Gate B's, and what the axes never subtract is the baseline questions. The caution bias is` | +| `:86:positive integer) moves the 3-pass floor, and `touch .context/codex-gate.off`` | `positive integer) moves the hook's reminder threshold, and …` | +| `coding-workflow.md:79:gates for a risky or security-relevant change (they never subtract any: Gate A's floor and` | `gates for a risky or security-relevant change (they never subtract a baseline question; the floor itself is derived from the profile, and` | -```bash -grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md -sh scripts/check-invariants.sh && echo INVARIANTS-OK -``` +- [ ] **Step 3: Re-run the check** -Expected: `1`, and the checker exits 0. +Expected: zero hits in all three files. -- [ ] **Step 4: Confirm the note is outside the fence** +- [ ] **Step 4: Commit alone — Gate B N/A** ```bash -awk 'NR>=186 && NR<=200' plugins/dev-workflow/commands/workflow-init.md +git add README.md docs/getting-started.md docs/coding-workflow.md +git diff --cached --name-only # MUST list exactly these three +git commit -m "docs: correct every sentence the derived floor falsifies" ``` -Expected: the note appears **before** the ```` ````markdown ```` line. Inside it, it would scaffold -into every user's `CLAUDE.md`. +**Verify the staged set before committing.** That verification is what earns the exemption; a +mixed commit forfeits it. -- [ ] **Step 5: Commit** +--- -```bash -git add plugins/dev-workflow/commands/workflow-init.md -git commit -m "docs(workflow-init): record item 1's n/a for the scaffolded CLAUDE.md" -``` +## Task 9: Packaging, then close the cycle ---- +- [ ] **Step 1: Bump the manifest** -## Task 9: Packaging +`plugins/dev-workflow/.claude-plugin/plugin.json`: `0.10.0` → `0.11.0`. Minor — shipped rules +change, no documented interface breaks. -- [ ] **Step 1: Write the check, and watch it fail** +- [ ] **Step 2: Add the CHANGELOG entry**, newest first. **Nothing enforces this** — neither + invariant 12 nor the checker mentions the changelog — so the story's criterion carries it. + +- [ ] **Step 3: Amend the WIP commit, THEN run the version check** ```bash -sh scripts/check-version-bump.sh main && echo BUMP-OK || echo BUMP-MISSING +git add plugins/dev-workflow/.claude-plugin/plugin.json plugins/dev-workflow/CHANGELOG.md +git commit --amend --no-edit +sh scripts/check-version-bump.sh main && echo BUMP-OK ``` -Expected before the bump: the checker reports the plugin changed without a version change. -**Run it before bumping** — it is the only step here whose failure state is observable. +**Order matters and this is why:** `check-version-bump.sh` compares *committed* state against the +merge-base and ignores the worktree and index. Run before the amend it reports clean — correctly +and uselessly, as `AGENTS.md` says in as many words. The first version of this plan ran it first +and called that a failing check. -- [ ] **Step 2: Bump the manifest** +- [ ] **Step 4: Run the full battery** -`plugins/dev-workflow/.claude-plugin/plugin.json`: `0.10.0` → `0.11.0`. Minor, because this -changes shipped rules without breaking a documented interface. +The whole `AGENTS.md` § Commands chain, exit 0, with assertion counts recorded. -- [ ] **Step 3: Add the CHANGELOG entry** +- [ ] **Step 5: Gate B — the review loop** -Newest first, describing the floor derivation, the severity test, the two pinned records and the -nonce. **Nothing enforces this** — neither invariant 12 nor the checker mentions the changelog — -so the story's criterion is what carries it. +Against the WIP commit, `baseSha` = its parent. Floor 3 (this story is risk `high`). Re-review +after every fix; the final pass must be clean. Findings to +`.context/codex-reviews/gate-b--rle-pass-

.md`, targets deleted and confirmed gone +before each call. -- [ ] **Step 4: Re-run the check and the full battery** +- [ ] **Step 6: Close the cycle** ```bash -sh scripts/check-version-bump.sh main && echo BUMP-OK -``` +git commit --amend -m "$(cat <<'MSG' +feat(gates): derive the pass floor from the profile; decide severity by consequence -Then the whole `AGENTS.md` § Commands chain, which must exit 0. - -- [ ] **Step 5: Commit** - -```bash -git add plugins/dev-workflow/.claude-plugin/plugin.json plugins/dev-workflow/CHANGELOG.md -git commit -m "chore(dev-workflow): 0.11.0 — profile-derived floor and consequence-keyed severity" + +MSG +)" ``` +**This is the first message without `WIP:`**, and the hook reads it as the cycle closing. + --- ## Task 10: The evidence pack -The mode is `battery+check+verification`. This task produces what the closing commit body cites. - -- [ ] **Step 1: The battery** +Mode `battery+check+verification`. Produced during Task 9's cycle and **revalidated before every +re-review and before the closing amend**, against the content the close will carry. -The full `AGENTS.md` § Commands chain, green, with the assertion counts recorded. +- [ ] **Step 1: The battery** — the § Commands chain, green, counts recorded. - [ ] **Step 2: The differential named verification — both revisions read** -No automated test is possible for prose, so this takes §5's other permitted route. **Pose one -question about behaviour under a derived floor of 1** and answer it against both revisions: +One question about behaviour at a derived floor of 1, answered against **both** revisions: -> *At floor 1, does a Blocker/Major-free pass 1 carrying a Minor close or keep looping?* +> *At floor 1, does a Blocker/Major-free pass 1 carrying a Minor close, or keep looping?* -- Against the **pre-change** text (`git show :CLAUDE.md`): the sentence says it **keeps - looping** — wrong, since pass 1 is at the floor. -- Against the **post-change** text: it says a pass **below the floor** keeps looping — correct. +- **Pre-change** (`git show :CLAUDE.md`, line 126): says it **keeps looping** — wrong, since + pass 1 is at the floor. +- **Post-change**: says a pass **below the floor** keeps looping — correct. -Ask the same question of `below 3` versus `below the floor`. **Record which revision was read for -each answer.** A verification that consults only the post-change text cannot fail and would report -success because of how it was wired — that is the failure mode this step exists to avoid, and -naming it is part of the entry. +Ask the same of `below 3` versus `below the floor` at line 79. **Record which revision was read for +each answer.** A verification consulting only the post-change text cannot fail and would report +success because of how it was wired. - [ ] **Step 3: The risk-path verification** -The risk is that **the floor is derived by the agent and nothing mechanical checks it**. Recompute -each provenance line's floor from the cited stories' profile headers. The observation that would -exist if the claim were false is **a provenance line whose number the profiles do not license**. -Also confirm **no floor file is present at close where none was present at start** — which detects -a persisting write and **cannot** detect a transient one. +Recompute each provenance line's floor from the cited stories' profile headers. The observation +that would exist if the claim were false is **a provenance line whose number the profiles do not +license**. Also confirm **no floor file is present at close where none was at start** — which +detects a persisting write and **cannot** detect a transient one. -- [ ] **Step 4: The user-knob verification, conditionally** +- [ ] **Step 4: The knob verification, conditionally** -If a `.context/codex-gate.floor` exists before a cycle, it is byte-identical after. **If none +If `.context/codex-gate.floor` exists before the cycle, it is byte-identical after. **If none exists, record not-applicable with that reason.** Do not create one — a fixture supplying its own input proves nothing. -- [ ] **Step 5: Write the evidence entry** - -Naming the story path and each verification's observation. It is **revalidated before every Gate-B -re-review and before the closing amend**, against the content the close will carry rather than a -commit that does not exist yet. - ---- - -## Task 11: The three closing bodies - -Two story criteria require this branch to demonstrate the pinned forms. - -- [ ] **Step 1: Write the check** - -```bash -git log --format='%b' -3 | grep -cE '^cycle (none \(pre-rule\)|[a-z0-9]{8,16});' -``` - -- [ ] **Step 2: Write each cycle's closing body** - -The Gate-A spec cycle, the Gate-A plan cycle and the Gate-B cycle each carry a provenance line and -a curve, in the pinned forms. **All three use `cycle none (pre-rule)`**: every cycle on this branch -began before these rules ship, so none has a nonce and minting one would be late-created -provenance. The branch demonstrates **every field of both forms except two** — the cycle -identifier, and the knob clause's non-absent form, which needs a workspace that has a knob. Both -are recorded as **undemonstrable-here with their reasons**, not as gaps and not as satisfied. - -- [ ] **Step 3: Record the nonce obligation** +- [ ] **Step 5: The three closing bodies** -The implementation commit records that **any cycle that both starts under these rules and closes** -discharges the nonce demonstration; duplicate discharge is harmless and allowed. - -- [ ] **Step 4: Re-run the check** - -Expected: three matching lines. +Each cycle's closing commit carries a provenance line and a curve in the pinned forms. **All three +use `cycle none (pre-rule)`** — every cycle on this branch began before these rules ship, so none +has a nonce and minting one would be late-created provenance. The branch demonstrates **every field +of both forms except two**: the cycle identifier, and the knob clause's non-absent form. Both are +recorded as **undemonstrable-here with their reasons**, not as gaps and not as satisfied. The +implementation commit records that **any cycle that both starts under these rules and closes** +discharges the nonce demonstration; duplicate discharge is harmless. --- ## Self-Review -**Spec coverage.** §2 floor predicate → Task 1. §2.1 knob and precedence → Task 1. §2.2 pass report -→ Task 1. §2.3 provenance → Task 4. §2.4 mid-cycle → Task 1. §3 severity → Task 3. §4 curve → -Task 4. §5 nonce → Task 4. §6 accounting → Task 0. §7 rollout, falsified sentences, packaging → -Tasks 7, 8, 9. §8 evidence → Task 10. §9 scope → the Global Constraints. §10 risks → carried into -the shipped text by Tasks 1 and 4. +**Spec coverage.** §2 predicate → Task 1. §2.1 knob, precedence, residual → Task 1 Step 3. §2.2 +pass report → Task 1 Step 3. §2.3 provenance → Task 4. §2.4 mid-cycle → Task 1. §3 severity → +Task 3. §4 curve → Task 4. §5 nonce incl. retry policy → Task 4 Step 3. §6 accounting → Task 0. +§7 rollout, falsified sentences, packaging → Tasks 6, 8, 9. §8 evidence → Task 10. §9 scope → +Global Constraints. §10 risks → shipped by Task 1 Step 3. -**Placeholders.** None: every replacement is quoted, every check is runnable, no step says "handle -appropriately". +**Placeholders.** None. Every anchor is pasted `grep -n` output; every replacement is written out; +every check has its demonstrated output recorded. **Type consistency.** ``, ``, ``, ``, ``, ``, -`` are used identically in Tasks 4 and 11 and are defined in the spec, which travels with -this plan. +`` are defined in the spec, which travels with this plan, and are used identically in +Tasks 4 and 10. + +**Gate-B classification, per path.** Tasks 1–6 and 9 stage prompts or plugin files → **one full +Gate-B cycle**, opened at Task 1 and closed at Task 9. Tasks 0, 7 and 8 stage only `docs/**.md` or +`README.md` → **N/A**, committed alone. The first version claimed Tasks 0 and 6 fired full Gate B; +they do not. -**One known gap, stated rather than hidden.** Task 2's replacement wordings are proposals, not -transcriptions — the spec pins the *rules*, not the sentences. The Gate-A plan cycle reviews them, -which is exactly what that cycle is for. +**Known limit.** Task 2's replacement wordings are proposals, not transcriptions — the spec pins +the rules, not the sentences — and this plan's Gate A is what reviews them. From 3b94dbf2459efe04c04d5689e6536981f4ece0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 19:25:56 +0200 Subject: [PATCH 055/117] =?UTF-8?q?docs(plan):=20Plan=20A=20=E2=80=94=20th?= =?UTF-8?q?e=20rules=20edits,=20split=20from=20the=20single-plan=20attempt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single 748-line plan opened 31 Blocker/Major at Gate-A pass 1 (5c00f8c). Twelve findings were one shape: the plan gestured at settled spec content instead of shipping it, while already being too long and still needing to grow. Split into three sequenced plans, each its own Gate-A cycle, A -> B -> C: A (this) rules edits: the floor predicate and the severity test, site by site in both copies. Spec 2, 2.1, 2.2, 2.4, 3, and 10 in part. B records: provenance line, per-pass curve, cycle nonce, slot naming. C rollout: falsified user-facing sentences, packaging, evidence pack. Grounding: the only two interventions that moved a finding curve in this cycle were splitting an artifact and slimming one. It costs three separate 3-pass floors under the story's high profile - the story's own floor rule applying to the work that writes it, recorded rather than worked around. What pass 1's 33 findings changed here, beyond the split: - The conditions accounting is a SECTION of this plan, derived from the edits the plan actually makes, reviewed by the plan's own Gate-A cycle. That dissolves the circular Task 0 (B1) and the unsound imported passage list (B2) together - B2's own suggested fix was to derive the set from the planned edits rather than import it from an earlier spec revision. - One accounting row per passage PER COPY (M1), with the ten-pair byte-identity check run rather than asserted: all ten identical, so the rows are shared and the check is in the plan as the thing that must hold. - Every check states its expected value FOR ITS POINT IN THE SEQUENCE. The predecessor recorded 7/7 for a check that runs after an earlier task removed one of the seven matches; in sequence it is 6. A true measurement of today's tree is not the check's value mid-plan (B8). - The commit topology follows shipped 5 carefully rather than needing a scope change: all ordinary commits land before the WIP opens, one WIP commit, every cycle-internal fold an amend restating -m "WIP: ...", the close an ordinary amend. Never --no-edit: codex-gate.sh:763 greps the Bash command string for -m ... wip, so an amend without -m is not recognized and the cycle resets (B14, B15, B16). The trap is stated in the plan for the executor. - The false rationale "which is where the 3 come from" is corrected rather than reworded - the number no longer comes from pass invalidation, and rewording it would have preserved the false claim in new words (M4). - Every finding from pass 1 is assigned to exactly one of the three plans in a table, so nothing falls between three documents the way the passage list fell between two. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 993 ++++++++++++++++++ 1 file changed, 993 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md new file mode 100644 index 0000000..8ed39bb --- /dev/null +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -0,0 +1,993 @@ +# Plan A — the rules edits (floor predicate + severity semantics) + +> **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:** Ship parts 1 and 2 of the review-loop-economics change into both prompt copies — the +pass floor becomes a function of the cited story's profile, and finding severity is decided by +whether something in the system takes a different decision. + +**Architecture:** Two mirrored prose edits, site by site: `CLAUDE.md` §5 and the inline template in +`/workflow-init`. **No code. Nothing under `plugins/dev-workflow/hooks/` is touched and no hook +state file is written.** + +**Tech Stack:** Markdown prompts; POSIX shell for every check; `git` for the records. + +**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36, Gate A +closed clean at pass 34). **Read it alongside this plan.** Plan A implements §2, §2.1, §2.2, §2.4, +§3 and the part of §10 that covers the rules it ships. + +**Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` + +> **Read the profile from that header and from nowhere else.** This plan deliberately does not +> copy the risk value, the security value or the validation mode into itself. §5 makes the story +> header the single writable copy: a copy here would go stale the moment the profile moved, and a +> stale floor is this change's own central failure mode. + +--- + +## Why this is one of three plans + +The single 748-line plan this replaces opened **31 Blocker/Major at Gate-A pass 1** (`5c00f8c`; +findings at `.context/codex-reviews/gate-a-plan-rle-pass-1.md`). Twelve of those findings were one +shape — the plan gestured at settled spec content instead of shipping it — and the artifact was +already too long while still needing to grow. The split is A → B → C, sequenced, each its own +Gate-A cycle: + +| Plan | Ships | Spec sections | +|---|---|---| +| **A — this one** | the floor predicate and the severity test, site by site in both copies | §2, §2.1, §2.2, §2.4, §3, §10 (partial) | +| **B** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | +| **C** | rollout: the falsified user-facing sentences, packaging, the evidence pack | §7, §8 | + +**B may not open before A's loop closes.** Only two interventions ever moved a finding curve in +this cycle — splitting an artifact and slimming one — so this is the move with evidence behind it, +not a preference. It costs three separate 3-pass floors under the story's `high` profile. That is +the story's own floor rule applying to the work that writes it, and it is recorded as such rather +than worked around. + +### Where pass 1's findings went — every one, to exactly one plan + +Stated so nothing falls between three documents the way the passage list fell between two. + +| Finding | Owner | Note | +|---|---|---| +| B1 conditions artifact circular | **A** | dissolved: the accounting is a *section of this plan*, reviewed by this plan's Gate-A passes | +| B2 passage set unsound | **A** | dissolved the way B2 asked: the set is **derived from the planned edits**, not imported from revision 15 | +| B3 pass-report paragraph unedited | **A** | Task 4 | +| B4 floor replacement range undelimited | **A** | Task 2 gives exact old and new text and dispositions `counted by the hook` | +| B5 gate-off list read as complete | **A** | Task 2 Step 4 ships §10's "routes known today, not a complete list" framing | +| B6 §2.4 mid-cycle missing | **A** | Task 5 | +| B7 §10 activation missing | **A** | Task 6, for the two parts A ships; **B extends it** to the three record duties | +| B8 `7/7` false in sequence | **A** | every check below states its value *for its point in the sequence* | +| B9-B13 records/nonce/slots under-shipped | **B** | | +| B14-B16 commit topology | **A**, restated in B and C | resolved once, below; each plan repeats the resolution | +| B17 `N/N cycle` in getting-started | **C** | | +| B18 re-review without amending | **A**, restated in B and C | Task 8 | +| B19 evidence produced after close | **C** | | +| B20 literal `` placeholder | **A**, restated in B and C | Task 8 builds the body before amending | +| B21 rewriting closed commit bodies | **C** | | +| M1 one row for two copies | **A** | the accounting has a row **per copy** | +| M2 plan copies the profile values | **A** | header above carries the path only | +| M3 ellipses in replacement text | **A** | every replacement is complete text | +| M4 false `where the 3 come from` rationale | **A** | Task 3 | +| M5 "the floor is unchanged" | **A** | Task 3 | +| M6 nonce diagnostics | **B** | | +| M7 parity result schema | **A** | Task 7 pins the tables | +| M8 future-nonce checkpoint | **C** | | +| M9 interruption / rerun safety | **A**, restated in B and C | every edit task has a preflight | +| M10 `main` currency | **C** | | +| MINOR 11 CHANGELOG content | **C** | | +| NIT 12 `(identical)` in a pasted block | **A** | no editorial token appears inside any pasted block below | + +--- + +## Global Constraints + +Verbatim from the spec. Every task's requirements implicitly include these. + +- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes; no hook state file is + written, in particular not `.context/codex-gate.floor`. +- **The §5 heading must keep matching `^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`.** + `codex-gate.sh:94` greps `CLAUDE.md` for it to build every reminder's citation. +- **Every rule lands in BOTH copies**, except where a difference is deliberate and stated. +- **§5's other closure rules are never restated, only referred to.** Three spec revisions were + spent on this: each summary of the triviality skip dropped a different condition. +- **Anchors are pasted `grep -n` output.** No editorial token ever appears inside a pasted block — + writing `(identical)` in place of a second grep line cost the predecessor its blanket claim. + +### The commit protocol — read this before Task 1 + +**All ordinary commits happen BEFORE the WIP opens. Then one WIP commit, then the review loop, +then the close.** Nothing commits ordinarily while the cycle is open. + +> **The `--no-edit` trap — this is why the protocol looks verbose.** +> `plugins/dev-workflow/hooks/codex-gate.sh:763` is +> `is_wip_commit() { printf '%s' "$1" | grep -Eiq -- "-m[[:space:]]*['\"]?[[:space:]]*wip"; }` +> It matches **the Bash command string**, not git state and not the commit message. So +> `git commit --amend --no-edit` carries no `-m`, is **not** recognized as a WIP commit, and at +> `:886` `is_commit "$cmd" && ! is_wip_commit "$cmd"` is true — **the cycle resets and your passes +> are discarded.** Every cycle-internal amend below therefore restates `-m "WIP: ..."` in full. +> Never `--no-edit` inside the cycle. +> +> That the shipped rules do not warn about this is a real defect, routed to the backlog. **Do not +> fix the hook here** — this plan is prompt-only by its own constraint above. + +The topology, in order: + +1. **Task 1** commits the conditions accounting — ordinary commit, Gate B N/A, **cycle not yet + open**. +2. **Task 2 opens the cycle**: `git commit -m "WIP: "`. +3. **Tasks 3-7 amend it**, each restating `-m "WIP: "`. Same subject every time. +4. **Task 8** runs the review loop against that commit (`baseSha` = its parent), re-amending after + every fix, and closes with `git commit --amend -m ""` — the first message without + `WIP:`, which the hook correctly reads as the cycle closing. + +**Task 7's parity and conformance results are folded into the WIP commit, not committed +separately.** A separate docs commit after the WIP opens would reset the cycle regardless of which +paths it staged. + +--- + +## File Structure + +| File | Responsibility | Gate B | +|---|---|---| +| this plan, § "Old-conditions accounting" | what the existing prose required, per copy, dispositioned | N/A — reviewed by this plan's Gate-A cycle | +| `CLAUDE.md` §5 | the live rules | full (in the cycle) | +| `plugins/dev-workflow/commands/workflow-init.md` §5 | the scaffolded mirror | full (in the cycle) | +| `docs/superpowers/specs/…-plan-a-conditions.md` | the accounting, extracted for the record | N/A — ordinary commit, Task 1, before the cycle opens | + +--- + +## Old-conditions accounting + +**Derived from the edits this plan actually makes**, against §5 as it stands at HEAD — not +imported from an earlier spec revision. That was finding B2: an imported list can be stale, +overinclusive and incomplete at once, and the exact-once grep that guarded it proved only that the +lead-ins existed. + +**One row per passage per copy.** Where the two copies' text is byte-identical for a passage the +rows are identical and say so; a condition unique to one mirror cannot hide behind a shared row +(finding M1). + +Anchors, pasted from `grep -n` — `CLAUDE.md` then `plugins/dev-workflow/commands/workflow-init.md`: + +``` +CLAUDE.md:72:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major +CLAUDE.md:77:final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +CLAUDE.md:79:below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +CLAUDE.md:133:**From pass 4 onward every pass report carries three lines.** The carrier is **your own +CLAUDE.md:236:act on the partial list, don't count it toward the 3-pass floor, and don't read "no +CLAUDE.md:300:- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the +CLAUDE.md:335: invalidates the prior pass, which is where the 3 come from. +CLAUDE.md:403:Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major +CLAUDE.md:480:**Changing a profile:** the pass **proposes the complete resulting header** — both axes, +CLAUDE.md:495:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → +``` + +``` +plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major +plugins/dev-workflow/commands/workflow-init.md:277:final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +plugins/dev-workflow/commands/workflow-init.md:279:below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +plugins/dev-workflow/commands/workflow-init.md:330:**From pass 4 onward every pass report carries three lines.** The carrier is **your own +plugins/dev-workflow/commands/workflow-init.md:421:act on the partial list, don't count it toward the 3-pass floor, and don't read "no +plugins/dev-workflow/commands/workflow-init.md:485:- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the +plugins/dev-workflow/commands/workflow-init.md:519: invalidates the prior pass, which is where the 3 come from. +plugins/dev-workflow/commands/workflow-init.md:582:Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major +plugins/dev-workflow/commands/workflow-init.md:659:**Changing a profile:** the pass **proposes the complete resulting header** — both axes, +plugins/dev-workflow/commands/workflow-init.md:674:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → +``` + +**Ten passages, each in two copies — twenty rows.** Both copies are byte-identical at every one of +these ten passages; Task 1 Step 2 proves that mechanically rather than asserting it, and the rows +below are written once with that proof standing in for the second copy. **If that proof fails for +any passage, that passage gets two separate rows and the difference is dispositioned.** + +| # | Passage | What the existing prose requires | Disposition | +|---|---|---|---| +| 1 | floor paragraph (72 / 272) | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. a satisfied count is not a clean review · e. Gate A is instruction-backed · f. the hook resets at `writing-plans` · g. a TodoWrite per pass · h. fix Blocker/Major after each · i. Codex is advisory · j. validate before applying · k. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 2) · c. **moved** — the hook still counts, but as its reminder threshold, not the obligation (Task 2 Step 3) · b, d-k **kept verbatim** | +| 2 | `if pass 3 still` (77 / 277) | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, with `pass 3` → `the pass at the floor` (Task 3) | +| 3 | `below 3` (79 / 279) | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, with `below 3` → `below the floor` (Task 3) | +| 4 | pass-report paragraph (133 / 330) | a. from pass 4 onward, three lines · b. the carrier is your own status report · c. never the Codex reply · d. never the findings file · e. the trend line · f. the cluster line · g. the require↔withdraw line | a. **kept and extended** — the three lines still start at pass 4; §2.2's three fields are owed by **every** pass (Task 4) · b-g **kept verbatim** | +| 5 | incomplete-pass (236 / 421) | an incomplete pass is not a review: don't act on the partial list, don't count it toward the floor, don't read "no Blocker/Major visible" as clean | **kept**, with `the 3-pass floor` → `the floor` (Task 3) | +| 6 | Gate A loop (300 / 485) | Gate A is two runs, each its own 3-pass loop; one broad prompt; re-run each pass over the revised artifact | **kept**, with `3-pass loop` → `loop at the derived floor` (Task 3) | +| 7 | `where the 3 come from` (335 / 519) | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept**, but its **rationale is corrected** — the number no longer comes from invalidation (Task 3, finding M4) | +| 8 | Lenses (403 / 582) | lenses are different questions, not more passes; the floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are unchanged | **kept**, reworded so "unchanged" no longer claims the floor is fixed (Task 3, finding M5) | +| 9 | `Changing a profile:` (480 / 659) | a. the pass proposes the complete resulting header · b. the human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's mid-cycle rules are **appended** after them, not merged into them (Task 5) | +| 10 | Severity (495 / 674) | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test is **appended** as the procedure that sets a ceiling on them (Task 6) | + +**Nothing in §5 outside these ten passages is edited by Plan A.** Task 7 Step 3 proves that by diff. + +--- + +## Task 1: The accounting, committed before the cycle opens + +**Files:** Create `docs/superpowers/specs/2026-08-29-review-loop-economics-plan-a-conditions.md` + +- [ ] **Step 1: Preflight — is this already done?** + +```bash +test -e docs/superpowers/specs/2026-08-29-review-loop-economics-plan-a-conditions.md \ + && echo "EXISTS — read it and skip to Step 4" || echo "ABSENT — proceed" +``` + +Every edit task below opens with a preflight, because a plan abandoned halfway must be resumable +without duplicating an append (finding M9). + +- [ ] **Step 2: Prove the two copies are byte-identical at all ten passages** + +The accounting above writes one row per passage on the strength of this. Run it before trusting it: + +```bash +C=CLAUDE.md; T=plugins/dev-workflow/commands/workflow-init.md +for pair in 72:272 77:277 79:279 133:330 236:421 300:485 335:519 403:582 480:659 495:674; do + a=${pair%:*}; b=${pair#*:} + if [ "$(sed -n "${a}p" "$C")" = "$(sed -n "${b}p" "$T")" ]; then + printf 'IDENTICAL %s/%s\n' "$a" "$b" + else + printf 'DIFFERS %s/%s <<<%s>>> <<<%s>>>\n' "$a" "$b" \ + "$(sed -n "${a}p" "$C")" "$(sed -n "${b}p" "$T")" + fi +done +``` + +**Expected at this point in the sequence: ten `IDENTICAL` lines, no `DIFFERS`.** Any `DIFFERS` line +means that passage needs two rows in the accounting and a stated disposition for the difference — +fix the accounting before proceeding, do not proceed with a shared row. + +- [ ] **Step 3: Extract the accounting to the conditions file** + +Copy this plan's § "Old-conditions accounting" verbatim into that file, with a one-paragraph header +naming this plan as its source and Plan A's Gate-A cycle as its review. + +- [ ] **Step 4: Commit — ordinary commit, Gate B N/A, cycle not yet open** + +```bash +git add docs/superpowers/specs/2026-08-29-review-loop-economics-plan-a-conditions.md +git diff --cached --name-only # MUST list exactly that one path +git commit -m "docs(spec): old-conditions accounting for the Plan A rules edits" +``` + +**This is the last ordinary commit until the cycle closes.** Everything after Task 2 amends. + +--- + +## Task 2: Open the cycle, and replace the floor + +**Files:** `CLAUDE.md:72`, `plugins/dev-workflow/commands/workflow-init.md:272` + +**Interfaces:** +- Produces: the phrase `max(risk, security)` inside the floor paragraph, which Task 3's sites and + Task 6's severity text both refer back to. + +- [ ] **Step 1: Preflight** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + sed -n '/Both gates are a LOOP with a HARD FLOOR/,/^$/p' "$f" | grep -c 'max(risk, security)' +done +``` + +**Expected at this point in the sequence: `0` and `0`** — this is the failing check. `1` and `1` +means Task 2 already ran; verify against Step 3's text and skip to Step 5. + +**The `sed` scoping is load-bearing.** An unscoped `grep -c 'max(risk, security)'` returns **1** for +both files even before any edit, because the Profiles section already contains the phrase. The +check would pass before the edit and prove nothing. + +- [ ] **Step 2: Read the exact text being replaced** + +```bash +sed -n '72,80p' CLAUDE.md +sed -n '272,280p' plugins/dev-workflow/commands/workflow-init.md +``` + +The old text, in both copies (finding B4 — the replaced range is delimited, not left to judgement): + +``` +**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major +only), counted by the hook.** The hook counts passes but can't read findings or +tell the spec run from the plan run (it resets at `writing-plans`), so Gate A — +the spec run especially — is instruction-backed: a satisfied count is not a clean +review. Open a TodoWrite "Codex pass N" per pass; fix Blocker/Major after each. Your +final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +clean or clearly stuck → then STOP and surface to the user. The only early exit +below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +advisory — validate before applying; dismissed finding → one-line why. +``` + +**Lines 77 and 79 of that block are Task 3's sites 2 and 3.** Task 2 replaces only the first two +sentences — through `counted by the hook.` and the sentence that explains it — and leaves the rest +of the paragraph on disk untouched. Task 3 then edits lines 77 and 79 in place. + +- [ ] **Step 3: The replacement — complete text, no ellipsis** + +Replace exactly these two sentences: + +``` +**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major +only), counted by the hook.** The hook counts passes but can't read findings or +tell the spec run from the plan run (it resets at `writing-plans`), so Gate A — +the spec run especially — is instruction-backed: a satisfied count is not a clean +review. +``` + +with: + +```markdown +**Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run (Blocker/Major +only), derived from the cited story's profile.** `max(risk, security) == 0` gives a floor of +**1**; every resolvable profile above that, and an artifact citing **no** story, gives **3**. Two +levels, not three — `high` takes its rigor from lens sets and evidence mode, not from extra +passes. **A cited story whose profile is present but unresolvable stops and surfaces** under §5's +existing rule; it does not fall through to 3, because reading it as 3 would turn a stop condition +into a silent default. **Across a cited set the floor is 1 if and only if the set is non-empty and +every member is profiled, resolvable and at level 0** — all four conditions, since "every cited +story" is vacuously true of an empty set; no story cited, or any cited story unprofiled, gives 3. +**One derived value governs all three cycles** — the Gate-A spec loop, the Gate-A plan loop and the +Gate-B cycle. Not because they are one cycle (§5 is explicit that they are three) but because they +derive from the same cited-story set. + +**The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder threshold +that controls nothing.** The hook still counts passes, and it still can't read findings or tell the +spec run from the plan run (it resets at `writing-plans`), so Gate A — the spec run especially — is +instruction-backed: **a satisfied count is not a clean review, and a below-threshold reminder is +noted in the pass report and disregarded** where the cycle's own closure rules are satisfied. **This +replaces the pass-count number and nothing else.** Every other rule §5 states about how a cycle +closes stands as written, and none of them is restated here — a summary is where their conditions +would get dropped. **Nothing here writes `.context/codex-gate.floor`**: the knob stays the user's, +never written, never removed, never read for this derivation. +``` + +- [ ] **Step 4: Add the residual and the gate-off disclosure** + +Immediately after the block above, in both copies. Finding B5: the list is explicitly **not** +exhaustive, and it carries every route §10 names. + +```markdown +**Named residual:** the hook's messages state its own threshold as an obligation, so at a floor of +1 they report a shortfall the cycle does not owe. Hook text is out of scope here by decision; what +makes that tolerable is the precedence rule above plus the hook exiting 0 on every branch, not the +reminder being harmless. + +**The gate-off surface — routes known today, not a complete list**, since an enumeration read as +complete guarantees the routes it omits. **One route is created here**: a stated floor the cited +set does not license, which could not exist before there was a derived floor to state. **Pre-existing +and unchanged**: omitting a higher-risk cited story; minting or editing a profile to level 0; +presenting an incomplete cited set; falsifying evidence entries; silencing reminders; or not +running a pass and reporting that it ran. **A user-set floor is not the lever** — it moves what the +hook says, not what the cycle owes. **None of this is a guard**: the floor is produced by the agent +and nothing checks it against the cited profiles. +``` + +- [ ] **Step 5: Re-run Step 1's check** + +**Expected now: `1` and `1`.** + +- [ ] **Step 6: Verify the paragraph's tail survived** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + tr '\n' ' ' < "$f" | tr -s ' ' | grep -c "don't manufacture findings to pad" +done +``` + +**Expected: `1` and `1`** — same as before the edit. This catches a replacement that swallowed its +neighbours. + +- [ ] **Step 7: OPEN THE CYCLE** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit -m "WIP: review-loop economics Plan A — floor predicate and severity test" +``` + +**That subject is reused verbatim by every amend in Tasks 3-7.** + +--- + +## Task 3: The seven floor-wording sites, per copy + +**Interfaces:** consumes Task 2's predicate; produces nothing. + +- [ ] **Step 1: Preflight — the sequence-correct count** + +```bash +grep -cE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +**Expected at this point in the sequence: `6` and `6` — not 7.** Seven is the count in an +untouched tree; Task 2 has already removed the `min 3 passes` match at line 72/272. Recording 7 +here was finding B8: a true measurement of today's tree is not the check's value mid-plan. + +After this task both must be **0**. + +- [ ] **Step 2: The `pass 1` site the regex does not match** + +```bash +grep -n 'carrying a Minor keeps' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +**Expected: `CLAUDE.md:126` and `workflow-init.md:322`.** This site contains no digit `3` and the +Step 1 regex never matched it — so Step 1 reaching 0 does **not** cover it. It is asserted +separately at Step 5. + +- [ ] **Step 3: Apply six replacements per copy** + +Complete text, no ellipses (finding M3). **CLAUDE.md line : template line.** + +**77 : 277** — old: +``` +final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +``` +new: +``` +final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until +``` + +**79 : 279** — old: +``` +below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +``` +new: +``` +below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is +``` + +**236 : 421** — old: +``` +act on the partial list, don't count it toward the 3-pass floor, and don't read "no +``` +new: +``` +act on the partial list, don't count it toward the floor, and don't read "no +``` + +**300 : 485** — old: +``` +- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the +``` +new: +``` +- **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the +``` + +**335 : 519** — old: +``` + invalidates the prior pass, which is where the 3 come from. +``` +new: +``` + invalidates the prior pass — which is why a fix costs another pass, though the floor itself + comes from the profile. +``` + +> Finding M4: `which is where the 3 come from` was a **causal claim that the new design makes +> false**. The lower bound now comes from the story profile; invalidation only explains why a fix +> requires another review. Rewording it to "where the floor's lower bound comes from" would have +> preserved the false claim in new words — the exact failure `AGENTS.md` records as this repo's +> most persistent defect. + +**403 : 582** — old: +``` +Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major +``` +new: +``` +Lenses are **different questions, not more passes** — they change what a pass asks, never how many +passes a cycle owes, which the profile alone decides. The floor, the Blocker/Major +``` + +> Finding M5: the old sentence continues "… is unchanged." Leaving `The floor … is unchanged` in +> the very change that makes the floor profile-dependent tells readers the opposite of what +> shipped. The rewrite says what is actually unchanged — that lenses add no passes — and lets the +> tail's list of unchanged rules stand. + +- [ ] **Step 4: Re-run Step 1's check** + +**Expected now: `0` and `0`.** + +- [ ] **Step 5: The `pass 1` site, asserted separately** + +Old, in both copies (it continues onto the next line — match the fragment, not a reconstructed +sentence): +``` +the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps +``` +new: +``` +the only exception, exactly as above; a Blocker/Major-free pass **below the floor** carrying a Minor keeps +``` + +```bash +grep -c 'a Blocker/Major-free pass \*\*below the floor\*\* carrying a Minor keeps' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -c 'Blocker/Major-free pass 1 carrying a Minor' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +**Expected: `1` and `1` for the first; `0` and `0` for the second.** + +> This is the sentence that inverts at a floor of 1. Before the edit it tells a floor-1 cycle that +> a clean pass 1 keeps looping, which is wrong — pass 1 *is* the floor there. + +- [ ] **Step 6: Confirm the historical citation was NOT touched** + +```bash +grep -c "PR #23's Gate-B pass 3 returned all four findings" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +**Expected: `1` and `1`.** It cites an actual pass, not a rule; changing it would falsify a record. + +- [ ] **Step 7: Amend the WIP commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +``` + +**Never `--no-edit`.** See the commit protocol above: the hook greps this command string for +`-m ... wip`, and an amend without `-m` resets the cycle. + +--- + +## Task 4: The pass report (§2.2) + +**Files:** `CLAUDE.md:133`, `plugins/dev-workflow/commands/workflow-init.md:330` + +Finding B3: the old plan added every-pass fields to the floor paragraph while leaving the +pass-report paragraph prescribing only the pass-4 three lines, so the shipped prompt would have +described the report shape in two places that disagreed. + +- [ ] **Step 1: Preflight** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'the cited stories they were read from' +done +``` + +**Expected at this point in the sequence: `0` and `0`.** + +- [ ] **Step 2: Read the paragraph being extended** + +```bash +sed -n '133,140p' CLAUDE.md +sed -n '330,337p' plugins/dev-workflow/commands/workflow-init.md +``` + +- [ ] **Step 3: Insert §2.2's fields immediately BEFORE that paragraph** + +Anchor, pasted from `grep -n`: +``` +CLAUDE.md:133:**From pass 4 onward every pass report carries three lines.** The carrier is **your own +plugins/dev-workflow/commands/workflow-init.md:330:**From pass 4 onward every pass report carries three lines.** The carrier is **your own +``` + +Insert before it, in both copies: + +```markdown +**Every pass report states three things about the floor**, from pass 1 onward: the **derived +floor**, the **risk and security values read**, and the **cited stories they were read from**. A +report giving the number alone leaves a reader unable to check the derivation while passes are +still being spent — which is the only time checking it is cheap. This is owed by every pass; the +three lines below are owed from pass 4 and are a different obligation. +``` + +**The existing paragraph is not modified** — its seven requirements (a-g in accounting row 4) all +stand verbatim, and the inserted text says explicitly that the two obligations are distinct so no +reader merges them. + +- [ ] **Step 4: Re-run Step 1's check, and confirm the old paragraph is intact** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'the cited stories they were read from' +done +grep -c 'From pass 4 onward every pass report carries three lines' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -c 'require↔withdraw pair' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +**Expected: `1`/`1`, then `1`/`1`, then `1`/`1`.** + +- [ ] **Step 5: Amend the WIP commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +``` + +--- + +## Task 5: A profile or cited set that moves mid-cycle (§2.4) + +**Files:** `CLAUDE.md:480`, `plugins/dev-workflow/commands/workflow-init.md:659` + +Finding B6. §2.4 composes three existing rules and adds no new one, but none of the composition is +currently written down, so a mid-cycle profile or set edit can silently reuse an old clean pass. + +- [ ] **Step 1: Preflight** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'Any profile change costs at least one further pass' +done +``` + +**Expected at this point in the sequence: `0` and `0`.** + +- [ ] **Step 2: Read the nine conditions being preserved** + +```bash +sed -n '480,493p' CLAUDE.md +``` + +All nine are accounting row 9 (a-i). **They are kept verbatim.** §2.4's rules are **appended after +them**, never merged into them — merging is how a rewrite drops a condition, which `AGENTS.md` +names as this repo's tenth-recorded instance. + +- [ ] **Step 3: Append after the `Changing a profile:` paragraph, both copies** + +```markdown +**While a gate is running, the floor derives from the *current* profile at each pass.** Passes +already run keep counting; closing requires the floor **as currently derived**. These are +pass-count rules, so they apply while §5 says a gate is running and are silent otherwise — what §5 +says about *when* a gate runs is §5's, unchanged and deliberately not summarised here. + +**Any profile change costs at least one further pass**, in either direction and **whether or not +the floor number moves**, because the final clean pass must run under the current profile — so no +already-banked pass can be it. That further pass must itself be clean and every other closure duty +must be satisfied; it is one more pass, not a licence to close on the next one. **What a lowering +drops is whatever the changed values drop, not a fixed pair**: a mode-only override changes the +evidence obligations while leaving the axis-derived lens sets alone, and security `high` → +`standard` keeps the security lens set while changing what evidence is owed. **Every derived +obligation is recomputed from the current profile.** + +**The cited set is re-read at each pass, and the final clean pass runs against the current set — +whenever its membership changes, not only when the floor number moves.** Adding a high-risk story +to a set already at floor 3 leaves the number alone while adding that story's lens set, its +evidence obligations and its review scope; a pass run before it joined did not cover them. +**Removing a story** recomputes obligations from the current set and so does remove *that story's* +lenses and evidence duty — but it **never discharges an accepted in-set Blocker or Major**: the +acceptance put that finding in the fix set, not the citation. +``` + +- [ ] **Step 4: Re-run Step 1's check, and confirm all nine survived** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'Any profile change costs at least one further pass' +done +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f" + tr '\n' ' ' < "$f" | tr -s ' ' | grep -o 'proposes the complete resulting header\|human confirms it\|never moves it alone\|append one profile-log line\|voids every prior override\|follows the current security value\|keep counting\|final clean pass\|fold the edit into the active' | wc -l +done +``` + +**Expected: `1`/`1` for the first; the second must report the same number for both copies and that +number must not fall below its pre-edit value — capture that value in Step 1 before editing.** + +- [ ] **Step 5: Amend the WIP commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +``` + +--- + +## Task 6: Severity semantics (§3), and activation (§10, partial) + +**Files:** `CLAUDE.md:495`, `plugins/dev-workflow/commands/workflow-init.md:674` + +- [ ] **Step 1: Preflight** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'what in the system consumes this text' +done +``` + +**Expected at this point in the sequence: `0` and `0`.** + +- [ ] **Step 2: Append the test after the four definitions, both copies** + +Anchor, pasted from `grep -n`: +``` +CLAUDE.md:495:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → +plugins/dev-workflow/commands/workflow-init.md:674:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → +``` + +The four definitions stay verbatim. Append: + +```markdown + **Deciding severity — one procedure. The subject list is illustration, not a second rule.** + Name **what in the system consumes this text** — whatever *acts* on it — and the decision that + act takes differently if the text is wrong. **Both are required. If you cannot name both**, the + finding is **Minor or below**: collect, never iterate. + + The exclusions are contract, not commentary. **The reader must consume the text in the system's + *operation*, not in reviewing it** — the review pass raising the finding is not an in-system + reader of the text it reviews; without this the test demotes nothing. **Gates remain legitimate + readers** of rule text they will later apply. **A human reader never satisfies the test** — §5's + prose exemption already prices that cost as non-gating. **The list of reader kinds is + illustrative, not closed**, because this ships into projects whose readers we have never seen. + **The test sets a ceiling, not a floor, and never chooses between Blocker and Major** — the four + definitions above still decide that. **The instrument carve-out is symmetric**: an instrument + finding keeps its severity whenever it shows the instrument changes what a gate concludes about + product behaviour — a false green, and equally a false red or a check blocking a valid change. + **Rationale prose is Minor only when no rule's application depends on it**, not categorically: + `docs/prompt-standards.md` requires rules to carry their why, so rationale a reader must consult + to apply a rule passes the test. **This removes arbitrariness, not judgement.** Coverage-first is + unchanged — the reviewer reports every finding with severity and confidence; the filter is ours. + + This is the **finding-level analog of the path-level prose exemption**: one principle at two + granularities — text that *describes* the product versus text that *is* the product. +``` + +- [ ] **Step 3: Append the activation block after Task 2's gate-off disclosure, both copies** + +Finding B7. **Plan A ships the two parts Plan A ships. Plan B extends this list** to the +provenance-line, curve and nonce duties — §10 says extending is safe and replacing is not, so Plan +B appends to this list rather than rewriting it. + +```markdown +**When these rules bind.** From the commit that ships them, and **a cycle already running finishes +under the rules it started with**. **Where a cycle's starting rules cannot be established it takes +the stricter reading of every part this change touches** — floor 3, and severity classified without +the demotion. Not a re-derivation, which could hand a level-0 cycle a floor of 1 and *skip* passes +on the strength of not knowing when it started. A user knob set above 3 is not lowered by this +fallback. **A revert is itself a shipping commit for the old rules**, and the activation rule wins +wherever the start is determinable; the fallback covers only where it is not. + +**Downstream has no shipping commit.** Adoption binds from the `/workflow-init` run that *actually +writes* the text — which may write nothing, be declined, or be merged in part — so **these rules +bind only over the text a project's `CLAUDE.md` actually contains**, and a partial adoption can +persist undetected. A project taking the floor rule without the severity test gets a floor whose +`docs-only` question the severity test is what settles. What prompt text can do is done; what it +cannot is said. +``` + +- [ ] **Step 4: Re-run the checks** + +```bash +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'what in the system consumes this text' +done +grep -c 'a cycle already running finishes' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF '**Severity:** Blocker (wrong/unsafe/breaks invariant)' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +**Expected: `1`/`1`, `1`/`1`, `1`/`1`.** + +- [ ] **Step 5: Amend the WIP commit** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +``` + +--- + +## Task 7: Parity and the twelve-item conformance pass + +No new rules. This is the verification invariant 11 requires. Finding M7: the results have a +defined schema, so an executor cannot discharge them with an unauditable paragraph. + +- [ ] **Step 1: The parity table — one row per changed rule** + +Columns: *rule* · *`CLAUDE.md` text* · *template text* · **status** from the closed set +`IDENTICAL` | `DELIBERATE-DIFFERENCE` | `DEFECT` · *reason, required for the latter two*. +One row for each rule added or changed by Tasks 2-6. + +```bash +C=CLAUDE.md; T=plugins/dev-workflow/commands/workflow-init.md +for p in 'derived from the cited story'"'"'s profile' 'reminder threshold' 'Named residual' \ + 'routes known today' 'states three things about the floor' \ + 'Any profile change costs at least one further pass' \ + 'what in the system consumes this text' 'a cycle already running finishes'; do + a=$(grep -cF "$p" "$C"); b=$(grep -cF "$p" "$T") + [ "$a" = "$b" ] && printf 'PARITY %s : %s\n' "$a" "$p" || printf 'MISMATCH %s/%s : %s\n' "$a" "$b" "$p" +done +``` + +**Expected: eight `PARITY 1` lines, no `MISMATCH`.** + +A whole-section diff proves nothing here — the two copies already diverge on roughly 192 lines +overall for reasons predating this change. Walk the named rules. + +- [ ] **Step 2: The twelve-item table — one status row per item per artifact** + +Artifacts: the **resulting scaffolded template**, and +`plugins/dev-workflow/commands/workflow-init.md` as the outer command prompt. Columns: *item* · +*artifact* · **status** from `PASS` | `N/A` | `FAIL` · *reason, required for `N/A` and `FAIL`*. +**Item 7 is read against the whole resulting artifact**, not the diff. + +**Root `CLAUDE.md` is outside invariant 11's list and is not part of this pass.** + +> **Item 1 for the scaffolded template is `N/A`, and Plan C ships the note that says why** — the +> file the template writes is model-agnostic by design, so a `Target model:` line inside it would +> be false in every repo it lands in. Plan A records the `N/A` here; Plan C writes the reader-facing +> note outside the fence, where it cannot scaffold and cannot become a second `Target model:` +> declaration. + +- [ ] **Step 3: Prove no passage outside the ten was touched** + +```bash +git diff --stat HEAD~1 -- CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git diff HEAD~1 -- CLAUDE.md | grep -c '^[+-][^+-]' +``` + +Read the diff and confirm every hunk falls inside one of the ten accounted passages. **A hunk +outside them is a finding**, not something to wave through. + +- [ ] **Step 4: Invariant checks still pass** + +```bash +grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md +sh scripts/check-invariants.sh && echo INVARIANTS-OK +``` + +**Expected: `1`, then `INVARIANTS-OK`.** The count must be 1 — `scripts/check-invariants.sh` fails +on any other, and a naive item-1 fix that adds a second declaration is exactly how that breaks. + +- [ ] **Step 5: Fold the results into the WIP commit** + +Write both tables into the conditions file from Task 1, then: + +```bash +git add docs/superpowers/specs/2026-08-29-review-loop-economics-plan-a-conditions.md +git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +``` + +**Not a separate commit.** An ordinary commit here — even one staging only a `docs/**.md` path — +resets the cycle and strands the WIP, because the hook's reset is keyed on the commit, not on the +staged paths. + +--- + +## Task 8: Gate B, and closing the cycle + +- [ ] **Step 1: The battery** + +The whole `AGENTS.md` § Commands chain, exit 0, assertion counts recorded. + +```bash +shellcheck --shell=sh plugins/dev-workflow/hooks/codex-gate.sh && \ +shellcheck --shell=sh --exclude=SC2015 plugins/dev-workflow/hooks/codex-gate.test.sh && \ +shellcheck --shell=sh scripts/check-invariants.sh && \ +shellcheck --shell=sh --exclude=SC2015 scripts/check-invariants.test.sh && \ +shellcheck --shell=sh scripts/check-version-bump.sh && \ +shellcheck --shell=sh scripts/check-version-bump.test.sh && \ +HOOK_SH=sh sh plugins/dev-workflow/hooks/codex-gate.test.sh && \ +HOOK_SH=dash dash plugins/dev-workflow/hooks/codex-gate.test.sh && \ +sh scripts/check-invariants.test.sh && sh scripts/check-invariants.sh && \ +sh scripts/check-version-bump.test.sh && sh scripts/check-version-bump.sh main && \ +claude plugin validate . --strict && echo BATTERY-GREEN +``` + +- [ ] **Step 2: The differential check — both revisions read** + +The mode is `battery+check+verification`; **read it from the story header, not from here.** This +step is the check that fails without the change, and it is differential by construction: a check +consulting only the post-change text cannot fail. + +One question, answered against **both** revisions: + +> *At a derived floor of 1, does a Blocker/Major-free pass 1 carrying a Minor close, or keep +> looping?* + +```bash +git show HEAD~1:CLAUDE.md | grep -n 'carrying a Minor keeps' # pre-change +grep -n 'carrying a Minor keeps' CLAUDE.md # post-change +``` + +- **Pre-change** says `pass 1 ... keeps looping` — **wrong at floor 1**, where pass 1 is the floor. +- **Post-change** says `pass **below the floor** ... keeps looping` — correct. + +Ask the same of `below 3` versus `below the floor`. **Record which revision produced which +answer.** The observation that would exist if the claim were false is a pre-change revision that +already answers correctly — and it does not. + +- [ ] **Step 3: The named risk-path verification** + +Recompute the floor this cycle owes from the cited story's profile header, and confirm the pass +reports state it with the axes and the source story. **The observation that would exist if the +claim were false is a pass report whose floor the cited profile does not license.** + +Then confirm no floor file appeared: + +```bash +test -e .context/codex-gate.floor && echo "PRESENT — was it present at start?" || echo "ABSENT" +``` + +**This detects a persisting write and cannot detect a transient one** — stated, not glossed. + +- [ ] **Step 4: The review loop** + +`mcp__codex__review` against the WIP commit, `baseSha` = its parent. Findings to +`.context/codex-reviews/gate-b--plana-pass-

.md`; **delete both branch targets and +confirm them gone before each call.** + +Floor: derived from the story profile — read the header. Carry into every call: the story path, +and the current evidence entry quoted verbatim. + +**After every accepted fix:** + +```bash +git add -u +git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +``` + +**then** re-review that new commit against the same base. Finding B18: `mcp__codex__review` reads +the committed range, so a re-review run over uncommitted fixes inspects the old snapshot and can +return a clean pass on content that is not what closes. + +- [ ] **Step 5: Build the closing body, then close** + +Finding B20: no placeholder reaches the amend. Build the body as a file first, read it back, and +only then amend. + +```bash +cat > /tmp/plan-a-close.txt <<'EOF' +feat(gates): derive the pass floor from the story profile; decide severity by consequence + + + +Evidence (docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md): + battery: + check: the floor-1 Minor sentence, read against both revisions — pre-change says pass 1 + keeps looping, which is wrong at floor 1; post-change says below the floor. + verification: +EOF +cat /tmp/plan-a-close.txt # read it back — no angle-bracket placeholder may survive +grep -c '<' /tmp/plan-a-close.txt +``` + +**Expected: `0`.** A non-zero count means a placeholder is still in the body — fix it before +amending. + +```bash +git commit --amend -F /tmp/plan-a-close.txt +``` + +**This is the first message without `WIP:`**, and the hook reads it as the cycle closing. + +> Plan A's closing body carries the evidence entry. **The provenance line and the per-pass curve +> are Plan B's forms and are not owed by this commit** — they do not exist yet. Plan C's closing +> body carries them for the branch. + +--- + +## Self-Review + +**Spec coverage.** §2 predicate, unanimity, unresolvable-stop, one-value-three-cycles → Task 2 +Step 3. §2.1 precedence, knob, residual → Task 2 Steps 3-4. §2.2 pass report → Task 4. §2.4 +mid-cycle → Task 5. §3 severity → Task 6 Step 2. §10 activation, revert, downstream adoption, +gate-off surface → Task 2 Step 4 and Task 6 Step 3, **partial by design**: the three record duties +in §10's strict-fallback list are Plan B's to append. §2.3, §4, §5, §6 → Plan B. §7, §8 → Plan C. + +**Placeholders.** None. Every anchor is pasted `grep -n` output with no editorial token inside any +pasted block; every replacement is complete text with no ellipsis; every check states its expected +value **for its point in the sequence**; the one templated artifact — the closing commit body — is +built as a file and asserted placeholder-free before it is used. + +**Type consistency.** `max(risk, security)`, "derived floor", "reminder threshold", "cited set" and +"level 0" are used identically in Tasks 2, 3, 4, 5 and 8, and match the spec's spellings. + +**Gate-B classification, per path.** Task 1 stages one `docs/superpowers/**.md` path → **N/A**, +ordinary commit, cycle not yet open. Tasks 2-7 stage prompts → **one full Gate-B cycle**, opened at +Task 2 Step 7 and closed at Task 8 Step 5, with every intermediate commit an amend restating +`-m "WIP: ..."`. No ordinary commit occurs between those two points. + +**Known limit.** The replacement wordings are proposals, not transcriptions — the spec pins the +rules, not the sentences — and this plan's Gate A is what reviews them. Where a wording restates a +spec rule the spec pinned as *required properties*, it is quoted rather than paraphrased. From cba08867328816e12559a97993f38d468cad3ce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 19:49:25 +0200 Subject: [PATCH 056/117] docs(todos): record the --no-edit Gate-B cycle reset as a hook defect codex-gate.sh:763 recognizes a WIP commit by grepping the BASH COMMAND STRING for `-m ... wip` - not git state, not the commit message. So an amend written the natural way, `git commit --amend --no-edit`, carries no -m, is not recognized, and at :886 `is_commit && ! is_wip_commit` is true: the hook resets and the cycle's accumulated Gate-B passes are discarded with no signal. CLAUDE.md 5 tells everyone to snapshot with `WIP: ...` and close with `--amend -m ""` but never says the recognition is a grep over what you type, so following 5 as written and amending with --no-edit mid-cycle destroys the cycle. Found while writing the review-loop-economics plans, whose whole commit protocol had to be built around it (gate-a-plan-rle-pass-1.md B15, confirmed against source before it was acted on). Loose in both directions, same root cause, both observed this session: a cat heredoc merely CONTAINING the text `WIP:` fired the WIP notice, and one containing `git commit` fired a Gate-B STOP. That direction is harmless per invariant 2; the reset direction is not. Not fixed here: the fix touches hook code and the plans in flight are prompt-only by their own constraint. The row names two candidate rungs and says the choice is the point. Gate B: SKIPPED - triviality skip. todos.md is not in 5's prose-exempt list (docs/**.md, README.md, MANIFEST.md), so the exemption does not apply and the skip is the judgement-based one. Both conditions hold: the change is behaviourally trivial - one backlog row, nothing executes todos.md - and the change cites no story, so it is unprofiled and owes no mode-derived evidence entry, only the reason and the battery result. Battery: green, exit 0, run at 2026-08-29 on this tree. Full AGENTS.md Commands chain including both hook-suite runs (sh and dash), both checker suites, check-version-bump.sh main, and claude plugin validate --strict. The battery's inputs - shell scripts, plugin files, manifest - are untouched by this commit. --- todos.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/todos.md b/todos.md index d7b8a33..fde5722 100644 --- a/todos.md +++ b/todos.md @@ -430,6 +430,32 @@ That belongs in each product project's own `todos.md` once `/workflow-init` has there, not here: this repo ships the workflow, it does not hold another project's backlog. +- [ ] **`git commit --amend --no-edit` silently resets a Gate-B cycle, and nothing warns.** + `plugins/dev-workflow/hooks/codex-gate.sh:763` is + `is_wip_commit() { printf '%s' "$1" | grep -Eiq -- "-m[[:space:]]*['\"]?[[:space:]]*wip"; }` + — it matches the **Bash command string**, not git state and not the commit message. + So an amend written the natural way carries no `-m`, is not recognized as a WIP + commit, and at `:886` `is_commit "$cmd" && ! is_wip_commit "$cmd"` is true: the hook + resets and the accumulated Gate-B passes are discarded. §5 tells everyone to snapshot + with `WIP: …` and close with `--amend -m ""` but never says the + recognition is a grep over what you type, so following §5 as written and amending + with `--no-edit` mid-cycle destroys the cycle with no signal. + **Loose in both directions**, which is the same root cause: a `cat` heredoc merely + *containing* the text `WIP:` fires the WIP notice, and one containing `git commit` + fires a Gate-B STOP. Both observed in the 2026-08-29 session. That direction is + harmless per invariant 2; the reset direction is not. + Found while writing the review-loop-economics plans, whose whole commit protocol had + to be built around it — see + `docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md` + § "The `--no-edit` trap" and `.context/codex-reviews/gate-a-plan-rle-pass-1.md` B15. + **Not fixed there**: those plans are prompt-only by their own constraint, and the fix + touches hook code. Two candidate rungs, and the choice is the point — prose (§5 warns + about `--no-edit` explicitly, cheap, does not stop the next person who forgets) or a + real fix in the hook (recognize an amend that preserves a `WIP:` subject, which means + reading git state rather than the command string). Prefer the latter if the class + recurs; log it through `harden-finding` when it does. + *Trigger: the next change that touches the hook, or a second observed cycle reset.* + - [x] **P2 — risk/security profiles, and the derived validation mode.** Shipped: two human-confirmed axes in the story header, a mode derived as `max(risk, security)`, lens sets appended to the §5 gate prompts, and the Gate-B triviality skip narrowed From 83b17e098603671d790025f82a247004dc80ce63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sat, 29 Aug 2026 19:49:25 +0200 Subject: [PATCH 057/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=202?= =?UTF-8?q?=20=E2=80=94=20repair=20the=20fifteen=20in-set=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 1 on revision 1 (3b94dbf) returned 21 findings, 17 Blocker/Major. Two of those Blockers were about the split itself and are resolved by a topology decision rather than by editing this plan; the other fifteen are repaired here. This is a repair, not a rewrite, so the pass count continues. TOPOLOGY, decided: three Gate-A cycles, ONE Gate-B cycle. Each plan is reviewed as its own artifact - that is where the finding curve actually moved - but the three ship one diff, so the diff is reviewed once, after Plan C. Giving Plan A its own Gate-B cycle produced two Blockers that were pure infrastructure paradox: the battery could not go green because the manifest bump is Plan C's (B4), and the findings slots needed a discriminator whose grammar is Plan B's (B5). Each plan's cycle needed infrastructure a later plan ships. 5's own architecture already says Gate A is per-artifact and Gate B is per-diff. The repairs, by the finding that forced them: - B1: every copied profile value removed. The plan states no risk value, no security value, no mode and no pass count derived from any of them - not even inside the paragraph explaining the defect, because quoting a stale-able value to explain why values go stale reintroduces it. - B2: the accounting has ELEVEN passages, not ten. Revision 1 listed ten and then edited an eleventh, which its own out-of-inventory check would have rejected as a defect. - B3: every executable step addresses by CONTENT, never by line number. Task 1 inserts two large blocks and shifts every later line, so revision 1's `sed -n '133,140p'` and `sed -n '480,493p'` would have read unrelated text. Revision 1 fixed the expected VALUES for their point in the sequence and left the ADDRESSES at untouched-tree positions; line numbers now appear only as pasted provenance, never as instructions. - B7: the Gate-B cycle reviewing this change runs under the OLD rules in force at its start, stated in Global Constraints and carried in every Gate-B call. A reviewer applying the new Minor-or-below ceiling to the change that introduces it would under-iterate on exactly the diff needing most iteration. - M1: the pass-report passage gets two accounting rows. Verified with diff, not by comparing first lines: the copies genuinely differ - wrapping, and "you report the tells" against "report the tells". The divergence is pre-existing and is left alone, which the plan says explicitly so no executor "harmonizes" it outside the accounted dispositions. - M2: one record, not three. Revision 1 wrote the accounting in the plan, copied it to a second artifact, and later mutated that copy with result tables. - M6: the Gate-B rationale is corrected rather than reworded twice. The original claim ("where the 3 come from") was false under the new design; revision 1's replacement made the advisory hook the CAUSE of the re-review obligation, which is a subtler version of the same error. The obligation exists because the prior review no longer covers the changed artifact. This is the "each correction introduced a subtler version of the same claim" pattern AGENTS.md records over four rounds, caught here at round one. - M4: the Task 4 verification captures its baseline before the append and scopes both measurements to the paragraph, so a token the appended text introduces cannot mask a condition the edit dropped. - M7: parity compares extracted TEXT with diff, not marker occurrence counts, and treats an empty range as a failure - revision 1 would have printed PARITY for two absences comparing equal. - M3: every preflight distinguishes not-started, complete, duplicate and damaged, and asymmetry between the two copies is its own state with its own answer. - MINOR 13: the "no hook state file is written" claim is qualified. Gate-B calls and commit events do write counters, fingerprints and markers under .context/; only codex-gate.floor is untouched. - MINOR 14: the blanket sequence-expected-value claim is replaced by the claim plus its two named exceptions. Also found by this session's own checks after writing, not by the review: the Tech Stack claimed POSIX shell for every check while the parity check used process substitution. The check was rewritten POSIX-clean rather than the claim weakened. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 989 ++++++++---------- 1 file changed, 453 insertions(+), 536 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index 8ed39bb..c119a52 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -9,31 +9,32 @@ pass floor becomes a function of the cited story's profile, and finding severity whether something in the system takes a different decision. **Architecture:** Two mirrored prose edits, site by site: `CLAUDE.md` §5 and the inline template in -`/workflow-init`. **No code. Nothing under `plugins/dev-workflow/hooks/` is touched and no hook -state file is written.** +`/workflow-init`. **No code.** No file under `plugins/dev-workflow/hooks/` changes. **Tech Stack:** Markdown prompts; POSIX shell for every check; `git` for the records. **Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36, Gate A closed clean at pass 34). **Read it alongside this plan.** Plan A implements §2, §2.1, §2.2, §2.4, -§3 and the part of §10 that covers the rules it ships. +§3, and §10 in part. **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` -> **Read the profile from that header and from nowhere else.** This plan deliberately does not -> copy the risk value, the security value or the validation mode into itself. §5 makes the story -> header the single writable copy: a copy here would go stale the moment the profile moved, and a -> stale floor is this change's own central failure mode. +> **Read the profile from that header at execution time. This plan states no risk value, no +> security value, no validation mode and no pass count derived from any of them.** §5 makes the +> story header the single writable copy; a value copied here goes stale the moment the profile +> moves, and a stale floor is this change's own central failure mode. Revision 1 of this plan +> promised exactly that and then wrote the story's risk value, its validation mode, and a pass +> count derived from them into its own prose — finding B1. They are not repeated here, not even +> to name the mistake: quoting a stale-able value to explain why values go stale reintroduces it. --- ## Why this is one of three plans -The single 748-line plan this replaces opened **31 Blocker/Major at Gate-A pass 1** (`5c00f8c`; -findings at `.context/codex-reviews/gate-a-plan-rle-pass-1.md`). Twelve of those findings were one -shape — the plan gestured at settled spec content instead of shipping it — and the artifact was -already too long while still needing to grow. The split is A → B → C, sequenced, each its own -Gate-A cycle: +The single 748-line plan this replaces opened **31 Blocker/Major at Gate-A pass 1** (`5c00f8c`). +Twelve of those findings were one shape — the plan gestured at settled spec content instead of +shipping it — and the artifact was already too long while still needing to grow. Plan A revision 1 +then opened **17** (`3b94dbf`), with Blockers falling 21 → 7. | Plan | Ships | Spec sections | |---|---|---| @@ -41,45 +42,40 @@ Gate-A cycle: | **B** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | | **C** | rollout: the falsified user-facing sentences, packaging, the evidence pack | §7, §8 | -**B may not open before A's loop closes.** Only two interventions ever moved a finding curve in -this cycle — splitting an artifact and slimming one — so this is the move with evidence behind it, -not a preference. It costs three separate 3-pass floors under the story's `high` profile. That is -the story's own floor rule applying to the work that writes it, and it is recorded as such rather -than worked around. +**Three Gate-A cycles, ONE Gate-B cycle.** Each plan is reviewed as its own artifact — that is +where the finding curve actually moved — but the three ship **one diff**, so the diff is reviewed +once, after Plan C. §5's own architecture already works this way: Gate A is per-artifact and Gate B +is per-diff. -### Where pass 1's findings went — every one, to exactly one plan +Revision 1 tried to give Plan A its own Gate-B cycle and two Blockers fell straight out of it: the +battery could not go green because the manifest bump is Plan C's (B4), and the findings slots +needed a discriminator whose grammar is Plan B's (B5). **Each plan's own cycle needed +infrastructure a later plan ships.** Splitting the review of one diff was never coherent. -Stated so nothing falls between three documents the way the passage list fell between two. +**Execution order A → B → C. Plan B may not open before Plan A's Gate-A loop closes.** -| Finding | Owner | Note | -|---|---|---| -| B1 conditions artifact circular | **A** | dissolved: the accounting is a *section of this plan*, reviewed by this plan's Gate-A passes | -| B2 passage set unsound | **A** | dissolved the way B2 asked: the set is **derived from the planned edits**, not imported from revision 15 | -| B3 pass-report paragraph unedited | **A** | Task 4 | -| B4 floor replacement range undelimited | **A** | Task 2 gives exact old and new text and dispositions `counted by the hook` | -| B5 gate-off list read as complete | **A** | Task 2 Step 4 ships §10's "routes known today, not a complete list" framing | -| B6 §2.4 mid-cycle missing | **A** | Task 5 | -| B7 §10 activation missing | **A** | Task 6, for the two parts A ships; **B extends it** to the three record duties | -| B8 `7/7` false in sequence | **A** | every check below states its value *for its point in the sequence* | -| B9-B13 records/nonce/slots under-shipped | **B** | | -| B14-B16 commit topology | **A**, restated in B and C | resolved once, below; each plan repeats the resolution | -| B17 `N/N cycle` in getting-started | **C** | | -| B18 re-review without amending | **A**, restated in B and C | Task 8 | -| B19 evidence produced after close | **C** | | -| B20 literal `` placeholder | **A**, restated in B and C | Task 8 builds the body before amending | -| B21 rewriting closed commit bodies | **C** | | -| M1 one row for two copies | **A** | the accounting has a row **per copy** | -| M2 plan copies the profile values | **A** | header above carries the path only | -| M3 ellipses in replacement text | **A** | every replacement is complete text | -| M4 false `where the 3 come from` rationale | **A** | Task 3 | -| M5 "the floor is unchanged" | **A** | Task 3 | -| M6 nonce diagnostics | **B** | | -| M7 parity result schema | **A** | Task 7 pins the tables | -| M8 future-nonce checkpoint | **C** | | -| M9 interruption / rerun safety | **A**, restated in B and C | every edit task has a preflight | -| M10 `main` currency | **C** | | -| MINOR 11 CHANGELOG content | **C** | | -| NIT 12 `(identical)` in a pasted block | **A** | no editorial token appears inside any pasted block below | +### Where pass 1's findings went + +Every finding from `.context/codex-reviews/gate-a-plan-rle-pass-1.md` (the 748-line plan) and +`.context/codex-reviews/gate-a-plan-plana-pass-1.md` (this plan, revision 1) is assigned to exactly +one plan, so nothing falls between three documents. + +| From | Finding | Owner | Resolution | +|---|---|---|---| +| rle | B1 conditions artifact circular · B2 passage set unsound | **A** | the accounting is a *section of this plan*, derived from the edits this plan makes, reviewed by this plan's Gate-A cycle. No second artifact — see finding M2 below | +| rle | B3 pass-report unedited · B4 replacement range · B5 gate-off list · B6 §2.4 · B7 §10 · B8 sequence values | **A** | Tasks 1-5 | +| rle | B9-B13 records/nonce/slots · M6 nonce diagnostics | **B** | | +| rle | B17 · B19 · B21 · M8 · M10 · MINOR 11 | **C** | | +| rle | B14-B16, B18, B20 commit topology | **A**, restated in B and C | resolved below; one WIP, one loop after C | +| rle | M1 rows per copy · M2 profile values · M3 ellipses · M4 false rationale · M5 "unchanged" · M7 parity schema · M9 rerun safety | **A** | | +| plana | B1 profile values copied | **A** | header above; no derived pass count anywhere | +| plana | B2 eleventh passage missing | **A** | the accounting has **eleven** passages | +| plana | B3 line numbers shift under earlier tasks | **A** | **every executable step addresses by content, never by line number** | +| plana | B4 battery cannot go green · B5 illegal slot names | **A** | dissolved by the one-Gate-B-cycle topology | +| plana | B6 evidence not revalidated | **C** | the evidence pack belongs to the combined close | +| plana | B7 cycle runs under old rules | **A**, restated in B and C | Global Constraints below | +| plana | M1 pass-report passage differs · M3 preflight states · M4 baseline · M5 "profile alone" · M6 causal claim · M7 parity script · M8 recovery · M9 knob bytes · M10 `git add -u` | **A** | | +| plana | 4 MINOR | **A** | Task 6 Step 3 proof, `mktemp`, hook-state qualification, self-review claim | --- @@ -87,46 +83,54 @@ Stated so nothing falls between three documents the way the passage list fell be Verbatim from the spec. Every task's requirements implicitly include these. -- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes; no hook state file is - written, in particular not `.context/codex-gate.floor`. +- **This Gate-B cycle runs under the rules in force at its start — the OLD ones.** The new + severity semantics, the new floor rule and the new record forms bind only **after** the closing + commit ships them. This is spec §10's activation rule applied to the change's own review: a + reviewer applying the new Minor-or-below ceiling to the change that introduces it would + under-iterate on exactly the diff that needs the most iteration. **Carry this sentence in the + `additionalContext` of every Gate-B call** (finding plana-B7). +- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes, and + **`.context/codex-gate.floor` is never written, never removed and never read for the + derivation.** Note what this does *not* claim: the Gate-B calls and commit events in this work + will write pass counters, fingerprints and disclosure markers under `.context/` as they always + do. Only the floor knob is untouched (finding plana-MINOR 13, which read revision 1's blanket + "no hook state file is written" as false — it was). - **The §5 heading must keep matching `^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`.** `codex-gate.sh:94` greps `CLAUDE.md` for it to build every reminder's citation. - **Every rule lands in BOTH copies**, except where a difference is deliberate and stated. - **§5's other closure rules are never restated, only referred to.** Three spec revisions were spent on this: each summary of the triviality skip dropped a different condition. -- **Anchors are pasted `grep -n` output.** No editorial token ever appears inside a pasted block — - writing `(identical)` in place of a second grep line cost the predecessor its blanket claim. +- **Address by content, never by line number.** Line numbers appear below only as *provenance* — + pasted `grep -n` output showing where a passage stood in the untouched tree. Every **executable** + step locates its target by matching text, because Task 1 inserts two large blocks and shifts + every line number after them. Revision 1 fixed the expected *values* for their point in the + sequence and left the *addresses* at their untouched-tree positions (finding plana-B3). +- **Anchors are pasted `grep -n` output.** No editorial token ever appears inside a pasted block. -### The commit protocol — read this before Task 1 +### The commit protocol -**All ordinary commits happen BEFORE the WIP opens. Then one WIP commit, then the review loop, -then the close.** Nothing commits ordinarily while the cycle is open. +**One WIP commit, opened here, amended by Plans B and C, reviewed once after C, closed once.** -> **The `--no-edit` trap — this is why the protocol looks verbose.** +> **The `--no-edit` trap — this is why every amend below looks verbose.** > `plugins/dev-workflow/hooks/codex-gate.sh:763` is > `is_wip_commit() { printf '%s' "$1" | grep -Eiq -- "-m[[:space:]]*['\"]?[[:space:]]*wip"; }` > It matches **the Bash command string**, not git state and not the commit message. So > `git commit --amend --no-edit` carries no `-m`, is **not** recognized as a WIP commit, and at -> `:886` `is_commit "$cmd" && ! is_wip_commit "$cmd"` is true — **the cycle resets and your passes -> are discarded.** Every cycle-internal amend below therefore restates `-m "WIP: ..."` in full. -> Never `--no-edit` inside the cycle. +> `:886` the hook resets — **discarding the cycle's accumulated passes.** Every amend below +> therefore restates `-m "WIP: ..."` in full. **Never `--no-edit` inside the cycle.** > -> That the shipped rules do not warn about this is a real defect, routed to the backlog. **Do not -> fix the hook here** — this plan is prompt-only by its own constraint above. - -The topology, in order: +> That the shipped rules do not warn about this is a real defect; it is on the backlog. **Do not +> fix the hook here.** -1. **Task 1** commits the conditions accounting — ordinary commit, Gate B N/A, **cycle not yet - open**. -2. **Task 2 opens the cycle**: `git commit -m "WIP: "`. -3. **Tasks 3-7 amend it**, each restating `-m "WIP: "`. Same subject every time. -4. **Task 8** runs the review loop against that commit (`baseSha` = its parent), re-amending after - every fix, and closes with `git commit --amend -m ""` — the first message without - `WIP:`, which the hook correctly reads as the cycle closing. +1. **No ordinary commit happens from Task 1 onward** until the combined cycle closes in Plan C. + Plan A needs none: the accounting lives in this document, which is already committed. +2. **Task 1 opens the cycle**: `git commit -m "WIP: review-loop economics"`. +3. **Tasks 2-6 amend it**, each restating that exact message. +4. **Plans B and C amend the same commit.** Plan C adds the manifest bump, runs the single Gate-B + loop, and closes with `git commit --amend -m ""`. -**Task 7's parity and conformance results are folded into the WIP commit, not committed -separately.** A separate docs commit after the WIP opens would reset the cycle regardless of which -paths it staged. +**The subject is `WIP: review-loop economics` — not plan-specific**, because all three plans amend +one commit. --- @@ -134,30 +138,34 @@ paths it staged. | File | Responsibility | Gate B | |---|---|---| -| this plan, § "Old-conditions accounting" | what the existing prose required, per copy, dispositioned | N/A — reviewed by this plan's Gate-A cycle | -| `CLAUDE.md` §5 | the live rules | full (in the cycle) | -| `plugins/dev-workflow/commands/workflow-init.md` §5 | the scaffolded mirror | full (in the cycle) | -| `docs/superpowers/specs/…-plan-a-conditions.md` | the accounting, extracted for the record | N/A — ordinary commit, Task 1, before the cycle opens | +| this plan, § "Old-conditions accounting" | what the existing prose requires, per copy, dispositioned | N/A — reviewed by this plan's Gate-A cycle | +| `CLAUDE.md` §5 | the live rules | in the combined cycle, closed by Plan C | +| `plugins/dev-workflow/commands/workflow-init.md` §5 | the scaffolded mirror | in the combined cycle, closed by Plan C | + +**There is no separate conditions artifact.** Revision 1 wrote the accounting here *and* copied it +to a second file *and* later mutated that copy with result tables — three records able to disagree, +where spec §6 asks for one (finding plana-M2). Task 6's results append to **this document**. --- ## Old-conditions accounting -**Derived from the edits this plan actually makes**, against §5 as it stands at HEAD — not -imported from an earlier spec revision. That was finding B2: an imported list can be stale, -overinclusive and incomplete at once, and the exact-once grep that guarded it proved only that the -lead-ins existed. +**Derived from the edits this plan actually makes**, against §5 as it stands at HEAD — not imported +from an earlier spec revision. **Eleven passages.** Revision 1 listed ten and then edited an +eleventh in Task 3, which Task 7's out-of-inventory check would have rejected as a defect +(finding plana-B2). -**One row per passage per copy.** Where the two copies' text is byte-identical for a passage the -rows are identical and say so; a condition unique to one mirror cannot hide behind a shared row -(finding M1). +**Rows are per copy where the copies differ.** Ten passages are byte-identical across both copies +over their whole block, so they carry one shared row each. **The pass-report passage is not** — +verified by `diff`, not by comparing its first line — so it carries two (finding plana-M1). -Anchors, pasted from `grep -n` — `CLAUDE.md` then `plugins/dev-workflow/commands/workflow-init.md`: +Provenance, pasted from `grep -n` — these are *addresses in the untouched tree*, not instructions: ``` CLAUDE.md:72:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major CLAUDE.md:77:final pass must be clean — if pass 3 still finds Blocker/Major, keep going until CLAUDE.md:79:below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +CLAUDE.md:126:the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps CLAUDE.md:133:**From pass 4 onward every pass report carries three lines.** The carrier is **your own CLAUDE.md:236:act on the partial list, don't count it toward the 3-pass floor, and don't read "no CLAUDE.md:300:- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the @@ -171,6 +179,7 @@ CLAUDE.md:495:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major ( plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major plugins/dev-workflow/commands/workflow-init.md:277:final pass must be clean — if pass 3 still finds Blocker/Major, keep going until plugins/dev-workflow/commands/workflow-init.md:279:below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +plugins/dev-workflow/commands/workflow-init.md:322:the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps plugins/dev-workflow/commands/workflow-init.md:330:**From pass 4 onward every pass report carries three lines.** The carrier is **your own plugins/dev-workflow/commands/workflow-init.md:421:act on the partial list, don't count it toward the 3-pass floor, and don't read "no plugins/dev-workflow/commands/workflow-init.md:485:- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the @@ -180,112 +189,64 @@ plugins/dev-workflow/commands/workflow-init.md:659:**Changing a profile:** the p plugins/dev-workflow/commands/workflow-init.md:674:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → ``` -**Ten passages, each in two copies — twenty rows.** Both copies are byte-identical at every one of -these ten passages; Task 1 Step 2 proves that mechanically rather than asserting it, and the rows -below are written once with that proof standing in for the second copy. **If that proof fails for -any passage, that passage gets two separate rows and the difference is dispositioned.** - -| # | Passage | What the existing prose requires | Disposition | -|---|---|---|---| -| 1 | floor paragraph (72 / 272) | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. a satisfied count is not a clean review · e. Gate A is instruction-backed · f. the hook resets at `writing-plans` · g. a TodoWrite per pass · h. fix Blocker/Major after each · i. Codex is advisory · j. validate before applying · k. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 2) · c. **moved** — the hook still counts, but as its reminder threshold, not the obligation (Task 2 Step 3) · b, d-k **kept verbatim** | -| 2 | `if pass 3 still` (77 / 277) | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, with `pass 3` → `the pass at the floor` (Task 3) | -| 3 | `below 3` (79 / 279) | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, with `below 3` → `below the floor` (Task 3) | -| 4 | pass-report paragraph (133 / 330) | a. from pass 4 onward, three lines · b. the carrier is your own status report · c. never the Codex reply · d. never the findings file · e. the trend line · f. the cluster line · g. the require↔withdraw line | a. **kept and extended** — the three lines still start at pass 4; §2.2's three fields are owed by **every** pass (Task 4) · b-g **kept verbatim** | -| 5 | incomplete-pass (236 / 421) | an incomplete pass is not a review: don't act on the partial list, don't count it toward the floor, don't read "no Blocker/Major visible" as clean | **kept**, with `the 3-pass floor` → `the floor` (Task 3) | -| 6 | Gate A loop (300 / 485) | Gate A is two runs, each its own 3-pass loop; one broad prompt; re-run each pass over the revised artifact | **kept**, with `3-pass loop` → `loop at the derived floor` (Task 3) | -| 7 | `where the 3 come from` (335 / 519) | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept**, but its **rationale is corrected** — the number no longer comes from invalidation (Task 3, finding M4) | -| 8 | Lenses (403 / 582) | lenses are different questions, not more passes; the floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are unchanged | **kept**, reworded so "unchanged" no longer claims the floor is fixed (Task 3, finding M5) | -| 9 | `Changing a profile:` (480 / 659) | a. the pass proposes the complete resulting header · b. the human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's mid-cycle rules are **appended** after them, not merged into them (Task 5) | -| 10 | Severity (495 / 674) | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test is **appended** as the procedure that sets a ceiling on them (Task 6) | - -**Nothing in §5 outside these ten passages is edited by Plan A.** Task 7 Step 3 proves that by diff. +| # | Passage | Copy | What the existing prose requires | Disposition | +|---|---|---|---|---| +| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. a satisfied count is not a clean review · e. Gate A is instruction-backed · f. the hook resets at `writing-plans` · g. a TodoWrite per pass · h. fix Blocker/Major after each · i. Codex is advisory · j. validate before applying · k. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1) · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · b, d-k **kept verbatim** | +| 2 | `if pass 3 still` | both | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, `pass 3` → `the pass at the floor` (Task 2) | +| 3 | `below 3` | both | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, `below 3` → `below the floor` (Task 2) | +| 4 | pass-1 Minor sentence | both | inside the "clearly stuck" rule: below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass **below the floor**` (Task 2 Step 4). **This is the sentence that inverts at floor 1**, where pass 1 *is* the floor | +| 5a | pass-report paragraph | `CLAUDE.md` | a. from pass 4 onward, three lines · b. the carrier is your own status report · c. never the Codex reply · d. never the findings file · e. trend · f. cluster · g. require↔withdraw · **h. "you report the tells"** — second person, addressee named | a. **kept and extended** — the three lines still start at pass 4; §2.2's three fields are owed by **every** pass (Task 3) · b-h **kept verbatim, including the second person** | +| 5b | pass-report paragraph | template | a-g as above · **h′. "report the tells"** — imperative, no addressee · plus different line wrapping | same disposition; **the divergence is pre-existing and is left alone.** Task 3 inserts a new paragraph *before* this one and modifies neither copy, so neither the wording nor the wrapping difference is touched | +| 6 | incomplete-pass | both | an incomplete pass is not a review: don't act on the partial list, don't count it toward the floor, don't read "no Blocker/Major visible" as clean | **kept**, `the 3-pass floor` → `the floor` (Task 2) | +| 7 | Gate A loop | both | Gate A is two runs, each its own 3-pass loop; one broad prompt; re-run each pass over the revised artifact | **kept**, `3-pass loop` → `loop at the derived floor` (Task 2) | +| 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept**, and its **rationale corrected** — see Task 2, finding rle-M4 and plana-M6 | +| 9 | Lenses | both | lenses are different questions, not more passes; the floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are unchanged | **kept**, reworded so "unchanged" no longer claims the floor is fixed (Task 2, finding rle-M5) | +| 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. the human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules are **appended after them**, never merged in (Task 4) | +| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test is **appended** as the procedure that sets a ceiling on them (Task 5) | + +**Nothing in §5 outside these eleven passages is edited by Plan A.** Task 6 Step 3 proves it by +reading the diff, for both files. --- -## Task 1: The accounting, committed before the cycle opens - -**Files:** Create `docs/superpowers/specs/2026-08-29-review-loop-economics-plan-a-conditions.md` - -- [ ] **Step 1: Preflight — is this already done?** - -```bash -test -e docs/superpowers/specs/2026-08-29-review-loop-economics-plan-a-conditions.md \ - && echo "EXISTS — read it and skip to Step 4" || echo "ABSENT — proceed" -``` - -Every edit task below opens with a preflight, because a plan abandoned halfway must be resumable -without duplicating an append (finding M9). - -- [ ] **Step 2: Prove the two copies are byte-identical at all ten passages** - -The accounting above writes one row per passage on the strength of this. Run it before trusting it: - -```bash -C=CLAUDE.md; T=plugins/dev-workflow/commands/workflow-init.md -for pair in 72:272 77:277 79:279 133:330 236:421 300:485 335:519 403:582 480:659 495:674; do - a=${pair%:*}; b=${pair#*:} - if [ "$(sed -n "${a}p" "$C")" = "$(sed -n "${b}p" "$T")" ]; then - printf 'IDENTICAL %s/%s\n' "$a" "$b" - else - printf 'DIFFERS %s/%s <<<%s>>> <<<%s>>>\n' "$a" "$b" \ - "$(sed -n "${a}p" "$C")" "$(sed -n "${b}p" "$T")" - fi -done -``` +## Task 1: Open the cycle, and replace the floor -**Expected at this point in the sequence: ten `IDENTICAL` lines, no `DIFFERS`.** Any `DIFFERS` line -means that passage needs two rows in the accounting and a stated disposition for the difference — -fix the accounting before proceeding, do not proceed with a shared row. - -- [ ] **Step 3: Extract the accounting to the conditions file** - -Copy this plan's § "Old-conditions accounting" verbatim into that file, with a one-paragraph header -naming this plan as its source and Plan A's Gate-A cycle as its review. - -- [ ] **Step 4: Commit — ordinary commit, Gate B N/A, cycle not yet open** - -```bash -git add docs/superpowers/specs/2026-08-29-review-loop-economics-plan-a-conditions.md -git diff --cached --name-only # MUST list exactly that one path -git commit -m "docs(spec): old-conditions accounting for the Plan A rules edits" -``` - -**This is the last ordinary commit until the cycle closes.** Everything after Task 2 amends. - ---- - -## Task 2: Open the cycle, and replace the floor - -**Files:** `CLAUDE.md:72`, `plugins/dev-workflow/commands/workflow-init.md:272` +**Files:** `CLAUDE.md`, `plugins/dev-workflow/commands/workflow-init.md` — floor paragraph. **Interfaces:** -- Produces: the phrase `max(risk, security)` inside the floor paragraph, which Task 3's sites and - Task 6's severity text both refer back to. +- Produces: `max(risk, security)` inside the floor paragraph, which Tasks 2 and 5 refer back to. -- [ ] **Step 1: Preflight** +- [ ] **Step 1: Preflight — four states, four answers** ```bash for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f" - sed -n '/Both gates are a LOOP with a HARD FLOOR/,/^$/p' "$f" | grep -c 'max(risk, security)' + old=$(grep -cF 'HARD FLOOR: min 3 passes per run' "$f") + new=$(grep -cF 'derived from the cited story' "$f") + printf '%s old=%s new=%s\n' "$f" "$old" "$new" done ``` -**Expected at this point in the sequence: `0` and `0`** — this is the failing check. `1` and `1` -means Task 2 already ran; verify against Step 3's text and skip to Step 5. +| old / new | state | what to do | +|---|---|---| +| `1 / 0` | **not started** | proceed to Step 2 | +| `0 / 1` | **complete** | verify against Step 3's text, skip to Step 6 | +| `1 / 1` | **duplicate insert** | a rerun appended without removing; delete the inserted block and restart | +| `0 / 0` | **damaged** | neither text present — STOP, restore from `git show HEAD:` | -**The `sed` scoping is load-bearing.** An unscoped `grep -c 'max(risk, security)'` returns **1** for -both files even before any edit, because the Profiles section already contains the phrase. The -check would pass before the edit and prove nothing. +**Asymmetry between the copies is its own state:** if the two files report different pairs, +execution was abandoned between them. Bring the lagging copy to the leading copy's state before +proceeding — never proceed with the mirrors disagreeing (finding plana-M3). -- [ ] **Step 2: Read the exact text being replaced** +- [ ] **Step 2: Read the exact text being replaced, by content** ```bash -sed -n '72,80p' CLAUDE.md -sed -n '272,280p' plugins/dev-workflow/commands/workflow-init.md +for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do + echo "--- $f ---" + awk '/HARD FLOOR: min 3 passes per run/,/dismissed finding → one-line why/' "$f" +done ``` -The old text, in both copies (finding B4 — the replaced range is delimited, not left to judgement): +The old text, in both copies: ``` **Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major @@ -299,13 +260,12 @@ below 3 is a pass with **zero** findings; don't manufacture findings to pad. Cod advisory — validate before applying; dismissed finding → one-line why. ``` -**Lines 77 and 79 of that block are Task 3's sites 2 and 3.** Task 2 replaces only the first two -sentences — through `counted by the hook.` and the sentence that explains it — and leaves the rest -of the paragraph on disk untouched. Task 3 then edits lines 77 and 79 in place. +**Task 1 replaces only the first two sentences** — through `a satisfied count is not a clean +review.` The rest of the paragraph stays on disk; Task 2 edits two of its lines in place. - [ ] **Step 3: The replacement — complete text, no ellipsis** -Replace exactly these two sentences: +Replace exactly: ``` **Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major @@ -344,8 +304,8 @@ never written, never removed, never read for this derivation. - [ ] **Step 4: Add the residual and the gate-off disclosure** -Immediately after the block above, in both copies. Finding B5: the list is explicitly **not** -exhaustive, and it carries every route §10 names. +Immediately after the block above, in both copies. The list is explicitly **not** exhaustive +(finding rle-B5). ```markdown **Named residual:** the hook's messages state its own threshold as an obligation, so at a floor of @@ -355,44 +315,50 @@ reminder being harmless. **The gate-off surface — routes known today, not a complete list**, since an enumeration read as complete guarantees the routes it omits. **One route is created here**: a stated floor the cited -set does not license, which could not exist before there was a derived floor to state. **Pre-existing -and unchanged**: omitting a higher-risk cited story; minting or editing a profile to level 0; -presenting an incomplete cited set; falsifying evidence entries; silencing reminders; or not -running a pass and reporting that it ran. **A user-set floor is not the lever** — it moves what the -hook says, not what the cycle owes. **None of this is a guard**: the floor is produced by the agent -and nothing checks it against the cited profiles. +set does not license, which could not exist before there was a derived floor to state. +**Pre-existing and unchanged**: omitting a higher-risk cited story; minting or editing a profile to +level 0; presenting an incomplete cited set; falsifying evidence entries; silencing reminders; or +not running a pass and reporting that it ran. **A user-set floor is not the lever** — it moves what +the hook says, not what the cycle owes. **None of this is a guard**: the floor is produced by the +agent and nothing checks it against the cited profiles. ``` -- [ ] **Step 5: Re-run Step 1's check** - -**Expected now: `1` and `1`.** - -- [ ] **Step 6: Verify the paragraph's tail survived** +- [ ] **Step 5: Verify by content** ```bash for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f" - tr '\n' ' ' < "$f" | tr -s ' ' | grep -c "don't manufacture findings to pad" + printf '%s ' "$f" + printf 'predicate=%s ' "$(awk '/HARD FLOOR/,/^$/' "$f" | grep -c 'max(risk, security)')" + printf 'residual=%s ' "$(grep -cF 'Named residual' "$f")" + printf 'gateoff=%s ' "$(grep -cF 'routes known today, not a complete list' "$f")" + printf 'tail=%s\n' "$(grep -cF "don't manufacture findings to pad" "$f")" done ``` -**Expected: `1` and `1`** — same as before the edit. This catches a replacement that swallowed its -neighbours. +**Expected at this point in the sequence: `predicate=1 residual=1 gateoff=1 tail=1` for both.** -- [ ] **Step 7: OPEN THE CYCLE** +The `awk` scoping on `predicate` is load-bearing: an unscoped `grep -c 'max(risk, security)'` +returns 1 for both files **even before any edit**, because the Profiles section already contains +the phrase. Unscoped, the check would pass before the edit and prove nothing. + +`tail=1` must hold **before and after** — it catches a replacement that swallowed its neighbours. + +- [ ] **Step 6: OPEN THE CYCLE** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit -m "WIP: review-loop economics Plan A — floor predicate and severity test" +git diff --cached --name-only # exactly these two paths +git commit -m "WIP: review-loop economics" ``` -**That subject is reused verbatim by every amend in Tasks 3-7.** +**Plans B and C amend this same commit with this same message.** No ordinary commit occurs from +here until Plan C closes the cycle. --- -## Task 3: The seven floor-wording sites, per copy +## Task 2: The floor-wording sites -**Interfaces:** consumes Task 2's predicate; produces nothing. +**Interfaces:** consumes Task 1's predicate; produces nothing. - [ ] **Step 1: Preflight — the sequence-correct count** @@ -401,176 +367,125 @@ grep -cE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected at this point in the sequence: `6` and `6` — not 7.** Seven is the count in an -untouched tree; Task 2 has already removed the `min 3 passes` match at line 72/272. Recording 7 -here was finding B8: a true measurement of today's tree is not the check's value mid-plan. +**Expected at this point in the sequence: `6` and `6` — not 7.** Seven is the count in an untouched +tree; **Task 1 has already removed the `min 3 passes` match.** Recording 7 here was finding rle-B8. -After this task both must be **0**. +`0` and `0` means this task already ran — verify Steps 3-4 by content and skip to Step 6. -- [ ] **Step 2: The `pass 1` site the regex does not match** +- [ ] **Step 2: This regex does not cover every site** ```bash -grep -n 'carrying a Minor keeps' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -c 'carrying a Minor keeps' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected: `CLAUDE.md:126` and `workflow-init.md:322`.** This site contains no digit `3` and the -Step 1 regex never matched it — so Step 1 reaching 0 does **not** cover it. It is asserted -separately at Step 5. +**Expected: `1` and `1`.** This is accounting passage 4. It contains no digit `3`, so Step 1's +regex never matched it and **Step 1 reaching 0 does not cover it.** Step 4 asserts it separately. -- [ ] **Step 3: Apply six replacements per copy** +- [ ] **Step 3: Six replacements per copy — complete text, no ellipses** -Complete text, no ellipses (finding M3). **CLAUDE.md line : template line.** +Located by content. Old text, then new text. -**77 : 277** — old: -``` -final pass must be clean — if pass 3 still finds Blocker/Major, keep going until -``` -new: -``` -final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until -``` +**a.** old: `final pass must be clean — if pass 3 still finds Blocker/Major, keep going until` +new: `final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until` -**79 : 279** — old: -``` -below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is -``` -new: -``` -below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is -``` +**b.** old: `below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is` +new: `below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is` -**236 : 421** — old: -``` -act on the partial list, don't count it toward the 3-pass floor, and don't read "no -``` -new: -``` -act on the partial list, don't count it toward the floor, and don't read "no -``` +**c.** old: `act on the partial list, don't count it toward the 3-pass floor, and don't read "no` +new: `act on the partial list, don't count it toward the floor, and don't read "no` -**300 : 485** — old: -``` -- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the -``` -new: -``` -- **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the -``` +**d.** old: `- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the` +new: `- **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the` -**335 : 519** — old: -``` - invalidates the prior pass, which is where the 3 come from. -``` +**e.** old: ` invalidates the prior pass, which is where the 3 come from.` new: ``` - invalidates the prior pass — which is why a fix costs another pass, though the floor itself - comes from the profile. + no longer covers the artifact — which is why a fix costs another pass. The floor itself + comes from the profile, and the hook's fingerprint only decides when it reminds you. ``` -> Finding M4: `which is where the 3 come from` was a **causal claim that the new design makes -> false**. The lower bound now comes from the story profile; invalidation only explains why a fix -> requires another review. Rewording it to "where the floor's lower bound comes from" would have -> preserved the false claim in new words — the exact failure `AGENTS.md` records as this repo's -> most persistent defect. - -**403 : 582** — old: -``` -Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major -``` +> Two findings, one sentence. rle-M4: `which is where the 3 come from` is a **causal claim the new +> design makes false** — the lower bound now comes from the profile. plana-M6: revision 1's +> replacement, `the hook invalidates the prior pass — which is why a fix costs another pass`, +> **made the advisory hook the cause of the review obligation**, which is a subtler version of the +> same error. The obligation exists because **the prior review no longer covers the changed +> artifact**; the hook merely compares a fingerprint at commit and review events. This is the +> "each correction introduced a subtler version of the same claim" pattern `AGENTS.md` records — +> caught here at round one instead of round four. +> +> **The preceding text must be read before applying this**, because the replacement changes where +> the sentence's subject comes from: +> ```bash +> grep -B2 -F 'which is where the 3 come from' CLAUDE.md +> ``` +> Apply the new text so the sentence reads grammatically from whatever precedes it. + +**f.** old: `Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major` new: ``` Lenses are **different questions, not more passes** — they change what a pass asks, never how many -passes a cycle owes, which the profile alone decides. The floor, the Blocker/Major +passes a cycle owes, which the profile and the cited set decide together. The floor, the Blocker/Major ``` -> Finding M5: the old sentence continues "… is unchanged." Leaving `The floor … is unchanged` in -> the very change that makes the floor profile-dependent tells readers the opposite of what -> shipped. The rewrite says what is actually unchanged — that lenses add no passes — and lets the -> tail's list of unchanged rules stand. +> rle-M5: the old sentence continues "… is unchanged", which in the change that makes the floor +> profile-dependent tells readers the opposite of what shipped. plana-M5: revision 1 wrote "which +> the profile alone decides" — **also wrong**, because cited-set emptiness, membership, unprofiled +> members and unresolvable members all bear on the result. "The profile and the cited set decide +> together" is what Task 1's predicate actually says. -- [ ] **Step 4: Re-run Step 1's check** +- [ ] **Step 4: The pass-1 Minor sentence, asserted separately** -**Expected now: `0` and `0`.** +old: `the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps` +new: `the only exception, exactly as above; a Blocker/Major-free pass **below the floor** carrying a Minor keeps` -- [ ] **Step 5: The `pass 1` site, asserted separately** - -Old, in both copies (it continues onto the next line — match the fragment, not a reconstructed -sentence): -``` -the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps -``` -new: -``` -the only exception, exactly as above; a Blocker/Major-free pass **below the floor** carrying a Minor keeps -``` +- [ ] **Step 5: Verify** ```bash -grep -c 'a Blocker/Major-free pass \*\*below the floor\*\* carrying a Minor keeps' \ +grep -cE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -c 'Blocker/Major-free pass 1 carrying a Minor' \ +grep -cF 'a Blocker/Major-free pass **below the floor** carrying a Minor keeps' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -``` - -**Expected: `1` and `1` for the first; `0` and `0` for the second.** - -> This is the sentence that inverts at a floor of 1. Before the edit it tells a floor-1 cycle that -> a clean pass 1 keeps looping, which is wrong — pass 1 *is* the floor there. - -- [ ] **Step 6: Confirm the historical citation was NOT touched** - -```bash -grep -c "PR #23's Gate-B pass 3 returned all four findings" \ +grep -cF 'Blocker/Major-free pass 1 carrying a Minor' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF "PR #23's Gate-B pass 3 returned all four findings" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected: `1` and `1`.** It cites an actual pass, not a rule; changing it would falsify a record. +**Expected: `0`/`0`, then `1`/`1`, then `0`/`0`, then `1`/`1`.** + +The last one must stay `1`: it cites an actual pass, not a rule, and changing it would falsify a +record. -- [ ] **Step 7: Amend the WIP commit** +- [ ] **Step 6: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +git commit --amend -m "WIP: review-loop economics" ``` -**Never `--no-edit`.** See the commit protocol above: the hook greps this command string for -`-m ... wip`, and an amend without `-m` resets the cycle. +**Never `--no-edit`.** See the commit protocol. --- -## Task 4: The pass report (§2.2) +## Task 3: The pass report (§2.2) -**Files:** `CLAUDE.md:133`, `plugins/dev-workflow/commands/workflow-init.md:330` - -Finding B3: the old plan added every-pass fields to the floor paragraph while leaving the -pass-report paragraph prescribing only the pass-4 three lines, so the shipped prompt would have -described the report shape in two places that disagreed. +Finding rle-B3: revision 1 of the *748-line* plan added every-pass fields to the floor paragraph +while leaving the pass-report paragraph prescribing only the pass-4 three lines, so the shipped +prompt would have described the report shape in two places that disagreed. - [ ] **Step 1: Preflight** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'the cited stories they were read from' -done -``` - -**Expected at this point in the sequence: `0` and `0`.** - -- [ ] **Step 2: Read the paragraph being extended** - -```bash -sed -n '133,140p' CLAUDE.md -sed -n '330,337p' plugins/dev-workflow/commands/workflow-init.md +grep -cF 'the cited stories they were read from' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -- [ ] **Step 3: Insert §2.2's fields immediately BEFORE that paragraph** +**Expected at this point in the sequence: `0` and `0`.** `1`/`1` means done. -Anchor, pasted from `grep -n`: -``` -CLAUDE.md:133:**From pass 4 onward every pass report carries three lines.** The carrier is **your own -plugins/dev-workflow/commands/workflow-init.md:330:**From pass 4 onward every pass report carries three lines.** The carrier is **your own -``` +- [ ] **Step 2: Insert BEFORE the pass-report paragraph, in both copies** -Insert before it, in both copies: +Locate by content — the paragraph beginning `**From pass 4 onward every pass report carries three +lines.**`. **Insert before it; modify nothing in it.** ```markdown **Every pass report states three things about the floor**, from pass 1 onward: the **derived @@ -580,60 +495,69 @@ still being spent — which is the only time checking it is cheap. This is owed three lines below are owed from pass 4 and are a different obligation. ``` -**The existing paragraph is not modified** — its seven requirements (a-g in accounting row 4) all -stand verbatim, and the inserted text says explicitly that the two obligations are distinct so no -reader merges them. +> **The two copies of the following paragraph differ** — wrapping, and `you report the tells` in +> `CLAUDE.md` against `report the tells` in the template (accounting rows 5a/5b). **That divergence +> is pre-existing and stays.** This step inserts a new paragraph and touches neither copy of the +> old one, so it neither fixes nor worsens the difference. Do not "harmonize" them here: that would +> be an edit outside the accounted dispositions. -- [ ] **Step 4: Re-run Step 1's check, and confirm the old paragraph is intact** +- [ ] **Step 3: Verify, including that the old paragraph is untouched** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'the cited stories they were read from' -done -grep -c 'From pass 4 onward every pass report carries three lines' \ +grep -cF 'the cited stories they were read from' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'From pass 4 onward every pass report carries three lines' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -c 'require↔withdraw pair' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'require↔withdraw pair' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'you report the tells' CLAUDE.md +grep -cF 'report the tells' plugins/dev-workflow/commands/workflow-init.md ``` -**Expected: `1`/`1`, then `1`/`1`, then `1`/`1`.** +**Expected: `1`/`1`, `1`/`1`, `1`/`1`, then `1`, then `1`.** The last two assert the pre-existing +divergence is still exactly as it was. -- [ ] **Step 5: Amend the WIP commit** +- [ ] **Step 4: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +git commit --amend -m "WIP: review-loop economics" ``` --- -## Task 5: A profile or cited set that moves mid-cycle (§2.4) +## Task 4: A profile or cited set that moves mid-cycle (§2.4) -**Files:** `CLAUDE.md:480`, `plugins/dev-workflow/commands/workflow-init.md:659` +Finding rle-B6. §2.4 composes three existing rules and adds no new one, but none of the composition +is currently written down, so a mid-cycle profile or set edit can silently reuse an old clean pass. -Finding B6. §2.4 composes three existing rules and adds no new one, but none of the composition is -currently written down, so a mid-cycle profile or set edit can silently reuse an old clean pass. - -- [ ] **Step 1: Preflight** +- [ ] **Step 1: Preflight, and capture the baseline the verification needs** ```bash +grep -cF 'Any profile change costs at least one further pass' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md + for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'Any profile change costs at least one further pass' + printf 'BASELINE %s: ' "$f" + awk '/\*\*Changing a profile:\*\*/,/^$/' "$f" | grep -o \ + 'proposes the complete resulting header\|human confirms it\|never moves it alone\|append one profile-log line\|voids every prior override\|follows the current security value\|keep counting\|final clean pass\|fold the edit into the active' \ + | sort | uniq -c | tr '\n' ' '; echo done ``` -**Expected at this point in the sequence: `0` and `0`.** +**Expected: `0` and `0` for the first.** **Record the BASELINE lines verbatim** — Step 3 compares +against them. -- [ ] **Step 2: Read the nine conditions being preserved** +> Finding plana-M4: revision 1 told the executor to compare against a baseline it never captured, +> and its post-edit check counted tokens across the **whole file**, where the appended text itself +> adds `keep counting` and `final clean pass` — so the count could rise while an old condition was +> dropped. This baseline is **scoped to the `Changing a profile:` paragraph** and captured **before** +> the append, and Step 3 re-scopes the same way. -```bash -sed -n '480,493p' CLAUDE.md -``` +- [ ] **Step 2: Append after the `Changing a profile:` paragraph, both copies** -All nine are accounting row 9 (a-i). **They are kept verbatim.** §2.4's rules are **appended after -them**, never merged into them — merging is how a rewrite drops a condition, which `AGENTS.md` -names as this repo's tenth-recorded instance. - -- [ ] **Step 3: Append after the `Changing a profile:` paragraph, both copies** +Its nine conditions are accounting row 10 (a-i). **They are kept verbatim.** §2.4's rules are +**appended after them**, never merged into them — merging is how a rewrite drops a condition, which +`AGENTS.md` names as this repo's tenth recorded instance. ```markdown **While a gate is running, the floor derives from the *current* profile at each pass.** Passes @@ -659,53 +583,51 @@ lenses and evidence duty — but it **never discharges an accepted in-set Blocke acceptance put that finding in the fix set, not the citation. ``` -- [ ] **Step 4: Re-run Step 1's check, and confirm all nine survived** +- [ ] **Step 3: Verify against the captured baseline** ```bash +grep -cF 'Any profile change costs at least one further pass' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md + for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'Any profile change costs at least one further pass' -done -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f" - tr '\n' ' ' < "$f" | tr -s ' ' | grep -o 'proposes the complete resulting header\|human confirms it\|never moves it alone\|append one profile-log line\|voids every prior override\|follows the current security value\|keep counting\|final clean pass\|fold the edit into the active' | wc -l + printf 'AFTER %s: ' "$f" + awk '/\*\*Changing a profile:\*\*/,/^\*\*While a gate is running/' "$f" | grep -o \ + 'proposes the complete resulting header\|human confirms it\|never moves it alone\|append one profile-log line\|voids every prior override\|follows the current security value\|keep counting\|final clean pass\|fold the edit into the active' \ + | sort | uniq -c | tr '\n' ' '; echo done ``` -**Expected: `1`/`1` for the first; the second must report the same number for both copies and that -number must not fall below its pre-edit value — capture that value in Step 1 before editing.** +**Expected: `1`/`1`, then AFTER lines identical to Step 1's BASELINE lines.** The `awk` range now +stops at the inserted text, so it measures the original paragraph only — a token the append +introduced cannot mask a condition the edit dropped. **Any difference is a dropped condition, not +a formatting artefact.** -- [ ] **Step 5: Amend the WIP commit** +- [ ] **Step 4: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +git commit --amend -m "WIP: review-loop economics" ``` --- -## Task 6: Severity semantics (§3), and activation (§10, partial) - -**Files:** `CLAUDE.md:495`, `plugins/dev-workflow/commands/workflow-init.md:674` +## Task 5: Severity semantics (§3), and activation (§10, partial) - [ ] **Step 1: Preflight** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'what in the system consumes this text' -done +grep -cF 'what in the system consumes this text' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'a cycle already running finishes' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected at this point in the sequence: `0` and `0`.** +**Expected at this point in the sequence: `0`/`0` and `0`/`0`.** -- [ ] **Step 2: Append the test after the four definitions, both copies** - -Anchor, pasted from `grep -n`: -``` -CLAUDE.md:495:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → -plugins/dev-workflow/commands/workflow-init.md:674:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → -``` +- [ ] **Step 2: Append the test after the four Severity definitions, both copies** -The four definitions stay verbatim. Append: +Locate by content — the `- **Severity:** Blocker (wrong/unsafe/breaks invariant)` bullet. The four +definitions stay verbatim. Append: ```markdown **Deciding severity — one procedure. The subject list is illustration, not a second rule.** @@ -732,20 +654,21 @@ The four definitions stay verbatim. Append: granularities — text that *describes* the product versus text that *is* the product. ``` -- [ ] **Step 3: Append the activation block after Task 2's gate-off disclosure, both copies** +- [ ] **Step 3: Append the activation block after Task 1's gate-off disclosure, both copies** -Finding B7. **Plan A ships the two parts Plan A ships. Plan B extends this list** to the -provenance-line, curve and nonce duties — §10 says extending is safe and replacing is not, so Plan -B appends to this list rather than rewriting it. +Finding rle-B7. **Plan A ships the two parts Plan A ships. Plan B extends this list** — §10 says +extending is safe and replacing is not, so the wording below is a list Plan B appends to rather +than a closed enumeration Plan B would have to rewrite. ```markdown **When these rules bind.** From the commit that ships them, and **a cycle already running finishes under the rules it started with**. **Where a cycle's starting rules cannot be established it takes -the stricter reading of every part this change touches** — floor 3, and severity classified without -the demotion. Not a re-derivation, which could hand a level-0 cycle a floor of 1 and *skip* passes -on the strength of not knowing when it started. A user knob set above 3 is not lowered by this -fallback. **A revert is itself a shipping commit for the old rules**, and the activation rule wins -wherever the start is determinable; the fallback covers only where it is not. +the stricter reading of every part this change touches** — at minimum floor 3, and severity +classified without the demotion; **each further rule this change ships adds its own strict reading +to this list.** Not a re-derivation, which could hand a level-0 cycle a floor of 1 and *skip* +passes on the strength of not knowing when it started. A user knob set above 3 is not lowered by +this fallback. **A revert is itself a shipping commit for the old rules**, and the activation rule +wins wherever the start is determinable; the fallback covers only where it is not. **Downstream has no shipping commit.** Adoption binds from the `/workflow-init` run that *actually writes* the text — which may write nothing, be declined, or be merged in part — so **these rules @@ -755,81 +678,134 @@ persist undetected. A project taking the floor rule without the severity test ge cannot is said. ``` -- [ ] **Step 4: Re-run the checks** +> `at minimum` and `each further rule this change ships adds its own strict reading to this list` +> are what make this extensible: Plan B appends the provenance-line, curve and nonce duties without +> rewriting the sentence. Without them, a two-item list reads as closed and Plan B would have to +> replace it — which §10 says is the unsafe move. + +- [ ] **Step 4: Verify** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s: ' "$f"; tr '\n' ' ' < "$f" | tr -s ' ' | grep -c 'what in the system consumes this text' -done -grep -c 'a cycle already running finishes' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF '**Severity:** Blocker (wrong/unsafe/breaks invariant)' \ +grep -cF 'what in the system consumes this text' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'a cycle already running finishes' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'adds its own strict reading to this list' \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF '- **Severity:** Blocker (wrong/unsafe/breaks invariant)' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected: `1`/`1`, `1`/`1`, `1`/`1`.** +**Expected: `1`/`1` four times.** - [ ] **Step 5: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +git commit --amend -m "WIP: review-loop economics" ``` --- -## Task 7: Parity and the twelve-item conformance pass +## Task 6: Parity, conformance, and the diff proof -No new rules. This is the verification invariant 11 requires. Finding M7: the results have a -defined schema, so an executor cannot discharge them with an unauditable paragraph. +No new rules. This is the verification invariant 11 requires. Finding rle-M7 / plana-M7: the results +have a defined schema and an asserted row count, and the parity check compares **text**, not +occurrence counts. -- [ ] **Step 1: The parity table — one row per changed rule** +- [ ] **Step 1: Preflight** + +This task appends result tables to **this plan document**. If the tables already exist, this task +ran — verify their row counts against Steps 2-3 rather than appending again. -Columns: *rule* · *`CLAUDE.md` text* · *template text* · **status** from the closed set -`IDENTICAL` | `DELIBERATE-DIFFERENCE` | `DEFECT` · *reason, required for the latter two*. -One row for each rule added or changed by Tasks 2-6. +- [ ] **Step 2: Parity — compare the shipped text, not counts** ```bash C=CLAUDE.md; T=plugins/dev-workflow/commands/workflow-init.md -for p in 'derived from the cited story'"'"'s profile' 'reminder threshold' 'Named residual' \ - 'routes known today' 'states three things about the floor' \ - 'Any profile change costs at least one further pass' \ - 'what in the system consumes this text' 'a cycle already running finishes'; do - a=$(grep -cF "$p" "$C"); b=$(grep -cF "$p" "$T") - [ "$a" = "$b" ] && printf 'PARITY %s : %s\n' "$a" "$p" || printf 'MISMATCH %s/%s : %s\n' "$a" "$b" "$p" -done +tmp=$(mktemp -d) || exit 1 +i=0; ok=0 +while IFS='|' read -r start end; do + i=$((i+1)) + awk "/$start/,/$end/" "$C" > "$tmp/c.$i" + awk "/$start/,/$end/" "$T" > "$tmp/t.$i" + if [ ! -s "$tmp/c.$i" ] || [ ! -s "$tmp/t.$i" ]; then + printf 'EMPTY %s: %s — range matched nothing; the edit is missing or the anchor is wrong\n' "$i" "$start" + elif diff -q "$tmp/c.$i" "$tmp/t.$i" >/dev/null; then + printf 'PARITY %s: %s\n' "$i" "$start"; ok=$((ok+1)) + else + printf 'DIFFERS %s: %s\n' "$i" "$start" + diff "$tmp/c.$i" "$tmp/t.$i" + fi +done <<'RANGES' +HARD FLOOR: a minimum number|derive from the same cited-story set +The derived floor is the pass count|never read for this derivation +Named residual|not the reminder being harmless +The gate-off surface|against the cited profiles +Every pass report states three things|a different obligation +While a gate is running|as currently derived +Any profile change costs at least|recomputed from the current profile +The cited set is re-read|not the citation +Deciding severity|collect, never iterate +The exclusions are contract|the filter is ours +finding-level analog|that is the product +When these rules bind|only where it is not +Downstream has no shipping commit|what it cannot is said +RANGES ``` -**Expected: eight `PARITY 1` lines, no `MISMATCH`.** +**Expected: thirteen `PARITY` lines, numbered 1 to 13, no `DIFFERS`, and no `EMPTY`.** The loop +asserts it itself — append this immediately after the `RANGES` terminator: -A whole-section diff proves nothing here — the two copies already diverge on roughly 192 lines -overall for reasons predating this change. Walk the named rules. - -- [ ] **Step 2: The twelve-item table — one status row per item per artifact** +```bash +rm -rf "$tmp" +[ "$i" = 13 ] && [ "$ok" = 13 ] && echo "PARITY-COMPLETE 13/13" \ + || { echo "PARITY-INCOMPLETE ranges=$i parity=$ok"; exit 1; } +``` -Artifacts: the **resulting scaffolded template**, and -`plugins/dev-workflow/commands/workflow-init.md` as the outer command prompt. Columns: *item* · -*artifact* · **status** from `PASS` | `N/A` | `FAIL` · *reason, required for `N/A` and `FAIL`*. -**Item 7 is read against the whole resulting artifact**, not the diff. +**An `EMPTY` line is not a pass.** Revision 1's check could print `PARITY` for a range that matched +nothing in either copy — two absences comparing equal. `-s` catches that, and `ok` counts only +ranges that actually compared non-empty text. -**Root `CLAUDE.md` is outside invariant 11's list and is not part of this pass.** +> Revision 1's parity script compared **occurrence counts** of a marker phrase and printed `PARITY` +> whenever both were 1 — so two copies with the same marker and different surrounding text passed +> (finding plana-M7). This compares the extracted block text with `diff` and prints the difference +> when it finds one. -> **Item 1 for the scaffolded template is `N/A`, and Plan C ships the note that says why** — the -> file the template writes is model-agnostic by design, so a `Target model:` line inside it would -> be false in every repo it lands in. Plan A records the `N/A` here; Plan C writes the reader-facing -> note outside the fence, where it cannot scaffold and cannot become a second `Target model:` -> declaration. +Record each row in a table in this document: *rule* · *status* from the closed set `IDENTICAL` | +`DELIBERATE-DIFFERENCE` | `DEFECT` · *reason, required for the latter two*. **Thirteen rows, +asserted.** -- [ ] **Step 3: Prove no passage outside the ten was touched** +- [ ] **Step 3: The diff proof — both files, every hunk** ```bash -git diff --stat HEAD~1 -- CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git diff HEAD~1 -- CLAUDE.md | grep -c '^[+-][^+-]' +git diff HEAD~1 -- CLAUDE.md +git diff HEAD~1 -- plugins/dev-workflow/commands/workflow-init.md +git diff HEAD~1 --stat -- CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -Read the diff and confirm every hunk falls inside one of the ten accounted passages. **A hunk -outside them is a finding**, not something to wave through. +Read **both** diffs in full and confirm every hunk falls inside one of the eleven accounted +passages. **A hunk outside them is a finding.** + +> Revision 1 printed a stat for both files but the changed-line count for only one, and never +> displayed the template's hunks it claimed the executor must verify (finding plana-MINOR 11). No +> expected line count is stated here on purpose: the correct check is a human reading two diffs +> against an eleven-row inventory, and a number would invite substituting the number for the read. + +- [ ] **Step 4: The twelve-item conformance pass** + +Artifacts: the **resulting scaffolded template**, and +`plugins/dev-workflow/commands/workflow-init.md` as the outer command prompt. One row per item per +artifact: *item* · *artifact* · **status** from `PASS` | `N/A` | `FAIL` · *reason, required for +`N/A` and `FAIL`*. **Twenty-four rows, asserted.** **Item 7 is read against the whole resulting +artifact**, not the diff. + +**Root `CLAUDE.md` is outside invariant 11's list and is not part of this pass.** + +> **Item 1 for the scaffolded template is `N/A`; Plan C ships the note that says why** — the file +> the template writes is model-agnostic by design, so a `Target model:` line inside it would be +> false in every repo it lands in. -- [ ] **Step 4: Invariant checks still pass** +- [ ] **Step 5: Invariant checks** ```bash grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md @@ -839,26 +815,24 @@ sh scripts/check-invariants.sh && echo INVARIANTS-OK **Expected: `1`, then `INVARIANTS-OK`.** The count must be 1 — `scripts/check-invariants.sh` fails on any other, and a naive item-1 fix that adds a second declaration is exactly how that breaks. -- [ ] **Step 5: Fold the results into the WIP commit** - -Write both tables into the conditions file from Task 1, then: +- [ ] **Step 6: Fold the results into the WIP commit** ```bash -git add docs/superpowers/specs/2026-08-29-review-loop-economics-plan-a-conditions.md -git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" +git add docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +git commit --amend -m "WIP: review-loop economics" ``` -**Not a separate commit.** An ordinary commit here — even one staging only a `docs/**.md` path — -resets the cycle and strands the WIP, because the hook's reset is keyed on the commit, not on the -staged paths. +**Not a separate commit.** An ordinary commit here — even staging only a `docs/**.md` path — resets +the cycle and strands the WIP, because the reset is keyed on the commit, not on the staged paths. --- -## Task 8: Gate B, and closing the cycle +## Task 7: The battery, and the hand-off to Plan B -- [ ] **Step 1: The battery** +Plan A runs no Gate-B cycle. The single cycle covering all three plans opens here and is reviewed +and closed by Plan C. -The whole `AGENTS.md` § Commands chain, exit 0, assertion counts recorded. +- [ ] **Step 1: The battery, minus one step, for a stated reason** ```bash shellcheck --shell=sh plugins/dev-workflow/hooks/codex-gate.sh && \ @@ -870,124 +844,67 @@ shellcheck --shell=sh scripts/check-version-bump.test.sh && \ HOOK_SH=sh sh plugins/dev-workflow/hooks/codex-gate.test.sh && \ HOOK_SH=dash dash plugins/dev-workflow/hooks/codex-gate.test.sh && \ sh scripts/check-invariants.test.sh && sh scripts/check-invariants.sh && \ -sh scripts/check-version-bump.test.sh && sh scripts/check-version-bump.sh main && \ -claude plugin validate . --strict && echo BATTERY-GREEN +sh scripts/check-version-bump.test.sh && \ +claude plugin validate . --strict && echo "BATTERY-GREEN (version-bump deferred)" ``` -- [ ] **Step 2: The differential check — both revisions read** - -The mode is `battery+check+verification`; **read it from the story header, not from here.** This -step is the check that fails without the change, and it is differential by construction: a check -consulting only the post-change text cannot fail. - -One question, answered against **both** revisions: - -> *At a derived floor of 1, does a Blocker/Major-free pass 1 carrying a Minor close, or keep -> looping?* - -```bash -git show HEAD~1:CLAUDE.md | grep -n 'carrying a Minor keeps' # pre-change -grep -n 'carrying a Minor keeps' CLAUDE.md # post-change -``` - -- **Pre-change** says `pass 1 ... keeps looping` — **wrong at floor 1**, where pass 1 is the floor. -- **Post-change** says `pass **below the floor** ... keeps looping` — correct. - -Ask the same of `below 3` versus `below the floor`. **Record which revision produced which -answer.** The observation that would exist if the claim were false is a pre-change revision that -already answers correctly — and it does not. - -- [ ] **Step 3: The named risk-path verification** - -Recompute the floor this cycle owes from the cited story's profile header, and confirm the pass -reports state it with the axes and the source story. **The observation that would exist if the -claim were false is a pass report whose floor the cited profile does not license.** - -Then confirm no floor file appeared: - -```bash -test -e .context/codex-gate.floor && echo "PRESENT — was it present at start?" || echo "ABSENT" -``` - -**This detects a persisting write and cannot detect a transient one** — stated, not glossed. - -- [ ] **Step 4: The review loop** - -`mcp__codex__review` against the WIP commit, `baseSha` = its parent. Findings to -`.context/codex-reviews/gate-b--plana-pass-

.md`; **delete both branch targets and -confirm them gone before each call.** - -Floor: derived from the story profile — read the header. Carry into every call: the story path, -and the current evidence entry quoted verbatim. - -**After every accepted fix:** - -```bash -git add -u -git commit --amend -m "WIP: review-loop economics Plan A — floor predicate and severity test" -``` - -**then** re-review that new commit against the same base. Finding B18: `mcp__codex__review` reads -the committed range, so a re-review run over uncommitted fixes inspects the old snapshot and can -return a clean pass on content that is not what closes. - -- [ ] **Step 5: Build the closing body, then close** +> **`sh scripts/check-version-bump.sh main` is deliberately absent, and only that one step.** +> Plan A changes a path under `plugins/dev-workflow/` while the manifest bump is Plan C's, so the +> checker **would correctly fail here**. It is deferred to the combined close, where the bump +> exists. `AGENTS.md` already states this checker's precondition — it compares *commits*, and run +> mid-work it reports clean and uselessly. +> +> **This is a deferral of one step with a named reason, not licence to skip the rest.** Every other +> command above must pass before Plan B opens. `scripts/check-version-bump.test.sh` — the suite — +> still runs, because it does not depend on the working tree's bump state. -Finding B20: no placeholder reaches the amend. Build the body as a file first, read it back, and -only then amend. +- [ ] **Step 2: Confirm the cycle is intact before handing off** ```bash -cat > /tmp/plan-a-close.txt <<'EOF' -feat(gates): derive the pass floor from the story profile; decide severity by consequence - - - -Evidence (docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md): - battery: - check: the floor-1 Minor sentence, read against both revisions — pre-change says pass 1 - keeps looping, which is wrong at floor 1; post-change says below the floor. - verification: -EOF -cat /tmp/plan-a-close.txt # read it back — no angle-bracket placeholder may survive -grep -c '<' /tmp/plan-a-close.txt +git log --oneline -1 +git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' && echo "CYCLE OPEN" || echo "CYCLE LOST — investigate" +git status --porcelain ``` -**Expected: `0`.** A non-zero count means a placeholder is still in the body — fix it before -amending. +**Expected: the WIP subject, `CYCLE OPEN`, and a clean worktree.** A lost cycle here means an +ordinary commit slipped in; find it before Plan B adds to the damage. -```bash -git commit --amend -F /tmp/plan-a-close.txt -``` - -**This is the first message without `WIP:`**, and the hook reads it as the cycle closing. +- [ ] **Step 3: Hand off** -> Plan A's closing body carries the evidence entry. **The provenance line and the per-pass curve -> are Plan B's forms and are not owed by this commit** — they do not exist yet. Plan C's closing -> body carries them for the branch. +Plan B opens against this WIP commit and amends it with the same message. Plan A's Gate-A loop must +have closed clean before Plan B's Gate-A loop opens. --- ## Self-Review -**Spec coverage.** §2 predicate, unanimity, unresolvable-stop, one-value-three-cycles → Task 2 -Step 3. §2.1 precedence, knob, residual → Task 2 Steps 3-4. §2.2 pass report → Task 4. §2.4 -mid-cycle → Task 5. §3 severity → Task 6 Step 2. §10 activation, revert, downstream adoption, -gate-off surface → Task 2 Step 4 and Task 6 Step 3, **partial by design**: the three record duties -in §10's strict-fallback list are Plan B's to append. §2.3, §4, §5, §6 → Plan B. §7, §8 → Plan C. +**Spec coverage.** §2 predicate, unanimity, unresolvable-stop, one-value-three-cycles → Task 1 +Step 3. §2.1 precedence, knob, residual → Task 1 Steps 3-4. §2.2 pass report → Task 3. §2.4 +mid-cycle → Task 4. §3 severity → Task 5 Step 2. §10 activation, revert, downstream adoption, +gate-off surface → Task 1 Step 4 and Task 5 Step 3, **partial by design and written to be +extended**: the record duties in §10's strict-fallback list are Plan B's to append. §2.3, §4, §5, +§6 → Plan B. §7, §8 → Plan C. -**Placeholders.** None. Every anchor is pasted `grep -n` output with no editorial token inside any -pasted block; every replacement is complete text with no ellipsis; every check states its expected -value **for its point in the sequence**; the one templated artifact — the closing commit body — is -built as a file and asserted placeholder-free before it is used. +**Placeholders.** None. + +**Sequence-correct expected values — the claim, and its two known limits.** Every check states an +expected value for its point in the sequence, and every executable step addresses by content rather +than by a line number an earlier task can shift. Two places state no numeric expectation on +purpose, rather than by omission: Task 6 Step 3 asks for a human read of two diffs against an +eleven-row inventory, where a number would invite substituting the number for the read; and Task 6 +Step 1's preflight is a judgement about already-appended tables. Revision 1 made this claim +blanket and it was false in two places (finding plana-MINOR 14) — these are the two, named. **Type consistency.** `max(risk, security)`, "derived floor", "reminder threshold", "cited set" and -"level 0" are used identically in Tasks 2, 3, 4, 5 and 8, and match the spec's spellings. +"level 0" are used identically in Tasks 1, 2, 3, 4 and 5, and match the spec's spellings. + +**Gate-B classification.** Plan A opens the single cycle at Task 1 Step 6 and runs no review. Every +commit from that point is an amend restating `-m "WIP: review-loop economics"`. Plan C closes. -**Gate-B classification, per path.** Task 1 stages one `docs/superpowers/**.md` path → **N/A**, -ordinary commit, cycle not yet open. Tasks 2-7 stage prompts → **one full Gate-B cycle**, opened at -Task 2 Step 7 and closed at Task 8 Step 5, with every intermediate commit an amend restating -`-m "WIP: ..."`. No ordinary commit occurs between those two points. +**Rerun and interruption.** Every task opens with a preflight distinguishing not-started, complete, +duplicate and damaged, and **asymmetry between the two copies is its own state** with its own +answer — an execution abandoned between the two files leaves the mirrors disagreeing, and Task 1 +Step 1 says to reconcile before proceeding rather than continuing (finding plana-M3). **Known limit.** The replacement wordings are proposals, not transcriptions — the spec pins the -rules, not the sentences — and this plan's Gate A is what reviews them. Where a wording restates a -spec rule the spec pinned as *required properties*, it is quoted rather than paraphrased. +rules, not the sentences — and this plan's Gate A is what reviews them. From 3799ac9fcb6a8b755aa2e3543174e89562b1b564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 09:04:33 +0200 Subject: [PATCH 058/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=203?= =?UTF-8?q?=20=E2=80=94=20every=20check=20executed,=20not=20predicted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 2 returned 14 Blocker/Major with two of the five tells firing: findings clustering on the INSTRUMENT (five of six Blockers were broken checks, not wrong rules) and on prose about it. Root cause was one error, not fourteen: checks written against Markdown and ASSERTED to work rather than EXECUTED. Four shapes - a phrase containing **emphasis** matched as plain text; a phrase split across a line break matched as one string; a `grep -cF` pattern beginning `- ` parsed by grep as an option (exit 2); an awk range whose end anchor existed on no line. THE RULE THIS REVISION ADDS: a check's stated output is a pasted observation, never a prediction. A simulated post-edit tree was built, all thirteen edits applied to copies of both files, snapshots taken after each task, and every check run against the snapshot that task would actually see. Every expected value in the plan is what came back. Observed, and now pasted into the plan: T1 preflight (untouched) old=1 new=0 T1 verify (after T1) predicate=1 residual=1 gateoff=1 tail=1 T2 preflight(after T1) 6 <- not 7; T1 already removed one match T2 verify (after T2) regex=0 new=1 old=0 PR#23=1 T3 verify (after T3) marker=1 old-para=1 tells=1 T4 baseline/after 9 / 9 <- the nine profile conditions survive T5 verify (after T5) 1 1 1 1 T6 parity (after T5) PARITY-COMPLETE 13/13 all values identical in both copies. The plan is GENERATED from the same source the simulation executed, so its replacement texts cannot drift from what was tested. Both directions verified: every old and new text in the plan is byte-identical to what the simulation applied, and re-applying the plan's texts to the real files matches exactly once each, 13 edits across 2 copies. Other repairs: - B6: an immutable pre-cycle base SHA is recorded before Task 1 and is the reference for every diff and every Gate-B call, never HEAD~1. Every plan handoff asserts the tip is the SOLE WIP child of that base. Without it a resumed Task 1 or a second WIP stacks commits while still reporting the cycle open, and part of the combined diff escapes the only Gate-B review. - B1: the Task 1 replacement now matches the real mid-line boundary. The text on disk ends `review. Open a TodoWrite ...` on one line; revision 2 quoted through `review.` and would not have matched. - B2: Task 1's preflight is a closed state matrix over four INDEPENDENT markers, so an interruption between the floor replacement and the residual insert, or between the two mirrors, is no longer misread as complete. Cross-copy asymmetry is a named state with a named action. - M1, third attempt at one sentence: BOTH lines replaced, with the claim named rather than the phrase. Eliminated claim: "the hook causes the re-review obligation." The replacement passes the test - delete the hook and it stays true: a fix changes the artifact, so the prior review no longer covers it. - M2: the Gate-A passage gets its own split row. Over its full 30/29-line extent the copies diverge - CLAUDE.md carries a docs/prompt-standards.md citation the template drops entirely. Revision 2 called the passage byte-identical on the strength of a 13-line window that ended before the divergence. - M3: accounting row 1 no longer folds away the hook's two stated limitations. - M5: "the profile and the cited set decide together" - the cited set's emptiness, membership and unprofiled members all bear on the floor, so "the profile alone" was wrong in the other direction. - M6: the four findings that lived in the removed Gate-B section are named as Plan C's inheritance rather than marked repaired. A finding whose section moved is relocated, not fixed. - M7: parity uses one anchor per block with extraction bounded by a condition that exists, reports line counts as part of the expectation, and stops on DIFFERS or EMPTY. Both result tables have fixed headings, row counts and stop rules. - M8: a clean-worktree assertion precedes every `git add`, since staging whole files would otherwise fold an unrelated edit into the reviewed diff. - MINOR 9: the pass-report text now covers the no-story and unprofiled branches and the multi-story mapping. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 1089 +++++++++-------- 1 file changed, 582 insertions(+), 507 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index c119a52..5466586 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -8,158 +8,156 @@ pass floor becomes a function of the cited story's profile, and finding severity is decided by whether something in the system takes a different decision. -**Architecture:** Two mirrored prose edits, site by site: `CLAUDE.md` §5 and the inline template in -`/workflow-init`. **No code.** No file under `plugins/dev-workflow/hooks/` changes. +**Architecture:** Two mirrored prose edits, block by block: `CLAUDE.md` §5 and the inline template +in `/workflow-init`. **No code.** No file under `plugins/dev-workflow/hooks/` changes. **Tech Stack:** Markdown prompts; POSIX shell for every check; `git` for the records. -**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36, Gate A -closed clean at pass 34). **Read it alongside this plan.** Plan A implements §2, §2.1, §2.2, §2.4, -§3, and §10 in part. +**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). +Plan A implements §2, §2.1, §2.2, §2.4, §3, and §10 in part. **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` > **Read the profile from that header at execution time. This plan states no risk value, no -> security value, no validation mode and no pass count derived from any of them.** §5 makes the -> story header the single writable copy; a value copied here goes stale the moment the profile -> moves, and a stale floor is this change's own central failure mode. Revision 1 of this plan -> promised exactly that and then wrote the story's risk value, its validation mode, and a pass -> count derived from them into its own prose — finding B1. They are not repeated here, not even -> to name the mistake: quoting a stale-able value to explain why values go stale reintroduces it. +> security value, no validation mode and no pass count derived from any of them** — not even to +> illustrate a mistake, since quoting a stale-able value to explain why values go stale +> reintroduces it. --- -## Why this is one of three plans +## Every check below is a pasted observation, not a prediction + +**This is the rule that produced revision 3.** Revision 2 stated what its checks *would* print. +Fourteen Blocker/Major followed, five of them broken checks: a phrase containing `**emphasis**` +matched as plain text; a phrase split across a line break matched as one string; a `grep -cF` +pattern beginning `- ` parsed by grep as an option (exit 2); an `awk` range whose end anchor +existed on no line, running to EOF. + +Every expected value in this revision was produced by **executing** the check against a simulated +post-edit tree, at its point in the sequence, and pasting what came back. The simulation applies +all sixteen edits to copies of both files, snapshots after each task, and runs each check against +the snapshot that task would actually see. + +**The replacement texts below are byte-identical to the ones the simulation applied** — this +document is generated from the same source the simulation executed, so the two cannot drift. + +Result of that run, verbatim: -The single 748-line plan this replaces opened **31 Blocker/Major at Gate-A pass 1** (`5c00f8c`). -Twelve of those findings were one shape — the plan gestured at settled spec content instead of -shipping it — and the artifact was already too long while still needing to grow. Plan A revision 1 -then opened **17** (`3b94dbf`), with Blockers falling 21 → 7. +``` +T1 preflight (untouched tree) old=1 new=0 both copies +T1 verify (after T1) predicate=1 residual=1 gateoff=1 tail=1 both copies +T2 preflight(after T1) 6 both copies +T2 minor-site(after T1) 1 both copies +T2 verify (after T2) regex=0 new=1 old=0 PR#23=1 both copies +T3 preflight(after T2) 0 both copies +T3 verify (after T3) marker=1 old-para=1 tells=1 both copies +T4 baseline (after T3) 9 both copies +T4 verify (after T4) 9 both copies (identical to baseline) +T5 verify (after T5) severity=1 activation=1 extensible=1 defs=1 both copies +T6 parity (after T5) PARITY-COMPLETE 13/13, 0 problems +``` + +--- + +## Why this is one of three plans | Plan | Ships | Spec sections | |---|---|---| -| **A — this one** | the floor predicate and the severity test, site by site in both copies | §2, §2.1, §2.2, §2.4, §3, §10 (partial) | +| **A — this one** | the floor predicate and the severity test | §2, §2.1, §2.2, §2.4, §3, §10 (partial) | | **B** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | -| **C** | rollout: the falsified user-facing sentences, packaging, the evidence pack | §7, §8 | - -**Three Gate-A cycles, ONE Gate-B cycle.** Each plan is reviewed as its own artifact — that is -where the finding curve actually moved — but the three ship **one diff**, so the diff is reviewed -once, after Plan C. §5's own architecture already works this way: Gate A is per-artifact and Gate B -is per-diff. +| **C** | rollout: falsified sentences, packaging, the evidence pack, the review loop | §7, §8 | -Revision 1 tried to give Plan A its own Gate-B cycle and two Blockers fell straight out of it: the -battery could not go green because the manifest bump is Plan C's (B4), and the findings slots -needed a discriminator whose grammar is Plan B's (B5). **Each plan's own cycle needed -infrastructure a later plan ships.** Splitting the review of one diff was never coherent. +**Three Gate-A cycles, ONE Gate-B cycle.** Each plan is reviewed as its own artifact; the three +ship **one diff**, reviewed once after Plan C. Giving Plan A its own cycle produced two Blockers +that were pure infrastructure paradox — the battery could not go green because the manifest bump is +Plan C's, and the findings slots needed a grammar Plan B ships. **Execution order A → B → C. Plan B may not open before Plan A's Gate-A loop closes.** -### Where pass 1's findings went - -Every finding from `.context/codex-reviews/gate-a-plan-rle-pass-1.md` (the 748-line plan) and -`.context/codex-reviews/gate-a-plan-plana-pass-1.md` (this plan, revision 1) is assigned to exactly -one plan, so nothing falls between three documents. - -| From | Finding | Owner | Resolution | -|---|---|---|---| -| rle | B1 conditions artifact circular · B2 passage set unsound | **A** | the accounting is a *section of this plan*, derived from the edits this plan makes, reviewed by this plan's Gate-A cycle. No second artifact — see finding M2 below | -| rle | B3 pass-report unedited · B4 replacement range · B5 gate-off list · B6 §2.4 · B7 §10 · B8 sequence values | **A** | Tasks 1-5 | -| rle | B9-B13 records/nonce/slots · M6 nonce diagnostics | **B** | | -| rle | B17 · B19 · B21 · M8 · M10 · MINOR 11 | **C** | | -| rle | B14-B16, B18, B20 commit topology | **A**, restated in B and C | resolved below; one WIP, one loop after C | -| rle | M1 rows per copy · M2 profile values · M3 ellipses · M4 false rationale · M5 "unchanged" · M7 parity schema · M9 rerun safety | **A** | | -| plana | B1 profile values copied | **A** | header above; no derived pass count anywhere | -| plana | B2 eleventh passage missing | **A** | the accounting has **eleven** passages | -| plana | B3 line numbers shift under earlier tasks | **A** | **every executable step addresses by content, never by line number** | -| plana | B4 battery cannot go green · B5 illegal slot names | **A** | dissolved by the one-Gate-B-cycle topology | -| plana | B6 evidence not revalidated | **C** | the evidence pack belongs to the combined close | -| plana | B7 cycle runs under old rules | **A**, restated in B and C | Global Constraints below | -| plana | M1 pass-report passage differs · M3 preflight states · M4 baseline · M5 "profile alone" · M6 causal claim · M7 parity script · M8 recovery · M9 knob bytes · M10 `git add -u` | **A** | | -| plana | 4 MINOR | **A** | Task 6 Step 3 proof, `mktemp`, hook-state qualification, self-review claim | +### Findings inherited by Plan C — named, because they were relocated, not repaired + +Plan A revision 1 carried a Gate-B section that no longer exists here. Four pass-1 findings lived +in it, and a finding whose section moved is **relocated, not fixed** (finding plana-M6). **Plan C +must inherit these explicitly:** + +| Finding | What it requires of Plan C | +|---|---| +| pass-1 M8 | single-branch Gate-B recovery: delete only the failed branch, never both | +| pass-1 M9 | record the floor knob's existence **and bytes** before the cycle, compare after | +| pass-1 M10 | never `git add -u`; stage an explicitly inspected path set | +| pass-1 MINOR 12 | build the closing body with `mktemp`, not a fixed `/tmp` path | +| pass-2 B6 | evidence revalidated after every fix and again before the closing amend | --- ## Global Constraints -Verbatim from the spec. Every task's requirements implicitly include these. - - **This Gate-B cycle runs under the rules in force at its start — the OLD ones.** The new - severity semantics, the new floor rule and the new record forms bind only **after** the closing - commit ships them. This is spec §10's activation rule applied to the change's own review: a - reviewer applying the new Minor-or-below ceiling to the change that introduces it would - under-iterate on exactly the diff that needs the most iteration. **Carry this sentence in the - `additionalContext` of every Gate-B call** (finding plana-B7). -- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes, and - **`.context/codex-gate.floor` is never written, never removed and never read for the - derivation.** Note what this does *not* claim: the Gate-B calls and commit events in this work - will write pass counters, fingerprints and disclosure markers under `.context/` as they always - do. Only the floor knob is untouched (finding plana-MINOR 13, which read revision 1's blanket - "no hook state file is written" as false — it was). + severity semantics, floor rule and record forms bind only **after** the closing commit ships + them. This is spec §10's activation rule applied to the change's own review: a reviewer applying + the new Minor-or-below ceiling to the change that introduces it would under-iterate on exactly + the diff needing most iteration. **Carry this sentence in the `additionalContext` of every + Gate-B call.** +- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes, and the floor knob + `.context/codex-gate.floor` is never written, never removed, never read for the derivation. + **This is not a claim that nothing under `.context/` is written**: the Gate-B calls and commit + events in this work write pass counters, fingerprints and disclosure markers there as always. + Only the floor knob is untouched. - **The §5 heading must keep matching `^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`.** `codex-gate.sh:94` greps `CLAUDE.md` for it to build every reminder's citation. -- **Every rule lands in BOTH copies**, except where a difference is deliberate and stated. -- **§5's other closure rules are never restated, only referred to.** Three spec revisions were - spent on this: each summary of the triviality skip dropped a different condition. -- **Address by content, never by line number.** Line numbers appear below only as *provenance* — - pasted `grep -n` output showing where a passage stood in the untouched tree. Every **executable** - step locates its target by matching text, because Task 1 inserts two large blocks and shifts - every line number after them. Revision 1 fixed the expected *values* for their point in the - sequence and left the *addresses* at their untouched-tree positions (finding plana-B3). -- **Anchors are pasted `grep -n` output.** No editorial token ever appears inside a pasted block. +- **Every rule lands in BOTH copies.** Where the two copies already differ, the difference is + pre-existing, is named in the accounting, and is **left exactly as it is**. +- **§5's other closure rules are never restated, only referred to.** +- **Address by content, never by line number.** Line numbers appear only as pasted provenance. +- **Check patterns obey four rules**, each learned from a broken check: no `**` inside a match + pattern; no pattern spanning a line break; any pattern beginning `-` passed with `-e` or after + `--`; any range bounded by a condition that exists, never by an end anchor assumed to exist. ### The commit protocol **One WIP commit, opened here, amended by Plans B and C, reviewed once after C, closed once.** -> **The `--no-edit` trap — this is why every amend below looks verbose.** -> `plugins/dev-workflow/hooks/codex-gate.sh:763` is +> **The `--no-edit` trap.** `plugins/dev-workflow/hooks/codex-gate.sh:763` is > `is_wip_commit() { printf '%s' "$1" | grep -Eiq -- "-m[[:space:]]*['\"]?[[:space:]]*wip"; }` -> It matches **the Bash command string**, not git state and not the commit message. So -> `git commit --amend --no-edit` carries no `-m`, is **not** recognized as a WIP commit, and at -> `:886` the hook resets — **discarding the cycle's accumulated passes.** Every amend below -> therefore restates `-m "WIP: ..."` in full. **Never `--no-edit` inside the cycle.** -> -> That the shipped rules do not warn about this is a real defect; it is on the backlog. **Do not -> fix the hook here.** - -1. **No ordinary commit happens from Task 1 onward** until the combined cycle closes in Plan C. - Plan A needs none: the accounting lives in this document, which is already committed. -2. **Task 1 opens the cycle**: `git commit -m "WIP: review-loop economics"`. -3. **Tasks 2-6 amend it**, each restating that exact message. -4. **Plans B and C amend the same commit.** Plan C adds the manifest bump, runs the single Gate-B - loop, and closes with `git commit --amend -m ""`. +> It matches **the Bash command string**, not git state. `git commit --amend --no-edit` carries no +> `-m`, is not recognized, and at `:886` the hook **resets, discarding the cycle's passes.** Every +> amend restates `-m "WIP: ..."`. **Never `--no-edit` inside the cycle.** (Backlog: `todos.md`.) -**The subject is `WIP: review-loop economics` — not plan-specific**, because all three plans amend -one commit. +**The base SHA is recorded before Task 1 and is the reference for every diff and every Gate-B +call** — never `HEAD~1` (finding plana-B6). Without it, a resumed Task 1 or an accidental second +WIP stacks commits while still reporting the cycle open, and a review based on the tip's parent +silently omits the earlier WIP: part of the combined diff escapes the only Gate-B review. ---- +```bash +git rev-parse HEAD > .context/plan-a-base-sha +cat .context/plan-a-base-sha +``` -## File Structure +**At every plan handoff, the tip must be the sole WIP child of that base:** -| File | Responsibility | Gate B | -|---|---|---| -| this plan, § "Old-conditions accounting" | what the existing prose requires, per copy, dispositioned | N/A — reviewed by this plan's Gate-A cycle | -| `CLAUDE.md` §5 | the live rules | in the combined cycle, closed by Plan C | -| `plugins/dev-workflow/commands/workflow-init.md` §5 | the scaffolded mirror | in the combined cycle, closed by Plan C | +```bash +base=$(cat .context/plan-a-base-sha) +n=$(git rev-list --count "$base"..HEAD) +[ "$n" = 1 ] || { echo "STACKED: $n commits above base — collapse with git reset --soft $base, then one WIP commit"; exit 1; } +git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } +echo "CYCLE OK — single WIP on $base" +``` -**There is no separate conditions artifact.** Revision 1 wrote the accounting here *and* copied it -to a second file *and* later mutated that copy with result tables — three records able to disagree, -where spec §6 asks for one (finding plana-M2). Task 6's results append to **this document**. +1. **No ordinary commit from Task 1 onward** until the combined cycle closes in Plan C. +2. **Task 1 opens the cycle**: `git commit -m "WIP: review-loop economics"`. +3. **Tasks 2-6 amend it**, each restating that exact message. +4. **Plans B and C amend the same commit.** Plan C adds the bump, runs the single Gate-B loop with + `baseSha` = the recorded base, and closes with `git commit --amend -m ""`. --- ## Old-conditions accounting -**Derived from the edits this plan actually makes**, against §5 as it stands at HEAD — not imported -from an earlier spec revision. **Eleven passages.** Revision 1 listed ten and then edited an -eleventh in Task 3, which Task 7's out-of-inventory check would have rejected as a defect -(finding plana-B2). +**Derived from the edits this plan makes**, against §5 at HEAD. **Eleven passages.** -**Rows are per copy where the copies differ.** Ten passages are byte-identical across both copies -over their whole block, so they carry one shared row each. **The pass-report passage is not** — -verified by `diff`, not by comparing its first line — so it carries two (finding plana-M1). - -Provenance, pasted from `grep -n` — these are *addresses in the untouched tree*, not instructions: +**Two passages diverge between the copies and get a row each.** Both divergences were found by +`diff` over the passage's **full extent**, after a 13-line comparison window on the Gate-A passage +produced a false IDENTICAL — a check that ended before the divergence (finding plana-M2). ``` CLAUDE.md:72:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major @@ -191,81 +189,70 @@ plugins/dev-workflow/commands/workflow-init.md:674:- **Severity:** Blocker (wron | # | Passage | Copy | What the existing prose requires | Disposition | |---|---|---|---|---| -| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. a satisfied count is not a clean review · e. Gate A is instruction-backed · f. the hook resets at `writing-plans` · g. a TodoWrite per pass · h. fix Blocker/Major after each · i. Codex is advisory · j. validate before applying · k. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1) · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · b, d-k **kept verbatim** | -| 2 | `if pass 3 still` | both | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, `pass 3` → `the pass at the floor` (Task 2) | -| 3 | `below 3` | both | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, `below 3` → `below the floor` (Task 2) | -| 4 | pass-1 Minor sentence | both | inside the "clearly stuck" rule: below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass **below the floor**` (Task 2 Step 4). **This is the sentence that inverts at floor 1**, where pass 1 *is* the floor | -| 5a | pass-report paragraph | `CLAUDE.md` | a. from pass 4 onward, three lines · b. the carrier is your own status report · c. never the Codex reply · d. never the findings file · e. trend · f. cluster · g. require↔withdraw · **h. "you report the tells"** — second person, addressee named | a. **kept and extended** — the three lines still start at pass 4; §2.2's three fields are owed by **every** pass (Task 3) · b-h **kept verbatim, including the second person** | -| 5b | pass-report paragraph | template | a-g as above · **h′. "report the tells"** — imperative, no addressee · plus different line wrapping | same disposition; **the divergence is pre-existing and is left alone.** Task 3 inserts a new paragraph *before* this one and modifies neither copy, so neither the wording nor the wrapping difference is touched | -| 6 | incomplete-pass | both | an incomplete pass is not a review: don't act on the partial list, don't count it toward the floor, don't read "no Blocker/Major visible" as clean | **kept**, `the 3-pass floor` → `the floor` (Task 2) | -| 7 | Gate A loop | both | Gate A is two runs, each its own 3-pass loop; one broad prompt; re-run each pass over the revised artifact | **kept**, `3-pass loop` → `loop at the derived floor` (Task 2) | -| 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept**, and its **rationale corrected** — see Task 2, finding rle-M4 and plana-M6 | -| 9 | Lenses | both | lenses are different questions, not more passes; the floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are unchanged | **kept**, reworded so "unchanged" no longer claims the floor is fixed (Task 2, finding rle-M5) | -| 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. the human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules are **appended after them**, never merged in (Task 4) | -| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test is **appended** as the procedure that sets a ceiling on them (Task 5) | - -**Nothing in §5 outside these eleven passages is edited by Plan A.** Task 6 Step 3 proves it by -reading the diff, for both files. +| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · **d. the hook cannot read findings** · **e. the hook cannot tell the spec run from the plan run** · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · **d, e, f, g, h kept verbatim in the second paragraph** (revision 2's row folded d and e away, finding plana-M3) · b, i-m **kept verbatim** | +| 2 | `if pass 3 still` | both | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, `pass 3` → `the pass at the floor` | +| 3 | `below 3` | both | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, `below 3` → `below the floor` | +| 4 | pass-1 Minor sentence | both | below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass below the floor`. **The sentence that inverts at floor 1**, where pass 1 *is* the floor | +| 5a | pass-report paragraph | `CLAUDE.md` | a. from pass 4 onward, three lines · b. carrier is your own status report · c. never the Codex reply · d. never the findings file · e. trend · f. cluster · g. require↔withdraw · h. the five tells · i. any-two makes stop-and-surface **mandatory** · j. "clearly stuck" is not a precondition · **k. "you report the tells" — second person** | **untouched.** Task 3 inserts a new paragraph *before* it and modifies nothing in it, so a-k all stand | +| 5b | pass-report paragraph | template | a-j as above · **k′. "report the tells" — imperative, no addressee** · plus different wrapping | **untouched**, same reason. **The divergence is pre-existing and is left alone** | +| 6 | incomplete-pass | both | an incomplete pass is not a review: don't act on the partial list, don't count it toward the floor, don't read "no Blocker/Major visible" as clean | **kept**, `the 3-pass floor` → `the floor` | +| 7a | Gate A loop | `CLAUDE.md` | a. two runs, each its own 3-pass loop · b. one broad prompt, re-run each pass · c. don't narrow per-dimension · d. the required opening phrase · e. coverage floor not a cage · f. every finding with severity and confidence · g. **the citation `` (`docs/prompt-standards.md`, "coverage first, filter later") ``** · h. one line per finding · i. literal `NO FINDINGS` · j. settle mechanically before each read pass | **a kept**, `3-pass loop` → `loop at the derived floor`; **b-j untouched, g included** | +| 7b | Gate A loop | template | a-f, h-j as above · **g′. the citation is ABSENT** — the template says "findings silently." and stops · plus different wrapping | same edit to `a`; **the missing citation is pre-existing and is left alone.** Revision 2 called this passage byte-identical on the strength of a 13-line window; over its full 30/29-line extent it is not | +| 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept, rationale replaced** — see Task 2 (e) | +| 9 | Lenses | both | lenses are different questions, not more passes; the floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are unchanged | **kept**, reworded so "unchanged" no longer claims the floor is fixed | +| 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules are **appended after them**, never merged in. Task 4's check measures all nine, scoped to the original paragraph, before and after | +| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test is **appended** as the procedure that sets a ceiling on them | + +**Nothing in §5 outside these eleven passages is edited.** Task 6 Step 3 proves it by reading both +diffs against this inventory. --- -## Task 1: Open the cycle, and replace the floor - -**Files:** `CLAUDE.md`, `plugins/dev-workflow/commands/workflow-init.md` — floor paragraph. +## Task 1: Record the base, open the cycle, replace the floor -**Interfaces:** -- Produces: `max(risk, security)` inside the floor paragraph, which Tasks 2 and 5 refer back to. - -- [ ] **Step 1: Preflight — four states, four answers** +- [ ] **Step 1: Record the immutable base SHA** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - old=$(grep -cF 'HARD FLOOR: min 3 passes per run' "$f") - new=$(grep -cF 'derived from the cited story' "$f") - printf '%s old=%s new=%s\n' "$f" "$old" "$new" -done +mkdir -p .context && git rev-parse HEAD > .context/plan-a-base-sha +cat .context/plan-a-base-sha ``` -| old / new | state | what to do | -|---|---|---| -| `1 / 0` | **not started** | proceed to Step 2 | -| `0 / 1` | **complete** | verify against Step 3's text, skip to Step 6 | -| `1 / 1` | **duplicate insert** | a rerun appended without removing; delete the inserted block and restart | -| `0 / 0` | **damaged** | neither text present — STOP, restore from `git show HEAD:` | +This file is the reference for every later diff and every Gate-B call. It is under `.context/`, +which is gitignored, so it never enters the reviewed diff. -**Asymmetry between the copies is its own state:** if the two files report different pairs, -execution was abandoned between them. Bring the lagging copy to the leading copy's state before -proceeding — never proceed with the mirrors disagreeing (finding plana-M3). +- [ ] **Step 2: Preflight — a closed state matrix over four independent markers** -- [ ] **Step 2: Read the exact text being replaced, by content** +Revision 2 collapsed this to one marker pair, so an interruption between the floor replacement and +the residual/gate-off insert, or between the two mirrors, read as complete (finding plana-B2). ```bash for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - echo "--- $f ---" - awk '/HARD FLOOR: min 3 passes per run/,/dismissed finding → one-line why/' "$f" + printf '%-46s old=%s pred=%s res=%s gate=%s\n' "$f" \ + "$(grep -cF 'HARD FLOOR: min 3 passes per run' "$f")" \ + "$(grep -cF "derived from the cited story's profile" "$f")" \ + "$(grep -cF 'Named residual' "$f")" \ + "$(grep -cF 'routes known today, not a complete list' "$f")" done ``` -The old text, in both copies: - -``` -**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major -only), counted by the hook.** The hook counts passes but can't read findings or -tell the spec run from the plan run (it resets at `writing-plans`), so Gate A — -the spec run especially — is instruction-backed: a satisfied count is not a clean -review. Open a TodoWrite "Codex pass N" per pass; fix Blocker/Major after each. Your -final pass must be clean — if pass 3 still finds Blocker/Major, keep going until -clean or clearly stuck → then STOP and surface to the user. The only early exit -below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is -advisory — validate before applying; dismissed finding → one-line why. -``` +| old pred res gate | state | action | +|---|---|---| +| `1 0 0 0` | not started | proceed to Step 3 | +| `0 1 0 0` | Step 3 done, Step 4 not | **do Step 4 only** | +| `0 1 1 0` | Step 4 half-applied | insert the gate-off block only | +| `0 1 1 1` | complete | verify against Step 5, skip to Step 6 | +| `1 1 * *` | duplicate insert | a rerun appended without removing — delete the inserted blocks, restart | +| any `pred` or `res` or `gate` > 1 | duplicate | same | +| `0 0 0 0` | damaged | **STOP** — restore with `git show $(cat .context/plan-a-base-sha):` | -**Task 1 replaces only the first two sentences** — through `a satisfied count is not a clean -review.` The rest of the paragraph stays on disk; Task 2 edits two of its lines in place. +**Cross-copy asymmetry is its own state.** If the two rows differ, execution stopped between the +files: bring the lagging copy to the leading copy's state before proceeding. **Never proceed with +the mirrors disagreeing.** -- [ ] **Step 3: The replacement — complete text, no ellipsis** +- [ ] **Step 3: Replace the floor sentences** -Replace exactly: +The text on disk ends **mid-line** — ` Open a TodoWrite "Codex pass N" per pass;` continues the +same line after `review.` Match exactly this and no more (finding plana-B1); what follows stays: ``` **Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major @@ -275,91 +262,103 @@ the spec run especially — is instruction-backed: a satisfied count is not a cl review. ``` -with: - -```markdown -**Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run (Blocker/Major -only), derived from the cited story's profile.** `max(risk, security) == 0` gives a floor of -**1**; every resolvable profile above that, and an artifact citing **no** story, gives **3**. Two -levels, not three — `high` takes its rigor from lens sets and evidence mode, not from extra -passes. **A cited story whose profile is present but unresolvable stops and surfaces** under §5's -existing rule; it does not fall through to 3, because reading it as 3 would turn a stop condition -into a silent default. **Across a cited set the floor is 1 if and only if the set is non-empty and -every member is profiled, resolvable and at level 0** — all four conditions, since "every cited -story" is vacuously true of an empty set; no story cited, or any cited story unprofiled, gives 3. -**One derived value governs all three cycles** — the Gate-A spec loop, the Gate-A plan loop and the -Gate-B cycle. Not because they are one cycle (§5 is explicit that they are three) but because they -derive from the same cited-story set. - -**The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder threshold -that controls nothing.** The hook still counts passes, and it still can't read findings or tell the -spec run from the plan run (it resets at `writing-plans`), so Gate A — the spec run especially — is -instruction-backed: **a satisfied count is not a clean review, and a below-threshold reminder is -noted in the pass report and disregarded** where the cycle's own closure rules are satisfied. **This -replaces the pass-count number and nothing else.** Every other rule §5 states about how a cycle -closes stands as written, and none of them is restated here — a summary is where their conditions -would get dropped. **Nothing here writes `.context/codex-gate.floor`**: the knob stays the user's, -never written, never removed, never read for this derivation. +Replace with: + +``` +**Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run +(Blocker/Major only), derived from the cited story's profile.** +The derivation is max(risk, security): a value of 0 gives a floor of 1; every +resolvable profile above that, and an artifact citing no story, gives 3. Two levels, +not three — `high` takes its rigor from lens sets and evidence mode, not from extra +passes. A cited story whose profile is present but unresolvable stops and surfaces +under the existing rule; it does not fall through to 3, because reading it as 3 would +turn a stop condition into a silent default. Across a cited set the floor is 1 if and +only if the set is non-empty and every member is profiled, resolvable and at level 0 +— all four conditions, since "every cited story" is vacuously true of an empty set; +no story cited, or any cited story unprofiled, gives 3. One derived value governs all +three cycles: the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle. Not +because they are one cycle — they are three — but because they derive from the same +cited-story set. + +**The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder +threshold that controls nothing.** The hook still counts passes, and it still can't read +findings or tell the spec run from the plan run (it resets at `writing-plans`), so +Gate A — the spec run especially — is instruction-backed: a satisfied count is not a +clean review, and a below-threshold reminder is noted in the pass report and disregarded +where the cycle's own closure rules are satisfied. This replaces the pass-count number +and nothing else. Every other rule stated here about how a cycle closes stands as +written, and none of them is restated — a summary is where their conditions would get +dropped. Nothing here writes the floor knob: it stays the user's, never written, never +removed, never read for this derivation. ``` - [ ] **Step 4: Add the residual and the gate-off disclosure** -Immediately after the block above, in both copies. The list is explicitly **not** exhaustive -(finding rle-B5). +Match the floor paragraph's final line **including its trailing newline**, and re-emit it followed +by the two new blocks: + +``` +advisory — validate before applying; dismissed finding → one-line why. +``` -```markdown -**Named residual:** the hook's messages state its own threshold as an obligation, so at a floor of -1 they report a shortfall the cycle does not owe. Hook text is out of scope here by decision; what -makes that tolerable is the precedence rule above plus the hook exiting 0 on every branch, not the -reminder being harmless. +Replace with: -**The gate-off surface — routes known today, not a complete list**, since an enumeration read as -complete guarantees the routes it omits. **One route is created here**: a stated floor the cited -set does not license, which could not exist before there was a derived floor to state. -**Pre-existing and unchanged**: omitting a higher-risk cited story; minting or editing a profile to -level 0; presenting an incomplete cited set; falsifying evidence entries; silencing reminders; or -not running a pass and reporting that it ran. **A user-set floor is not the lever** — it moves what -the hook says, not what the cycle owes. **None of this is a guard**: the floor is produced by the -agent and nothing checks it against the cited profiles. ``` +advisory — validate before applying; dismissed finding → one-line why. + +**Named residual:** the hook's messages state its own threshold as an obligation, so at a +floor of 1 they report a shortfall the cycle does not owe. Hook text is out of scope here +by decision; what makes that tolerable is the precedence rule above plus the hook exiting +0 on every branch, not the reminder being harmless. -- [ ] **Step 5: Verify by content** +**The gate-off surface — routes known today, not a complete list**, since an enumeration +read as complete guarantees the routes it omits. One route is created here: a stated floor +the cited set does not license, which could not exist before there was a derived floor to +state. Pre-existing and unchanged: omitting a higher-risk cited story; minting or editing a +profile to level 0; presenting an incomplete cited set; falsifying evidence entries; +silencing reminders; or not running a pass and reporting that it ran. A user-set floor is +not the lever — it moves what the hook says, not what the cycle owes. +None of this is a guard: the floor is produced by the agent and nothing checks it against +the cited profiles. +``` + +- [ ] **Step 5: Verify — observed values, both copies** ```bash for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%s ' "$f" - printf 'predicate=%s ' "$(awk '/HARD FLOOR/,/^$/' "$f" | grep -c 'max(risk, security)')" - printf 'residual=%s ' "$(grep -cF 'Named residual' "$f")" - printf 'gateoff=%s ' "$(grep -cF 'routes known today, not a complete list' "$f")" - printf 'tail=%s\n' "$(grep -cF "don't manufacture findings to pad" "$f")" + printf '%-46s predicate=%s residual=%s gateoff=%s tail=%s\n' "$f" \ + "$(awk '/HARD FLOOR/,/never read for this derivation/' "$f" | grep -c 'max(risk, security)')" \ + "$(grep -cF 'Named residual' "$f")" \ + "$(grep -cF 'routes known today, not a complete list' "$f")" \ + "$(grep -cF "don't manufacture findings to pad" "$f")" done ``` -**Expected at this point in the sequence: `predicate=1 residual=1 gateoff=1 tail=1` for both.** - -The `awk` scoping on `predicate` is load-bearing: an unscoped `grep -c 'max(risk, security)'` -returns 1 for both files **even before any edit**, because the Profiles section already contains -the phrase. Unscoped, the check would pass before the edit and prove nothing. +**Observed in the simulation: `predicate=1 residual=1 gateoff=1 tail=1` for both copies.** -`tail=1` must hold **before and after** — it catches a replacement that swallowed its neighbours. +The `awk` scoping on `predicate` is load-bearing: unscoped, `grep -c 'max(risk, security)'` returns +1 **before any edit**, because the Profiles section already contains the phrase — the check would +pass before the edit and prove nothing. `tail=1` holds before and after, catching a replacement +that swallowed its neighbours. - [ ] **Step 6: OPEN THE CYCLE** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git status --porcelain # nothing else may be dirty git diff --cached --name-only # exactly these two paths git commit -m "WIP: review-loop economics" +base=$(cat .context/plan-a-base-sha); git rev-list --count "$base"..HEAD # must be 1 ``` -**Plans B and C amend this same commit with this same message.** No ordinary commit occurs from -here until Plan C closes the cycle. +**The `git status` line is not decoration** (finding plana-M8): `git add` stages the complete +files, so a pre-existing unrelated edit inside either one would be folded into the reviewed diff +and closed under this story. If anything else is dirty, stop and resolve it first. --- ## Task 2: The floor-wording sites -**Interfaces:** consumes Task 1's predicate; produces nothing. - - [ ] **Step 1: Preflight — the sequence-correct count** ```bash @@ -367,83 +366,132 @@ grep -cE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected at this point in the sequence: `6` and `6` — not 7.** Seven is the count in an untouched -tree; **Task 1 has already removed the `min 3 passes` match.** Recording 7 here was finding rle-B8. - -`0` and `0` means this task already ran — verify Steps 3-4 by content and skip to Step 6. +**Observed after Task 1: `6` and `6` — not 7.** Seven is the count in an untouched tree; Task 1 has +already removed the `min 3 passes` match. `0`/`0` means this task ran. -- [ ] **Step 2: This regex does not cover every site** +- [ ] **Step 2: The site this regex does not cover** ```bash -grep -c 'carrying a Minor keeps' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'carrying a Minor keeps' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +**Observed after Task 1: `1` and `1`.** Accounting passage 4 contains no digit `3`, so Step 1's +regex never matched it — **Step 1 reaching 0 does not cover it.** Step 4 asserts it separately. + +- [ ] **Step 3: Six replacements per copy** + +**a.** OLD: + +``` +final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +``` + +NEW: + +``` +final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until +``` + +**b.** OLD: + +``` +below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +``` + +NEW: + +``` +below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is ``` -**Expected: `1` and `1`.** This is accounting passage 4. It contains no digit `3`, so Step 1's -regex never matched it and **Step 1 reaching 0 does not cover it.** Step 4 asserts it separately. +**c.** OLD: -- [ ] **Step 3: Six replacements per copy — complete text, no ellipses** +``` +act on the partial list, don't count it toward the 3-pass floor, and don't read "no +``` + +NEW: + +``` +act on the partial list, don't count it toward the floor, and don't read "no +``` -Located by content. Old text, then new text. +**d.** OLD: -**a.** old: `final pass must be clean — if pass 3 still finds Blocker/Major, keep going until` -new: `final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until` +``` +- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the +``` -**b.** old: `below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is` -new: `below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is` +NEW: -**c.** old: `act on the partial list, don't count it toward the 3-pass floor, and don't read "no` -new: `act on the partial list, don't count it toward the floor, and don't read "no` +``` +- **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the +``` -**d.** old: `- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the` -new: `- **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the` +**e.** OLD: -**e.** old: ` invalidates the prior pass, which is where the 3 come from.` -new: ``` - no longer covers the artifact — which is why a fix costs another pass. The floor itself - comes from the profile, and the hook's fingerprint only decides when it reminds you. + @AGENTS.md. Re-review after every fix — a fix changes the diff and the hook + invalidates the prior pass, which is where the 3 come from. ``` -> Two findings, one sentence. rle-M4: `which is where the 3 come from` is a **causal claim the new -> design makes false** — the lower bound now comes from the profile. plana-M6: revision 1's -> replacement, `the hook invalidates the prior pass — which is why a fix costs another pass`, -> **made the advisory hook the cause of the review obligation**, which is a subtler version of the -> same error. The obligation exists because **the prior review no longer covers the changed -> artifact**; the hook merely compares a fingerprint at commit and review events. This is the -> "each correction introduced a subtler version of the same claim" pattern `AGENTS.md` records — -> caught here at round one instead of round four. +NEW: + +``` + @AGENTS.md. Re-review after every fix — a fix changes the artifact, so the prior + review no longer covers it. The hook merely notices, at commit time. +``` + +> **Three findings, one sentence — and this is the third attempt at it.** The original, +> `which is where the 3 come from`, was a causal claim the new design makes false: the lower bound +> now comes from the profile. Revision 1 replaced it with *the hook invalidates the prior pass — +> which is why a fix costs another pass*, making the advisory hook the **cause** of the obligation. +> Revision 2 replaced only the second line, leaving `a fix changes the diff and the hook` in front +> of it, so the shipped sentence read **"the hook no longer covers the artifact"** — the hook still +> the subject. `AGENTS.md` records this exact pattern taking four Gate-B rounds because each +> correction searched for the previous **phrase** rather than the **claim**. > -> **The preceding text must be read before applying this**, because the replacement changes where -> the sentence's subject comes from: -> ```bash -> grep -B2 -F 'which is where the 3 come from' CLAUDE.md -> ``` -> Apply the new text so the sentence reads grammatically from whatever precedes it. +> **Both lines are replaced**, and the claim being eliminated is named: *the hook causes the +> re-review obligation.* The test the replacement passes — **delete the hook and the sentence is +> still true**: a fix changes the artifact, so the prior review no longer covers it. What the hook +> does is notice, at commit time. + +**f.** OLD: + +``` +Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major +``` + +NEW: -**f.** old: `Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major` -new: ``` -Lenses are **different questions, not more passes** — they change what a pass asks, never how many -passes a cycle owes, which the profile and the cited set decide together. The floor, the Blocker/Major +Lenses are **different questions, not more passes** — they change what a pass asks, +never how many passes a cycle owes, which the profile and the cited set decide together. +The floor, the Blocker/Major ``` -> rle-M5: the old sentence continues "… is unchanged", which in the change that makes the floor -> profile-dependent tells readers the opposite of what shipped. plana-M5: revision 1 wrote "which -> the profile alone decides" — **also wrong**, because cited-set emptiness, membership, unprofiled -> members and unresolvable members all bear on the result. "The profile and the cited set decide -> together" is what Task 1's predicate actually says. +> Two findings here too. The original tail reads "… is unchanged", which in the very change that makes the floor profile-dependent tells readers the opposite of what shipped. Revision 2 wrote "which the profile alone decides" — **also wrong**, because cited-set emptiness, membership, unprofiled members and unresolvable members all bear on the result. "The profile and the cited set decide together" is what Task 1's predicate actually says. -- [ ] **Step 4: The pass-1 Minor sentence, asserted separately** -old: `the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps` -new: `the only exception, exactly as above; a Blocker/Major-free pass **below the floor** carrying a Minor keeps` +- [ ] **Step 4: The pass-1 Minor sentence** -- [ ] **Step 5: Verify** +``` +the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps +``` + +Replace with: + +``` +the only exception, exactly as above; a Blocker/Major-free pass below the floor +carrying a Minor keeps +``` + +- [ ] **Step 5: Verify — observed values** ```bash grep -cE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'a Blocker/Major-free pass **below the floor** carrying a Minor keeps' \ +grep -cF 'a Blocker/Major-free pass below the floor' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md grep -cF 'Blocker/Major-free pass 1 carrying a Minor' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md @@ -451,70 +499,69 @@ grep -cF "PR #23's Gate-B pass 3 returned all four findings" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected: `0`/`0`, then `1`/`1`, then `0`/`0`, then `1`/`1`.** - -The last one must stay `1`: it cites an actual pass, not a rule, and changing it would falsify a -record. +**Observed after Task 2: `0`/`0`, then `1`/`1`, then `0`/`0`, then `1`/`1`.** The last must stay +`1`: it cites an actual pass, not a rule, and changing it would falsify a record. - [ ] **Step 6: Amend the WIP commit** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md git commit --amend -m "WIP: review-loop economics" +base=$(cat .context/plan-a-base-sha); git rev-list --count "$base"..HEAD # must still be 1 ``` -**Never `--no-edit`.** See the commit protocol. - --- ## Task 3: The pass report (§2.2) -Finding rle-B3: revision 1 of the *748-line* plan added every-pass fields to the floor paragraph -while leaving the pass-report paragraph prescribing only the pass-4 three lines, so the shipped -prompt would have described the report shape in two places that disagreed. - - [ ] **Step 1: Preflight** ```bash -grep -cF 'the cited stories they were read from' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'the cited stories they were read' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected at this point in the sequence: `0` and `0`.** `1`/`1` means done. +**Observed after Task 2: `0` and `0`.** The pattern deliberately stops before `from`, which begins +a new line in the inserted text — a pattern spanning a line break matches nothing. -- [ ] **Step 2: Insert BEFORE the pass-report paragraph, in both copies** +- [ ] **Step 2: Insert before the pass-report paragraph** -Locate by content — the paragraph beginning `**From pass 4 onward every pass report carries three -lines.**`. **Insert before it; modify nothing in it.** +Match the paragraph's opening line and re-emit it after the new text. **Nothing in the existing +paragraph is modified** — including the `you report the tells` / `report the tells` divergence +between the copies, which is pre-existing and stays (accounting rows 5a/5b). Do not harmonize it. -```markdown -**Every pass report states three things about the floor**, from pass 1 onward: the **derived -floor**, the **risk and security values read**, and the **cited stories they were read from**. A -report giving the number alone leaves a reader unable to check the derivation while passes are -still being spent — which is the only time checking it is cheap. This is owed by every pass; the -three lines below are owed from pass 4 and are a different obligation. +``` +**From pass 4 onward every pass report carries three lines.** ``` -> **The two copies of the following paragraph differ** — wrapping, and `you report the tells` in -> `CLAUDE.md` against `report the tells` in the template (accounting rows 5a/5b). **That divergence -> is pre-existing and stays.** This step inserts a new paragraph and touches neither copy of the -> old one, so it neither fixes nor worsens the difference. Do not "harmonize" them here: that would -> be an edit outside the accounted dispositions. +Replace with: -- [ ] **Step 3: Verify, including that the old paragraph is untouched** +``` +**Every pass report states three things about the floor**, from pass 1 onward: the +derived floor, the risk and security values read, and the cited stories they were read +from. A report giving the number alone leaves a reader unable to check the derivation +while passes are still being spent — which is the only time checking it is cheap. Where +no story is cited, or a cited story is unprofiled, the report says so in place of axis +values; a multi-story set names each story and its values. This is owed by every pass; +the three lines below are owed from pass 4 and are a different obligation. + +**From pass 4 onward every pass report carries three lines.** +``` + +- [ ] **Step 3: Verify — observed values** ```bash -grep -cF 'the cited stories they were read from' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'the cited stories they were read' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md grep -cF 'From pass 4 onward every pass report carries three lines' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md grep -cF 'require↔withdraw pair' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'you report the tells' CLAUDE.md -grep -cF 'report the tells' plugins/dev-workflow/commands/workflow-init.md +printf 'C:%s T:%s\n' "$(grep -cF -- '— you report' CLAUDE.md)" \ + "$(grep -cF -- '— report the' plugins/dev-workflow/commands/workflow-init.md)" ``` -**Expected: `1`/`1`, `1`/`1`, `1`/`1`, then `1`, then `1`.** The last two assert the pre-existing -divergence is still exactly as it was. +**Observed after Task 3: `1`/`1`, `1`/`1`, `1`/`1`, then `C:1 T:1`.** The last asserts the +pre-existing divergence is still exactly as it was — note `--` before a pattern starting with `—` +is unnecessary but `-- ` is used consistently for patterns whose first character could be read as +an option. - [ ] **Step 4: Amend the WIP commit** @@ -527,80 +574,76 @@ git commit --amend -m "WIP: review-loop economics" ## Task 4: A profile or cited set that moves mid-cycle (§2.4) -Finding rle-B6. §2.4 composes three existing rules and adds no new one, but none of the composition -is currently written down, so a mid-cycle profile or set edit can silently reuse an old clean pass. - -- [ ] **Step 1: Preflight, and capture the baseline the verification needs** +- [ ] **Step 1: Preflight, and capture the nine-condition baseline** ```bash grep -cF 'Any profile change costs at least one further pass' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf 'BASELINE %s: ' "$f" - awk '/\*\*Changing a profile:\*\*/,/^$/' "$f" | grep -o \ - 'proposes the complete resulting header\|human confirms it\|never moves it alone\|append one profile-log line\|voids every prior override\|follows the current security value\|keep counting\|final clean pass\|fold the edit into the active' \ - | sort | uniq -c | tr '\n' ' '; echo + printf 'BASELINE %-46s ' "$f" + awk '/\*\*Changing a profile:\*\*/,/discard the accumulated passes\./' "$f" \ + | grep -oF -e 'proposes the complete resulting header' -e 'human confirms it' \ + -e 'never moves it alone' -e 'one profile-log line' -e 'voids every prior override' \ + -e 'follows the current security value' -e 'keep counting' -e 'final clean pass' \ + -e 'fold the edit into the active' | wc -l | tr -d ' ' done ``` -**Expected: `0` and `0` for the first.** **Record the BASELINE lines verbatim** — Step 3 compares -against them. +**Observed after Task 3: `0`/`0`, then `BASELINE 9` for both copies.** Nine markers for the nine +conditions of accounting row 10. Two things make this measure what it claims: the `awk` range ends +at `discard the accumulated passes.`, **the original paragraph's last line**, so the appended text +is outside it; and `grep -oF -e ...` passes each phrase as a separate fixed string, so +`one profile-log line` matches although the source wraps `append` onto the previous line. -> Finding plana-M4: revision 1 told the executor to compare against a baseline it never captured, -> and its post-edit check counted tokens across the **whole file**, where the appended text itself -> adds `keep counting` and `final clean pass` — so the count could rise while an old condition was -> dropped. This baseline is **scoped to the `Changing a profile:` paragraph** and captured **before** -> the append, and Step 3 re-scopes the same way. +- [ ] **Step 2: Append after the `Changing a profile:` paragraph** -- [ ] **Step 2: Append after the `Changing a profile:` paragraph, both copies** +Its nine conditions are kept verbatim. §2.4's rules are **appended after them**, never merged in. -Its nine conditions are accounting row 10 (a-i). **They are kept verbatim.** §2.4's rules are -**appended after them**, never merged into them — merging is how a rewrite drops a condition, which -`AGENTS.md` names as this repo's tenth recorded instance. +``` +discard the accumulated passes. -```markdown -**While a gate is running, the floor derives from the *current* profile at each pass.** Passes -already run keep counting; closing requires the floor **as currently derived**. These are -pass-count rules, so they apply while §5 says a gate is running and are silent otherwise — what §5 -says about *when* a gate runs is §5's, unchanged and deliberately not summarised here. +**What this does not do:** +``` -**Any profile change costs at least one further pass**, in either direction and **whether or not -the floor number moves**, because the final clean pass must run under the current profile — so no -already-banked pass can be it. That further pass must itself be clean and every other closure duty -must be satisfied; it is one more pass, not a licence to close on the next one. **What a lowering -drops is whatever the changed values drop, not a fixed pair**: a mode-only override changes the -evidence obligations while leaving the axis-derived lens sets alone, and security `high` → -`standard` keeps the security lens set while changing what evidence is owed. **Every derived -obligation is recomputed from the current profile.** +Replace with: -**The cited set is re-read at each pass, and the final clean pass runs against the current set — -whenever its membership changes, not only when the floor number moves.** Adding a high-risk story -to a set already at floor 3 leaves the number alone while adding that story's lens set, its -evidence obligations and its review scope; a pass run before it joined did not cover them. -**Removing a story** recomputes obligations from the current set and so does remove *that story's* -lenses and evidence duty — but it **never discharges an accepted in-set Blocker or Major**: the -acceptance put that finding in the fix set, not the citation. ``` +discard the accumulated passes. -- [ ] **Step 3: Verify against the captured baseline** +**While a gate is running, the floor derives from the current profile at each pass.** +Passes already run keep counting; closing requires the floor as currently derived. These +are pass-count rules, so they apply while a gate is running and are silent otherwise — +what governs when a gate runs is unchanged and deliberately not summarised here. -```bash -grep -cF 'Any profile change costs at least one further pass' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +**Any profile change costs at least one further pass**, in either direction and whether or +not the floor number moves, because the final clean pass must run under the current +profile — so no already-banked pass can be it. That further pass must itself be clean and +every other closure duty must be satisfied; it is one more pass, not a licence to close on +the next one. What a lowering drops is whatever the changed values drop, not a fixed pair: +a mode-only override changes the evidence obligations while leaving the axis-derived lens +sets alone, and security `high` → `standard` keeps the security lens set while changing +what evidence is owed. Every derived obligation is recomputed from the current profile. -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf 'AFTER %s: ' "$f" - awk '/\*\*Changing a profile:\*\*/,/^\*\*While a gate is running/' "$f" | grep -o \ - 'proposes the complete resulting header\|human confirms it\|never moves it alone\|append one profile-log line\|voids every prior override\|follows the current security value\|keep counting\|final clean pass\|fold the edit into the active' \ - | sort | uniq -c | tr '\n' ' '; echo -done +**The cited set is re-read at each pass, and the final clean pass runs against the current +set** — whenever its membership changes, not only when the floor number moves. Adding a +high-risk story to a set already at floor 3 leaves the number alone while adding that +story's lens set, its evidence obligations and its review scope; a pass run before it +joined did not cover them. Removing a story recomputes obligations from the current set +and so does remove that story's lenses and evidence duty — but it never discharges an +accepted in-set Blocker or Major: the acceptance put that finding in the fix set, not the +citation. + +**What this does not do:** ``` -**Expected: `1`/`1`, then AFTER lines identical to Step 1's BASELINE lines.** The `awk` range now -stops at the inserted text, so it measures the original paragraph only — a token the append -introduced cannot mask a condition the edit dropped. **Any difference is a dropped condition, not -a formatting artefact.** +- [ ] **Step 3: Verify against the captured baseline** + +Re-run **the identical command from Step 1**, changing only the label. The `awk` range still ends +at the original paragraph's last line, so it measures the original paragraph alone. + +**Observed after Task 4: `1`/`1` for the new marker, and `9` for both copies — identical to the +baseline.** Any difference is a dropped condition, not a formatting artefact. - [ ] **Step 4: Amend the WIP commit** @@ -618,85 +661,98 @@ git commit --amend -m "WIP: review-loop economics" ```bash grep -cF 'what in the system consumes this text' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'a cycle already running finishes' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'a cycle already running' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected at this point in the sequence: `0`/`0` and `0`/`0`.** +**Observed after Task 4: `0`/`0` and `0`/`0`.** -- [ ] **Step 2: Append the test after the four Severity definitions, both copies** +- [ ] **Step 2: Append the severity test to the Severity bullet** -Locate by content — the `- **Severity:** Blocker (wrong/unsafe/breaks invariant)` bullet. The four -definitions stay verbatim. Append: +The four definitions stay verbatim. -```markdown - **Deciding severity — one procedure. The subject list is illustration, not a second rule.** - Name **what in the system consumes this text** — whatever *acts* on it — and the decision that - act takes differently if the text is wrong. **Both are required. If you cannot name both**, the - finding is **Minor or below**: collect, never iterate. +``` + rework) → both must resolve. Minor · Nit → collect, never iterate. +``` - The exclusions are contract, not commentary. **The reader must consume the text in the system's - *operation*, not in reviewing it** — the review pass raising the finding is not an in-system - reader of the text it reviews; without this the test demotes nothing. **Gates remain legitimate - readers** of rule text they will later apply. **A human reader never satisfies the test** — §5's - prose exemption already prices that cost as non-gating. **The list of reader kinds is - illustrative, not closed**, because this ships into projects whose readers we have never seen. - **The test sets a ceiling, not a floor, and never chooses between Blocker and Major** — the four - definitions above still decide that. **The instrument carve-out is symmetric**: an instrument - finding keeps its severity whenever it shows the instrument changes what a gate concludes about - product behaviour — a false green, and equally a false red or a check blocking a valid change. - **Rationale prose is Minor only when no rule's application depends on it**, not categorically: - `docs/prompt-standards.md` requires rules to carry their why, so rationale a reader must consult - to apply a rule passes the test. **This removes arbitrariness, not judgement.** Coverage-first is - unchanged — the reviewer reports every finding with severity and confidence; the filter is ours. +Replace with: - This is the **finding-level analog of the path-level prose exemption**: one principle at two +``` + rework) → both must resolve. Minor · Nit → collect, never iterate. + + **Deciding severity — one procedure. The subject list is illustration, not a second + rule.** Name what in the system consumes this text — whatever *acts* on it — and the + decision that act takes differently if the text is wrong. Both are required. If you + cannot name both, the finding is Minor or below: collect, never iterate. + + The exclusions are contract, not commentary. The reader must consume the text in the + system's *operation*, not in reviewing it — the review pass raising the finding is not + an in-system reader of the text it reviews; without this the test demotes nothing. + Gates remain legitimate readers of rule text they will later apply. A human reader never + satisfies the test — the prose exemption already prices that cost as non-gating. The + list of reader kinds is illustrative, not closed, because this ships into projects whose + readers we have never seen. The test sets a ceiling, not a floor, and never chooses + between Blocker and Major — the four definitions above still decide that. The instrument + carve-out is symmetric: an instrument finding keeps its severity whenever it shows the + instrument changes what a gate concludes about product behaviour — a false green, and + equally a false red or a check blocking a valid change. Rationale prose is Minor only + when no rule's application depends on it, not categorically: `docs/prompt-standards.md` + requires rules to carry their why, so rationale a reader must consult to apply a rule + passes the test. This removes arbitrariness, not judgement. Coverage-first is unchanged + — the reviewer reports every finding with severity and confidence; the filter is ours. + + This is the finding-level analog of the path-level prose exemption: one principle at two granularities — text that *describes* the product versus text that *is* the product. ``` -- [ ] **Step 3: Append the activation block after Task 1's gate-off disclosure, both copies** +- [ ] **Step 3: Append the activation block after the gate-off disclosure** -Finding rle-B7. **Plan A ships the two parts Plan A ships. Plan B extends this list** — §10 says -extending is safe and replacing is not, so the wording below is a list Plan B appends to rather -than a closed enumeration Plan B would have to rewrite. +**Plan B extends this list rather than rewriting it** — §10 says extending is safe and replacing is +not. `at minimum` and `each further rule this change ships adds its own strict reading to this +list` are what make that possible. -```markdown -**When these rules bind.** From the commit that ships them, and **a cycle already running finishes -under the rules it started with**. **Where a cycle's starting rules cannot be established it takes -the stricter reading of every part this change touches** — at minimum floor 3, and severity -classified without the demotion; **each further rule this change ships adds its own strict reading -to this list.** Not a re-derivation, which could hand a level-0 cycle a floor of 1 and *skip* -passes on the strength of not knowing when it started. A user knob set above 3 is not lowered by -this fallback. **A revert is itself a shipping commit for the old rules**, and the activation rule -wins wherever the start is determinable; the fallback covers only where it is not. +``` +None of this is a guard: the floor is produced by the agent and nothing checks it against +the cited profiles. +``` + +Replace with: -**Downstream has no shipping commit.** Adoption binds from the `/workflow-init` run that *actually -writes* the text — which may write nothing, be declined, or be merged in part — so **these rules -bind only over the text a project's `CLAUDE.md` actually contains**, and a partial adoption can -persist undetected. A project taking the floor rule without the severity test gets a floor whose -`docs-only` question the severity test is what settles. What prompt text can do is done; what it -cannot is said. ``` +None of this is a guard: the floor is produced by the agent and nothing checks it against +the cited profiles. -> `at minimum` and `each further rule this change ships adds its own strict reading to this list` -> are what make this extensible: Plan B appends the provenance-line, curve and nonce duties without -> rewriting the sentence. Without them, a two-item list reads as closed and Plan B would have to -> replace it — which §10 says is the unsafe move. +**When these rules bind.** From the commit that ships them, and a cycle already running +finishes under the rules it started with. Where a cycle's starting rules cannot be +established it takes the stricter reading of every part this change touches — at minimum +floor 3, and severity classified without the demotion; each further rule this change ships +adds its own strict reading to this list. Not a re-derivation, which could hand a level-0 +cycle a floor of 1 and skip passes on the strength of not knowing when it started. A user +knob set above 3 is not lowered by this fallback. A revert is itself a shipping commit for +the old rules, and the activation rule wins wherever the start is determinable; the +fallback covers only where it is not. -- [ ] **Step 4: Verify** +**Downstream has no shipping commit.** Adoption binds from the `/workflow-init` run that +actually writes the text — which may write nothing, be declined, or be merged in part — +so these rules bind only over the text a project's `CLAUDE.md` actually contains, and a +partial adoption can persist undetected. A project taking the floor rule without the +severity test gets a floor whose docs-only question the severity test is what settles. +What prompt text can do is done; what it cannot is said. +``` + +- [ ] **Step 4: Verify — observed values** ```bash grep -cF 'what in the system consumes this text' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'a cycle already running finishes' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'adds its own strict reading to this list' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF '- **Severity:** Blocker (wrong/unsafe/breaks invariant)' \ +grep -cF 'a cycle already running' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF 'adds its own strict reading' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +grep -cF -- '- **Severity:** Blocker (wrong/unsafe/breaks invariant)' \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Expected: `1`/`1` four times.** +**Observed after Task 5: `1`/`1` four times.** Two of these patterns were broken in revision 2: +`adds its own strict reading to this list` spanned a line break, and the Severity pattern begins +`- `, which grep parses as an option and exits 2 — hence `--` before it. - [ ] **Step 5: Amend the WIP commit** @@ -709,95 +765,118 @@ git commit --amend -m "WIP: review-loop economics" ## Task 6: Parity, conformance, and the diff proof -No new rules. This is the verification invariant 11 requires. Finding rle-M7 / plana-M7: the results -have a defined schema and an asserted row count, and the parity check compares **text**, not -occurrence counts. - - [ ] **Step 1: Preflight** -This task appends result tables to **this plan document**. If the tables already exist, this task -ran — verify their row counts against Steps 2-3 rather than appending again. +This task appends two result tables to **this plan document**. If a table with the marker +`PARITY RESULTS — Plan A` or `CONFORMANCE RESULTS — Plan A` already exists, this task ran: verify +its row count rather than appending again. + +```bash +grep -c 'PARITY RESULTS — Plan A\|CONFORMANCE RESULTS — Plan A' \ + docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +``` + +**Expected `0` before this task, `2` after.** - [ ] **Step 2: Parity — compare the shipped text, not counts** +One anchor per block, extraction bounded by a condition that **exists** (blank line or the next +`- **` bullet) rather than by an end anchor assumed to exist. Revision 2's two-anchor ranges +produced 10 of 13 rows because three end anchors occurred on no line (finding plana-B5). + ```bash C=CLAUDE.md; T=plugins/dev-workflow/commands/workflow-init.md tmp=$(mktemp -d) || exit 1 -i=0; ok=0 -while IFS='|' read -r start end; do +i=0; ok=0; bad=0 +while IFS= read -r start; do + [ -z "$start" ] && continue i=$((i+1)) - awk "/$start/,/$end/" "$C" > "$tmp/c.$i" - awk "/$start/,/$end/" "$T" > "$tmp/t.$i" - if [ ! -s "$tmp/c.$i" ] || [ ! -s "$tmp/t.$i" ]; then - printf 'EMPTY %s: %s — range matched nothing; the edit is missing or the anchor is wrong\n' "$i" "$start" - elif diff -q "$tmp/c.$i" "$tmp/t.$i" >/dev/null; then - printf 'PARITY %s: %s\n' "$i" "$start"; ok=$((ok+1)) + for pair in "C:$C" "T:$T"; do + tag=${pair%%:*}; f=${pair#*:} + awk -v s="$start" ' + index($0,s) && !f {f=1; print; next} + f && ($0=="" || $0 ~ /^- \*\*/) {exit} + f {print}' "$f" > "$tmp/$tag.$i" + done + cs=$(wc -l < "$tmp/C.$i"); ts=$(wc -l < "$tmp/T.$i") + if [ "$cs" -eq 0 ] || [ "$ts" -eq 0 ]; then + printf 'EMPTY %2s %s\n' "$i" "$start"; bad=$((bad+1)) + elif diff -q "$tmp/C.$i" "$tmp/T.$i" >/dev/null; then + printf 'PARITY %2s %2s lines %s\n' "$i" "$cs" "$start"; ok=$((ok+1)) else - printf 'DIFFERS %s: %s\n' "$i" "$start" - diff "$tmp/c.$i" "$tmp/t.$i" + printf 'DIFFERS %2s %s\n' "$i" "$start"; bad=$((bad+1)); diff "$tmp/C.$i" "$tmp/T.$i" fi -done <<'RANGES' -HARD FLOOR: a minimum number|derive from the same cited-story set -The derived floor is the pass count|never read for this derivation -Named residual|not the reminder being harmless -The gate-off surface|against the cited profiles -Every pass report states three things|a different obligation -While a gate is running|as currently derived -Any profile change costs at least|recomputed from the current profile -The cited set is re-read|not the citation -Deciding severity|collect, never iterate -The exclusions are contract|the filter is ours -finding-level analog|that is the product -When these rules bind|only where it is not -Downstream has no shipping commit|what it cannot is said -RANGES -``` - -**Expected: thirteen `PARITY` lines, numbered 1 to 13, no `DIFFERS`, and no `EMPTY`.** The loop -asserts it itself — append this immediately after the `RANGES` terminator: - -```bash +done <<'ANCHORS' +**Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run +**The derived floor is the pass count a cycle owes +**Named residual:** +**The gate-off surface +**When these rules bind.** +**Downstream has no shipping commit.** +**Every pass report states three things about the floor** +**While a gate is running, the floor derives from the current profile +**Any profile change costs at least one further pass** +**The cited set is re-read at each pass +**Deciding severity +The exclusions are contract, not commentary. +This is the finding-level analog +ANCHORS rm -rf "$tmp" [ "$i" = 13 ] && [ "$ok" = 13 ] && echo "PARITY-COMPLETE 13/13" \ - || { echo "PARITY-INCOMPLETE ranges=$i parity=$ok"; exit 1; } + || { echo "PARITY-INCOMPLETE ranges=$i parity=$ok problems=$bad"; exit 1; } ``` -**An `EMPTY` line is not a pass.** Revision 1's check could print `PARITY` for a range that matched -nothing in either copy — two absences comparing equal. `-s` catches that, and `ok` counts only -ranges that actually compared non-empty text. +**Observed on the simulated post-edit tree:** -> Revision 1's parity script compared **occurrence counts** of a marker phrase and printed `PARITY` -> whenever both were 1 — so two copies with the same marker and different surrounding text passed -> (finding plana-M7). This compares the extracted block text with `diff` and prints the difference -> when it finds one. +``` +PARITY 1 14 lines **Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run +PARITY 2 14 lines **The derived floor is the pass count a cycle owes +PARITY 3 4 lines **Named residual:** +PARITY 4 9 lines **The gate-off surface +PARITY 5 9 lines **When these rules bind.** +PARITY 6 6 lines **Downstream has no shipping commit.** +PARITY 7 7 lines **Every pass report states three things about the floor** +PARITY 8 4 lines **While a gate is running, the floor derives from the current profile +PARITY 9 8 lines **Any profile change costs at least one further pass** +PARITY 10 8 lines **The cited set is re-read at each pass +PARITY 11 4 lines **Deciding severity +PARITY 12 15 lines The exclusions are contract, not commentary. +PARITY 13 2 lines This is the finding-level analog +PARITY-COMPLETE 13/13 +``` -Record each row in a table in this document: *rule* · *status* from the closed set `IDENTICAL` | -`DELIBERATE-DIFFERENCE` | `DEFECT` · *reason, required for the latter two*. **Thirteen rows, -asserted.** +**The line counts are part of the expectation.** An `EMPTY` cannot be masked, and a block that +suddenly grows means the extractor ran past its intended end — which is exactly what happened +before the `^- \*\*` bound was added: range 13 captured 24 lines of the Mechanics list. -- [ ] **Step 3: The diff proof — both files, every hunk** +Record the result as a table headed **`PARITY RESULTS — Plan A`**: *n* · *anchor* · *lines* · +**status** from `PARITY` | `DIFFERS` | `EMPTY` · *reason, required for the latter two*. **Thirteen +rows.** **A `DIFFERS` or `EMPTY` row stops the task** — it is a parity defect, not a note. + +- [ ] **Step 3: The diff proof — both files, every hunk, against the recorded base** ```bash -git diff HEAD~1 -- CLAUDE.md -git diff HEAD~1 -- plugins/dev-workflow/commands/workflow-init.md -git diff HEAD~1 --stat -- CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +base=$(cat .context/plan-a-base-sha) +git diff "$base" -- CLAUDE.md +git diff "$base" -- plugins/dev-workflow/commands/workflow-init.md +git diff "$base" --stat -- CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` Read **both** diffs in full and confirm every hunk falls inside one of the eleven accounted passages. **A hunk outside them is a finding.** -> Revision 1 printed a stat for both files but the changed-line count for only one, and never -> displayed the template's hunks it claimed the executor must verify (finding plana-MINOR 11). No -> expected line count is stated here on purpose: the correct check is a human reading two diffs -> against an eleven-row inventory, and a number would invite substituting the number for the read. +No expected line count is stated, deliberately: the correct check is a human reading two diffs +against an eleven-row inventory, and a number invites substituting the number for the read. This is +one of exactly two places in this plan without a numeric expectation; the other is Step 1's +judgement about already-appended tables. - [ ] **Step 4: The twelve-item conformance pass** -Artifacts: the **resulting scaffolded template**, and -`plugins/dev-workflow/commands/workflow-init.md` as the outer command prompt. One row per item per -artifact: *item* · *artifact* · **status** from `PASS` | `N/A` | `FAIL` · *reason, required for -`N/A` and `FAIL`*. **Twenty-four rows, asserted.** **Item 7 is read against the whole resulting -artifact**, not the diff. +Artifacts: the **resulting scaffolded template**, and `plugins/dev-workflow/commands/workflow-init.md` +as the outer command prompt. One row per item per artifact: *item* · *artifact* · **status** from +`PASS` | `N/A` | `FAIL` · *reason, required for `N/A` and `FAIL`*. **Twenty-four rows**, headed +**`CONFORMANCE RESULTS — Plan A`**. **Item 7 is read against the whole resulting artifact.** +**A `FAIL` row stops the task.** **Root `CLAUDE.md` is outside invariant 11's list and is not part of this pass.** @@ -818,13 +897,11 @@ on any other, and a naive item-1 fix that adds a second declaration is exactly h - [ ] **Step 6: Fold the results into the WIP commit** ```bash +git status --porcelain # only the plan document may be dirty git add docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md git commit --amend -m "WIP: review-loop economics" ``` -**Not a separate commit.** An ordinary commit here — even staging only a `docs/**.md` path — resets -the cycle and strands the WIP, because the reset is keyed on the commit, not on the staged paths. - --- ## Task 7: The battery, and the hand-off to Plan B @@ -848,63 +925,61 @@ sh scripts/check-version-bump.test.sh && \ claude plugin validate . --strict && echo "BATTERY-GREEN (version-bump deferred)" ``` -> **`sh scripts/check-version-bump.sh main` is deliberately absent, and only that one step.** -> Plan A changes a path under `plugins/dev-workflow/` while the manifest bump is Plan C's, so the +> **`sh scripts/check-version-bump.sh main` is deliberately absent, and only that one step.** Plan +> A changes a path under `plugins/dev-workflow/` while the manifest bump is Plan C's, so the > checker **would correctly fail here**. It is deferred to the combined close, where the bump -> exists. `AGENTS.md` already states this checker's precondition — it compares *commits*, and run +> exists. `AGENTS.md` already states this checker's precondition: it compares *commits*, and run > mid-work it reports clean and uselessly. > > **This is a deferral of one step with a named reason, not licence to skip the rest.** Every other -> command above must pass before Plan B opens. `scripts/check-version-bump.test.sh` — the suite — -> still runs, because it does not depend on the working tree's bump state. +> command must pass before Plan B opens. `scripts/check-version-bump.test.sh` — the suite — still +> runs, because it does not depend on the working tree's bump state. - [ ] **Step 2: Confirm the cycle is intact before handing off** ```bash -git log --oneline -1 -git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' && echo "CYCLE OPEN" || echo "CYCLE LOST — investigate" +base=$(cat .context/plan-a-base-sha) +n=$(git rev-list --count "$base"..HEAD) +[ "$n" = 1 ] || { echo "STACKED: $n commits above base — collapse with git reset --soft $base, then one WIP commit"; exit 1; } +git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } git status --porcelain +echo "CYCLE OK — single WIP on $base" ``` -**Expected: the WIP subject, `CYCLE OPEN`, and a clean worktree.** A lost cycle here means an -ordinary commit slipped in; find it before Plan B adds to the damage. +**Expected: `CYCLE OK`, a clean worktree, and `n` exactly 1.** The count check is what catches a +stacked WIP that a subject-only check would pass while part of the diff escapes review. - [ ] **Step 3: Hand off** -Plan B opens against this WIP commit and amends it with the same message. Plan A's Gate-A loop must -have closed clean before Plan B's Gate-A loop opens. +Plan B opens against this WIP commit, amends it with the same message, and uses +`.context/plan-a-base-sha` as its base. Plan A's Gate-A loop must have closed clean first. --- ## Self-Review -**Spec coverage.** §2 predicate, unanimity, unresolvable-stop, one-value-three-cycles → Task 1 -Step 3. §2.1 precedence, knob, residual → Task 1 Steps 3-4. §2.2 pass report → Task 3. §2.4 -mid-cycle → Task 4. §3 severity → Task 5 Step 2. §10 activation, revert, downstream adoption, -gate-off surface → Task 1 Step 4 and Task 5 Step 3, **partial by design and written to be -extended**: the record duties in §10's strict-fallback list are Plan B's to append. §2.3, §4, §5, -§6 → Plan B. §7, §8 → Plan C. +**Spec coverage.** §2 → Task 1 Step 3. §2.1 → Task 1 Steps 3-4. §2.2 → Task 3. §2.4 → Task 4. §3 → +Task 5 Step 2. §10 activation, revert, downstream adoption, gate-off surface → Task 1 Step 4 and +Task 5 Step 3, **partial by design and written to be extended**. §2.3, §4, §5, §6 → Plan B. §7, §8 +→ Plan C, with the five relocated findings named above. **Placeholders.** None. -**Sequence-correct expected values — the claim, and its two known limits.** Every check states an -expected value for its point in the sequence, and every executable step addresses by content rather -than by a line number an earlier task can shift. Two places state no numeric expectation on -purpose, rather than by omission: Task 6 Step 3 asks for a human read of two diffs against an -eleven-row inventory, where a number would invite substituting the number for the read; and Task 6 -Step 1's preflight is a judgement about already-appended tables. Revision 1 made this claim -blanket and it was false in two places (finding plana-MINOR 14) — these are the two, named. +**Every expected value is an observation.** Each was produced by executing the check against a +simulated post-edit tree at its point in the sequence. **Exactly two steps state no numeric +expectation**, both deliberately and both named in place: Task 6 Step 3 (a human reading two diffs) +and Task 6 Step 1 (a judgement about already-appended tables). **Type consistency.** `max(risk, security)`, "derived floor", "reminder threshold", "cited set" and -"level 0" are used identically in Tasks 1, 2, 3, 4 and 5, and match the spec's spellings. +"level 0" are used identically throughout and match the spec's spellings. **Gate-B classification.** Plan A opens the single cycle at Task 1 Step 6 and runs no review. Every -commit from that point is an amend restating `-m "WIP: review-loop economics"`. Plan C closes. +later commit is an amend restating `-m "WIP: review-loop economics"`. Plan C closes. -**Rerun and interruption.** Every task opens with a preflight distinguishing not-started, complete, -duplicate and damaged, and **asymmetry between the two copies is its own state** with its own -answer — an execution abandoned between the two files leaves the mirrors disagreeing, and Task 1 -Step 1 says to reconcile before proceeding rather than continuing (finding plana-M3). +**Rerun and interruption.** Task 1 carries a closed state matrix over four independent markers; +Tasks 2-5 carry preflights whose observed values distinguish not-started from complete; Task 6 +Step 1 checks for its own output markers. **Cross-copy asymmetry is a named state with a named +action** in Task 1, and the parity check in Task 6 is what catches it if it survives that far. **Known limit.** The replacement wordings are proposals, not transcriptions — the spec pins the rules, not the sentences — and this plan's Gate A is what reviews them. From e4509add31bce87def1d51f0e24c371dafaec3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 09:40:22 +0200 Subject: [PATCH 059/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=204?= =?UTF-8?q?=20=E2=80=94=20strip=20the=20instrument?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 3 fired the standing escalation: 12 of 13 Blocker/Major were about the plan's own checks (92%), exactly one concerned the rules being shipped, and Blockers had not fallen in two rounds (7, 6, 6). The rules were converging and the verification machinery was diverging. The decisive evidence, and why "execute the check" was not the fix: revision 3's Task-2 verification proved the six OLD sentences were gone and never that the NEW ones had arrived, so DELETING THOSE SIX SENTENCES OUTRIGHT would have produced the plan's exact recorded observations. The check was executed and its pasted output was true. It still could not fail in the direction that mattered. Two more of the same shape: the awk scoping added in revision 2 ran to EOF when its end marker was absent and matched a pre-existing occurrence elsewhere in the file, so all four Task-1 counts could read 1 with the rule never inserted; and Task 6's preflight grepped for strings occurring in its own prose and its own command, returning 4 against a pasted 0. WHAT THIS REVISION DOES: each task carries exactly ONE check - the new text is present at the site - which fails trivially before the edit and passes after. Deleted: the parity loops, count assertions, awk-scoped range checks, preflight state matrices, staging proofs, and the plan-level base-SHA verification apparatus. Their subjects die by deletion, not repair. Each task is now: site (pasted grep -n anchor), OLD text (pasted), NEW text (full), one assert-new grep, one amend. Thirteen tasks, thirteen checks, plus the battery and handoff. All thirteen patterns were verified against a simulated post-edit tree to return 0/0 before their task and 1/1 after; each lies on one line, contains no ** emphasis, and is passed after -- so a leading dash cannot be read as an option. WHERE THE VERIFICATION WENT - relocated, not dropped, and stated in the plan: Gate B reviews the actual combined diff against the spec, which is its job and which it has demonstrably done better than plan prose (the pass-2 reviewer built a sandbox, the pass-3 reviewer a replay script - both executed the plan rather than reading it); the old-conditions accounting closes that obligation once, as a reviewed record rather than repeated greps; the differential verification and the battery discharge the evidence mode in Plan C; the story's criteria are checked at the combined close. One sentence survives from the base-SHA repair - the cycle records its base once at open and uses it for every diff and call - without the per-handoff proof apparatus. The two non-instrument findings are fixed: - M7: the Lenses sentence is replaced in BOTH halves, per the M1 lesson. Revision 3 fixed the first half and left the tail reading "The floor ... is unchanged", which told a reader the floor was unchanged in the very change that makes it profile-dependent. The claim eliminated is "the floor is unchanged"; the replacement says outright that it is not, and the accounting records condition 9b as deliberately dropped and replaced by its negation rather than as kept. - MINOR 8 and NIT 10: the "sixteen edits" claim is gone (there are 13), and the Plan-C inheritance table now attributes evidence revalidation to pass-1 B6, not pass-2 B6 - pass-2 B6 is the base-SHA defect, which Plan A owned. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 962 ++++++++---------- 1 file changed, 444 insertions(+), 518 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index 5466586..ef60a91 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -8,10 +8,10 @@ pass floor becomes a function of the cited story's profile, and finding severity is decided by whether something in the system takes a different decision. -**Architecture:** Two mirrored prose edits, block by block: `CLAUDE.md` §5 and the inline template -in `/workflow-init`. **No code.** No file under `plugins/dev-workflow/hooks/` changes. +**Architecture:** Thirteen mirrored prose edits, one per task: `CLAUDE.md` §5 and the inline +template in `/workflow-init`. **No code.** No file under `plugins/dev-workflow/hooks/` changes. -**Tech Stack:** Markdown prompts; POSIX shell for every check; `git` for the records. +**Tech Stack:** Markdown prompts; `git` for the records. **Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). Plan A implements §2, §2.1, §2.2, §2.4, §3, and §10 in part. @@ -25,68 +25,40 @@ Plan A implements §2, §2.1, §2.2, §2.4, §3, and §10 in part. --- -## Every check below is a pasted observation, not a prediction +## What this plan verifies, and what it does not -**This is the rule that produced revision 3.** Revision 2 stated what its checks *would* print. -Fourteen Blocker/Major followed, five of them broken checks: a phrase containing `**emphasis**` -matched as plain text; a phrase split across a line break matched as one string; a `grep -cF` -pattern beginning `- ` parsed by grep as an option (exit 2); an `awk` range whose end anchor -existed on no line, running to EOF. +**Each task carries exactly one check: the new text is present at the site.** It fails trivially +before the edit and passes after. That is the whole per-task instrument. -Every expected value in this revision was produced by **executing** the check against a simulated -post-edit tree, at its point in the sequence, and pasting what came back. The simulation applies -all sixteen edits to copies of both files, snapshots after each task, and runs each check against -the snapshot that task would actually see. +**This is a deliberate reduction, decided after three rounds of evidence.** Revisions 1–3 carried +parity loops, count assertions, scoped range extractions, preflight state matrices and staging +proofs. The Gate-A curve across those rounds: -**The replacement texts below are byte-identical to the ones the simulation applied** — this -document is generated from the same source the simulation executed, so the two cannot drift. - -Result of that run, verbatim: - -``` -T1 preflight (untouched tree) old=1 new=0 both copies -T1 verify (after T1) predicate=1 residual=1 gateoff=1 tail=1 both copies -T2 preflight(after T1) 6 both copies -T2 minor-site(after T1) 1 both copies -T2 verify (after T2) regex=0 new=1 old=0 PR#23=1 both copies -T3 preflight(after T2) 0 both copies -T3 verify (after T3) marker=1 old-para=1 tells=1 both copies -T4 baseline (after T3) 9 both copies -T4 verify (after T4) 9 both copies (identical to baseline) -T5 verify (after T5) severity=1 activation=1 extensible=1 defs=1 both copies -T6 parity (after T5) PARITY-COMPLETE 13/13, 0 problems -``` - ---- - -## Why this is one of three plans - -| Plan | Ships | Spec sections | -|---|---|---| -| **A — this one** | the floor predicate and the severity test | §2, §2.1, §2.2, §2.4, §3, §10 (partial) | -| **B** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | -| **C** | rollout: falsified sentences, packaging, the evidence pack, the review loop | §7, §8 | - -**Three Gate-A cycles, ONE Gate-B cycle.** Each plan is reviewed as its own artifact; the three -ship **one diff**, reviewed once after Plan C. Giving Plan A its own cycle produced two Blockers -that were pure infrastructure paradox — the battery could not go green because the manifest bump is -Plan C's, and the findings slots needed a grammar Plan B ships. - -**Execution order A → B → C. Plan B may not open before Plan A's Gate-A loop closes.** +| pass | findings | Blockers | B+M | of which instrument | +|---|---|---|---|---| +| 1 | 21 | 7 | 17 | most | +| 2 | 16 | 6 | 14 | 5 of 6 Blockers | +| 3 | 16 | 6 | 13 | **12 of 13 B+M — 92%** | -### Findings inherited by Plan C — named, because they were relocated, not repaired +By pass 3 exactly one Blocker/Major concerned the rules being shipped. The rules were converging +and the verification machinery was diverging: each round's checks grew, and each round the +reviewer found a new way they could report success while the thing they checked was absent or +wrong. The decisive example — Plan A's own Task-2 verification proved the six *old* sentences were +gone and never that the *new* ones had arrived, so **deleting those six sentences outright would +have produced the plan's exact recorded observations.** Executing a check and pasting its true +output does not make it a check that can fail in the direction that matters. -Plan A revision 1 carried a Gate-B section that no longer exists here. Four pass-1 findings lived -in it, and a finding whose section moved is **relocated, not fixed** (finding plana-M6). **Plan C -must inherit these explicitly:** +**Where the verification went — nothing is dropped, it is relocated to the artifact that owns it:** -| Finding | What it requires of Plan C | +| Obligation | Now discharged by | |---|---| -| pass-1 M8 | single-branch Gate-B recovery: delete only the failed branch, never both | -| pass-1 M9 | record the floor knob's existence **and bytes** before the cycle, compare after | -| pass-1 M10 | never `git add -u`; stage an explicitly inspected path set | -| pass-1 MINOR 12 | build the closing body with `mktemp`, not a fixed `/tmp` path | -| pass-2 B6 | evidence revalidated after every fix and again before the closing amend | +| the edits are correct, complete, and contradict nothing | **Gate B**, reviewing the actual combined A+B+C diff against the spec. This is its job, and it has demonstrably done it better than plan prose: the pass-2 reviewer built a sandbox at `.context/plan-a-pass2-sim/` and the pass-3 reviewer a replay at `.context/pass3_replay.rb`, each executing the plan rather than reading it | +| every old condition kept, moved or deliberately dropped | the **old-conditions accounting** below, closed once, reviewed by this plan's Gate-A cycle — the `AGENTS.md` Don't is satisfied by that record, not by repeated greps | +| the change does what it claims | the **differential named verification** and the **battery**, in Plan C, discharging the story's evidence mode | +| the story's acceptance criteria | checked at the combined close | + +**The two prompt copies staying in parity** is part of what Gate B reviews, and the accounting +below names the two passages where they already diverge so a reviewer is not surprised by them. --- @@ -99,160 +71,106 @@ must inherit these explicitly:** the diff needing most iteration. **Carry this sentence in the `additionalContext` of every Gate-B call.** - **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes, and the floor knob - `.context/codex-gate.floor` is never written, never removed, never read for the derivation. - **This is not a claim that nothing under `.context/` is written**: the Gate-B calls and commit - events in this work write pass counters, fingerprints and disclosure markers there as always. - Only the floor knob is untouched. + `.context/codex-gate.floor` is never written, never removed, never read for the derivation. This + is not a claim that nothing under `.context/` is written — the Gate-B calls and commit events in + this work write pass counters, fingerprints and disclosure markers there as always. Only the + floor knob is untouched. - **The §5 heading must keep matching `^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`.** `codex-gate.sh:94` greps `CLAUDE.md` for it to build every reminder's citation. -- **Every rule lands in BOTH copies.** Where the two copies already differ, the difference is - pre-existing, is named in the accounting, and is **left exactly as it is**. +- **Every edit lands in BOTH copies.** Where the two copies already differ, the difference is + pre-existing, is named in the accounting, and is left exactly as it is. - **§5's other closure rules are never restated, only referred to.** -- **Address by content, never by line number.** Line numbers appear only as pasted provenance. -- **Check patterns obey four rules**, each learned from a broken check: no `**` inside a match - pattern; no pattern spanning a line break; any pattern beginning `-` passed with `-e` or after - `--`; any range bounded by a condition that exists, never by an end anchor assumed to exist. +- **Line numbers are provenance, never instructions.** Each task pastes its `grep -n` anchor as it + stood in the untouched tree; the edit is located by its OLD text. ### The commit protocol -**One WIP commit, opened here, amended by Plans B and C, reviewed once after C, closed once.** +**One WIP commit, opened at Task 1, amended by every later task and by Plans B and C, reviewed +once after Plan C, closed once.** -> **The `--no-edit` trap.** `plugins/dev-workflow/hooks/codex-gate.sh:763` is -> `is_wip_commit() { printf '%s' "$1" | grep -Eiq -- "-m[[:space:]]*['\"]?[[:space:]]*wip"; }` -> It matches **the Bash command string**, not git state. `git commit --amend --no-edit` carries no -> `-m`, is not recognized, and at `:886` the hook **resets, discarding the cycle's passes.** Every -> amend restates `-m "WIP: ..."`. **Never `--no-edit` inside the cycle.** (Backlog: `todos.md`.) +> **Never `git commit --amend --no-edit` inside the cycle.** +> `plugins/dev-workflow/hooks/codex-gate.sh:763` recognizes a WIP commit by grepping the **Bash +> command string** for `-m ... wip`; an amend without `-m` is not recognized, and the hook resets, +> discarding the cycle's passes. Every amend below restates `-m "WIP: review-loop economics"`. +> (On the backlog: `todos.md`.) -**The base SHA is recorded before Task 1 and is the reference for every diff and every Gate-B -call** — never `HEAD~1` (finding plana-B6). Without it, a resumed Task 1 or an accidental second -WIP stacks commits while still reporting the cycle open, and a review based on the tip's parent -silently omits the earlier WIP: part of the combined diff escapes the only Gate-B review. +**The Gate-B cycle records its base SHA once, at cycle open, and uses it for every diff and every +Gate-B call** — not `HEAD~1`, which moves if a snapshot is ever stacked. Task 1 prints it. -```bash -git rev-parse HEAD > .context/plan-a-base-sha -cat .context/plan-a-base-sha -``` +Plans B and C amend the same commit. Plan C adds the manifest bump, runs the single Gate-B loop, +and closes with `git commit --amend -m ""`. -**At every plan handoff, the tip must be the sole WIP child of that base:** +--- -```bash -base=$(cat .context/plan-a-base-sha) -n=$(git rev-list --count "$base"..HEAD) -[ "$n" = 1 ] || { echo "STACKED: $n commits above base — collapse with git reset --soft $base, then one WIP commit"; exit 1; } -git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } -echo "CYCLE OK — single WIP on $base" -``` +## Why this is one of three plans + +| Plan | Ships | Spec sections | +|---|---|---| +| **A — this one** | the floor predicate and the severity test | §2, §2.1, §2.2, §2.4, §3, §10 (partial) | +| **B** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | +| **C** | rollout: falsified sentences, packaging, the evidence pack, the review loop | §7, §8 | -1. **No ordinary commit from Task 1 onward** until the combined cycle closes in Plan C. -2. **Task 1 opens the cycle**: `git commit -m "WIP: review-loop economics"`. -3. **Tasks 2-6 amend it**, each restating that exact message. -4. **Plans B and C amend the same commit.** Plan C adds the bump, runs the single Gate-B loop with - `baseSha` = the recorded base, and closes with `git commit --amend -m ""`. +**Three Gate-A cycles, ONE Gate-B cycle.** Each plan is reviewed as its own artifact; the three +ship **one diff**, reviewed once after Plan C. Execution order A → B → C; Plan B may not open +before Plan A's Gate-A loop closes. + +### What Plan C inherits + +These came from Plan A's earlier Gate-B section, which no longer exists here. A finding whose +section moved is **relocated, not repaired**, so Plan C must carry them explicitly: + +| Finding | What it requires of Plan C | +|---|---| +| pass-1 M8 | single-branch Gate-B recovery: delete only the failed branch, never both | +| pass-1 M9 | record the floor knob's existence and bytes before the cycle, compare after | +| pass-1 M10 | never `git add -u`; stage an explicitly inspected path set | +| pass-1 MINOR 12 | build the closing body with `mktemp`, not a fixed `/tmp` path | +| **pass-1 B6** | evidence revalidated after every fix and again before the closing amend | --- ## Old-conditions accounting -**Derived from the edits this plan makes**, against §5 at HEAD. **Eleven passages.** +**Derived from the edits this plan makes**, against §5 at HEAD. **Eleven passages, thirteen rows.** **Two passages diverge between the copies and get a row each.** Both divergences were found by `diff` over the passage's **full extent**, after a 13-line comparison window on the Gate-A passage -produced a false IDENTICAL — a check that ended before the divergence (finding plana-M2). - -``` -CLAUDE.md:72:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major -CLAUDE.md:77:final pass must be clean — if pass 3 still finds Blocker/Major, keep going until -CLAUDE.md:79:below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is -CLAUDE.md:126:the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps -CLAUDE.md:133:**From pass 4 onward every pass report carries three lines.** The carrier is **your own -CLAUDE.md:236:act on the partial list, don't count it toward the 3-pass floor, and don't read "no -CLAUDE.md:300:- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the -CLAUDE.md:335: invalidates the prior pass, which is where the 3 come from. -CLAUDE.md:403:Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major -CLAUDE.md:480:**Changing a profile:** the pass **proposes the complete resulting header** — both axes, -CLAUDE.md:495:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → -``` - -``` -plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major -plugins/dev-workflow/commands/workflow-init.md:277:final pass must be clean — if pass 3 still finds Blocker/Major, keep going until -plugins/dev-workflow/commands/workflow-init.md:279:below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is -plugins/dev-workflow/commands/workflow-init.md:322:the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps -plugins/dev-workflow/commands/workflow-init.md:330:**From pass 4 onward every pass report carries three lines.** The carrier is **your own -plugins/dev-workflow/commands/workflow-init.md:421:act on the partial list, don't count it toward the 3-pass floor, and don't read "no -plugins/dev-workflow/commands/workflow-init.md:485:- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the -plugins/dev-workflow/commands/workflow-init.md:519: invalidates the prior pass, which is where the 3 come from. -plugins/dev-workflow/commands/workflow-init.md:582:Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major -plugins/dev-workflow/commands/workflow-init.md:659:**Changing a profile:** the pass **proposes the complete resulting header** — both axes, -plugins/dev-workflow/commands/workflow-init.md:674:- **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → -``` +produced a false IDENTICAL — a comparison that ended before the divergence. | # | Passage | Copy | What the existing prose requires | Disposition | |---|---|---|---|---| -| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · **d. the hook cannot read findings** · **e. the hook cannot tell the spec run from the plan run** · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · **d, e, f, g, h kept verbatim in the second paragraph** (revision 2's row folded d and e away, finding plana-M3) · b, i-m **kept verbatim** | -| 2 | `if pass 3 still` | both | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, `pass 3` → `the pass at the floor` | -| 3 | `below 3` | both | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, `below 3` → `below the floor` | -| 4 | pass-1 Minor sentence | both | below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass below the floor`. **The sentence that inverts at floor 1**, where pass 1 *is* the floor | -| 5a | pass-report paragraph | `CLAUDE.md` | a. from pass 4 onward, three lines · b. carrier is your own status report · c. never the Codex reply · d. never the findings file · e. trend · f. cluster · g. require↔withdraw · h. the five tells · i. any-two makes stop-and-surface **mandatory** · j. "clearly stuck" is not a precondition · **k. "you report the tells" — second person** | **untouched.** Task 3 inserts a new paragraph *before* it and modifies nothing in it, so a-k all stand | -| 5b | pass-report paragraph | template | a-j as above · **k′. "report the tells" — imperative, no addressee** · plus different wrapping | **untouched**, same reason. **The divergence is pre-existing and is left alone** | -| 6 | incomplete-pass | both | an incomplete pass is not a review: don't act on the partial list, don't count it toward the floor, don't read "no Blocker/Major visible" as clean | **kept**, `the 3-pass floor` → `the floor` | -| 7a | Gate A loop | `CLAUDE.md` | a. two runs, each its own 3-pass loop · b. one broad prompt, re-run each pass · c. don't narrow per-dimension · d. the required opening phrase · e. coverage floor not a cage · f. every finding with severity and confidence · g. **the citation `` (`docs/prompt-standards.md`, "coverage first, filter later") ``** · h. one line per finding · i. literal `NO FINDINGS` · j. settle mechanically before each read pass | **a kept**, `3-pass loop` → `loop at the derived floor`; **b-j untouched, g included** | -| 7b | Gate A loop | template | a-f, h-j as above · **g′. the citation is ABSENT** — the template says "findings silently." and stops · plus different wrapping | same edit to `a`; **the missing citation is pre-existing and is left alone.** Revision 2 called this passage byte-identical on the strength of a 13-line window; over its full 30/29-line extent it is not | -| 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept, rationale replaced** — see Task 2 (e) | -| 9 | Lenses | both | lenses are different questions, not more passes; the floor, the Blocker/Major filter, the file-first protocol and the clean-final-pass rule are unchanged | **kept**, reworded so "unchanged" no longer claims the floor is fixed | -| 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules are **appended after them**, never merged in. Task 4's check measures all nine, scoped to the original paragraph, before and after | -| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test is **appended** as the procedure that sets a ceiling on them | - -**Nothing in §5 outside these eleven passages is edited.** Task 6 Step 3 proves it by reading both -diffs against this inventory. +| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. the hook cannot read findings · e. the hook cannot tell the spec run from the plan run · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1) · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · d–h **kept verbatim** in the second paragraph · b, i–m **kept verbatim**, outside the replaced range | +| 2 | `if pass 3 still` | both | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, `pass 3` → `the pass at the floor` (Task 3) | +| 3 | `below 3` | both | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, `below 3` → `below the floor` (Task 4) | +| 4 | pass-1 Minor sentence | both | below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass below the floor` (Task 9) | +| 5a | pass-report paragraph | `CLAUDE.md` | a. from pass 4 onward, three lines · b. carrier is your own status report · c. never the Codex reply · d. never the findings file · e. trend · f. cluster · g. require↔withdraw · h. the five tells · i. any-two makes stop-and-surface mandatory · j. "clearly stuck" is not a precondition · **k. "you report the tells" — second person** | **untouched.** Task 10 inserts a new paragraph *before* it and modifies nothing in it | +| 5b | pass-report paragraph | template | a–j as above · **k′. "report the tells" — imperative, no addressee** · plus different wrapping | **untouched**, same reason. The divergence is pre-existing and is left alone | +| 6 | incomplete-pass | both | an incomplete pass is not a review: don't act on the partial list, don't count it toward the floor, don't read "no Blocker/Major visible" as clean | **kept**, `the 3-pass floor` → `the floor` (Task 5) | +| 7a | Gate A loop | `CLAUDE.md` | a. two runs, each its own 3-pass loop · b. one broad prompt, re-run each pass · c. don't narrow per-dimension · d. the required opening phrase · e. coverage floor not a cage · f. every finding with severity and confidence · **g. the citation `` (`docs/prompt-standards.md`, "coverage first, filter later") ``** · h. one line per finding · i. literal `NO FINDINGS` · j. settle mechanically before each read pass | **a kept**, `3-pass loop` → `loop at the derived floor` (Task 6); **b–j untouched, g included** | +| 7b | Gate A loop | template | a–f, h–j as above · **g′. the citation is ABSENT** — the template says "findings silently." and stops · plus different wrapping | same edit to `a`; **the missing citation is pre-existing and is left alone** | +| 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept, rationale replaced** — both lines, claim named (Task 7) | +| 9 | Lenses | both | a. lenses are different questions, not more passes · b. the 3-pass floor is unchanged · c. the Blocker/Major filter is unchanged · d. the file-first protocol is unchanged · e. the clean-final-pass rule is unchanged | a **kept and sharpened** · **b deliberately dropped and replaced by its negation** — the floor is precisely what this change makes variable, so the sentence now says so · c, d, e **kept verbatim** (Task 8) | +| 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules appended after them, never merged in (Task 11) | +| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them (Task 12) | + +**Nothing in §5 outside these eleven passages is edited.** Gate B, reviewing the combined diff, is +what confirms that against this table. --- +## Task 1: The floor predicate -## Task 1: Record the base, open the cycle, replace the floor +**Spec:** §2, §2.1 -- [ ] **Step 1: Record the immutable base SHA** +**Site** — pasted `grep -n`: -```bash -mkdir -p .context && git rev-parse HEAD > .context/plan-a-base-sha -cat .context/plan-a-base-sha ``` - -This file is the reference for every later diff and every Gate-B call. It is under `.context/`, -which is gitignored, so it never enters the reviewed diff. - -- [ ] **Step 2: Preflight — a closed state matrix over four independent markers** - -Revision 2 collapsed this to one marker pair, so an interruption between the floor replacement and -the residual/gate-off insert, or between the two mirrors, read as complete (finding plana-B2). - -```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%-46s old=%s pred=%s res=%s gate=%s\n' "$f" \ - "$(grep -cF 'HARD FLOOR: min 3 passes per run' "$f")" \ - "$(grep -cF "derived from the cited story's profile" "$f")" \ - "$(grep -cF 'Named residual' "$f")" \ - "$(grep -cF 'routes known today, not a complete list' "$f")" -done +CLAUDE.md:72:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major +plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major ``` -| old pred res gate | state | action | -|---|---|---| -| `1 0 0 0` | not started | proceed to Step 3 | -| `0 1 0 0` | Step 3 done, Step 4 not | **do Step 4 only** | -| `0 1 1 0` | Step 4 half-applied | insert the gate-off block only | -| `0 1 1 1` | complete | verify against Step 5, skip to Step 6 | -| `1 1 * *` | duplicate insert | a rerun appended without removing — delete the inserted blocks, restart | -| any `pred` or `res` or `gate` > 1 | duplicate | same | -| `0 0 0 0` | damaged | **STOP** — restore with `git show $(cat .context/plan-a-base-sha):` | - -**Cross-copy asymmetry is its own state.** If the two rows differ, execution stopped between the -files: bring the lagging copy to the leading copy's state before proceeding. **Never proceed with -the mirrors disagreeing.** - -- [ ] **Step 3: Replace the floor sentences** +> The text on disk ends **mid-line**: ` Open a TodoWrite "Codex pass N" per pass;` continues the same line after `review.` Match exactly this and no more; what follows stays. -The text on disk ends **mid-line** — ` Open a TodoWrite "Codex pass N" per pass;` continues the -same line after `review.` Match exactly this and no more (finding plana-B1); what follows stays: +- [ ] **Replace, in both copies.** OLD: ``` **Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major @@ -262,7 +180,7 @@ the spec run especially — is instruction-backed: a satisfied count is not a cl review. ``` -Replace with: +NEW: ``` **Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run @@ -292,16 +210,45 @@ dropped. Nothing here writes the floor knob: it stays the user's, never written, removed, never read for this derivation. ``` -- [ ] **Step 4: Add the residual and the gate-off disclosure** +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "derived from the cited story's profile" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` -Match the floor paragraph's final line **including its trailing newline**, and re-emit it followed -by the two new blocks: +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. + +- [ ] **Open the cycle.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit -m "WIP: review-loop economics" +git rev-parse HEAD~1 # the cycle's base — Plan C uses this for every diff and Gate-B call +``` + +--- + +## Task 2: The residual and the gate-off surface + +**Spec:** §2.1, §10 + +**Site** — pasted `grep -n`: + +``` +CLAUDE.md:80:advisory — validate before applying; dismissed finding → one-line why. +plugins/dev-workflow/commands/workflow-init.md:280:advisory — validate before applying; dismissed finding → one-line why. +``` + +> Match the floor paragraph's final line including its trailing newline, and re-emit it followed by the two new blocks. The gate-off list is explicitly **not** exhaustive. + +- [ ] **Replace, in both copies.** OLD: ``` advisory — validate before applying; dismissed finding → one-line why. ``` -Replace with: +NEW: ``` advisory — validate before applying; dismissed finding → one-line why. @@ -322,89 +269,118 @@ None of this is a guard: the floor is produced by the agent and nothing checks i the cited profiles. ``` -- [ ] **Step 5: Verify — observed values, both copies** +- [ ] **Assert the new text is present.** ```bash -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf '%-46s predicate=%s residual=%s gateoff=%s tail=%s\n' "$f" \ - "$(awk '/HARD FLOOR/,/never read for this derivation/' "$f" | grep -c 'max(risk, security)')" \ - "$(grep -cF 'Named residual' "$f")" \ - "$(grep -cF 'routes known today, not a complete list' "$f")" \ - "$(grep -cF "don't manufacture findings to pad" "$f")" -done +grep -cF -- "routes known today, not a complete list" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Observed in the simulation: `predicate=1 residual=1 gateoff=1 tail=1` for both copies.** +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. -The `awk` scoping on `predicate` is load-bearing: unscoped, `grep -c 'max(risk, security)'` returns -1 **before any edit**, because the Profiles section already contains the phrase — the check would -pass before the edit and prove nothing. `tail=1` holds before and after, catching a replacement -that swallowed its neighbours. - -- [ ] **Step 6: OPEN THE CYCLE** +- [ ] **Amend the WIP commit.** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -git status --porcelain # nothing else may be dirty -git diff --cached --name-only # exactly these two paths -git commit -m "WIP: review-loop economics" -base=$(cat .context/plan-a-base-sha); git rev-list --count "$base"..HEAD # must be 1 +git commit --amend -m "WIP: review-loop economics" ``` -**The `git status` line is not decoration** (finding plana-M8): `git add` stages the complete -files, so a pre-existing unrelated edit inside either one would be folded into the reviewed diff -and closed under this story. If anything else is dirty, stop and resolve it first. - --- -## Task 2: The floor-wording sites +## Task 3: `pass 3` at the clean-final-pass rule + +**Spec:** §2 + +**Site** — pasted `grep -n`: + +``` +CLAUDE.md:77:final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +plugins/dev-workflow/commands/workflow-init.md:277:final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +``` + +- [ ] **Replace, in both copies.** OLD: + +``` +final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +``` + +NEW: + +``` +final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until +``` -- [ ] **Step 1: Preflight — the sequence-correct count** +- [ ] **Assert the new text is present.** ```bash -grep -cE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ +grep -cF -- "if the pass at the floor still finds" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Observed after Task 1: `6` and `6` — not 7.** Seven is the count in an untouched tree; Task 1 has -already removed the `min 3 passes` match. `0`/`0` means this task ran. +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. -- [ ] **Step 2: The site this regex does not cover** +- [ ] **Amend the WIP commit.** ```bash -grep -cF 'carrying a Minor keeps' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" ``` -**Observed after Task 1: `1` and `1`.** Accounting passage 4 contains no digit `3`, so Step 1's -regex never matched it — **Step 1 reaching 0 does not cover it.** Step 4 asserts it separately. +--- -- [ ] **Step 3: Six replacements per copy** +## Task 4: `below 3` at the early-exit rule -**a.** OLD: +**Spec:** §2 + +**Site** — pasted `grep -n`: ``` -final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +CLAUDE.md:79:below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +plugins/dev-workflow/commands/workflow-init.md:279:below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +``` + +- [ ] **Replace, in both copies.** OLD: + +``` +below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is ``` NEW: ``` -final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until +below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is ``` -**b.** OLD: +- [ ] **Assert the new text is present.** +```bash +grep -cF -- "below the floor is a pass with" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" ``` -NEW: +--- + +## Task 5: `3-pass floor` in the incomplete-pass rule + +**Spec:** §2 + +**Site** — pasted `grep -n`: ``` -below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is +CLAUDE.md:236:act on the partial list, don't count it toward the 3-pass floor, and don't read "no +plugins/dev-workflow/commands/workflow-init.md:421:act on the partial list, don't count it toward the 3-pass floor, and don't read "no ``` -**c.** OLD: +- [ ] **Replace, in both copies.** OLD: ``` act on the partial list, don't count it toward the 3-pass floor, and don't read "no @@ -416,7 +392,36 @@ NEW: act on the partial list, don't count it toward the floor, and don't read "no ``` -**d.** OLD: +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "count it toward the floor" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 6: `3-pass loop` in the Gate-A description + +**Spec:** §2 + +**Site** — pasted `grep -n`: + +``` +CLAUDE.md:300:- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the +plugins/dev-workflow/commands/workflow-init.md:485:- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the +``` + +- [ ] **Replace, in both copies.** OLD: ``` - **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the @@ -428,7 +433,40 @@ NEW: - **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the ``` -**e.** OLD: +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "each its own loop at the derived floor" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 7: The re-review rationale + +**Spec:** §2 + +**Site** — pasted `grep -n`: + +``` +CLAUDE.md:334: @AGENTS.md. Re-review after every fix — a fix changes the diff and the hook +plugins/dev-workflow/commands/workflow-init.md:518: @AGENTS.md. Re-review after every fix — a fix changes the diff and the hook +``` + +> **Third attempt at one sentence, and both lines are replaced.** The original, `which is where the 3 come from`, was a causal claim the new design makes false. Revision 1 made the hook the *cause* of the obligation. Revision 2 replaced only the second line, leaving `a fix changes the diff and the hook` in front of it, so the sentence read "the hook no longer covers the artifact" — the hook still the subject. `AGENTS.md` records this pattern taking four Gate-B rounds because each correction searched for the previous **phrase** rather than the **claim**. +> > +> > The claim being eliminated is named: *the hook causes the re-review obligation.* The test the replacement passes: **delete the hook and the sentence is still true.** + +- [ ] **Replace, in both copies.** OLD: ``` @AGENTS.md. Re-review after every fix — a fix changes the diff and the hook @@ -442,98 +480,134 @@ NEW: review no longer covers it. The hook merely notices, at commit time. ``` -> **Three findings, one sentence — and this is the third attempt at it.** The original, -> `which is where the 3 come from`, was a causal claim the new design makes false: the lower bound -> now comes from the profile. Revision 1 replaced it with *the hook invalidates the prior pass — -> which is why a fix costs another pass*, making the advisory hook the **cause** of the obligation. -> Revision 2 replaced only the second line, leaving `a fix changes the diff and the hook` in front -> of it, so the shipped sentence read **"the hook no longer covers the artifact"** — the hook still -> the subject. `AGENTS.md` records this exact pattern taking four Gate-B rounds because each -> correction searched for the previous **phrase** rather than the **claim**. -> -> **Both lines are replaced**, and the claim being eliminated is named: *the hook causes the -> re-review obligation.* The test the replacement passes — **delete the hook and the sentence is -> still true**: a fix changes the artifact, so the prior review no longer covers it. What the hook -> does is notice, at commit time. +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "review no longer covers it. The hook merely notices" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. -**f.** OLD: +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 8: The Lenses rule + +**Spec:** §2 + +**Site** — pasted `grep -n`: + +``` +CLAUDE.md:403:Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major +plugins/dev-workflow/commands/workflow-init.md:582:Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major +``` + +> **Both lines, for the same reason as the previous task.** The old sentence's tail says the floor "is unchanged". Revision 3 replaced the first half and left that tail standing, so the shipped sentence still told a reader the floor was unchanged in the change that makes it profile-dependent. The claim eliminated here is *the floor is unchanged*; the replacement says outright that it is not. + +- [ ] **Replace, in both copies.** OLD: ``` Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major +filter, the file-first findings protocol and the clean-final-pass rule are unchanged. ``` NEW: ``` -Lenses are **different questions, not more passes** — they change what a pass asks, -never how many passes a cycle owes, which the profile and the cited set decide together. -The floor, the Blocker/Major +Lenses are **different questions, not more passes** — they change what a pass asks, never +how many a cycle owes. The Blocker/Major filter, the file-first findings protocol and the +clean-final-pass rule are unchanged. The floor is not among them: it is no longer a fixed +number but derives from the profile and the cited set. +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "derives from the profile and the cited set" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" ``` -> Two findings here too. The original tail reads "… is unchanged", which in the very change that makes the floor profile-dependent tells readers the opposite of what shipped. Revision 2 wrote "which the profile alone decides" — **also wrong**, because cited-set emptiness, membership, unprofiled members and unresolvable members all bear on the result. "The profile and the cited set decide together" is what Task 1's predicate actually says. +--- + +## Task 9: The pass-1 Minor sentence + +**Spec:** §2 + +**Site** — pasted `grep -n`: + +``` +CLAUDE.md:126:the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps +plugins/dev-workflow/commands/workflow-init.md:322:the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps +``` +> **The sentence that inverts at a floor of 1**, where pass 1 *is* the floor. It contains no digit `3`, so no regex over the other sites reaches it. -- [ ] **Step 4: The pass-1 Minor sentence** +- [ ] **Replace, in both copies.** OLD: ``` the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps ``` -Replace with: +NEW: ``` the only exception, exactly as above; a Blocker/Major-free pass below the floor carrying a Minor keeps ``` -- [ ] **Step 5: Verify — observed values** +- [ ] **Assert the new text is present.** ```bash -grep -cE "min 3 passes|below 3|3-pass|where the 3 come from|if pass 3 still" \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'a Blocker/Major-free pass below the floor' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'Blocker/Major-free pass 1 carrying a Minor' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF "PR #23's Gate-B pass 3 returned all four findings" \ +grep -cF -- "a Blocker/Major-free pass below the floor" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Observed after Task 2: `0`/`0`, then `1`/`1`, then `0`/`0`, then `1`/`1`.** The last must stay -`1`: it cites an actual pass, not a rule, and changing it would falsify a record. +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. -- [ ] **Step 6: Amend the WIP commit** +- [ ] **Amend the WIP commit.** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md git commit --amend -m "WIP: review-loop economics" -base=$(cat .context/plan-a-base-sha); git rev-list --count "$base"..HEAD # must still be 1 ``` --- -## Task 3: The pass report (§2.2) +## Task 10: The pass report -- [ ] **Step 1: Preflight** +**Spec:** §2.2 + +**Site** — pasted `grep -n`: -```bash -grep -cF 'the cited stories they were read' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Observed after Task 2: `0` and `0`.** The pattern deliberately stops before `from`, which begins -a new line in the inserted text — a pattern spanning a line break matches nothing. +``` -- [ ] **Step 2: Insert before the pass-report paragraph** +> Inserted **before** the existing paragraph, which is not modified — including the `you report the tells` / `report the tells` divergence between the copies, which is pre-existing (accounting rows 5a/5b). Do not harmonize it. -Match the paragraph's opening line and re-emit it after the new text. **Nothing in the existing -paragraph is modified** — including the `you report the tells` / `report the tells` divergence -between the copies, which is pre-existing and stays (accounting rows 5a/5b). Do not harmonize it. +- [ ] **Replace, in both copies.** OLD: ``` **From pass 4 onward every pass report carries three lines.** ``` -Replace with: +NEW: ``` **Every pass report states three things about the floor**, from pass 1 onward: the @@ -547,23 +621,16 @@ the three lines below are owed from pass 4 and are a different obligation. **From pass 4 onward every pass report carries three lines.** ``` -- [ ] **Step 3: Verify — observed values** +- [ ] **Assert the new text is present.** ```bash -grep -cF 'the cited stories they were read' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'From pass 4 onward every pass report carries three lines' \ +grep -cF -- "the cited stories they were read" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'require↔withdraw pair' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -printf 'C:%s T:%s\n' "$(grep -cF -- '— you report' CLAUDE.md)" \ - "$(grep -cF -- '— report the' plugins/dev-workflow/commands/workflow-init.md)" ``` -**Observed after Task 3: `1`/`1`, `1`/`1`, `1`/`1`, then `C:1 T:1`.** The last asserts the -pre-existing divergence is still exactly as it was — note `--` before a pattern starting with `—` -is unnecessary but `-- ` is used consistently for patterns whose first character could be read as -an option. +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. -- [ ] **Step 4: Amend the WIP commit** +- [ ] **Amend the WIP commit.** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md @@ -572,33 +639,20 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 4: A profile or cited set that moves mid-cycle (§2.4) +## Task 11: A profile or cited set that moves mid-cycle -- [ ] **Step 1: Preflight, and capture the nine-condition baseline** +**Spec:** §2.4 -```bash -grep -cF 'Any profile change costs at least one further pass' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +**Site** — pasted `grep -n`: -for f in CLAUDE.md plugins/dev-workflow/commands/workflow-init.md; do - printf 'BASELINE %-46s ' "$f" - awk '/\*\*Changing a profile:\*\*/,/discard the accumulated passes\./' "$f" \ - | grep -oF -e 'proposes the complete resulting header' -e 'human confirms it' \ - -e 'never moves it alone' -e 'one profile-log line' -e 'voids every prior override' \ - -e 'follows the current security value' -e 'keep counting' -e 'final clean pass' \ - -e 'fold the edit into the active' | wc -l | tr -d ' ' -done +``` +CLAUDE.md:488:discard the accumulated passes. +plugins/dev-workflow/commands/workflow-init.md:667:discard the accumulated passes. ``` -**Observed after Task 3: `0`/`0`, then `BASELINE 9` for both copies.** Nine markers for the nine -conditions of accounting row 10. Two things make this measure what it claims: the `awk` range ends -at `discard the accumulated passes.`, **the original paragraph's last line**, so the appended text -is outside it; and `grep -oF -e ...` passes each phrase as a separate fixed string, so -`one profile-log line` matches although the source wraps `append` onto the previous line. - -- [ ] **Step 2: Append after the `Changing a profile:` paragraph** +> The nine conditions of the `Changing a profile:` paragraph are kept verbatim; §2.4's rules are **appended after them**, never merged in. -Its nine conditions are kept verbatim. §2.4's rules are **appended after them**, never merged in. +- [ ] **Replace, in both copies.** OLD: ``` discard the accumulated passes. @@ -606,7 +660,7 @@ discard the accumulated passes. **What this does not do:** ``` -Replace with: +NEW: ``` discard the accumulated passes. @@ -637,15 +691,16 @@ citation. **What this does not do:** ``` -- [ ] **Step 3: Verify against the captured baseline** +- [ ] **Assert the new text is present.** -Re-run **the identical command from Step 1**, changing only the label. The `awk` range still ends -at the original paragraph's last line, so it measures the original paragraph alone. +```bash +grep -cF -- "Any profile change costs at least one further pass" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` -**Observed after Task 4: `1`/`1` for the new marker, and `9` for both copies — identical to the -baseline.** Any difference is a dropped condition, not a formatting artefact. +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. -- [ ] **Step 4: Amend the WIP commit** +- [ ] **Amend the WIP commit.** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md @@ -654,27 +709,26 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 5: Severity semantics (§3), and activation (§10, partial) +## Task 12: Severity semantics -- [ ] **Step 1: Preflight** +**Spec:** §3 -```bash -grep -cF 'what in the system consumes this text' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'a cycle already running' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -``` +**Site** — pasted `grep -n`: -**Observed after Task 4: `0`/`0` and `0`/`0`.** +``` +CLAUDE.md:496: rework) → both must resolve. Minor · Nit → collect, never iterate. +plugins/dev-workflow/commands/workflow-init.md:675: rework) → both must resolve. Minor · Nit → collect, never iterate. +``` -- [ ] **Step 2: Append the severity test to the Severity bullet** +> The four severity definitions stay verbatim; the reachability test is appended as the procedure that sets a **ceiling** on them. -The four definitions stay verbatim. +- [ ] **Replace, in both copies.** OLD: ``` rework) → both must resolve. Minor · Nit → collect, never iterate. ``` -Replace with: +NEW: ``` rework) → both must resolve. Minor · Nit → collect, never iterate. @@ -704,18 +758,44 @@ Replace with: granularities — text that *describes* the product versus text that *is* the product. ``` -- [ ] **Step 3: Append the activation block after the gate-off disclosure** +- [ ] **Assert the new text is present.** -**Plan B extends this list rather than rewriting it** — §10 says extending is safe and replacing is -not. `at minimum` and `each further rule this change ships adds its own strict reading to this -list` are what make that possible. +```bash +grep -cF -- "what in the system consumes this text" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 13: Activation + +**Spec:** §10 + +**Site** — pasted `grep -n`: + +``` + +``` + +> **Plan B extends this list rather than rewriting it** — §10 says extending is safe and replacing is not. `at minimum` and `each further rule this change ships adds its own strict reading to this list` are what make that possible. + +- [ ] **Replace, in both copies.** OLD: ``` None of this is a guard: the floor is produced by the agent and nothing checks it against the cited profiles. ``` -Replace with: +NEW: ``` None of this is a guard: the floor is produced by the agent and nothing checks it against @@ -739,22 +819,16 @@ severity test gets a floor whose docs-only question the severity test is what se What prompt text can do is done; what it cannot is said. ``` -- [ ] **Step 4: Verify — observed values** +- [ ] **Assert the new text is present.** ```bash -grep -cF 'what in the system consumes this text' \ - CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'a cycle already running' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF 'adds its own strict reading' CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -grep -cF -- '- **Severity:** Blocker (wrong/unsafe/breaks invariant)' \ +grep -cF -- "a cycle already running" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` -**Observed after Task 5: `1`/`1` four times.** Two of these patterns were broken in revision 2: -`adds its own strict reading to this list` spanned a line break, and the Severity pattern begins -`- `, which grep parses as an option and exits 2 — hence `--` before it. +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated post-edit tree — the pattern lies on one line, contains no `**`, and is passed after `--` so a leading `-` cannot be read as an option. -- [ ] **Step 5: Amend the WIP commit** +- [ ] **Amend the WIP commit.** ```bash git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md @@ -763,153 +837,12 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 6: Parity, conformance, and the diff proof - -- [ ] **Step 1: Preflight** - -This task appends two result tables to **this plan document**. If a table with the marker -`PARITY RESULTS — Plan A` or `CONFORMANCE RESULTS — Plan A` already exists, this task ran: verify -its row count rather than appending again. - -```bash -grep -c 'PARITY RESULTS — Plan A\|CONFORMANCE RESULTS — Plan A' \ - docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md -``` - -**Expected `0` before this task, `2` after.** - -- [ ] **Step 2: Parity — compare the shipped text, not counts** +## Task 14: The battery, and the hand-off to Plan B -One anchor per block, extraction bounded by a condition that **exists** (blank line or the next -`- **` bullet) rather than by an end anchor assumed to exist. Revision 2's two-anchor ranges -produced 10 of 13 rows because three end anchors occurred on no line (finding plana-B5). +Plan A runs no Gate-B cycle. The single cycle covering all three plans opened at Task 1 and is +reviewed and closed by Plan C. -```bash -C=CLAUDE.md; T=plugins/dev-workflow/commands/workflow-init.md -tmp=$(mktemp -d) || exit 1 -i=0; ok=0; bad=0 -while IFS= read -r start; do - [ -z "$start" ] && continue - i=$((i+1)) - for pair in "C:$C" "T:$T"; do - tag=${pair%%:*}; f=${pair#*:} - awk -v s="$start" ' - index($0,s) && !f {f=1; print; next} - f && ($0=="" || $0 ~ /^- \*\*/) {exit} - f {print}' "$f" > "$tmp/$tag.$i" - done - cs=$(wc -l < "$tmp/C.$i"); ts=$(wc -l < "$tmp/T.$i") - if [ "$cs" -eq 0 ] || [ "$ts" -eq 0 ]; then - printf 'EMPTY %2s %s\n' "$i" "$start"; bad=$((bad+1)) - elif diff -q "$tmp/C.$i" "$tmp/T.$i" >/dev/null; then - printf 'PARITY %2s %2s lines %s\n' "$i" "$cs" "$start"; ok=$((ok+1)) - else - printf 'DIFFERS %2s %s\n' "$i" "$start"; bad=$((bad+1)); diff "$tmp/C.$i" "$tmp/T.$i" - fi -done <<'ANCHORS' -**Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run -**The derived floor is the pass count a cycle owes -**Named residual:** -**The gate-off surface -**When these rules bind.** -**Downstream has no shipping commit.** -**Every pass report states three things about the floor** -**While a gate is running, the floor derives from the current profile -**Any profile change costs at least one further pass** -**The cited set is re-read at each pass -**Deciding severity -The exclusions are contract, not commentary. -This is the finding-level analog -ANCHORS -rm -rf "$tmp" -[ "$i" = 13 ] && [ "$ok" = 13 ] && echo "PARITY-COMPLETE 13/13" \ - || { echo "PARITY-INCOMPLETE ranges=$i parity=$ok problems=$bad"; exit 1; } -``` - -**Observed on the simulated post-edit tree:** - -``` -PARITY 1 14 lines **Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run -PARITY 2 14 lines **The derived floor is the pass count a cycle owes -PARITY 3 4 lines **Named residual:** -PARITY 4 9 lines **The gate-off surface -PARITY 5 9 lines **When these rules bind.** -PARITY 6 6 lines **Downstream has no shipping commit.** -PARITY 7 7 lines **Every pass report states three things about the floor** -PARITY 8 4 lines **While a gate is running, the floor derives from the current profile -PARITY 9 8 lines **Any profile change costs at least one further pass** -PARITY 10 8 lines **The cited set is re-read at each pass -PARITY 11 4 lines **Deciding severity -PARITY 12 15 lines The exclusions are contract, not commentary. -PARITY 13 2 lines This is the finding-level analog -PARITY-COMPLETE 13/13 -``` - -**The line counts are part of the expectation.** An `EMPTY` cannot be masked, and a block that -suddenly grows means the extractor ran past its intended end — which is exactly what happened -before the `^- \*\*` bound was added: range 13 captured 24 lines of the Mechanics list. - -Record the result as a table headed **`PARITY RESULTS — Plan A`**: *n* · *anchor* · *lines* · -**status** from `PARITY` | `DIFFERS` | `EMPTY` · *reason, required for the latter two*. **Thirteen -rows.** **A `DIFFERS` or `EMPTY` row stops the task** — it is a parity defect, not a note. - -- [ ] **Step 3: The diff proof — both files, every hunk, against the recorded base** - -```bash -base=$(cat .context/plan-a-base-sha) -git diff "$base" -- CLAUDE.md -git diff "$base" -- plugins/dev-workflow/commands/workflow-init.md -git diff "$base" --stat -- CLAUDE.md plugins/dev-workflow/commands/workflow-init.md -``` - -Read **both** diffs in full and confirm every hunk falls inside one of the eleven accounted -passages. **A hunk outside them is a finding.** - -No expected line count is stated, deliberately: the correct check is a human reading two diffs -against an eleven-row inventory, and a number invites substituting the number for the read. This is -one of exactly two places in this plan without a numeric expectation; the other is Step 1's -judgement about already-appended tables. - -- [ ] **Step 4: The twelve-item conformance pass** - -Artifacts: the **resulting scaffolded template**, and `plugins/dev-workflow/commands/workflow-init.md` -as the outer command prompt. One row per item per artifact: *item* · *artifact* · **status** from -`PASS` | `N/A` | `FAIL` · *reason, required for `N/A` and `FAIL`*. **Twenty-four rows**, headed -**`CONFORMANCE RESULTS — Plan A`**. **Item 7 is read against the whole resulting artifact.** -**A `FAIL` row stops the task.** - -**Root `CLAUDE.md` is outside invariant 11's list and is not part of this pass.** - -> **Item 1 for the scaffolded template is `N/A`; Plan C ships the note that says why** — the file -> the template writes is model-agnostic by design, so a `Target model:` line inside it would be -> false in every repo it lands in. - -- [ ] **Step 5: Invariant checks** - -```bash -grep -c '^Target model:' plugins/dev-workflow/commands/workflow-init.md -sh scripts/check-invariants.sh && echo INVARIANTS-OK -``` - -**Expected: `1`, then `INVARIANTS-OK`.** The count must be 1 — `scripts/check-invariants.sh` fails -on any other, and a naive item-1 fix that adds a second declaration is exactly how that breaks. - -- [ ] **Step 6: Fold the results into the WIP commit** - -```bash -git status --porcelain # only the plan document may be dirty -git add docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md -git commit --amend -m "WIP: review-loop economics" -``` - ---- - -## Task 7: The battery, and the hand-off to Plan B - -Plan A runs no Gate-B cycle. The single cycle covering all three plans opens here and is reviewed -and closed by Plan C. - -- [ ] **Step 1: The battery, minus one step, for a stated reason** +- [ ] **Run the battery, minus one step, for a stated reason** ```bash shellcheck --shell=sh plugins/dev-workflow/hooks/codex-gate.sh && \ @@ -928,58 +861,51 @@ claude plugin validate . --strict && echo "BATTERY-GREEN (version-bump deferred) > **`sh scripts/check-version-bump.sh main` is deliberately absent, and only that one step.** Plan > A changes a path under `plugins/dev-workflow/` while the manifest bump is Plan C's, so the > checker **would correctly fail here**. It is deferred to the combined close, where the bump -> exists. `AGENTS.md` already states this checker's precondition: it compares *commits*, and run -> mid-work it reports clean and uselessly. -> -> **This is a deferral of one step with a named reason, not licence to skip the rest.** Every other -> command must pass before Plan B opens. `scripts/check-version-bump.test.sh` — the suite — still -> runs, because it does not depend on the working tree's bump state. - -- [ ] **Step 2: Confirm the cycle is intact before handing off** +> exists. `AGENTS.md` states this checker's precondition: it compares *commits*, and run mid-work +> it reports clean and uselessly. **This is a deferral of one step with a named reason, not licence +> to skip the rest** — `scripts/check-version-bump.test.sh`, the suite, still runs, because it does +> not depend on the working tree's bump state. -```bash -base=$(cat .context/plan-a-base-sha) -n=$(git rev-list --count "$base"..HEAD) -[ "$n" = 1 ] || { echo "STACKED: $n commits above base — collapse with git reset --soft $base, then one WIP commit"; exit 1; } -git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } -git status --porcelain -echo "CYCLE OK — single WIP on $base" -``` +- [ ] **Confirm `scripts/check-invariants.sh` still passes** -**Expected: `CYCLE OK`, a clean worktree, and `n` exactly 1.** The count check is what catches a -stacked WIP that a subject-only check would pass while part of the diff escapes review. +It is already in the battery above; called out because it is the one mechanical guard on a file +this plan edits — it fails if `plugins/dev-workflow/commands/workflow-init.md` stops having exactly +one `^Target model:` line. -- [ ] **Step 3: Hand off** +- [ ] **Hand off to Plan B** -Plan B opens against this WIP commit, amends it with the same message, and uses -`.context/plan-a-base-sha` as its base. Plan A's Gate-A loop must have closed clean first. +Plan B amends the same WIP commit with the same message and uses the base SHA Task 1 printed. Plan +A's Gate-A loop must have closed clean first. --- ## Self-Review -**Spec coverage.** §2 → Task 1 Step 3. §2.1 → Task 1 Steps 3-4. §2.2 → Task 3. §2.4 → Task 4. §3 → -Task 5 Step 2. §10 activation, revert, downstream adoption, gate-off surface → Task 1 Step 4 and -Task 5 Step 3, **partial by design and written to be extended**. §2.3, §4, §5, §6 → Plan B. §7, §8 -→ Plan C, with the five relocated findings named above. +**Spec coverage.** §2 → Tasks 1–9. §2.1 → Tasks 1–2. §2.2 → Task 10. §2.4 → Task 11. §3 → Task 12. +§10 activation, revert, downstream adoption, gate-off surface → Tasks 2 and 13, **partial by design +and written to be extended** by Plan B. §2.3, §4, §5, §6 → Plan B. §7, §8 → Plan C, with the five +relocated findings named above. **Placeholders.** None. -**Every expected value is an observation.** Each was produced by executing the check against a -simulated post-edit tree at its point in the sequence. **Exactly two steps state no numeric -expectation**, both deliberately and both named in place: Task 6 Step 3 (a human reading two diffs) -and Task 6 Step 1 (a judgement about already-appended tables). +**Instrument.** Thirteen edit tasks, one assert-new check each, no other checks. Every pattern was +verified against a simulated post-edit tree to return `0` and `0` before its task and `1` and `1` +after; each lies on one line, contains no `**`, and is passed after `--`. **What these checks +prove is that the edit landed at the site — not that its content is right.** Content is Gate B's, +against the combined diff. **Type consistency.** `max(risk, security)`, "derived floor", "reminder threshold", "cited set" and "level 0" are used identically throughout and match the spec's spellings. -**Gate-B classification.** Plan A opens the single cycle at Task 1 Step 6 and runs no review. Every -later commit is an amend restating `-m "WIP: review-loop economics"`. Plan C closes. +**Gate-B classification.** Plan A opens the single cycle at Task 1 and runs no review. Every later +commit is an amend restating `-m "WIP: review-loop economics"`. Plan C closes. -**Rerun and interruption.** Task 1 carries a closed state matrix over four independent markers; -Tasks 2-5 carry preflights whose observed values distinguish not-started from complete; Task 6 -Step 1 checks for its own output markers. **Cross-copy asymmetry is a named state with a named -action** in Task 1, and the parity check in Task 6 is what catches it if it survives that far. +**Rerun and interruption.** Each task's assert-new check doubles as its own preflight: `1`/`1` +means that task has run, `0`/`0` means it has not, and a `1`/`0` split means execution stopped +between the two copies — bring the lagging copy up before continuing. No other state machinery; +the OLD text is the edit's precondition and a task whose OLD text is absent has either run already +or been damaged, which the surrounding git history settles. -**Known limit.** The replacement wordings are proposals, not transcriptions — the spec pins the -rules, not the sentences — and this plan's Gate A is what reviews them. +**Known limit, stated rather than checked.** The replacement wordings are proposals, not +transcriptions — the spec pins the rules, not the sentences. This plan's Gate A reviews the +wordings; Gate B reviews what they do to the shipped files. From 895a94d2291acdceef2fea1565010f40d472f383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 09:56:38 +0200 Subject: [PATCH 060/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=205?= =?UTF-8?q?=20=E2=80=94=20fix=20the=20three=20rules=20findings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 4 on the stripped plan: 7 findings, ZERO Blockers, 3 Major. The strip worked. Curve across the cycle: pass 1: 21 findings, 7 B, 10 M = 17 B+M (instrument-heavy) pass 2: 16 findings, 6 B, 8 M = 14 B+M (5 of 6 Blockers instrument) pass 3: 16 findings, 6 B, 7 M = 13 B+M (12 of 13 B+M instrument, 92%) pass 4: 7 findings, 0 B, 3 M = 3 B+M (0 instrument) Every pass-4 finding is about the rules being shipped, which is what the review is for. All three Majors are in the assigned fix set and are repaired here. M1 - the demotion collided with the five-tells rule. The severity test demotes consequence-free instrument and rationale findings to Minor; the tells rule makes instrument clustering plus prose clustering a mandatory stop even on a Blocker/Major-free pass at or above the floor. The same pass could be both eligible to close and required to stop. Resolved in the shipped text rather than by touching the untouched tells paragraph: demotion changes WHICH FINDINGS GATE, not WHAT THE TELLS OBSERVE - a demoted finding still counts toward clustering, because the tells measure where a loop's attention goes, not how much of it blocks - and a Blocker/Major-free pass at or above the floor closes, the two-tell stop being a rule for a loop still running. M2 - the partial-adoption warning named only the floor-without-severity coupling and then claimed "what prompt text can do is done". It missed the sharper case: a merge taking Task 1 without the coordinated numeric replacements leaves the derived floor standing beside explicit `pass 3`, `below 3` and `3-pass floor` obligations that contradict it outright, with no precedence given. The text now names that state and says to stop for human resolution before running a gate under it, and the categorical claim is replaced by a statement of the actual limit. M3 - "one derived value governs all three cycles" was undefined across an ordinary edit between cycles, because the pass-count rules are silent when no gate is running. The promise is now about the PREDICATE, not a frozen number: each cycle derives its floor from the cited set as it stands during its own passes, and a change between cycles binds cycles not yet closed while never reopening a closed one - which is the existing passes-already-run-keep-counting rule, not a new one. The three Minor and one NIT are collected, not iterated, per §5. All 13 assert-new patterns re-verified after the fixes: 0/0 before their task, 1/1 after, both copies. The accounting rows for the floor paragraph and the Severity bullet now record the two added conditions. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index ef60a91..b1890ad 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -139,7 +139,7 @@ produced a false IDENTICAL — a comparison that ended before the divergence. | # | Passage | Copy | What the existing prose requires | Disposition | |---|---|---|---|---| -| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. the hook cannot read findings · e. the hook cannot tell the spec run from the plan run · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1) · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · d–h **kept verbatim** in the second paragraph · b, i–m **kept verbatim**, outside the replaced range | +| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. the hook cannot read findings · e. the hook cannot tell the spec run from the plan run · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1), which also settles what a change between cycles does: it binds cycles not yet closed and never reopens a closed one · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · d–h **kept verbatim** in the second paragraph · b, i–m **kept verbatim**, outside the replaced range | | 2 | `if pass 3 still` | both | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, `pass 3` → `the pass at the floor` (Task 3) | | 3 | `below 3` | both | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, `below 3` → `below the floor` (Task 4) | | 4 | pass-1 Minor sentence | both | below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass below the floor` (Task 9) | @@ -151,7 +151,7 @@ produced a false IDENTICAL — a comparison that ended before the divergence. | 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept, rationale replaced** — both lines, claim named (Task 7) | | 9 | Lenses | both | a. lenses are different questions, not more passes · b. the 3-pass floor is unchanged · c. the Blocker/Major filter is unchanged · d. the file-first protocol is unchanged · e. the clean-final-pass rule is unchanged | a **kept and sharpened** · **b deliberately dropped and replaced by its negation** — the floor is precisely what this change makes variable, so the sentence now says so · c, d, e **kept verbatim** (Task 8) | | 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules appended after them, never merged in (Task 11) | -| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them (Task 12) | +| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them, together with its precedence against the five-tells rule — a demoted finding still counts toward the tells, and a Blocker/Major-free pass at or above the floor still closes (Task 12) | **Nothing in §5 outside these eleven passages is edited.** Gate B, reviewing the combined diff, is what confirms that against this table. @@ -169,6 +169,8 @@ plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with ``` > The text on disk ends **mid-line**: ` Open a TodoWrite "Codex pass N" per pass;` continues the same line after `review.` Match exactly this and no more; what follows stays. +> > +> > The between-cycle rule at the end is new in revision 5. Revision 4 promised "one derived value governs all three cycles" while §2.4's rules are silent when no gate is running, so an ordinary profile edit between two cycles left the promise undefined — a closed cycle at one floor and the next at another. The promise is now about the **predicate**, not a frozen number, and the interval is settled explicitly. - [ ] **Replace, in both copies.** OLD: @@ -193,10 +195,13 @@ under the existing rule; it does not fall through to 3, because reading it as 3 turn a stop condition into a silent default. Across a cited set the floor is 1 if and only if the set is non-empty and every member is profiled, resolvable and at level 0 — all four conditions, since "every cited story" is vacuously true of an empty set; -no story cited, or any cited story unprofiled, gives 3. One derived value governs all +no story cited, or any cited story unprofiled, gives 3. One derivation governs all three cycles: the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle. Not -because they are one cycle — they are three — but because they derive from the same -cited-story set. +because they are one cycle — they are three — but because the same predicate reads the +same cited-story set. Each cycle derives its floor from that set as it stands during its +own passes. A profile or cited-set change between cycles binds the cycles that have not +closed and does not reopen one that has: passes already run keep counting, which is the +existing rule and is not changed here. **The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder threshold that controls nothing.** The hook still counts passes, and it still can't read @@ -721,6 +726,8 @@ plugins/dev-workflow/commands/workflow-init.md:675: rework) → both must resol ``` > The four severity definitions stay verbatim; the reachability test is appended as the procedure that sets a **ceiling** on them. +> > +> > The final paragraph is new in revision 5. Without it the demotion and the five-tells rule collide: a pass could be Blocker/Major-free at or above the floor — therefore closeable — while two tells made stopping mandatory. Demotion now explicitly changes **which findings gate**, not **what the tells observe**, and clean completion governs. - [ ] **Replace, in both copies.** OLD: @@ -756,6 +763,13 @@ NEW: This is the finding-level analog of the path-level prose exemption: one principle at two granularities — text that *describes* the product versus text that *is* the product. + + **Demotion changes which findings gate; it does not change what the pass-report tells + observe.** A demoted finding still counts toward instrument clustering and prose + clustering, because the tells measure where a loop's attention is going, not how much of + it blocks. Where a pass is Blocker/Major-free at or above the floor, the clean-completion + rule governs and the cycle closes: the two-tell stop is a rule for a loop still running, + not a bar to one that has finished. ``` - [ ] **Assert the new text is present.** @@ -787,6 +801,8 @@ git commit --amend -m "WIP: review-loop economics" ``` > **Plan B extends this list rather than rewriting it** — §10 says extending is safe and replacing is not. `at minimum` and `each further rule this change ships adds its own strict reading to this list` are what make that possible. +> > +> > The partial-adoption paragraph is widened in revision 5. Revision 4 named only the floor-without-severity coupling and then claimed "what prompt text can do is done" — an overclaim, and it missed the sharper case: a merge taking the floor predicate without the numeric replacements leaves the derived floor beside explicit `pass 3` obligations that contradict it. - [ ] **Replace, in both copies.** OLD: @@ -816,7 +832,11 @@ actually writes the text — which may write nothing, be declined, or be merged so these rules bind only over the text a project's `CLAUDE.md` actually contains, and a partial adoption can persist undetected. A project taking the floor rule without the severity test gets a floor whose docs-only question the severity test is what settles. -What prompt text can do is done; what it cannot is said. +**A partial adoption can also leave the derived floor standing beside the old fixed-number +obligations — `pass 3`, `below 3`, `3-pass floor` — which contradict it outright. Where a +project's text contains both, nothing here resolves which governs: stop, and have a human +resolve it, before running a gate under it.** What prompt text can do about downstream +adoption is limited, and that limit is what this paragraph states. ``` - [ ] **Assert the new text is present.** From 26e78859096124f9306538d239c70221a73129cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 10:08:06 +0200 Subject: [PATCH 061/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=206?= =?UTF-8?q?=20=E2=80=94=20repair=20three=20of=20four;=20one=20contract=20q?= =?UTF-8?q?uestion=20open?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 5: 5 findings, 0 Blockers, 4 Major. Curve: pass 1: 21, 7B 10M = 17 B+M instrument-heavy pass 2: 16, 6B 8M = 14 B+M 5 of 6 Blockers instrument pass 3: 16, 6B 7M = 13 B+M 12 of 13 B+M instrument (92%) pass 4: 7, 0B 3M = 3 B+M 0 instrument - the strip worked pass 5: 5, 0B 4M = 4 B+M 0 instrument; 3 of 4 correct pass-4's own fixes REPAIRED HERE - all three correct a pass-4 fix and stay inside the assigned fix set, so they are absorbed rather than routed, per §5's absorb rule: M2 - the clean-completion precedence was broader than the collision it repaired. "A Blocker/Major-free pass at or above the floor closes" would have let the new rule bypass §5's untouched scope stops, which require stopping when a finding leaves the assigned fix set or opens a new structural question REGARDLESS of severity. The precedence is now explicitly against the two-tell stop ONLY, and says outright that it overrides nothing else. M3 - "demotion does not change what the tells observe" was wrong in one direction. Demoting a Blocker to Minor DOES remove it from the Blocker curve - that is what demoting it is for. The rule is now stated per tell: every reported finding counts toward the total, the subject clusters and the require-withdraw comparison; the Blocker curve reads severity after the ceiling is applied. M4 - the partial-adoption trigger named three spellings and so missed the ones a partial merge happens to leave: the Gate-A loop description, the pass-1 closure rule and the re-review rationale each carry a fixed-three claim, and a merge can take some tasks and not others. The trigger is now SEMANTIC - any surviving claim that the floor is a fixed number, or that closing turns on a specific pass number, sitting beside the derived predicate. OPEN AND ROUTED, not repaired - pass-5 M1 is a CONTRACT QUESTION: Pass-4 M3 found that spec §2's "one derived value governs all three cycles" is undefined across an ordinary profile edit between cycles. Revision 5 answered by weakening it to a per-cycle derivation. Pass 5 shows that contradicts the approved spec, which says one derived VALUE, and the story's criterion with it. So the plan would implement a different cross-cycle contract while claiming to implement the spec. Per §5 that stops the loop: a finding that corrects the last correction AND opens a new contract question - the new question wins. Either the spec defines the shared snapshot it promises, or the spec and story criterion are revised to approve per-cycle derivation. That is not an agent's call. Task 1 carries a note saying its between-cycle sentence is awaiting that decision and must not be executed until it is resolved. The NIT is collected, not iterated. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index b1890ad..e945420 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -170,7 +170,7 @@ plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with > The text on disk ends **mid-line**: ` Open a TodoWrite "Codex pass N" per pass;` continues the same line after `review.` Match exactly this and no more; what follows stays. > > -> > The between-cycle rule at the end is new in revision 5. Revision 4 promised "one derived value governs all three cycles" while §2.4's rules are silent when no gate is running, so an ordinary profile edit between two cycles left the promise undefined — a closed cycle at one floor and the next at another. The promise is now about the **predicate**, not a frozen number, and the interval is settled explicitly. +> > **The between-cycle sentence is OPEN, not settled.** Pass-4 M3 found that "one derived value governs all three cycles" is undefined across an ordinary profile edit between cycles. Revision 5 answered by weakening the promise to a per-cycle derivation — and pass-5 M1 showed that contradicts spec §2, which says **one derived value** governs all three. The text below is revision 5's wording and is **awaiting a decision on whether the spec or the plan changes**; do not execute this task until that is resolved. - [ ] **Replace, in both copies.** OLD: @@ -467,9 +467,9 @@ CLAUDE.md:334: @AGENTS.md. Re-review after every fix — a fix changes the diff plugins/dev-workflow/commands/workflow-init.md:518: @AGENTS.md. Re-review after every fix — a fix changes the diff and the hook ``` -> **Third attempt at one sentence, and both lines are replaced.** The original, `which is where the 3 come from`, was a causal claim the new design makes false. Revision 1 made the hook the *cause* of the obligation. Revision 2 replaced only the second line, leaving `a fix changes the diff and the hook` in front of it, so the sentence read "the hook no longer covers the artifact" — the hook still the subject. `AGENTS.md` records this pattern taking four Gate-B rounds because each correction searched for the previous **phrase** rather than the **claim**. +> **Third attempt at one sentence, and both lines are replaced.** The original, `which is where the 3 come from`, was a causal claim the new design makes false. Revision 1 made the hook the *cause* of the obligation. Revision 2 replaced only the second line, leaving `a fix changes the diff and the hook` in front of it, so the sentence read "the hook no longer covers the artifact". > > -> > The claim being eliminated is named: *the hook causes the re-review obligation.* The test the replacement passes: **delete the hook and the sentence is still true.** +> > The claim eliminated is *the hook causes the re-review obligation*. The load-bearing half — `a fix changes the artifact, so the prior review no longer covers it` — stands without the hook; the trailing clause describes what the hook does and is true only while it exists, which is a description, not the cause. - [ ] **Replace, in both copies.** OLD: @@ -514,7 +514,7 @@ CLAUDE.md:403:Lenses are **different questions, not more passes.** The 3-pass fl plugins/dev-workflow/commands/workflow-init.md:582:Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major ``` -> **Both lines, for the same reason as the previous task.** The old sentence's tail says the floor "is unchanged". Revision 3 replaced the first half and left that tail standing, so the shipped sentence still told a reader the floor was unchanged in the change that makes it profile-dependent. The claim eliminated here is *the floor is unchanged*; the replacement says outright that it is not. +> **Both lines, for the same reason as the previous task.** The old tail says the floor "is unchanged". Revision 3 replaced the first half and left that tail, so the shipped sentence still told a reader the floor was unchanged in the change that makes it profile-dependent. The claim eliminated is *the floor is unchanged*. - [ ] **Replace, in both copies.** OLD: @@ -727,7 +727,7 @@ plugins/dev-workflow/commands/workflow-init.md:675: rework) → both must resol > The four severity definitions stay verbatim; the reachability test is appended as the procedure that sets a **ceiling** on them. > > -> > The final paragraph is new in revision 5. Without it the demotion and the five-tells rule collide: a pass could be Blocker/Major-free at or above the floor — therefore closeable — while two tells made stopping mandatory. Demotion now explicitly changes **which findings gate**, not **what the tells observe**, and clean completion governs. +> > The last two paragraphs settle how demotion interacts with the untouched five-tells rule. **Per tell**, because a blanket claim was wrong in one direction: demoting a Blocker to Minor *does* remove it from the Blocker curve — that is the point — while the total, the clusters and the require↔withdraw comparison still see every reported finding. And the precedence is **against the two-tell stop only**: a finding that leaves the assigned fix set or opens a new structural question still stops the cycle at any severity. - [ ] **Replace, in both copies.** OLD: @@ -764,12 +764,18 @@ NEW: This is the finding-level analog of the path-level prose exemption: one principle at two granularities — text that *describes* the product versus text that *is* the product. - **Demotion changes which findings gate; it does not change what the pass-report tells - observe.** A demoted finding still counts toward instrument clustering and prose - clustering, because the tells measure where a loop's attention is going, not how much of - it blocks. Where a pass is Blocker/Major-free at or above the floor, the clean-completion - rule governs and the cycle closes: the two-tell stop is a rule for a loop still running, - not a bar to one that has finished. + **Demotion changes which findings gate. Per tell:** every reported finding, demoted or + not, counts toward the finding total, toward the instrument and prose clusters, and + toward the require↔withdraw comparison — those measure where a loop's attention is + going, not how much of it blocks. **The Blocker curve reads severity after this ceiling + is applied**, so a finding demoted to Minor leaves that series, which is what demoting it + is for. + + **Against the two-tell stop only:** where a pass is Blocker/Major-free at or above the + floor **and no other rule here requires a stop**, the two-tell threshold alone does not + bar completion — it governs a loop still running, not one that has finished. It overrides + nothing else. A finding that leaves the assigned fix set, or opens a new structural or + contract question, still stops the cycle whatever severity it carries. ``` - [ ] **Assert the new text is present.** @@ -800,9 +806,9 @@ git commit --amend -m "WIP: review-loop economics" ``` -> **Plan B extends this list rather than rewriting it** — §10 says extending is safe and replacing is not. `at minimum` and `each further rule this change ships adds its own strict reading to this list` are what make that possible. +> **Plan B extends this list rather than rewriting it** — §10 says extending is safe and replacing is not. > > -> > The partial-adoption paragraph is widened in revision 5. Revision 4 named only the floor-without-severity coupling and then claimed "what prompt text can do is done" — an overclaim, and it missed the sharper case: a merge taking the floor predicate without the numeric replacements leaves the derived floor beside explicit `pass 3` obligations that contradict it. +> > The partial-adoption trigger is **semantic, not a list of spellings**. Naming `pass 3`, `below 3` and `3-pass floor` missed the ones a partial merge happens to leave: the Gate-A loop description, the pass-1 closure rule and the re-review rationale each carry a fixed-three claim, and a merge can take some tasks and not others. - [ ] **Replace, in both copies.** OLD: @@ -833,9 +839,14 @@ so these rules bind only over the text a project's `CLAUDE.md` actually contains partial adoption can persist undetected. A project taking the floor rule without the severity test gets a floor whose docs-only question the severity test is what settles. **A partial adoption can also leave the derived floor standing beside the old fixed-number -obligations — `pass 3`, `below 3`, `3-pass floor` — which contradict it outright. Where a -project's text contains both, nothing here resolves which governs: stop, and have a human -resolve it, before running a gate under it.** What prompt text can do about downstream +obligations, which contradict it outright.** The trigger is semantic, not a list of +spellings: **any surviving statement that the floor is a fixed number, or that closing +turns on a specific pass number, sitting beside the derived predicate.** Enumerating the +spellings would miss the ones a partial merge happens to leave — the Gate-A loop +description, the pass-1 closure rule and the re-review rationale each carry one, and a +merge can take some of them and not others. Where a project's text contains both kinds of +rule, nothing here resolves which governs: **stop, and have a human complete or revert the +adoption, before running a gate under it.** What prompt text can do about downstream adoption is limited, and that limit is what this paragraph states. ``` From b3258d14685dd00c6cd289f2fbb69221435c7985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 10:12:03 +0200 Subject: [PATCH 062/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=207?= =?UTF-8?q?=20=E2=80=94=20encode=20the=20between-cycle=20rule=20as=20curre?= =?UTF-8?q?nt-header-governs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass-5 M1 was routed as a contract question with two options. The answer is a third reading, and I verified it textually against revision 36 before encoding it, as instructed. THE RULE: the derived value is a function of the cited set's CURRENT confirmed profiles, read fresh wherever §5 already requires reading them. At any moment there is exactly one value because there is one source - which is what §2's sentence actually asserts, since it argues from the source ("because they derive from the same cited-story set") and never from time. A profile or set change binds every open and future cycle; a raise costs an affected open cycle a further pass under the current profile. A cycle that has already closed stands, its close having been valid under the profile current when it closed - the cycle-level form of "passes already run keep counting". VERIFICATION, four quotes checked one at a time, recorded in the dispositions: 1. §2's one-value sentence argues from the source, not from time. One value at any moment from one source satisfies it. 2. §2.4: "The floor derives from the CURRENT profile at each pass." Supports this directly; contradicts a snapshot directly. 3. §2.4: the pass-count rules "apply while §5 says a gate is running and are silent otherwise", and "what §5 says about when a gate runs - INCLUDING HOW A MOVING PROFILE OR CITED SET BEARS ON THAT - is §5's, unchanged". 4. CLAUDE.md:387-389: the story header is the single writable copy, values "read fresh at each pass, never a remembered or copied value". No sentence contradicts the rule. A snapshot at first-cycle-open was rejected on quote 4, which forbids it in those words, and because it points the wrong way on invariant 2: under a snapshot a human-confirmed RAISE between cycles would leave in-flight work reviewed under the weaker profile, which is the under-review direction. WHAT THE VERIFICATION CHANGED, and it is the useful part: pass-4 M3's premise - that no rule handles a between-cycle change - was partly wrong, and so was my accepting it. Quote 3 shows the spec DELEGATED that question to §5 rather than leaving it open. I read a delegation as a gap, invented an answer, and the invented answer then contradicted §2. The narrow lesson: a finding that says "no rule covers X" needs the same verification as one that says "rule Y is wrong" - namely that no rule covers it. Also recorded: the routed reading cited "§2.4/§4.2". There is no §4.2 - §4 has no subsections. Substance unaffected, citation corrected. Task 1's do-not-execute note is removed; the task now carries the rule's derivation and the quotes it rests on. All 13 assert-new patterns re-verified: 0/0 before their task, 1/1 after, both copies. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index e945420..c8b5005 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -170,7 +170,11 @@ plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with > The text on disk ends **mid-line**: ` Open a TodoWrite "Codex pass N" per pass;` continues the same line after `review.` Match exactly this and no more; what follows stays. > > -> > **The between-cycle sentence is OPEN, not settled.** Pass-4 M3 found that "one derived value governs all three cycles" is undefined across an ordinary profile edit between cycles. Revision 5 answered by weakening the promise to a per-cycle derivation — and pass-5 M1 showed that contradicts spec §2, which says **one derived value** governs all three. The text below is revision 5's wording and is **awaiting a decision on whether the spec or the plan changes**; do not execute this task until that is resolved. +> > **The between-cycle rule implements spec §2's one-value sentence, verified against revision 36 before it was written.** §2 says the value is one *"because they derive from **the same cited-story set**"* — a claim about the **source**, not about freezing a number in time. So the value is a function of that set's **current** confirmed profiles, read fresh wherever §5 already requires reading them: one source, therefore exactly one value at any moment. +> > +> > **A snapshot taken once at the first cycle's open was considered and rejected.** It would be "a remembered or copied value", which §5's Profiles section forbids in those words — *"the story header is the single writable copy … read the values fresh at each pass, never a remembered or copied value"* — and it points the wrong way on invariant 2: a human-confirmed **raise** between cycles would then leave work still in flight reviewed under the weaker profile, which is the under-review direction. +> > +> > **§2.4 does not merely permit this; it routes the question here.** Its pass-count rules *"apply while §5 says a gate is running and are silent otherwise"*, and it states that *"what §5 says about when a gate runs — including how a moving profile or cited set bears on that — is §5's, unchanged and deliberately not summarised here."* The between-cycle case was never a spec gap. It was delegated, and §5's read-fresh rule already answers it. - [ ] **Replace, in both copies.** OLD: @@ -195,13 +199,16 @@ under the existing rule; it does not fall through to 3, because reading it as 3 turn a stop condition into a silent default. Across a cited set the floor is 1 if and only if the set is non-empty and every member is profiled, resolvable and at level 0 — all four conditions, since "every cited story" is vacuously true of an empty set; -no story cited, or any cited story unprofiled, gives 3. One derivation governs all +no story cited, or any cited story unprofiled, gives 3. One derived value governs all three cycles: the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle. Not -because they are one cycle — they are three — but because the same predicate reads the -same cited-story set. Each cycle derives its floor from that set as it stands during its -own passes. A profile or cited-set change between cycles binds the cycles that have not -closed and does not reopen one that has: passes already run keep counting, which is the -existing rule and is not changed here. +because they are one cycle — they are three — but because they derive from the same +cited-story set. That value is a function of the current confirmed profiles of that set, +read fresh wherever this section already requires them to be read, so at any moment there +is exactly one value because there is one source. A change to a profile or to the set +therefore binds every open and future cycle — a raise costs an affected open cycle a +further pass under the current profile, as above — while a cycle that has already closed +stands, its close having been valid under the profile current when it closed, which is the +cycle-level form of passes already run keeping their count. **The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder threshold that controls nothing.** The hook still counts passes, and it still can't read From 1f1aa6fff39c9e3aaaab3c868924fef0f900a946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 10:21:48 +0200 Subject: [PATCH 063/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=208?= =?UTF-8?q?=20=E2=80=94=20give=20the=20cited=20set=20an=20authority=20and?= =?UTF-8?q?=20a=20stop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 6: 2 findings, 0 Blockers, 1 Major, 1 NIT. Curve for the cycle: pass 1: 21, 7B 10M = 17 B+M instrument-heavy pass 2: 16, 6B 8M = 14 B+M pass 3: 16, 6B 7M = 13 B+M 92% instrument -> escalation fired pass 4: 7, 0B 3M = 3 B+M instrument stripped pass 5: 5, 0B 4M = 4 B+M pass 6: 2, 0B 1M = 1 B+M MAJOR - absorbed, not routed, and the reason matters. Revision 7's current-header rule settled which PROFILE VALUES govern: the current confirmed header, read fresh. It did not settle which CITED SET. Nothing gave set membership one authority, and a spec, a plan and a Gate-B input can carry different citations - so two cycles open over disagreeing artifacts could derive 1 and 3 at the same moment while the text claimed one value. Absorbed rather than routed because the spec already contracts one set: §2 says the three cycles derive from "the same cited-story set". The plan was missing what to do when reality does not match that assertion, which is an implementation gap, not a contract change. The fix implements the spec's assertion instead of altering it, and takes the safe direction under invariant 2: The set has one authority - the citations of the artifact under review. Where two cycles of one change would run against artifacts citing different sets, the premise of a single value has failed: stop and surface the disagreement rather than deriving from either, exactly as an unresolvable profile stops rather than defaulting. Had the only available fix been "revise the spec and the story criterion to approve per-cycle sets", that would have been a contract question and would have routed. It was not: a fix that implements the approved sentence existed. NIT - fixed rather than merely collected, because the accounting row was being edited anyway and a record that misstates itself is worth a line. Row 1 said condition b ("Blocker/Major only") was kept verbatim OUTSIDE the replaced range; it is inside Task 1's OLD block and re-emitted in its NEW block. The disposition is now split: b kept verbatim inside the replacement, i-m kept verbatim outside it. Per §5 a Nit earns no repair round of its own, and this one did not get one. Also sharpened, from the same finding's reasoning: the Task 1 note said §2.4 "routes the question here", which could be read as claiming §2.4 supplies the between-cycle answer. It does not - it is silent outside a running gate and says so. It DELEGATES, and §5's read-fresh rule is what answers. All 13 assert-new patterns re-verified: 0/0 before their task, 1/1 after, both copies. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- .../2026-08-29-review-loop-economics-plan-a-rules.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index c8b5005..fb27021 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -139,7 +139,7 @@ produced a false IDENTICAL — a comparison that ended before the divergence. | # | Passage | Copy | What the existing prose requires | Disposition | |---|---|---|---|---| -| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. the hook cannot read findings · e. the hook cannot tell the spec run from the plan run · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1), which also settles what a change between cycles does: it binds cycles not yet closed and never reopens a closed one · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · d–h **kept verbatim** in the second paragraph · b, i–m **kept verbatim**, outside the replaced range | +| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. the hook cannot read findings · e. the hook cannot tell the spec run from the plan run · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1), which also settles what a change between cycles does: it binds cycles not yet closed and never reopens a closed one · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · d–h **kept verbatim** in the second paragraph · **b kept verbatim inside the replacement** (it is in Task 1's OLD block and re-emitted in its NEW block) · i–m **kept verbatim outside the replaced range** | | 2 | `if pass 3 still` | both | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, `pass 3` → `the pass at the floor` (Task 3) | | 3 | `below 3` | both | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, `below 3` → `below the floor` (Task 4) | | 4 | pass-1 Minor sentence | both | below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass below the floor` (Task 9) | @@ -174,7 +174,7 @@ plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with > > > > **A snapshot taken once at the first cycle's open was considered and rejected.** It would be "a remembered or copied value", which §5's Profiles section forbids in those words — *"the story header is the single writable copy … read the values fresh at each pass, never a remembered or copied value"* — and it points the wrong way on invariant 2: a human-confirmed **raise** between cycles would then leave work still in flight reviewed under the weaker profile, which is the under-review direction. > > -> > **§2.4 does not merely permit this; it routes the question here.** Its pass-count rules *"apply while §5 says a gate is running and are silent otherwise"*, and it states that *"what §5 says about when a gate runs — including how a moving profile or cited set bears on that — is §5's, unchanged and deliberately not summarised here."* The between-cycle case was never a spec gap. It was delegated, and §5's read-fresh rule already answers it. +> > **§2.4 does not merely permit this; it routes the question here.** Its pass-count rules *"apply while §5 says a gate is running and are silent otherwise"*, and it states that *"what §5 says about when a gate runs — including how a moving profile or cited set bears on that — is §5's, unchanged and deliberately not summarised here."* The between-cycle case was never a spec gap. §2.4 does not *supply* the answer — it is silent outside a running gate and says so — it **delegates** the question, and §5's read-fresh rule is what answers it. - [ ] **Replace, in both copies.** OLD: @@ -208,7 +208,11 @@ is exactly one value because there is one source. A change to a profile or to th therefore binds every open and future cycle — a raise costs an affected open cycle a further pass under the current profile, as above — while a cycle that has already closed stands, its close having been valid under the profile current when it closed, which is the -cycle-level form of passes already run keeping their count. +cycle-level form of passes already run keeping their count. **The set itself has one +authority: the citations of the artifact under review.** Where two cycles of one change +would run against artifacts citing different sets, the premise of a single value has +failed — **stop and surface the disagreement** rather than deriving from either, exactly as +an unresolvable profile stops rather than defaulting. **The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder threshold that controls nothing.** The hook still counts passes, and it still can't read From 385d6a27f769536575147ed9195fa986c28f7fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 10:34:19 +0200 Subject: [PATCH 064/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=209?= =?UTF-8?q?=20=E2=80=94=20bidirectional=20adoption=20rule;=20one=20Major?= =?UTF-8?q?=20routed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 7: 3 findings, 0 Blockers, 2 Major, 1 NIT. Curve: pass 1: 21, 7B 10M = 17 B+M pass 2: 16, 6B 8M = 14 B+M pass 3: 16, 6B 7M = 13 B+M 92% instrument -> escalation pass 4: 7, 0B 3M = 3 B+M instrument stripped pass 5: 5, 0B 4M = 4 B+M pass 6: 2, 0B 1M = 1 B+M pass 7: 3, 0B 2M = 2 B+M ABSORBED - MAJOR 2, the downstream adoption trigger was one-directional. It stopped when the derived predicate landed while an old fixed-number obligation survived, but not the inverse: a merge can take Task 6's "loop at the derived floor" or Task 8's "derives from the profile and cited set" while Task 1 is absent and the old fixed-three definition remains, leaving a project claiming a derived floor with nothing defining it. /workflow-init explicitly permits a user-selected partial merge, so both states are reachable. Restated as a coherence requirement in both directions: exactly one definition of the floor must be present, and every statement about pass counts or closing must resolve to it. Two states break it - a fixed-number obligation surviving beside the predicate, and any claim or dependency on a derived floor with no predicate present to define it. FIXED - the NIT was a cross-reference pointing at nothing. Task 1 said a raise costs a further pass "as above"; verified mechanically that "further pass" occurs zero times before Task 1 (line 160) and first appears at line 689, inside Task 11. Now points forward to the profile-change rule. Per §5 a Nit earns no repair round of its own; this rode along. ROUTED - MAJOR 1, the third finding on the cited-set axis, and I am not absorbing it again. It asks for one change-level carrier for the governing cited set, with each cycle reconciling against it, and says explicitly that if choosing that carrier is not already a settled contract it should go to the human. It is not settled: the spec asserts one set and never says where it lives, so a carrier is a new mechanism, not an implementation of an approved sentence. §5's rule for a genuinely unclear boundary is to treat the finding as outside, which costs a question rather than a silent expansion. Its supporting example is wrong and I checked before routing: the finding says the spec cites the successor loop-rule story while the plan cites only the pass-floor story. Both artifacts' governing Story headers cite the SAME story; the spec's other mention is line 32, a scope disclaimer stating the consolidation is explicitly not in this spec. So the artifacts do not disagree. The underlying gap survives the bad example: nothing in the shipped text distinguishes a governing citation from an incidental mention, and an agent grepping for story paths would find two in the spec and one in the plan. A narrow fix exists - name the artifact's Story header as the governing citation - and so does a broad one, the carrier plus reconciliation the finding asks for. Choosing between them is the routed question. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index fb27021..0413c85 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -206,7 +206,8 @@ cited-story set. That value is a function of the current confirmed profiles of t read fresh wherever this section already requires them to be read, so at any moment there is exactly one value because there is one source. A change to a profile or to the set therefore binds every open and future cycle — a raise costs an affected open cycle a -further pass under the current profile, as above — while a cycle that has already closed +further pass under the current profile, as the profile-change rule below requires — while a +cycle that has already closed stands, its close having been valid under the profile current when it closed, which is the cycle-level form of passes already run keeping their count. **The set itself has one authority: the citations of the artifact under review.** Where two cycles of one change @@ -849,15 +850,16 @@ actually writes the text — which may write nothing, be declined, or be merged so these rules bind only over the text a project's `CLAUDE.md` actually contains, and a partial adoption can persist undetected. A project taking the floor rule without the severity test gets a floor whose docs-only question the severity test is what settles. -**A partial adoption can also leave the derived floor standing beside the old fixed-number -obligations, which contradict it outright.** The trigger is semantic, not a list of -spellings: **any surviving statement that the floor is a fixed number, or that closing -turns on a specific pass number, sitting beside the derived predicate.** Enumerating the -spellings would miss the ones a partial merge happens to leave — the Gate-A loop -description, the pass-1 closure rule and the re-review rationale each carry one, and a -merge can take some of them and not others. Where a project's text contains both kinds of -rule, nothing here resolves which governs: **stop, and have a human complete or revert the -adoption, before running a gate under it.** What prompt text can do about downstream +**A partial adoption can leave a project's floor undefined or self-contradictory.** The rule +is a coherence requirement, stated semantically rather than as a list of spellings, and it +runs in **both** directions: **exactly one definition of the floor must be present, and every +statement about pass counts or about closing must resolve to it.** Two states break it — +**a fixed-number or specific-pass obligation surviving beside the derived predicate**, and +**any claim or dependency on a derived floor with no predicate present to define it.** A +merge can produce either: the Gate-A loop description, the pass-1 closure rule and the +re-review rationale each carry a fixed-three claim and can be taken or left independently of +the predicate itself. In either state nothing here resolves which rule governs: **stop, and +have a human complete or revert the adoption, before running a gate under it.** What prompt text can do about downstream adoption is limited, and that limit is what this paragraph states. ``` From bdb93607b4b43bc10fd4729bf927c4f6bac35d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 10:36:24 +0200 Subject: [PATCH 065/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=2010?= =?UTF-8?q?=20=E2=80=94=20the=20Story=20header=20is=20the=20governing=20ci?= =?UTF-8?q?tation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 7's routed question is answered: the artifact's `Story:` header is the governing citation; every other story-path mention is incidental and governs nothing. Plan-only, no new mechanism. Grounding verified before encoding, both quotes checked against the files: CLAUDE.md:387-389 - "the story header is the single writable copy - specs, plans, commit bodies and this file's prompts carry the story PATH and read the values fresh at each pass, never a remembered or copied value." CLAUDE.md:466 - "Every Gate-B call and re-review carries the path of every cited story." So this names a convention §5 already practises rather than inventing one, and both artifacts in this change already carry exactly that header, agreeing (spec:4, plan:19). The alternative - a change-level carrier with per-cycle reconciliation - would have built a mechanism for a disagreement that my own verification showed does not exist. Two edges shipped with it, so the rule is complete: 1. MULTI-STORY. The Story header carries the path of EVERY cited story. The floor's unanimity rule and the lens union both read the whole set, and §5 already requires every Gate-B call to carry every cited path. One header, N paths, no other source. 2. THE GREP RULE, stated as a negative too. An agent deriving the set reads the Story header ONLY and does not grep the body for story paths, because a grep finds mentions and cannot tell a citation from a disclaimer. A body mention - including a sentence placing a story OUTSIDE this change's scope - is not a citation. The negative is not hypothetical. Pass 7's finding claimed the spec and the plan cite different story sets; it had grepped the spec body and found two paths, the second being line 32, a sentence stating the loop-rule consolidation is NOT in this spec. The rule above is precisely what prevents that error, and the error that motivated it was made by a reviewer reading these artifacts. The disagreement stop is kept and tightened: where the HEADERS of two artifacts in one change name different sets, stop and surface rather than deriving from either. All 13 assert-new patterns re-verified: 0/0 before their task, 1/1 after, both copies. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...026-08-29-review-loop-economics-plan-a-rules.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index 0413c85..4c83500 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -209,11 +209,15 @@ therefore binds every open and future cycle — a raise costs an affected open c further pass under the current profile, as the profile-change rule below requires — while a cycle that has already closed stands, its close having been valid under the profile current when it closed, which is the -cycle-level form of passes already run keeping their count. **The set itself has one -authority: the citations of the artifact under review.** Where two cycles of one change -would run against artifacts citing different sets, the premise of a single value has -failed — **stop and surface the disagreement** rather than deriving from either, exactly as -an unresolvable profile stops rather than defaulting. +cycle-level form of passes already run keeping their count. **The set has one authority: +the artifact's `Story:` header, which carries the path of every cited story.** Nothing else +is a citation. A story path appearing anywhere else in an artifact's body — including a +sentence placing a story *outside* this change's scope — governs nothing, and **an agent +deriving the set reads that header and does not grep the body for story paths**, because a +grep finds mentions and cannot tell a citation from a disclaimer. Where the headers of two +artifacts in one change name different sets, the premise of a single value has failed: +**stop and surface the disagreement** rather than deriving from either, exactly as an +unresolvable profile stops rather than defaulting. **The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder threshold that controls nothing.** The hook still counts passes, and it still can't read From 3b95e1042daf6f5356ff8c3832c645e46da63f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 10:51:18 +0200 Subject: [PATCH 066/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=2011?= =?UTF-8?q?=20=E2=80=94=20withdraw=20an=20out-of-scope=20change=20to=20the?= =?UTF-8?q?=20loop=20rules?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 8: 4 findings, 0 Blockers, 2 Major, 2 Minor. WITHDRAWN - MAJOR 1, and this one is a scope error I made, not a drafting one. Revision 6 added a precedence rule to Task 12 saying that where a pass is Blocker/Major-free at or above the floor, the two-tell threshold alone does not bar completion. I wrote it to resolve a real collision pass 4 found between the new demotion and the existing five-tells rule. It is out of scope, and the spec says so in as many words. §9 Out of scope: "the §5 loop-rule consolidation AND EVERYTHING ITS SUCCESSOR STORY OWNS". The five-tells rule is one of those. The story records the same narrowing at line 72. So I shipped a change to a rule this change is explicitly forbidden to touch - and the untouched text I was overriding reads "Any two present makes stop-and-surface MANDATORY, NOT DISCRETIONARY". The accounting made it worse by recording the two-tell condition as untouched while the shipped text changed how it concludes. Removed. Task 12 now states the interaction and defers it: the collision is real, this change does not settle it, the loop rules are outside its scope, and the two-tell rule therefore stands exactly as written. That is a description of the status quo, not a decision - and it lands on the conservative side, which is the right direction for a rule about when to stop. The interaction is an input the successor story inherits. Accounting row 11 now records this honestly instead of claiming the tells were untouched. Removing an out-of-scope change needed no decision from anyone, so this was not routed. What is worth surfacing is that it happened at all: this is the second scope error in the cycle, after the cited-set carrier, and both times the pull was the same - a real defect existed, a fix was available, and the fix reached past the assigned boundary. ABSORBED - MAJOR 2. The Story-header authority was not operationally complete. A spec and a plan have headers; a Gate-B cycle reviews a diff and has none. Each cycle's governing header is now named: the spec's for the Gate-A spec loop, the plan's for the Gate-A plan loop, and the plan's for Gate B, which the Gate-B call must carry in full - which §5 already requires of every cited path. The comparison point is named too: before each pass, the deriving agent compares every governing header that then exists. MINOR 3 and 4 rode along rather than earning a repair round. The coherence rule listed two breaking states as if exhaustive and missed zero definitions and two; the list is now four and explicitly non-exhaustive. It also said "every statement about pass counts" must resolve to the floor, which would have stopped a coherent project because the hook's reminder threshold or a historical pass number does not - it is now limited to NORMATIVE statements, with thresholds and descriptive measurements explicitly outside. All 13 assert-new patterns re-verified: 0/0 before their task, 1/1 after, both copies. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index 4c83500..2ba04f6 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -151,7 +151,7 @@ produced a false IDENTICAL — a comparison that ended before the divergence. | 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept, rationale replaced** — both lines, claim named (Task 7) | | 9 | Lenses | both | a. lenses are different questions, not more passes · b. the 3-pass floor is unchanged · c. the Blocker/Major filter is unchanged · d. the file-first protocol is unchanged · e. the clean-final-pass rule is unchanged | a **kept and sharpened** · **b deliberately dropped and replaced by its negation** — the floor is precisely what this change makes variable, so the sentence now says so · c, d, e **kept verbatim** (Task 8) | | 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules appended after them, never merged in (Task 11) | -| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them, together with its precedence against the five-tells rule — a demoted finding still counts toward the tells, and a Blocker/Major-free pass at or above the floor still closes (Task 12) | +| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them, together with the per-tell consequence of demotion — every reported finding still counts toward the total, the clusters and the require↔withdraw comparison, while the Blocker curve reads severity after the ceiling. **The five-tells rule's own conclusion is NOT changed**: it is a §5 loop rule, which spec §9 places out of scope, so Task 12 states the interaction and defers it to the successor story (Task 12) | **Nothing in §5 outside these eleven passages is edited.** Gate B, reviewing the combined diff, is what confirms that against this table. @@ -214,9 +214,13 @@ the artifact's `Story:` header, which carries the path of every cited story.** N is a citation. A story path appearing anywhere else in an artifact's body — including a sentence placing a story *outside* this change's scope — governs nothing, and **an agent deriving the set reads that header and does not grep the body for story paths**, because a -grep finds mentions and cannot tell a citation from a disclaimer. Where the headers of two -artifacts in one change name different sets, the premise of a single value has failed: -**stop and surface the disagreement** rather than deriving from either, exactly as an +grep finds mentions and cannot tell a citation from a disclaimer. **Each cycle's governing header is the +header of the artifact it reviews**: the spec's for the Gate-A spec loop, the plan's for the +Gate-A plan loop, and — since a Gate-B cycle reviews a diff and has no header of its own — +**the plan's, which the Gate-B call must carry in full**, as this section already requires of +every cited path. **Before each pass, the deriving agent compares every governing header that +exists at that moment.** Where they name different sets the premise of a single value has +failed: **stop and surface the disagreement** rather than deriving from either, exactly as an unresolvable profile stops rather than defaulting. **The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder @@ -787,11 +791,11 @@ NEW: is applied**, so a finding demoted to Minor leaves that series, which is what demoting it is for. - **Against the two-tell stop only:** where a pass is Blocker/Major-free at or above the - floor **and no other rule here requires a stop**, the two-tell threshold alone does not - bar completion — it governs a loop still running, not one that has finished. It overrides - nothing else. A finding that leaves the assigned fix set, or opens a new structural or - contract question, still stops the cycle whatever severity it carries. + **What this does not decide.** Demotion and the five-tells rule interact: a pass can be + Blocker/Major-free at or above the floor while two tells are present. **This change does + not settle that interaction and does not weaken either rule.** The loop rules are outside + its scope by decision, and the two-tell rule therefore stands exactly as written — two + tells make stop-and-surface mandatory, not discretionary. ``` - [ ] **Assert the new text is present.** @@ -857,9 +861,13 @@ severity test gets a floor whose docs-only question the severity test is what se **A partial adoption can leave a project's floor undefined or self-contradictory.** The rule is a coherence requirement, stated semantically rather than as a list of spellings, and it runs in **both** directions: **exactly one definition of the floor must be present, and every -statement about pass counts or about closing must resolve to it.** Two states break it — -**a fixed-number or specific-pass obligation surviving beside the derived predicate**, and -**any claim or dependency on a derived floor with no predicate present to define it.** A +NORMATIVE statement — one that sets how many passes a gate owes, or when a cycle may close — +must resolve to it.** The hook's reminder threshold and any descriptive or historical pass +number are outside this: they state what a tool says or what once happened, not what a cycle +owes. Four states break it, and the list is **not exhaustive**: a fixed-number or +specific-pass obligation surviving beside the derived predicate; a claim or dependency on a +derived floor with no predicate to define it; **no definition at all**; and **two definitions +at once**. A merge can produce either: the Gate-A loop description, the pass-1 closure rule and the re-review rationale each carry a fixed-three claim and can be taken or left independently of the predicate itself. In either state nothing here resolves which rule governs: **stop, and From fbc63f1960ffb24e71b879a823f7fa4fe85260a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 11:14:55 +0200 Subject: [PATCH 067/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=2012?= =?UTF-8?q?=20=E2=80=94=20Task=2012=20ships=20spec=20=C2=A73=20and=20nothi?= =?UTF-8?q?ng=20else?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 9 was a mandatory stop: 6 findings, 5 Major, and three of the five tells present - findings rising (2, 3, 4, 6), half of them the plan's own prose contradicting its shipped text, and a require-withdraw CHAIN rather than a pair. Decision on the stop: ship §3 only and mark the deferral. TASK 12 REDUCED. It now ships exactly what spec §3 licenses - the severity procedure, the exclusions, the symmetric instrument carve-out, the rationale rule, coverage-first, the kinship sentence. Every statement about what demotion does to the finding total, the clusters, the Blocker curve or the two-tell stop is gone, PREMISES INCLUDED. Revision 11 removed the conclusion I had drawn and kept the premises; pass 9 showed those are equally §4 and successor-story material, and on the text it was right - "the Blocker curve reads severity after the ceiling" is a statement about the curve, and the curve is Plan B's. Verified: the shipped text now adds nothing about curves, clusters or tells. The one remaining match in the edited file is pre-existing - the "clearly stuck" paragraph's own "Blocker curve across passes", which this change does not touch, and the unedited file matches it identically. THE DEFERRAL IS MARKED, NOT SILENT. One sentence at the demotion rule names docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md as where the interaction is settled. That is §9's own deferral pattern - a scope disclaimer rather than a rule - and under the governing-header decision a body mention is not a citation, so it creates no scope leak. THE SUCCESSOR STORY GAINS THE RECIPROCAL, in its §4 settled-inputs section beside the three obligations both accountings already share. Named in both documents so it cannot fall between them, which is exactly the failure the passage list demonstrated in this same cycle. It also states what the design owes: whether a demoted finding still counts toward the total and the clusters, and whether the Blocker curve reads severity before or after the ceiling. THE TWO SURVIVING FINDINGS, both in-set, both repaired: - #1 the combined Gate-B cycle's governing citation is the UNION of the Story headers of every plan contributing to the reviewed diff - one header rule applied to a set of three artifacts. Revision 11 said "the plan's", singular, while Gate B reviews a combined A+B+C diff. - #4 the header and profile are re-read once more before a clean pass is accepted as the cycle's FINAL pass. A change found there means the pass is not final. Without it a concurrent change during the last pass was invisible, contradicting the promise that every change binds an open cycle. TWO MORE RODE ALONG, both corrections of my own earlier fixes: - The coherence rule over-triggered. "Every normative statement about when a cycle may close must resolve to the floor" would have stopped a correctly adopted §5, because its other closure predicates - assigned-fix-set membership, a new structural question, an accepted Blocker or Major, the tell thresholds - are independent of the floor by design. Now limited to statements that state or assume a pass count, with the independent predicates named as outside it. - My two-states-to-four expansion left "a merge can produce either" and "in either state" in the sentences below it, so the two states I added were listed but not unambiguously routed to the stop. Both now read "any of them" / "any such state". All 13 assert-new patterns re-verified: 0/0 before their task, 1/1 after, both copies. Gate B: N/A - docs/superpowers/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 42 +++++++++---------- ...026-08-29-loop-rule-consolidation-story.md | 13 ++++++ 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index 2ba04f6..1d4b793 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -151,7 +151,7 @@ produced a false IDENTICAL — a comparison that ended before the divergence. | 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept, rationale replaced** — both lines, claim named (Task 7) | | 9 | Lenses | both | a. lenses are different questions, not more passes · b. the 3-pass floor is unchanged · c. the Blocker/Major filter is unchanged · d. the file-first protocol is unchanged · e. the clean-final-pass rule is unchanged | a **kept and sharpened** · **b deliberately dropped and replaced by its negation** — the floor is precisely what this change makes variable, so the sentence now says so · c, d, e **kept verbatim** (Task 8) | | 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules appended after them, never merged in (Task 11) | -| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them, together with the per-tell consequence of demotion — every reported finding still counts toward the total, the clusters and the require↔withdraw comparison, while the Blocker curve reads severity after the ceiling. **The five-tells rule's own conclusion is NOT changed**: it is a §5 loop rule, which spec §9 places out of scope, so Task 12 states the interaction and defers it to the successor story (Task 12) | +| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them, **and nothing else**. Task 12 ships spec §3 alone; it states no consequence for the per-pass counts, the clusters or the stop thresholds, and chooses no precedence against any loop rule — all of that is §4 and successor-story material that spec §9 excludes. One sentence names where the interaction is settled, which is a scope disclaimer rather than a rule (Task 12) | **Nothing in §5 outside these eleven passages is edited.** Gate B, reviewing the combined diff, is what confirms that against this table. @@ -217,9 +217,11 @@ deriving the set reads that header and does not grep the body for story paths**, grep finds mentions and cannot tell a citation from a disclaimer. **Each cycle's governing header is the header of the artifact it reviews**: the spec's for the Gate-A spec loop, the plan's for the Gate-A plan loop, and — since a Gate-B cycle reviews a diff and has no header of its own — -**the plan's, which the Gate-B call must carry in full**, as this section already requires of -every cited path. **Before each pass, the deriving agent compares every governing header that -exists at that moment.** Where they name different sets the premise of a single value has +**the union of the `Story:` headers of every plan contributing to that diff, which the Gate-B +call must carry in full**, as this section already requires of every cited path. **Before each pass, the deriving agent compares every governing header that +exists at that moment, and again before a clean pass is accepted as the cycle's final pass.** +A header or profile that changed during that pass means the pass is not final — the same +answer a change gets at every other read point. Where they name different sets the premise of a single value has failed: **stop and surface the disagreement** rather than deriving from either, exactly as an unresolvable profile stops rather than defaulting. @@ -747,7 +749,7 @@ plugins/dev-workflow/commands/workflow-init.md:675: rework) → both must resol > The four severity definitions stay verbatim; the reachability test is appended as the procedure that sets a **ceiling** on them. > > -> > The last two paragraphs settle how demotion interacts with the untouched five-tells rule. **Per tell**, because a blanket claim was wrong in one direction: demoting a Blocker to Minor *does* remove it from the Blocker curve — that is the point — while the total, the clusters and the require↔withdraw comparison still see every reported finding. And the precedence is **against the two-tell stop only**: a finding that leaves the assigned fix set or opens a new structural question still stops the cycle at any severity. +> > **Task 12 ships spec §3 and nothing else** — the procedure, the exclusions, the symmetric instrument carve-out, the rationale rule, coverage-first and the kinship sentence. It chooses no precedence against any loop rule and states no consequence for the per-pass counts, the clusters or the stop thresholds. Earlier revisions did, three times, each time reaching past spec §9's exclusion of "the §5 loop-rule consolidation and everything its successor story owns", and each removal found another layer underneath. What remains is one sentence naming where that interaction is settled — a scope disclaimer in §9's own pattern, not a rule, and a body mention rather than a citation. - [ ] **Replace, in both copies.** OLD: @@ -784,18 +786,9 @@ NEW: This is the finding-level analog of the path-level prose exemption: one principle at two granularities — text that *describes* the product versus text that *is* the product. - **Demotion changes which findings gate. Per tell:** every reported finding, demoted or - not, counts toward the finding total, toward the instrument and prose clusters, and - toward the require↔withdraw comparison — those measure where a loop's attention is - going, not how much of it blocks. **The Blocker curve reads severity after this ceiling - is applied**, so a finding demoted to Minor leaves that series, which is what demoting it - is for. - - **What this does not decide.** Demotion and the five-tells rule interact: a pass can be - Blocker/Major-free at or above the floor while two tells are present. **This change does - not settle that interaction and does not weaken either rule.** The loop rules are outside - its scope by decision, and the two-tell rule therefore stands exactly as written — two - tells make stop-and-surface mandatory, not discretionary. + **How this demotion bears on the loop-health measures — the per-pass counts, the finding + clusters and the stop thresholds — is settled by + `docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md`, not here.** ``` - [ ] **Assert the new text is present.** @@ -861,16 +854,19 @@ severity test gets a floor whose docs-only question the severity test is what se **A partial adoption can leave a project's floor undefined or self-contradictory.** The rule is a coherence requirement, stated semantically rather than as a list of spellings, and it runs in **both** directions: **exactly one definition of the floor must be present, and every -NORMATIVE statement — one that sets how many passes a gate owes, or when a cycle may close — -must resolve to it.** The hook's reminder threshold and any descriptive or historical pass -number are outside this: they state what a tool says or what once happened, not what a cycle -owes. Four states break it, and the list is **not exhaustive**: a fixed-number or +NORMATIVE statement that states or assumes a pass count — a numeric floor, a specific pass +ordinal, or a dependency on the derived floor — must resolve to it.** Two kinds of statement +are outside this and are not required to derive from the floor: **the other closure and stop +predicates**, which are independent of it by design — assigned-fix-set membership, a new +structural question, an accepted Blocker or Major, the tell thresholds — and **the hook's +reminder threshold together with any descriptive or historical pass number**, which state +what a tool says or what once happened rather than what a cycle owes. Four states break it, and the list is **not exhaustive**: a fixed-number or specific-pass obligation surviving beside the derived predicate; a claim or dependency on a derived floor with no predicate to define it; **no definition at all**; and **two definitions at once**. A -merge can produce either: the Gate-A loop description, the pass-1 closure rule and the +merge can produce any of them: the Gate-A loop description, the pass-1 closure rule and the re-review rationale each carry a fixed-three claim and can be taken or left independently of -the predicate itself. In either state nothing here resolves which rule governs: **stop, and +the predicate itself. In any such state nothing here resolves which rule governs: **stop, and have a human complete or revert the adoption, before running a gate under it.** What prompt text can do about downstream adoption is limited, and that limit is what this paragraph states. ``` diff --git a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md index ce26909..e5462c8 100644 --- a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md +++ b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md @@ -124,6 +124,19 @@ A third, for the same reason: the **clearly-stuck paragraph** is rewritten by bo because its "pass 1 carrying a Minor" sentence is false under a floor of 1, and by this story because the exit becomes a suspension in the ordering. **Both accountings owe it.** +**A fourth, handed over rather than shared — the demotion/loop-health interaction.** The parent's +severity test demotes a finding when nothing in the system takes a different decision from it. +That bears directly on the loop-health measures this story owns: what a demoted finding does to +the per-pass counts, to the finding clusters, and to the stop thresholds. The parent **does not +settle it** — three of its revisions tried, each reaching past its own §9 exclusion of "the §5 +loop-rule consolidation and everything its successor story owns", and each removal found another +layer underneath. What it ships is one sentence naming this story as where the interaction is +settled, and **this paragraph is the reciprocal**: the obligation is named in both documents so it +cannot fall between them, which is the failure the passage list demonstrated in that same cycle. +The design owes an answer covering at least: whether a demoted finding still counts toward the +finding total and the clusters, and whether the Blocker curve reads severity before or after the +ceiling. + ## 5. Open questions - **The profile.** Proposed `high / none / battery+check+verification`, on the same reasoning the From 7208756db87944b42eff1b29dbe1ff215ebb0aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 11:27:55 +0200 Subject: [PATCH 068/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=2013?= =?UTF-8?q?=20=E2=80=94=20the=20deferral=20must=20not=20ship=20a=20repo-lo?= =?UTF-8?q?cal=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 10: 6 findings, 1 BLOCKER, 5 Major, after six passes at zero Blockers. THE BLOCKER, and it is one I should have caught when I added the sentence. Revision 12's deferral cited docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md in the SHIPPED text - which includes the inline /workflow-init template. That template writes a CLAUDE.md into somebody else's repository, where that path does not exist and is never scaffolded (invariant 7, and the architecture boundary saying nothing may depend on this repo's internal layout). Every initialized downstream project would have carried an unresolvable authority for part of its own gate semantics. Fixed as the plan's ONE deliberate divergence between the copies, stated because the Global Constraints require any divergence to be: both copies carry the disclaimer "not settled here, and this change does not settle it"; CLAUDE.md alone adds the pointer to the successor story. The successor story already carries the reciprocal, so the handoff is named on both sides regardless of which copy a reader has. Verified: the template now contains zero references to that path, CLAUDE.md one. This needed a per-file edit, so the simulation's apply step now supports edits restricted to one copy, and the accounting records the divergence. THE FIVE MAJORS, all in-set, all corrections of last revision's text: - The deferral said the interaction "is settled by" the successor story. It is not - that story says the parent does not settle it and that the successor design still OWES an answer. A reader would have followed the reference expecting an answer and found an obligation. Now "is not settled here". - "At any moment there is exactly one value" was an unconditional claim sitting above two states that derive NO value - disagreeing headers, and a present but unresolvable profile, both of which stop. Exactly the overclaim class AGENTS.md forbids. Now qualified, with both no-value states named. - Task 1 said a body story path "governs nothing" while Task 12 used a body path to name where the interaction is handed off - mutually exclusive instructions. Narrowed to what was actually meant: a body path contributes nothing to the cited set and nothing to the floor derivation, and may still be a cross- reference for any other purpose. - The header model had no result for an absent or repeated Story header. Now every expected artifact contributes a set, absence contributes the EMPTY set rather than dropping out of the comparison, identical repeats are one entry, differing entries are two members, and an unreadable header is malformed and stops. - The coherence rule still over-triggered. It covered every normative pass ordinal while exempting only closure predicates, hook thresholds and historical numbers - so a correct adoption would have been declared incoherent by its own reporting duties, including Task 10's "from pass 1 onward" and the untouched "From pass 4 onward". Now limited to statements that define or constrain the floor or make closing depend on it, with independent reporting and diagnostic ordinals named as outside it. All 13 assert-new patterns re-verified: 0/0 before their task, 1/1 after, both copies. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index 1d4b793..34b922c 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -151,7 +151,7 @@ produced a false IDENTICAL — a comparison that ended before the divergence. | 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept, rationale replaced** — both lines, claim named (Task 7) | | 9 | Lenses | both | a. lenses are different questions, not more passes · b. the 3-pass floor is unchanged · c. the Blocker/Major filter is unchanged · d. the file-first protocol is unchanged · e. the clean-final-pass rule is unchanged | a **kept and sharpened** · **b deliberately dropped and replaced by its negation** — the floor is precisely what this change makes variable, so the sentence now says so · c, d, e **kept verbatim** (Task 8) | | 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules appended after them, never merged in (Task 11) | -| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them, **and nothing else**. Task 12 ships spec §3 alone; it states no consequence for the per-pass counts, the clusters or the stop thresholds, and chooses no precedence against any loop rule — all of that is §4 and successor-story material that spec §9 excludes. One sentence names where the interaction is settled, which is a scope disclaimer rather than a rule (Task 12) | +| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them, **and nothing else**. Task 12 ships spec §3 alone; it states no consequence for the per-pass counts, the clusters or the stop thresholds, and chooses no precedence against any loop rule — all of that is §4 and successor-story material that spec §9 excludes. One sentence says the interaction is not settled here — a scope disclaimer rather than a rule. **`CLAUDE.md` alone adds the successor story's path**; the template must not, since it scaffolds into repositories where that path does not exist (Task 12) | **Nothing in §5 outside these eleven passages is edited.** Gate B, reviewing the combined diff, is what confirms that against this table. @@ -203,8 +203,10 @@ no story cited, or any cited story unprofiled, gives 3. One derived value govern three cycles: the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle. Not because they are one cycle — they are three — but because they derive from the same cited-story set. That value is a function of the current confirmed profiles of that set, -read fresh wherever this section already requires them to be read, so at any moment there -is exactly one value because there is one source. A change to a profile or to the set +read fresh wherever this section already requires them to be read, so wherever a value can be +derived at all there is exactly one, because there is one source. Two states below derive **no** +value rather than a second one: governing headers that disagree, and a cited profile that is +present but unresolvable. Both stop. A change to a profile or to the set therefore binds every open and future cycle — a raise costs an affected open cycle a further pass under the current profile, as the profile-change rule below requires — while a cycle that has already closed @@ -212,14 +214,20 @@ stands, its close having been valid under the profile current when it closed, wh cycle-level form of passes already run keeping their count. **The set has one authority: the artifact's `Story:` header, which carries the path of every cited story.** Nothing else is a citation. A story path appearing anywhere else in an artifact's body — including a -sentence placing a story *outside* this change's scope — governs nothing, and **an agent +sentence placing a story *outside* this change's scope — **contributes nothing to the cited +set and nothing to the floor derivation**, which is the only claim made about it; it may still +be a perfectly good cross-reference for any other purpose. And **an agent deriving the set reads that header and does not grep the body for story paths**, because a grep finds mentions and cannot tell a citation from a disclaimer. **Each cycle's governing header is the header of the artifact it reviews**: the spec's for the Gate-A spec loop, the plan's for the Gate-A plan loop, and — since a Gate-B cycle reviews a diff and has no header of its own — **the union of the `Story:` headers of every plan contributing to that diff, which the Gate-B -call must carry in full**, as this section already requires of every cited path. **Before each pass, the deriving agent compares every governing header that -exists at that moment, and again before a clean pass is accepted as the cycle's final pass.** +call must carry in full**, as this section already requires of every cited path. **Every expected artifact contributes a set — a spec, and every plan contributing to the +reviewed diff — and an expected artifact whose `Story:` header is absent contributes the empty +set rather than dropping out of the comparison.** Within one header, identical repeated paths +are one entry; two entries naming different stories are two members, and a header that cannot +be read as a list of paths is malformed and stops. **Before each pass the deriving agent +compares every such set, and again before a clean pass is accepted as the cycle's final pass.** A header or profile that changed during that pass means the pass is not final — the same answer a change gets at every other read point. Where they name different sets the premise of a single value has failed: **stop and surface the disagreement** rather than deriving from either, exactly as an @@ -749,7 +757,9 @@ plugins/dev-workflow/commands/workflow-init.md:675: rework) → both must resol > The four severity definitions stay verbatim; the reachability test is appended as the procedure that sets a **ceiling** on them. > > -> > **Task 12 ships spec §3 and nothing else** — the procedure, the exclusions, the symmetric instrument carve-out, the rationale rule, coverage-first and the kinship sentence. It chooses no precedence against any loop rule and states no consequence for the per-pass counts, the clusters or the stop thresholds. Earlier revisions did, three times, each time reaching past spec §9's exclusion of "the §5 loop-rule consolidation and everything its successor story owns", and each removal found another layer underneath. What remains is one sentence naming where that interaction is settled — a scope disclaimer in §9's own pattern, not a rule, and a body mention rather than a citation. +> > **Task 12 ships spec §3 and nothing else** — the procedure, the exclusions, the symmetric instrument carve-out, the rationale rule, coverage-first and the kinship sentence. It chooses no precedence against any loop rule and states no consequence for the per-pass counts, the clusters or the stop thresholds. Earlier revisions did, three times, each time reaching past spec §9's exclusion of "the §5 loop-rule consolidation and everything its successor story owns", and each removal found another layer underneath. What remains is one sentence saying the interaction is **not settled here** — a scope disclaimer in §9's own pattern, not a rule. +> > +> > **This is the plan's one deliberate divergence between the copies, and it is stated because the constraint above requires that.** `CLAUDE.md` adds a second sentence naming the successor story's path; the scaffolded template does **not**. The template writes a `CLAUDE.md` into somebody else's repository, where `docs/superpowers/stories/…` does not exist and is never scaffolded — invariant 7 and the architecture boundary both say so. A shipped scaffold citing a path only this checkout has would make every initialized project carry an unresolvable authority for part of its own gate semantics. Both copies carry the disclaimer; only this repo's copy carries the pointer, and the successor story carries the reciprocal so the handoff is named on both sides regardless. - [ ] **Replace, in both copies.** OLD: @@ -787,8 +797,7 @@ NEW: granularities — text that *describes* the product versus text that *is* the product. **How this demotion bears on the loop-health measures — the per-pass counts, the finding - clusters and the stop thresholds — is settled by - `docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md`, not here.** + clusters and the stop thresholds — is not settled here, and this change does not settle it.** ``` - [ ] **Assert the new text is present.** @@ -854,13 +863,13 @@ severity test gets a floor whose docs-only question the severity test is what se **A partial adoption can leave a project's floor undefined or self-contradictory.** The rule is a coherence requirement, stated semantically rather than as a list of spellings, and it runs in **both** directions: **exactly one definition of the floor must be present, and every -NORMATIVE statement that states or assumes a pass count — a numeric floor, a specific pass -ordinal, or a dependency on the derived floor — must resolve to it.** Two kinds of statement -are outside this and are not required to derive from the floor: **the other closure and stop -predicates**, which are independent of it by design — assigned-fix-set membership, a new -structural question, an accepted Blocker or Major, the tell thresholds — and **the hook's -reminder threshold together with any descriptive or historical pass number**, which state -what a tool says or what once happened rather than what a cycle owes. Four states break it, and the list is **not exhaustive**: a fixed-number or +statement that defines or constrains the floor, or makes closing depend on it, must resolve to +that one definition.** Everything else keeps its own footing and is **not** required to derive +from the floor: **the other closure and stop predicates** — assigned-fix-set membership, a new +structural question, an accepted Blocker or Major, the tell thresholds; **independent reporting +and diagnostic ordinals**, such as a duty owed from a given pass onward; and **the hook's +reminder threshold together with any descriptive or historical pass number**, which say what a +tool reports or what once happened rather than what a cycle owes. Four states break it, and the list is **not exhaustive**: a fixed-number or specific-pass obligation surviving beside the derived predicate; a claim or dependency on a derived floor with no predicate to define it; **no definition at all**; and **two definitions at once**. A From 89056e69b50e9c9a12a2b057fdede1f1d88c65e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 11:48:38 +0200 Subject: [PATCH 069/117] =?UTF-8?q?docs(plan):=20Plan=20A=20revision=2014?= =?UTF-8?q?=20=E2=80=94=20strip=20the=20declaration=20layer;=20fix=20all?= =?UTF-8?q?=20eight?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 11 was the second mandatory stop in three passes. Decision: one more round, then Plan A's Gate-A cycle ends at pass 12 either way. THE BLOCKER, fixed. Revision 13 DECLARED a CLAUDE.md-only divergence in prose and never emitted it as a step, so executing the plan produced identical text in both copies and no pointer anywhere. It is now Task 14, with its own OLD/NEW text and an asymmetric check - 1 in CLAUDE.md, 0 in the template - where the second number is as load-bearing as the first. Verified. THE DECLARATION LAYER IS DELETED, not reconciled. Same cure as passes 4 and 10, applied to the last self-referential layer: the architecture's "all thirteen edits mirrored", the standalone constraint declarations, the curve table, the File Structure section and the rationale prose that kept disagreeing with the shipped text are gone. What survives is what binds an executor or records a decision: the tasks, the accounting table, the Story header, the global constraints, the commit protocol, and Plan C's inherited obligations. A plan does not need to describe itself; it needs to be executable and accounted. THE ACCOUNTING IS CORRECTED RATHER THAN DELETED - it is the plan's guard, and it was overclaiming twice: - Row 1 said conditions d-h were "kept verbatim". Task 1 re-emits them around the new precedence, so they are kept IN SUBSTANCE and rewritten in wording. Now says that. - Row 8 said the old rationale was "kept". Task 7 deliberately DROPS it - both the hook-invalidation claim and the framing that put the hook in the causal position - and replaces it. Now recorded as a drop with what replaced it, which is what a dropped condition is supposed to look like in this table. THE OTHER SIX: - The no-value states were stated as exactly two while a third existed - an unreadable Story header. Now three, each stopping and reporting its own cause. - The coherence rule would have flagged the unknown-start fallback floor of 3 as a competing definition. It is explicitly conditional and governs only that state, so it coexists rather than competes. Said so. - The template's deferral named no authority it could reach and no terminal action, leaving the shipped gate nondeterministic at a reachable state. It now carries the conservative action: a pass whose outcome would turn on the unsettled question reports it and stops rather than deciding it. - The successor story's reciprocal still described the parent as naming it "where the interaction is settled". It does not - it says not settled here, and only this repo's copy names the owner. Corrected in the story. - The multi-story header had no syntax. One path per entry; exact duplicates are one member; unreadable is malformed and stops. - NIT: I had cited invariant 7 for the no-downstream-path rule. Invariant 7 governs examples/ as read-only reference and says nothing about story paths in templates. The rule is right; the support is the architecture dependency boundary plus prompt-standards item 11. Corrected - a false citation teaches the next maintainer to lean on a rule that does not cover the case. All 14 assert-new patterns verified against the simulated post-edit tree: 0/0 before their task, 1/1 after, and 1/0 for the deliberately divergent Task 14. Gate B: N/A - docs/superpowers/**.md only, staged set verified. --- ...8-29-review-loop-economics-plan-a-rules.md | 306 ++++++++---------- ...026-08-29-loop-rule-consolidation-story.md | 7 +- 2 files changed, 141 insertions(+), 172 deletions(-) diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index 34b922c..89a10b8 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -8,57 +8,37 @@ pass floor becomes a function of the cited story's profile, and finding severity is decided by whether something in the system takes a different decision. -**Architecture:** Thirteen mirrored prose edits, one per task: `CLAUDE.md` §5 and the inline -template in `/workflow-init`. **No code.** No file under `plugins/dev-workflow/hooks/` changes. - -**Tech Stack:** Markdown prompts; `git` for the records. - **Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). Plan A implements §2, §2.1, §2.2, §2.4, §3, and §10 in part. **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` -> **Read the profile from that header at execution time. This plan states no risk value, no -> security value, no validation mode and no pass count derived from any of them** — not even to -> illustrate a mistake, since quoting a stale-able value to explain why values go stale -> reintroduces it. +> **Read the profile from that header at execution time.** This plan states no risk value, no +> security value, no validation mode and no pass count derived from any of them. --- -## What this plan verifies, and what it does not - -**Each task carries exactly one check: the new text is present at the site.** It fails trivially -before the edit and passes after. That is the whole per-task instrument. +## Plan A of three -**This is a deliberate reduction, decided after three rounds of evidence.** Revisions 1–3 carried -parity loops, count assertions, scoped range extractions, preflight state matrices and staging -proofs. The Gate-A curve across those rounds: - -| pass | findings | Blockers | B+M | of which instrument | -|---|---|---|---|---| -| 1 | 21 | 7 | 17 | most | -| 2 | 16 | 6 | 14 | 5 of 6 Blockers | -| 3 | 16 | 6 | 13 | **12 of 13 B+M — 92%** | +| Plan | Ships | Spec sections | +|---|---|---| +| **A — this one** | the floor predicate and the severity test | §2, §2.1, §2.2, §2.4, §3, §10 (partial) | +| **B** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | +| **C** | rollout: falsified sentences, packaging, the evidence pack, the review loop | §7, §8 | -By pass 3 exactly one Blocker/Major concerned the rules being shipped. The rules were converging -and the verification machinery was diverging: each round's checks grew, and each round the -reviewer found a new way they could report success while the thing they checked was absent or -wrong. The decisive example — Plan A's own Task-2 verification proved the six *old* sentences were -gone and never that the *new* ones had arrived, so **deleting those six sentences outright would -have produced the plan's exact recorded observations.** Executing a check and pasting its true -output does not make it a check that can fail in the direction that matters. +**Three Gate-A cycles, one Gate-B cycle** over the combined A+B+C diff, run and closed by Plan C. +**Execution order A → B → C; Plan B may not open before Plan A's Gate-A loop closes.** -**Where the verification went — nothing is dropped, it is relocated to the artifact that owns it:** +**Plan C inherits these obligations**, which lived in a Gate-B section Plan A no longer has. A +finding whose section moved is relocated, not repaired: -| Obligation | Now discharged by | +| Finding | What Plan C owes | |---|---| -| the edits are correct, complete, and contradict nothing | **Gate B**, reviewing the actual combined A+B+C diff against the spec. This is its job, and it has demonstrably done it better than plan prose: the pass-2 reviewer built a sandbox at `.context/plan-a-pass2-sim/` and the pass-3 reviewer a replay at `.context/pass3_replay.rb`, each executing the plan rather than reading it | -| every old condition kept, moved or deliberately dropped | the **old-conditions accounting** below, closed once, reviewed by this plan's Gate-A cycle — the `AGENTS.md` Don't is satisfied by that record, not by repeated greps | -| the change does what it claims | the **differential named verification** and the **battery**, in Plan C, discharging the story's evidence mode | -| the story's acceptance criteria | checked at the combined close | - -**The two prompt copies staying in parity** is part of what Gate B reviews, and the accounting -below names the two passages where they already diverge so a reviewer is not surprised by them. +| pass-1 M8 | single-branch Gate-B recovery: delete only the failed branch, never both | +| pass-1 M9 | record the floor knob's existence and bytes before the cycle, compare after | +| pass-1 M10 | never `git add -u`; stage an explicitly inspected path set | +| pass-1 MINOR 12 | build the closing body with `mktemp`, not a fixed `/tmp` path | +| pass-1 B6 | evidence revalidated after every fix and again before the closing amend | --- @@ -66,22 +46,21 @@ below names the two passages where they already diverge so a reviewer is not sur - **This Gate-B cycle runs under the rules in force at its start — the OLD ones.** The new severity semantics, floor rule and record forms bind only **after** the closing commit ships - them. This is spec §10's activation rule applied to the change's own review: a reviewer applying - the new Minor-or-below ceiling to the change that introduces it would under-iterate on exactly - the diff needing most iteration. **Carry this sentence in the `additionalContext` of every - Gate-B call.** + them. **Carry this sentence in the `additionalContext` of every Gate-B call**: a reviewer + applying the new Minor-or-below ceiling to the change that introduces it would under-iterate + on exactly the diff needing most iteration. - **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes, and the floor knob - `.context/codex-gate.floor` is never written, never removed, never read for the derivation. This - is not a claim that nothing under `.context/` is written — the Gate-B calls and commit events in - this work write pass counters, fingerprints and disclosure markers there as always. Only the - floor knob is untouched. + `.context/codex-gate.floor` is never written, never removed, never read for the derivation. + This is not a claim that nothing under `.context/` is written — Gate-B calls and commit events + write counters, fingerprints and markers there as always. - **The §5 heading must keep matching `^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`.** `codex-gate.sh:94` greps `CLAUDE.md` for it to build every reminder's citation. -- **Every edit lands in BOTH copies.** Where the two copies already differ, the difference is - pre-existing, is named in the accounting, and is left exactly as it is. +- **Every edit lands in both copies except Task 14**, which is `CLAUDE.md`-only and says why. + Where the two copies already differ, the difference is pre-existing, named in the accounting, + and left as it is. - **§5's other closure rules are never restated, only referred to.** -- **Line numbers are provenance, never instructions.** Each task pastes its `grep -n` anchor as it - stood in the untouched tree; the edit is located by its OLD text. +- **Line numbers are provenance, never instructions.** Each task pastes its `grep -n` anchor as + it stood in the untouched tree; the edit is located by its OLD text. ### The commit protocol @@ -91,70 +70,37 @@ once after Plan C, closed once.** > **Never `git commit --amend --no-edit` inside the cycle.** > `plugins/dev-workflow/hooks/codex-gate.sh:763` recognizes a WIP commit by grepping the **Bash > command string** for `-m ... wip`; an amend without `-m` is not recognized, and the hook resets, -> discarding the cycle's passes. Every amend below restates `-m "WIP: review-loop economics"`. -> (On the backlog: `todos.md`.) +> discarding the cycle's passes. Every amend restates `-m "WIP: review-loop economics"`. -**The Gate-B cycle records its base SHA once, at cycle open, and uses it for every diff and every +**The cycle records its base SHA once, at cycle open, and uses it for every diff and every Gate-B call** — not `HEAD~1`, which moves if a snapshot is ever stacked. Task 1 prints it. -Plans B and C amend the same commit. Plan C adds the manifest bump, runs the single Gate-B loop, -and closes with `git commit --amend -m ""`. - ---- - -## Why this is one of three plans - -| Plan | Ships | Spec sections | -|---|---|---| -| **A — this one** | the floor predicate and the severity test | §2, §2.1, §2.2, §2.4, §3, §10 (partial) | -| **B** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | -| **C** | rollout: falsified sentences, packaging, the evidence pack, the review loop | §7, §8 | - -**Three Gate-A cycles, ONE Gate-B cycle.** Each plan is reviewed as its own artifact; the three -ship **one diff**, reviewed once after Plan C. Execution order A → B → C; Plan B may not open -before Plan A's Gate-A loop closes. - -### What Plan C inherits - -These came from Plan A's earlier Gate-B section, which no longer exists here. A finding whose -section moved is **relocated, not repaired**, so Plan C must carry them explicitly: - -| Finding | What it requires of Plan C | -|---|---| -| pass-1 M8 | single-branch Gate-B recovery: delete only the failed branch, never both | -| pass-1 M9 | record the floor knob's existence and bytes before the cycle, compare after | -| pass-1 M10 | never `git add -u`; stage an explicitly inspected path set | -| pass-1 MINOR 12 | build the closing body with `mktemp`, not a fixed `/tmp` path | -| **pass-1 B6** | evidence revalidated after every fix and again before the closing amend | - --- ## Old-conditions accounting -**Derived from the edits this plan makes**, against §5 at HEAD. **Eleven passages, thirteen rows.** - -**Two passages diverge between the copies and get a row each.** Both divergences were found by -`diff` over the passage's **full extent**, after a 13-line comparison window on the Gate-A passage -produced a false IDENTICAL — a comparison that ended before the divergence. +Derived from the edits this plan makes, against §5 at HEAD. **Eleven passages, thirteen rows.** +Two passages diverge between the copies and get a row each; both divergences were found by +`diff` over the passage's full extent. | # | Passage | Copy | What the existing prose requires | Disposition | |---|---|---|---|---| -| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. the hook cannot read findings · e. the hook cannot tell the spec run from the plan run · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1), which also settles what a change between cycles does: it binds cycles not yet closed and never reopens a closed one · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · d–h **kept verbatim** in the second paragraph · **b kept verbatim inside the replacement** (it is in Task 1's OLD block and re-emitted in its NEW block) · i–m **kept verbatim outside the replaced range** | +| 1 | floor paragraph | both | a. a hard floor of 3 passes per run · b. Blocker/Major only · c. the count is the hook's · d. the hook cannot read findings · e. the hook cannot tell the spec run from the plan run · f. it resets at `writing-plans` · g. therefore Gate A is instruction-backed · h. a satisfied count is not a clean review · i. a TodoWrite per pass · j. fix Blocker/Major after each · k. Codex is advisory · l. validate before applying · m. dismissed finding → one-line why | a. **replaced** by the derived predicate (Task 1) · c. **moved** — the hook still counts, as its reminder threshold, not the obligation · **d–h kept in substance and rewritten in wording** in the second paragraph, since the replacement re-emits them around the new precedence — not kept verbatim, and the earlier claim that they were is corrected here · b **kept verbatim inside the replacement** · i–m **kept verbatim outside the replaced range** | | 2 | `if pass 3 still` | both | the final pass must be clean; if the pass at 3 still finds Blocker/Major, keep going until clean or clearly stuck, then STOP and surface | **kept**, `pass 3` → `the pass at the floor` (Task 3) | | 3 | `below 3` | both | the only early exit below the floor is a zero-finding pass; don't pad | **kept**, `below 3` → `below the floor` (Task 4) | -| 4 | pass-1 Minor sentence | both | below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass below the floor` (Task 9) | -| 5a | pass-report paragraph | `CLAUDE.md` | a. from pass 4 onward, three lines · b. carrier is your own status report · c. never the Codex reply · d. never the findings file · e. trend · f. cluster · g. require↔withdraw · h. the five tells · i. any-two makes stop-and-surface mandatory · j. "clearly stuck" is not a precondition · **k. "you report the tells" — second person** | **untouched.** Task 10 inserts a new paragraph *before* it and modifies nothing in it | -| 5b | pass-report paragraph | template | a–j as above · **k′. "report the tells" — imperative, no addressee** · plus different wrapping | **untouched**, same reason. The divergence is pre-existing and is left alone | +| 4 | pass-1 Minor sentence | both | below the floor nothing closes; a zero-finding pass is the only exception; a Blocker/Major-free pass 1 carrying a Minor keeps looping | **kept**, `pass 1` → `pass below the floor` (Task 9). The sentence that inverts at floor 1 | +| 5a | pass-report paragraph | `CLAUDE.md` | a. from pass 4 onward, three lines · b. carrier is your own status report · c. never the Codex reply · d. never the findings file · e. trend · f. cluster · g. require↔withdraw · h. the five tells · i. any-two makes stop-and-surface mandatory · j. "clearly stuck" is not a precondition · **k. "you report the tells" — second person** | **untouched.** Task 10 inserts a new paragraph before it and modifies nothing in it | +| 5b | pass-report paragraph | template | a–j as above · **k′. "report the tells" — imperative** · plus different wrapping | **untouched**, same reason. The divergence is pre-existing | | 6 | incomplete-pass | both | an incomplete pass is not a review: don't act on the partial list, don't count it toward the floor, don't read "no Blocker/Major visible" as clean | **kept**, `the 3-pass floor` → `the floor` (Task 5) | -| 7a | Gate A loop | `CLAUDE.md` | a. two runs, each its own 3-pass loop · b. one broad prompt, re-run each pass · c. don't narrow per-dimension · d. the required opening phrase · e. coverage floor not a cage · f. every finding with severity and confidence · **g. the citation `` (`docs/prompt-standards.md`, "coverage first, filter later") ``** · h. one line per finding · i. literal `NO FINDINGS` · j. settle mechanically before each read pass | **a kept**, `3-pass loop` → `loop at the derived floor` (Task 6); **b–j untouched, g included** | -| 7b | Gate A loop | template | a–f, h–j as above · **g′. the citation is ABSENT** — the template says "findings silently." and stops · plus different wrapping | same edit to `a`; **the missing citation is pre-existing and is left alone** | -| 8 | `where the 3 come from` | both | re-review after every fix, because a fix changes the diff and the hook invalidates the prior pass | **kept, rationale replaced** — both lines, claim named (Task 7) | -| 9 | Lenses | both | a. lenses are different questions, not more passes · b. the 3-pass floor is unchanged · c. the Blocker/Major filter is unchanged · d. the file-first protocol is unchanged · e. the clean-final-pass rule is unchanged | a **kept and sharpened** · **b deliberately dropped and replaced by its negation** — the floor is precisely what this change makes variable, so the sentence now says so · c, d, e **kept verbatim** (Task 8) | +| 7a | Gate A loop | `CLAUDE.md` | a. two runs, each its own 3-pass loop · b. one broad prompt, re-run each pass · c. don't narrow per-dimension · d. the required opening phrase · e. coverage floor not a cage · f. every finding with severity and confidence · **g. the citation `` (`docs/prompt-standards.md`, "coverage first, filter later") `` ** · h. one line per finding · i. literal `NO FINDINGS` · j. settle mechanically before each read pass | **a kept**, `3-pass loop` → `loop at the derived floor` (Task 6); **b–j untouched, g included** | +| 7b | Gate A loop | template | a–f, h–j as above · **g′. the citation is ABSENT** · plus different wrapping | same edit to `a`; the missing citation is pre-existing and left alone | +| 8 | `where the 3 come from` | both | a. re-review after every fix · b. because a fix changes the diff · **c. and the hook invalidates the prior pass — which is where the 3 come from** | **a kept.** **b and c are DELIBERATELY DROPPED and replaced** (Task 7): the new design makes c false — the floor comes from the profile, not from invalidation — and b's framing put the hook in the causal position. What replaces them: a fix changes the artifact, so the prior review no longer covers it; the hook merely notices, at commit time. Recorded as a drop rather than as a keep, which the earlier row got wrong | +| 9 | Lenses | both | a. lenses are different questions, not more passes · b. the 3-pass floor is unchanged · c. the Blocker/Major filter is unchanged · d. the file-first protocol is unchanged · e. the clean-final-pass rule is unchanged | a **kept and sharpened** · **b deliberately dropped and replaced by its negation** — the floor is what this change makes variable · c, d, e **kept verbatim** (Task 8) | | 10 | `Changing a profile:` | both | a. proposes the complete resulting header · b. human confirms, both directions · c. an agent never moves it alone · d. correct the header, append one log line · e. any axis change voids every prior override · f. `+abuse-path` follows current security · g. passes under the lower profile keep counting · h. only the final clean pass must run under the current profile · i. fold mid-cycle edits into the WIP by amend | **all nine kept verbatim**; §2.4's rules appended after them, never merged in (Task 11) | -| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended as the procedure that sets a ceiling on them, **and nothing else**. Task 12 ships spec §3 alone; it states no consequence for the per-pass counts, the clusters or the stop thresholds, and chooses no precedence against any loop rule — all of that is §4 and successor-story material that spec §9 excludes. One sentence says the interaction is not settled here — a scope disclaimer rather than a rule. **`CLAUDE.md` alone adds the successor story's path**; the template must not, since it scaffolds into repositories where that path does not exist (Task 12) | +| 11 | Severity | both | Blocker = wrong/unsafe/breaks invariant · Major = design flaw → rework · both must resolve · Minor and Nit → collect, never iterate | **all four kept verbatim**; the reachability test appended, and nothing else. Task 12 ships spec §3 alone — no consequence for the per-pass counts, the clusters or the stop thresholds, and no precedence against any loop rule, all of which §9 excludes. One sentence says the interaction is not settled here and gives the unresolved-state action; **`CLAUDE.md` alone adds the owner's path** (Task 14) | -**Nothing in §5 outside these eleven passages is edited.** Gate B, reviewing the combined diff, is -what confirms that against this table. +**Nothing in §5 outside these eleven passages is edited.** Gate B, reviewing the combined diff, +is what confirms that against this table. --- ## Task 1: The floor predicate @@ -169,12 +115,12 @@ plugins/dev-workflow/commands/workflow-init.md:272:**Both gates are a LOOP with ``` > The text on disk ends **mid-line**: ` Open a TodoWrite "Codex pass N" per pass;` continues the same line after `review.` Match exactly this and no more; what follows stays. -> > -> > **The between-cycle rule implements spec §2's one-value sentence, verified against revision 36 before it was written.** §2 says the value is one *"because they derive from **the same cited-story set**"* — a claim about the **source**, not about freezing a number in time. So the value is a function of that set's **current** confirmed profiles, read fresh wherever §5 already requires reading them: one source, therefore exactly one value at any moment. -> > -> > **A snapshot taken once at the first cycle's open was considered and rejected.** It would be "a remembered or copied value", which §5's Profiles section forbids in those words — *"the story header is the single writable copy … read the values fresh at each pass, never a remembered or copied value"* — and it points the wrong way on invariant 2: a human-confirmed **raise** between cycles would then leave work still in flight reviewed under the weaker profile, which is the under-review direction. -> > -> > **§2.4 does not merely permit this; it routes the question here.** Its pass-count rules *"apply while §5 says a gate is running and are silent otherwise"*, and it states that *"what §5 says about when a gate runs — including how a moving profile or cited set bears on that — is §5's, unchanged and deliberately not summarised here."* The between-cycle case was never a spec gap. §2.4 does not *supply* the answer — it is silent outside a running gate and says so — it **delegates** the question, and §5's read-fresh rule is what answers it. +> +> **The between-cycle rule implements spec §2's one-value sentence, verified against revision 36 before it was written.** §2 says the value is one *"because they derive from **the same cited-story set**"* — a claim about the **source**, not about freezing a number in time. So the value is a function of that set's **current** confirmed profiles, read fresh wherever §5 already requires reading them: one source, therefore exactly one value at any moment. +> +> **A snapshot taken once at the first cycle's open was considered and rejected.** It would be "a remembered or copied value", which §5's Profiles section forbids in those words — *"the story header is the single writable copy … read the values fresh at each pass, never a remembered or copied value"* — and it points the wrong way on invariant 2: a human-confirmed **raise** between cycles would then leave work still in flight reviewed under the weaker profile, which is the under-review direction. +> +> **§2.4 does not merely permit this; it routes the question here.** Its pass-count rules *"apply while §5 says a gate is running and are silent otherwise"*, and it states that *"what §5 says about when a gate runs — including how a moving profile or cited set bears on that — is §5's, unchanged and deliberately not summarised here."* The between-cycle case was never a spec gap. §2.4 does not *supply* the answer — it is silent outside a running gate and says so — it **delegates** the question, and §5's read-fresh rule is what answers it. - [ ] **Replace, in both copies.** OLD: @@ -204,9 +150,10 @@ three cycles: the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle. N because they are one cycle — they are three — but because they derive from the same cited-story set. That value is a function of the current confirmed profiles of that set, read fresh wherever this section already requires them to be read, so wherever a value can be -derived at all there is exactly one, because there is one source. Two states below derive **no** -value rather than a second one: governing headers that disagree, and a cited profile that is -present but unresolvable. Both stop. A change to a profile or to the set +derived at all there is exactly one, because there is one source. Some states derive **no** value +rather than a second one, and each stops rather than defaulting: governing headers that +disagree; a cited profile that is present but unresolvable; and a `Story:` header that cannot +be read. A change to a profile or to the set therefore binds every open and future cycle — a raise costs an affected open cycle a further pass under the current profile, as the profile-change rule below requires — while a cycle that has already closed @@ -224,9 +171,11 @@ Gate-A plan loop, and — since a Gate-B cycle reviews a diff and has no header **the union of the `Story:` headers of every plan contributing to that diff, which the Gate-B call must carry in full**, as this section already requires of every cited path. **Every expected artifact contributes a set — a spec, and every plan contributing to the reviewed diff — and an expected artifact whose `Story:` header is absent contributes the empty -set rather than dropping out of the comparison.** Within one header, identical repeated paths -are one entry; two entries naming different stories are two members, and a header that cannot -be read as a list of paths is malformed and stops. **Before each pass the deriving agent +set rather than dropping out of the comparison.** **One path per entry**: a header citing several +stories carries several entries, one path each. Exact duplicate paths are one member; entries +naming different stories are different members; and a header that cannot be read as a list of +paths that way is malformed and stops, reporting that as the cause rather than as a +disagreement. **Before each pass the deriving agent compares every such set, and again before a clean pass is accepted as the cycle's final pass.** A header or profile that changed during that pass means the pass is not final — the same answer a change gets at every other read point. Where they name different sets the premise of a single value has @@ -498,8 +447,8 @@ plugins/dev-workflow/commands/workflow-init.md:518: @AGENTS.md. Re-review after ``` > **Third attempt at one sentence, and both lines are replaced.** The original, `which is where the 3 come from`, was a causal claim the new design makes false. Revision 1 made the hook the *cause* of the obligation. Revision 2 replaced only the second line, leaving `a fix changes the diff and the hook` in front of it, so the sentence read "the hook no longer covers the artifact". -> > -> > The claim eliminated is *the hook causes the re-review obligation*. The load-bearing half — `a fix changes the artifact, so the prior review no longer covers it` — stands without the hook; the trailing clause describes what the hook does and is true only while it exists, which is a description, not the cause. +> +> The claim eliminated is *the hook causes the re-review obligation*. The load-bearing half — `a fix changes the artifact, so the prior review no longer covers it` — stands without the hook; the trailing clause describes what the hook does and is true only while it exists, which is a description, not the cause. - [ ] **Replace, in both copies.** OLD: @@ -628,11 +577,7 @@ git commit --amend -m "WIP: review-loop economics" **Spec:** §2.2 -**Site** — pasted `grep -n`: - -``` - -``` +**Site** — no `grep -n` line, deliberately: this task's target does not exist in the untouched tree. It is the text an earlier task inserts, so it is located by its OLD text below and by nothing else. > Inserted **before** the existing paragraph, which is not modified — including the `you report the tells` / `report the tells` divergence between the copies, which is pre-existing (accounting rows 5a/5b). Do not harmonize it. @@ -756,10 +701,10 @@ plugins/dev-workflow/commands/workflow-init.md:675: rework) → both must resol ``` > The four severity definitions stay verbatim; the reachability test is appended as the procedure that sets a **ceiling** on them. -> > -> > **Task 12 ships spec §3 and nothing else** — the procedure, the exclusions, the symmetric instrument carve-out, the rationale rule, coverage-first and the kinship sentence. It chooses no precedence against any loop rule and states no consequence for the per-pass counts, the clusters or the stop thresholds. Earlier revisions did, three times, each time reaching past spec §9's exclusion of "the §5 loop-rule consolidation and everything its successor story owns", and each removal found another layer underneath. What remains is one sentence saying the interaction is **not settled here** — a scope disclaimer in §9's own pattern, not a rule. -> > -> > **This is the plan's one deliberate divergence between the copies, and it is stated because the constraint above requires that.** `CLAUDE.md` adds a second sentence naming the successor story's path; the scaffolded template does **not**. The template writes a `CLAUDE.md` into somebody else's repository, where `docs/superpowers/stories/…` does not exist and is never scaffolded — invariant 7 and the architecture boundary both say so. A shipped scaffold citing a path only this checkout has would make every initialized project carry an unresolvable authority for part of its own gate semantics. Both copies carry the disclaimer; only this repo's copy carries the pointer, and the successor story carries the reciprocal so the handoff is named on both sides regardless. +> +> **Task 12 ships spec §3 and nothing else** — the procedure, the exclusions, the symmetric instrument carve-out, the rationale rule, coverage-first and the kinship sentence. It chooses no precedence against any loop rule and states no consequence for the per-pass counts, the clusters or the stop thresholds. Earlier revisions did, three times, each time reaching past spec §9's exclusion of "the §5 loop-rule consolidation and everything its successor story owns", and each removal found another layer underneath. What remains is one sentence saying the interaction is **not settled here** — a scope disclaimer in §9's own pattern, not a rule. +> +> **This is the plan's one deliberate divergence between the copies, and it is stated because the constraint above requires that.** `CLAUDE.md` adds a second sentence naming the successor story's path; the scaffolded template does **not**. The template writes a `CLAUDE.md` into somebody else's repository, where `docs/superpowers/stories/…` does not exist and is never scaffolded. What forbids it is the **architecture dependency boundary** — nothing may depend on this repo's internal layout — together with **prompt-standards item 11**, which the scaffolded template must satisfy on its own. (Not invariant 7, which governs `examples/` as read-only reference and says nothing about story paths in templates.) A shipped scaffold citing a path only this checkout has would make every initialized project carry an unresolvable authority for part of its own gate semantics. Both copies carry the disclaimer; only this repo's copy carries the pointer, and the successor story carries the reciprocal so the handoff is named on both sides regardless. - [ ] **Replace, in both copies.** OLD: @@ -797,7 +742,9 @@ NEW: granularities — text that *describes* the product versus text that *is* the product. **How this demotion bears on the loop-health measures — the per-pass counts, the finding - clusters and the stop thresholds — is not settled here, and this change does not settle it.** + clusters and the stop thresholds — is not settled here, and this change does not settle it. + Until it is, a pass whose outcome would turn on that question reports the question and + stops rather than deciding it** — the same answer any unresolved gate question gets. ``` - [ ] **Assert the new text is present.** @@ -822,15 +769,11 @@ git commit --amend -m "WIP: review-loop economics" **Spec:** §10 -**Site** — pasted `grep -n`: - -``` - -``` +**Site** — no `grep -n` line, deliberately: this task's target does not exist in the untouched tree. It is the text an earlier task inserts, so it is located by its OLD text below and by nothing else. > **Plan B extends this list rather than rewriting it** — §10 says extending is safe and replacing is not. -> > -> > The partial-adoption trigger is **semantic, not a list of spellings**. Naming `pass 3`, `below 3` and `3-pass floor` missed the ones a partial merge happens to leave: the Gate-A loop description, the pass-1 closure rule and the re-review rationale each carry a fixed-three claim, and a merge can take some tasks and not others. +> +> The partial-adoption trigger is **semantic, not a list of spellings**. Naming `pass 3`, `below 3` and `3-pass floor` missed the ones a partial merge happens to leave: the Gate-A loop description, the pass-1 closure rule and the re-review rationale each carry a fixed-three claim, and a merge can take some tasks and not others. - [ ] **Replace, in both copies.** OLD: @@ -864,8 +807,10 @@ severity test gets a floor whose docs-only question the severity test is what se is a coherence requirement, stated semantically rather than as a list of spellings, and it runs in **both** directions: **exactly one definition of the floor must be present, and every statement that defines or constrains the floor, or makes closing depend on it, must resolve to -that one definition.** Everything else keeps its own footing and is **not** required to derive -from the floor: **the other closure and stop predicates** — assigned-fix-set membership, a new +that one definition.** **The unknown-start fallback is not a second definition**: it is +explicitly conditional on a cycle's starting rules being undeterminable and governs only that +state, so it coexists with the predicate rather than competing with it. Everything else +likewise keeps its own footing and is **not** required to derive from the floor: **the other closure and stop predicates** — assigned-fix-set membership, a new structural question, an accepted Blocker or Major, the tell thresholds; **independent reporting and diagnostic ordinals**, such as a duty owed from a given pass onward; and **the hook's reminder threshold together with any descriptive or historical pass number**, which say what a @@ -898,7 +843,49 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 14: The battery, and the hand-off to Plan B +## Task 14: The successor pointer — `CLAUDE.md` only + +**Spec:** §9 deferral + +**Site** — no `grep -n` line, deliberately: this task's target does not exist in the untouched tree. It is the text an earlier task inserts, so it is located by its OLD text below and by nothing else. + +> **This task edits one copy and not the other, and that is the whole point.** The previous task's disclaimer ships to both. This pointer names a path that exists only in this repository, so it must **not** reach the scaffolded template — that template writes a `CLAUDE.md` into somebody else's project, and a shipped rule citing a path only this checkout has would leave every initialized project holding an unresolvable authority for part of its own gate semantics. +> +> The handoff is still named on both sides regardless of which copy a reader holds: the successor story carries the reciprocal obligation in its own §4. + +- [ ] **Replace, in `CLAUDE.md` only.** OLD: + +``` + stops rather than deciding it** — the same answer any unresolved gate question gets. +``` + +NEW: + +``` + stops rather than deciding it** — the same answer any unresolved gate question gets. + That question is owned by the loop-rule consolidation work in + `docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md`. +``` + +- [ ] **Assert the new text is present in `CLAUDE.md`, and absent from the template.** + +```bash +grep -cF -- "owned by the loop-rule consolidation work" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: **`1` and `0`** — the divergence is the point, and the second number is as load-bearing as the first. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 15: The battery, and the hand-off to Plan B Plan A runs no Gate-B cycle. The single cycle covering all three plans opened at Task 1 and is reviewed and closed by Plan C. @@ -921,52 +908,31 @@ claude plugin validate . --strict && echo "BATTERY-GREEN (version-bump deferred) > **`sh scripts/check-version-bump.sh main` is deliberately absent, and only that one step.** Plan > A changes a path under `plugins/dev-workflow/` while the manifest bump is Plan C's, so the -> checker **would correctly fail here**. It is deferred to the combined close, where the bump -> exists. `AGENTS.md` states this checker's precondition: it compares *commits*, and run mid-work -> it reports clean and uselessly. **This is a deferral of one step with a named reason, not licence -> to skip the rest** — `scripts/check-version-bump.test.sh`, the suite, still runs, because it does -> not depend on the working tree's bump state. - -- [ ] **Confirm `scripts/check-invariants.sh` still passes** - -It is already in the battery above; called out because it is the one mechanical guard on a file -this plan edits — it fails if `plugins/dev-workflow/commands/workflow-init.md` stops having exactly -one `^Target model:` line. +> checker **would correctly fail here**. `AGENTS.md` states its precondition: it compares +> *commits*, and run mid-work it reports clean and uselessly. **This is a deferral of one step +> with a named reason, not licence to skip the rest** — `scripts/check-version-bump.test.sh`, the +> suite, still runs, because it does not depend on the working tree's bump state. - [ ] **Hand off to Plan B** -Plan B amends the same WIP commit with the same message and uses the base SHA Task 1 printed. Plan -A's Gate-A loop must have closed clean first. +Plan B amends the same WIP commit with the same message and uses the base SHA Task 1 printed. +Plan A's Gate-A loop must have closed first. --- ## Self-Review -**Spec coverage.** §2 → Tasks 1–9. §2.1 → Tasks 1–2. §2.2 → Task 10. §2.4 → Task 11. §3 → Task 12. -§10 activation, revert, downstream adoption, gate-off surface → Tasks 2 and 13, **partial by design -and written to be extended** by Plan B. §2.3, §4, §5, §6 → Plan B. §7, §8 → Plan C, with the five -relocated findings named above. +**Spec coverage.** §2 → Tasks 1–9. §2.1 → Tasks 1–2. §2.2 → Task 10. §2.4 → Task 11. §3 → Task +12. §10 activation, revert, downstream adoption, gate-off surface → Tasks 2 and 13, partial by +design and written to be extended by Plan B. The §9 deferral → Tasks 12 and 14. §2.3, §4, §5, +§6 → Plan B. §7, §8 → Plan C, with the five relocated obligations named above. **Placeholders.** None. -**Instrument.** Thirteen edit tasks, one assert-new check each, no other checks. Every pattern was -verified against a simulated post-edit tree to return `0` and `0` before its task and `1` and `1` -after; each lies on one line, contains no `**`, and is passed after `--`. **What these checks -prove is that the edit landed at the site — not that its content is right.** Content is Gate B's, -against the combined diff. - -**Type consistency.** `max(risk, security)`, "derived floor", "reminder threshold", "cited set" and -"level 0" are used identically throughout and match the spec's spellings. - -**Gate-B classification.** Plan A opens the single cycle at Task 1 and runs no review. Every later -commit is an amend restating `-m "WIP: review-loop economics"`. Plan C closes. - -**Rerun and interruption.** Each task's assert-new check doubles as its own preflight: `1`/`1` -means that task has run, `0`/`0` means it has not, and a `1`/`0` split means execution stopped -between the two copies — bring the lagging copy up before continuing. No other state machinery; -the OLD text is the edit's precondition and a task whose OLD text is absent has either run already -or been damaged, which the surrounding git history settles. +**Type consistency.** `max(risk, security)`, "derived floor", "reminder threshold", "cited set" +and "level 0" are used identically throughout and match the spec's spellings. -**Known limit, stated rather than checked.** The replacement wordings are proposals, not -transcriptions — the spec pins the rules, not the sentences. This plan's Gate A reviews the -wordings; Gate B reviews what they do to the shipped files. +**Known limit.** The replacement wordings are proposals, not transcriptions — the spec pins the +rules, not the sentences. Gate B, reading the real edits, is what checks what they do to the +shipped files; this plan's own check per task establishes only that each edit landed at its +site. diff --git a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md index e5462c8..b094ec5 100644 --- a/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md +++ b/docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md @@ -130,8 +130,11 @@ That bears directly on the loop-health measures this story owns: what a demoted the per-pass counts, to the finding clusters, and to the stop thresholds. The parent **does not settle it** — three of its revisions tried, each reaching past its own §9 exclusion of "the §5 loop-rule consolidation and everything its successor story owns", and each removal found another -layer underneath. What it ships is one sentence naming this story as where the interaction is -settled, and **this paragraph is the reciprocal**: the obligation is named in both documents so it +layer underneath. What it ships is one sentence saying the interaction is **not settled +there**, together with the conservative action for a pass that would turn on it — report the +question and stop rather than deciding it. Only this repository's `CLAUDE.md` also names this +story as the owner; the scaffolded template deliberately does not, because it writes into +projects where this path does not exist. **This paragraph is the reciprocal**: the obligation is named in both documents so it cannot fall between them, which is the failure the passage list demonstrated in that same cycle. The design owes an answer covering at least: whether a demoted finding still counts toward the finding total and the clusters, and whether the Blocker curve reads severity before or after the From 75a9a5bef1808edfab92a847bdcf94677f1f1435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 12:03:49 +0200 Subject: [PATCH 070/117] =?UTF-8?q?docs(plan):=20Plan=20B=20=E2=80=94=20th?= =?UTF-8?q?e=20records,=20written=20in=20the=20shape=20Plan=20A=20converge?= =?UTF-8?q?d=20to?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan A's Gate-A cycle closed clean at pass 12 after twelve passes and three deletions. Plan B is written lean from the start, so there is no declaration layer to shed: tasks (site / old / new / one assert-new / amend), the old-conditions accounting, the Story header, the binding constraints, the commit protocol. 539 lines against Plan A's 938 for comparable content. SHIPS: spec §2.3 the provenance line, §4 the per-pass curve and its squash carry, §5 the cycle nonce and the slot rules, §6's method applied to its own passages, and the §10 extension Plan A's wording was built to permit. WRITTEN AGAINST THE POST-PLAN-A TREE, which is the thing that makes it correct rather than a detail: every grep -n anchor was taken against the files as Plan A leaves them, and Task 6 edits a sentence Plan A creates. Executing Plan B against an unedited tree fails at Task 6, correctly. The simulation was staged the same way - Plan A's edits applied first, then Plan B's against the result. THE TWO GRAMMARS ARE COPIED FROM THE SPEC VERBATIM, not paraphrased. The spec pins them because a program parses them, and a paraphrase would be a second form - which is the one thing a pinned form cannot survive. WHAT PLAN B FIXES THAT PLAN A COULD NOT: the slot rule that stops a bare findings slot being overwritten. That rule is in this change because a bare slot WAS overwritten during this very cycle, destroying a previous cycle's findings file - an ls and an rm in one command, so the evidence the slot was occupied arrived after it was gone. The bare names stay valid for the legacy single-cycle case. The bounded nonce retry policy the spec delegates to the plan is fixed here: at most three attempts, then stop and surface, NAMING which of the three causes occurred, since randomness-unavailable, invalid-value and collision need different fixes and one token would name a symptom. Two extensions are appends rather than rewrites, deliberately: the squash carry (the successor story adds a decline record to that same sentence later, so a rewrite here would drop what that adds, or be dropped by it) and the activation strict-fallback list (Plan A shipped it with the clause "each further rule this change ships adds its own strict reading to this list" precisely so this could append - §10 says extending is safe and replacing is not). VERIFIED BEFORE COMMIT, not asserted: - All six assert-new patterns: 0/0 before their task against the post-Plan-A tree, 1/1 after, both copies. - All five passages Plan B touches are identical across the two copies over their full extent, so no accounting row is split - checked with diff over each passage rather than by comparing first lines. - Fence nesting: the two grammars sit inside task blocks, and an indented ``` closes an outer ``` under CommonMark, so the generator now sizes each outer fence longer than any backtick run it contains. Both plans re-checked for balance; Plan A came out byte-identical to 89056e6, so its clean pass stands. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-b-records.md | 539 ++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md new file mode 100644 index 0000000..1cf2277 --- /dev/null +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -0,0 +1,539 @@ +# Plan B — the records (provenance line, per-pass curve, cycle nonce, slot naming) + +> **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:** Ship the two pinned commit-body records — the provenance line and the per-pass curve — +together with the cycle nonce that attributes them and the slot naming that keeps two cycles from +overwriting each other. + +**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). +Plan B implements §2.3, §4, §5, and §6's method applied to its own passages, plus the §10 +extension Plan A's wording was written to permit. + +**Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` + +> **Read the profile from that header at execution time.** This plan states no risk value, no +> security value, no validation mode and no pass count derived from any of them. + +--- + +## Plan B of three + +| Plan | Ships | Spec sections | +|---|---|---| +| A | the floor predicate and the severity test | §2, §2.1, §2.2, §2.4, §3, §10 (partial) | +| **B — this one** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | +| C | rollout: falsified sentences, packaging, the evidence pack, the review loop | §7, §8 | + +**Three Gate-A cycles, one Gate-B cycle** over the combined A+B+C diff, run and closed by Plan C. +**Plan B may not open before Plan A's Gate-A loop closes** — it did, clean at pass 12. + +**Plan B edits the POST-PLAN-A tree.** Every `grep -n` anchor below was taken against the files +as Plan A leaves them, not against the untouched tree, and Task 6 edits a sentence Plan A +creates. Executing Plan B against an un-edited tree will fail at Task 6, correctly. + +--- + +## Global Constraints + +- **This Gate-B cycle runs under the rules in force at its start — the OLD ones.** The record + forms this plan ships bind only **after** the closing commit ships them. **Carry this sentence + in the `additionalContext` of every Gate-B call.** +- **Prompt-only.** No file under `plugins/dev-workflow/hooks/` changes, and the floor knob + `.context/codex-gate.floor` is never written, never removed, never read. +- **The §5 heading must keep matching `^#{1,6}[[:space:]]+([0-9]+\.)?[[:space:]]*Cross-Model Review`.** +- **Every edit lands in both copies.** All five passages Plan B touches were verified identical + across the two copies over their full extent, so no row below is split. +- **The two pinned grammars are copied from the spec verbatim, never paraphrased.** The spec pins + them because a program parses them; a paraphrase would be a second form, which is the one thing + a pinned form cannot survive. +- **§5's other closure rules are never restated, only referred to.** +- **Line numbers are provenance, never instructions.** + +### The commit protocol + +**Plan B amends the WIP commit Plan A opened**, with the same message, and uses the base SHA +Task 1 of Plan A printed. Plan C adds the manifest bump, runs the single Gate-B loop, and closes. + +> **Never `git commit --amend --no-edit` inside the cycle.** +> `plugins/dev-workflow/hooks/codex-gate.sh:763` recognizes a WIP commit by grepping the **Bash +> command string** for `-m ... wip`; an amend without `-m` is not recognized, and the hook resets, +> discarding the cycle's passes. + +--- + +## Old-conditions accounting + +Per spec §6's method, against the post-Plan-A text. **Three passages are rewritten and get rows.** +Three further sites are **insertion points whose existing text is re-emitted unchanged** — they +rewrite nothing, and are listed so a reader can confirm that rather than assume it. + +| # | Passage | What the existing prose requires | Disposition | +|---|---|---|---| +| 1 | the findings-slot grammar | a. write the full list to `.context/codex-reviews/.md` · b. create the directory if needed · c. the path is relative to the reviewed repo root, because Codex resolves writes against its working directory · d. `` is one of exactly three names | a–c **kept verbatim** · d **kept and extended** (Task 1): the three bare names remain, reserved for the legacy single-cycle case, and a cycle holding a nonce uses the infixed form in every slot more than one cycle could write. A refusal rule is added, which is new and not a change to d | +| 2 | the squash-merge carry | a. copy every evidence entry in the squash range · b. copy every human-exception record · c. into the squash body · d. because the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable | **a–d all kept verbatim**; three members added — the provenance lines, the curves, and a skipped cycle's skip record (Task 5). Extended rather than rewritten, because the successor story adds a decline record to this same sentence later | +| 3 | the activation strict-fallback list *(text Plan A creates; this is the second accounting that passage owes, per §6)* | a. from the shipping commit · b. a running cycle finishes under its starting rules · c. where the start cannot be established, take the stricter reading of every part · d. at minimum floor 3 and severity without the demotion · e. each further rule this change ships adds its own strict reading · f. not a re-derivation · g. a user knob above 3 is not lowered · h. a revert is itself a shipping commit | **a–h all kept verbatim**; the list gains the provenance-line duty, the curve duty and the nonce duties at their strictest (Task 6). This is the append that **e** exists to license | + +**Insertion points, rewriting nothing:** the optional-companions block (Task 2 appends after it), +the closing-message paragraph (Task 3 inserts before the squash sentence), and the human-exception +block (Task 4 inserts before it). Each task re-emits the existing text verbatim as part of its +replacement, which is what makes the insert auditable as an insert. + +--- +## Task 1: The findings-slot grammar gains a per-cycle infix + +**Spec:** §5 slot rules + +**Site** — pasted `grep -n` **against the post-Plan-A tree**, which is what Plan B edits: + +``` +CLAUDE.md:292:> `gate-a-spec-pass-

`, `gate-a-plan-pass-

`, or `gate-b--pass-

`. +plugins/dev-workflow/commands/workflow-init.md:479:> `gate-a-spec-pass-

`, `gate-a-plan-pass-

`, or `gate-b--pass-

`. +``` + +> **This is the rule the cycle that wrote it paid for.** A bare slot was overwritten during this very change, destroying a previous cycle's findings file — an `ls` and an `rm` in one command, so the evidence that the slot was occupied arrived after it was gone. The bare names stay valid for the legacy single-cycle case; anything more than one cycle could write takes the nonce. + +- [ ] **Replace, in both copies.** OLD: + +``` +> `gate-a-spec-pass-

`, `gate-a-plan-pass-

`, or `gate-b--pass-

`. +``` + +NEW: + +``` +> `gate-a-spec-pass-

`, `gate-a-plan-pass-

`, or `gate-b--pass-

` for a +> cycle with no nonce; a cycle that has one writes `gate-a-spec--pass-

`, +> `gate-a-plan--pass-

` or `gate-b---pass-

` instead, and uses +> the nonce in every slot more than one cycle could write. The bare names are reserved for the +> legacy single-cycle case they already serve. **A target owned by another nonce is refused, +> not overwritten, and the refusal names the collision** — this section already stops on a +> target that survives deletion, and this extends that to a target that must not be deleted at +> all. That rule exists because a bare slot was in fact overwritten once, destroying a previous +> cycle's findings file. +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "A target owned by another nonce is refused," \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated tree carrying Plan A's edits — the pattern lies on one line, contains no `**`, and is passed after `--`. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 2: The cycle nonce + +**Spec:** §5 + +**Site** — pasted `grep -n` **against the post-Plan-A tree**, which is what Plan B edits: + +``` +CLAUDE.md:335: Whoever runs the cycle writes it when useful, replaces it as the cycle moves, and +plugins/dev-workflow/commands/workflow-init.md:522: Whoever runs the cycle writes it when useful, replaces it as the cycle moves, and +``` + +> Inserted after the optional-companions block, whose text is re-emitted unchanged — this task rewrites nothing, it appends. The block is the right neighbour because the working record it describes is itself one of the records the nonce keys. +> +> The bounded retry policy is this plan's to fix and it is fixed here: **at most three attempts, then stop and surface, naming which of the three causes occurred.** The spec delegates the number; what it requires is that the causes stay distinguishable, since randomness-unavailable, invalid-value and collision need different fixes. + +- [ ] **Replace, in both copies.** OLD: + +``` + Whoever runs the cycle writes it when useful, replaces it as the cycle moves, and + deletes it once the cycle closes. Nothing depends on it existing. +``` + +NEW: + +``` + Whoever runs the cycle writes it when useful, replaces it as the cycle moves, and + deletes it once the cycle closes. Nothing depends on it existing. + +**The cycle nonce.** Both shipped records below carry a **cycle field**, because a record that +cannot be attributed to a cycle cannot be told apart from another cycle's when several are read +together. That is a limitation rather than a disqualification — a human reading one cycle's +records knows which cycle they came from; what attribution buys is that a *later* reader does +not have to. This section defines three cycles, so a run of all three produces three cycle +fields and, for cycles started after these rules ship, three distinct nonces. + +Generated once at cycle start, immutable, and collision-resistant operationally: **8 to 16 +characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — 8 being where +collision resistance starts and 16 where the field stops being a usable infix. **Never derived +from a name, a timestamp or a commit**, each of which collides exactly where sibling cycles do, +which is the one thing the nonce exists to prevent. The character set keeps it safe as a slot +infix and a path component. + +**It appears in every record the cycle writes, and that set is named rather than left open**: +the provenance line, the per-pass curve (including a skip record standing in for one), the +cycle's findings slots, and its advisory working record — **the working record is a cycle +record too**, and the slot rules above apply to it as they do to a findings slot. It is not +required in records this change neither introduces nor keys to a cycle, the evidence entry and +a human-exception record among them. + +**A nonce is a candidate for recovery only if** it is keyed to this cycle's type — Gate-A spec, +Gate-A plan, or Gate B — **and** this cycle's artifact, **and** that cycle is still open. +History normally holds many closed cycles' nonces and they are not candidates; a working record +left by a closed cycle is not one either, which is why that record is **retired at closure** +rather than left to be found later. **Recovery has two sources**, because a Gate-A cycle's +commit does not exist while it runs: the working record during the cycle, and history at its +commit. Recovering a single candidate from **either** keeps identity. **No candidate, +disagreeing sources, or more than one candidate → no identity: start a new cycle**, which costs +passes rather than letting one cycle's records read as another's. + +**A cycle does not start without a valid, unique nonce.** Where generation fails — randomness +unavailable, an invalid value, or a collision with an open cycle — **retry at most three times, +then stop and surface, naming which of the three causes occurred**, since they need different +fixes and one token would name a symptom rather than a cause. **No deterministic fallback.** + +**A cycle that began before these rules shipped has no nonce and cannot acquire one.** Its +records carry the reserved `cycle none (pre-rule)` field and are, by construction, not +cycle-attributable. That exception is bounded and self-terminating: it reaches only cycles +already running when the rules land, and no later cycle can enter the state. +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "Generated once at cycle start, immutable" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated tree carrying Plan A's edits — the pattern lies on one line, contains no `**`, and is passed after `--`. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 3: The provenance line + +**Spec:** §2.3 + +**Site** — pasted `grep -n` **against the post-Plan-A tree**, which is what Plan B edits: + +``` +CLAUDE.md:687: **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** +plugins/dev-workflow/commands/workflow-init.md:864: **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** +``` + +> Inserted before the squash-merge sentence, which is re-emitted unchanged here and then edited by Task 5. **The grammar is copied verbatim from spec §2.3 rather than paraphrased** — the spec pins it precisely because a program parses it, and a paraphrase would be a second form. + +- [ ] **Replace, in both copies.** OLD: + +``` + **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** +``` + +NEW: + +```` + **Every cycle records one provenance line in its closing commit body** — default floor or + not, so an absent line is never ambiguous between "the default applied" and "someone forgot". + Three cycles means three lines, one each. There is no informal variant; anything quoting this + form elsewhere quotes an instance of it, because the deferred metrics work parses it. + + ``` + ; floor per ; hook reminder threshold + + := "cycle " | "cycle none (pre-rule)" + := [a-z0-9]{8,16} + := [1-9][0-9]* + := "none" | "{" ("," )* "}" + each appears at most once; a repeated path, with or without + conflicting levels, is malformed + := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" + := | + := [A-Za-z0-9._/-]+ contains no delimiter, quote or whitespace + := a double-quoted string, non-empty, whose only escapes are \" and \\ ; + a path containing a newline or other control character is NOT + representable — the cycle stops and surfaces rather than emitting one + := "absent" | [1-9][0-9]* | "unusable(" ")" + := "unreadable" | "empty" | "non-numeric" | "out-of-range" + ``` + + It carries that cycle's **cycle field** — the nonce for any cycle started after these rules + ship, `none (pre-rule)` only for one that began before them — the **derived floor**, and **the + cited set that produced it**, each member with its level as a numeral. One floor and one set, + not an entry per story, since unanimity makes the floor a property of the set. It + distinguishes **a cited story with no profile** from **no story cited**. It records the + **workspace knob whenever the file exists**, including when present but unusable, **naming the + cause**, because those need different fixes. + + **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** +```` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "floor per ; hook reminder threshold " \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated tree carrying Plan A's edits — the pattern lies on one line, contains no `**`, and is passed after `--`. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 4: The per-pass curve + +**Spec:** §4 + +**Site** — pasted `grep -n` **against the post-Plan-A tree**, which is what Plan B edits: + +``` +CLAUDE.md:689: **Recording a human exception.** Where a human decides that something **no applicable rule +plugins/dev-workflow/commands/workflow-init.md:866: **Recording a human exception.** Where a human decides that something **no applicable rule +``` + +> Inserted before the human-exception block, which is re-emitted unchanged. Same rule as the previous task: the grammar is copied from spec §4, not restated. +> +> Note what the shipped text says about its own worth, because it is the part most likely to be dropped as hedging: the curve is durable **across** cycles and not **within** one, and it is **author-written and unchecked** — nothing compares it against the validated pass files. Whatever later reads it reads a self-report. + +- [ ] **Replace, in both copies.** OLD: + +``` + **Recording a human exception.** Where a human decides that something **no applicable rule + required** was nonetheless worth skipping +``` + +NEW: + +```` + **Every cycle records its own per-pass curve in its own commit body.** Gate B alone would + leave the dominant cost unrecorded — the loops this rule was built from are Gate-A loops. + + ``` + ; (passes , ): Findings . Blockers . Majors . + + := "Gate-A spec" | "Gate-A plan" | "Gate B" + := ("," )* strictly ascending, non-overlapping + :=

|

"-"

+

:= [1-9][0-9]* + := ("," )* exactly as many entries as enumerates + := 0 | [1-9][0-9]* | "?" "?" = unrecoverable for that pass + := | ("; " )* + := "pass "

" " ("+" )* + := | | "undetermined" + := printable ASCII minus ; : , ( ) + " and space, and not the + literal "undetermined", which is reserved + := a non-empty double-quoted string, same two escapes as + ``` + + A skipped cycle writes `; : skipped (see skip reason)` and no counts. + **`` keys must be exactly the passes `` expands to, each once, ascending** — a + list that omits or repeats a pass is malformed, not partially informative — and **every model + contributing to a split logical pass is listed**, joined by `+`, since recording one of two is + the same loss as recording none. A reported identifier carrying a control character is written + `undetermined` and **the raw value is not reproduced anywhere in the body**; what the body + records instead is where the value came from and which bytes were rejected, described rather + than embedded. + + **Majors are recorded as well as Findings and Blockers**, because the severity rule moves the + Blocker/Major line rather than the total, so totals and Blockers alone could not show even a + change in the mix. **Subject categories are deliberately not recorded** — they are a judgement + per finding rather than a count, and the findings files carry the material. + + **One entry per valid pass**, and since incomplete passes are excluded while still consuming + pass numbers, the record **states which pass numbers it covers**. A valid zero-finding pass is + recorded as zero, never omitted. **A count that cannot be recovered is written `?`, never + guessed and never written as `0`** — a cycle keeps its identity through the nonce rather than + through its pass files, so a resumed cycle may know a pass happened and not what it found, and + zero and unknown are different facts. **`?` is per series**: a pass whose Findings are unknown + may still have usable Blocker and Major counts, and a reader excludes the unknown value from + the comparisons that read that series while keeping the pass's other series. + + A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are + **branches of one logical pass** contributing one summed entry — **the curve counts logical + passes; the hook counts calls**, and where they differ the body says so. **Both branches must + have reviewed the same tracked reviewed commit**; if it changed between them they are not one + pass, the completed branch is recorded as incomplete and excluded, and the later branch begins + a new one. Ending the pass is the conservative direction; merging two revisions would produce + one entry describing two different artifacts. + + **What the curve is worth, stated rather than implied.** Durable **across** cycles; **not + within** a running one, since the commit does not exist until the cycle closes. And + **author-written and unchecked** — nothing compares it against the validated pass files, so + whatever reads it reads a self-reported curve and must not present it as measurement. + + **Recording a human exception.** Where a human decides that something **no applicable rule + required** was nonetheless worth skipping +```` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "Findings . Blockers . Majors ." \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated tree carrying Plan A's edits — the pattern lies on one line, contains no `**`, and is passed after `--`. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 5: The squash carry + +**Spec:** §4 squash carry + +**Site** — pasted `grep -n` **against the post-Plan-A tree**, which is what Plan B edits: + +``` +CLAUDE.md:687: **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** +plugins/dev-workflow/commands/workflow-init.md:864: **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** +``` + +> **Extends the list; does not rewrite the sentence.** The successor story adds a decline record to this same passage later, and a rewrite here would drop what that adds — or be dropped by it. Both existing members and the reason clause are preserved verbatim. + +- [ ] **Replace, in both copies.** OLD: + +``` + **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** +``` + +NEW: + +``` + **On squash-merge, copy every evidence entry, every human-exception record, the provenance lines, the curves and any skipped cycle's skip record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "the provenance lines, the curves and any skipped cycle's skip record" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated tree carrying Plan A's edits — the pattern lies on one line, contains no `**`, and is passed after `--`. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 6: The activation list gains the three record duties + +**Spec:** §10 + +**Site** — pasted `grep -n` **against the post-Plan-A tree**, which is what Plan B edits: + +``` +CLAUDE.md:152:floor 3, and severity classified without the demotion; each further rule this change ships +plugins/dev-workflow/commands/workflow-init.md:352:floor 3, and severity classified without the demotion; each further rule this change ships +``` + +> **This is the extension Plan A's wording was built to permit.** Plan A shipped the strict-fallback list with two members and the clause "each further rule this change ships adds its own strict reading to this list" precisely so this task could append rather than rewrite — §10 says extending is safe and replacing is not. +> +> Plan B is the successor in that sentence's sense **within this change**; the loop-rule consolidation story is a different successor and extends it again later. + +- [ ] **Replace, in both copies.** OLD: + +``` +floor 3, and severity classified without the demotion; each further rule this change ships +adds its own strict reading to this list. +``` + +NEW: + +``` +floor 3, severity classified without the demotion, the provenance-line duty owed, the curve +duty owed, and the nonce duties at their strictest; each further rule this change ships adds +its own strict reading to this list. +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "the provenance-line duty owed, the curve" \ + CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0` and `0`. After: `1` and `1`. Verified executable against a simulated tree carrying Plan A's edits — the pattern lies on one line, contains no `**`, and is passed after `--`. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 7: The battery, and the hand-off to Plan C + +Plan B runs no Gate-B cycle. The single cycle covering all three plans is reviewed and closed by +Plan C. + +- [ ] **Run the battery, minus one step, for a stated reason** + +```bash +shellcheck --shell=sh plugins/dev-workflow/hooks/codex-gate.sh && \ +shellcheck --shell=sh --exclude=SC2015 plugins/dev-workflow/hooks/codex-gate.test.sh && \ +shellcheck --shell=sh scripts/check-invariants.sh && \ +shellcheck --shell=sh --exclude=SC2015 scripts/check-invariants.test.sh && \ +shellcheck --shell=sh scripts/check-version-bump.sh && \ +shellcheck --shell=sh scripts/check-version-bump.test.sh && \ +HOOK_SH=sh sh plugins/dev-workflow/hooks/codex-gate.test.sh && \ +HOOK_SH=dash dash plugins/dev-workflow/hooks/codex-gate.test.sh && \ +sh scripts/check-invariants.test.sh && sh scripts/check-invariants.sh && \ +sh scripts/check-version-bump.test.sh && \ +claude plugin validate . --strict && echo "BATTERY-GREEN (version-bump deferred)" +``` + +> **`sh scripts/check-version-bump.sh main` is deliberately absent, and only that one step**, for +> the same reason it is absent from Plan A: the manifest bump is Plan C's, so the checker would +> correctly fail here. Every other command must pass before Plan C opens. + +- [ ] **Hand off to Plan C** + +Plan C amends the same WIP commit, adds the manifest bump and the CHANGELOG entry, corrects the +user-facing sentences the two rule changes falsify, produces the evidence pack, runs the single +Gate-B loop against the recorded base SHA, and closes the cycle. Plan B's Gate-A loop must have +closed first. + +--- + +## Self-Review + +**Spec coverage.** §2.3 → Task 3. §4 including the squash carry → Tasks 4 and 5. §5 nonce and +slot rules → Tasks 1 and 2. §6's method → the accounting above. §10 extension → Task 6. + +**Placeholders.** None. + +**Type consistency.** ``, ``, ``, ``, ``, `` +and `` are used exactly as spec §2.3 and §4 define them, copied rather than restated. + +**Known limit, and it is the same boundary Plan A closed on.** Gate A reviews this plan, not the +edits. Each task's single check establishes that its edit landed at its site and nothing more. +What those edits do to the shipped files is Gate B's, reading the real diff. From c05a86e7cf39c9aef175de9b4662828741a4969c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 12:19:10 +0200 Subject: [PATCH 071/117] =?UTF-8?q?docs(plan):=20Plan=20B=20revision=202?= =?UTF-8?q?=20=E2=80=94=20the=20grammars=20are=20the=20spec's=20block,=20n?= =?UTF-8?q?ot=20a=20retyping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 1: 11 findings, 2 BLOCKER, 7 MAJOR, 1 MINOR, 1 NIT = 9 B+M. Instrument and prose-about together were 2 of 11 (18%), below the third that routes back, so this iterates. BOTH BLOCKERS WERE THE SAME CLAIM BEING FALSE. I said the two pinned grammars were copied verbatim from the spec. They were not. - The CURVE grammar had diverged materially: the pinned `[!-~]{1,}` character class for a bare model identifier had become the prose "printable ASCII", and the quoted-model control-character rule was compressed. A character class is exactly the thing a parser reads. - The PROVENANCE grammar had been re-aligned and re-wrapped, and then something worse: embedding it through a transcription step interpreted `\"` as `"` and `\\` as `\`, so the shipped text read "whose only escapes are " and \ ;" - THE PRODUCTION THAT SPECIFIES WHICH ESCAPES ARE LEGAL HAD ITS OWN ESCAPES EATEN. Silent, and invisible to any check that did not compare against the spec byte-for-byte. Both are now inserted from the spec's own block, loaded at build time rather than retyped, with two spaces of indentation added so each stays inside its bullet and nothing else changed. Verified by de-indenting the shipped block and comparing it character-for-character with the spec: both pass. THE OTHER NINE: - The working record had no naming rule under a nonce. It gets one, mirroring the findings slots: gate-a-spec--resume.md and so on, bare names reserved for the legacy single-cycle case. - "Three cycles means three lines" was wrong for this change. §5 defines three KINDS of cycle; one field is produced per cycle RUN. A change carrying three plans runs three Gate-A plan cycles. Now says one line per cycle, and a change running five cycles records five. - Uniqueness was presented as if it were a guarantee. It compares against cycles KNOWN to be open, and two cycles starting at the same moment can each check before either publishes. Both are now disclosed as residuals, with the honest statement that what makes them unlikely is the width of the draw, not the check. Neither is a guard. - Recovery said to start a new cycle on no/disagreeing/multiple candidates but never said what happens to the cycles those candidates belong to. They stay open, keep their own nonces, and are a human's to resolve; the new cycle simply does not claim them. - "The nonce duties at their strictest" had no executable meaning. It now has one: the cycle is treated as post-rule, so it owes a nonce, and where it cannot recover one it starts a new cycle rather than claiming `none (pre-rule)` - that reserved field being unavailable to a cycle whose start cannot be established. - "Retry at most three times" reads as one attempt plus three. Now "at most three attempts in total". - Tasks 2, 3 and 4 re-emit their OLD text verbatim, so the OLD still matches AFTER the task and says nothing about whether it ran. The plan now states that the assert-new check is the preflight for those tasks and the OLD is not, since re-running on a 1/1 state would insert a second copy. - The accounting said the old slot grammar was "one of exactly three names"; gate-b--pass-

expands to two, so it is three forms and four concrete names. All six assert-new patterns re-verified: 0/0 before their task against the post-Plan-A tree, 1/1 after, both copies. Fences balanced; the provenance grammar's escapes survive into the committed plan. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-b-records.md | 147 +++++++++++------- 1 file changed, 92 insertions(+), 55 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md index 1cf2277..1713086 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -51,6 +51,15 @@ creates. Executing Plan B against an un-edited tree will fail at Task 6, correct a pinned form cannot survive. - **§5's other closure rules are never restated, only referred to.** - **Line numbers are provenance, never instructions.** +- **For Tasks 2, 3 and 4 the assert-new check is also the preflight, and the OLD text is not.** + Each of those tasks re-emits its OLD text verbatim inside its NEW text, so **the OLD still + matches after the task has run** and tells you nothing about whether it did. `1` and `1` on the + assert means the task is done; `0` and `0` means it is not. Re-running on a `1/1` state would + insert a second copy. +- **The two pinned grammars are inserted from the spec's own text, not retyped.** The escape + specification inside the provenance grammar (`\"` and `\\`) is itself made of backslashes, + and a transcription step ate them once — the grammar that pins the escape rules had its own + escapes collapsed. Copy the block; do not re-key it. ### The commit protocol @@ -72,7 +81,7 @@ rewrite nothing, and are listed so a reader can confirm that rather than assume | # | Passage | What the existing prose requires | Disposition | |---|---|---|---| -| 1 | the findings-slot grammar | a. write the full list to `.context/codex-reviews/.md` · b. create the directory if needed · c. the path is relative to the reviewed repo root, because Codex resolves writes against its working directory · d. `` is one of exactly three names | a–c **kept verbatim** · d **kept and extended** (Task 1): the three bare names remain, reserved for the legacy single-cycle case, and a cycle holding a nonce uses the infixed form in every slot more than one cycle could write. A refusal rule is added, which is new and not a change to d | +| 1 | the findings-slot grammar | a. write the full list to `.context/codex-reviews/.md` · b. create the directory if needed · c. the path is relative to the reviewed repo root, because Codex resolves writes against its working directory · d. `` is one of three named forms — four concrete names, since `gate-b--pass-

` expands to two | a–c **kept verbatim** · d **kept and extended** (Task 1): the three bare names remain, reserved for the legacy single-cycle case, and a cycle holding a nonce uses the infixed form in every slot more than one cycle could write. A refusal rule is added, which is new and not a change to d | | 2 | the squash-merge carry | a. copy every evidence entry in the squash range · b. copy every human-exception record · c. into the squash body · d. because the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable | **a–d all kept verbatim**; three members added — the provenance lines, the curves, and a skipped cycle's skip record (Task 5). Extended rather than rewritten, because the successor story adds a decline record to this same sentence later | | 3 | the activation strict-fallback list *(text Plan A creates; this is the second accounting that passage owes, per §6)* | a. from the shipping commit · b. a running cycle finishes under its starting rules · c. where the start cannot be established, take the stricter reading of every part · d. at minimum floor 3 and severity without the demotion · e. each further rule this change ships adds its own strict reading · f. not a re-derivation · g. a user knob above 3 is not lowered · h. a revert is itself a shipping commit | **a–h all kept verbatim**; the list gains the provenance-line duty, the curve duty and the nonce duties at their strictest (Task 6). This is the append that **e** exists to license | @@ -146,7 +155,11 @@ plugins/dev-workflow/commands/workflow-init.md:522: Whoever runs the cycle writ > Inserted after the optional-companions block, whose text is re-emitted unchanged — this task rewrites nothing, it appends. The block is the right neighbour because the working record it describes is itself one of the records the nonce keys. > -> The bounded retry policy is this plan's to fix and it is fixed here: **at most three attempts, then stop and surface, naming which of the three causes occurred.** The spec delegates the number; what it requires is that the causes stay distinguishable, since randomness-unavailable, invalid-value and collision need different fixes. +> The bounded retry policy is this plan's to fix and it is fixed here: **at most three attempts in total** — not three retries after a first try — **then stop and surface, naming which of the three causes occurred.** The spec delegates the number; what it requires is that the causes stay distinguishable, since randomness-unavailable, invalid-value and collision need different fixes. +> +> **Two residuals are disclosed in the shipped text rather than guarded**, because neither can be closed by prompt rules: the uniqueness check compares against cycles *known to be open*, and two cycles starting simultaneously can each check before either publishes. What makes both unlikely is the width of the draw, not the check. +> +> **This task inserts; it rewrites nothing.** The companions block is re-emitted verbatim, so its OLD text still matches afterwards — the assert-new check is this task's preflight, not the OLD. - [ ] **Replace, in both copies.** OLD: @@ -165,8 +178,10 @@ NEW: cannot be attributed to a cycle cannot be told apart from another cycle's when several are read together. That is a limitation rather than a disqualification — a human reading one cycle's records knows which cycle they came from; what attribution buys is that a *later* reader does -not have to. This section defines three cycles, so a run of all three produces three cycle -fields and, for cycles started after these rules ship, three distinct nonces. +not have to. This section defines three **kinds** of cycle — the Gate-A spec loop, the Gate-A +plan loop and the Gate-B cycle — and **one cycle field is produced per cycle run, not per +kind**: a change carrying several plans runs a Gate-A plan cycle for each, and each of those is +its own cycle with its own nonce. Generated once at cycle start, immutable, and collision-resistant operationally: **8 to 16 characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — 8 being where @@ -177,12 +192,14 @@ infix and a path component. **It appears in every record the cycle writes, and that set is named rather than left open**: the provenance line, the per-pass curve (including a skip record standing in for one), the -cycle's findings slots, and its advisory working record — **the working record is a cycle -record too**, and the slot rules above apply to it as they do to a findings slot. It is not -required in records this change neither introduces nor keys to a cycle, the evidence entry and +cycle's findings slots, and its advisory working record. **The working record is a cycle record +too**: a cycle holding a nonce names it `gate-a-spec--resume.md`, +`gate-a-plan--resume.md` or `gate-b--resume.md`, and the bare names above stay +reserved for the legacy single-cycle case, exactly as the findings slots do. The nonce is not +required in records this change neither introduces nor keys to a cycle — the evidence entry and a human-exception record among them. -**A nonce is a candidate for recovery only if** it is keyed to this cycle's type — Gate-A spec, +**A nonce is a candidate for recovery only if** it is keyed to this cycle's kind — Gate-A spec, Gate-A plan, or Gate B — **and** this cycle's artifact, **and** that cycle is still open. History normally holds many closed cycles' nonces and they are not candidates; a working record left by a closed cycle is not one either, which is why that record is **retired at closure** @@ -190,12 +207,21 @@ rather than left to be found later. **Recovery has two sources**, because a Gate commit does not exist while it runs: the working record during the cycle, and history at its commit. Recovering a single candidate from **either** keeps identity. **No candidate, disagreeing sources, or more than one candidate → no identity: start a new cycle**, which costs -passes rather than letting one cycle's records read as another's. +passes rather than letting one cycle's records read as another's. **Starting a new cycle does +not close, adopt or retire the cycles those candidates belong to** — they stay open, keep their +own nonces, and are a human's to resolve; the new cycle simply does not claim them. **A cycle does not start without a valid, unique nonce.** Where generation fails — randomness -unavailable, an invalid value, or a collision with an open cycle — **retry at most three times, -then stop and surface, naming which of the three causes occurred**, since they need different -fixes and one token would name a symptom rather than a cause. **No deterministic fallback.** +unavailable, an invalid value, or a collision with a cycle known to be open — make **at most +three attempts in total**, then stop and surface, **naming which of the three causes occurred**, +since they need different fixes and one token would name a symptom rather than a cause. **No +deterministic fallback.** + +**Two residuals, disclosed rather than guarded.** The uniqueness check compares against cycles +*known to be open*, so a nonce can repeat one belonging to a cycle nobody can see; and two +cycles starting at the same moment can each check before either has published, so neither +observes the other. **What makes both unlikely is the width of the draw, not the check** — and +unlikely is the honest word. Neither is a guard. **A cycle that began before these rules shipped has no nonce and cannot acquire one.** Its records carry the reserved `cycle none (pre-rule)` field and are, by construction, not @@ -232,7 +258,9 @@ CLAUDE.md:687: **On squash-merge, copy every evidence entry and every human-exc plugins/dev-workflow/commands/workflow-init.md:864: **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** ``` -> Inserted before the squash-merge sentence, which is re-emitted unchanged here and then edited by Task 5. **The grammar is copied verbatim from spec §2.3 rather than paraphrased** — the spec pins it precisely because a program parses it, and a paraphrase would be a second form. +> Inserted before the squash-merge sentence, which is re-emitted unchanged here and then edited by Task 5 — so the OLD still matches after this task, and the assert-new check is its preflight. +> +> **The grammar is the spec's block, copied — two spaces of indentation added so it stays inside its bullet, and nothing else changed.** Verified by de-indenting the shipped block and comparing it byte-for-byte with spec §2.3. That check earns its keep: an earlier attempt embedded the block through a transcription step that interpreted `\\"` as `"` and `\\\\` as `\\`, silently rewriting the very production that specifies which escapes are legal. - [ ] **Replace, in both copies.** OLD: @@ -242,30 +270,30 @@ plugins/dev-workflow/commands/workflow-init.md:864: **On squash-merge, copy eve NEW: -```` +``` **Every cycle records one provenance line in its closing commit body** — default floor or not, so an absent line is never ambiguous between "the default applied" and "someone forgot". - Three cycles means three lines, one each. There is no informal variant; anything quoting this - form elsewhere quotes an instance of it, because the deferred metrics work parses it. + **One line per cycle**, so a change running five cycles records five. There is no informal + variant; anything quoting this form elsewhere quotes an instance of it, because the deferred + metrics work parses it. - ``` ; floor per ; hook reminder threshold := "cycle " | "cycle none (pre-rule)" - := [a-z0-9]{8,16} - := [1-9][0-9]* - := "none" | "{" ("," )* "}" - each appears at most once; a repeated path, with or without - conflicting levels, is malformed - := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" - := | - := [A-Za-z0-9._/-]+ contains no delimiter, quote or whitespace - := a double-quoted string, non-empty, whose only escapes are \" and \\ ; - a path containing a newline or other control character is NOT - representable — the cycle stops and surfaces rather than emitting one - := "absent" | [1-9][0-9]* | "unusable(" ")" - := "unreadable" | "empty" | "non-numeric" | "out-of-range" - ``` + := [a-z0-9]{8,16} + := [1-9][0-9]* + := "none" | "{" ("," )* "}" + each appears at most once; a repeated path, + with or without conflicting levels, is malformed + := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" + := | + := [A-Za-z0-9._/-]+ contains no delimiter, quote or whitespace + := a double-quoted string, non-empty, whose only escapes are \" and \\ ; + a path containing a newline or other control + character is NOT representable — the cycle stops + and surfaces rather than emitting one + := "absent" | [1-9][0-9]* | "unusable(" ")" + := "unreadable" | "empty" | "non-numeric" | "out-of-range" It carries that cycle's **cycle field** — the nonce for any cycle started after these rules ship, `none (pre-rule)` only for one that began before them — the **derived floor**, and **the @@ -276,7 +304,7 @@ NEW: cause**, because those need different fixes. **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** -```` +``` - [ ] **Assert the new text is present.** @@ -307,7 +335,7 @@ CLAUDE.md:689: **Recording a human exception.** Where a human decides that some plugins/dev-workflow/commands/workflow-init.md:866: **Recording a human exception.** Where a human decides that something **no applicable rule ``` -> Inserted before the human-exception block, which is re-emitted unchanged. Same rule as the previous task: the grammar is copied from spec §4, not restated. +> Inserted before the human-exception block, which is re-emitted unchanged — so again the OLD still matches afterwards and the assert-new check is the preflight. The grammar is spec §4's block with two spaces of indentation added and nothing else, verified the same way. > > Note what the shipped text says about its own worth, because it is the part most likely to be dropped as hedging: the curve is durable **across** cycles and not **within** one, and it is **author-written and unchecked** — nothing compares it against the validated pass files. Whatever later reads it reads a self-report. @@ -320,35 +348,41 @@ plugins/dev-workflow/commands/workflow-init.md:866: **Recording a human excepti NEW: -```` +``` **Every cycle records its own per-pass curve in its own commit body.** Gate B alone would leave the dominant cost unrecorded — the loops this rule was built from are Gate-A loops. - ``` ; (passes , ): Findings . Blockers . Majors . - := "Gate-A spec" | "Gate-A plan" | "Gate B" - := ("," )* strictly ascending, non-overlapping - :=

|

"-"

-

:= [1-9][0-9]* - := ("," )* exactly as many entries as enumerates - := 0 | [1-9][0-9]* | "?" "?" = unrecoverable for that pass - := | ("; " )* - := "pass "

" " ("+" )* - := | | "undetermined" - := printable ASCII minus ; : , ( ) + " and space, and not the - literal "undetermined", which is reserved - := a non-empty double-quoted string, same two escapes as - ``` + := "Gate-A spec" | "Gate-A plan" | "Gate B" + := ("," )* strictly ascending, non-overlapping + :=

|

"-"

+

:= [1-9][0-9]* + := ("," )* exactly as many entries as enumerates + := 0 | [1-9][0-9]* | "?" "?" = the count is unrecoverable for that pass + := | ("; " )* + := "pass "

" " ("+" )* + := | | "undetermined" + "undetermined" means the model could not be determined; + a real model so named is written as + := [!-~]{1,} minus ; : , ( ) + " and space, and not the + literal "undetermined", which is reserved + printable ASCII only; a control character makes the + identifier unrepresentable, handled below + := a non-empty double-quoted string, same two escapes as ; + a reported identifier containing a control character is + written `undetermined` — and the raw value is NOT + reproduced anywhere in the body, since a commit message + cannot safely carry one (NUL cannot appear at all). + What the body records instead is where the value came + from and which bytes were rejected, described rather + than embedded A skipped cycle writes `; : skipped (see skip reason)` and no counts. **`` keys must be exactly the passes `` expands to, each once, ascending** — a list that omits or repeats a pass is malformed, not partially informative — and **every model contributing to a split logical pass is listed**, joined by `+`, since recording one of two is - the same loss as recording none. A reported identifier carrying a control character is written - `undetermined` and **the raw value is not reproduced anywhere in the body**; what the body - records instead is where the value came from and which bytes were rejected, described rather - than embedded. + the same loss as recording none. **Majors are recorded as well as Findings and Blockers**, because the severity rule moves the Blocker/Major line rather than the total, so totals and Blockers alone could not show even a @@ -379,7 +413,7 @@ NEW: **Recording a human exception.** Where a human decides that something **no applicable rule required** was nonetheless worth skipping -```` +``` - [ ] **Assert the new text is present.** @@ -468,8 +502,11 @@ NEW: ``` floor 3, severity classified without the demotion, the provenance-line duty owed, the curve -duty owed, and the nonce duties at their strictest; each further rule this change ships adds -its own strict reading to this list. +duty owed, and the nonce duties at their strictest — the cycle is treated as post-rule, so it +owes a nonce and every record that carries one, and where it cannot recover one it starts a new +cycle rather than claiming `none (pre-rule)`, that reserved field being unavailable to a cycle +whose start cannot be established. Each further rule this change ships adds its own strict +reading to this list. ``` - [ ] **Assert the new text is present.** From 6f84ebc26166b1de6cc1bbd11ca86597cb263ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 12:30:06 +0200 Subject: [PATCH 072/117] =?UTF-8?q?docs(plan):=20Plan=20B=20revision=203?= =?UTF-8?q?=20=E2=80=94=20close=20the=20preflight=20state=20space;=20drop?= =?UTF-8?q?=20"unique"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 2: 3 findings, 0 Blockers, 3 Majors. Down from 11 findings and 9 B+M at pass 1. All three correct revision 2 and stay inside the assigned fix set, so they are absorbed rather than routed. - The assert-as-preflight rule I added last revision defined only the all-absent and all-present states. An interruption between the two copies leaves 1/0, and a previous double-insert leaves a count above one, and neither had a branch. A resumed executor would have applied the replacement to both copies, duplicating the block in the one that already had it while repairing the other. The rule is now a closed four-row table: 0/0 apply, 1/1 skip, mixed STOP without touching either copy, above one STOP for hand removal. Same single check, complete state space - which is the Plan A lesson recurring, and I introduced the gap in the same revision that introduced the rule. - "A cycle does not start without a valid, UNIQUE nonce" contradicted the residuals I had just disclosed one paragraph below: an unseen-open collision or a check-then-start race can let two cycles start with the same value. Stating a guarantee and then admitting it does not hold is the overclaim class AGENTS.md names, made worse by the disclosure sitting right there. Replaced with the property actually obtainable: valid, and not equal to any nonce observed on a known-open cycle at check time. The attribution benefit is now stated as probabilistic - "usually, not always" - rather than categorical. - The three named failure causes had no per-cause check or fix, which docs/prompt-standards.md item 10 requires for a diagnostic state and invariant 11 makes binding. Each now carries both: randomness unavailable is not helped by retrying and the attempts are spent proving it; an invalid value is redrawn, and a recurrence means the generator is wrong rather than unlucky; a collision is redrawn, and a second one at this width means the source is not behaving randomly. Same three-attempt total, same terminal stop. Verified after the fixes: all six assert-new patterns 0/0 before their task against the post-Plan-A tree and 1/1 after, both copies; both grammars still character-for-character the spec's, including the provenance escapes and the curve's [!-~]{1,}. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-b-records.md | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md index 1713086..89be880 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -53,9 +53,15 @@ creates. Executing Plan B against an un-edited tree will fail at Task 6, correct - **Line numbers are provenance, never instructions.** - **For Tasks 2, 3 and 4 the assert-new check is also the preflight, and the OLD text is not.** Each of those tasks re-emits its OLD text verbatim inside its NEW text, so **the OLD still - matches after the task has run** and tells you nothing about whether it did. `1` and `1` on the - assert means the task is done; `0` and `0` means it is not. Re-running on a `1/1` state would - insert a second copy. + matches after the task has run** and tells you nothing about whether it did. Read the assert's + two counts as a closed state space, and stop on anything that is not one of the first two: + + | counts | state | action | + |---|---|---| + | `0` and `0` | not started | apply the replacement to both copies | + | `1` and `1` | done | skip the task | + | `1` and `0`, or `0` and `1` | **interrupted between the copies** | **STOP and surface. Do not replace either copy** — applying to both would duplicate the block in the copy that already has it while repairing the other | + | anything above `1` | **already duplicated** | **STOP and surface.** A previous run inserted twice; the extra copy is removed by hand before the task is retried | - **The two pinned grammars are inserted from the spec's own text, not retyped.** The escape specification inside the provenance grammar (`\"` and `\\`) is itself made of backslashes, and a transcription step ate them once — the grammar that pins the escape rules had its own @@ -177,8 +183,9 @@ NEW: **The cycle nonce.** Both shipped records below carry a **cycle field**, because a record that cannot be attributed to a cycle cannot be told apart from another cycle's when several are read together. That is a limitation rather than a disqualification — a human reading one cycle's -records knows which cycle they came from; what attribution buys is that a *later* reader does -not have to. This section defines three **kinds** of cycle — the Gate-A spec loop, the Gate-A +records knows which cycle they came from; what attribution buys is that a *later* reader +**usually** does not have to. Usually, not always: the guarantee is probabilistic, for the two +reasons stated at the end of this block. This section defines three **kinds** of cycle — the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle — and **one cycle field is produced per cycle run, not per kind**: a change carrying several plans runs a Gate-A plan cycle for each, and each of those is its own cycle with its own nonce. @@ -211,11 +218,22 @@ passes rather than letting one cycle's records read as another's. **Starting a n not close, adopt or retire the cycles those candidates belong to** — they stay open, keep their own nonces, and are a human's to resolve; the new cycle simply does not claim them. -**A cycle does not start without a valid, unique nonce.** Where generation fails — randomness -unavailable, an invalid value, or a collision with a cycle known to be open — make **at most -three attempts in total**, then stop and surface, **naming which of the three causes occurred**, -since they need different fixes and one token would name a symptom rather than a cause. **No -deterministic fallback.** +**A cycle does not start without a nonce that is valid and not equal to any nonce observed on a +known-open cycle at check time** — which is the exact property obtainable here, and is weaker +than uniqueness. Where that fails, make **at most three attempts in total**, then stop and +surface, **naming which of the three causes occurred**; each has its own check and its own fix, +and one token would name a symptom rather than a cause: + +- **randomness unavailable** — the source errors or returns nothing. *Fix:* make a source + available, or run where one is. Retrying does not help and the attempts are spent proving it. +- **an invalid value** — the drawn value is not 8 to 16 characters from `[a-z0-9]`. *Fix:* + redraw, which is what the remaining attempts are for. If it recurs the generator is wrong, not + unlucky, and that is what gets reported. +- **a collision with a known-open cycle** — the value equals a nonce on a cycle still open. + *Fix:* redraw. A second collision at this width means the source is not behaving randomly, and + the report says so rather than repeating the draw. + +**No deterministic fallback.** **Two residuals, disclosed rather than guarded.** The uniqueness check compares against cycles *known to be open*, so a nonce can repeat one belonging to a cycle nobody can see; and two From 8272674e7e5bae8fe98dde65bc9f5db88a86b491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 12:42:48 +0200 Subject: [PATCH 073/117] =?UTF-8?q?docs(plan):=20Plan=20B=20revision=204?= =?UTF-8?q?=20=E2=80=94=20collision-resistant,=20not=20collision-proof?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 3: 6 findings, 0 Blockers, 4 Major, 2 Minor. Curve 9 -> 3 -> 4 B+M. All product-level, no instrument or prose-about cluster, so this iterated. TWO FINDINGS WERE CONSEQUENCES OF PASS 2'S FIX THAT I DID NOT CHASE. Weakening "unique nonce" to the property actually obtainable was right, and I left everything built on top still claiming the strong version: - The slot rule said a target owned by another nonce is refused. It now says a DIFFERENT nonce, because that is the case the rule can detect - two cycles that drew the same nonce are indistinguishable to it and can still overwrite each other. What makes that unlikely is the width of the draw, not the rule. - The shipped text now states what follows from a resistant rather than proof nonce, instead of leaving it to be discovered: two cycles sharing a nonce write to the same slots and are not refused, their records read as one cycle's, and a later reader cannot separate them. Attribution is a strong default, not a guarantee, and any reading that would be wrong if two cycles shared a field should say so rather than assume they did not. THE PREFLIGHT TABLE HAD A DEAD END. `2/0` is an above-one state; removing the surplus copy makes it `1/0`, which is the other stop row - so the recovery moved between stop rows without ever reaching a runnable one. The exit is now defined as a STATE rather than as an edit: restore both copies by hand to `0/0` or `1/1` and retry from there, with the explicit note that removing a surplus copy is not by itself an exit. PARTIAL DOWNSTREAM ADOPTION. Plan A gave the floor rule a coherence stop; the records had none, and they are one contract - a curve without a cycle field cannot be attributed, a slot rule without a nonce has nothing to key on, a carry rule naming records a project does not produce is inert. A project carrying some and not others now stops for a human to complete or revert. The two Minors rode along: the cause-specific diagnostics claimed retrying does not help when a randomness failure can be transient, and claimed a second collision means the source is defective when it is merely unlikely - both are now stated as suspicions rather than findings. And the terminal report now names EVERY distinct cause observed across the attempts, in order, since attempts can fail for different reasons and naming only the last describes the tail. CAUGHT BY MY OWN CHECK, NOT BY THE REVIEW: editing the slot sentence broke Task 1's assert pattern, which still grepped "another nonce" - the plan's own check would have failed at execution. The generator now refuses to build unless every assert pattern occurs exactly once in its task's NEW text and zero times in its OLD, which also rules out a pattern that matches before the edit. The first version of that guard's test did not fire, because I sabotaged the pattern by truncating a word and the truncation was still a prefix of the real text. Re-tested with a genuinely absent pattern and with one that also appears in the OLD text; the guard fires on both. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-b-records.md | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md index 89be880..1d9c90e 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -61,7 +61,13 @@ creates. Executing Plan B against an un-edited tree will fail at Task 6, correct | `0` and `0` | not started | apply the replacement to both copies | | `1` and `1` | done | skip the task | | `1` and `0`, or `0` and `1` | **interrupted between the copies** | **STOP and surface. Do not replace either copy** — applying to both would duplicate the block in the copy that already has it while repairing the other | - | anything above `1` | **already duplicated** | **STOP and surface.** A previous run inserted twice; the extra copy is removed by hand before the task is retried | + | any count above `1` | **already duplicated** | **STOP and surface.** | + + **The two stop rows have one exit, and it is a human's**: restore both copies by hand to a + state the first two rows recognise — either both without the block, or both carrying exactly + one — and retry the task from there. **Removing a surplus copy is not by itself an exit**: a + `2/0` that becomes `1/0` has moved from one stop row to the other, which is why the exit is + defined as reaching `0/0` or `1/1` rather than as an edit. - **The two pinned grammars are inserted from the spec's own text, not retyped.** The escape specification inside the provenance grammar (`\"` and `\\`) is itself made of backslashes, and a transcription step ate them once — the grammar that pins the escape rules had its own @@ -123,8 +129,10 @@ NEW: > cycle with no nonce; a cycle that has one writes `gate-a-spec--pass-

`, > `gate-a-plan--pass-

` or `gate-b---pass-

` instead, and uses > the nonce in every slot more than one cycle could write. The bare names are reserved for the -> legacy single-cycle case they already serve. **A target owned by another nonce is refused, -> not overwritten, and the refusal names the collision** — this section already stops on a +> legacy single-cycle case they already serve. **A target owned by a DIFFERENT nonce is refused, +> not overwritten, and the refusal names the collision** — which is the case this rule can +> detect. Two cycles that drew the same nonce are indistinguishable to it and can still overwrite +> each other; what makes that unlikely is the width of the draw, not this rule — this section already stops on a > target that survives deletion, and this extends that to a target that must not be deleted at > all. That rule exists because a bare slot was in fact overwritten once, destroying a previous > cycle's findings file. @@ -133,7 +141,7 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- "A target owned by another nonce is refused," \ +grep -cF -- "A target owned by a DIFFERENT nonce is refused," \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` @@ -224,22 +232,35 @@ than uniqueness. Where that fails, make **at most three attempts in total**, the surface, **naming which of the three causes occurred**; each has its own check and its own fix, and one token would name a symptom rather than a cause: -- **randomness unavailable** — the source errors or returns nothing. *Fix:* make a source - available, or run where one is. Retrying does not help and the attempts are spent proving it. +- **randomness unavailable** — the source errors or returns nothing. *Fix:* retry, since the + condition can be transient; if it persists across the attempts, make a source available or run + where one is, which is a change to the environment rather than another draw. - **an invalid value** — the drawn value is not 8 to 16 characters from `[a-z0-9]`. *Fix:* - redraw, which is what the remaining attempts are for. If it recurs the generator is wrong, not - unlucky, and that is what gets reported. + redraw. Repeated invalid output points at the generator rather than at luck, and the report + says which. - **a collision with a known-open cycle** — the value equals a nonce on a cycle still open. - *Fix:* redraw. A second collision at this width means the source is not behaving randomly, and - the report says so rather than repeating the draw. + *Fix:* redraw. A second collision at this width is possible but unlikely enough to be worth + reporting as a possible source defect, which the report states as a suspicion rather than a + finding. + +**Report every distinct cause observed across the attempts, in the order they occurred** — the +attempts can fail for different reasons, and naming only the last would describe the tail of the +sequence rather than what happened. **No deterministic fallback.** -**Two residuals, disclosed rather than guarded.** The uniqueness check compares against cycles -*known to be open*, so a nonce can repeat one belonging to a cycle nobody can see; and two -cycles starting at the same moment can each check before either has published, so neither -observes the other. **What makes both unlikely is the width of the draw, not the check** — and -unlikely is the honest word. Neither is a guard. +**Two residuals, disclosed rather than guarded.** The check compares against cycles *known to be +open*, so a nonce can repeat one belonging to a cycle nobody can see; and two cycles starting at +the same moment can each check before either has published, so neither observes the other. +**What makes both unlikely is the width of the draw, not the check** — and unlikely is the +honest word. Neither is a guard. + +**What follows from that, said here rather than left to be discovered.** The nonce is +collision-**resistant**, not collision-**proof**, so everything built on it inherits that bound: +two cycles sharing a nonce write to the same slots and are not refused, their records read as +one cycle's, and a later reader cannot separate them. Attribution is therefore a strong default +rather than a guarantee, and any reading of these records that would be wrong if two cycles +shared a field should say so rather than assume they did not. **A cycle that began before these rules shipped has no nonce and cannot acquire one.** Its records carry the reserved `cycle none (pre-rule)` field and are, by construction, not @@ -473,6 +494,14 @@ plugins/dev-workflow/commands/workflow-init.md:864: **On squash-merge, copy eve NEW: ``` + **These records are one contract, and a partial adoption breaks it.** The nonce, the slot + naming, the provenance line, the curve and this carry rule depend on one another: a curve + without a cycle field cannot be attributed, a slot rule without a nonce has nothing to key on, + and a carry rule naming records a project does not produce is inert. **A project whose text + carries some of them and not others stops and has a human complete or revert the adoption + before running a gate under it** — the same answer, and for the same reason, as a partial + adoption of the floor rule. + **On squash-merge, copy every evidence entry, every human-exception record, the provenance lines, the curves and any skipped cycle's skip record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** ``` From c01ff6f0654f39db5c41f3a29aa61c9f7a078949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 12:59:03 +0200 Subject: [PATCH 074/117] =?UTF-8?q?docs(plan):=20Plan=20B=20revision=205?= =?UTF-8?q?=20=E2=80=94=20six=20absorbed;=20the=20cycle-cardinality=20ques?= =?UTF-8?q?tion=20routed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 4: 10 findings, 0 Blockers, 7 Major, 3 Minor. Curve 9 -> 3 -> 4 -> 7 B+M, rising for two passes. Five of the seven Majors are on the nonce axis, its third round. ONE FINDING WAS HALF WRONG, AND CHECKING IT CHANGED THE FIX. Pass 4 said my weakened nonce property contradicts the approved spec and story, which require uniqueness. Verified against both: spec §8: "it must differ from that of ANY CYCLE OPEN AT THE TIME IT WAS GENERATED - which is the uniqueness the rule actually requires, and all it can check." story: "unique among cycles open when it was generated" So the governing artifacts already say what I shipped. What they also do, and I had collapsed, is SEPARATE the requirement (§5 states it) from what a check can establish (§8 explains it). My text had merged them into a single weakened requirement, which reads as shipping a lesser contract even though the property is the same. Restored to the spec's own structure: the rule states the requirement as approved, and the residuals below state the gap between it and what the check can observe. That implements the contract rather than amending it, which is why it is absorbed rather than routed. THE OTHER FIVE MAJORS, absorbed: - The residual list said "two residuals" and was missing a third reachable path: generation deliberately ignores CLOSED cycles, so a new cycle can redraw a closed one's value and then write to its surviving findings slots and working record. Added, and the list is now explicitly non-exhaustive. - Three mechanism sentences still read categorically - one recovered candidate keeps identity, a new cycle prevents cross-reading. Each is now qualified at its own site rather than relying on one blanket caveat elsewhere, since a reader following the operational sentence never reaches the caveat. - Recovery scopes candidates by artifact, but the working record's only defined form was kind-plus-nonce - no artifact key at all. The record now names the artifact in its CONTENTS, quoted by the provenance line's rule where needed; the filename carries kind and nonce, where a path is not representable. - The coupled-adoption set omitted the unknown-start activation semantics, so a project could carry all five named blocks, miss that one, and look complete. Added - and the requirement is now that the adopted definitions AGREE, not merely that all are present. - "Owes a nonce and every record that carries one" made the explicitly optional working record mandatory and left a skipped cycle unrepresentable. Now: owes the provenance line and the curve or skip record, and uses the nonce in every record it does write - which changes what a record is NAMED, never whether one is owed. ROUTED, NOT ABSORBED - the cycle cardinality. The story's criterion reads "one per cycle, so a run of all three holds three". A three-plan change runs FIVE cycles: one Gate-A spec, three Gate-A plan, one Gate B. My reading is that the rule is "one per cycle" and the clause after "so" is a worked example for the one-plan case - but that is me resolving an ambiguity in a story criterion in my own favour, on the third round of findings touching this axis, and amending a criterion is not an agent's call. Routed with that reading offered. Three Minors collected: the curve grammar has no field for the logical-pass / hook-call discrepancy the prose requires be stated; the rejected-bytes record for a control-bearing model identifier has no defined form; and the accounting calls Tasks 5 and 6 pure appends when each also rewords a clause. All six assert-new patterns re-verified, both copies. The generator's drift guard passed. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-b-records.md | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md index 1d9c90e..0a6d628 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -210,7 +210,10 @@ the provenance line, the per-pass curve (including a skip record standing in for cycle's findings slots, and its advisory working record. **The working record is a cycle record too**: a cycle holding a nonce names it `gate-a-spec--resume.md`, `gate-a-plan--resume.md` or `gate-b--resume.md`, and the bare names above stay -reserved for the legacy single-cycle case, exactly as the findings slots do. The nonce is not +reserved for the legacy single-cycle case, exactly as the findings slots do. **Because recovery +scopes candidates by artifact as well as by kind, the record's contents name that artifact** — +its path, quoted by the same rule the provenance line uses where quoting is needed. The filename +carries kind and nonce; the artifact key lives inside, where a path is representable. The nonce is not required in records this change neither introduces nor keys to a cycle — the evidence entry and a human-exception record among them. @@ -220,15 +223,18 @@ History normally holds many closed cycles' nonces and they are not candidates; a left by a closed cycle is not one either, which is why that record is **retired at closure** rather than left to be found later. **Recovery has two sources**, because a Gate-A cycle's commit does not exist while it runs: the working record during the cycle, and history at its -commit. Recovering a single candidate from **either** keeps identity. **No candidate, +commit. Recovering a single candidate from **either** keeps identity **as far as the field can +distinguish cycles** — two cycles sharing a nonce are one cycle to it. **No candidate, disagreeing sources, or more than one candidate → no identity: start a new cycle**, which costs -passes rather than letting one cycle's records read as another's. **Starting a new cycle does +passes rather than letting one cycle's records read as another's — again, as far as distinct +nonces allow. **Starting a new cycle does not close, adopt or retire the cycles those candidates belong to** — they stay open, keep their own nonces, and are a human's to resolve; the new cycle simply does not claim them. -**A cycle does not start without a nonce that is valid and not equal to any nonce observed on a -known-open cycle at check time** — which is the exact property obtainable here, and is weaker -than uniqueness. Where that fails, make **at most three attempts in total**, then stop and +**A cycle does not start without a valid nonce, unique among the cycles open when it was +generated.** That is the requirement. **What the check can establish is narrower** — it compares +against the cycles it can observe — and the gap between the two is the residual set out below. +Where generation fails, make **at most three attempts in total**, then stop and surface, **naming which of the three causes occurred**; each has its own check and its own fix, and one token would name a symptom rather than a cause: @@ -249,9 +255,11 @@ sequence rather than what happened. **No deterministic fallback.** -**Two residuals, disclosed rather than guarded.** The check compares against cycles *known to be -open*, so a nonce can repeat one belonging to a cycle nobody can see; and two cycles starting at -the same moment can each check before either has published, so neither observes the other. +**Residuals, disclosed rather than guarded, and this list is not exhaustive.** The check compares +against cycles *known to be open*, so a nonce can repeat one belonging to a cycle nobody can see; +two cycles starting at the same moment can each check before either has published, so neither +observes the other; and the check deliberately ignores **closed** cycles, so a new cycle can +redraw a closed one's value and then write to its surviving findings slots and working record. **What makes both unlikely is the width of the draw, not the check** — and unlikely is the honest word. Neither is a guard. @@ -495,7 +503,10 @@ NEW: ``` **These records are one contract, and a partial adoption breaks it.** The nonce, the slot - naming, the provenance line, the curve and this carry rule depend on one another: a curve + naming, the provenance line, the curve, this carry rule **and the unknown-start activation + semantics that say what a cycle owes when its starting rules cannot be established** depend on + one another, and the requirement is that the adopted definitions **agree**, not merely that all + of them are present: a curve without a cycle field cannot be attributed, a slot rule without a nonce has nothing to key on, and a carry rule naming records a project does not produce is inert. **A project whose text carries some of them and not others stops and has a human complete or revert the adoption @@ -550,9 +561,11 @@ NEW: ``` floor 3, severity classified without the demotion, the provenance-line duty owed, the curve duty owed, and the nonce duties at their strictest — the cycle is treated as post-rule, so it -owes a nonce and every record that carries one, and where it cannot recover one it starts a new -cycle rather than claiming `none (pre-rule)`, that reserved field being unavailable to a cycle -whose start cannot be established. Each further rule this change ships adds its own strict +owes a nonce, owes its provenance line and its curve or skip record, and uses that nonce in every +cycle record it does write — which changes what a record is named, never whether one is owed, so +the working record stays optional and a skipped cycle still writes no findings slots. Where it +cannot recover a nonce it starts a new cycle rather than claiming `none (pre-rule)`, that reserved +field being unavailable to a cycle whose start cannot be established. Each further rule this change ships adds its own strict reading to this list. ``` From cf9e304853777dc6a179ee088a9a032fb3dd5e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 13:00:47 +0200 Subject: [PATCH 075/117] =?UTF-8?q?docs(plan):=20Plan=20B=20revision=206?= =?UTF-8?q?=20=E2=80=94=20record=20the=20confirmed=20cycle-cardinality=20r?= =?UTF-8?q?eading?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The routed question is answered: "one per cycle" is the rule; the story criterion's "so a run of all three holds three" is a worked example written before this change was split into three plans, and a stale example does not outrank the rule it illustrates. This change runs FIVE cycles - one Gate-A spec, three Gate-A plan, one Gate B - and therefore holds five cycle fields, five provenance lines and five curves. The five-cycle shape is a consequence of two decisions already taken, the plan split and the single Gate-B topology, not a new claim by this plan. RECORDED IN THREE PLACES BECAUSE TWO OF THEM DO NOT SURVIVE. The dispositions and the closure record live under .context/, which is gitignored and per-clone, so a reading recorded only there reaches nobody. It is therefore also in the plan's binding constraints, which are committed, and in this commit body. The example clause itself gets a one-line touch-up in the final-round story-edit batch, so the next reader meets the corrected example rather than re-deriving the reading. That gitignored records do not reach the next reader is this cycle's own availability lesson applied to itself - the same reason the passage list was recovered from a commit rather than from a working file. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- .../2026-08-30-review-loop-economics-plan-b-records.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md index 0a6d628..00afc03 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -51,6 +51,13 @@ creates. Executing Plan B against an un-edited tree will fail at Task 6, correct a pinned form cannot survive. - **§5's other closure rules are never restated, only referred to.** - **Line numbers are provenance, never instructions.** +- **One cycle field per cycle RUN, not per cycle kind — a confirmed reading, not a new rule.** + The story's criterion says "one per cycle, so a run of all three holds three". **"One per + cycle" is the rule**; the clause after "so" is a worked example written before this change was + split into three plans, and it does not outrank the rule it illustrates. This change runs + **five** cycles — one Gate-A spec, three Gate-A plan, one Gate B — and therefore holds five + fields, five provenance lines and five curves. The five-cycle shape is a consequence of two + decisions already taken: the plan split, and the single Gate-B cycle. - **For Tasks 2, 3 and 4 the assert-new check is also the preflight, and the OLD text is not.** Each of those tasks re-emits its OLD text verbatim inside its NEW text, so **the OLD still matches after the task has run** and tells you nothing about whether it did. Read the assert's From bd5ca5bfa308c995c0adad7cf4d0dbaece9ed0e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 13:13:48 +0200 Subject: [PATCH 076/117] =?UTF-8?q?docs(plan):=20Plan=20B=20revision=207?= =?UTF-8?q?=20=E2=80=94=20the=20slot=20refusal=20gets=20an=20observable=20?= =?UTF-8?q?procedure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 5: 9 findings, 0 Blockers, 5 Major, 2 Minor, 2 NIT. Curve 9, 3, 4, 7, 5 B+M and 11, 3, 6, 10, 9 findings - BOTH FALLING for the first time since pass 2. No tells; instrument and prose-about at 22%, below the third. THE SHARPEST FINDING: my slot refusal had no observable decision procedure. "A target owned by a different nonce is refused" - but different nonces produce different paths, so a writer never encounters another nonce's target in the course of writing its own. The rule described a situation that does not arise. Rewritten as a decision on the PATH, which is what a writer can actually observe: a cycle holding a nonce writes only paths carrying that nonce and never a bare path; a bare path that exists belongs to a legacy cycle and is neither written nor deleted; an existing path carrying a different nonce is not one this cycle would write at all, and finding one while resolving a slot is refused and reported rather than removed. Two cycles that drew the same nonce resolve to the same paths and are indistinguishable here - which is the residual, not the rule. THE OTHER FOUR MAJORS: - Two categorical sites still lacked the same-nonce qualification. Pass 4 had me qualify three; there were five. Now qualified where the claim is made rather than by one caveat elsewhere. - Spec §4 says the plan fixes HOW the tracked reviewed commit is encoded, and I had only named the thing. Pinned: the full 40-character hex object name, since abbreviations are ambiguous across repositories and across time and this comparison is the entire point of the rule. - Recovery scopes candidates by artifact, and "its path" only works for a Gate-A cycle. Gate B reviews a diff, not a file. The artifact key is now defined per cycle kind: the reviewed document's path for Gate A, the base commit's full hex object name for Gate B. - The coupled-adoption rule required adopted definitions to AGREE but its only terminal action covered a missing member. Disagreement is the harder case and now gets the same stop, because a project holding two definitions of a record has no single answer to what it owes. One Minor rode along: the three nonce-generation causes were not mutually exclusive as written - an empty source result read as both "returns nothing" and "an invalid value". They are now distinguished by where the attempt stopped: the source produced no bytes; or bytes that are not a well-formed nonce; or a well-formed nonce already in use. Editing the slot sentence changed Task 1's assert pattern for the second time. The generator's drift guard caught it both times, which is what it is for. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-b-records.md | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md index 00afc03..c6397c6 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -136,10 +136,13 @@ NEW: > cycle with no nonce; a cycle that has one writes `gate-a-spec--pass-

`, > `gate-a-plan--pass-

` or `gate-b---pass-

` instead, and uses > the nonce in every slot more than one cycle could write. The bare names are reserved for the -> legacy single-cycle case they already serve. **A target owned by a DIFFERENT nonce is refused, -> not overwritten, and the refusal names the collision** — which is the case this rule can -> detect. Two cycles that drew the same nonce are indistinguishable to it and can still overwrite -> each other; what makes that unlikely is the width of the draw, not this rule — this section already stops on a +> legacy single-cycle case they already serve. **The decision is made on the path, which is what +> a writer can observe: a cycle holding a nonce writes only paths carrying that nonce, and never +> a bare path.** A bare path that already exists belongs to a legacy cycle and is neither written +> nor deleted; an existing path carrying a *different* nonce is not one this cycle would write at +> all, and finding one while resolving a slot is refused and reported rather than removed. **Two +> cycles that drew the same nonce resolve to the same paths and are indistinguishable here** — +> what makes that unlikely is the width of the draw, not this rule — this section already stops on a > target that survives deletion, and this extends that to a target that must not be deleted at > all. That rule exists because a bare slot was in fact overwritten once, destroying a previous > cycle's findings file. @@ -148,7 +151,7 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- "A target owned by a DIFFERENT nonce is refused," \ +grep -cF -- "The decision is made on the path, which is what" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` @@ -212,15 +215,20 @@ from a name, a timestamp or a commit**, each of which collides exactly where sib which is the one thing the nonce exists to prevent. The character set keeps it safe as a slot infix and a path component. -**It appears in every record the cycle writes, and that set is named rather than left open**: +**It appears in every record the cycle writes** — which keeps records apart **as far as distinct +nonces allow**, and no further — **and that set is named rather than left open**: the provenance line, the per-pass curve (including a skip record standing in for one), the cycle's findings slots, and its advisory working record. **The working record is a cycle record too**: a cycle holding a nonce names it `gate-a-spec--resume.md`, `gate-a-plan--resume.md` or `gate-b--resume.md`, and the bare names above stay reserved for the legacy single-cycle case, exactly as the findings slots do. **Because recovery -scopes candidates by artifact as well as by kind, the record's contents name that artifact** — -its path, quoted by the same rule the provenance line uses where quoting is needed. The filename -carries kind and nonce; the artifact key lives inside, where a path is representable. The nonce is not +scopes candidates by artifact as well as by kind, the record's contents name that artifact**, and +what counts as the artifact depends on the cycle kind: for a Gate-A cycle it is the reviewed +document's path, quoted by the same rule the provenance line uses where quoting is needed; for a +Gate-B cycle, which reviews a diff rather than a file, it is the **base commit's full +40-character hex object name**, the same value the cycle's reviews are run against. The filename +carries kind and nonce; the artifact key lives inside, where neither a path nor a hex name has to +survive a filename. The nonce is not required in records this change neither introduces nor keys to a cycle — the evidence entry and a human-exception record among them. @@ -245,7 +253,12 @@ Where generation fails, make **at most three attempts in total**, then stop and surface, **naming which of the three causes occurred**; each has its own check and its own fix, and one token would name a symptom rather than a cause: -- **randomness unavailable** — the source errors or returns nothing. *Fix:* retry, since the +The three are distinguished by **where** the attempt stopped, so they cannot both apply: the +source failed to produce bytes; or it produced bytes that are not a well-formed nonce; or it +produced a well-formed nonce that is already in use. An empty result is the first, never the +second. + +- **randomness unavailable** — the source errors or produces no bytes. *Fix:* retry, since the condition can be transient; if it persists across the attempts, make a source available or run where one is, which is a change to the environment rather than another draw. - **an invalid value** — the drawn value is not 8 to 16 characters from `[a-z0-9]`. *Fix:* @@ -455,7 +468,9 @@ NEW: A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are **branches of one logical pass** contributing one summed entry — **the curve counts logical passes; the hook counts calls**, and where they differ the body says so. **Both branches must - have reviewed the same tracked reviewed commit**; if it changed between them they are not one + have reviewed the same tracked reviewed commit**, recorded as its **full 40-character hex + object name** — abbreviations are ambiguous across repositories and across time, and this + comparison is the whole point of the rule; if it changed between them they are not one pass, the completed branch is recorded as incomplete and excluded, and the later branch begins a new one. Ending the pass is the conservative direction; merging two revisions would produce one entry describing two different artifacts. @@ -516,8 +531,10 @@ NEW: of them are present: a curve without a cycle field cannot be attributed, a slot rule without a nonce has nothing to key on, and a carry rule naming records a project does not produce is inert. **A project whose text - carries some of them and not others stops and has a human complete or revert the adoption - before running a gate under it** — the same answer, and for the same reason, as a partial + carries some of them and not others, or carries all of them in versions that disagree, stops + and has a human complete, revert or reconcile the adoption before running a gate under it** — + disagreement is the harder case and gets the same stop, because a project holding two + definitions of a record has no single answer to what it owes — the same answer, and for the same reason, as a partial adoption of the floor rule. **On squash-merge, copy every evidence entry, every human-exception record, the provenance lines, the curves and any skipped cycle's skip record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** From f132c572bb0333cc2e80c35fcc7c95c0e7df4874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 13:25:43 +0200 Subject: [PATCH 077/117] =?UTF-8?q?docs(plan):=20Plan=20B=20revision=208?= =?UTF-8?q?=20=E2=80=94=20bind=20the=20slot=20rule=20to=20the=20deletion?= =?UTF-8?q?=20step?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 6: 7 findings, 0 Blockers, 6 Major, 1 NIT. Findings 9 -> 7, B+M 5 -> 6. No tells; prose-about at 14%. TWO FINDINGS SAID MY PASS-5 FIXES HAD NOT LANDED, and both were right. - The categorical overwrite claim: I qualified it in the shipped text and left it standing in the PLAN'S OWN GOAL, which is the first thing a reader meets. Qualified there and at the curve's identity sentence. - The slot refusal: pass 5 asked for an observable procedure and I gave one that cannot be reached. "An existing path carrying a different nonce is refused" - but resolving your own path never yields a different nonce, and scanning the slot family would flag legitimate concurrent siblings as collisions. The rule described a situation that does not arise, which is the same defect pass 5 named, in new words. Rewritten to bind THE DELETION STEP, which is where the damage actually happens. §5 already requires every target to be deleted and confirmed gone before a call. A cycle holding a nonce deletes only paths carrying its own nonce; it never deletes a bare path or a different nonce's path, and an attempt to do either stops and names the path. That is reachable, because the step operates on a path it computed and the check is whether that path is its own - and it is the case that actually occurred, a nonce-holding cycle computing a legacy bare path and deleting somebody else's file. Distinct-nonce paths coexist by construction and were never in conflict; saying so removes the false collision the previous wording invented. THE OTHER FOUR: - Recovery keys were stable but not selective. Two Gate-A cycles can review the same document and two Gate-B cycles commonly share a base commit, so a sole match on kind and artifact is not identity - it would adopt a sibling. A candidate is now adopted only if POSITIVELY LINKED to this run: the working record this run itself wrote. A merely consistent match is no identity and the cycle starts fresh. - The two recovery sources were not executable as stated. The provenance line and curve carry no artifact key, so history could not be searched by it. Corrected by saying what each source actually does: the working record is the mid-cycle source and the one the candidate rules apply to, since several files may be present; HISTORY IS NOT A SEARCH - the cycle reads its own commit body, so kind and artifact are settled by which commit is read, and the nonce comes from the provenance line and curve, which must agree. A Gate-A cycle mid-run has no such commit and has only the working record. - The skip record reads "skipped (see skip reason)" and the squash copied only the record - a pointer into a body the squash makes unreachable. The carry now takes the skip reason with it. - The tracked reviewed commit had a representation but no procedure. Now: take it from the head commit each call reports reviewing, capture it WITH THAT BRANCH'S RESULT rather than re-reading later - an intervening WIP amend moves HEAD, so a later read is a different commit - and require exact equality before summing. The NIT is collected. The drift guard caught Task 1's pattern changing for the third time. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-b-records.md | 62 +++++++++++++------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md index c6397c6..d0fda4d 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -5,8 +5,10 @@ > checkbox (`- [ ]`) syntax for tracking. **Goal:** Ship the two pinned commit-body records — the provenance line and the per-pass curve — -together with the cycle nonce that attributes them and the slot naming that keeps two cycles from -overwriting each other. +together with the cycle nonce that attributes them and the slot naming that keeps cycles holding +**distinct** nonces from overwriting each other. Two cycles that drew the same nonce are +indistinguishable to the naming, which is why the nonce is collision-resistant rather than +collision-proof and why the shipped text says so at each claim. **Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). Plan B implements §2.3, §4, §5, and §6's method applied to its own passages, plus the §10 @@ -136,13 +138,19 @@ NEW: > cycle with no nonce; a cycle that has one writes `gate-a-spec--pass-

`, > `gate-a-plan--pass-

` or `gate-b---pass-

` instead, and uses > the nonce in every slot more than one cycle could write. The bare names are reserved for the -> legacy single-cycle case they already serve. **The decision is made on the path, which is what -> a writer can observe: a cycle holding a nonce writes only paths carrying that nonce, and never -> a bare path.** A bare path that already exists belongs to a legacy cycle and is neither written -> nor deleted; an existing path carrying a *different* nonce is not one this cycle would write at -> all, and finding one while resolving a slot is refused and reported rather than removed. **Two -> cycles that drew the same nonce resolve to the same paths and are indistinguishable here** — -> what makes that unlikely is the width of the draw, not this rule — this section already stops on a +> legacy single-cycle case they already serve. **Distinct-nonce paths coexist by construction and +> are never in conflict** — a sibling cycle's slot is simply a different file. +> +> **The rule binds the deletion step, which is where the damage is done.** This section already +> requires every target to be deleted and confirmed gone before a call. A cycle holding a nonce +> **deletes only paths carrying its own nonce**; it never deletes a bare path or one carrying a +> different nonce, and an attempt to do either **stops and names the path** instead of removing +> it. That is reachable and observable: the step operates on a path it computed, and the check is +> whether that path is the cycle's own. **The case it exists for is a nonce-holding cycle +> computing a bare path** — the legacy spelling — **and deleting a file that belongs to somebody +> else**, which is exactly what happened once. **Two cycles that drew the same nonce compute the +> same paths and are indistinguishable to this rule**; what makes that unlikely is the width of +> the draw, not this rule — this section already stops on a > target that survives deletion, and this extends that to a target that must not be deleted at > all. That rule exists because a bare slot was in fact overwritten once, destroying a previous > cycle's findings file. @@ -151,7 +159,7 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- "The decision is made on the path, which is what" \ +grep -cF -- "The rule binds the deletion step, which is where" \ CLAUDE.md plugins/dev-workflow/commands/workflow-init.md ``` @@ -233,13 +241,24 @@ required in records this change neither introduces nor keys to a cycle — the e a human-exception record among them. **A nonce is a candidate for recovery only if** it is keyed to this cycle's kind — Gate-A spec, -Gate-A plan, or Gate B — **and** this cycle's artifact, **and** that cycle is still open. +Gate-A plan, or Gate B — **and** this cycle's artifact, **and** that cycle is still open. **Those +three are necessary and not sufficient, and the difference matters**: two Gate-A cycles can review +the same document and two Gate-B cycles commonly share a base commit, so a sole match on kind and +artifact is **not** identity. **A candidate is adopted only if it is positively linked to this +run** — the working record this run itself wrote. A match that is merely consistent is treated as +no identity, and the cycle starts fresh; adopting a sibling on a shared key would merge two +cycles under one nonce, which is the failure this rule exists to prevent. History normally holds many closed cycles' nonces and they are not candidates; a working record left by a closed cycle is not one either, which is why that record is **retired at closure** -rather than left to be found later. **Recovery has two sources**, because a Gate-A cycle's -commit does not exist while it runs: the working record during the cycle, and history at its -commit. Recovering a single candidate from **either** keeps identity **as far as the field can -distinguish cycles** — two cycles sharing a nonce are one cycle to it. **No candidate, +rather than left to be found later. **Recovery has two sources, and they answer different questions.** The **working record** is the +source while the cycle runs, and it is the one the candidate rules above apply to — several files +may be present and the run must decide which, if any, is its own. **History is the source once +the cycle's own commit exists**, and there is no search there: the cycle is reading **its own +commit body**, so kind and artifact are settled by which commit is being read, and the nonce is +taken from the provenance line and the curve, which must agree. A Gate-A cycle mid-run has no +such commit and therefore has only the working record. Recovering a single candidate from +**either** keeps identity **as far as the field can distinguish cycles** — two cycles sharing a +nonce are one cycle to it. **No candidate, disagreeing sources, or more than one candidate → no identity: start a new cycle**, which costs passes rather than letting one cycle's records read as another's — again, as far as distinct nonces allow. **Starting a new cycle does @@ -460,7 +479,7 @@ NEW: pass numbers, the record **states which pass numbers it covers**. A valid zero-finding pass is recorded as zero, never omitted. **A count that cannot be recovered is written `?`, never guessed and never written as `0`** — a cycle keeps its identity through the nonce rather than - through its pass files, so a resumed cycle may know a pass happened and not what it found, and + through its pass files, as far as distinct nonces allow, so a resumed cycle may know a pass happened and not what it found, and zero and unknown are different facts. **`?` is per series**: a pass whose Findings are unknown may still have usable Blocker and Major counts, and a reader excludes the unknown value from the comparisons that read that series while keeping the pass's other series. @@ -468,9 +487,12 @@ NEW: A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are **branches of one logical pass** contributing one summed entry — **the curve counts logical passes; the hook counts calls**, and where they differ the body says so. **Both branches must - have reviewed the same tracked reviewed commit**, recorded as its **full 40-character hex - object name** — abbreviations are ambiguous across repositories and across time, and this - comparison is the whole point of the rule; if it changed between them they are not one + have reviewed the same tracked reviewed commit.** Take it from **the head commit each call + reports having reviewed**, capture it **with that branch's result** rather than re-reading it + later — an intervening `WIP:` amend moves `HEAD`, so a value read afterwards is a different + commit — and require the two captured values to be **exactly equal** before the branches are + summed. Record it as the **full 40-character hex object name**, since abbreviations are + ambiguous across repositories and across time; if it changed between them they are not one pass, the completed branch is recorded as incomplete and excluded, and the later branch begins a new one. Ending the pass is the conservative direction; merging two revisions would produce one entry describing two different artifacts. @@ -537,7 +559,7 @@ NEW: definitions of a record has no single answer to what it owes — the same answer, and for the same reason, as a partial adoption of the floor rule. - **On squash-merge, copy every evidence entry, every human-exception record, the provenance lines, the curves and any skipped cycle's skip record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** + **On squash-merge, copy every evidence entry, every human-exception record, the provenance lines, the curves and any skipped cycle's skip record TOGETHER WITH THE SKIP REASON IT POINTS AT in the squash range into the squash body — a skip record carried without its reason is a pointer into a body the squash has made unreachable — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** ``` - [ ] **Assert the new text is present.** From 51840fd9f77bb5b57bc7c0346ab31fa2db96b053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:14:20 +0200 Subject: [PATCH 078/117] =?UTF-8?q?docs(plan):=20Plan=20C=20=E2=80=94=20ro?= =?UTF-8?q?llout,=20packaging,=20evidence,=20and=20the=20close?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third and last plan, in the shape the first two converged to: tasks (site / old / new / one assert-new / amend), the old-conditions accounting, the Story header, binding constraints, no self-description layer. SHIPS: spec §7 rollout and §8 evidence, plus the single Gate-B cycle that reviews the combined A+B+C diff and closes it. TWELVE EDIT TASKS. Nine correct user-facing sentences that Plans A and B falsify - README's knob row, seven in getting-started, one in coding-workflow - and all nine were re-verified to still read as catalogued before either plan was written. The other three are the item-1 n/a note, the manifest bump to 0.11.0, and the CHANGELOG entry. THE ACCOUNTING IS §6's METHOD APPLIED TO A DIFFERENT CLASS. These passages are not §5 rules but user-facing statements the change makes false, so each row says what the statement asserted and what replaces it. Two are worth naming: the axes-add-lenses sentence tangled a true claim (the axes never subtract) with one this change falsifies (the floor is unchanged at every level), so the first is kept and the second deliberately dropped rather than the sentence reworded; and the knob is described wrongly in TWO places, so both are corrected - fixing one would have left the other teaching it. THE FIVE INHERITED OBLIGATIONS ARE DISCHARGED AT NAMED STEPS, not merely listed. They came from a Gate-B section Plan A dropped when the topology became one cycle, and a finding whose section moved is relocated rather than repaired: single-branch recovery deletes only the failed branch (Task 14 Step 2); the floor knob is recorded by BYTES AND HASH before the cycle and compared after, since existence alone would pass a knob whose contents changed (Task 13 Step 4); staging names explicit paths and never -u (Task 14 Step 3); the closing body is built with mktemp (Task 15 Step 2); and the evidence is revalidated after every fix and again against the content the close will carry (Task 14 Step 3, Task 15 Step 1). THE BATTERY RUNS IN FULL HERE, version-bump check included. Plans A and B deferred that one step with a stated reason - it compares commits and the bump did not exist yet. Task 11 lands the bump and the WIP exists, so the deferral ends where it was said it would. FIVE CYCLES, FIVE PROVENANCE LINES, FIVE CURVES in the closing body - the Gate-A spec cycle, three Gate-A plan cycles, and Gate B. All five carry "cycle none (pre-rule)": every one began before these rules ship, so none has a nonce and minting one would be late-created provenance. The branch therefore demonstrates every field of both pinned forms EXCEPT the cycle identifier and the knob clause's non-absent form, both recorded as undemonstrable-here with reasons rather than as gaps or as satisfied. Verified before commit: all twelve edits apply exactly once against the correct base for their file - the post-Plan-A-and-B tree for workflow-init, the untouched tree for the docs; all twelve assert patterns absent before and present after; exactly one `^Target model:` line survives, so check-invariants still passes; the item-1 note lands OUTSIDE the template fence, verified by line order; and fences balance. One thing my own verification got wrong and the shape caught: my scratch check for "stale patterns are gone" matched the CORRECTED coding-workflow sentence, because the fix reads "so it is not the same at every level" and the check grepped "the same at every level". The plan asserts the NEW text rather than the absence of the old, which is exactly why that class of false alarm cannot reach an executor. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-c-rollout.md | 821 ++++++++++++++++++ 1 file changed, 821 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md new file mode 100644 index 0000000..dafea98 --- /dev/null +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -0,0 +1,821 @@ +# Plan C — rollout: falsified sentences, packaging, evidence, and the close + +> **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:** Correct every user-facing sentence Plans A and B falsify, ship the packaging, produce +the evidence the story's mode requires, run the one Gate-B cycle over the combined diff, and close +it. + +**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). +Plan C implements §7 and §8. + +**Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` + +> **Read the profile from that header at execution time.** This plan states no risk value, no +> security value, no validation mode and no pass count derived from any of them. Task 13 reads the +> mode from the header and produces what it names. + +--- + +## Plan C of three + +| Plan | Ships | Status | +|---|---|---| +| A | the floor predicate and the severity test | Gate-A closed clean, pass 12 | +| B | the provenance line, the per-pass curve, the cycle nonce, slot naming | Gate-A closed clean, pass 7 | +| **C — this one** | rollout, packaging, evidence, and the single Gate-B cycle | this cycle | + +**Plan C edits the tree Plans A and B leave behind** for `workflow-init.md`; the user-facing docs +it corrects are untouched by either. Every `grep -n` anchor below was taken against the +correct base for its file. + +### The five obligations Plan C inherits + +These came from a Gate-B section Plan A dropped when the topology became one cycle. **A finding +whose section moved is relocated, not repaired**, so they are discharged here or nowhere: + +| From | What Plan C owes | +|---|---| +| pass-1 M8 | **single-branch Gate-B recovery** — delete only the failed branch, never both | +| pass-1 M9 | **record the floor knob's existence and bytes before the cycle, compare after** | +| pass-1 M10 | **never `git add -u`** — stage an explicitly inspected path set | +| pass-1 MINOR 12 | **build the closing body with `mktemp`**, not a fixed `/tmp` path | +| pass-1 B6 | **evidence revalidated after every fix and again before the closing amend** | + +--- + +## Global Constraints + +- **This Gate-B cycle runs under the rules in force at its start — the OLD ones.** Everything + Plans A and B ship binds only **after** the closing commit. **Carry this sentence in the + `additionalContext` of every Gate-B call**: a reviewer applying the new Minor-or-below ceiling + to the change that introduces it would under-iterate on exactly the diff needing most iteration. +- **The floor for this cycle is 3** — the constant, because the old rules govern it. Not the + derived value the diff introduces. +- **No file under `plugins/dev-workflow/hooks/` changes.** The floor knob + `.context/codex-gate.floor` is never written or removed; Task 13 reads it, and reading is what + the evidence requires. +- **Never `git commit --amend --no-edit` inside the cycle.** + `plugins/dev-workflow/hooks/codex-gate.sh:763` recognizes a WIP commit by grepping the **Bash + command string** for `-m ... wip`; an amend without `-m` is not recognized, and the hook resets, + discarding the cycle's passes. Every amend restates `-m "WIP: review-loop economics"`. +- **No ordinary commit until the close.** The cycle Plan A opened is still open. Every task here + amends it, including the ones touching only `docs/**.md` — the prose exemption decides whether a + *commit* needs Gate B, and this content is going into a commit that already does. +- **`git add` names paths explicitly, never `-u`.** Staging whole-tree changes would fold an + unrelated edit into the reviewed diff and close it under this story. +- **Line numbers are provenance, never instructions.** + +--- + +## Old-conditions accounting + +§6's method, applied to a different class: these passages are not §5 rules but **user-facing +statements that Plans A and B make false**. For each, what it asserted and what replaces it. + +| # | Statement | What it asserted | Disposition | +|---|---|---|---| +| 1 | `README.md` knob row | the knob moves the pass floor | **false as of Plan A** — the knob moves the hook's reminder threshold and never bound an agent. Replaced, with the distinction stated (Task 1) | +| 2 | `getting-started.md` Gate-A loop | three passes minimum | **replaced** by the derived floor; "final pass clean" and the zero-finding early exit are **kept** (Task 2) | +| 3 | same, plan loop | the same 3-pass loop | **replaced**; the rest of the sentence kept (Task 3) | +| 4 | same, hook message | the Gate-A floor wasn't met | **false** — the hook reports its own threshold, which at a derived floor of 1 reports a shortfall the cycle does not owe. Replaced (Task 4) | +| 5 | same, Gate-B loop | three passes, final clean | **replaced** by the derived floor; "final clean" kept (Task 5) | +| 6 | same, satisfied message | `3/3 cycle` | **kept as an example, relabelled** — the literal is the hook's ratio, so `N/N` with N named as the threshold rather than the floor (Task 6) | +| 7 | same, axes sentence | the axes never subtract **and** the floor is unchanged at every level | first clause **kept** — it is true and is the point; second **deliberately dropped**, since this change is what makes the floor vary (Task 7) | +| 8 | same, second knob mention | the knob moves the 3-pass floor | **false**, same as row 1. Both sites corrected, because fixing one would leave the other teaching it (Task 8) | +| 9 | `coding-workflow.md` axes sentence | as row 7 | same disposition (Task 9) | +| 10 | `workflow-init.md` before the template fence | *(nothing — this is an insertion)* | **rewrites nothing**; the note is added outside the fence so it never scaffolds (Task 10) | +| 11 | `plugin.json` version | `0.10.0` | **replaced** by `0.11.0` (Task 11) | +| 12 | `CHANGELOG.md` | *(nothing — an append)* | **rewrites nothing**; a new newest-first entry (Task 12) | + +**Rows 10 and 12 are insertions and are listed so a reader can confirm that rather than assume +it.** Each re-emits its anchor verbatim inside its replacement. + +--- +## Task 1: The `codex-gate.floor` knob description + +**File:** `README.md` + +**Site** — pasted `grep -n`: + +``` +README.md:130:| `codex-gate.floor` | a positive integer; moves the 3-passes-per-gate floor. | +``` + +> The knob never bound an agent — `$floor` appears in the hook's control flow, but that flow only selects which advisory message fires, and the hook exits 0 on every branch. The old line said it moved the floor, which was the clearest statement of the wrong model anywhere in the docs. + +- [ ] **Replace.** OLD: + +``` +| `codex-gate.floor` | a positive integer; moves the 3-passes-per-gate floor. | +``` + +NEW: + +``` +| `codex-gate.floor` | a positive integer; moves the hook's reminder threshold. It does not change the floor §5 obliges, which is derived from the cited story's profile. | +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "moves the hook's reminder threshold. It does not change the floor" README.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add README.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 2: The Gate-A loop description + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:34:three passes minimum, final pass clean — the one early exit is a pass that comes +``` + +- [ ] **Replace.** OLD: + +``` +three passes minimum, final pass clean — the one early exit is a pass that comes +back with zero findings. +``` + +NEW: + +``` +the floor its profile derives, final pass clean — the one early exit is a pass that +comes back with zero findings. +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "the floor its profile derives, final pass clean" docs/getting-started.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 3: The plan-loop sentence + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:40:task-by-task plan (each task starts with a failing test); the same 3-pass loop runs +``` + +- [ ] **Replace.** OLD: + +``` +task-by-task plan (each task starts with a failing test); the same 3-pass loop runs +``` + +NEW: + +``` +task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "the same loop runs at the derived floor" docs/getting-started.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 4: The below-floor hook message + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:44:progress claims backed by test runs. If the Gate-A floor wasn't met, the hook says +``` + +> The hook reports against its own threshold, not against what the cycle owes. At a derived floor of 1 it will report a shortfall the cycle does not have — the named residual, and this sentence is where a reader would otherwise learn the opposite. + +- [ ] **Replace.** OLD: + +``` +progress claims backed by test runs. If the Gate-A floor wasn't met, the hook says +``` + +NEW: + +``` +progress claims backed by test runs. If the hook's own threshold wasn't met, it says +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "If the hook's own threshold wasn't met, it says" docs/getting-started.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 5: The Gate-B loop description + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:53:`mcp__codex__review` the same way: three passes, final clean. Verification is by +``` + +- [ ] **Replace.** OLD: + +``` +`mcp__codex__review` the same way: three passes, final clean. Verification is by +``` + +NEW: + +``` +`mcp__codex__review` the same way: the derived floor, final clean. Verification is by +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "the derived floor, final clean. Verification is by" docs/getting-started.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 6: The satisfied-message example + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:58:`✓ Codex Gate B satisfied (3/3 cycle, 3 on current fingerprint)`, the real commit replaces +``` + +> The literal `3/3` in the example is the hook's ratio. Writing `N/N` without saying which N it is would have replaced a wrong number with an ambiguous one. + +- [ ] **Replace.** OLD: + +``` +`✓ Codex Gate B satisfied (3/3 cycle, 3 on current fingerprint)`, the real commit replaces +``` + +NEW: + +``` +`✓ Codex Gate B satisfied (N/N cycle, N on current fingerprint)` — N being the hook's threshold, not the derived floor — the real commit replaces +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "N being the hook's threshold, not the derived floor" docs/getting-started.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 7: The axes-add-lenses sentence + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:84:is still owed and Gate A's floor is unchanged at every level. The caution bias is +``` + +> Two claims were tangled here: that the axes never subtract, which is true and stays, and that the floor is unchanged at every level, which this change makes false. Separated rather than reworded. + +- [ ] **Replace.** OLD: + +``` +is still owed and Gate A's floor is unchanged at every level. The caution bias is +``` + +NEW: + +``` +is still owed; Gate A's floor derives from the profile exactly as Gate B's does, and what the axes never subtract is the baseline questions. The caution bias is +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "Gate A's floor derives from the profile exactly as Gate B's does" docs/getting-started.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 8: The second knob mention + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:86:positive integer) moves the 3-pass floor, and `touch .context/codex-gate.off` +``` + +> The same correction as Task 1, in the second place the docs describe the knob. Both sites say the same wrong thing and a fix to one would have left the other teaching it. + +- [ ] **Replace.** OLD: + +``` +positive integer) moves the 3-pass floor, and `touch .context/codex-gate.off` +``` + +NEW: + +``` +positive integer) moves the hook's reminder threshold, and `touch .context/codex-gate.off` +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "moves the hook's reminder threshold, and" docs/getting-started.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 9: The coding-workflow axes sentence + +**File:** `docs/coding-workflow.md` + +**Site** — pasted `grep -n`: + +``` +docs/coding-workflow.md:79:gates for a risky or security-relevant change (they never subtract any: Gate A's floor and +``` + +- [ ] **Replace.** OLD: + +``` +gates for a risky or security-relevant change (they never subtract any: Gate A's floor and +the baseline questions are the same at every level), while the derived mode calibrates +``` + +NEW: + +``` +gates for a risky or security-relevant change (they never subtract a baseline question; the +floor itself derives from the profile, so it is not the same at every level), while the derived +mode calibrates +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "floor itself derives from the profile, so it is not the same at every level" docs/coding-workflow.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/coding-workflow.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 10: The item-1 n/a note, outside the fence + +**File:** `plugins/dev-workflow/commands/workflow-init.md` + +**Site** — pasted `grep -n`: + +``` +plugins/dev-workflow/commands/workflow-init.md:190:numbers) and say so in the report. +``` + +> **Outside the fence, deliberately.** Inside it the note would scaffold into every initialized project, where it is meaningless. And it is **not** written as a `Target model:` line: that would make this file's declaration count 2 and fail `scripts/check-invariants.sh`, which is the naive form of this fix. +> +> The fence here opens with four backticks because the template it wraps contains three-backtick blocks of its own; the note goes above that opener. + +- [ ] **Replace.** OLD: + +````` +numbers) and say so in the report. + +````markdown +````` + +NEW: + +````` +numbers) and say so in the report. + +> **Prompt-standards item 1 for the scaffolded `CLAUDE.md`: n/a, and why.** The file this +> template writes is model-agnostic by design — its executing model is whatever the reader of +> that project runs — so a `Target model:` line inside it would be false in every repo it lands +> in. Recorded as a reasoned n/a rather than skipped: the item is answered. **This note sits +> outside the fence** so it never scaffolds, and is deliberately **not** a `Target model:` line, +> which would make this file's declaration count 2 and fail `scripts/check-invariants.sh`. + +````markdown +````` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "Prompt-standards item 1 for the scaffolded" plugins/dev-workflow/commands/workflow-init.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 11: The manifest version bump + +**File:** `plugins/dev-workflow/.claude-plugin/plugin.json` + +**Site** — pasted `grep -n`: + +``` +plugins/dev-workflow/.claude-plugin/plugin.json:4: "version": "0.10.0", +``` + +> Minor, not patch: shipped rules change and no documented interface breaks. Invariant 12 requires a bump for any change under `plugins//`, and Plans A and B both changed `workflow-init.md`. + +- [ ] **Replace.** OLD: + +``` + "version": "0.10.0", +``` + +NEW: + +``` + "version": "0.11.0", +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- ""version": "0.11.0"" plugins/dev-workflow/.claude-plugin/plugin.json +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add plugins/dev-workflow/.claude-plugin/plugin.json +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 12: The CHANGELOG entry + +**File:** `plugins/dev-workflow/CHANGELOG.md` + +**Site** — pasted `grep -n`: + +``` +plugins/dev-workflow/CHANGELOG.md:25:## 0.10.0 +``` + +> Newest first, matching the file's stated convention. **Nothing enforces this** — neither invariant 12 nor `check-version-bump.sh` mentions the changelog — so it is carried by the story's criterion and by this task. + +- [ ] **Replace.** OLD: + +``` +## 0.10.0 +``` + +NEW: + +``` +## 0.11.0 + +- **The mandatory pass floor is now a function of the cited story's profile**, not the constant 3. + `max(risk, security) == 0` gives a floor of **1**; every resolvable profile above that, and an + artifact citing no story, gives **3**. A cited story whose profile is present but unresolvable + **stops and surfaces** rather than defaulting. Across a cited set the floor is 1 only if the set + is non-empty and every member is profiled, resolvable and at level 0. +- **The hook's ratio is a reminder threshold and controls nothing.** It always did; the text now + says so, and the `codex-gate.floor` knob is described as moving that threshold rather than the + obligation. `README.md` and `docs/getting-started.md` carried the old description and are + corrected. +- **Finding severity is decided by whether something in the system takes a different decision.** + Name what consumes the text and the decision that changes if it is wrong; if you cannot name + both, the finding is Minor or below. The review pass raising a finding is not an in-system + reader of the text it reviews, gates remain readers of rule text they will later apply, and a + human reader never satisfies the test. It sets a ceiling, never a floor, and never chooses + between Blocker and Major. +- **Two commit-body records are pinned**, because a program parses them: a **provenance line** + carrying the cycle field, the derived floor and the cited set that produced it, and a + **per-pass curve** carrying Findings, Blockers and Majors per pass. One of each per cycle — a + change running five cycles records five. +- **A cycle nonce** attributes those records. Eight to sixteen characters from `[a-z0-9]`, from a + source of randomness, never derived from a name, timestamp or commit. It is + collision-**resistant**, not collision-proof, and the shipped text says where that bound bites + rather than implying a guarantee. +- **Findings slots take a per-cycle infix**, and the deletion step §5 already requires now deletes + only paths carrying the cycle's own nonce. That rule exists because a bare slot was overwritten + during this change's own development, destroying a previous cycle's findings file. +- **What this change does not settle** is how demotion bears on the loop-health measures — the + per-pass counts, the clusters and the stop thresholds. That is the loop-rule consolidation + story's, and both documents say so, so the obligation cannot fall between them. + +## 0.10.0 +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "The mandatory pass floor is now a function of the cited story's profile" plugins/dev-workflow/CHANGELOG.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add plugins/dev-workflow/CHANGELOG.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 13: The evidence pack + +**Read the validation mode from the story header now.** This task produces what that mode names +and nothing derived from a value written here. + +- [ ] **Step 1: The battery, in full** + +Every step, including the version-bump check — which **now runs and must pass**, because Task 11 +has landed the bump and the WIP commit exists. Plans A and B deferred exactly this one step for +exactly that reason. + +```bash +shellcheck --shell=sh plugins/dev-workflow/hooks/codex-gate.sh && \ +shellcheck --shell=sh --exclude=SC2015 plugins/dev-workflow/hooks/codex-gate.test.sh && \ +shellcheck --shell=sh scripts/check-invariants.sh && \ +shellcheck --shell=sh --exclude=SC2015 scripts/check-invariants.test.sh && \ +shellcheck --shell=sh scripts/check-version-bump.sh && \ +shellcheck --shell=sh scripts/check-version-bump.test.sh && \ +HOOK_SH=sh sh plugins/dev-workflow/hooks/codex-gate.test.sh && \ +HOOK_SH=dash dash plugins/dev-workflow/hooks/codex-gate.test.sh && \ +sh scripts/check-invariants.test.sh && sh scripts/check-invariants.sh && \ +sh scripts/check-version-bump.test.sh && sh scripts/check-version-bump.sh main && \ +claude plugin validate . --strict && echo BATTERY-GREEN +``` + +`check-version-bump.sh` compares **commits**, so it is meaningless before the WIP exists and +meaningful now. Confirm `main` is current before trusting it. + +- [ ] **Step 2: The differential check — both revisions read** + +One question about behaviour at a derived floor of 1, answered against **both** revisions. A +check that consults only the post-change text cannot fail, which is what makes this differential +rather than decorative. + +> *At a derived floor of 1, does a Blocker/Major-free pass 1 carrying a Minor close, or keep +> looping?* + +```bash +base=$(cat .context/plan-a-base-sha) +git show "$base":CLAUDE.md | grep -n 'carrying a Minor keeps' +grep -n 'carrying a Minor keeps' CLAUDE.md +``` + +- **Pre-change** says a **pass 1** carrying a Minor keeps looping — **wrong at floor 1**, where + pass 1 *is* the floor. +- **Post-change** says a pass **below the floor** keeps looping — correct. + +**Record which revision produced which answer.** The observation that would exist if the claim +were false is a pre-change revision that already answers correctly; it does not. + +- [ ] **Step 3: The named risk-path verification** + +Recompute the floor this cycle owes from the cited story's profile header, and confirm each pass +report states it together with the axes read and the story they were read from. **The observation +that would exist if the claim were false is a pass report whose floor the cited profile does not +license.** + +- [ ] **Step 4: The knob verification — bytes, not just existence** + +*(Inherited obligation, pass-1 M9.)* Recorded **before** the cycle's first Gate-B call and +compared after: + +```bash +# before +if [ -e .context/codex-gate.floor ]; then + printf 'KNOB present, %s bytes, sha %s\n' \ + "$(wc -c < .context/codex-gate.floor | tr -d ' ')" \ + "$(shasum -a 256 .context/codex-gate.floor | cut -d' ' -f1)" +else + echo "KNOB absent — record this, and expect it absent after" +fi +``` + +Re-run after the final pass and compare. **Existence alone is not the check**: a knob whose +contents changed while its path survived would pass an existence test. **If no knob exists, +record not-applicable with that reason and do not create one** — a fixture supplying its own +input proves nothing. + +--- + +## Task 14: The Gate-B cycle + +- [ ] **Step 1: Confirm the cycle is intact** + +```bash +base=$(cat .context/plan-a-base-sha) +n=$(git rev-list --count "$base"..HEAD) +[ "$n" = 1 ] || { echo "STACKED: $n commits above base"; exit 1; } +git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } +git status --porcelain +echo "CYCLE OK — single WIP on $base" +``` + +- [ ] **Step 2: Run the loop** + +`mcp__codex__review` against the WIP commit, **`baseSha` = the recorded base**, never `HEAD~1`. +Floor **3** — the old rules govern this cycle. + +**Every call carries:** the old-rules sentence from Global Constraints; **the union of the +`Story:` headers of Plans A, B and C**, which is the governing cited set for a Gate-B cycle; and +the current evidence entry quoted verbatim. + +Findings to `.context/codex-reviews/gate-b---pass-

.md`. **Delete both +branch targets and confirm them gone before a full call.** + +> **Recovery is one attempt per pass, and deleting is not symmetric.** *(Inherited obligation, +> pass-1 M8.)* For a **full re-run**, delete both branch files. For a **single-branch resume**, +> delete **only the failed branch** — deleting both and recreating one makes the both-files check +> fail by construction, spending the attempt on a path that cannot succeed. A resume passes the +> `sessionId` back **and** its `reviewType` alongside, since the tool defaults to `full` and a +> resume omitting it can run the other reviewer and write the wrong slot. + +- [ ] **Step 3: After every accepted fix** + +```bash +git add # never -u +git status --porcelain +git commit --amend -m "WIP: review-loop economics" +``` + +**then revalidate the evidence entry, then re-review that new commit against the same base.** +*(Inherited obligation, pass-1 B6.)* A fix changes the diff, so a pass run before it does not +cover what is being committed — and evidence produced against the old diff no longer describes +the new one. + +--- + +## Task 15: Close the cycle + +- [ ] **Step 1: Revalidate the evidence one last time** + +*(Inherited obligation, pass-1 B6, second half.)* Against the content the close will carry, not +against the content the last clean pass saw — if those differ, the pass did not cover the close. + +- [ ] **Step 2: Build the closing body as a file, and check it** + +*(Inherited obligation, pass-1 MINOR 12 — `mktemp`, not a fixed path, so a concurrent process +cannot overwrite the body between validation and use.)* + +```bash +body=$(mktemp) || exit 1 +cat > "$body" <<'MSG' +feat(gates): derive the pass floor from the story profile; decide severity by consequence +MSG +# then append, in the body: what the change does; the evidence entry naming the story path +# and each verification's observation; and for EACH of the five cycles its provenance line +# and its per-pass curve, in the forms Plan B pins. +grep -c '<' "$body" # must be 0 — no angle-bracket placeholder may survive +cat "$body" +``` + +**Five cycles, so five provenance lines and five curves**: the Gate-A spec cycle, the three Gate-A +plan cycles, and this Gate-B cycle. All five carry `cycle none (pre-rule)` — every one of them +began before these rules ship, so none has a nonce, and minting one would be late-created +provenance. + +**The branch therefore demonstrates every field of both pinned forms except two**: the cycle +identifier, and the knob clause's non-absent form. Both are recorded as **undemonstrable here with +their reasons** — not as gaps, and not as satisfied. **Any cycle that both starts under these +rules and closes discharges the nonce demonstration**; duplicate discharge is harmless and +explicitly allowed. + +- [ ] **Step 3: Close** + +```bash +git commit --amend -F "$body" +rm -f "$body" +git log -1 --pretty=%s # must NOT start with WIP: +``` + +This is the first message without `WIP:`, and the hook reads it as the cycle closing. + +--- + +## Self-Review + +**Spec coverage.** §7 rollout — Tasks 1-12. §8 evidence — Task 13, with the mode read from the +story header at execution time. The combined Gate-B cycle and the close — Tasks 14 and 15. §2, +§2.1, §2.2, §2.4, §3 and §10 were Plan A's; §2.3, §4, §5 and §6 were Plan B's; both closed clean. + +**Placeholders.** None. The one templated artifact — the closing body — is built as a file and +asserted placeholder-free before it is used. + +**Inherited obligations.** All five are discharged at a named step: M8 at Task 14 Step 2, M9 at +Task 13 Step 4, M10 at Task 14 Step 3, MINOR 12 at Task 15 Step 2, B6 at Task 14 Step 3 and Task +15 Step 1. + +**Known limit, the same one both closed plans ended on.** Gate A reviews this plan, not the edits. +Each task's single check establishes that its edit landed at its site and nothing more. What the +edits do to the shipped files is Gate B's — and in this plan Gate B is not a later stage but Task +14, reading the real combined diff of all three plans. From b2800994c42d820b7ed2c829f9b12da554eb49cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:31:09 +0200 Subject: [PATCH 079/117] =?UTF-8?q?docs(plan):=20Plan=20C=20revision=202?= =?UTF-8?q?=20=E2=80=94=20fifteen=20fixed,=20one=20contract=20question=20r?= =?UTF-8?q?outed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 1: 18 findings, 5 BLOCKER, 11 MAJOR, 2 MINOR = 16 B+M. TWO CROSS-PLAN DEFECTS THAT NEITHER CLOSED CYCLE COULD HAVE SEEN, both confirmed against source before acting: - PLAN A PRINTS ITS BASE SHA AND NEVER WRITES IT. Plan A line 211 runs `git rev-parse HEAD~1` with a comment saying Plan C uses it; no task writes `.context/plan-a-base-sha`, which Plan C then read in two places. Every consumer would have failed on a missing file. Plan A's cycle is closed and byte-frozen, so Plan C now RECOVERS the value itself - validates the tip is the WIP, that it is not a merge, that its parent is a commit, and that the WIP is the sole commit above it - rather than reopening a closed artifact. - PROCESS-PR-REVIEW CONTRADICTS PLAN B. That shipped prompt tells a skipped cycle it owes the skip reason, the battery result and its evidence entries. Plan B makes the provenance line owed by EVERY cycle and requires a skip record in place of the curve. Left alone, an ordinary trivial-fix skip closes without records the other prompt calls mandatory - which invariant 11 forbids outright. Added as Task 14. The completeness sweep found a third missed site: coding-workflow's model-recording instruction sends the value to the evidence entry or the dispositions file, NEITHER OF WHICH IS KEYED TO A PASS, while Plan B pins a model field inside the per-pass curve. Added as Task 13. All three were missed because the nine-site catalogue was built before Plans A and B existed. THE EVIDENCE PACK WAS INCOMPLETE AGAINST SPEC §8 and is now seven steps: the battery against a validated base; the differential check; the risk-path verification; a PARSE CHECK over constructed valid and invalid instances of both pinned grammars; PARITY across the two copies for every shipped rule; a fresh TWELVE-ITEM prompt-standards pass; and the knob verification. The middle three were absent. THE DIFFERENTIAL CHECK COULD NOT FAIL. It grepped `carrying a Minor keeps`, which appears in BOTH revisions - success whether or not the edit happened. That is the fifth time this cycle a check has been written so it cannot fail, and the fix is the same each time: grep the discriminating text. Three counts now differ between the revisions. OTHER FIXES: the satisfied-message example claimed one N in three positions when the hook emits three different numbers; the version-bump check ran against local `main` with "confirm it is current" as prose, which can false-green on exactly the omitted-bump path it exists to catch, and now takes the recovered base and stops on mismatch; the knob check treated every existing path as a readable file and is now a state machine that also classifies the value; the Gate-B findings slots were nonce-infixed for a cycle that is pre-rule and cannot mint a nonce, so they are the bare names; the closing body is validated against both grammars and their cardinality before the amend rather than checked for angle brackets; and the temp body is now deleted only after a successful amend, with the subject check failing rather than printing. Also fixed, mine and caught by my own guard: the manifest assert emitted `grep -cF -- ""version": "0.11.0""`, which shell-parses to a pattern that can never match. The generator now single-quotes a pattern containing double quotes and refuses outright if a pattern contains both quote kinds. ROUTED, NOT ABSORBED: which commit carries each cycle's provenance line and curve. The pinned contract says each cycle records in ITS OWN closing commit body; the four Gate-A cycles have already closed and their commits contain no such records. Either those four are amended, or one aggregate body is permitted and the reconstruction documented. That is a contract decision. Task 18 Step 2 carries a blocking note and must not be executed until it is answered. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-c-rollout.md | 380 ++++++++++++------ 1 file changed, 262 insertions(+), 118 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md index dafea98..6819f6c 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -316,13 +316,13 @@ docs/getting-started.md:58:`✓ Codex Gate B satisfied (3/3 cycle, 3 on current NEW: ``` -`✓ Codex Gate B satisfied (N/N cycle, N on current fingerprint)` — N being the hook's threshold, not the derived floor — the real commit replaces +`✓ Codex Gate B satisfied (/ cycle, on current fingerprint)` — three different numbers: the calls this cycle made, the hook's reminder threshold, and how many ran against the current fingerprint. None of them is the floor §5 obliges — the real commit replaces ``` - [ ] **Assert the new text is present.** ```bash -grep -cF -- "N being the hook's threshold, not the derived floor" docs/getting-started.md +grep -cF -- "three different numbers: the calls this cycle made" docs/getting-started.md ``` Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. @@ -541,7 +541,7 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- ""version": "0.11.0"" plugins/dev-workflow/.claude-plugin/plugin.json +grep -cF -- '"version": "0.11.0"' plugins/dev-workflow/.claude-plugin/plugin.json ``` Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. @@ -628,117 +628,258 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 13: The evidence pack +## Task 13: The model-recording instruction -**Read the validation mode from the story header now.** This task produces what that mode names -and nothing derived from a value written here. +**File:** `docs/coding-workflow.md` + +**Site** — pasted `grep -n`: + +``` +docs/coding-workflow.md:279:the finding count in the pass record: the commit body's evidence entry, or the slot's +``` + +> Plan B pins a model field inside the per-pass curve. This sentence sent the value to the evidence entry or the dispositions file instead — **neither of which is keyed to a pass**, so a reader could not tell which pass a model belonged to. The health-probe procedure above it is untouched and is still how the value is established; only the destination changes. + +- [ ] **Replace.** OLD: + +``` +the finding count in the pass record: the commit body's evidence entry, or the slot's +dispositions file. This is +bookkeeping, not enforcement: nothing checks it, and a wrong entry looks exactly like a right +``` + +NEW: + +``` +the finding count in **the cycle's per-pass curve**, which pins a field for it — not the +evidence entry and not the dispositions file, neither of which is keyed to a pass. The health +probe above is how the value is established; the curve is where it goes. This is +bookkeeping, not enforcement: nothing checks it, and a wrong entry looks exactly like a right +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "the finding count in **the cycle's per-pass curve**" docs/coding-workflow.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/coding-workflow.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 14: The skipped-cycle duties in `process-pr-review` + +**File:** `plugins/dev-workflow/commands/process-pr-review.md` + +**Site** — pasted `grep -n`: + +``` +plugins/dev-workflow/commands/process-pr-review.md:159: A skip removes the review and never the evidence. Every skipped cycle runs the battery +``` + +> **A contradiction between two shipped prompts, which invariant 11 forbids.** This command told a skipped cycle it owed the skip reason, the battery result and its evidence entries. Plan B makes the provenance line owed by **every** cycle, skipped or not, and requires a skip record in place of the curve. Left alone, an ordinary trivial-fix skip would close without records the other prompt says are mandatory. +> +> Found by the completeness sweep rather than by the catalogue: the nine sites were collected before Plans A and B existed, and this one is falsified by **Plan B**, which was written afterwards. + +- [ ] **Replace.** OLD: + +``` + A skip removes the review and never the evidence. Every skipped cycle runs the battery + and records, in the commit body, **the skip reason and the battery result**. On top of + that: one mode-derived evidence entry per cited **profiled** story, and none for an + unprofiled one — which owes the reason and battery result and nothing further. +``` + +NEW: + +``` + A skip removes the review and never the evidence. Every skipped cycle runs the battery + and records, in the commit body, **the skip reason and the battery result** — and, like + every other cycle, **its provenance line and, in place of a curve, its skip record**. On + top of that: one mode-derived evidence entry per cited **profiled** story, and none for an + unprofiled one — which owes the reason, the battery result, the provenance line and the + skip record, and nothing further. +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "its provenance line and, in place of a curve, its skip record" plugins/dev-workflow/commands/process-pr-review.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add plugins/dev-workflow/commands/process-pr-review.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 15: Establish the cycle base -- [ ] **Step 1: The battery, in full** +**Plan A prints its base SHA and does not persist it.** Plan C read `.context/plan-a-base-sha`, +which nothing writes — every consumer would have failed on a missing file. Plan A's Gate-A cycle +is closed and byte-frozen, so **Plan C recovers the value itself** rather than reopening it. -Every step, including the version-bump check — which **now runs and must pass**, because Task 11 -has landed the bump and the WIP commit exists. Plans A and B deferred exactly this one step for -exactly that reason. +- [ ] **Recover, validate, and persist the base** ```bash -shellcheck --shell=sh plugins/dev-workflow/hooks/codex-gate.sh && \ -shellcheck --shell=sh --exclude=SC2015 plugins/dev-workflow/hooks/codex-gate.test.sh && \ -shellcheck --shell=sh scripts/check-invariants.sh && \ -shellcheck --shell=sh --exclude=SC2015 scripts/check-invariants.test.sh && \ -shellcheck --shell=sh scripts/check-version-bump.sh && \ -shellcheck --shell=sh scripts/check-version-bump.test.sh && \ -HOOK_SH=sh sh plugins/dev-workflow/hooks/codex-gate.test.sh && \ -HOOK_SH=dash dash plugins/dev-workflow/hooks/codex-gate.test.sh && \ -sh scripts/check-invariants.test.sh && sh scripts/check-invariants.sh && \ -sh scripts/check-version-bump.test.sh && sh scripts/check-version-bump.sh main && \ -claude plugin validate . --strict && echo BATTERY-GREEN +git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } +[ "$(git rev-list --count HEAD --not --max-count=1 HEAD~1 2>/dev/null)" ] || true +n=$(git rev-parse --verify HEAD^2 2>/dev/null && echo merge || echo single) +[ "$n" = single ] || { echo "WIP IS A MERGE — stop"; exit 1; } +base=$(git rev-parse HEAD^) +git cat-file -e "$base^{commit}" || { echo "BASE NOT A COMMIT"; exit 1; } +mkdir -p .context && printf '%s\n' "$base" > .context/plan-a-base-sha +echo "base recorded: $base" ``` -`check-version-bump.sh` compares **commits**, so it is meaningless before the WIP exists and -meaningful now. Confirm `main` is current before trusting it. +**The WIP must be the sole commit above that base**, or an earlier snapshot has been stranded: -- [ ] **Step 2: The differential check — both revisions read** +```bash +[ "$(git rev-list --count "$(cat .context/plan-a-base-sha)"..HEAD)" = 1 ] \ + || { echo "STACKED — collapse to one WIP before continuing"; exit 1; } +``` -One question about behaviour at a derived floor of 1, answered against **both** revisions. A -check that consults only the post-change text cannot fail, which is what makes this differential -rather than decorative. +`.context/` is gitignored, so this file never enters the reviewed diff. + +--- + +## Task 16: The evidence pack + +**Read the validation mode from the story header now.** This task produces what that mode names. + +- [ ] **Step 1: The battery, in full, against a base that is actually current** + +```bash +base=$(cat .context/plan-a-base-sha) +git rev-parse --verify "$base" >/dev/null || { echo "NO BASE"; exit 1; } +git merge-base --is-ancestor "$base" HEAD || { echo "BASE NOT AN ANCESTOR"; exit 1; } +``` + +Then the whole `AGENTS.md` § Commands chain **including `sh scripts/check-version-bump.sh`**, which +Plans A and B deferred for a stated reason and which now runs because Task 11 landed the bump and +the WIP exists. + +> **Pass the recovered base, not the literal `main`.** Run against a stale local `main` that +> predates an earlier bump, an *unchanged* manifest can still differ from that stale base and the +> check passes — a false green on precisely the omitted-bump path it exists to catch. Give it the +> base this cycle actually branched from, and **stop** if the two disagree rather than noting it +> in prose. + +- [ ] **Step 2: The differential check — and it must be able to fail** + +One question at a derived floor of 1, answered against **both** revisions: > *At a derived floor of 1, does a Blocker/Major-free pass 1 carrying a Minor close, or keep > looping?* ```bash base=$(cat .context/plan-a-base-sha) -git show "$base":CLAUDE.md | grep -n 'carrying a Minor keeps' -grep -n 'carrying a Minor keeps' CLAUDE.md +git show "$base":CLAUDE.md | grep -cF 'Blocker/Major-free pass 1 carrying a Minor' # expect 1 +grep -cF 'Blocker/Major-free pass 1 carrying a Minor' CLAUDE.md # expect 0 +grep -cF 'a Blocker/Major-free pass below the floor' CLAUDE.md # expect 1 ``` -- **Pre-change** says a **pass 1** carrying a Minor keeps looping — **wrong at floor 1**, where - pass 1 *is* the floor. -- **Post-change** says a pass **below the floor** keeps looping — correct. +**Grep the discriminating text, not the common tail.** `carrying a Minor keeps` appears in *both* +revisions, so a check on it returns success whether or not the edit happened — the shape of +non-failing check this cycle has found five times. The three counts above differ between the +revisions and are the observation. -**Record which revision produced which answer.** The observation that would exist if the claim -were false is a pre-change revision that already answers correctly; it does not. +- **Pre-change** says a **pass 1** carrying a Minor keeps looping — wrong at floor 1, where pass 1 + *is* the floor. **Post-change** says a pass **below the floor**. Record which revision gave + which answer. - [ ] **Step 3: The named risk-path verification** -Recompute the floor this cycle owes from the cited story's profile header, and confirm each pass -report states it together with the axes read and the story they were read from. **The observation -that would exist if the claim were false is a pass report whose floor the cited profile does not -license.** +**Recompute every provenance line's floor from the cited stories' profile headers**, not from the +pass reports — the line is the durable record and the thing a later reader parses. For each of the +five, read the `Story:` header of the artifact that cycle reviewed, derive the floor, and compare +with the line. **The observation that would exist if the claim were false is a provenance line +whose floor the cited profiles do not license.** -- [ ] **Step 4: The knob verification — bytes, not just existence** +- [ ] **Step 4: Parse both pinned grammars against constructed instances** -*(Inherited obligation, pass-1 M9.)* Recorded **before** the cycle's first Gate-B call and -compared after: +Spec §8 requires this and it is not covered by anything above. Construct and parse, for **each** +grammar: a quoted path; every `unusable()` value; a gapped `` such as `1,2,4`; a +split-model pass; a skipped cycle; a `?` count; and **one instance that must be rejected** — +`` shorter than `` enumerates. Record which feature each instance exercised. **A +grammar nothing ever parsed is a format claim, not a format.** + +- [ ] **Step 5: Parity across the two copies, for every rule Plans A and B shipped** + +Extract each shipped rule from `CLAUDE.md` and from the scaffolded template and compare the text. +**One rule is expected to differ and it is the only one**: the successor-story pointer, which +`CLAUDE.md` carries and the template must not. Any other difference is a defect. + +- [ ] **Step 6: The twelve-item prompt-standards pass, fresh** + +Over the **resulting scaffolded template** and over `workflow-init.md` as the outer command +prompt. Invariant 11 binds it and spec §8 requires it; the per-task checks do not cover it. One +status row per item per artifact, `PASS` | `N/A` | `FAIL`, with a reason for the latter two. **Item +1 for the scaffolded template is `N/A`** — Task 10 ships the note saying why. + +- [ ] **Step 7: The knob verification — states, not existence** ```bash -# before -if [ -e .context/codex-gate.floor ]; then - printf 'KNOB present, %s bytes, sha %s\n' \ - "$(wc -c < .context/codex-gate.floor | tr -d ' ')" \ - "$(shasum -a 256 .context/codex-gate.floor | cut -d' ' -f1)" +K=.context/codex-gate.floor +if [ ! -e "$K" ]; then + echo "KNOB absent" +elif [ ! -f "$K" ]; then + echo "KNOB present but not a regular file — unusable(unreadable)"; exit 1 +elif [ ! -r "$K" ]; then + echo "KNOB unreadable — unusable(unreadable)"; exit 1 else - echo "KNOB absent — record this, and expect it absent after" + bytes=$(wc -c < "$K" | tr -d ' ') || exit 1 + digest=$(shasum -a 256 "$K" | cut -d' ' -f1) || { echo "no shasum available"; exit 1; } + printf 'KNOB present, %s bytes, sha %s\n' "$bytes" "$digest" fi ``` -Re-run after the final pass and compare. **Existence alone is not the check**: a knob whose -contents changed while its path survived would pass an existence test. **If no knob exists, -record not-applicable with that reason and do not create one** — a fixture supplying its own -input proves nothing. +Record before the first Gate-B call and compare after. **Existence is not the check** — a knob +whose contents changed while its path survived would pass one. **If absent, record +not-applicable with that reason and do not create one**; a fixture supplying its own input proves +nothing. Separately **classify the value** as numeric or as one of the pinned unusable causes, +since the provenance lines' knob clause needs that classification and preservation alone does not +supply it. --- -## Task 14: The Gate-B cycle +## Task 17: The Gate-B cycle -- [ ] **Step 1: Confirm the cycle is intact** - -```bash -base=$(cat .context/plan-a-base-sha) -n=$(git rev-list --count "$base"..HEAD) -[ "$n" = 1 ] || { echo "STACKED: $n commits above base"; exit 1; } -git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } -git status --porcelain -echo "CYCLE OK — single WIP on $base" -``` +- [ ] **Step 1: Confirm the cycle is intact** — the check from Task 15, re-run. - [ ] **Step 2: Run the loop** `mcp__codex__review` against the WIP commit, **`baseSha` = the recorded base**, never `HEAD~1`. -Floor **3** — the old rules govern this cycle. +**Floor 3** — the old rules govern this cycle. **Every call carries:** the old-rules sentence from Global Constraints; **the union of the `Story:` headers of Plans A, B and C**, which is the governing cited set for a Gate-B cycle; and the current evidence entry quoted verbatim. -Findings to `.context/codex-reviews/gate-b---pass-

.md`. **Delete both -branch targets and confirm them gone before a full call.** +**Findings go to the bare slots — `gate-b--pass-

.md`.** This cycle began before +the nonce rules ship, so it is pre-rule, has no nonce, and **cannot mint one**; the nonce-infixed +names Plan B introduces are reserved for cycles that start after. Using them here would fabricate +the very provenance the closing body records as absent. + +**Delete both branch targets and confirm them gone before a full call.** -> **Recovery is one attempt per pass, and deleting is not symmetric.** *(Inherited obligation, -> pass-1 M8.)* For a **full re-run**, delete both branch files. For a **single-branch resume**, -> delete **only the failed branch** — deleting both and recreating one makes the both-files check -> fail by construction, spending the attempt on a path that cannot succeed. A resume passes the -> `sessionId` back **and** its `reviewType` alongside, since the tool defaults to `full` and a -> resume omitting it can run the other reviewer and write the wrong slot. +> **Recovery is one attempt per pass, and deleting is not symmetric.** For a **full re-run**, +> delete both branch files. For a **single-branch resume**, delete **only the failed branch** — +> deleting both and recreating one makes the both-files check fail by construction. A resume +> passes the `sessionId` back **and** its `reviewType` alongside, since the tool defaults to +> `full` and a resume omitting it can run the other reviewer and write the wrong slot. - [ ] **Step 3: After every accepted fix** @@ -748,74 +889,77 @@ git status --porcelain git commit --amend -m "WIP: review-loop economics" ``` -**then revalidate the evidence entry, then re-review that new commit against the same base.** -*(Inherited obligation, pass-1 B6.)* A fix changes the diff, so a pass run before it does not -cover what is being committed — and evidence produced against the old diff no longer describes -the new one. +**then revalidate the evidence entry, then re-review that new commit against the same base.** A +fix changes the diff, so a pass run before it does not cover what is being committed — and +evidence produced against the old diff no longer describes the new one. --- -## Task 15: Close the cycle +## Task 18: Close the cycle -- [ ] **Step 1: Revalidate the evidence one last time** +- [ ] **Step 1: Revalidate the evidence** against the content the close will carry, not against + what the last clean pass saw. If they differ, that pass did not cover the close. -*(Inherited obligation, pass-1 B6, second half.)* Against the content the close will carry, not -against the content the last clean pass saw — if those differ, the pass did not cover the close. - -- [ ] **Step 2: Build the closing body as a file, and check it** - -*(Inherited obligation, pass-1 MINOR 12 — `mktemp`, not a fixed path, so a concurrent process -cannot overwrite the body between validation and use.)* +- [ ] **Step 2: Build the closing body and validate it before use** ```bash body=$(mktemp) || exit 1 -cat > "$body" <<'MSG' -feat(gates): derive the pass floor from the story profile; decide severity by consequence -MSG -# then append, in the body: what the change does; the evidence entry naming the story path -# and each verification's observation; and for EACH of the five cycles its provenance line -# and its per-pass curve, in the forms Plan B pins. -grep -c '<' "$body" # must be 0 — no angle-bracket placeholder may survive -cat "$body" +# assemble: subject; what the change does; the evidence entry naming the story path and each +# verification's observation; and for EACH cycle its provenance line and its per-pass curve. +grep -c '<' "$body" # 0 — no placeholder survived +grep -c '; floor .* per .*; hook reminder threshold ' "$body" # one per cycle recorded +grep -c ': Findings .* Blockers .* Majors ' "$body" # one per cycle recorded ``` -**Five cycles, so five provenance lines and five curves**: the Gate-A spec cycle, the three Gate-A -plan cycles, and this Gate-B cycle. All five carry `cycle none (pre-rule)` — every one of them -began before these rules ship, so none has a nonce, and minting one would be late-created -provenance. +**Assert the cardinality and parse each record** against the two pinned grammars before amending — +absence of angle brackets is not validation. Check that each curve's `` has exactly as +many entries as its `` enumerates, and that every cycle field agrees between a cycle's line +and its curve. + +**Every cycle on this branch is pre-rule** — each began before these rules ship — so each carries +`cycle none (pre-rule)`, and minting a nonce for any of them would be late-created provenance. +**The branch therefore demonstrates every field of both forms except two**: the cycle identifier, +and the knob clause's non-absent form. Record both as **undemonstrable here, with their reasons** — +not as gaps, not as satisfied. **Any cycle that both starts under these rules and closes +discharges the nonce demonstration**; duplicate discharge is harmless. -**The branch therefore demonstrates every field of both pinned forms except two**: the cycle -identifier, and the knob clause's non-absent form. Both are recorded as **undemonstrable here with -their reasons** — not as gaps, and not as satisfied. **Any cycle that both starts under these -rules and closes discharges the nonce demonstration**; duplicate discharge is harmless and -explicitly allowed. +> **Which commit carries which record is an open question routed to the human**, and this step +> must not be executed until it is answered. The pinned contract says each cycle records in **its +> own** closing commit body; four Gate-A cycles have already closed, and their commits contain no +> such records. Either those four commits are amended, or one aggregate body is permitted and the +> reconstruction is documented. **That is a contract decision, not a drafting choice.** -- [ ] **Step 3: Close** +- [ ] **Step 3: Close, and fail loudly if it does not** ```bash -git commit --amend -F "$body" -rm -f "$body" -git log -1 --pretty=%s # must NOT start with WIP: +if git commit --amend -F "$body"; then + git log -1 --pretty=%s | grep -q '^WIP:' && { echo "STILL WIP — cycle not closed"; exit 1; } + echo "CLOSED: $(git log -1 --pretty=%s)" + rm -f "$body" +else + echo "AMEND FAILED — body preserved at $body"; exit 1 +fi ``` -This is the first message without `WIP:`, and the hook reads it as the cycle closing. +**The body is deleted only after a successful amend**, and the subject check **fails** rather than +printing. A transient failure that removed the assembled body would destroy the only copy of the +evidence pack while leaving the cycle open. --- ## Self-Review -**Spec coverage.** §7 rollout — Tasks 1-12. §8 evidence — Task 13, with the mode read from the -story header at execution time. The combined Gate-B cycle and the close — Tasks 14 and 15. §2, -§2.1, §2.2, §2.4, §3 and §10 were Plan A's; §2.3, §4, §5 and §6 were Plan B's; both closed clean. +**Spec coverage.** §7 rollout — Tasks 1-14. §8 evidence — Task 16, all seven steps, with the mode +read from the story header at execution time. The cycle and the close — Tasks 15, 17, 18. + +**Placeholders.** None. The closing body is built as a file and validated before use. -**Placeholders.** None. The one templated artifact — the closing body — is built as a file and -asserted placeholder-free before it is used. +**Inherited obligations.** M8 at Task 17 Step 2; M9 at Task 16 Step 7; M10 at Task 17 Step 3; +MINOR 12 at Task 18 Steps 2-3; B6 at Task 17 Step 3 and Task 18 Step 1. -**Inherited obligations.** All five are discharged at a named step: M8 at Task 14 Step 2, M9 at -Task 13 Step 4, M10 at Task 14 Step 3, MINOR 12 at Task 15 Step 2, B6 at Task 14 Step 3 and Task -15 Step 1. +**Open, and blocking Task 18 Step 2 only:** which commit carries each cycle's provenance line and +curve. Routed. -**Known limit, the same one both closed plans ended on.** Gate A reviews this plan, not the edits. -Each task's single check establishes that its edit landed at its site and nothing more. What the -edits do to the shipped files is Gate B's — and in this plan Gate B is not a later stage but Task -14, reading the real combined diff of all three plans. +**Known limit.** Gate A reviews this plan, not the edits. Each task's single check establishes +that its edit landed at its site. What the edits do is Gate B's — which here is Task 17, reading +the real combined diff of all three plans. From 5b12030338d4c8321530a4b6036252b9ecb9b18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:24:24 +0200 Subject: [PATCH 080/117] =?UTF-8?q?docs(plan):=20Plan=20C=20revision=203?= =?UTF-8?q?=20=E2=80=94=20strip=20the=20theater,=20settle=20the=20records,?= =?UTF-8?q?=20add=20two=20sites?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 2: 20 findings, 4 BLOCKER, 13 MAJOR = 17 B+M, up from 16 after fixing fifteen. Three of the four Blockers were caused by those fixes. Stopped rather than starting a third repair round; both open decisions are now answered. THE CHECKS ARE STRIPPED, the established cure applied a third time. Revision 2 carried SIX checks that could not fail: a base recovery that defined the base as the tip's parent and then proved one commit sat above that parent, true by construction; three greps that printed counts and never compared them; a parse check with no parser; a knob state machine testing `! -e` before path type, so a broken symlink classified as absent - the exact case it was added to catch; and closing-body validation that printed rather than asserted. Gone. WHAT DID NOT GET STRIPPED, and this is a correction to the instruction I was given. The survive-list I received named four items. Spec §8 names SEVEN, and the three omitted ones are explicit obligations, not plan self-checking: "- A parse check over both pinned grammars. The branch's own instances cannot exercise them ... A grammar nothing ever parsed is a format claim, not a format. - Parity across every changed rule, in both copies ... - A fresh twelve-item docs/prompt-standards.md pass." Dropping a spec-required obligation because the version I wrote of it was badly built would be the wrong lesson. All seven are in Task 18. The theater was inside them; the obligations are what the author owes. THE RECORDS QUESTION IS SETTLED - one aggregate closing body, one record per cycle, five of each, with the four Gate-A cycles' marked "reconstructed from validated pass files (pre-rule cycle)" and their sources named. The grounding offered to me was §10's activation rule. Reading §8 directly gives a stronger one: it says "No reconstruction is needed and none is claimed" and states its grounds in the same breath - "the Gate-A spec cycle has not closed ... and the Gate-A plan cycle has not started, so both write their closing bodies natively." THAT PREMISE FAILED. The spec cycle closed at pass 34, before Plan B existed to pin any form, and the single plan cycle §8 anticipated became three, all closing the same way. So §8's sentence is a true prediction about a topology that changed, not a prohibition being read around - and what it was protecting, that no record silently claims to be contemporaneous, is exactly what the marking preserves. §8's "three closing bodies" is stale for the same reason, and in the same way as the story criterion's "a run of all three holds three": both predate the split. The rule is one record per cycle; five cycles ran. TASK 14'S CLAIM LIVES IN THREE FILES, not one. I repaired process-pr-review and reported it fixed; both §5 copies still said the old thing. Now Tasks 14, 15 and 16, one repair. The rule taken from it is about WHEN rather than what: a fixed statement's other homes are found by grepping for the CLAIM at fix time, not by the next review pass. Grepping the phrasing finds the copy you already have. AGENTS.md records the same failure in another form, where four rounds each searched for the previous phrase and a synonym survived every time. Sixteen edits, sixteen assert patterns, guard green: each occurs exactly once in its task's NEW text and zero times in its OLD. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-c-rollout.md | 351 ++++++++++-------- 1 file changed, 190 insertions(+), 161 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md index 6819f6c..ea7c6e3 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -674,7 +674,7 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 14: The skipped-cycle duties in `process-pr-review` +## Task 14: The skipped-cycle duties — 1 of 3, `process-pr-review` **File:** `plugins/dev-workflow/commands/process-pr-review.md` @@ -686,7 +686,9 @@ plugins/dev-workflow/commands/process-pr-review.md:159: A skip removes the rev > **A contradiction between two shipped prompts, which invariant 11 forbids.** This command told a skipped cycle it owed the skip reason, the battery result and its evidence entries. Plan B makes the provenance line owed by **every** cycle, skipped or not, and requires a skip record in place of the curve. Left alone, an ordinary trivial-fix skip would close without records the other prompt says are mandatory. > -> Found by the completeness sweep rather than by the catalogue: the nine sites were collected before Plans A and B existed, and this one is falsified by **Plan B**, which was written afterwards. +> **This claim lives in three files, and Tasks 14, 15 and 16 are one repair.** I fixed this one and reported it fixed; the sweep found the second and third a pass later. +> +> **The rule taken from it, and it is a rule about when rather than what: a fixed statement's other homes are found by grepping for the CLAIM at fix time, not by the next review pass.** Grepping for the phrasing finds the copy you already have; grepping for what the sentence asserts finds the others. `AGENTS.md` records the same failure in a different form, where four rounds each searched for the previous phrase and a synonym survived every time. - [ ] **Replace.** OLD: @@ -725,241 +727,268 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 15: Establish the cycle base +## Task 15: The skipped-cycle duties — 2 of 3, `CLAUDE.md` §5 -**Plan A prints its base SHA and does not persist it.** Plan C read `.context/plan-a-base-sha`, -which nothing writes — every consumer would have failed on a missing file. Plan A's Gate-A cycle -is closed and byte-frozen, so **Plan C recovers the value itself** rather than reopening it. +**File:** `CLAUDE.md` -- [ ] **Recover, validate, and persist the base** +**Site** — pasted `grep -n`: -```bash -git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } -[ "$(git rev-list --count HEAD --not --max-count=1 HEAD~1 2>/dev/null)" ] || true -n=$(git rev-parse --verify HEAD^2 2>/dev/null && echo merge || echo single) -[ "$n" = single ] || { echo "WIP IS A MERGE — stop"; exit 1; } -base=$(git rev-parse HEAD^) -git cat-file -e "$base^{commit}" || { echo "BASE NOT A COMMIT"; exit 1; } -mkdir -p .context && printf '%s\n' "$base" > .context/plan-a-base-sha -echo "base recorded: $base" +``` +CLAUDE.md:677:the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the ``` -**The WIP must be the sole commit above that base**, or an earlier snapshot has been stranded: +> The same claim as the previous task, in the governing copy. See that task's note for the sweep rule. -```bash -[ "$(git rev-list --count "$(cat .context/plan-a-base-sha)"..HEAD)" = 1 ] \ - || { echo "STACKED — collapse to one WIP before continuing"; exit 1; } +- [ ] **Replace.** OLD: + +``` +the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the +battery and lands its evidence entry beside the reason; a skipped **unprofiled** story +records the reason and the battery result and nothing more, because it owes no mode-derived +entry and keeps exactly today's judgement-based skip. ``` -`.context/` is gitignored, so this file never enters the reviewed diff. +NEW: ---- +``` +the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the +battery and lands its evidence entry beside the reason; a skipped **unprofiled** story +records the reason and the battery result, because it owes no mode-derived entry and keeps +exactly today's judgement-based skip. **Neither is excused the records every cycle owes** — +the provenance line, and a skip record in place of the curve. +``` -## Task 16: The evidence pack +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "Neither is excused the records every cycle owes" CLAUDE.md +``` -**Read the validation mode from the story header now.** This task produces what that mode names. +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. -- [ ] **Step 1: The battery, in full, against a base that is actually current** +- [ ] **Amend the WIP commit.** ```bash -base=$(cat .context/plan-a-base-sha) -git rev-parse --verify "$base" >/dev/null || { echo "NO BASE"; exit 1; } -git merge-base --is-ancestor "$base" HEAD || { echo "BASE NOT AN ANCESTOR"; exit 1; } +git add CLAUDE.md +git commit --amend -m "WIP: review-loop economics" ``` -Then the whole `AGENTS.md` § Commands chain **including `sh scripts/check-version-bump.sh`**, which -Plans A and B deferred for a stated reason and which now runs because Task 11 landed the bump and -the WIP exists. +--- -> **Pass the recovered base, not the literal `main`.** Run against a stale local `main` that -> predates an earlier bump, an *unchanged* manifest can still differ from that stale base and the -> check passes — a false green on precisely the omitted-bump path it exists to catch. Give it the -> base this cycle actually branched from, and **stop** if the two disagree rather than noting it -> in prose. +## Task 16: The skipped-cycle duties — 3 of 3, the scaffolded template -- [ ] **Step 2: The differential check — and it must be able to fail** +**File:** `plugins/dev-workflow/commands/workflow-init.md` -One question at a derived floor of 1, answered against **both** revisions: +**Site** — pasted `grep -n`: -> *At a derived floor of 1, does a Blocker/Major-free pass 1 carrying a Minor close, or keep -> looping?* +``` +plugins/dev-workflow/commands/workflow-init.md:856:the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the +``` + +> The same claim as the previous two, in the mirror. **The assert here counts 1 in this file and the previous task's counts 1 in `CLAUDE.md`** — the same pattern in two files, because the two copies carry the sentence independently and a fix to one has never implied a fix to the other. + +- [ ] **Replace.** OLD: -```bash -base=$(cat .context/plan-a-base-sha) -git show "$base":CLAUDE.md | grep -cF 'Blocker/Major-free pass 1 carrying a Minor' # expect 1 -grep -cF 'Blocker/Major-free pass 1 carrying a Minor' CLAUDE.md # expect 0 -grep -cF 'a Blocker/Major-free pass below the floor' CLAUDE.md # expect 1 +``` +the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the +battery and lands its evidence entry beside the reason; a skipped **unprofiled** story +records the reason and the battery result and nothing more, because it owes no mode-derived +entry and keeps exactly today's judgement-based skip. ``` -**Grep the discriminating text, not the common tail.** `carrying a Minor keeps` appears in *both* -revisions, so a check on it returns success whether or not the edit happened — the shape of -non-failing check this cycle has found five times. The three counts above differ between the -revisions and are the observation. +NEW: -- **Pre-change** says a **pass 1** carrying a Minor keeps looping — wrong at floor 1, where pass 1 - *is* the floor. **Post-change** says a pass **below the floor**. Record which revision gave - which answer. +``` +the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the +battery and lands its evidence entry beside the reason; a skipped **unprofiled** story +records the reason and the battery result, because it owes no mode-derived entry and keeps +exactly today's judgement-based skip. **Neither is excused the records every cycle owes** — +the provenance line, and a skip record in place of the curve. +``` -- [ ] **Step 3: The named risk-path verification** +- [ ] **Assert the new text is present.** -**Recompute every provenance line's floor from the cited stories' profile headers**, not from the -pass reports — the line is the durable record and the thing a later reader parses. For each of the -five, read the `Story:` header of the artifact that cycle reviewed, derive the floor, and compare -with the line. **The observation that would exist if the claim were false is a provenance line -whose floor the cited profiles do not license.** +```bash +grep -cF -- "Neither is excused the records every cycle owes" plugins/dev-workflow/commands/workflow-init.md +``` -- [ ] **Step 4: Parse both pinned grammars against constructed instances** +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. -Spec §8 requires this and it is not covered by anything above. Construct and parse, for **each** -grammar: a quoted path; every `unusable()` value; a gapped `` such as `1,2,4`; a -split-model pass; a skipped cycle; a `?` count; and **one instance that must be rejected** — -`` shorter than `` enumerates. Record which feature each instance exercised. **A -grammar nothing ever parsed is a format claim, not a format.** +- [ ] **Amend the WIP commit.** -- [ ] **Step 5: Parity across the two copies, for every rule Plans A and B shipped** +```bash +git add plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` -Extract each shipped rule from `CLAUDE.md` and from the scaffolded template and compare the text. -**One rule is expected to differ and it is the only one**: the successor-story pointer, which -`CLAUDE.md` carries and the template must not. Any other difference is a defect. +--- -- [ ] **Step 6: The twelve-item prompt-standards pass, fresh** +## Task 17: Record the cycle base -Over the **resulting scaffolded template** and over `workflow-init.md` as the outer command -prompt. Invariant 11 binds it and spec §8 requires it; the per-task checks do not cover it. One -status row per item per artifact, `PASS` | `N/A` | `FAIL`, with a reason for the latter two. **Item -1 for the scaffolded template is `N/A`** — Task 10 ships the note saying why. +**Plan A prints its base SHA and does not persist it**, and Plan A is closed and byte-frozen, so +Plan C establishes it. This is an action, not a proof: revision 2 wrapped it in a check that +defined the base as the tip's parent and then proved one commit sat above that parent, which is +true by construction and could not fail. -- [ ] **Step 7: The knob verification — states, not existence** +- [ ] **Record it** ```bash -K=.context/codex-gate.floor -if [ ! -e "$K" ]; then - echo "KNOB absent" -elif [ ! -f "$K" ]; then - echo "KNOB present but not a regular file — unusable(unreadable)"; exit 1 -elif [ ! -r "$K" ]; then - echo "KNOB unreadable — unusable(unreadable)"; exit 1 -else - bytes=$(wc -c < "$K" | tr -d ' ') || exit 1 - digest=$(shasum -a 256 "$K" | cut -d' ' -f1) || { echo "no shasum available"; exit 1; } - printf 'KNOB present, %s bytes, sha %s\n' "$bytes" "$digest" -fi +git rev-parse --verify HEAD^2 >/dev/null 2>&1 && { echo "WIP IS A MERGE — stop"; exit 1; } +mkdir -p .context +git rev-parse HEAD^ > .context/plan-a-base-sha +cat .context/plan-a-base-sha ``` -Record before the first Gate-B call and compare after. **Existence is not the check** — a knob -whose contents changed while its path survived would pass one. **If absent, record -not-applicable with that reason and do not create one**; a fixture supplying its own input proves -nothing. Separately **classify the value** as numeric or as one of the pinned unusable causes, -since the provenance lines' knob clause needs that classification and preservation alone does not -supply it. +The merge test is the one condition worth asserting, because `HEAD^` is ambiguous only there. +`.context/` is gitignored, so this never enters the reviewed diff. --- -## Task 17: The Gate-B cycle +## Task 18: The evidence pack -- [ ] **Step 1: Confirm the cycle is intact** — the check from Task 15, re-run. +Spec §8 names seven obligations. **All seven are here** — they are what the author owes, not +checks the plan invented about itself, and the mode is read from the story header at execution +time. -- [ ] **Step 2: Run the loop** +- [ ] **1 — Battery.** The full `AGENTS.md` § Commands chain, green, **including + `sh scripts/check-version-bump.sh`**, which Plans A and B deferred for a stated reason and which + now runs because Task 11 landed the bump and the WIP exists. Pass it the recorded base rather + than a local `main` that may predate an earlier bump. -`mcp__codex__review` against the WIP commit, **`baseSha` = the recorded base**, never `HEAD~1`. -**Floor 3** — the old rules govern this cycle. +- [ ] **2 — A check that fails without the change**, read against **both** revisions: -**Every call carries:** the old-rules sentence from Global Constraints; **the union of the -`Story:` headers of Plans A, B and C**, which is the governing cited set for a Gate-B cycle; and -the current evidence entry quoted verbatim. +> *At a derived floor of 1, does a Blocker/Major-free pass 1 carrying a Minor close, or keep +> looping?* -**Findings go to the bare slots — `gate-b--pass-

.md`.** This cycle began before -the nonce rules ship, so it is pre-rule, has no nonce, and **cannot mint one**; the nonce-infixed -names Plan B introduces are reserved for cycles that start after. Using them here would fabricate -the very provenance the closing body records as absent. +```bash +base=$(cat .context/plan-a-base-sha) +git show "$base":CLAUDE.md | grep -cF 'Blocker/Major-free pass 1 carrying a Minor' +grep -cF 'a Blocker/Major-free pass below the floor' CLAUDE.md +``` -**Delete both branch targets and confirm them gone before a full call.** +**Grep the discriminating text, not the common tail** — `carrying a Minor keeps` appears in both +revisions and would return success either way. Pre-change says **pass 1** keeps looping, wrong at +floor 1 where pass 1 *is* the floor; post-change says **below the floor**. Record which revision +gave which answer. -> **Recovery is one attempt per pass, and deleting is not symmetric.** For a **full re-run**, -> delete both branch files. For a **single-branch resume**, delete **only the failed branch** — -> deleting both and recreating one makes the both-files check fail by construction. A resume -> passes the `sessionId` back **and** its `reviewType` alongside, since the tool defaults to -> `full` and a resume omitting it can run the other reviewer and write the wrong slot. +- [ ] **3 — A named verification of the risk path.** Recompute each provenance line's floor from + the `Story:` header of the artifact that cycle reviewed. **The observation that would exist if + the claim were false is a line whose floor the cited profiles do not license.** -- [ ] **Step 3: After every accepted fix** +- [ ] **4 — The conditional knob verification.** If `.context/codex-gate.floor` exists, record its + bytes and digest before the first Gate-B call and compare after. **If it does not exist, record + not-applicable with that reason and do not create one** — a fixture supplying its own input + proves nothing. Separately classify the value as numeric or as one of the pinned unusable + causes, which the provenance lines' knob clause needs and preservation does not supply. -```bash -git add # never -u -git status --porcelain -git commit --amend -m "WIP: review-loop economics" -``` +- [ ] **5 — A parse check over both pinned grammars.** Construct and parse, for each: a quoted + path; every `unusable()` value; a gapped `` such as `1,2,4`; a split-model pass; a + skipped cycle; a `?` count; and **at least one instance that must be rejected**. Record which + feature each exercises. §8 is explicit that the branch's own instances cannot exercise the + grammars, which is why this is constructed rather than observed. + +- [ ] **6 — Parity across every changed rule**, in both copies. **One difference is expected and + is the only one**: the successor-story pointer, which `CLAUDE.md` carries and the template must + not. -**then revalidate the evidence entry, then re-review that new commit against the same base.** A -fix changes the diff, so a pass run before it does not cover what is being committed — and -evidence produced against the old diff no longer describes the new one. +- [ ] **7 — A fresh twelve-item `docs/prompt-standards.md` pass**, over each changed prompt + artifact invariant 11 names — the **resulting scaffolded template**, **`workflow-init.md`**, and + **`process-pr-review.md`**, which Task 14 changes. One status row per item per artifact. + **Item 1 for the scaffolded template is `N/A`**, and Task 10 ships the note saying why. --- -## Task 18: Close the cycle +## Task 19: The Gate-B cycle -- [ ] **Step 1: Revalidate the evidence** against the content the close will carry, not against - what the last clean pass saw. If they differ, that pass did not cover the close. +- [ ] **Run the loop.** `mcp__codex__review` against the WIP commit, **`baseSha` = the recorded + base**. **Floor 3** — the old rules govern this cycle. -- [ ] **Step 2: Build the closing body and validate it before use** +**Every call carries:** the old-rules sentence from Global Constraints; **the union of the +`Story:` headers of Plans A, B and C**; and the current evidence entry quoted verbatim. -```bash -body=$(mktemp) || exit 1 -# assemble: subject; what the change does; the evidence entry naming the story path and each -# verification's observation; and for EACH cycle its provenance line and its per-pass curve. -grep -c '<' "$body" # 0 — no placeholder survived -grep -c '; floor .* per .*; hook reminder threshold ' "$body" # one per cycle recorded -grep -c ': Findings .* Blockers .* Majors ' "$body" # one per cycle recorded -``` +**Findings go to the bare slots — `gate-b--pass-

.md`.** This cycle is pre-rule, +has no nonce and cannot mint one; the nonce-infixed names Plan B introduces are reserved for +cycles starting after the rules ship. + +**Delete both branch targets before a full call. For a single-branch resume, delete only the +failed branch** — deleting both and recreating one makes the both-files check fail by +construction. A resume passes the `sessionId` back **and** its `reviewType`, since the tool +defaults to `full` and a resume omitting it can write the wrong slot. -**Assert the cardinality and parse each record** against the two pinned grammars before amending — -absence of angle brackets is not validation. Check that each curve's `` has exactly as -many entries as its `` enumerates, and that every cycle field agrees between a cycle's line -and its curve. +- [ ] **After every accepted fix:** stage the exact paths the fix touched — **never `-u`** — amend + with `-m "WIP: review-loop economics"`, **revalidate the evidence entry**, then re-review that + new commit against the same base. A fix changes the diff, so a pass run before it does not cover + what is being committed. -**Every cycle on this branch is pre-rule** — each began before these rules ship — so each carries -`cycle none (pre-rule)`, and minting a nonce for any of them would be late-created provenance. -**The branch therefore demonstrates every field of both forms except two**: the cycle identifier, -and the knob clause's non-absent form. Record both as **undemonstrable here, with their reasons** — -not as gaps, not as satisfied. **Any cycle that both starts under these rules and closes -discharges the nonce demonstration**; duplicate discharge is harmless. +--- -> **Which commit carries which record is an open question routed to the human**, and this step -> must not be executed until it is answered. The pinned contract says each cycle records in **its -> own** closing commit body; four Gate-A cycles have already closed, and their commits contain no -> such records. Either those four commits are amended, or one aggregate body is permitted and the -> reconstruction is documented. **That is a contract decision, not a drafting choice.** +## Task 20: Close the cycle + +- [ ] **Step 1: Revalidate the evidence** against the content the close will carry. + +- [ ] **Step 2: Assemble the closing body** + +It carries the evidence entry, and **one provenance line and one curve per cycle — five of each**, +this change having run one Gate-A spec cycle, three Gate-A plan cycles and one Gate-B cycle. + +**One is native and four are reconstructed, and the body says which.** The Gate-B cycle writes its +own records as it closes. The four Gate-A cycles closed before Plan B pinned the forms, so each of +theirs is marked **"reconstructed from validated pass files (pre-rule cycle)"** with the files +named. **Reconstruction from validated sources, marked as such, is not fabrication** — the marking +is what separates them, exactly as `cycle none (pre-rule)` already separates a cycle that cannot +have a nonce from one that simply lacks one. + +> **Why this does not contradict §8, and the reason is a failed premise rather than a scope +> limit.** §8 says *"No reconstruction is needed and none is claimed"* — and gives its grounds in +> the same breath: *"the Gate-A spec cycle has not closed … and the Gate-A plan cycle has not +> started, so both write their closing bodies natively."* **That premise no longer holds.** The +> spec cycle closed at pass 34, before Plan B existed to pin any form; and the single plan cycle +> §8 anticipated became three, all of which closed the same way. §8's sentence was a true +> prediction about a topology that changed. **What it was protecting — that no record silently +> claims to be contemporaneous — is preserved by the marking, which is the stronger reading.** +> +> **§8's "three closing bodies" is stale for the same reason**, and in the same way as the story +> criterion's "a run of all three holds three": both were written before this change was split +> into three plans. **The rule is one record per cycle**; the count follows from how many cycles +> ran, which is five. + +**Every cycle here is pre-rule**, so each carries `cycle none (pre-rule)`; minting a nonce for any +would be late-created provenance. **The branch demonstrates every field of both forms except two** +— the cycle identifier and the knob clause's non-absent form — **recorded as undemonstrable here +with their reasons**, not as gaps and not as satisfied. **Any cycle that both starts under these +rules and closes discharges the nonce demonstration**; duplicate discharge is harmless. - [ ] **Step 3: Close, and fail loudly if it does not** ```bash if git commit --amend -F "$body"; then - git log -1 --pretty=%s | grep -q '^WIP:' && { echo "STILL WIP — cycle not closed"; exit 1; } - echo "CLOSED: $(git log -1 --pretty=%s)" - rm -f "$body" + git log -1 --pretty=%s | grep -q '^WIP:' && { echo "STILL WIP — not closed"; exit 1; } + echo "CLOSED: $(git log -1 --pretty=%s)"; rm -f "$body" else echo "AMEND FAILED — body preserved at $body"; exit 1 fi ``` -**The body is deleted only after a successful amend**, and the subject check **fails** rather than -printing. A transient failure that removed the assembled body would destroy the only copy of the +The body is deleted **only after a successful amend**, and the subject check **fails** rather than +printing. A transient failure that removed the body would destroy the only assembled copy of the evidence pack while leaving the cycle open. --- ## Self-Review -**Spec coverage.** §7 rollout — Tasks 1-14. §8 evidence — Task 16, all seven steps, with the mode -read from the story header at execution time. The cycle and the close — Tasks 15, 17, 18. - -**Placeholders.** None. The closing body is built as a file and validated before use. +**Spec coverage.** §7 rollout — Tasks 1-16. §8 evidence — Task 18, **all seven obligations**. The +cycle and the close — Tasks 17, 19, 20. -**Inherited obligations.** M8 at Task 17 Step 2; M9 at Task 16 Step 7; M10 at Task 17 Step 3; -MINOR 12 at Task 18 Steps 2-3; B6 at Task 17 Step 3 and Task 18 Step 1. +**What was stripped, and what was not.** Revision 2 carried six checks that could not fail — a +circular base proof, greps that printed counts without comparing them, a parse check with no +parser, a knob state machine that classified a broken symlink as absent, and validation that +printed rather than asserted. **Those are gone.** §8's seven obligations are not checks the plan +invented about its own execution; they are what the author owes, and all seven stayed. -**Open, and blocking Task 18 Step 2 only:** which commit carries each cycle's provenance line and -curve. Routed. +**Inherited obligations.** M8 and M10 at Task 19; M9 at Task 18 item 4; MINOR 12 and B6 at Task +20. **Known limit.** Gate A reviews this plan, not the edits. Each task's single check establishes -that its edit landed at its site. What the edits do is Gate B's — which here is Task 17, reading -the real combined diff of all three plans. +that its edit landed at its site. What the edits do is Gate B's — here, Task 19, reading the real +combined diff of all three plans. From 83a61b65d30447dd15314a2ed88e0671d692a31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 16:40:11 +0200 Subject: [PATCH 081/117] =?UTF-8?q?docs(plan):=20Plan=20C=20revision=204?= =?UTF-8?q?=20=E2=80=94=20the=20strip=20took=20two=20real=20things=20with?= =?UTF-8?q?=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 3: 20 findings, 2 BLOCKER, 13 MAJOR = 15 B+M. Curve 16, 17, 15; Blockers 5, 4, 2. Both trending down for the first time. BOTH BLOCKERS WERE THE SAME LESSON: stripping a check deleted something real. - Task 17's base check had a CIRCULAR PROOF but a GENUINE NEED. Revision 2 defined the base as HEAD^ then proved one commit sat above it - true by construction. Revision 3 deleted the proof AND the need. A stacked WIP would silently exclude the earlier snapshot from the only review. What is checkable without circularity is whether the PARENT is itself a WIP commit - that asks about a different commit, which is why it can fail. - Task 20's closing-body block held `body=$(mktemp)` as well as its validation. Stripping the validation removed the assignment; Step 3 still expanded $body. Recorded in the self-review as the cure's cost: deleting a check deletes whatever else was in the block. THE MOST SERIOUS FINDING WAS NEITHER OF THOSE. Revision 3 sent Gate-B findings to the BARE slots, reasoning that a pre-rule cycle cannot mint a nonce. THIS WORKSPACE ALREADY CONTAINS 61 FILES IN THE BARE gate-b-*-pass-* FAMILY from earlier cycles, and the delete-before-call step would destroy them - the exact incident the slot rule exists to prevent, about to be committed by the plan that ships the rule. Spec §8 already answers it, and I had read the section without applying it: "its slot discriminator is short and deterministic, so it is not a nonce". So the slots are gate-b--rle-pass-

.md - discriminated, not bare, and explicitly not a nonce. SPEC §8 ALSO CORRECTED THE INSTRUCTION I WAS GIVEN. The survive-list named four evidence items; §8 names seven, and the three omitted - the parse check, parity across every changed rule, and the fresh twelve-item pass - are explicit obligations. Dropping a spec obligation because my version of it was badly built would have been the wrong lesson. All seven are in Task 19. THE SUPERSEDED SINGLE-PLAN ARTIFACT is still in docs/superpowers/plans/ - 34 KB describing a topology, task numbering and commit protocol that all contradict the three plans replacing it. An executor finding it would follow it. Now marked SUPERSEDED with a do-not-execute line and pointers to A, B and C. Marked rather than deleted: the field record cites its curve, and deleting evidence to tidy a directory is how a record stops being able to explain a decision. Also fixed: the version-bump checker exits 2 with no argument, so the bare invocation was not a run - it now takes the recorded base; the risk-path verification is scheduled after the provenance lines it verifies exist; the parse check assigns features PER GRAMMAR rather than both; the knob rule takes one observation and states that a broken symlink is present-and-unusable, never absent; single-branch recovery captures the surviving branch's reviewed commit before the resume overwrites it; a pass is asserted clean about the commit it reviewed before it counts as final; the working tree is proved to hold only this change by reading the diff the reviewer will see; the reconstruction marker travels inside each record rather than in surrounding prose, because the squash carry copies records individually; and the closing-body validation is restored as an obligation with explicit cardinality and parse assertions. Seventeen edits, seventeen assert patterns, guard green. Gate B: N/A - docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-c-rollout.md | 264 ++++++++++++------ 1 file changed, 174 insertions(+), 90 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md index ea7c6e3..6adc32c 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -823,82 +823,157 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 17: Record the cycle base +## Task 17: Mark the superseded single-plan artifact + +**File:** `docs/superpowers/plans/2026-08-29-review-loop-economics.md` + +**Site** — pasted `grep -n`: + +``` +docs/superpowers/plans/2026-08-29-review-loop-economics.md:1:# Review-loop economics (pass floor + severity semantics) — Implementation Plan +``` + +> A 34 KB implementation plan for this same change is still in `docs/superpowers/plans/`, describing a topology, a task numbering and a commit protocol that all contradict the three plans that replaced it. **An executor finding it would follow it.** +> +> **Marked, not deleted.** The field record cites its curve — 31 Blocker/Major at pass 1 — and it is the clearest specimen this cycle produced of a plan describing itself into failure. Deleting evidence to tidy a directory is how a record stops being able to explain a decision. + +- [ ] **Replace.** OLD: + +``` +# Review-loop economics (pass floor + severity semantics) — Implementation Plan +``` + +NEW: + +``` +# Review-loop economics (pass floor + severity semantics) — Implementation Plan + +> **SUPERSEDED, and kept as evidence rather than as instruction.** This single-plan version opened +> **31 Blocker/Major at Gate-A pass 1** and was replaced by three plans: +> `2026-08-29-review-loop-economics-plan-a-rules.md`, +> `2026-08-30-review-loop-economics-plan-b-records.md` and +> `2026-08-30-review-loop-economics-plan-c-rollout.md`. +> **Do not execute anything below.** Its topology, its task numbering and its commit protocol all +> disagree with the plans that replaced it. It is retained because the field record cites its +> curve, and because the three findings that killed it are the clearest specimen this cycle +> produced of a plan describing itself into failure. +``` + +- [ ] **Assert the new text is present.** + +```bash +grep -cF -- "SUPERSEDED, and kept as evidence rather than as instruction" docs/superpowers/plans/2026-08-29-review-loop-economics.md +``` + +Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/superpowers/plans/2026-08-29-review-loop-economics.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 18: Record the cycle base **Plan A prints its base SHA and does not persist it**, and Plan A is closed and byte-frozen, so -Plan C establishes it. This is an action, not a proof: revision 2 wrapped it in a check that -defined the base as the tip's parent and then proved one commit sat above that parent, which is -true by construction and could not fail. +Plan C establishes it. -- [ ] **Record it** +- [ ] **Record it, and assert the one thing that is not circular** ```bash git rev-parse --verify HEAD^2 >/dev/null 2>&1 && { echo "WIP IS A MERGE — stop"; exit 1; } -mkdir -p .context +git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } +git log -1 --pretty=%s HEAD^ | grep -qi -- '-\?wip' && { echo "PARENT IS ALSO A WIP — stacked, collapse first"; exit 1; } git rev-parse HEAD^ > .context/plan-a-base-sha cat .context/plan-a-base-sha ``` -The merge test is the one condition worth asserting, because `HEAD^` is ambiguous only there. +> Revision 2 defined the base as `HEAD^` and then proved one commit sat above it — true by +> construction, a check that could not fail. Revision 3 deleted the proof **and the need with it**. +> The need is real: a **stacked** WIP would silently exclude the earlier snapshot from the review. +> **What is checkable without circularity is whether the parent is itself a WIP commit** — that +> asks something about a different commit, which is why it can fail. + `.context/` is gitignored, so this never enters the reviewed diff. --- -## Task 18: The evidence pack +## Task 19: The evidence pack Spec §8 names seven obligations. **All seven are here** — they are what the author owes, not -checks the plan invented about itself, and the mode is read from the story header at execution -time. +checks the plan invented about itself. The mode is read from the story header at execution time. - [ ] **1 — Battery.** The full `AGENTS.md` § Commands chain, green, **including - `sh scripts/check-version-bump.sh`**, which Plans A and B deferred for a stated reason and which - now runs because Task 11 landed the bump and the WIP exists. Pass it the recorded base rather - than a local `main` that may predate an earlier bump. + `sh scripts/check-version-bump.sh "$(cat .context/plan-a-base-sha)"`** — the checker takes a base + ref argument and **exits 2 with no argument**, so the bare invocation is not a run. Plans A and B + deferred this step for a stated reason; it runs now because Task 11 landed the bump and the WIP + exists. - [ ] **2 — A check that fails without the change**, read against **both** revisions: -> *At a derived floor of 1, does a Blocker/Major-free pass 1 carrying a Minor close, or keep -> looping?* - ```bash base=$(cat .context/plan-a-base-sha) git show "$base":CLAUDE.md | grep -cF 'Blocker/Major-free pass 1 carrying a Minor' grep -cF 'a Blocker/Major-free pass below the floor' CLAUDE.md ``` -**Grep the discriminating text, not the common tail** — `carrying a Minor keeps` appears in both -revisions and would return success either way. Pre-change says **pass 1** keeps looping, wrong at -floor 1 where pass 1 *is* the floor; post-change says **below the floor**. Record which revision -gave which answer. +**Grep the discriminating text, not the common tail.** Pre-change says **pass 1** keeps looping — +wrong at floor 1, where pass 1 *is* the floor; post-change says **below the floor**. Record which +revision gave which answer. + +- [ ] **3 — A named verification of the risk path.** **Runs after Task 21 Step 2 drafts the + provenance lines and before Step 3 closes** — it verifies those lines, so it cannot precede them. + Recompute each line's floor from the `Story:` header of the artifact that cycle reviewed. **The + observation that would exist if the claim were false is a line whose floor the cited profiles do + not license.** + +- [ ] **4 — The conditional knob verification.** Take **one** observation of the path and derive + everything from it, so bytes, digest and value class describe the same read: -- [ ] **3 — A named verification of the risk path.** Recompute each provenance line's floor from - the `Story:` header of the artifact that cycle reviewed. **The observation that would exist if - the claim were false is a line whose floor the cited profiles do not license.** + - **absent** — no path. Record not-applicable with that reason. **Do not create one**; a fixture + supplying its own input proves nothing. + - **present but not a readable regular file** — a directory, a device, an unreadable file, **or a + symlink whose target does not resolve**. Record `unusable(unreadable)`. A broken symlink is + *present and unusable*, never absent — the state the stripped machinery got wrong and the one + this rule exists for. + - **a readable regular file** — record bytes and digest, and classify the value as numeric or as + one of the pinned unusable causes. -- [ ] **4 — The conditional knob verification.** If `.context/codex-gate.floor` exists, record its - bytes and digest before the first Gate-B call and compare after. **If it does not exist, record - not-applicable with that reason and do not create one** — a fixture supplying its own input - proves nothing. Separately classify the value as numeric or as one of the pinned unusable - causes, which the provenance lines' knob clause needs and preservation does not supply. + Recorded before the first Gate-B call and compared after. **Existence is not the check.** -- [ ] **5 — A parse check over both pinned grammars.** Construct and parse, for each: a quoted - path; every `unusable()` value; a gapped `` such as `1,2,4`; a split-model pass; a - skipped cycle; a `?` count; and **at least one instance that must be rejected**. Record which - feature each exercises. §8 is explicit that the branch's own instances cannot exercise the - grammars, which is why this is constructed rather than observed. +- [ ] **5 — A parse check over both pinned grammars**, with the features **assigned per grammar** + rather than to both. **Provenance:** a quoted path; each `unusable()` value; a + cited-story-with-no-profile entry; `none` for no story cited. **Curve:** a gapped `` such + as `1,2,4`; a split-model pass; a `?` count; a skipped cycle's skip record. **Each grammar gets + at least one instance that must be rejected** — for the curve, `` shorter than `` + enumerates; for provenance, a repeated ``. Record which feature each exercises. - [ ] **6 — Parity across every changed rule**, in both copies. **One difference is expected and is the only one**: the successor-story pointer, which `CLAUDE.md` carries and the template must not. -- [ ] **7 — A fresh twelve-item `docs/prompt-standards.md` pass**, over each changed prompt +- [ ] **7 — A fresh twelve-item `docs/prompt-standards.md` pass** over each changed prompt artifact invariant 11 names — the **resulting scaffolded template**, **`workflow-init.md`**, and - **`process-pr-review.md`**, which Task 14 changes. One status row per item per artifact. - **Item 1 for the scaffolded template is `N/A`**, and Task 10 ships the note saying why. + **`process-pr-review.md`**. One status row per item per artifact. **Item 1 for the scaffolded + template is `N/A`**; Task 10 ships the note saying why. --- -## Task 19: The Gate-B cycle +## Task 20: The Gate-B cycle + +- [ ] **Before the first call: prove the working tree holds only this change** + +```bash +git status --porcelain # must be empty +base=$(cat .context/plan-a-base-sha) +git diff --name-only "$base"..HEAD # read it: every path must belong to Plans A, B or C +``` + +Explicit `git add` paths keep *new* strays out; they say nothing about content **already staged or +already inside the WIP** from an earlier step. This reads what the reviewer will actually see. - [ ] **Run the loop.** `mcp__codex__review` against the WIP commit, **`baseSha` = the recorded base**. **Floor 3** — the old rules govern this cycle. @@ -906,57 +981,70 @@ gave which answer. **Every call carries:** the old-rules sentence from Global Constraints; **the union of the `Story:` headers of Plans A, B and C**; and the current evidence entry quoted verbatim. -**Findings go to the bare slots — `gate-b--pass-

.md`.** This cycle is pre-rule, -has no nonce and cannot mint one; the nonce-infixed names Plan B introduces are reserved for -cycles starting after the rules ship. +**Findings go to `gate-b--rle-pass-

.md`.** Not the bare names: **this workspace +already contains 61 files in the bare `gate-b-*-pass-*` family from earlier cycles**, and the +delete-before-call step would destroy them — which is precisely the incident the slot rule exists +to prevent, committed by the plan that ships the rule. Not a nonce either: this cycle is pre-rule +and cannot mint one. **§8 names exactly this case** — *"its slot discriminator is short and +deterministic, so it is not a nonce"* — and `rle` is that discriminator. **Delete both branch targets before a full call. For a single-branch resume, delete only the -failed branch** — deleting both and recreating one makes the both-files check fail by -construction. A resume passes the `sessionId` back **and** its `reviewType`, since the tool -defaults to `full` and a resume omitting it can write the wrong slot. +failed branch**, and **capture the reviewed commit the surviving branch ran against** before +resuming — the two branches form one logical pass only if both reviewed the same commit, and the +surviving branch's value is unrecoverable once the resume overwrites its result. - [ ] **After every accepted fix:** stage the exact paths the fix touched — **never `-u`** — amend with `-m "WIP: review-loop economics"`, **revalidate the evidence entry**, then re-review that - new commit against the same base. A fix changes the diff, so a pass run before it does not cover - what is being committed. + new commit against the same base. + +- [ ] **Before accepting a pass as final:** assert `HEAD` is the commit that pass reviewed. A pass + is clean about a commit, not about a branch, and an amend between the pass and the close would + leave the clean pass describing something else. --- -## Task 20: Close the cycle +## Task 21: Close the cycle - [ ] **Step 1: Revalidate the evidence** against the content the close will carry. - [ ] **Step 2: Assemble the closing body** -It carries the evidence entry, and **one provenance line and one curve per cycle — five of each**, -this change having run one Gate-A spec cycle, three Gate-A plan cycles and one Gate-B cycle. - -**One is native and four are reconstructed, and the body says which.** The Gate-B cycle writes its -own records as it closes. The four Gate-A cycles closed before Plan B pinned the forms, so each of -theirs is marked **"reconstructed from validated pass files (pre-rule cycle)"** with the files -named. **Reconstruction from validated sources, marked as such, is not fabrication** — the marking -is what separates them, exactly as `cycle none (pre-rule)` already separates a cycle that cannot -have a nonce from one that simply lacks one. - -> **Why this does not contradict §8, and the reason is a failed premise rather than a scope -> limit.** §8 says *"No reconstruction is needed and none is claimed"* — and gives its grounds in -> the same breath: *"the Gate-A spec cycle has not closed … and the Gate-A plan cycle has not -> started, so both write their closing bodies natively."* **That premise no longer holds.** The -> spec cycle closed at pass 34, before Plan B existed to pin any form; and the single plan cycle -> §8 anticipated became three, all of which closed the same way. §8's sentence was a true -> prediction about a topology that changed. **What it was protecting — that no record silently -> claims to be contemporaneous — is preserved by the marking, which is the stronger reading.** -> -> **§8's "three closing bodies" is stale for the same reason**, and in the same way as the story -> criterion's "a run of all three holds three": both were written before this change was split -> into three plans. **The rule is one record per cycle**; the count follows from how many cycles -> ran, which is five. - -**Every cycle here is pre-rule**, so each carries `cycle none (pre-rule)`; minting a nonce for any -would be late-created provenance. **The branch demonstrates every field of both forms except two** -— the cycle identifier and the knob clause's non-absent form — **recorded as undemonstrable here -with their reasons**, not as gaps and not as satisfied. **Any cycle that both starts under these -rules and closes discharges the nonce demonstration**; duplicate discharge is harmless. +```bash +body=$(mktemp) || exit 1 +``` + +*(Revision 3 stripped the block that assigned `body` and left Step 3 expanding it — the strip +removed an **action**, not theater. The lesson is the one this pass was asked to check for: +deleting a check deletes whatever else was in the block.)* + +It carries the evidence entry, and **one provenance line and one curve per cycle — five of each**: +one Gate-A spec cycle, three Gate-A plan cycles, one Gate-B cycle. + +**One is native and four are reconstructed, and each record says which in its own text** — a +trailing ` — reconstructed from validated pass files (pre-rule cycle): ` on the four, and +nothing on the native one. **The marker travels with the record**, because the squash carry copies +records individually and a marker held in surrounding prose would not survive. + +**Validate the assembled body before amending** — this is an obligation, not a check the plan +invented: **exactly five provenance lines and five curves**; each parses against its pinned +grammar; each curve's `` has exactly as many entries as its `` enumerates; every +cycle field agrees between a cycle's line and its curve; four carry the reconstruction marker and +one does not; and no `<`-placeholder survives. + +> **Why reconstruction does not contradict §8.** §8 says *"No reconstruction is needed and none is +> claimed"* and gives its grounds in the same breath — *"the Gate-A spec cycle has not closed … +> and the Gate-A plan cycle has not started"*. **That premise failed**: the spec cycle closed at +> pass 34 before the forms existed, and the one plan cycle became three. §8's sentence was a true +> prediction about a topology that changed. What it protects — that no record silently claims to be +> contemporaneous — is preserved by the marker, which is the stronger reading. **§8's "three +> closing bodies" is stale for the same reason**, exactly as the story criterion's "a run of all +> three holds three" is: the rule is one record per cycle, and five cycles ran. + +**Every cycle here is pre-rule**, so each carries `cycle none (pre-rule)`. **The branch +demonstrates every field of both forms except two** — the cycle identifier and the knob clause's +non-absent form — **recorded as undemonstrable here with their reasons**, not as gaps and not as +satisfied. **Any cycle that both starts under these rules and closes discharges the nonce +demonstration.** - [ ] **Step 3: Close, and fail loudly if it does not** @@ -969,26 +1057,22 @@ else fi ``` -The body is deleted **only after a successful amend**, and the subject check **fails** rather than -printing. A transient failure that removed the body would destroy the only assembled copy of the -evidence pack while leaving the cycle open. +The body is deleted **only after a successful amend**. --- ## Self-Review -**Spec coverage.** §7 rollout — Tasks 1-16. §8 evidence — Task 18, **all seven obligations**. The -cycle and the close — Tasks 17, 19, 20. +**Spec coverage.** §7 rollout — Tasks 1-17. §8 evidence — Task 19, all seven obligations. The +cycle and the close — Tasks 18, 20, 21. -**What was stripped, and what was not.** Revision 2 carried six checks that could not fail — a -circular base proof, greps that printed counts without comparing them, a parse check with no -parser, a knob state machine that classified a broken symlink as absent, and validation that -printed rather than asserted. **Those are gone.** §8's seven obligations are not checks the plan -invented about its own execution; they are what the author owes, and all seven stayed. +**What was stripped, and what came back.** Revision 3 removed six checks that could not fail. Two +of those removals took something real with them, and pass 3 found both: the base check had a +circular *proof* but a genuine *need* (a stacked WIP), now met by asking about a different commit; +and the closing-body block held `body=$(mktemp)` as well as its validation. **Deleting a check +deletes whatever else was in the block** — recorded, because it is the cost of the cure. -**Inherited obligations.** M8 and M10 at Task 19; M9 at Task 18 item 4; MINOR 12 and B6 at Task -20. +**Inherited obligations.** M8 and M10 at Task 20; M9 at Task 19 item 4; MINOR 12 and B6 at Task 21. -**Known limit.** Gate A reviews this plan, not the edits. Each task's single check establishes -that its edit landed at its site. What the edits do is Gate B's — here, Task 19, reading the real -combined diff of all three plans. +**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's — here, +Task 20, reading the real combined diff of all three plans. From bc40a11d51021b391c8bd4313bcaf0e1bd32428b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 17:20:28 +0200 Subject: [PATCH 082/117] =?UTF-8?q?docs(plan):=20Plan=20C=20revision=205?= =?UTF-8?q?=20=E2=80=94=20the=20plan=20stops=20restating=20=C2=A75?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 4: 23 findings, 4 BLOCKER, 16 MAJOR = 20 B+M. Curve 16, 17, 15, 20; Blockers 5, 4, 2, 4. Four of the five tells present, so the mandatory stop-and-surface fired and Daniel ruled. SEVENTEEN OF TWENTY-THREE FINDINGS LANDED ON THE INSTRUMENT — Tasks 18-21, the plan's own restatement of the Gate-B protocol. Six touched the rollout. The plan had grown a second, drifting copy of CLAUDE.md §5, and Gate A was reviewing the copy against the original. Tasks 18, 20 and 21 collapse into one Task 20 that says: run the cycle per §5. Three facts are this cycle's own and are not in §5 — record the WIP parent as baseSha at cycle open; the slots are gate-b--rle-pass-

.md; the cycle runs under the OLD rules at floor 3. Everything else is deleted, not repaired. THAT INCLUDES FOUR DEFECTS PASS 4 FOUND AND REVISION 4 HAD JUST RESTORED: the unassigned `body`, the symlink-following redirect into .context, the loose `-\?wip` match, the missing .context creation. They were parts of the copy. Repairing them would have kept it. This is not revision 3's over-strip in a new coat — that stripped checks and took actions with them; this removes a restatement whose original an executor follows either way. THE PINNED-GRAMMAR CONFLICT DISSOLVES. The reconstruction marker moves BESIDE the records — one adjacent line in the closing body — instead of inside the grammar-parsed strings, where revision 4 had put it to survive the squash carry. The carry copies the body's records; an adjacent line in the same block travels with them. Records stay byte-conformant. THE PARSE OBLIGATION REDUCES TO WHAT CAN EXECUTE. Spec §8's item 5 asked for a parse check over both grammars; no parser exists in this repo and none is invented. The records are written FROM the grammars and checked by reading, and the obligation now says which productions this branch cannot demonstrate and why, rather than implying an enforcement that does not exist. Same standard revision 3 applied to the six checks it stripped. TASK 17 IS REMOVED. The spec places supersession remedies out of scope, and pass 3 had asked for the marker that pass 4 asked to withdraw — the require/withdraw pair resolves in the spec's favour. The superseded single-plan artifact gets one line in the closure record instead of a shipped edit. THE SWEEP WAS TWO FILES SHORT. The skipped-cycle claim lives in five files, not three: docs/getting-started.md:105 and docs/coding-workflow.md:129 enumerate the same duties and omit the same provenance line and skip record. New Tasks 17 and 18. Tasks 14-16 renumber to "of 5". Its own lesson, applied a third time: grep for the claim, not the phrasing. THE ASSERTS NOW ASSERT. Every one was a bare `grep -cF` that succeeded at any positive count with the expected value stated only in prose — pass 4 called that what it was. All 18 are equality tests that exit nonzero on anything else. The two bare counts left are deliberate: Task 19 item 2 is a counterfactual across two revisions and must print. AND EVERY TASK IS SKIP-IF-APPLIED. One Global Constraint replaces twenty state tables: run the assert first, 1 means skip, 0 means apply, anything else is a stop rather than a retry. A second names Tasks 10 and 12 as the two that re-emit their anchor by design, so only their NEW count discriminates. Also: the old-conditions accounting extends through Task 18 and stops claiming none of these passages are §5 rules — rows 15 and 16 are; the header pointed at Task 13 for the mode and now points at Task 19; MINOR 12 is recorded moot rather than dropped, since the plan no longer scripts the close; M8 and B6 are named as §5's own rules rather than restated. Twenty tasks, every assert string verified absent from its target before the change, every bash fence checked with `sh -n` (37 blocks, 0 failures). Fences balanced, every cited path resolves, both new OLD blocks match once. Gate B: N/A — docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-c-rollout.md | 412 +++++++++--------- 1 file changed, 215 insertions(+), 197 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md index 6adc32c..4731126 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -14,7 +14,7 @@ Plan C implements §7 and §8. **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` > **Read the profile from that header at execution time.** This plan states no risk value, no -> security value, no validation mode and no pass count derived from any of them. Task 13 reads the +> security value, no validation mode and no pass count derived from any of them. Task 19 reads the > mode from the header and produces what it names. --- @@ -67,13 +67,24 @@ whose section moved is relocated, not repaired**, so they are discharged here or - **`git add` names paths explicitly, never `-u`.** Staging whole-tree changes would fold an unrelated edit into the reviewed diff and close it under this story. - **Line numbers are provenance, never instructions.** +- **Every assert is an equality test, never a count print**, and every task is + **skip-if-applied**: run its assert first. `1` means the task already ran — skip it, do not + re-apply. `0` means apply. **Any other count is a stop**, not a retry: a duplicate insertion or + a half-applied edit, which re-running only compounds. This is what makes the plan safe to + resume, and it is why the asserts test equality — a bare `grep -c` succeeds at any positive + count, so it cannot tell one insertion from two. +- **Tasks 10 and 12 re-emit their anchor inside their replacement**, so their OLD text survives + by design and only the NEW count discriminates. Every other task's OLD text is gone after it + runs. --- ## Old-conditions accounting -§6's method, applied to a different class: these passages are not §5 rules but **user-facing -statements that Plans A and B make false**. For each, what it asserted and what replaces it. +§6's method, applied to a different class: these are **user-facing statements that Plans A and B +make false**. Most are not §5 rules; **rows 15 and 16 are** — the skipped-cycle duty sentence in §5 +itself and in the scaffolded mirror — and they get the same accounting. For each, what it asserted +and what replaces it. | # | Statement | What it asserted | Disposition | |---|---|---|---| @@ -89,10 +100,20 @@ statements that Plans A and B make false**. For each, what it asserted and what | 10 | `workflow-init.md` before the template fence | *(nothing — this is an insertion)* | **rewrites nothing**; the note is added outside the fence so it never scaffolds (Task 10) | | 11 | `plugin.json` version | `0.10.0` | **replaced** by `0.11.0` (Task 11) | | 12 | `CHANGELOG.md` | *(nothing — an append)* | **rewrites nothing**; a new newest-first entry (Task 12) | +| 13 | `coding-workflow.md` model destination | the model goes in the evidence entry or the dispositions file | **replaced** by the per-pass curve's model field — neither old destination is keyed to a pass. The health-probe procedure above it is **kept** untouched (Task 13) | +| 14 | `process-pr-review.md` skip duties | a skipped cycle owes the skip reason, the battery result, and one evidence entry per cited profiled story | **all three kept and extended**: the provenance line and, in place of a curve, the skip record are **added**, because Plan B makes them owed by every cycle, skipped or not (Task 14) | +| 15 | `CLAUDE.md` §5 skip duties | as row 14, in the governing copy | same disposition (Task 15) | +| 16 | scaffolded template skip duties | as row 14, in the mirror | same disposition (Task 16) | +| 17 | `getting-started.md` skip duties | as row 14, in an explanatory duty summary | same disposition (Task 17) | +| 18 | `coding-workflow.md` skip duties | as row 14, in the other explanatory duty summary | same disposition (Task 18) | **Rows 10 and 12 are insertions and are listed so a reader can confirm that rather than assume it.** Each re-emits its anchor verbatim inside its replacement. +**Rows 14-18 are one claim in five files.** The sweep that found three stopped at the prompt +copies; Gate-A pass 4 found the two documentation summaries, which enumerate the same duties and +omit the same two records. + --- ## Task 1: The `codex-gate.floor` knob description @@ -121,10 +142,10 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- "moves the hook's reminder threshold. It does not change the floor" README.md +test "$(grep -cF -- "moves the hook's reminder threshold. It does not change the floor" README.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -162,10 +183,10 @@ comes back with zero findings. - [ ] **Assert the new text is present.** ```bash -grep -cF -- "the floor its profile derives, final pass clean" docs/getting-started.md +test "$(grep -cF -- "the floor its profile derives, final pass clean" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -201,10 +222,10 @@ task-by-task plan (each task starts with a failing test); the same loop runs at - [ ] **Assert the new text is present.** ```bash -grep -cF -- "the same loop runs at the derived floor" docs/getting-started.md +test "$(grep -cF -- "the same loop runs at the derived floor" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -242,10 +263,10 @@ progress claims backed by test runs. If the hook's own threshold wasn't met, it - [ ] **Assert the new text is present.** ```bash -grep -cF -- "If the hook's own threshold wasn't met, it says" docs/getting-started.md +test "$(grep -cF -- "If the hook's own threshold wasn't met, it says" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -281,10 +302,10 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- "the derived floor, final clean. Verification is by" docs/getting-started.md +test "$(grep -cF -- "the derived floor, final clean. Verification is by" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -322,10 +343,10 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- "three different numbers: the calls this cycle made" docs/getting-started.md +test "$(grep -cF -- "three different numbers: the calls this cycle made" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -363,10 +384,10 @@ is still owed; Gate A's floor derives from the profile exactly as Gate B's does, - [ ] **Assert the new text is present.** ```bash -grep -cF -- "Gate A's floor derives from the profile exactly as Gate B's does" docs/getting-started.md +test "$(grep -cF -- "Gate A's floor derives from the profile exactly as Gate B's does" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -404,10 +425,10 @@ positive integer) moves the hook's reminder threshold, and `touch .context/codex - [ ] **Assert the new text is present.** ```bash -grep -cF -- "moves the hook's reminder threshold, and" docs/getting-started.md +test "$(grep -cF -- "moves the hook's reminder threshold, and" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -446,10 +467,10 @@ mode calibrates - [ ] **Assert the new text is present.** ```bash -grep -cF -- "floor itself derives from the profile, so it is not the same at every level" docs/coding-workflow.md +test "$(grep -cF -- "floor itself derives from the profile, so it is not the same at every level" docs/coding-workflow.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -500,10 +521,10 @@ numbers) and say so in the report. - [ ] **Assert the new text is present.** ```bash -grep -cF -- "Prompt-standards item 1 for the scaffolded" plugins/dev-workflow/commands/workflow-init.md +test "$(grep -cF -- "Prompt-standards item 1 for the scaffolded" plugins/dev-workflow/commands/workflow-init.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -541,10 +562,12 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- '"version": "0.11.0"' plugins/dev-workflow/.claude-plugin/plugin.json +test "$(grep -cF -- '"version": "0.11.0"' plugins/dev-workflow/.claude-plugin/plugin.json)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +The outer single quotes are what make the inner JSON quotes survive the shell — the same fix +pass 1 required. Before this task the count is `0`, so this exits nonzero if it is run early; +after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -614,10 +637,10 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- "The mandatory pass floor is now a function of the cited story's profile" plugins/dev-workflow/CHANGELOG.md +test "$(grep -cF -- "The mandatory pass floor is now a function of the cited story's profile" plugins/dev-workflow/CHANGELOG.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -660,10 +683,10 @@ bookkeeping, not enforcement: nothing checks it, and a wrong entry looks exactly - [ ] **Assert the new text is present.** ```bash -grep -cF -- "the finding count in **the cycle's per-pass curve**" docs/coding-workflow.md +test "$(grep -cF -- "the finding count in **the cycle's per-pass curve**" docs/coding-workflow.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -674,7 +697,7 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 14: The skipped-cycle duties — 1 of 3, `process-pr-review` +## Task 14: The skipped-cycle duties — 1 of 5, `process-pr-review` **File:** `plugins/dev-workflow/commands/process-pr-review.md` @@ -686,7 +709,7 @@ plugins/dev-workflow/commands/process-pr-review.md:159: A skip removes the rev > **A contradiction between two shipped prompts, which invariant 11 forbids.** This command told a skipped cycle it owed the skip reason, the battery result and its evidence entries. Plan B makes the provenance line owed by **every** cycle, skipped or not, and requires a skip record in place of the curve. Left alone, an ordinary trivial-fix skip would close without records the other prompt says are mandatory. > -> **This claim lives in three files, and Tasks 14, 15 and 16 are one repair.** I fixed this one and reported it fixed; the sweep found the second and third a pass later. +> **This claim lives in five files, and Tasks 14 through 18 are one repair.** I fixed this one and reported it fixed; the sweep found the second and third a pass later, and the fourth and fifth a Gate-A pass after that — each time by grepping for the claim rather than for the phrasing. > > **The rule taken from it, and it is a rule about when rather than what: a fixed statement's other homes are found by grepping for the CLAIM at fix time, not by the next review pass.** Grepping for the phrasing finds the copy you already have; grepping for what the sentence asserts finds the others. `AGENTS.md` records the same failure in a different form, where four rounds each searched for the previous phrase and a synonym survived every time. @@ -713,10 +736,10 @@ NEW: - [ ] **Assert the new text is present.** ```bash -grep -cF -- "its provenance line and, in place of a curve, its skip record" plugins/dev-workflow/commands/process-pr-review.md +test "$(grep -cF -- "its provenance line and, in place of a curve, its skip record" plugins/dev-workflow/commands/process-pr-review.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -727,7 +750,7 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 15: The skipped-cycle duties — 2 of 3, `CLAUDE.md` §5 +## Task 15: The skipped-cycle duties — 2 of 5, `CLAUDE.md` §5 **File:** `CLAUDE.md` @@ -761,10 +784,10 @@ the provenance line, and a skip record in place of the curve. - [ ] **Assert the new text is present.** ```bash -grep -cF -- "Neither is excused the records every cycle owes" CLAUDE.md +test "$(grep -cF -- "Neither is excused the records every cycle owes" CLAUDE.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -775,7 +798,7 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 16: The skipped-cycle duties — 3 of 3, the scaffolded template +## Task 16: The skipped-cycle duties — 3 of 5, the scaffolded template **File:** `plugins/dev-workflow/commands/workflow-init.md` @@ -785,7 +808,7 @@ git commit --amend -m "WIP: review-loop economics" plugins/dev-workflow/commands/workflow-init.md:856:the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the ``` -> The same claim as the previous two, in the mirror. **The assert here counts 1 in this file and the previous task's counts 1 in `CLAUDE.md`** — the same pattern in two files, because the two copies carry the sentence independently and a fix to one has never implied a fix to the other. +> The same claim as Tasks 14 and 15, in the mirror. **The assert here counts 1 in this file and the previous task's counts 1 in `CLAUDE.md`** — the same pattern in two files, because the two copies carry the sentence independently and a fix to one has never implied a fix to the other. - [ ] **Replace.** OLD: @@ -809,10 +832,10 @@ the provenance line, and a skip record in place of the curve. - [ ] **Assert the new text is present.** ```bash -grep -cF -- "Neither is excused the records every cycle owes" plugins/dev-workflow/commands/workflow-init.md +test "$(grep -cF -- "Neither is excused the records every cycle owes" plugins/dev-workflow/commands/workflow-init.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. - [ ] **Amend the WIP commit.** @@ -823,81 +846,96 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 17: Mark the superseded single-plan artifact +## Task 17: The skipped-cycle duties — 4 of 5, `getting-started.md` -**File:** `docs/superpowers/plans/2026-08-29-review-loop-economics.md` +**File:** `docs/getting-started.md` **Site** — pasted `grep -n`: ``` -docs/superpowers/plans/2026-08-29-review-loop-economics.md:1:# Review-loop economics (pass floor + severity semantics) — Implementation Plan +docs/getting-started.md:105: still owes the battery, and records both the skip reason and its evidence entry in the ``` -> A 34 KB implementation plan for this same change is still in `docs/superpowers/plans/`, describing a topology, a task numbering and a commit protocol that all contradict the three plans that replaced it. **An executor finding it would follow it.** -> -> **Marked, not deleted.** The field record cites its curve — 31 Blocker/Major at pass 1 — and it is the clearest specimen this cycle produced of a plan describing itself into failure. Deleting evidence to tidy a directory is how a record stops being able to explain a decision. +> **The same claim as Tasks 14-16, in an explanatory duty summary.** The three-file sweep stopped +> at the prompt copies. This page and the next task's enumerate the same duties and omit the same +> two records, and they are where a user actually reads what a skip owes — so the failure the +> three-file repair was for survives in the more likely place. - [ ] **Replace.** OLD: ``` -# Review-loop economics (pass floor + severity semantics) — Implementation Plan + still owes the battery, and records both the skip reason and its evidence entry in the + commit body. Gate A is not ``` NEW: ``` -# Review-loop economics (pass floor + severity semantics) — Implementation Plan - -> **SUPERSEDED, and kept as evidence rather than as instruction.** This single-plan version opened -> **31 Blocker/Major at Gate-A pass 1** and was replaced by three plans: -> `2026-08-29-review-loop-economics-plan-a-rules.md`, -> `2026-08-30-review-loop-economics-plan-b-records.md` and -> `2026-08-30-review-loop-economics-plan-c-rollout.md`. -> **Do not execute anything below.** Its topology, its task numbering and its commit protocol all -> disagree with the plans that replaced it. It is retained because the field record cites its -> curve, and because the three findings that killed it are the clearest specimen this cycle -> produced of a plan describing itself into failure. + still owes the battery, and records the skip reason, the cycle's provenance line, a skip + record in place of the curve, and one evidence entry per cited profiled story, in the + commit body. Gate A is not ``` - [ ] **Assert the new text is present.** ```bash -grep -cF -- "SUPERSEDED, and kept as evidence rather than as instruction" docs/superpowers/plans/2026-08-29-review-loop-economics.md +test "$(grep -cF -- "records the skip reason, the cycle's provenance line, a skip" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -Before this task: `0`. After: `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. - [ ] **Amend the WIP commit.** ```bash -git add docs/superpowers/plans/2026-08-29-review-loop-economics.md +git add docs/getting-started.md git commit --amend -m "WIP: review-loop economics" ``` --- -## Task 18: Record the cycle base +## Task 18: The skipped-cycle duties — 5 of 5, `coding-workflow.md` + +**File:** `docs/coding-workflow.md` + +**Site** — pasted `grep -n`: + +``` +docs/coding-workflow.md:129:change on security-relevant surface is not eligible. A skip removes the review, never +``` + +> The same claim as Task 17, in the other explanatory summary. **Both are corrected in the same +> revision**, because fixing one would leave the other teaching it — the rule row 8 of the +> accounting states, applied a second time. + +- [ ] **Replace.** OLD: + +``` +the evidence: the battery still runs, the reason is recorded in the commit body, and +so is one evidence entry per cited profiled story. **Explanatory** +``` + +NEW: -**Plan A prints its base SHA and does not persist it**, and Plan A is closed and byte-frozen, so -Plan C establishes it. +``` +the evidence: the battery still runs, and the commit body carries the reason, the cycle's +provenance line, a skip record in place of the curve, and one evidence entry per cited +profiled story. **Explanatory** +``` -- [ ] **Record it, and assert the one thing that is not circular** +- [ ] **Assert the new text is present.** ```bash -git rev-parse --verify HEAD^2 >/dev/null 2>&1 && { echo "WIP IS A MERGE — stop"; exit 1; } -git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } -git log -1 --pretty=%s HEAD^ | grep -qi -- '-\?wip' && { echo "PARENT IS ALSO A WIP — stacked, collapse first"; exit 1; } -git rev-parse HEAD^ > .context/plan-a-base-sha -cat .context/plan-a-base-sha +test "$(grep -cF -- "the commit body carries the reason, the cycle's" docs/coding-workflow.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -> Revision 2 defined the base as `HEAD^` and then proved one commit sat above it — true by -> construction, a check that could not fail. Revision 3 deleted the proof **and the need with it**. -> The need is real: a **stacked** WIP would silently exclude the earlier snapshot from the review. -> **What is checkable without circularity is whether the parent is itself a WIP commit** — that -> asks something about a different commit, which is why it can fail. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. + +- [ ] **Amend the WIP commit.** -`.context/` is gitignored, so this never enters the reviewed diff. +```bash +git add docs/coding-workflow.md +git commit --amend -m "WIP: review-loop economics" +``` --- @@ -906,16 +944,17 @@ cat .context/plan-a-base-sha Spec §8 names seven obligations. **All seven are here** — they are what the author owes, not checks the plan invented about itself. The mode is read from the story header at execution time. -- [ ] **1 — Battery.** The full `AGENTS.md` § Commands chain, green, **including - `sh scripts/check-version-bump.sh "$(cat .context/plan-a-base-sha)"`** — the checker takes a base - ref argument and **exits 2 with no argument**, so the bare invocation is not a run. Plans A and B - deferred this step for a stated reason; it runs now because Task 11 landed the bump and the WIP - exists. +`$base` below is the cycle base — the WIP commit's parent, recorded at cycle open per Task 20's +first fact. It exists before this task runs, because Plan A opened the WIP. + +- [ ] **1 — Battery.** The full `AGENTS.md` § Commands chain, green, **including the version-bump + checker with the recorded base as its argument** — it takes a base ref and **exits 2 with no + argument**, so the bare invocation is not a run. Plans A and B deferred this step for a stated + reason; it runs now because Task 11 landed the bump and the WIP exists. - [ ] **2 — A check that fails without the change**, read against **both** revisions: ```bash -base=$(cat .context/plan-a-base-sha) git show "$base":CLAUDE.md | grep -cF 'Blocker/Major-free pass 1 carrying a Minor' grep -cF 'a Blocker/Major-free pass below the floor' CLAUDE.md ``` @@ -924,11 +963,10 @@ grep -cF 'a Blocker/Major-free pass below the floor' CLAUDE.md wrong at floor 1, where pass 1 *is* the floor; post-change says **below the floor**. Record which revision gave which answer. -- [ ] **3 — A named verification of the risk path.** **Runs after Task 21 Step 2 drafts the - provenance lines and before Step 3 closes** — it verifies those lines, so it cannot precede them. - Recompute each line's floor from the `Story:` header of the artifact that cycle reviewed. **The - observation that would exist if the claim were false is a line whose floor the cited profiles do - not license.** +- [ ] **3 — A named verification of the risk path.** **Runs after the provenance lines are drafted + and before the close** — it verifies those lines, so it cannot precede them. Recompute each + line's floor from the `Story:` header of the artifact that cycle reviewed. **The observation that + would exist if the claim were false is a line whose floor the cited profiles do not license.** - [ ] **4 — The conditional knob verification.** Take **one** observation of the path and derive everything from it, so bytes, digest and value class describe the same read: @@ -944,12 +982,17 @@ revision gave which answer. Recorded before the first Gate-B call and compared after. **Existence is not the check.** -- [ ] **5 — A parse check over both pinned grammars**, with the features **assigned per grammar** - rather than to both. **Provenance:** a quoted path; each `unusable()` value; a - cited-story-with-no-profile entry; `none` for no story cited. **Curve:** a gapped `` such - as `1,2,4`; a split-model pass; a `?` count; a skipped cycle's skip record. **Each grammar gets - at least one instance that must be rejected** — for the curve, `` shorter than `` - enumerates; for provenance, a repeated ``. Record which feature each exercises. +- [ ] **5 — Both pinned grammars exercised by the records this cycle actually writes.** The five + provenance lines and five curves are **written from the grammars** — each field produced by + reading its production, not by copying an example — and **checked by reading**: they go into the + closing body, and the Gate-B reviewer receives it. Record which feature each of the ten records + exercises, and **which productions this branch cannot demonstrate, with the reason** (the cycle + identifier and the knob clause's non-absent form; see Task 20). + + **No parser is invented here.** A parse check needs a parser, this repo has none for either + grammar, and *a check that cannot execute is not a check* — the standard revision 3 applied to + the six it stripped, applied to this one. The programmatic consumer is P8's; until it ships, the + grammars are enforced by a reader, and this obligation says so rather than implying otherwise. - [ ] **6 — Parity across every changed rule**, in both copies. **One difference is expected and is the only one**: the successor-story pointer, which `CLAUDE.md` carries and the template must @@ -962,117 +1005,92 @@ revision gave which answer. --- -## Task 20: The Gate-B cycle - -- [ ] **Before the first call: prove the working tree holds only this change** - -```bash -git status --porcelain # must be empty -base=$(cat .context/plan-a-base-sha) -git diff --name-only "$base"..HEAD # read it: every path must belong to Plans A, B or C -``` - -Explicit `git add` paths keep *new* strays out; they say nothing about content **already staged or -already inside the WIP** from an earlier step. This reads what the reviewer will actually see. - -- [ ] **Run the loop.** `mcp__codex__review` against the WIP commit, **`baseSha` = the recorded - base**. **Floor 3** — the old rules govern this cycle. - -**Every call carries:** the old-rules sentence from Global Constraints; **the union of the -`Story:` headers of Plans A, B and C**; and the current evidence entry quoted verbatim. - -**Findings go to `gate-b--rle-pass-

.md`.** Not the bare names: **this workspace -already contains 61 files in the bare `gate-b-*-pass-*` family from earlier cycles**, and the -delete-before-call step would destroy them — which is precisely the incident the slot rule exists -to prevent, committed by the plan that ships the rule. Not a nonce either: this cycle is pre-rule -and cannot mint one. **§8 names exactly this case** — *"its slot discriminator is short and -deterministic, so it is not a nonce"* — and `rle` is that discriminator. - -**Delete both branch targets before a full call. For a single-branch resume, delete only the -failed branch**, and **capture the reviewed commit the surviving branch ran against** before -resuming — the two branches form one logical pass only if both reviewed the same commit, and the -surviving branch's value is unrecoverable once the resume overwrites its result. - -- [ ] **After every accepted fix:** stage the exact paths the fix touched — **never `-u`** — amend - with `-m "WIP: review-loop economics"`, **revalidate the evidence entry**, then re-review that - new commit against the same base. - -- [ ] **Before accepting a pass as final:** assert `HEAD` is the commit that pass reviewed. A pass - is clean about a commit, not about a branch, and an amend between the pass and the close would - leave the clean pass describing something else. - ---- - -## Task 21: Close the cycle - -- [ ] **Step 1: Revalidate the evidence** against the content the close will carry. - -- [ ] **Step 2: Assemble the closing body** - -```bash -body=$(mktemp) || exit 1 -``` - -*(Revision 3 stripped the block that assigned `body` and left Step 3 expanding it — the strip -removed an **action**, not theater. The lesson is the one this pass was asked to check for: -deleting a check deletes whatever else was in the block.)* - -It carries the evidence entry, and **one provenance line and one curve per cycle — five of each**: -one Gate-A spec cycle, three Gate-A plan cycles, one Gate-B cycle. - -**One is native and four are reconstructed, and each record says which in its own text** — a -trailing ` — reconstructed from validated pass files (pre-rule cycle): ` on the four, and -nothing on the native one. **The marker travels with the record**, because the squash carry copies -records individually and a marker held in surrounding prose would not survive. - -**Validate the assembled body before amending** — this is an obligation, not a check the plan -invented: **exactly five provenance lines and five curves**; each parses against its pinned -grammar; each curve's `` has exactly as many entries as its `` enumerates; every -cycle field agrees between a cycle's line and its curve; four carry the reconstruction marker and -one does not; and no `<`-placeholder survives. +## Task 20: The Gate-B cycle and the close + +**Run the cycle per `CLAUDE.md` §5.** The floor, the file-first findings protocol, +delete-before-call, what makes a pass valid, single-branch recovery, re-review after every fix, the +clean-final-pass rule and the closing amend are **§5's, and this plan does not restate them**. +Revision 4 did, and Gate-A pass 4 spent seventeen of its twenty-three findings reviewing that +restatement against the rules it was restating. **Three facts belong to this cycle and are not in +§5:** + +1. **The base.** Record the WIP commit's parent at cycle open and pass it as `baseSha`, per §5's + own `baseSha` rule. Plan A prints its base and does not persist it, and Plan A is closed and + byte-frozen, so this cycle establishes it. + +2. **The slots are `gate-b--rle-pass-

.md`**, never the bare names. This workspace + already holds **61 files in the bare `gate-b-*-pass-*` family** from earlier cycles, and §5's + delete-before-call step would destroy them — precisely the incident the slot rule exists to + prevent, committed by the plan that ships it. Not a nonce either: this cycle is pre-rule and + cannot mint one. **§8 names exactly this case** — *"its slot discriminator is short and + deterministic, so it is not a nonce"* — and `rle` is that discriminator. + +3. **The cycle runs under the OLD rules**, per the activation constraint: **floor 3**, the + constant, not the derived value this diff introduces. Carry the old-rules sentence from Global + Constraints in every call's `additionalContext`. + +**Every call also carries** the union of the `Story:` headers of Plans A, B and C, and the current +evidence entry quoted verbatim. + +**Before the first call**, confirm `git status --porcelain` is empty and read +`git diff --name-only "$base"..HEAD` against the three plans' declared surface. **This reads path +names, not content**: it catches a stray *file*, not a stray hunk inside a file the plans +legitimately touch. + +### What the closing body carries + +The evidence entry, and **one provenance line and one curve per cycle — five of each**: one Gate-A +spec cycle, three Gate-A plan cycles, one Gate-B cycle. + +**The reconstruction marker sits beside the records, never inside them** — one adjacent line, of +the form *"Records for the Gate-A spec cycle and the three Gate-A plan cycles are reconstructed +from validated pass files; those cycles closed before the forms were active."* — leaving every +record string byte-conformant to its pinned grammar. **A marker inside a record would have to be a +grammar production, and neither grammar has one.** Revision 4 put it inside to survive the squash +carry; the carry copies the body's records, and an adjacent line in the same block travels with +them. + +**One line records the superseded artifact:** `2026-08-29-review-loop-economics.md` is superseded +by Plans A, B and C and is not executable. **It is not edited.** Revision 4 shipped a task to mark +it; the spec places supersession remedies out of scope, and Gate-A pass 4 was right that the task +expanded the settled change without an approved decision. A line in the record is what a +closure record is for. + +**Every cycle here is pre-rule**, so each carries `cycle none (pre-rule)`. **The branch demonstrates +every field of both forms except two** — the cycle identifier and the knob clause's non-absent form +— **recorded as undemonstrable here with their reasons**, not as gaps and not as satisfied. **Any +cycle that both starts under these rules and closes discharges the nonce demonstration.** > **Why reconstruction does not contradict §8.** §8 says *"No reconstruction is needed and none is > claimed"* and gives its grounds in the same breath — *"the Gate-A spec cycle has not closed … > and the Gate-A plan cycle has not started"*. **That premise failed**: the spec cycle closed at > pass 34 before the forms existed, and the one plan cycle became three. §8's sentence was a true -> prediction about a topology that changed. What it protects — that no record silently claims to be -> contemporaneous — is preserved by the marker, which is the stronger reading. **§8's "three -> closing bodies" is stale for the same reason**, exactly as the story criterion's "a run of all -> three holds three" is: the rule is one record per cycle, and five cycles ran. - -**Every cycle here is pre-rule**, so each carries `cycle none (pre-rule)`. **The branch -demonstrates every field of both forms except two** — the cycle identifier and the knob clause's -non-absent form — **recorded as undemonstrable here with their reasons**, not as gaps and not as -satisfied. **Any cycle that both starts under these rules and closes discharges the nonce -demonstration.** - -- [ ] **Step 3: Close, and fail loudly if it does not** - -```bash -if git commit --amend -F "$body"; then - git log -1 --pretty=%s | grep -q '^WIP:' && { echo "STILL WIP — not closed"; exit 1; } - echo "CLOSED: $(git log -1 --pretty=%s)"; rm -f "$body" -else - echo "AMEND FAILED — body preserved at $body"; exit 1 -fi -``` - -The body is deleted **only after a successful amend**. +> prediction about a topology that changed, and what it protects — that no record silently claims +> to be contemporaneous — is preserved by the adjacent marker. **§8's "three closing bodies" is +> stale for the same reason**, exactly as the story criterion's "a run of all three holds three" +> is: the rule is one record per cycle, and five cycles ran. --- ## Self-Review -**Spec coverage.** §7 rollout — Tasks 1-17. §8 evidence — Task 19, all seven obligations. The -cycle and the close — Tasks 18, 20, 21. - -**What was stripped, and what came back.** Revision 3 removed six checks that could not fail. Two -of those removals took something real with them, and pass 3 found both: the base check had a -circular *proof* but a genuine *need* (a stacked WIP), now met by asking about a different commit; -and the closing-body block held `body=$(mktemp)` as well as its validation. **Deleting a check -deletes whatever else was in the block** — recorded, because it is the cost of the cure. - -**Inherited obligations.** M8 and M10 at Task 20; M9 at Task 19 item 4; MINOR 12 and B6 at Task 21. - -**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's — here, -Task 20, reading the real combined diff of all three plans. +**Spec coverage.** §7 rollout — Tasks 1-18. §8 evidence — Task 19, all seven obligations. The +cycle and the close — Task 20. + +**What revision 5 removed, and why it is not another over-strip.** Revision 3 stripped six checks +that could not fail and took two real actions with them; revision 4 restored those. **Revision 5 +removes something different in kind: the plan's own restatement of a protocol `CLAUDE.md` §5 +already owns.** Nothing is lost, because §5 is what an executor follows either way — the +restatement was a second, drifting copy of it, and Gate-A pass 4 reviewed the copy instead of the +rollout. The three facts §5 does *not* carry are stated in Task 20. The mechanics pass 4 found +broken inside the restatement — an unassigned `body`, a symlink-following redirect, a loose +`-\?wip` match, a missing `.context` — **go with it rather than being repaired**, because +repairing them would have kept the copy. + +**Inherited obligations.** M9 at Task 19 item 4. M10 in Global Constraints. **M8 and B6 are §5's +own rules** — single-branch recovery, and evidence revalidated after every fix and before the +close — so Task 20 defers to §5 rather than restating them. **MINOR 12 is moot**: it asked for +`mktemp` over a fixed `/tmp` path, and the plan no longer scripts the close at all. + +**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's — Task 20, +reading the real combined diff of all three plans. From 5f1eddd898962fca5d719c07ad583db03adcc2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:22:16 +0200 Subject: [PATCH 083/117] docs(field-report): the three Gate-A plan cycles for rle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion to the spec cycle's record. Curves for Plans A, B and C, extracted mechanically from the pass files under .context/codex-reviews/, which is gitignored and whose slots later cycles reuse. This is the destination Daniel chose for the four pre-rule cycles' records (2026-08-30), replacing the reconstruct-into-the-commit-body approach three Gate-A passes contested in a row on three separate grounds: wrong destination under the settled per-cycle rule, a marker that could not sit inside a record without breaking the pinned grammar, and — moved beside the record — a marker the squash-carry rule does not carry, because that rule enumerates records and not prose. Committed prose needs no carry; git has it. Plan A: 12 passes, 21/16/16/7/5/2/3/4/6/6/8/0, clean. Plan B: 7 passes, 11/3/6/10/9/7/0, clean. The cheap cycle, and the file says why: it ships grammars, and a grammar is decidable by reading it against itself. Plan C: open at 5, 18/20/20/23/22, Blocker line never zero. The finding that generalizes: a plan restating a protocol its own repo governs grows a second copy that drifts, and Gate A reviews the copy instead of the work. Plan C's two mandatory stops both trace there. Also records the superseded single-plan artifact as history — 31 Blocker/Major at its first pass — explicitly not as a task, since the spec puts supersession remedies out of scope and both of revision 4's and revision 5's attempts were the same remedy in two locations. Gate B: N/A — docs/field-reports/**.md only, explanatory prose, staged set verified. --- .../2026-08-30-gate-a-rle-plan-cycles.md | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md diff --git a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md new file mode 100644 index 0000000..b1f6151 --- /dev/null +++ b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md @@ -0,0 +1,103 @@ +# Gate-A plan cycles `rle` — A, B and C, the record preserved + +Companion to `2026-08-29-gate-a-rle-cycle-evidence.md`, which holds the **spec** cycle. This file +holds the **three plan** cycles for the same change. Their validated findings files live under +`.context/codex-reviews/`, which is gitignored and whose slots are reused, so this is the durable +record — the same reason the spec file and `2026-08-26-fic2-cycle-evidence.md` exist. + +Every number below was extracted mechanically from those files +(`grep -cE '^(BLOCKER|MAJOR|MINOR|NIT) \|'`, `grep -c '^BLOCKER'`, `grep -c '^MAJOR'` per pass), +not recalled. + +**Why the records are here and not in a commit body.** The four Gate-A cycles for this change — +one spec, three plans — all closed, or are closing, **before the record rules they helped write +are active**. An earlier revision of Plan C tried to reconstruct them into the implementation +commit's body with a marker saying they were reconstructed. Three Gate-A passes contested that in +a row, on three separate grounds: the destination is wrong (the settled rule puts each cycle's +record in *that cycle's* closing body), the marker could not live inside a record without breaking +the pinned grammar, and moved beside the record it was not covered by the squash-carry rule, which +enumerates records and not prose. **Daniel's resolution, 2026-08-30: the reconstruction leaves the +commit body entirely.** Pre-rule history goes to this channel, which is committed prose that git +carries on its own. The implementation commit will carry only the native records of the one cycle +the new rules actually bind. + +## The three curves + +**Plan A — the floor predicate and the severity test.** 12 passes, closed clean. + +``` +Findings 21, 16, 16, 7, 5, 2, 3, 4, 6, 6, 8, 0 +Blockers 7, 6, 6, 0, 0, 0, 0, 0, 0, 1, 1, 0 +Majors 10, 8, 7, 3, 4, 1, 2, 2, 5, 5, 4, 0 +``` + +**Plan B — the provenance line, the per-pass curve, the cycle nonce, slot naming.** 7 passes, +closed clean. + +``` +Findings 11, 3, 6, 10, 9, 7, 0 +Blockers 2, 0, 0, 0, 0, 0, 0 +Majors 7, 3, 4, 7, 5, 6, 0 +``` + +**Plan C — rollout, packaging, evidence, the close.** Open at 5 passes when this file was written. +Its closing figures belong in a later revision of this file, and its absence from the list below +is a statement that the cycle had not closed, not that it closed at five. + +``` +Findings 18, 20, 20, 23, 22 +Blockers 5, 4, 2, 4, 5 +Majors 11, 13, 13, 16, 13 +``` + +## What the three cost, and why they differ + +**Plan A's shape is the ordinary one**: Blockers exhausted in three passes, then a long tail of +Majors, then clean. Its late Blockers at passes 10 and 11 were its own fixes regenerating — +the pattern the spec cycle recorded from pass 10 onward. + +**Plan B is the cheap cycle**, and it is worth saying why, because it is the only counterexample +in the set. It ships four record *forms* — pinned grammars with named fields. A grammar is +checkable by reading it against itself: a reviewer can ask whether every field has a production +and whether every production is reachable, and get a decidable answer. It never needed a second +structural revision. + +**Plan C never got its Blocker line to zero**, and both of its mandatory stops trace to the same +cause: **the plan restating rules that live somewhere else.** + +| Revision | What changed | Next pass | +|---|---|---| +| 1–2 | ordinary repair rounds | 16 → 17 Blocker/Major | +| 3 | six unfailable checks stripped | 15 B+M — and the strip took two real actions with it | +| 4 | those two restored | 20 B+M, 17 of 23 findings on the plan's own copy of `CLAUDE.md` §5 | +| 5 | that copy deleted; the plan defers to §5 | 18 B+M, 16 of 22 still on what the deferral left unspecified | + +Two mandatory two-tell stops fired, at passes 4 and 5. The first produced revision 5's +de-restatement; the second produced the resolution recorded at the top of this file. + +**The finding that generalizes:** a plan that restates a protocol its own repo already governs +creates a second copy that drifts, and Gate A will review the copy instead of the work. Plan A and +Plan B state rules; Plan C had to *use* rules, and using them tempted it into repeating them. The +remedy was the same one the spec cycle recorded for restatements generally — **not a better +restatement, a deletion** — but deleting a restatement leaves a gap where the plan-specific facts +were tangled up in it, and pass 5 is a list of those. + +## The superseded single-plan artifact + +`docs/superpowers/plans/2026-08-29-review-loop-economics.md` is the single-plan version of this +change. It **opened at 31 Blocker/Major on its first Gate-A pass** and was replaced by the three +plans above. It is still in the tree, unmarked. + +This is recorded as history, not as a task. Plan C's revision 4 shipped a task to mark the file +in place; the approved spec puts any remedy to the supersession convention out of scope, Gate-A +pass 4 said so, and revision 5's replacement — a line in the closing commit body — was itself the +same remedy relocated, which pass 5 also said. The convention is a real gap and it belongs to a +story of its own; it is not this cycle's to fix, and a note here is not a mechanism. + +## Known limits of this record + +The per-pass files behind these numbers stay in `.context/` and will be overwritten by later +cycles; what survives is the counts above and the dispositions files committed beside them. No +finding is classified under both the old and the new severity rules in any of the three cycles, +so — exactly as the spec cycle recorded — **no demotion figure is derivable from this data**, only +a comparison of recorded mixes across cycles that reviewed different artifacts. From ab8a8fe5867d6c99ce9374f7a157f3a74e18a742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:26:42 +0200 Subject: [PATCH 084/117] =?UTF-8?q?docs(plan):=20Plan=20C=20revision=206?= =?UTF-8?q?=20=E2=80=94=20reconstruction=20leaves=20the=20commit=20body?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 5: 22 findings, 5 BLOCKER, 13 MAJOR = 18 B+M. Curve 16, 17, 15, 20, 18; Blockers 5, 4, 2, 4, 5. Three tells, and both of Daniel's own route-immediately conditions fired: instrument share 73% against a predicted under-a-quarter, and aggregate reconstruction contested a third time. DANIEL'S RESOLUTION, and it answers four routed questions with one move: the reconstruction leaves the commit body entirely. The four pre-rule Gate-A cycles go to docs/field-reports/ — committed prose, the channel with fic2 and rle precedents, which git carries without a carry rule. Landed first, as 5f1eddd, so pass 6 can verify the destination exists. THE DESTINATION WAS THE ERROR, NOT THE MARKER'S POSITION. Three passes contested reconstruction on three grounds: wrong destination under the settled per-cycle rule; a marker that broke the pinned grammar from inside; a marker the squash-carry rule - which enumerates provenance lines, curves and skip records - does not reach from outside. Revisions 4 and 5 answered the second and third and left the first standing. The closing body now carries exactly four things: the evidence entry per cited profiled story, this Gate-B cycle's provenance line, its per-pass curve, and any decline records. Nothing else, and the exclusions are named. SQUASH CARRY: moot, nothing transitional to carry. SUPERSESSION LINE: dropped. It was the out-of-scope remedy relocated into history. The artifact gets a narrative line in the field report, which ships no mechanism. SPEC §8's "PARSE" IS CORRECTED IN THIS COMMIT, per §5's own rule that a fix changing specified behaviour updates the spec alongside it. No parser exists for either grammar and none is built; the pinned forms are greppable field grammars, so the check is a `grep -E` match against each production, with the cardinality rule named as the one case decided by counting instead. The coverage requirement is untouched - constructed valid and invalid strings, features recorded per grammar - which restores what revision 5's "checked by reading" had given up. New Task 19. THE DEPENDENCY CYCLE IS GONE. Revision 5 put all seven evidence obligations before the cycle while three needed the cycle's own output. Split at the only line that can split them: Task 20 runs the six that do not, Task 22 runs the risk-path verification and the knob after-comparison, then composes the body, then closes. $base IS GONE. §5 already says baseSha is the WIP commit's parent, so HEAD^ is the value - deterministic from git, no recorded variable, no file under .context/, and none of the failure paths a recorded one brought: the missing directory, the symlink-following redirect, the loose -\?wip match. THE FOUR HELD REPAIRS APPLIED. Preflight and assert are now separate commands, because they answer different questions and reversing them aborts every task that has not run yet - 0 means apply, and the three-way case says so. The assert string is named as a discriminator, with the whole-block confirmation stated as a reading and the reason given: grep -F reads a multi-line pattern as alternatives, so a whole-block grep would silently OR its lines. Task 6 now describes what the hook actually counts. The CHANGELOG no longer contradicts Plan B's bare-slot reservation - and this cycle is that exception. Also: the 61-file count is marked as observed-when-written with a re-inventory required at execution; item 2's counterfactual is four assertions rather than two prints; the scope check says which part is names and which is hunks. Twenty-two tasks. 41 bash fences, all parse under sh -n. Every assert sentinel verified absent from its target file before the change; all three new OLD blocks match their targets exactly once; fences balanced; every cited path resolves. Gate B: N/A — docs/superpowers/plans/**.md only, staged set verified. --- ...30-review-loop-economics-plan-c-rollout.md | 345 ++++++++++++------ 1 file changed, 235 insertions(+), 110 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md index 4731126..cb22bde 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -14,8 +14,8 @@ Plan C implements §7 and §8. **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` > **Read the profile from that header at execution time.** This plan states no risk value, no -> security value, no validation mode and no pass count derived from any of them. Task 19 reads the -> mode from the header and produces what it names. +> security value, no validation mode and no pass count derived from any of them. Tasks 20 and 22 +> read the mode from the header and produce what it names. --- @@ -67,14 +67,30 @@ whose section moved is relocated, not repaired**, so they are discharged here or - **`git add` names paths explicitly, never `-u`.** Staging whole-tree changes would fold an unrelated edit into the reviewed diff and close it under this story. - **Line numbers are provenance, never instructions.** -- **Every assert is an equality test, never a count print**, and every task is - **skip-if-applied**: run its assert first. `1` means the task already ran — skip it, do not - re-apply. `0` means apply. **Any other count is a stop**, not a retry: a duplicate insertion or - a half-applied edit, which re-running only compounds. This is what makes the plan safe to - resume, and it is why the asserts test equality — a bare `grep -c` succeeds at any positive - count, so it cannot tell one insertion from two. +- **Every task is skip-if-applied, and the preflight is a decision while the assert is a test.** + They answer different questions, so they are different commands and **reversing them is a real + defect**: an assert used as a preflight exits nonzero on `0`, which aborts every task that has + not run yet. **Preflight**, before the edit, where `0` is a valid answer meaning *apply*: + +```sh +case "$(grep -cF -- "" )" in + 0) : ;; # not applied — apply it + 1) echo "ALREADY APPLIED — skip this task"; exit 0 ;; + *) echo "AMBIGUOUS — stop, do not retry"; exit 1 ;; # duplicate or half-applied +esac +``` + + **Assert**, after the edit, where anything but `1` is a failure. The `test ... -eq 1` block in + each task below is that second command, and only that. +- **The assert string discriminates; it does not prove the block landed.** Before amending, + **read the edited site** and confirm the complete NEW block stands exactly once and — except for + Tasks 10 and 12 — no OLD text survives beside it. A sentinel counts `1` in a half-applied edit + and in a file holding OLD and NEW together. **This step is a reading, not a command, and that is + a limitation rather than a preference:** a multi-line pattern given to `grep -F` is read as + several alternative patterns, so a whole-block `grep` would silently OR the lines and report a + match on any one of them. - **Tasks 10 and 12 re-emit their anchor inside their replacement**, so their OLD text survives - by design and only the NEW count discriminates. Every other task's OLD text is gone after it + by design and only the NEW block discriminates. Every other task's OLD text is gone after it runs. --- @@ -106,6 +122,7 @@ and what replaces it. | 16 | scaffolded template skip duties | as row 14, in the mirror | same disposition (Task 16) | | 17 | `getting-started.md` skip duties | as row 14, in an explanatory duty summary | same disposition (Task 17) | | 18 | `coding-workflow.md` skip duties | as row 14, in the other explanatory duty summary | same disposition (Task 18) | +| 19 | spec §8 parse-check item | the evidence is a **parse** check over both grammars | **kept, and its mechanism corrected**: the coverage requirement — constructed valid and invalid strings, features recorded per grammar — is **unchanged**; the word `parse` is replaced by the comparison that exists, a `grep -E` match against each grammar's productions, with the cardinality rule named as the one case decided by counting instead. §5's own rule sends a fix that changes specified behaviour into the same commit (Task 19) | **Rows 10 and 12 are insertions and are listed so a reader can confirm that rather than assume it.** Each re-emits its anchor verbatim inside its replacement. @@ -337,13 +354,13 @@ docs/getting-started.md:58:`✓ Codex Gate B satisfied (3/3 cycle, 3 on current NEW: ``` -`✓ Codex Gate B satisfied (/ cycle, on current fingerprint)` — three different numbers: the calls this cycle made, the hook's reminder threshold, and how many ran against the current fingerprint. None of them is the floor §5 obliges — the real commit replaces +`✓ Codex Gate B satisfied (/ cycle, on current fingerprint)` — three different numbers: the calls the hook counted this cycle, the hook's own reminder threshold, and how many of those counted calls carry a stored fingerprint equal to the current one. The first is not the calls you made: the hook withholds the count for a recognized failure envelope, the backgrounding notice, and a result it can get no text from. None of the three is the floor §5 obliges — the real commit replaces ``` - [ ] **Assert the new text is present.** ```bash -test "$(grep -cF -- "three different numbers: the calls this cycle made" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "the calls the hook counted this cycle" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. @@ -624,9 +641,12 @@ NEW: source of randomness, never derived from a name, timestamp or commit. It is collision-**resistant**, not collision-proof, and the shipped text says where that bound bites rather than implying a guarantee. -- **Findings slots take a per-cycle infix**, and the deletion step §5 already requires now deletes - only paths carrying the cycle's own nonce. That rule exists because a bare slot was overwritten - during this change's own development, destroying a previous cycle's findings file. +- **Findings slots take a per-cycle infix for a cycle holding a nonce**, and for such a cycle the + deletion step §5 already requires now deletes only paths carrying that cycle's own nonce. **The + bare names stay valid** and stay reserved for the legacy single-cycle case — a pre-rule or + no-nonce cycle keeps the bare form, and this release's own Gate-B cycle is one. That rule exists + because a bare slot was overwritten during this change's own development, destroying a previous + cycle's findings file. - **What this change does not settle** is how demotion bears on the loop-health measures — the per-pass counts, the clusters and the stop thresholds. That is the loop-rule consolidation story's, and both documents say so, so the obligation cannot fall between them. @@ -637,7 +657,7 @@ NEW: - [ ] **Assert the new text is present.** ```bash -test "$(grep -cF -- "The mandatory pass floor is now a function of the cited story's profile" plugins/dev-workflow/CHANGELOG.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "The bare names stay valid" plugins/dev-workflow/CHANGELOG.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. @@ -939,37 +959,99 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 19: The evidence pack +## Task 19: The spec §8 wording correction -Spec §8 names seven obligations. **All seven are here** — they are what the author owes, not -checks the plan invented about itself. The mode is read from the story header at execution time. +**File:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` -`$base` below is the cycle base — the WIP commit's parent, recorded at cycle open per Task 20's -first fact. It exists before this task runs, because Plan A opened the WIP. +**Site** — pasted `grep -n`: + +``` +docs/superpowers/specs/2026-08-28-review-loop-economics-design.md:467:- **A parse check over both pinned grammars.** The branch's own instances cannot exercise them: +``` + +> **§5 sends this here rather than to a Gate-A reopening:** *"A fix that changes specified +> behaviour updates the spec in the same commit."* §8 asks for a **parse** check. No parser for +> either grammar exists in this repo, and none is built — a check that cannot execute is not a +> check. What the pinned forms actually support is a **grep**, because they are field grammars +> with delimiters, and a grep decides the same question for them. **The coverage requirement is +> untouched:** constructed strings, valid and invalid, features recorded per grammar. Only the +> named mechanism changes, and it changes to one that exists. + +- [ ] **Replace.** OLD: + +``` +- **A parse check over both pinned grammars.** The branch's own instances cannot exercise them: + three lines cannot cover quoted paths, each unusable-knob cause, gapped pass ranges, split-model + passes, a skipped cycle, or the cardinality rule that `` matches ``. **The check + reads a set of constructed strings — valid ones that must parse and invalid ones that must be + rejected** — and records which grammar features each exercises. A grammar nothing ever parsed is + a format claim, not a format. +``` + +NEW: -- [ ] **1 — Battery.** The full `AGENTS.md` § Commands chain, green, **including the version-bump - checker with the recorded base as its argument** — it takes a base ref and **exits 2 with no - argument**, so the bare invocation is not a run. Plans A and B deferred this step for a stated - reason; it runs now because Task 11 landed the bump and the WIP exists. +``` +- **A grammar check over both pinned grammars.** The branch's own instances cannot exercise them: + three lines cannot cover quoted paths, each unusable-knob cause, gapped pass ranges, split-model + passes, a skipped cycle, or the cardinality rule that `` matches ``. **The check + matches a set of constructed strings against each grammar's own productions with `grep -E` — + valid ones must match, invalid ones must not** — and records which grammar features each + exercises. **It is a grep, not a parser**: these forms are pinned as greppable field grammars + and this repo ships no parser for either, so naming a parser would name a check nobody can run. + The one production a single match cannot decide is the cardinality rule, which is a count and is + checked by counting the entries on each side. A grammar nothing was ever matched against is a + format claim, not a format. +``` -- [ ] **2 — A check that fails without the change**, read against **both** revisions: +- [ ] **Assert the new text is present.** ```bash -git show "$base":CLAUDE.md | grep -cF 'Blocker/Major-free pass 1 carrying a Minor' -grep -cF 'a Blocker/Major-free pass below the floor' CLAUDE.md +test "$(grep -cF -- "It is a grep, not a parser" docs/superpowers/specs/2026-08-28-review-loop-economics-design.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` -**Grep the discriminating text, not the common tail.** Pre-change says **pass 1** keeps looping — -wrong at floor 1, where pass 1 *is* the floor; post-change says **below the floor**. Record which -revision gave which answer. +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. + +- [ ] **Amend the WIP commit.** + +```bash +git add docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 20: The evidence pack — everything that runs before the cycle + +Spec §8 names seven obligations. **All seven are discharged, split by when they can run**: six +here, and the two halves that depend on the cycle's own output in Task 22. The mode is read from +the story header at execution time. + +**The cycle base is `HEAD^`** — §5's own rule, that `baseSha` is the WIP commit's parent. Exactly +one WIP commit stands (Task 21 checks it), so no variable is recorded and no file holds it. + +- [ ] **1 — Battery.** The full `AGENTS.md` § Commands chain, green, **with the version-bump + checker given `HEAD^` as its base ref** — it takes one and **exits 2 with no argument**, so the + bare invocation is not a run. Plans A and B deferred this step for a stated reason; it runs now + because Task 11 landed the bump and the WIP exists. + +- [ ] **2 — A check that fails without the change.** Four assertions, two per revision: -- [ ] **3 — A named verification of the risk path.** **Runs after the provenance lines are drafted - and before the close** — it verifies those lines, so it cannot precede them. Recompute each - line's floor from the `Story:` header of the artifact that cycle reviewed. **The observation that - would exist if the claim were false is a line whose floor the cited profiles do not license.** +```bash +test "$(git show HEAD^:CLAUDE.md | grep -cF 'Blocker/Major-free pass 1 carrying a Minor')" -eq 1 || { echo "BASE LACKS THE OLD SENTENCE"; exit 1; } +test "$(git show HEAD^:CLAUDE.md | grep -cF 'a Blocker/Major-free pass below the floor')" -eq 0 || { echo "BASE ALREADY CARRIES THE NEW ONE"; exit 1; } +test "$(grep -cF 'Blocker/Major-free pass 1 carrying a Minor' CLAUDE.md)" -eq 0 || { echo "OLD SENTENCE SURVIVES"; exit 1; } +test "$(grep -cF 'a Blocker/Major-free pass below the floor' CLAUDE.md)" -eq 1 || { echo "NEW SENTENCE MISSING"; exit 1; } +``` + +**Grep the discriminating text, not the common tail.** Pre-change says **pass 1** keeps looping — +wrong at floor 1, where pass 1 *is* the floor; post-change says **below the floor**. **The +counterfactual is the third and fourth assertions**: without the change they fail, and the wiring +can produce that failure because the first two show the old text really is in the base. The +scaffolded mirror carries this rule too and is covered by item 6, which compares both copies. -- [ ] **4 — The conditional knob verification.** Take **one** observation of the path and derive - everything from it, so bytes, digest and value class describe the same read: +- [ ] **4a — The knob observation, before the cycle.** Take **one** observation of + `.context/codex-gate.floor` and derive everything from it, so bytes, digest and value class + describe the same read: - **absent** — no path. Record not-applicable with that reason. **Do not create one**; a fixture supplying its own input proves nothing. @@ -980,19 +1062,25 @@ revision gave which answer. - **a readable regular file** — record bytes and digest, and classify the value as numeric or as one of the pinned unusable causes. - Recorded before the first Gate-B call and compared after. **Existence is not the check.** + **Existence is not the check.** Task 22 repeats the observation and compares. + +- [ ] **5 — A grammar check over both pinned grammars**, features assigned **per grammar**. + Construct strings and match each against that grammar's own productions with `grep -E`: valid + ones must match, invalid ones must not. + + **Provenance:** a quoted path; each `unusable()` value; a cited-story-with-no-profile + entry; `none` for no story cited. **Curve:** a gapped `` such as `1,2,4`; a split-model + pass; a `?` count; a skipped cycle's skip record. **Each grammar gets at least one string that + must fail to match** — for provenance, a repeated ``. -- [ ] **5 — Both pinned grammars exercised by the records this cycle actually writes.** The five - provenance lines and five curves are **written from the grammars** — each field produced by - reading its production, not by copying an example — and **checked by reading**: they go into the - closing body, and the Gate-B reviewer receives it. Record which feature each of the ten records - exercises, and **which productions this branch cannot demonstrate, with the reason** (the cycle - identifier and the knob clause's non-absent form; see Task 20). + **The cardinality rule is the exception, and it is named rather than glossed:** `` having + as many entries as `` enumerates is a count, not a match, and no single regex decides it. + Check it by counting the entries on each side and comparing, with one passing and one failing + instance. Both routes execute; neither is a parser, which is why Task 19 corrects §8's word for + it. - **No parser is invented here.** A parse check needs a parser, this repo has none for either - grammar, and *a check that cannot execute is not a check* — the standard revision 3 applied to - the six it stripped, applied to this one. The programmatic consumer is P8's; until it ships, the - grammars are enforced by a reader, and this obligation says so rather than implying otherwise. + Record which feature each string exercises. **This is where the grammars get their coverage** — + the cycle's own two records cannot, which is the reason §8 asked for constructed strings. - [ ] **6 — Parity across every changed rule**, in both copies. **One difference is expected and is the only one**: the successor-story pointer, which `CLAUDE.md` carries and the template must @@ -1005,25 +1093,37 @@ revision gave which answer. --- -## Task 20: The Gate-B cycle and the close +## Task 21: The Gate-B cycle **Run the cycle per `CLAUDE.md` §5.** The floor, the file-first findings protocol, delete-before-call, what makes a pass valid, single-branch recovery, re-review after every fix, the clean-final-pass rule and the closing amend are **§5's, and this plan does not restate them**. -Revision 4 did, and Gate-A pass 4 spent seventeen of its twenty-three findings reviewing that +Revision 4 restated them, and Gate-A pass 4 spent seventeen of twenty-three findings reviewing the restatement against the rules it was restating. **Three facts belong to this cycle and are not in §5:** -1. **The base.** Record the WIP commit's parent at cycle open and pass it as `baseSha`, per §5's - own `baseSha` rule. Plan A prints its base and does not persist it, and Plan A is closed and - byte-frozen, so this cycle establishes it. +1. **The base is `HEAD^`.** §5's own rule: `baseSha` is the WIP commit's parent. Confirm first + that exactly one WIP commit stands and that it is not a merge — + +```bash +git rev-parse --verify HEAD^2 >/dev/null 2>&1 && { echo "WIP IS A MERGE — stop"; exit 1; } +git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } +git log -1 --pretty=%s HEAD^ | grep -q '^WIP:' && { echo "PARENT IS ALSO A WIP — stacked, collapse first"; exit 1; } +``` + + The third asks about a **different** commit, which is why it can fail. A stacked WIP would + silently leave the earlier snapshot out of the only review. 2. **The slots are `gate-b--rle-pass-

.md`**, never the bare names. This workspace - already holds **61 files in the bare `gate-b-*-pass-*` family** from earlier cycles, and §5's - delete-before-call step would destroy them — precisely the incident the slot rule exists to - prevent, committed by the plan that ships it. Not a nonce either: this cycle is pre-rule and - cannot mint one. **§8 names exactly this case** — *"its slot discriminator is short and - deterministic, so it is not a nonce"* — and `rle` is that discriminator. + already holds **61 files in the bare `gate-b-*-pass-*` family** — observed when this plan was + written, so **re-inventory before the first deletion rather than trusting the number**; the + slot choice is right either way. §5's delete-before-call step would destroy whatever is there, + which is precisely the incident the slot rule exists to prevent, committed by the plan that + ships it. Not a nonce either: this cycle is pre-rule and cannot mint one, and **Plan B reserves + the bare names for exactly the legacy no-nonce case** — which is why the discriminator, not the + bare name, is what keeps this cycle off the old files. **§8 names this case** — *"its slot + discriminator is short and deterministic, so it is not a nonce"* — and `rle` is that + discriminator. 3. **The cycle runs under the OLD rules**, per the activation constraint: **floor 3**, the constant, not the derived value this diff introduces. Carry the old-rules sentence from Global @@ -1033,64 +1133,89 @@ restatement against the rules it was restating. **Three facts belong to this cyc evidence entry quoted verbatim. **Before the first call**, confirm `git status --porcelain` is empty and read -`git diff --name-only "$base"..HEAD` against the three plans' declared surface. **This reads path -names, not content**: it catches a stray *file*, not a stray hunk inside a file the plans -legitimately touch. - -### What the closing body carries - -The evidence entry, and **one provenance line and one curve per cycle — five of each**: one Gate-A -spec cycle, three Gate-A plan cycles, one Gate-B cycle. - -**The reconstruction marker sits beside the records, never inside them** — one adjacent line, of -the form *"Records for the Gate-A spec cycle and the three Gate-A plan cycles are reconstructed -from validated pass files; those cycles closed before the forms were active."* — leaving every -record string byte-conformant to its pinned grammar. **A marker inside a record would have to be a -grammar production, and neither grammar has one.** Revision 4 put it inside to survive the squash -carry; the carry copies the body's records, and an adjacent line in the same block travels with -them. - -**One line records the superseded artifact:** `2026-08-29-review-loop-economics.md` is superseded -by Plans A, B and C and is not executable. **It is not edited.** Revision 4 shipped a task to mark -it; the spec places supersession remedies out of scope, and Gate-A pass 4 was right that the task -expanded the settled change without an approved decision. A line in the record is what a -closure record is for. - -**Every cycle here is pre-rule**, so each carries `cycle none (pre-rule)`. **The branch demonstrates -every field of both forms except two** — the cycle identifier and the knob clause's non-absent form -— **recorded as undemonstrable here with their reasons**, not as gaps and not as satisfied. **Any +`git diff --name-only HEAD^..HEAD` against the three plans' declared surface. **This reads path +names, not content** — it catches a stray *file*, not a stray hunk inside a file the plans +legitimately touch. **Read the full `git diff HEAD^..HEAD` too**, against each task's own OLD-to-NEW +text above; that is the check that covers hunks, and it is a reading because the plan's OLD and NEW +blocks are what it compares against. + +--- + +## Task 22: The closing body, and the close + +**Runs after the final clean pass and before the amend.** The two evidence halves that need the +cycle's own output land here, then the body is composed from them. + +- [ ] **3 — A named verification of the risk path.** This cycle emits **one** provenance line. + Recompute its floor from the `Story:` header of the artifact this cycle reviewed. **The + observation that would exist if the claim were false is a line whose floor the cited profile + does not license.** + +- [ ] **4b — The knob observation, after the cycle.** Repeat Task 20 item 4a's single observation + and assert equality with the before-state: same path type, same bytes, same digest, or the same + recorded absence. **The knob is never written or removed by this plan**; this is what makes that + a demonstrated claim rather than an assurance. + +- [ ] **Revalidate the evidence entry** against the content the close will carry — §5 requires it + after every fix and again before the closing amend, and a fix changes the diff even when the + profile sits still. + +### What the closing body carries — and this is the complete list + +1. **The evidence entry**, one per cited **profiled** story, per §5. The story governing this + cycle is profiled, so there is one. +2. **This Gate-B cycle's provenance line.** +3. **This Gate-B cycle's per-pass curve.** +4. **Decline records**, if the cycle produced any. + +**Nothing else, and the exclusions are the point.** No records for the four Gate-A cycles, no +reconstruction, no reconstruction marker, no adjacent prose explaining one, and no line about the +superseded single-plan artifact. **Those four cycles closed before these rules bound anything, and +their record is `docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md`** — committed prose, which +git carries without a carry rule. Revision 4 put them in the body with a marker inside each record, +which broke the pinned grammar; revision 5 moved the marker beside the records, where the +squash-carry rule — which enumerates provenance lines, curves and skip records — does not reach it. +**The destination was the error, not the marker's position.** + +**The cycle field is `cycle none (pre-rule)`.** Plan B reserves that value for a cycle that began +before the rules shipped, and this one did. **The branch therefore demonstrates every field of both +forms except two** — a real nonce, and the knob clause's non-absent form if the knob is absent — +**recorded as undemonstrable here with their reasons**, not as gaps and not as satisfied. Task 20 +item 5's constructed strings are what cover the rest, which is why that obligation exists. **Any cycle that both starts under these rules and closes discharges the nonce demonstration.** -> **Why reconstruction does not contradict §8.** §8 says *"No reconstruction is needed and none is -> claimed"* and gives its grounds in the same breath — *"the Gate-A spec cycle has not closed … -> and the Gate-A plan cycle has not started"*. **That premise failed**: the spec cycle closed at -> pass 34 before the forms existed, and the one plan cycle became three. §8's sentence was a true -> prediction about a topology that changed, and what it protects — that no record silently claims -> to be contemporaneous — is preserved by the adjacent marker. **§8's "three closing bodies" is -> stale for the same reason**, exactly as the story criterion's "a run of all three holds three" -> is: the rule is one record per cycle, and five cycles ran. +- [ ] **Close**, per §5's Mechanics: `git commit --amend -m` with the real message replacing the + WIP one, carrying the four items above. **Not `--no-edit`** — see Global Constraints. If the + amend fails, **the cycle is invalid, not retryable**: the hook resets Gate-B state on any + non-WIP commit command whether or not git succeeded. Repair while `HEAD` is still the WIP, run + the floor again, revalidate the evidence, then close. --- ## Self-Review -**Spec coverage.** §7 rollout — Tasks 1-18. §8 evidence — Task 19, all seven obligations. The -cycle and the close — Task 20. - -**What revision 5 removed, and why it is not another over-strip.** Revision 3 stripped six checks -that could not fail and took two real actions with them; revision 4 restored those. **Revision 5 -removes something different in kind: the plan's own restatement of a protocol `CLAUDE.md` §5 -already owns.** Nothing is lost, because §5 is what an executor follows either way — the -restatement was a second, drifting copy of it, and Gate-A pass 4 reviewed the copy instead of the -rollout. The three facts §5 does *not* carry are stated in Task 20. The mechanics pass 4 found -broken inside the restatement — an unassigned `body`, a symlink-following redirect, a loose -`-\?wip` match, a missing `.context` — **go with it rather than being repaired**, because -repairing them would have kept the copy. - -**Inherited obligations.** M9 at Task 19 item 4. M10 in Global Constraints. **M8 and B6 are §5's -own rules** — single-branch recovery, and evidence revalidated after every fix and before the -close — so Task 20 defers to §5 rather than restating them. **MINOR 12 is moot**: it asked for -`mktemp` over a fixed `/tmp` path, and the plan no longer scripts the close at all. - -**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's — Task 20, +**Spec coverage.** §7 rollout — Tasks 1-18. **§8 evidence — Tasks 20 and 22**, all seven +obligations, split at the only line that could split them: whether the obligation needs the +cycle's own output. §8's own wording — Task 19. The cycle and the close — Tasks 21 and 22. + +**The dependency cycle is gone.** Revision 5 put the whole evidence pack before the cycle while +three of its seven items needed the cycle's output, so no task could truthfully complete. Items 3 +and 4b now sit after the final clean pass, where their inputs exist. + +**Reconstruction is gone, and the reason is worth keeping.** Three Gate-A passes contested it on +three different grounds — wrong destination, a marker that broke the grammar from inside, a marker +the squash-carry rule does not reach from outside. Two revisions answered the second and third and +left the first standing. **The destination was the error.** Pre-rule history is committed prose in +`docs/field-reports/`; the commit body carries only what this cycle natively produced. + +**`$base` is gone too.** §5 already says `baseSha` is the WIP commit's parent, so `HEAD^` is the +value, deterministic from git — no recorded variable, no file under `.context/`, and none of the +failure paths a recorded one brought with it. + +**Inherited obligations.** M9 at Task 20 item 4a and Task 22 item 4b. M10 in Global Constraints. +**M8 and B6 are §5's own rules** — single-branch recovery, and evidence revalidated after every +fix and before the close — so Task 21 defers rather than restating. **MINOR 12 is moot**: it asked +for `mktemp` over a fixed `/tmp` path, and the body is now four items carried by §5's own amend. + +**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's — Task 21, reading the real combined diff of all three plans. From 2badb57e9afe7b051af2a294a93c02fa33d52bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:38:18 +0200 Subject: [PATCH 085/117] docs(vision): dark-factory target vision and decomposition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decomposition document from Daniel's 2026-08-30 brainstorming session, recorded verbatim by the sparring session and committed unedited. Not an implementation spec: every build step in §7 becomes its own story through the normal workflow, and nothing here overrides a shipped rule. Touches no prompt and no AGENTS.md — deliberately, since the AGENTS.md extension it names (project goals and an explicit out-of-scope list) is step 4 of its own build path. Daniel's review of the content is pending with the sparring session; a follow-up docs commit carries any delta. Gate B: N/A — docs/superpowers/specs/**.md only, staged alone, staged set verified. --- .../specs/2026-08-30-dark-factory-vision.md | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-30-dark-factory-vision.md diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md new file mode 100644 index 0000000..f512f87 --- /dev/null +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -0,0 +1,161 @@ +# Dark Factory — target vision and decomposition + +**Date:** 2026-08-30 · **Kind:** decomposition document, not an implementation spec. +Each build step below becomes its own story through the normal workflow +(intake → spec → Gate A → plan → Gate A → execute → Gate B). Nothing in this +document is executable on its own, and nothing here overrides a shipped rule. + +Brainstormed 2026-08-30 (Daniel + sparring session); all decisions below are +Daniel's, taken in that session. Inspiration: the loop/graph-engineering framing +from an external video (four loop maturity stages; artifacts as the only handoff +between fresh-context nodes; a "dark factory" as a repository that ships its own +code, policed by an adversarial model with sampled human audit). + +## 1. The vision + +Stories and ideas flow into a pool. The factory turns approved pool items into +merged, reviewed software with the human concentrated where human judgement +measurably matters — and, at full maturity, sampled rather than omnipresent: + +``` +Story-Pool (status: draft → freigegeben) + → Takt-Loop (polls the pool) [missing] + → Klassifizierung (classification card, may reject/split/ask) [missing] + → reads Projekt-Wahrheit: AGENTS.md [exists, needs one extension] + → Spec-Loop (intake + design + Gate A) [exists] + → Plan-Loop (writing-plans + Gate A) [exists] + → Bau-Loop (executing-plans, goal-based, TDD) [exists] + → Verify (battery + Gate B — adversarial model) [exists] + → Sample-Gate (draws x% of PRs for human audit) [missing] + → Audit (human, sampled) → Merge/Deploy [missing] +``` + +Every node is a loop with its own fresh context; only the artifact crosses an +edge (story, spec, plan, diff, PR) — never the intermediate steps. Mandatory +stops, scope questions and architecture re-evaluations escalate to the human; +the factory stops and reports instead of spinning. A human override becomes a +labeled example for tightening the rules. + +The kit already holds the middle of this pipeline, and in one respect exceeds +the inspiration: the adversarial verifier is a different model *family* +(Codex), not merely a different context. + +## 2. Decisions (all 2026-08-30, Daniel) + +1. **Orchestrator lives hybrid.** The orchestrator is kit prompts (a skill / + agent definition any session can load — the product stays prompts). Only the + *clock* uses platform mechanics: a scheduled/loop wake-up starts an + orchestrator session. No daemon, no server-side infrastructure. +2. **Project truth is AGENTS.md, and the architecture tree is subordinate to + the pool.** The architecture is built *from* the pool initially and + continuously re-evaluated against it (Bewertungs-Loop), versioned, living in + AGENTS.md beside the invariants and conventions. No second architecture + document that could drift. +3. **Architecture re-evaluation is a meta-story through the same factory.** + When a story breaks the tree, classification produces a story "extend the + architecture for X" with a high risk profile (heavy review, human in the + loop); the triggering story waits on it. One process for everything — the + factory rebuilds itself the same way it builds features. +4. **Classification is the mandatory first station.** Every new pool item gets + the card (§5 below) before anything else; only "freigegeben" is pulled by + the clock. +5. **End state is sampled audit, not per-merge approval.** The adversarial + gate checks every merge; the human audits a sample. No merge skips both + gates. (Until step 6 of the build path matures, merge remains human.) + +## 3. Maturity ladder + +| Stage | Loop kind | Status in the kit | +|---|---|---| +| 1 | Turn-based — skills with self-checks, red-first tests | shipped | +| 2 | Goal-based — acceptance criteria as target, gates loop to clean with mandatory stops | shipped | +| 3 | Time-based — clock loops: poll the pool, drift audits, PR-bot processing | missing | +| 4 | Proactive — event-triggered: a spec turns "freigegeben" and the factory runs | missing | + +## 4. Conventions and roadmap — where each truth lives + +- **Kit conventions** (travel with the plugin): the workflow itself — gates, + profiles, loop rules, prompt standards, the classification card. Home: the + kit's prompts. They are the product. +- **Project conventions** (per target project): AGENTS.md — architecture tree, + invariants, verified commands, conventions, **plus one extension this vision + requires: project goals and an explicit out-of-scope list**, which the + classification's reject verdict reads. One document, read by gates, bots and + triage alike. +- **A project's roadmap is a view, not a document**: pool items plus status, + priority and dependencies (from classification) yield the order. The pool is + the single source; a separate roadmap file would be a second copy that + drifts. +- **The kit's own roadmap** to this vision is §7 of this document. + +## 5. The classification card + +Every new pool item is classified before anything else. Dimensions 1–4 exist +in the intake skill today; 5–7 are new: + +1. **Size** — story, or epic that must be split. +2. **Risk/security profile** — drives floor, lenses, evidence mode. +3. **Completeness** — too thin → one question back to the human; nothing is + invented. +4. **Invariant touch-list** — which AGENTS.md invariants the item affects. +5. **Scope verdict** — inside project goals? Duplicate of a pooled or in-flight + story? Violates an invariant by design? (Source: the goals/out-of-scope + extension of AGENTS.md.) +6. **Architecture verdict** — from the Bewertungs-Loop: seamless, or + re-evaluation needed (→ meta-story per decision 3). +7. **Dependencies** — needs story Y first → wait mark and ordering. + +Verdicts: **freigeben / teilen / rückfragen / warten-auf / ablehnen** (with +reason, back to the human — rejection is never deletion). Split rules: multiple +independent subsystems in one item; mixed profiles in one item (so the cheap +part gets the cheap floor); mixed architecture verdict (the seamless part +proceeds, the breaking part waits on the meta-story). + +## 6. Gaps (current state → vision) + +1. Story pool with status — the factory's trigger. *(missing)* +2. Classification station beyond intake's current card (scope, architecture, + dependency dimensions). *(missing)* +3. Architecture tree + Bewertungs-Loop in AGENTS.md. *(missing)* +4. Orchestrator as product — the sparring-session practice codified as + skill/agent: question routing, artifact handoff, prediction ledger, batched + human decisions. *(missing; exists as practice)* +5. Time-based and proactive loops (stage 3/4). *(missing)* +6. Sample-Gate + sampled audit. *(missing)* +7. Fresh context per stage is convention, not enforced — long sessions + measurably degrade. *(partial)* +8. Same-repo parallelism — N worktrees × 1 agent works today; the record/nonce + rules of the in-flight review-economics story are the foundation for more. + *(in flight)* + +## 7. Build path (each step = one story through the normal workflow) + +1. Finish the review-economics story (in flight) — floors by profile, severity + calibration, measurable records. Without calibrated review economics every + factory is a token furnace. +2. Loop-rule consolidation (successor story) + P8 passive metrics — measure + before automating further. +3. Orchestrator story (codify the role as skill/agent per decision 1). +4. Story pool + status + classification + architecture tree (decisions 2 and 4). +5. Stage 3: clock and event loops (poll, audit, PR processing). +6. Stage 4 + sampled audit (decision 5) — full automation first only for + level-0 stories; merge stays human until this step ships and holds. + +## 8. Non-goals + +- No daemon or server-side runner (decision 1). +- No second architecture or roadmap document (decisions 2, §4). +- No removal of the human from mandatory stops, profile confirmations, scope + changes, or architecture meta-stories — "dark" means sampled presence, not + absence. +- No autonomy expansion ahead of the measured evidence (P8) that the review + economics support it. + +## 9. Open questions (owned by the stories that will answer them) + +- Sample percentage and drawing rule for the Sample-Gate (step 6). +- Storage form of the pool (files in-repo vs. external board) — step 4. +- Hidden verification scenarios (checks written before build, unseen by the + builder) as a Gate-B supplement — candidate small story, unscheduled. +- How the clock's platform mechanics (loop/schedule) are configured per + project — step 5. From bcd45dca1e60f2192c82fa7f5f9f31309d9ce861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:46:39 +0200 Subject: [PATCH 086/117] =?UTF-8?q?docs(plan):=20Plan=20C=20revision=207?= =?UTF-8?q?=20=E2=80=94=20the=20claim=20sweep?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 6: 19 findings, 2 BLOCKER, 16 MAJOR = 18 B+M. Curve 16, 17, 15, 20, 18, 18. Tripwire fired; Daniel's answer was a method, not a list: sweep the CLAIMS, fix every site of each in one pass, then one bounded final pass. WHY A SWEEP. Three of pass 6's findings were one claim corrected in one place and left standing in another — all three mine, all inside revision 6. That is the disease AGENTS.md already names: search for the CLAIM, not the phrase. Revision 7 uses that recipe as its method rather than as a warning about it. CLAIM 1 — WHAT THE HOOK STORES AND COUNTS. Sites: Task 6's NEW block, accounting row 6. Resolution: is a CONSECUTIVE STREAK on the current fingerprint since it last changed, not a tally. The hook keeps the last fingerprint plus that streak (codex-gate.sh:826-834), so a pass on a changed fingerprint restarts it and an earlier matching pass separated by a different fingerprint is not counted. Revision 6 had corrected "calls you made" into a subtler wrong sentence. CLAIM 2 — PARSER EXISTENCE. Sites: Task 12's CHANGELOG NEW, Task 21's rationale and NEW block, Task 22 item 5, accounting rows 19/21, and spec §1 — a site no task had touched. Resolution: nothing parses these forms today; P8's deferred measurement is the intended consumer. Future-facing everywhere. Task 21 gains a second replacement for §1's present-tense "a program parses them". Left alone deliberately, with the reason stated: §2.3 and §2.4 already name the consumer as deferred, and "a parser needs no special case" is a property of the grammar, not an existence claim. CLAIM 3 — WHICH SLOT FORMS ARE VALID. This one changed what the plan ships. Sites: Plan B's shipped rule in both prompt copies, Task 12's CHANGELOG, Task 23. The contradiction was real and worse than a wording clash: Plan B sends a NO-NONCE cycle to the BARE names, this cycle has no nonce, and the bare names are where the 61 files live that §5's delete-before-call step would destroy. Task 23's rle form was admitted by neither the bare names nor the nonce grammar. The plan and the prompt it ships could not both be followed. Resolved ONE way: the shipped rule gains the production (new Tasks 19 and 20, both copies). Spec §8 already describes it — "short and deterministic, so it is not a nonce" — so this makes the prompt state what the approved spec assumes. The text says outright that a deterministic discriminator promises nothing about collisions. CLAIM 4 — WHAT A GREP CAN DECIDE. Sites: Task 21's spec NEW block, Task 22 item 5. Revision 6 named cardinality as the only rule a match cannot decide. Wrong, and the same overclaim in a smaller font. Four are named now — cardinality, repeated , range ordering and overlap, per-pass key agreement — each with a passing and a failing instance, and the list carries its own limit: it is what reading both grammars produced and is not proven complete. CLAIM 5 — THE PREFLIGHT'S DESIRED-STATE EXIT CODE. The third guard's `grep -q ... && { ...; }` returned 1 when the parent was correctly NOT a WIP: the block reported failure exactly when it should have reported success. All four guards are `if` statements now, HEAD^ is positively verified, and the block ends on a command that succeeds. CLAIM 6 — THE FIELD REPORT COMPLETES. New Task 25, after the close, ordinary docs commit: extract Plan C's final curve mechanically the way that file's other numbers were taken, replace the provisional "open at 5 passes" passage, assert the provisional wording is gone. An artifact chosen for observability and left knowingly incomplete is worse than none. Also swept: the field report said the commit body carries the cycle "the new rules actually bind" while the plan calls the same cycle pre-rule. Both now say it is pre-rule and writes native records because this branch's criteria require them. And item 5 now constructs a real nonce and a valid numeric knob value — the two fields the live cycle cannot produce, which the close was recording as covered. Twenty-five tasks. 48 bash fences, all parse under sh -n. Both slot tasks' OLD anchor verified to match Plan B's NEW text verbatim, once. Every assert sentinel verified absent from its target before the change. Fences balanced, every cited path resolves. Gate B: N/A — docs/superpowers/plans/**.md and docs/field-reports/**.md only, staged set verified. --- .../2026-08-30-gate-a-rle-plan-cycles.md | 7 +- ...30-review-loop-economics-plan-c-rollout.md | 353 +++++++++++++++--- 2 files changed, 298 insertions(+), 62 deletions(-) diff --git a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md index b1f6151..de30bb6 100644 --- a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md +++ b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md @@ -18,8 +18,11 @@ record in *that cycle's* closing body), the marker could not live inside a recor the pinned grammar, and moved beside the record it was not covered by the squash-carry rule, which enumerates records and not prose. **Daniel's resolution, 2026-08-30: the reconstruction leaves the commit body entirely.** Pre-rule history goes to this channel, which is committed prose that git -carries on its own. The implementation commit will carry only the native records of the one cycle -the new rules actually bind. +carries on its own. The implementation commit carries only the native records of +its own Gate-B cycle. **That cycle is also pre-rule** — it began before these rules shipped, so +its records carry `cycle none (pre-rule)` like the rest. It writes them natively not because the +new operational rules bind it, but because this branch's own acceptance and evidence criteria +require them. ## The three curves diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md index cb22bde..c47fe5b 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -14,7 +14,7 @@ Plan C implements §7 and §8. **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` > **Read the profile from that header at execution time.** This plan states no risk value, no -> security value, no validation mode and no pass count derived from any of them. Tasks 20 and 22 +> security value, no validation mode and no pass count derived from any of them. Tasks 22 and 24 > read the mode from the header and produce what it names. --- @@ -109,7 +109,7 @@ and what replaces it. | 3 | same, plan loop | the same 3-pass loop | **replaced**; the rest of the sentence kept (Task 3) | | 4 | same, hook message | the Gate-A floor wasn't met | **false** — the hook reports its own threshold, which at a derived floor of 1 reports a shortfall the cycle does not owe. Replaced (Task 4) | | 5 | same, Gate-B loop | three passes, final clean | **replaced** by the derived floor; "final clean" kept (Task 5) | -| 6 | same, satisfied message | `3/3 cycle` | **kept as an example, relabelled** — the literal is the hook's ratio, so `N/N` with N named as the threshold rather than the floor (Task 6) | +| 6 | same, satisfied message | `3/3 cycle`, and `3 on current fingerprint` read as a tally | **replaced with named placeholders** — the numerator is the calls the hook *counted*, the denominator its own threshold, and the third value a *consecutive streak* on the current fingerprint. None is the floor (Task 6) | | 7 | same, axes sentence | the axes never subtract **and** the floor is unchanged at every level | first clause **kept** — it is true and is the point; second **deliberately dropped**, since this change is what makes the floor vary (Task 7) | | 8 | same, second knob mention | the knob moves the 3-pass floor | **false**, same as row 1. Both sites corrected, because fixing one would leave the other teaching it (Task 8) | | 9 | `coding-workflow.md` axes sentence | as row 7 | same disposition (Task 9) | @@ -122,7 +122,9 @@ and what replaces it. | 16 | scaffolded template skip duties | as row 14, in the mirror | same disposition (Task 16) | | 17 | `getting-started.md` skip duties | as row 14, in an explanatory duty summary | same disposition (Task 17) | | 18 | `coding-workflow.md` skip duties | as row 14, in the other explanatory duty summary | same disposition (Task 18) | -| 19 | spec §8 parse-check item | the evidence is a **parse** check over both grammars | **kept, and its mechanism corrected**: the coverage requirement — constructed valid and invalid strings, features recorded per grammar — is **unchanged**; the word `parse` is replaced by the comparison that exists, a `grep -E` match against each grammar's productions, with the cardinality rule named as the one case decided by counting instead. §5's own rule sends a fix that changes specified behaviour into the same commit (Task 19) | +| 19 | §5 findings-slot rule, as Plan B leaves it | the bare names serve every cycle with no nonce | **kept and extended**: the bare-name reservation stands and the deletion binding stands; a no-nonce cycle writing where bare-slot files already exist takes a deterministic discriminator instead. Nothing is dropped — the exception is added because this cycle is that case (Task 19) | +| 20 | the same rule in the scaffolded mirror | as row 19 | same disposition, second copy (Task 20) | +| 21 | spec §8 parse-check item | the evidence is a **parse** check over both grammars | **kept, and its mechanism corrected**: the coverage requirement — constructed valid and invalid strings, features recorded per grammar — is **unchanged**; the word `parse` is replaced by the comparison that exists, a `grep -E` match against each grammar's productions, with the cardinality rule named as the one case decided by counting instead. §5's own rule sends a fix that changes specified behaviour into the same commit (Task 21) | **Rows 10 and 12 are insertions and are listed so a reader can confirm that rather than assume it.** Each re-emits its anchor verbatim inside its replacement. @@ -354,7 +356,7 @@ docs/getting-started.md:58:`✓ Codex Gate B satisfied (3/3 cycle, 3 on current NEW: ``` -`✓ Codex Gate B satisfied (/ cycle, on current fingerprint)` — three different numbers: the calls the hook counted this cycle, the hook's own reminder threshold, and how many of those counted calls carry a stored fingerprint equal to the current one. The first is not the calls you made: the hook withholds the count for a recognized failure envelope, the backgrounding notice, and a result it can get no text from. None of the three is the floor §5 obliges — the real commit replaces +`✓ Codex Gate B satisfied (/ cycle, on current fingerprint)` — three different numbers: the calls the hook counted this cycle, the hook's own reminder threshold, and the **consecutive** counted calls on the current fingerprint since it last changed. The first is not the calls you made: the hook withholds the count for a recognized failure envelope, the backgrounding notice, and a result it can get no text from. The third is a streak, not a tally — the hook keeps the last fingerprint and that streak, so a pass on a changed fingerprint restarts it and an earlier matching pass separated by a different fingerprint is not counted. None of the three is the floor §5 obliges — the real commit replaces ``` - [ ] **Assert the new text is present.** @@ -633,7 +635,10 @@ NEW: reader of the text it reviews, gates remain readers of rule text they will later apply, and a human reader never satisfies the test. It sets a ceiling, never a floor, and never chooses between Blocker and Major. -- **Two commit-body records are pinned**, because a program parses them: a **provenance line** +- **Two commit-body records are pinned**, so that a program can parse them — P8's deferred + measurement is the intended consumer, and **no parser for either form ships today**; the + evidence for this release matched constructed strings against the grammars instead: a + **provenance line** carrying the cycle field, the derived floor and the cited set that produced it, and a **per-pass curve** carrying Findings, Blockers and Majors per pass. One of each per cycle — a change running five cycles records five. @@ -641,12 +646,15 @@ NEW: source of randomness, never derived from a name, timestamp or commit. It is collision-**resistant**, not collision-proof, and the shipped text says where that bound bites rather than implying a guarantee. -- **Findings slots take a per-cycle infix for a cycle holding a nonce**, and for such a cycle the - deletion step §5 already requires now deletes only paths carrying that cycle's own nonce. **The - bare names stay valid** and stay reserved for the legacy single-cycle case — a pre-rule or - no-nonce cycle keeps the bare form, and this release's own Gate-B cycle is one. That rule exists - because a bare slot was overwritten during this change's own development, destroying a previous - cycle's findings file. +- **Findings slots take a per-cycle infix**, and the deletion step §5 already requires deletes + only paths carrying the cycle's own infix. A cycle that holds a **nonce** uses it; a cycle with + **no** nonce keeps the **bare** names, *except* when the workspace already holds bare-slot files + from earlier cycles — then it takes a **short, deterministic discriminator** in the nonce's + position, which is not a nonce and claims none of a nonce's properties. Either way a cycle + deletes only its own paths, never a bare path belonging to somebody else and never another + cycle's. That rule exists because a bare slot was overwritten during this change's own + development, destroying a previous cycle's findings file — and this release's own Gate-B cycle + is the discriminator case, in the workspace where it happened. - **What this change does not settle** is how demotion bears on the loop-health measures — the per-pass counts, the clusters and the stop thresholds. That is the loop-rule consolidation story's, and both documents say so, so the obligation cannot fall between them. @@ -657,7 +665,7 @@ NEW: - [ ] **Assert the new text is present.** ```bash -test "$(grep -cF -- "The bare names stay valid" plugins/dev-workflow/CHANGELOG.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "no parser for either form ships today" plugins/dev-workflow/CHANGELOG.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. Verified against a simulated tree carrying Plan A's and Plan B's edits. @@ -959,7 +967,108 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 19: The spec §8 wording correction +## Task 19: The slot rule admits a deterministic discriminator — 1 of 2, `CLAUDE.md` §5 + +**File:** `CLAUDE.md` + +**Site** — pasted `grep -n`, against the tree Plan B leaves behind: + +``` +CLAUDE.md: > the nonce in every slot more than one cycle could write. The bare names are reserved for the +``` + +> **This is the contradiction Gate-A pass 6 found, and it was a real one.** Plan B's shipped rule +> sends a cycle with **no nonce** to the bare slot names. This cycle has no nonce — it is pre-rule +> and cannot mint one — so the shipped rule sends it to the bare names, **into the one workspace +> that already holds 61 bare-slot files**, where §5's delete-before-call step destroys them. Task +> 23 uses `gate-b--rle-pass-

`, which the shipped grammar admits nowhere: neither +> a bare name nor a nonce. **The plan and the prompt it ships cannot both be followed.** +> +> **Resolved one way, and this is the way:** the shipped rule gains the production. Spec §8 +> already describes it — *"its slot discriminator is short and deterministic, so it is not a +> nonce"* — so this makes the prompt state what the approved spec already assumes, rather than +> inventing a rule. The alternative, bare names plus a hand-rolled preservation step, recreates +> the incident the rule exists to prevent and leaves the spec's sentence describing nothing. +> +> **It admits a discriminator; it promises nothing about collisions.** A deterministic value is +> not drawn, so two cycles that pick the same one collide by construction. The text says so. + +- [ ] **Replace.** OLD — the complete sentence pair, so the replacement leaves no half-line: + +``` +> the nonce in every slot more than one cycle could write. The bare names are reserved for the +> legacy single-cycle case they already serve. **Distinct-nonce paths coexist by construction and +> are never in conflict** — a sibling cycle's slot is simply a different file. +``` + +NEW: + +``` +> the nonce in every slot more than one cycle could write. The bare names are reserved for the +> legacy single-cycle case they already serve — **with one exception, and it is the case that +> caused the incident**: a cycle with **no** nonce, writing into a workspace that already holds +> bare-slot files from earlier cycles, takes a **short, deterministic discriminator** in the +> nonce's position (`gate-b---pass-

`, and likewise for the Gate-A forms). +> **It is not a nonce and claims none of a nonce's properties** — it is derived rather than drawn, +> so two cycles choosing the same discriminator compute the same paths and are indistinguishable, +> exactly as two cycles drawing the same nonce would be. It binds the deletion step the same way: +> such a cycle **deletes only paths carrying its own discriminator**, never a bare path and never +> another cycle's. **Distinct-nonce paths coexist by construction and are never in conflict** — a +> sibling cycle's slot is simply a different file, and so is a distinct-discriminator path. +``` + +- [ ] **Assert the new text is present.** + +```bash +test "$(grep -cF -- "takes a **short, deterministic discriminator** in the" CLAUDE.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +``` + +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. + +- [ ] **Amend the WIP commit.** + +```bash +git add CLAUDE.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 20: The slot rule admits a deterministic discriminator — 2 of 2, the scaffolded template + +**File:** `plugins/dev-workflow/commands/workflow-init.md` + +**Site** — pasted `grep -n`, against the tree Plan B leaves behind: + +``` +plugins/dev-workflow/commands/workflow-init.md: > the nonce in every slot more than one cycle could write. The bare names are reserved for the +``` + +> The same edit in the mirror. Plan B wrote this passage into both copies in one task; **Plan C +> corrects it in two**, because the assert counts `1` in each file separately and a fix to one +> has never implied a fix to the other. Invariant 11 forbids the two prompts disagreeing, and +> item 6 of the evidence pack is where that is demonstrated. + +- [ ] **Replace.** Same OLD and NEW as Task 19, in this file. + +- [ ] **Assert the new text is present.** + +```bash +test "$(grep -cF -- "takes a **short, deterministic discriminator** in the" plugins/dev-workflow/commands/workflow-init.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +``` + +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. + +- [ ] **Amend the WIP commit.** + +```bash +git add plugins/dev-workflow/commands/workflow-init.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 21: The spec §8 wording correction **File:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` @@ -972,10 +1081,17 @@ docs/superpowers/specs/2026-08-28-review-loop-economics-design.md:467:- **A pars > **§5 sends this here rather than to a Gate-A reopening:** *"A fix that changes specified > behaviour updates the spec in the same commit."* §8 asks for a **parse** check. No parser for > either grammar exists in this repo, and none is built — a check that cannot execute is not a -> check. What the pinned forms actually support is a **grep**, because they are field grammars -> with delimiters, and a grep decides the same question for them. **The coverage requirement is -> untouched:** constructed strings, valid and invalid, features recorded per grammar. Only the -> named mechanism changes, and it changes to one that exists. +> check. **The coverage requirement is untouched:** constructed strings, valid and invalid, +> features recorded per grammar. Only the named mechanism changes, and it changes to what exists. +> +> **A grep is not the whole of it, and revision 6 said it was.** That revision named cardinality +> as the single rule a match cannot decide. It is not the only one, and claiming so was the same +> overclaim in a smaller font. Every constraint a single regular match cannot decide is named in +> the replacement and checked by a separate comparison over the extracted fields. +> +> **This task carries a second replacement, in §1.** The same claim lives there in the present +> tense — *"a program parses them"* — about a program that does not exist. One claim, both sites, +> one pass. - [ ] **Replace.** OLD: @@ -996,17 +1112,48 @@ NEW: passes, a skipped cycle, or the cardinality rule that `` matches ``. **The check matches a set of constructed strings against each grammar's own productions with `grep -E` — valid ones must match, invalid ones must not** — and records which grammar features each - exercises. **It is a grep, not a parser**: these forms are pinned as greppable field grammars - and this repo ships no parser for either, so naming a parser would name a check nobody can run. - The one production a single match cannot decide is the cardinality rule, which is a count and is - checked by counting the entries on each side. A grammar nothing was ever matched against is a - format claim, not a format. + exercises. **It is a grep plus field comparisons, not a parser**: these forms are pinned as + greppable field grammars and this repo ships no parser for either, so naming a parser would name + a check nobody can run. **A match decides the lexical productions and nothing else.** Every + constraint it cannot decide is checked by a comparison over the extracted fields: `` + having as many entries as `` enumerates; a `` occurring more than once; `` + ranges ascending and non-overlapping; a per-pass key present in one field and absent from its + partner. **That list is what reading both grammars for non-regular constraints produced, and it + is not proven complete** — a further one is checked the same way rather than folded into the + match. A grammar nothing was ever matched against is a format claim, not a format. ``` - [ ] **Assert the new text is present.** ```bash -test "$(grep -cF -- "It is a grep, not a parser" docs/superpowers/specs/2026-08-28-review-loop-economics-design.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "It is a grep plus field comparisons, not a parser" docs/superpowers/specs/2026-08-28-review-loop-economics-design.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +``` + +Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. + +- [ ] **Replace, second site — §1, the same claim in the present tense.** OLD: + +``` +spec. Two things stay pinned as *required properties* because a shipped criterion reads them and a +program parses them: the provenance line (§2.3) and the per-pass curve (§4). +``` + +NEW: + +``` +spec. Two things stay pinned as *required properties* because a shipped criterion reads them and a +program is intended to parse them — P8's deferred measurement, which does not exist yet: the +provenance line (§2.3) and the per-pass curve (§4). +``` + +> **Left alone deliberately:** §2.3's *"P8 parses it"* and §2.4's *"the deferred P8 measurement +> parses it"* already name the consumer as deferred, and §2.4's *"a parser needs no special case"* +> is a property of the grammar rather than a claim that one exists. + +- [ ] **Assert the second replacement.** + +```bash +test "$(grep -cF -- "program is intended to parse them — P8's deferred measurement, which does not exist yet" docs/superpowers/specs/2026-08-28-review-loop-economics-design.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } ``` Before this task the count is `0`, so this exits nonzero if it is run early; after, `1`. @@ -1020,14 +1167,14 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 20: The evidence pack — everything that runs before the cycle +## Task 22: The evidence pack — everything that runs before the cycle Spec §8 names seven obligations. **All seven are discharged, split by when they can run**: six -here, and the two halves that depend on the cycle's own output in Task 22. The mode is read from +here, and the two halves that depend on the cycle's own output in Task 24. The mode is read from the story header at execution time. **The cycle base is `HEAD^`** — §5's own rule, that `baseSha` is the WIP commit's parent. Exactly -one WIP commit stands (Task 21 checks it), so no variable is recorded and no file holds it. +one WIP commit stands (Task 23 checks it), so no variable is recorded and no file holds it. - [ ] **1 — Battery.** The full `AGENTS.md` § Commands chain, green, **with the version-bump checker given `HEAD^` as its base ref** — it takes one and **exits 2 with no argument**, so the @@ -1062,22 +1209,33 @@ scaffolded mirror carries this rule too and is covered by item 6, which compares - **a readable regular file** — record bytes and digest, and classify the value as numeric or as one of the pinned unusable causes. - **Existence is not the check.** Task 22 repeats the observation and compares. + **Existence is not the check.** Task 24 repeats the observation and compares. - [ ] **5 — A grammar check over both pinned grammars**, features assigned **per grammar**. Construct strings and match each against that grammar's own productions with `grep -E`: valid ones must match, invalid ones must not. - **Provenance:** a quoted path; each `unusable()` value; a cited-story-with-no-profile - entry; `none` for no story cited. **Curve:** a gapped `` such as `1,2,4`; a split-model - pass; a `?` count; a skipped cycle's skip record. **Each grammar gets at least one string that - must fail to match** — for provenance, a repeated ``. - - **The cardinality rule is the exception, and it is named rather than glossed:** `` having - as many entries as `` enumerates is a count, not a match, and no single regex decides it. - Check it by counting the entries on each side and comparing, with one passing and one failing - instance. Both routes execute; neither is a parser, which is why Task 19 corrects §8's word for - it. + **Provenance:** a quoted path; each `unusable()` value; **a valid numeric knob value**; + a cited-story-with-no-profile entry; `none` for no story cited; **a real `cycle ` field**. + **Curve:** a gapped `` such as `1,2,4`; a split-model pass; a `?` count; a skipped cycle's + skip record; **a `cycle ` field, the same nonce as the provenance instance**, so + cross-record agreement is exercised too. **Each grammar gets at least one string that must fail + to match** — for provenance, a malformed nonce. + + **The nonce and the numeric knob are in this list for a specific reason:** they are exactly the + two fields the live cycle cannot produce (Task 24), so without them the close would record as + covered two productions nothing ever exercised. What stays undemonstrable is narrower and is + named there: that a *drawn* nonce attributes a *live* cycle. A constructed one exercises the + form, not the attribution. + + **What a match cannot decide, checked by comparison instead** — each with one passing and one + failing instance: `` having as many entries as `` enumerates; a `` occurring + twice; `` ranges out of order or overlapping; a per-pass key present in one field and + absent from its partner. **That list came from reading both grammars for constraints no single + regular match decides, and it is not proven complete.** Both routes execute; neither is a + parser, which is why Task 21 corrects §8's word for it — **and revision 6's claim that + cardinality was the only such rule was itself an overclaim**, which is why this list is four + items and carries its own limit. Record which feature each string exercises. **This is where the grammars get their coverage** — the cycle's own two records cannot, which is the reason §8 asked for constructed strings. @@ -1093,7 +1251,7 @@ scaffolded mirror carries this rule too and is covered by item 6, which compares --- -## Task 21: The Gate-B cycle +## Task 23: The Gate-B cycle **Run the cycle per `CLAUDE.md` §5.** The floor, the file-first findings protocol, delete-before-call, what makes a pass valid, single-branch recovery, re-review after every fix, the @@ -1106,24 +1264,38 @@ restatement against the rules it was restating. **Three facts belong to this cyc that exactly one WIP commit stands and that it is not a merge — ```bash -git rev-parse --verify HEAD^2 >/dev/null 2>&1 && { echo "WIP IS A MERGE — stop"; exit 1; } -git log -1 --pretty=%s | grep -q '^WIP: review-loop economics' || { echo "TIP IS NOT THE WIP"; exit 1; } -git log -1 --pretty=%s HEAD^ | grep -q '^WIP:' && { echo "PARENT IS ALSO A WIP — stacked, collapse first"; exit 1; } +if ! git rev-parse --verify HEAD^ >/dev/null 2>&1; then echo "NO PARENT — no cycle base"; exit 1; fi +if git rev-parse --verify HEAD^2 >/dev/null 2>&1; then echo "WIP IS A MERGE — stop"; exit 1; fi +if ! git log -1 --pretty=%s | grep -q '^WIP: review-loop economics'; then echo "TIP IS NOT THE WIP"; exit 1; fi +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "PARENT IS ALSO A WIP — stacked, collapse first"; exit 1; fi +echo "BASE OK: $(git rev-parse HEAD^)" ``` +**Every guard is an `if`, and the block ends on a command that succeeds.** Written as +` && { …; exit 1; }`, the last guard returns the failing grep's status **1 in the desired +state** — the parent correctly not being a WIP — so the block reports failure exactly when it +should report success, and under `set -e` the first negative guard aborts before the others run. + The third asks about a **different** commit, which is why it can fail. A stacked WIP would silently leave the earlier snapshot out of the only review. -2. **The slots are `gate-b--rle-pass-

.md`**, never the bare names. This workspace - already holds **61 files in the bare `gate-b-*-pass-*` family** — observed when this plan was - written, so **re-inventory before the first deletion rather than trusting the number**; the - slot choice is right either way. §5's delete-before-call step would destroy whatever is there, - which is precisely the incident the slot rule exists to prevent, committed by the plan that - ships it. Not a nonce either: this cycle is pre-rule and cannot mint one, and **Plan B reserves - the bare names for exactly the legacy no-nonce case** — which is why the discriminator, not the - bare name, is what keeps this cycle off the old files. **§8 names this case** — *"its slot - discriminator is short and deterministic, so it is not a nonce"* — and `rle` is that - discriminator. +2. **The slots are `gate-b--rle-pass-

.md`**, and `rle` is the deterministic + discriminator **Tasks 19 and 20 admit into the shipped rule** — without them this form is + admitted by neither the bare names nor the nonce grammar, and the plan would contradict the + prompt it ships. **§8 states the same thing** — *"its slot discriminator is short and + deterministic, so it is not a nonce"*. + + The reason it is needed here: this cycle is pre-rule and **cannot mint a nonce**, so the + shipped rule would send it to the bare names — into a workspace that already holds **61 files + in the bare `gate-b-*-pass-*` family**, where §5's delete-before-call step would destroy them. + That is precisely the incident the slot rule exists to prevent, about to be committed by the + plan that ships it. **Re-inventory before the first deletion rather than trusting the count**: + 61 was observed when this plan was written, and the discriminator is right at any number. + + **`rle` is not collision-resistant and claims nothing of the sort.** It is derived from the + change's name, so a second concurrent Plan-C executor computes the same paths. Before the + first deletion, confirm no live writer owns them; **stop rather than delete a target whose + owner you cannot establish.** 3. **The cycle runs under the OLD rules**, per the activation constraint: **floor 3**, the constant, not the derived value this diff introduces. Carry the old-rules sentence from Global @@ -1141,7 +1313,7 @@ blocks are what it compares against. --- -## Task 22: The closing body, and the close +## Task 24: The closing body, and the close **Runs after the final clean pass and before the amend.** The two evidence halves that need the cycle's own output land here, then the body is composed from them. @@ -1151,7 +1323,7 @@ cycle's own output land here, then the body is composed from them. observation that would exist if the claim were false is a line whose floor the cited profile does not license.** -- [ ] **4b — The knob observation, after the cycle.** Repeat Task 20 item 4a's single observation +- [ ] **4b — The knob observation, after the cycle.** Repeat Task 22 item 4a's single observation and assert equality with the before-state: same path type, same bytes, same digest, or the same recorded absence. **The knob is never written or removed by this plan**; this is what makes that a demonstrated claim rather than an assurance. @@ -1180,7 +1352,7 @@ squash-carry rule — which enumerates provenance lines, curves and skip records **The cycle field is `cycle none (pre-rule)`.** Plan B reserves that value for a cycle that began before the rules shipped, and this one did. **The branch therefore demonstrates every field of both forms except two** — a real nonce, and the knob clause's non-absent form if the knob is absent — -**recorded as undemonstrable here with their reasons**, not as gaps and not as satisfied. Task 20 +**recorded as undemonstrable here with their reasons**, not as gaps and not as satisfied. Task 22 item 5's constructed strings are what cover the rest, which is why that obligation exists. **Any cycle that both starts under these rules and closes discharges the nonce demonstration.** @@ -1192,11 +1364,68 @@ cycle that both starts under these rules and closes discharges the nonce demonst --- +## Task 25: Complete the field report + +**File:** `docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md` + +**Runs after Task 24's closing amend**, as an **ordinary docs commit** — the first ordinary commit +this plan makes, and it is allowed because the cycle is closed. `docs/field-reports/**.md` is +explanatory prose, so Gate B is N/A. + +> **Why this task exists at all.** That file is the destination chosen for the four pre-rule +> Gate-A cycles' records, precisely so the observability those records carry survives outside the +> commit body. It currently says Plan C's cycle is **open at five passes** and promises the +> closing figures in a later revision. **Nothing scheduled that revision.** An artifact chosen for +> observability and then left knowingly incomplete is worse than no artifact, because a reader +> takes its silence for the end of the story. + +- [ ] **Extract Plan C's final curve mechanically**, the way that file's own numbers were taken: + +```bash +n=1 +while [ -f ".context/codex-reviews/gate-a-plan-planc-pass-$n.md" ]; do + f=$(grep -cE '^(BLOCKER|MAJOR|MINOR|NIT) \|' ".context/codex-reviews/gate-a-plan-planc-pass-$n.md") + b=$(grep -c '^BLOCKER' ".context/codex-reviews/gate-a-plan-planc-pass-$n.md") + m=$(grep -c '^MAJOR' ".context/codex-reviews/gate-a-plan-planc-pass-$n.md") + printf 'pass %d: %s findings, %s B, %s M\n' "$n" "$f" "$b" "$m" + n=$((n+1)) +done +``` + +- [ ] **Replace the provisional passage** — the one saying the cycle is open at five passes and + promising later figures — with the closed cycle's full three-row block, in the same format the + Plan A and Plan B blocks use. + +- [ ] **Assert the provisional wording is gone and the closed record is present.** + +```bash +test "$(grep -cF -- "Open at 5 passes when this file was written" docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md)" -eq 0 || { echo "PROVISIONAL WORDING SURVIVES"; exit 1; } +``` + +- [ ] **Commit it alone**, ordinary message, no `WIP:` prefix — the cycle is already closed and + this content was never part of the reviewed diff. + +--- + ## Self-Review -**Spec coverage.** §7 rollout — Tasks 1-18. **§8 evidence — Tasks 20 and 22**, all seven -obligations, split at the only line that could split them: whether the obligation needs the -cycle's own output. §8's own wording — Task 19. The cycle and the close — Tasks 21 and 22. +**Spec coverage.** §7 rollout — Tasks 1-18. The shipped slot rule — Tasks 19 and 20. §8's own +wording — Task 21. **§8 evidence — Tasks 22 and 24**, all seven obligations, split at the only +line that could split them: whether the obligation needs the cycle's own output. The cycle and the +close — Tasks 23 and 24. The durable record — Task 25. + +**Revision 7's method was a claim sweep, not per-finding repair**, because pass 6 showed the +disease. Three of its findings were one claim corrected in one place and left standing in another +— all mine, all inside revision 6. Six claims were enumerated, every statement site grepped, and +every occurrence fixed in one pass: `AGENTS.md`'s own recipe, *search for the claim, not the +phrase*, used as the method rather than as a warning about it. The claim-to-site map is in the +commit body. + +**Two of those claims changed what the plan ships**, and neither was a wording fix. The **slot +rule gains a production** (Tasks 19 and 20), because the plan's own slot form was admitted by +nothing the plan ships — the plan and the prompt it ships could not both be followed. And **§8's +mechanism names four constraints a grep cannot decide instead of one**, because revision 6's +correction of an overclaim was itself an overclaim. **The dependency cycle is gone.** Revision 5 put the whole evidence pack before the cycle while three of its seven items needed the cycle's output, so no task could truthfully complete. Items 3 @@ -1212,10 +1441,14 @@ left the first standing. **The destination was the error.** Pre-rule history is value, deterministic from git — no recorded variable, no file under `.context/`, and none of the failure paths a recorded one brought with it. -**Inherited obligations.** M9 at Task 20 item 4a and Task 22 item 4b. M10 in Global Constraints. +**What is still not mechanical**, said once rather than implied: the whole-block confirmation, +the parity comparison and the twelve-item pass are **readings, not commands**, and are named as +readings wherever they appear. Nothing checks a reading. That is a limit of this plan. + +**Inherited obligations.** M9 at Task 22 item 4a and Task 24 item 4b. M10 in Global Constraints. **M8 and B6 are §5's own rules** — single-branch recovery, and evidence revalidated after every -fix and before the close — so Task 21 defers rather than restating. **MINOR 12 is moot**: it asked +fix and before the close — so Task 23 defers rather than restating. **MINOR 12 is moot**: it asked for `mktemp` over a fixed `/tmp` path, and the body is now four items carried by §5's own amend. -**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's — Task 21, +**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's — Task 23, reading the real combined diff of all three plans. From 4cec17c69ef4d8a6631c9ec851dce69888f417d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:46:39 +0200 Subject: [PATCH 087/117] docs(vision): parallelism and flow control decisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review delta from Daniel's 2026-08-30 session with the sparring session, committed unedited and staged alone. New §9 (Phase 0, branch-scoped locks with warten-auf and batched meta-stories, the tree as parallelism map, a user-owned lanes budget in the pool header with two automatic throttles); waves added to §4 and as classification dimension 8; old §9 renumbered to §10. Gate B: N/A — docs/superpowers/specs/**.md only, staged alone, staged set verified. --- .../specs/2026-08-30-dark-factory-vision.md | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index f512f87..c9a1660 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -83,9 +83,16 @@ the inspiration: the adversarial verifier is a different model *family* classification's reject verdict reads. One document, read by gates, bots and triage alike. - **A project's roadmap is a view, not a document**: pool items plus status, - priority and dependencies (from classification) yield the order. The pool is - the single source; a separate roadmap file would be a second copy that - drifts. + priority, dependencies and **wave** yield the order. The pool is the single + source; a separate roadmap file would be a second copy that drifts. +- **Waves structure a new project.** Phase 0 assigns every initial pool item a + wave mark (wave 1, 2, … or named milestones) — the deliberate "these + subareas develop together first, those later" decision, usually aligned with + tree branches but not required to be. The orchestrator pulls only from the + active wave (a focus throttle beside the lanes budget: lanes = how much at + once, wave = what at all). Opening the next wave is the human's call (or + automatic when the prior wave is fully merged — settled by build step 4). + Later stories get their wave mark at classification. - **The kit's own roadmap** to this vision is §7 of this document. ## 5. The classification card @@ -104,6 +111,8 @@ in the intake skill today; 5–7 are new: 6. **Architecture verdict** — from the Bewertungs-Loop: seamless, or re-evaluation needed (→ meta-story per decision 3). 7. **Dependencies** — needs story Y first → wait mark and ordering. +8. **Wave** — which development wave the item belongs to (§4); assigned in + Phase 0 for initial items, at classification for later ones. Verdicts: **freigeben / teilen / rückfragen / warten-auf / ablehnen** (with reason, back to the human — rejection is never deletion). Split rules: multiple @@ -151,7 +160,37 @@ proceeds, the breaking part waits on the meta-story). - No autonomy expansion ahead of the measured evidence (P8) that the review economics support it. -## 9. Open questions (owned by the stories that will answer them) +## 9. Parallelism and flow control (decisions 2026-08-30, Daniel) + +- **Phase 0 exists once.** Initial brainstorming builds architecture tree v1 + plus goals/out-of-scope before production starts. It is the only + everything-waits moment; after it, intake never freezes. +- **Architecture churn blocks branches, never the factory.** An + architecture-relevant story is not rejected: classification parks it with + warten-auf behind its meta-story, and the meta-story locks exactly the tree + branches it touches. Stories on other branches keep flowing. The + Bewertungs-Loop batches architecture-relevant items into one meta-story per + wave instead of serial tree churn; the meta-story's priority is the human's + knob (pull it forward to unlock branches sooner, or defer it and let the + parked stories wait). +- **The tree is the parallelism map.** Disjoint branches run in parallel + (N worktrees × one pipeline each — possible today; the record/nonce rules + are the foundation); same branch serializes. Classification's dependency and + architecture verdicts yield the schedule. Within a story, stages fan out to + subagents; Gate B's two review branches already run in parallel. What does + not parallelize: the serial passes of one review loop, the merge queue to + main, and the human's decisions. +- **The parallelism budget is a user-owned knob.** It lives in the story + pool's header (one committed, visible place, e.g. `lanes: 3`), is never + written by an agent, and is read fresh by the orchestrator at every tick — + raising it buys throughput while the tree has disjoint branches; lowering it + throttles token spend (`lanes: 0` pauses intake of new lanes; running lanes + drain). Two automatic throttles on top: no new lane opens while any lane + stands in a mandatory stop, and no new lane opens while more than N + decisions are queued for the human — the measured bottleneck is decision + bandwidth, not compute. + +## 10. Open questions (owned by the stories that will answer them) - Sample percentage and drawing rule for the Sample-Gate (step 6). - Storage form of the pool (files in-repo vs. external board) — step 4. From b110a3af32a150a668e2b1e5183db21df2436e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 19:57:57 +0200 Subject: [PATCH 088/117] docs(vision): prior-art review of godarkfactory, adoptions and non-adoptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second review delta from Daniel's 2026-08-30 session with the sparring session, committed unedited and staged alone. New §10 reviews godarkfactory.com as prior art: adopted ideas routed to their owning build step, deliberate non-adoptions (their quality_strictness_decay anti-feature, same-model review, daemon/Docker/GitHub as hard requirements, stop-the-world milestones), and their documented scars kept here as constraints. Old §10 renumbered to §11. Gate B: N/A — docs/superpowers/specs/**.md only, staged alone, staged set verified. --- .../specs/2026-08-30-dark-factory-vision.md | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index c9a1660..a152ed8 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -190,7 +190,51 @@ proceeds, the breaking part waits on the meta-story). decisions are queued for the human — the measured bottleneck is decision bandwidth, not compute. -## 10. Open questions (owned by the stories that will answer them) +## 10. Prior art: godarkfactory.com (reviewed 2026-08-30) + +A shipped, self-hosted dark factory (Go binary `godark`, Elastic License 2.0, +beta): GitHub issues grouped into milestone "phases", topological dependency +waves, a three-agent loop (implement → quality+functional review → auto-merge) +in Docker sandboxes, all agents Claude. Validates most of this vision's shape +— milestones↔waves, scenario specs↔pre-build checks, define-architecture / +define-conventions↔Phase 0, watch↔clock loops, needs-human-review↔escalation. + +**Adopted into the build path** (owning step in brackets): +- Local SQLite analytics — cost/duration/retries per step, written + non-fatally post-run; plus a trace ID per story propagated through every + stage artifact, and mechanical spec-delta capture. [step 2 — this is P8's + concrete shape] +- Orchestrator dry-run (print the tick's execution plan before spending + tokens) and a forced-fresh-session knob (`max_resume_retries` analog) — + turns gap 7 into a mechanism. [step 3] +- Mechanical vet preflight on pool items and artifacts before any model pass; + numeric split thresholds for the teilen verdict (their working values: max + 5 acceptance criteria, 7 test cases); a machine-readable architecture + projection (may_depend_on / must_not_depend_on, cycle-checked) *generated + from* AGENTS.md — never a second source. Their GitHub-issues-as-pool is a + working data point for the pool-storage question. [step 4] +- A judge watchdog (idle / tool-thrash / no-progress supervisor that is not + the hanging model) and push notifications on run events — the human is the + bottleneck; push, don't make them poll. [step 5] +- Graduated auto-merge with mechanical risk thresholds (max lines/files) as + an independent floor under the semantic profile; punchlist generation as + the artifact a sampled audit works from. [step 6] + +**Deliberately not adopted:** `quality_strictness_decay` (their default — +review gates weaken as retries mount; the inverse of the clean-final-pass +rule); same-model review (their implementer and reviewers share one model +family — correlated blind spots; our adversarial gate stays cross-model); +daemon/Docker/GitHub as hard requirements (non-goal 1 stands); stop-the-world +sequential milestones (branch-scoped locks and the lanes budget replace it). + +**Their documented scars, kept as constraints here:** manual state mutation +under an automated resolver breaks it — every hand-edit of pool status must +be a defined operation; planning artifacts need a defined home so they never +contaminate implementation branches; run artifacts need a retention stance +from day one (their P0 gap: unbounded disk growth); absent real-time cost +visibility is what makes a token furnace invisible (their P0 gap, our P8). + +## 11. Open questions (owned by the stories that will answer them) - Sample percentage and drawing rule for the Sample-Gate (step 6). - Storage form of the pool (files in-repo vs. external board) — step 4. From bb358c27f50bda55494e9cc6a147331824f6e92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:02:41 +0200 Subject: [PATCH 089/117] =?UTF-8?q?docs(plan):=20Plan=20C1=20=E2=80=94=20t?= =?UTF-8?q?he=20user-facing=20floor=20description?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan C's Gate-A cycle closed as NOT CONVERGED at pass 7: seven passes, seven revisions, Blocker/Major never outside 15-21, and the last pass the worst of the set (29 findings, 21 B+M). Record in .context/codex-reviews/gate-a-plan-planc-CLOSURE.md. No clean pass is claimed anywhere. Daniel's decision: split by statement site. C1 is the first of three. C1 OWNS EXACTLY ONE CLAIM: "three passes" is not the floor — the floor derives from the cited story's profile, and the codex-gate.floor knob moves the hook's reminder threshold rather than that floor. Seven sentences state it wrongly, across README.md and docs/getting-started.md. They are the complete set in those two files, found by grepping the claim rather than a phrase. WHY THE SPLIT IS THE RIGHT SHAPE, on the evidence: Plan A and Plan B each ship their rules to one place and closed clean at 12 and 7 passes, Plan B with one Blocker in its whole cycle. Plan C carried a rollout across seven files plus a spec plus a CHANGELOG plus a live Gate-B cycle, and revision 7's claim sweep — the right method — missed two sites of the very claim it was sweeping and shipped a fix that contradicted itself. That is a width result, not a care result. WHAT C1 DOES NOT OWN IS NAMED, NOT ABSORBED. The satisfied-message sentence goes to C2; the parser claim, the slot rule and spec §8 go to C3, whose site list must include the two shipped prompt copies the sweep missed. Two rows are flagged UNASSIGNED rather than quietly taken: docs/coding-workflow.md's axes and model-recording sentences, and the skipped-cycle duty claim that lives in five files. Absorbing them would make C1 a second Plan C. Every assert is two commands — new text present exactly once, old text gone. Plan C spent a Major on the single-sided version, where a sentinel counted 1 in a file still carrying what it replaced. All seven negative anchors verified present exactly once in the live files, so each check can actually fail. Task 8 re-runs the discovery grep against the finished files, so completeness is a check rather than an assertion — and states its own limit: a fixed-floor claim phrased without a number escapes it. Neither file is touched by Plan A or Plan B, so every anchor was verified against the current tree. No simulation, and none claimed. 23 shell blocks, all parse under sh -n. Gate B: N/A — docs/superpowers/plans/**.md only, staged set verified. --- ...review-loop-economics-plan-c1-user-docs.md | 493 ++++++++++++++++++ 1 file changed, 493 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md new file mode 100644 index 0000000..717d58e --- /dev/null +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md @@ -0,0 +1,493 @@ +# Plan C1 — the user-facing floor description + +> **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:** Correct every sentence in `README.md` and `docs/getting-started.md` that Plan A makes +false about **where the pass floor comes from**. Seven sentences, two files, one claim. + +**Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). +C1 implements the part of §7 that lands in the user-facing docs. + +**Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` + +> **Read the profile from that header at execution time.** This plan states no risk value, no +> security value, no validation mode and no pass count derived from any of them. The evidence +> the mode requires is produced by Plan C3, which runs the one Gate-B cycle. + +--- + +## Why this plan is small + +Plan C tried to carry this rollout, the packaging, the evidence and the Gate-B cycle in one +artifact. Its Gate-A cycle **ran seven passes and never converged** — Blocker/Major stayed +between 15 and 21, and the last pass was the worst. The record is +`.context/codex-reviews/gate-a-plan-planc-CLOSURE.md`. Daniel split it by statement site. + +**C1 is the first of three.** C2 takes the packaging and the hook-message description; C3 takes +the evidence, the Gate-B cycle and the close. + +### The one claim this plan owns + +**"Three passes" is not the floor; the floor is derived from the cited story's profile, and the +`codex-gate.floor` knob moves the hook's reminder threshold rather than that floor.** + +Seven sentences state it wrongly. They are the complete set in these two files, found by +grepping the **claim** — every place a number of passes, a floor, or the knob is described — +not by grepping a phrase. + +### What this plan does not own, so that nothing falls between the three + +| Not here | Where | Why not here | +|---|---|---| +| `docs/getting-started.md:58`, the satisfied-message example | **C2** | It describes what the hook's message *reports* — three numbers and their meanings. A different claim in the same file. | +| `docs/coding-workflow.md` — the axes sentence and the model-recording sentence | **unassigned** | Neither a C1 file nor packaging nor the close. **Flagged, not absorbed.** | +| The skipped-cycle duty sentence in five files, `getting-started.md` among them | **unassigned** | One claim across `process-pr-review.md`, `CLAUDE.md`, the scaffolded template and both explanatory summaries. Splitting it by file would recreate the defect it exists to fix. **Flagged, not absorbed.** | +| The parser claim, the slot rule, spec §8 | **C3** | Named in C3's site list, including the two shipped prompt copies Plan C's sweep missed. | + +**The two unassigned rows are a real gap in the split and are named rather than quietly taken.** +Absorbing them here would make C1 a second Plan C. + +--- + +## Global Constraints + +- **These edits join the open cycle.** Every task amends the WIP commit with + `-m "WIP: review-loop economics"`. `plugins/dev-workflow/hooks/codex-gate.sh:763` recognizes a + WIP commit by grepping the Bash command string for `-m ... wip`; an amend without `-m` is not + recognized and the hook resets, discarding the cycle's passes. **Never + `git commit --amend --no-edit`.** +- **`git add` names paths explicitly, never `-u`.** +- **No ordinary commit until C3 closes the cycle.** +- **Line numbers are provenance, never instructions.** +- **Preflight and assert are different commands.** The preflight decides whether to apply and + `0` is a valid answer meaning *apply*; the assert tests the result and anything but `1` fails. + Each task carries both, instantiated with its own file and string — no template. +- **Neither file is touched by Plan A or Plan B**, so every anchor below was verified against + the **current** tree, not a simulated one. + +--- + +## Old-conditions accounting + +For each sentence: what it asserted, and what replaces it. + +| # | Sentence | What it asserted | Disposition | +|---|---|---|---| +| 1 | `README.md:130` knob row | the knob moves the 3-passes-per-gate floor | **false as of Plan A** — it moves the hook's reminder threshold and never bound an agent. Replaced, with the distinction stated | +| 2 | `getting-started.md:34` Gate-A loop | three passes minimum | **replaced** by the derived floor. "final pass clean" and the zero-finding early exit are **kept** | +| 3 | `getting-started.md:40` plan loop | the same 3-pass loop | **replaced**; the rest of the sentence kept | +| 4 | `getting-started.md:44` below-floor message | the hook reports that the Gate-A floor wasn't met | **false** — it reports its own threshold, which at a derived floor of 1 announces a shortfall the cycle does not owe. Replaced | +| 5 | `getting-started.md:53` Gate-B loop | three passes, final clean | **replaced** by the derived floor; "final clean" **kept** | +| 6 | `getting-started.md:84` axes sentence | the axes never subtract **and** the floor is unchanged at every level | first clause **kept** — true, and the point; second **deliberately dropped**, since this change is what makes the floor vary | +| 7 | `getting-started.md:86` second knob mention | the knob moves the 3-pass floor | **false**, same as row 1. Both sites corrected, because fixing one leaves the other teaching it | + +**Rows 1 and 7 are one claim in two places, and rows 2, 3 and 5 are one claim in three.** That is +why they are in one plan: a fix to any one of them alone leaves the others contradicting it. + +--- + +## Task 1: The knob row in `README.md` + +**File:** `README.md` + +**Site** — pasted `grep -n`: + +``` +README.md:130:| `codex-gate.floor` | a positive integer; moves the 3-passes-per-gate floor. | +``` + +> The knob never bound an agent. `$floor` appears in the hook's control flow, but that flow only +> selects which advisory message fires, and the hook exits 0 on every branch. This line was the +> clearest statement of the wrong model anywhere in the docs. + +- [ ] **Preflight.** + +```sh +case "$(grep -cF -- "moves the hook's reminder threshold. It does not change the floor" README.md)" in + 0) : ;; + 1) echo "ALREADY APPLIED — skip"; exit 0 ;; + *) echo "AMBIGUOUS — stop"; exit 1 ;; +esac +``` + +- [ ] **Replace.** OLD: + +``` +| `codex-gate.floor` | a positive integer; moves the 3-passes-per-gate floor. | +``` + +NEW: + +``` +| `codex-gate.floor` | a positive integer; moves the hook's reminder threshold. It does not change the floor §5 obliges, which is derived from the cited story's profile. | +``` + +- [ ] **Assert.** + +```bash +test "$(grep -cF -- "moves the hook's reminder threshold. It does not change the floor" README.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "moves the 3-passes-per-gate floor" README.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +``` + +- [ ] **Amend.** + +```bash +git add README.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 2: The Gate-A loop description + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:34:three passes minimum, final pass clean — the one early exit is a pass that comes +``` + +- [ ] **Preflight.** + +```sh +case "$(grep -cF -- "the floor its profile derives, final pass clean" docs/getting-started.md)" in + 0) : ;; + 1) echo "ALREADY APPLIED — skip"; exit 0 ;; + *) echo "AMBIGUOUS — stop"; exit 1 ;; +esac +``` + +- [ ] **Replace.** OLD: + +``` +three passes minimum, final pass clean — the one early exit is a pass that comes +back with zero findings. +``` + +NEW: + +``` +the floor its profile derives, final pass clean — the one early exit is a pass that +comes back with zero findings. +``` + +- [ ] **Assert.** + +```bash +test "$(grep -cF -- "the floor its profile derives, final pass clean" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "three passes minimum" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +``` + +- [ ] **Amend.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 3: The plan-loop sentence + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:40:task-by-task plan (each task starts with a failing test); the same 3-pass loop runs +``` + +- [ ] **Preflight.** + +```sh +case "$(grep -cF -- "the same loop runs at the derived floor" docs/getting-started.md)" in + 0) : ;; + 1) echo "ALREADY APPLIED — skip"; exit 0 ;; + *) echo "AMBIGUOUS — stop"; exit 1 ;; +esac +``` + +- [ ] **Replace.** OLD: + +``` +task-by-task plan (each task starts with a failing test); the same 3-pass loop runs +``` + +NEW: + +``` +task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor +``` + +- [ ] **Assert.** + +```bash +test "$(grep -cF -- "the same loop runs at the derived floor" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "the same 3-pass loop runs" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +``` + +- [ ] **Amend.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 4: The below-floor hook message + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:44:progress claims backed by test runs. If the Gate-A floor wasn't met, the hook says +``` + +> The hook reports against its **own threshold**, not against what the cycle owes. At a derived +> floor of 1 it announces a shortfall the cycle does not have — the named residual of this +> change, and this sentence is where a reader would otherwise learn the opposite. + +- [ ] **Preflight.** + +```sh +case "$(grep -cF -- "If the hook's own threshold wasn't met, it says" docs/getting-started.md)" in + 0) : ;; + 1) echo "ALREADY APPLIED — skip"; exit 0 ;; + *) echo "AMBIGUOUS — stop"; exit 1 ;; +esac +``` + +- [ ] **Replace.** OLD: + +``` +progress claims backed by test runs. If the Gate-A floor wasn't met, the hook says +``` + +NEW: + +``` +progress claims backed by test runs. If the hook's own threshold wasn't met, it says +``` + +- [ ] **Assert.** + +```bash +test "$(grep -cF -- "If the hook's own threshold wasn't met, it says" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "If the Gate-A floor wasn't met, the hook says" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +``` + +- [ ] **Amend.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 5: The Gate-B loop description + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:53:`mcp__codex__review` the same way: three passes, final clean. Verification is by +``` + +- [ ] **Preflight.** + +```sh +case "$(grep -cF -- "the derived floor, final clean. Verification is by" docs/getting-started.md)" in + 0) : ;; + 1) echo "ALREADY APPLIED — skip"; exit 0 ;; + *) echo "AMBIGUOUS — stop"; exit 1 ;; +esac +``` + +- [ ] **Replace.** OLD: + +``` +`mcp__codex__review` the same way: three passes, final clean. Verification is by +``` + +NEW: + +``` +`mcp__codex__review` the same way: the derived floor, final clean. Verification is by +``` + +- [ ] **Assert.** + +```bash +test "$(grep -cF -- "the derived floor, final clean. Verification is by" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "the same way: three passes, final clean" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +``` + +- [ ] **Amend.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 6: The axes sentence + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:84:is still owed and Gate A's floor is unchanged at every level. The caution bias is +``` + +> **Two claims were tangled in one sentence.** That the axes never subtract is true and stays. +> That the floor is unchanged at every level is exactly what this change makes false. Separated +> rather than reworded, so the surviving clause is visibly the one that survived. + +- [ ] **Preflight.** + +```sh +case "$(grep -cF -- "Gate A's floor derives from the profile exactly as Gate B's does" docs/getting-started.md)" in + 0) : ;; + 1) echo "ALREADY APPLIED — skip"; exit 0 ;; + *) echo "AMBIGUOUS — stop"; exit 1 ;; +esac +``` + +- [ ] **Replace.** OLD: + +``` +is still owed and Gate A's floor is unchanged at every level. The caution bias is +``` + +NEW: + +``` +is still owed; Gate A's floor derives from the profile exactly as Gate B's does, and what the axes never subtract is the baseline questions. The caution bias is +``` + +- [ ] **Assert.** + +```bash +test "$(grep -cF -- "Gate A's floor derives from the profile exactly as Gate B's does" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "Gate A's floor is unchanged at every level" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +``` + +- [ ] **Amend.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 7: The second knob mention + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:86:positive integer) moves the 3-pass floor, and `touch .context/codex-gate.off` +``` + +> The same correction as Task 1, in the second place the docs describe the knob. **Both sites say +> the same wrong thing**, and this repo's own record is that a fix to one has never implied a fix +> to the other. + +- [ ] **Preflight.** + +```sh +case "$(grep -cF -- "moves the hook's reminder threshold, and" docs/getting-started.md)" in + 0) : ;; + 1) echo "ALREADY APPLIED — skip"; exit 0 ;; + *) echo "AMBIGUOUS — stop"; exit 1 ;; +esac +``` + +- [ ] **Replace.** OLD: + +``` +positive integer) moves the 3-pass floor, and `touch .context/codex-gate.off` +``` + +NEW: + +``` +positive integer) moves the hook's reminder threshold, and `touch .context/codex-gate.off` +``` + +- [ ] **Assert.** + +```bash +test "$(grep -cF -- "moves the hook's reminder threshold, and" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } +test "$(grep -cF -- "moves the 3-pass floor" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +``` + +- [ ] **Amend.** + +```bash +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +--- + +## Task 8: Confirm the claim has no eighth site + +**Runs last.** The seven tasks above were found by grepping the claim. This re-runs that grep +against the finished files, so the plan's completeness is a check rather than an assertion. + +- [ ] **No stale floor language survives in either file.** + +```bash +if grep -nEi '3-pass|three passes|3 passes|3-passes-per-gate' README.md docs/getting-started.md; then + echo "A FLOOR CLAIM SURVIVES — read the hits printed above"; exit 1 +fi +``` + +> **What this catches and what it does not.** It catches the wrong claim in the spellings this +> repo has actually used. It does **not** catch a sentence asserting a fixed floor without a +> number — *"the loop always runs the same number of times"* would pass. That residue is a +> reading, and it is named here rather than covered by implication. + +- [ ] **The knob is described identically in both files.** Both say it moves the hook's + reminder threshold; neither says it moves the floor. + +```bash +test "$(grep -cF -- "moves the hook's reminder threshold" README.md)" -eq 1 || { echo "README KNOB LINE WRONG"; exit 1; } +test "$(grep -cF -- "moves the hook's reminder threshold" docs/getting-started.md)" -eq 1 || { echo "GETTING-STARTED KNOB LINE WRONG"; exit 1; } +``` + +- [ ] **Amend** if either check produced a fix; otherwise nothing to stage. + +--- + +## Self-Review + +**Scope.** Seven sentences, two files, one claim. Every anchor verified against the current +tree — neither file is touched by Plan A or Plan B, so no simulation was needed and none is +claimed. + +**Every assert is two commands, not one:** the new text present exactly once, and the old text +gone. Plan C's Gate-A cycle spent a Major on the single-sided version of this, where a sentinel +counted `1` in a file that still carried the text it replaced. + +**Task 8 is the completeness check, and it is deliberately weaker than the claim it checks.** +It greps the spellings this repo has used, and says so. A fixed-floor assertion phrased without +a number escapes it. That limit is stated rather than left for a reviewer to discover. + +**What this plan does not do.** It ships no rule, touches no prompt, produces no evidence and +runs no Gate-B cycle. The evidence the story's mode requires is C3's, and one combined Gate-B +cycle covers A, B, C1, C2 and C3 together. + +**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's. From 6dad4547e29780999ed91a707be373d04afe820b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:02:53 +0200 Subject: [PATCH 090/117] docs(vision): intake loop, Freigabe, and naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third review delta from Daniel's 2026-08-30 session with the sparring session, committed unedited and staged alone. §1's flow gains the intake zone (consequence-free pool filling → proactive debounced Intake-Loop attaching card and architecture verdict → human Freigabe as the only production trigger → Takt waking the orchestrator); new decision 6 in §2 carries the two seam rules (cards-only writer with defined status operations; the Phase-0 architecture verdict waits for tree v1); "Projekt-Wahrheit" is renamed to "Architektur" throughout — zero occurrences of the old name remain. Gate B: N/A — docs/superpowers/specs/**.md only, staged alone, staged set verified. --- .../specs/2026-08-30-dark-factory-vision.md | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index a152ed8..fe02e30 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -18,10 +18,15 @@ merged, reviewed software with the human concentrated where human judgement measurably matters — and, at full maturity, sampled rather than omnipresent: ``` -Story-Pool (status: draft → freigegeben) - → Takt-Loop (polls the pool) [missing] - → Klassifizierung (classification card, may reject/split/ask) [missing] - → reads Projekt-Wahrheit: AGENTS.md [exists, needs one extension] +Story-Pool — filling is always consequence-free [missing] + → Intake-Loop (PROACTIVE: a new story appearing triggers + classification + architecture verdict, debounced/batched; + attaches the card, triggers NO production) [missing] + ↳ reads the Architektur (AGENTS.md) [exists, needs one extension] + → Freigabe (human sets "freigegeben" — the ONLY + production trigger) [human] + → Takt-Loop (polls freigegebene stories of the active + wave → wakes the orchestrator → a lane opens) [missing] → Spec-Loop (intake + design + Gate A) [exists] → Plan-Loop (writing-plans + Gate A) [exists] → Bau-Loop (executing-plans, goal-based, TDD) [exists] @@ -62,6 +67,17 @@ the inspiration: the adversarial verifier is a different model *family* 5. **End state is sampled audit, not per-merge approval.** The adversarial gate checks every merge; the human audits a sample. No merge skips both gates. (Until step 6 of the build path matures, merge remains human.) +6. **Filling the pool is always consequence-free; classification is a + proactive loop of its own.** A new story's appearance (debounced into + mini-batches) triggers exactly one thing: the Intake-Loop attaches the + classification card and the architecture verdict. Production starts only + through the human's Freigabe plus the clock. Two rules guard the seam: the + Intake-Loop only attaches cards, status changes only through defined + operations (human: freigeben; loop: klassifiziert) — never free-form edits + by both writers on one field; and in Phase 0 the architecture verdict + waits until tree v1 exists. The intake zone (pool, Intake-Loop, + Architektur) is the factory's first stage-4 proactive loop — at the front + of the pipeline, not the end. ## 3. Maturity ladder From 476236b3fe39d61bab97f23531f01f36b9334906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:16:23 +0200 Subject: [PATCH 091/117] =?UTF-8?q?docs(plan):=20Plan=20C1=20revision=202?= =?UTF-8?q?=20=E2=80=94=20eight=20sites,=20whole-line=20asserts,=20both-te?= =?UTF-8?q?xt=20preflights?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 1: 14 findings, 0 BLOCKER, 8 MAJOR. Opening 8 B+M against a predicted <=6 and a route-back threshold of 12 — under the threshold, so the loop continued. Plan C opened at 16 B+M; Plan B at 9. SIX MAJORS FIXED. - The floor's source is a SET, not a story. Plan A derives 1 only if every cited story is level 0, 3 if no story is cited or any is unprofiled, and STOPS on an unresolvable profile. "The cited story's profile" was false on three of those four states. Every occurrence now uses Plan A's own words: the profile and the cited set. - AN EIGHTH SITE. docs/getting-started.md:35 prints `Gate A below floor (1/3)` and calls it "the counter" without saying whose. It would have sat one line from Task 2's derived-floor sentence, contradicting it on the same screen. Now Task 3 — and not C2's: C2 owns the satisfied message at line 58; this is a below-floor message, the same claim as Task 5. - Preflights read only the new sentinel, so an interruption between Replace and Amend left the edit stranded in the worktree while the task reported itself done. Each is now a state machine over both texts, and the already-replaced branch checks whether the edit reached the WIP before skipping. - No amend verified HEAD is the WIP. Every amend checks the exact subject first. - Explicit paths keep other FILES out and do nothing about other content in the same file. Every amend reads the scoped diff before staging, and the plan says why. - Asserts counted substrings: Task 7 could have dropped half its replacement and passed. Six of eight replacements are whole lines and use grep -cxF, exact whole-line equality. Tasks 2 and 3 land mid-line, cannot, and each says so. TWO MAJORS ROUTED, NOT ABSORBED: coding-workflow.md's two sentences and the five-file skipped-cycle claim have no owner in the split. Both findings are right, and both are about the CLOSE rather than about C1 — the story requires every falsified sentence corrected in the same change, so C3 cannot close while they are unowned. The scope table now says that. Taking them into C1 would rebuild Plan C. MINORS. The accounting row for the axes sentence was written from a summary rather than the live text and attributed a baseline-questions clause the original never made — and the draft replacement had ADDED that clause. Read from the live sentence now: three claims, two kept verbatim, one replaced, nothing added. Task 9 no longer claims the knob is described "identically" in both files; it is not, and should not be. FOUND WHILE FIXING, not from the pass: the generic knob regex written for Task 9 errored on this machine's grep ("exceeds complexity limits"). A command that cannot run reports nothing and reads as a pass. Replaced with fixed-string checks, reason recorded in the plan, and both Task 9 greps then executed live against the current files. 26 shell blocks, all parse under sh -n. Six whole-line anchors verified to appear exactly once in the live files; all eight new texts verified absent. Gate B: N/A — docs/superpowers/plans/**.md only, staged set verified. --- ...review-loop-economics-plan-c1-user-docs.md | 523 +++++++++++++----- 1 file changed, 376 insertions(+), 147 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md index 717d58e..0e2f31f 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md @@ -5,49 +5,59 @@ > checkbox (`- [ ]`) syntax for tracking. **Goal:** Correct every sentence in `README.md` and `docs/getting-started.md` that Plan A makes -false about **where the pass floor comes from**. Seven sentences, two files, one claim. +false about **where the pass floor comes from**. Eight sentences, two files, one claim. **Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). -C1 implements the part of §7 that lands in the user-facing docs. +C1 implements the part of §7 that lands in these two user-facing docs. **Story:** `docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md` > **Read the profile from that header at execution time.** This plan states no risk value, no -> security value, no validation mode and no pass count derived from any of them. The evidence -> the mode requires is produced by Plan C3, which runs the one Gate-B cycle. +> security value, no validation mode and no pass count derived from any of them. The evidence the +> mode requires is produced by Plan C3, which runs the one Gate-B cycle. --- ## Why this plan is small Plan C tried to carry this rollout, the packaging, the evidence and the Gate-B cycle in one -artifact. Its Gate-A cycle **ran seven passes and never converged** — Blocker/Major stayed -between 15 and 21, and the last pass was the worst. The record is +artifact. Its Gate-A cycle **ran seven passes and never converged** — Blocker/Major stayed between +15 and 21, and the last pass was the worst. The record is `.context/codex-reviews/gate-a-plan-planc-CLOSURE.md`. Daniel split it by statement site. -**C1 is the first of three.** C2 takes the packaging and the hook-message description; C3 takes -the evidence, the Gate-B cycle and the close. +**C1 is the first of three.** C2 takes the packaging and the satisfied-message description; C3 +takes the evidence, the Gate-B cycle and the close. ### The one claim this plan owns -**"Three passes" is not the floor; the floor is derived from the cited story's profile, and the -`codex-gate.floor` knob moves the hook's reminder threshold rather than that floor.** +**"Three passes" is not the floor. §5 derives the floor from the profile and the cited set, and +the `codex-gate.floor` knob moves the hook's reminder threshold rather than that floor.** -Seven sentences state it wrongly. They are the complete set in these two files, found by -grepping the **claim** — every place a number of passes, a floor, or the knob is described — -not by grepping a phrase. +**The source is a set, not a story**, and the replacements say so in Plan A's own words. Plan A +derives one value from the current governing cited-story set: **1 only if every cited story is +level 0**; **3 if no story is cited or any cited story is unprofiled**; and a present-but- +unresolvable profile **stops** rather than defaulting. A phrase like *"the cited story's profile"* +is false on three of those four states, which is why it appears nowhere below. -### What this plan does not own, so that nothing falls between the three +Eight sentences state the claim wrongly. They are the complete set in these two files, found by +grepping the **claim** — every place a number of passes, a floor, a threshold or the knob is +described — not by grepping a phrase. Task 9 re-runs that grep against the finished files. + +### What this plan does not own, so that nothing falls between the pieces | Not here | Where | Why not here | |---|---|---| -| `docs/getting-started.md:58`, the satisfied-message example | **C2** | It describes what the hook's message *reports* — three numbers and their meanings. A different claim in the same file. | -| `docs/coding-workflow.md` — the axes sentence and the model-recording sentence | **unassigned** | Neither a C1 file nor packaging nor the close. **Flagged, not absorbed.** | -| The skipped-cycle duty sentence in five files, `getting-started.md` among them | **unassigned** | One claim across `process-pr-review.md`, `CLAUDE.md`, the scaffolded template and both explanatory summaries. Splitting it by file would recreate the defect it exists to fix. **Flagged, not absorbed.** | -| The parser claim, the slot rule, spec §8 | **C3** | Named in C3's site list, including the two shipped prompt copies Plan C's sweep missed. | - -**The two unassigned rows are a real gap in the split and are named rather than quietly taken.** -Absorbing them here would make C1 a second Plan C. +| `docs/getting-started.md:58`, the satisfied-message example | **C2** | It describes what the hook's message *reports* — three numbers, three meanings. A different claim in the same file. The below-floor examples at lines 35 and 44 are **not** that claim and are Tasks 3 and 5 here. | +| CHANGELOG entry, manifest bump, the item-1 n/a sentence | **C2** | Packaging. | +| The parser claim, the findings-slot rule, spec §8, the evidence pack, the Gate-B cycle, the close, the field report | **C3** | C3's site list must name the two shipped prompt copies carrying *"because the deferred metrics work parses it"* — the two sites Plan C's own claim sweep missed. | +| `docs/coding-workflow.md` — the axes sentence and the model-recording sentence | **UNASSIGNED** | Neither a C1 file, nor packaging, nor the close. | +| The skipped-cycle duty sentence in five files | **UNASSIGNED** | One claim across `process-pr-review.md`, `CLAUDE.md`, the scaffolded template and both explanatory summaries. | + +**The two unassigned rows are a real gap in the split, and they block the combined close rather +than this plan.** The story requires every falsified shipped sentence corrected in the same +change, so C3 cannot close while they have no owner. They are named here, routed to Daniel, and +**not absorbed** — taking them would make C1 a second Plan C, which is the thing the split exists +to prevent. --- @@ -55,36 +65,51 @@ Absorbing them here would make C1 a second Plan C. - **These edits join the open cycle.** Every task amends the WIP commit with `-m "WIP: review-loop economics"`. `plugins/dev-workflow/hooks/codex-gate.sh:763` recognizes a - WIP commit by grepping the Bash command string for `-m ... wip`; an amend without `-m` is not - recognized and the hook resets, discarding the cycle's passes. **Never - `git commit --amend --no-edit`.** -- **`git add` names paths explicitly, never `-u`.** + WIP commit by grepping the Bash command string for `-m ... wip`. **An amend the hook does not + recognize as WIP is treated as a Gate-B cycle boundary and the cycle's Gate-B state is reset — + regardless of whether the commit itself succeeded**, since that branch cannot observe exit + status. Gate-A state is reset by skill events, not here. **Never `git commit --amend --no-edit`.** +- **Every task checks that HEAD is the WIP before amending.** Started out of order, resumed in + the wrong checkout, or run after the cycle closed, the first amend would rewrite an unrelated + commit. +- **`git add` names paths explicitly, never `-u`** — and explicit paths are **not** a scope + guard for content *inside* the named file. Each amend step reads the scoped diff first. - **No ordinary commit until C3 closes the cycle.** - **Line numbers are provenance, never instructions.** -- **Preflight and assert are different commands.** The preflight decides whether to apply and - `0` is a valid answer meaning *apply*; the assert tests the result and anything but `1` fails. - Each task carries both, instantiated with its own file and string — no template. -- **Neither file is touched by Plan A or Plan B**, so every anchor below was verified against - the **current** tree, not a simulated one. +- **Preflight and assert are different commands and every task carries both, instantiated.** + The preflight is a state machine over the old and new texts where **`0` new means apply**; the + assert runs after the edit, where the new text must appear **exactly once** and the old text + **zero** times. Reversing them would abort every task that has not run yet. +- **Six of the eight replacements are whole lines and are asserted with `grep -cxF`** — exact + whole-line equality, so a replacement that drops a clause fails. Tasks 2 and 3 land mid-line and + are asserted by substring; each says so. +- **Record the WIP commit's SHA before Task 1.** Nothing here is destructive, but a half-executed + C1 is undone by resetting the WIP to that SHA and re-running, and without the value written down + an executor is left reconstructing it from the reflog. +- **Neither file is touched by Plan A or Plan B**, so every anchor below was verified against the + **current** tree. No simulation, and none claimed. --- ## Old-conditions accounting -For each sentence: what it asserted, and what replaces it. +For each sentence: what it asserted, and what replaces it. Read from the **live** sentence, not +from its summary. | # | Sentence | What it asserted | Disposition | |---|---|---|---| -| 1 | `README.md:130` knob row | the knob moves the 3-passes-per-gate floor | **false as of Plan A** — it moves the hook's reminder threshold and never bound an agent. Replaced, with the distinction stated | +| 1 | `README.md:130` knob row | the knob moves the 3-passes-per-gate floor | **false as of Plan A** — it moves the hook's reminder threshold and never bound an agent. Replaced, with the source named as the profile and the cited set | | 2 | `getting-started.md:34` Gate-A loop | three passes minimum | **replaced** by the derived floor. "final pass clean" and the zero-finding early exit are **kept** | -| 3 | `getting-started.md:40` plan loop | the same 3-pass loop | **replaced**; the rest of the sentence kept | -| 4 | `getting-started.md:44` below-floor message | the hook reports that the Gate-A floor wasn't met | **false** — it reports its own threshold, which at a derived floor of 1 announces a shortfall the cycle does not owe. Replaced | -| 5 | `getting-started.md:53` Gate-B loop | three passes, final clean | **replaced** by the derived floor; "final clean" **kept** | -| 6 | `getting-started.md:84` axes sentence | the axes never subtract **and** the floor is unchanged at every level | first clause **kept** — true, and the point; second **deliberately dropped**, since this change is what makes the floor vary | -| 7 | `getting-started.md:86` second knob mention | the knob moves the 3-pass floor | **false**, same as row 1. Both sites corrected, because fixing one leaves the other teaching it | +| 3 | `getting-started.md:35` counter example | `1/3` is "the counter" | **kept as an example, relabelled** — the literal is the hook's own ratio, so the sentence now says which number is whose. "not an error" is **kept** | +| 4 | `getting-started.md:40` plan loop | the same 3-pass loop | **replaced**; the rest of the sentence kept | +| 5 | `getting-started.md:44` below-floor message | the hook reports that the Gate-A floor wasn't met | **false** — it reports its own threshold, which at a derived floor of 1 announces a shortfall the cycle does not owe. Replaced | +| 6 | `getting-started.md:53` Gate-B loop | three passes, final clean | **replaced** by the derived floor; "final clean" **kept** | +| 7 | `getting-started.md:84` axes sentence | the profile supplies eligibility not the skip · the battery is still owed · Gate A's floor is unchanged at every level | first two **kept verbatim**; the third **deliberately dropped and replaced**, since this change is what makes the floor vary. **Nothing added** | +| 8 | `getting-started.md:86` second knob mention | the knob moves the 3-pass floor | **false**, same as row 1. Both sites corrected, because fixing one leaves the other teaching it | -**Rows 1 and 7 are one claim in two places, and rows 2, 3 and 5 are one claim in three.** That is -why they are in one plan: a fix to any one of them alone leaves the others contradicting it. +**Rows 1 and 8 are one claim in two places; rows 2, 4 and 6 are one claim in three; rows 3 and 5 +are one claim in two.** That is why they are in one plan: a fix to any one alone leaves the others +contradicting it. --- @@ -101,15 +126,27 @@ README.md:130:| `codex-gate.floor` | a positive integer; moves the 3-passes-per- > The knob never bound an agent. `$floor` appears in the hook's control flow, but that flow only > selects which advisory message fires, and the hook exits 0 on every branch. This line was the > clearest statement of the wrong model anywhere in the docs. +> +> **The floor's source is a set, not a story.** Plan A derives one value from the current governing +> cited-story **set**: level 1 only if every cited story is level 0, and 3 if no story is cited or +> any cited story is unprofiled. The replacement uses Plan A's own words — *the profile and the +> cited set*. -- [ ] **Preflight.** +- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** ```sh -case "$(grep -cF -- "moves the hook's reminder threshold. It does not change the floor" README.md)" in - 0) : ;; - 1) echo "ALREADY APPLIED — skip"; exit 0 ;; - *) echo "AMBIGUOUS — stop"; exit 1 ;; -esac +old=$(grep -cF -- 'moves the 3-passes-per-gate floor' README.md) +new=$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the hook'\''s reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. |' README.md) +if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then + : # not applied — apply it +elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then + if [ -n "$(git diff -- README.md)" ] || [ -n "$(git diff --cached -- README.md)" ]; then + echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + fi + echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 +else + echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 +fi ``` - [ ] **Replace.** OLD: @@ -121,23 +158,29 @@ esac NEW: ``` -| `codex-gate.floor` | a positive integer; moves the hook's reminder threshold. It does not change the floor §5 obliges, which is derived from the cited story's profile. | +| `codex-gate.floor` | a positive integer; moves the hook's reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. | ``` -- [ ] **Assert.** +- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. ```bash -test "$(grep -cF -- "moves the hook's reminder threshold. It does not change the floor" README.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } -test "$(grep -cF -- "moves the 3-passes-per-gate floor" README.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the hook'\''s reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. |' README.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } +test "$(grep -cF -- 'moves the 3-passes-per-gate floor' README.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } ``` -- [ ] **Amend.** +- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. ```bash +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +git diff -- README.md git add README.md git commit --amend -m "WIP: review-loop economics" ``` +**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing +about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed +into the shared WIP and closed under this story. + --- ## Task 2: The Gate-A loop description @@ -150,14 +193,25 @@ git commit --amend -m "WIP: review-loop economics" docs/getting-started.md:34:three passes minimum, final pass clean — the one early exit is a pass that comes ``` -- [ ] **Preflight.** +> **The replacement lands mid-line**, because the old sentence ends part-way through line 35 and +> the next task owns the rest of it. Its assert is therefore a substring count, not the whole-line +> equality the other tasks use — stated here rather than left as an inconsistency to notice. + +- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** ```sh -case "$(grep -cF -- "the floor its profile derives, final pass clean" docs/getting-started.md)" in - 0) : ;; - 1) echo "ALREADY APPLIED — skip"; exit 0 ;; - *) echo "AMBIGUOUS — stop"; exit 1 ;; -esac +old=$(grep -cF -- 'three passes minimum' docs/getting-started.md) +new=$(grep -cF -- 'the floor §5 derives, final pass clean' docs/getting-started.md) +if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then + : # not applied — apply it +elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then + if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + fi + echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 +else + echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 +fi ``` - [ ] **Replace.** OLD: @@ -170,27 +224,109 @@ back with zero findings. NEW: ``` -the floor its profile derives, final pass clean — the one early exit is a pass that +the floor §5 derives, final pass clean — the one early exit is a pass that comes back with zero findings. ``` -- [ ] **Assert.** +- [ ] **Assert** — the new text (substring — this replacement lands mid-line) present exactly once, and the old text gone. + +```bash +test "$(grep -cF -- 'the floor §5 derives, final pass clean' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } +test "$(grep -cF -- 'three passes minimum' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +``` + +- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. + +```bash +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +git diff -- docs/getting-started.md +git add docs/getting-started.md +git commit --amend -m "WIP: review-loop economics" +``` + +**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing +about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed +into the shared WIP and closed under this story. + +--- + +## Task 3: The below-floor counter example + +**File:** `docs/getting-started.md` + +**Site** — pasted `grep -n`: + +``` +docs/getting-started.md:35:back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` are +``` + +> **The eighth site, and it was missed by the first sweep.** This example prints a fixed `1/3` and +> calls it *the counter* without saying whose. The `3` is the hook's own threshold; after Task 2 +> the sentence beside it says the floor is derived, and the two would contradict each other on the +> same screen. +> +> **Not C2's.** C2 owns the *satisfied* message at line 58, which reports three different numbers. +> This is a below-floor message and carries the same threshold-versus-floor claim as Task 4, so it +> belongs to C1's one claim. +> +> **Independent of Task 2** despite sharing line 35: Task 2's OLD ends at *"back with zero +> findings."* and this OLD begins at *"Hook messages"*. The two replacements touch disjoint text +> and may run in either order. + +- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** + +```sh +old=$(grep -cF -- 'the counter, not an error' docs/getting-started.md) +new=$(grep -cF -- 'count the calls against the' docs/getting-started.md) +if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then + : # not applied — apply it +elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then + if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + fi + echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 +else + echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 +fi +``` + +- [ ] **Replace.** OLD: + +``` +Hook messages like `⚠ Codex Gate A below floor (1/3)` are +the counter, not an error. +``` + +NEW: + +``` +Hook messages like `⚠ Codex Gate A below floor (1/3)` count the calls against the +hook's own reminder threshold, not against the floor §5 obliges, and are not an error. +``` + +- [ ] **Assert** — the new text (substring — this replacement lands mid-line) present exactly once, and the old text gone. ```bash -test "$(grep -cF -- "the floor its profile derives, final pass clean" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } -test "$(grep -cF -- "three passes minimum" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cF -- 'count the calls against the' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } +test "$(grep -cF -- 'the counter, not an error' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } ``` -- [ ] **Amend.** +- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. ```bash +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +git diff -- docs/getting-started.md git add docs/getting-started.md git commit --amend -m "WIP: review-loop economics" ``` +**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing +about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed +into the shared WIP and closed under this story. + --- -## Task 3: The plan-loop sentence +## Task 4: The plan-loop sentence **File:** `docs/getting-started.md` @@ -200,14 +336,21 @@ git commit --amend -m "WIP: review-loop economics" docs/getting-started.md:40:task-by-task plan (each task starts with a failing test); the same 3-pass loop runs ``` -- [ ] **Preflight.** +- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** ```sh -case "$(grep -cF -- "the same loop runs at the derived floor" docs/getting-started.md)" in - 0) : ;; - 1) echo "ALREADY APPLIED — skip"; exit 0 ;; - *) echo "AMBIGUOUS — stop"; exit 1 ;; -esac +old=$(grep -cF -- 'the same 3-pass loop runs' docs/getting-started.md) +new=$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor' docs/getting-started.md) +if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then + : # not applied — apply it +elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then + if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + fi + echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 +else + echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 +fi ``` - [ ] **Replace.** OLD: @@ -222,23 +365,29 @@ NEW: task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor ``` -- [ ] **Assert.** +- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. ```bash -test "$(grep -cF -- "the same loop runs at the derived floor" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } -test "$(grep -cF -- "the same 3-pass loop runs" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } +test "$(grep -cF -- 'the same 3-pass loop runs' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } ``` -- [ ] **Amend.** +- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. ```bash +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +git diff -- docs/getting-started.md git add docs/getting-started.md git commit --amend -m "WIP: review-loop economics" ``` +**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing +about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed +into the shared WIP and closed under this story. + --- -## Task 4: The below-floor hook message +## Task 5: The below-floor hook message **File:** `docs/getting-started.md` @@ -249,17 +398,24 @@ docs/getting-started.md:44:progress claims backed by test runs. If the Gate-A fl ``` > The hook reports against its **own threshold**, not against what the cycle owes. At a derived -> floor of 1 it announces a shortfall the cycle does not have — the named residual of this -> change, and this sentence is where a reader would otherwise learn the opposite. +> floor of 1 it announces a shortfall the cycle does not have — the named residual of this change, +> and this sentence is where a reader would otherwise learn the opposite. -- [ ] **Preflight.** +- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** ```sh -case "$(grep -cF -- "If the hook's own threshold wasn't met, it says" docs/getting-started.md)" in - 0) : ;; - 1) echo "ALREADY APPLIED — skip"; exit 0 ;; - *) echo "AMBIGUOUS — stop"; exit 1 ;; -esac +old=$(grep -cF -- 'If the Gate-A floor wasn'\''t met, the hook says' docs/getting-started.md) +new=$(grep -cxF -- 'progress claims backed by test runs. If the hook'\''s own threshold wasn'\''t met, it says' docs/getting-started.md) +if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then + : # not applied — apply it +elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then + if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + fi + echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 +else + echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 +fi ``` - [ ] **Replace.** OLD: @@ -274,23 +430,29 @@ NEW: progress claims backed by test runs. If the hook's own threshold wasn't met, it says ``` -- [ ] **Assert.** +- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. ```bash -test "$(grep -cF -- "If the hook's own threshold wasn't met, it says" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } -test "$(grep -cF -- "If the Gate-A floor wasn't met, the hook says" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'progress claims backed by test runs. If the hook'\''s own threshold wasn'\''t met, it says' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } +test "$(grep -cF -- 'If the Gate-A floor wasn'\''t met, the hook says' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } ``` -- [ ] **Amend.** +- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. ```bash +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +git diff -- docs/getting-started.md git add docs/getting-started.md git commit --amend -m "WIP: review-loop economics" ``` +**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing +about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed +into the shared WIP and closed under this story. + --- -## Task 5: The Gate-B loop description +## Task 6: The Gate-B loop description **File:** `docs/getting-started.md` @@ -300,14 +462,21 @@ git commit --amend -m "WIP: review-loop economics" docs/getting-started.md:53:`mcp__codex__review` the same way: three passes, final clean. Verification is by ``` -- [ ] **Preflight.** +- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** ```sh -case "$(grep -cF -- "the derived floor, final clean. Verification is by" docs/getting-started.md)" in - 0) : ;; - 1) echo "ALREADY APPLIED — skip"; exit 0 ;; - *) echo "AMBIGUOUS — stop"; exit 1 ;; -esac +old=$(grep -cF -- 'the same way: three passes, final clean' docs/getting-started.md) +new=$(grep -cxF -- '`mcp__codex__review` the same way: the derived floor, final clean. Verification is by' docs/getting-started.md) +if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then + : # not applied — apply it +elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then + if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + fi + echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 +else + echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 +fi ``` - [ ] **Replace.** OLD: @@ -322,23 +491,29 @@ NEW: `mcp__codex__review` the same way: the derived floor, final clean. Verification is by ``` -- [ ] **Assert.** +- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. ```bash -test "$(grep -cF -- "the derived floor, final clean. Verification is by" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } -test "$(grep -cF -- "the same way: three passes, final clean" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- '`mcp__codex__review` the same way: the derived floor, final clean. Verification is by' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } +test "$(grep -cF -- 'the same way: three passes, final clean' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } ``` -- [ ] **Amend.** +- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. ```bash +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +git diff -- docs/getting-started.md git add docs/getting-started.md git commit --amend -m "WIP: review-loop economics" ``` +**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing +about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed +into the shared WIP and closed under this story. + --- -## Task 6: The axes sentence +## Task 7: The axes sentence **File:** `docs/getting-started.md` @@ -348,18 +523,27 @@ git commit --amend -m "WIP: review-loop economics" docs/getting-started.md:84:is still owed and Gate A's floor is unchanged at every level. The caution bias is ``` -> **Two claims were tangled in one sentence.** That the axes never subtract is true and stays. -> That the floor is unchanged at every level is exactly what this change makes false. Separated -> rather than reworded, so the surviving clause is visibly the one that survived. +> **Three claims share this sentence and only one is false.** That the profile supplies +> eligibility rather than the skip: **kept**. That the battery is still owed: **kept**. That Gate +> A's floor is unchanged at every level: **this change makes it false**, and it is the only clause +> replaced. Nothing is added — an earlier draft introduced a clause about baseline questions that +> the old sentence never made, which the accounting would then have had to explain. -- [ ] **Preflight.** +- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** ```sh -case "$(grep -cF -- "Gate A's floor derives from the profile exactly as Gate B's does" docs/getting-started.md)" in - 0) : ;; - 1) echo "ALREADY APPLIED — skip"; exit 0 ;; - *) echo "AMBIGUOUS — stop"; exit 1 ;; -esac +old=$(grep -cF -- 'Gate A'\''s floor is unchanged at every level' docs/getting-started.md) +new=$(grep -cxF -- 'is still owed; Gate A'\''s floor derives from the profile and the cited set exactly as Gate B'\''s does. The caution bias is' docs/getting-started.md) +if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then + : # not applied — apply it +elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then + if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + fi + echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 +else + echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 +fi ``` - [ ] **Replace.** OLD: @@ -371,26 +555,32 @@ is still owed and Gate A's floor is unchanged at every level. The caution bias i NEW: ``` -is still owed; Gate A's floor derives from the profile exactly as Gate B's does, and what the axes never subtract is the baseline questions. The caution bias is +is still owed; Gate A's floor derives from the profile and the cited set exactly as Gate B's does. The caution bias is ``` -- [ ] **Assert.** +- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. ```bash -test "$(grep -cF -- "Gate A's floor derives from the profile exactly as Gate B's does" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } -test "$(grep -cF -- "Gate A's floor is unchanged at every level" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'is still owed; Gate A'\''s floor derives from the profile and the cited set exactly as Gate B'\''s does. The caution bias is' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } +test "$(grep -cF -- 'Gate A'\''s floor is unchanged at every level' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } ``` -- [ ] **Amend.** +- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. ```bash +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +git diff -- docs/getting-started.md git add docs/getting-started.md git commit --amend -m "WIP: review-loop economics" ``` +**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing +about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed +into the shared WIP and closed under this story. + --- -## Task 7: The second knob mention +## Task 8: The second knob mention **File:** `docs/getting-started.md` @@ -404,14 +594,21 @@ docs/getting-started.md:86:positive integer) moves the 3-pass floor, and `touch > the same wrong thing**, and this repo's own record is that a fix to one has never implied a fix > to the other. -- [ ] **Preflight.** +- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** ```sh -case "$(grep -cF -- "moves the hook's reminder threshold, and" docs/getting-started.md)" in - 0) : ;; - 1) echo "ALREADY APPLIED — skip"; exit 0 ;; - *) echo "AMBIGUOUS — stop"; exit 1 ;; -esac +old=$(grep -cF -- 'moves the 3-pass floor' docs/getting-started.md) +new=$(grep -cxF -- 'positive integer) moves the hook'\''s reminder threshold, and `touch .context/codex-gate.off`' docs/getting-started.md) +if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then + : # not applied — apply it +elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then + if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + fi + echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 +else + echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 +fi ``` - [ ] **Replace.** OLD: @@ -426,68 +623,100 @@ NEW: positive integer) moves the hook's reminder threshold, and `touch .context/codex-gate.off` ``` -- [ ] **Assert.** +- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. ```bash -test "$(grep -cF -- "moves the hook's reminder threshold, and" docs/getting-started.md)" -eq 1 || { echo "ASSERT FAILED"; exit 1; } -test "$(grep -cF -- "moves the 3-pass floor" docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'positive integer) moves the hook'\''s reminder threshold, and `touch .context/codex-gate.off`' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } +test "$(grep -cF -- 'moves the 3-pass floor' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } ``` -- [ ] **Amend.** +- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. ```bash +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +git diff -- docs/getting-started.md git add docs/getting-started.md git commit --amend -m "WIP: review-loop economics" ``` +**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing +about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed +into the shared WIP and closed under this story. + --- -## Task 8: Confirm the claim has no eighth site +## Task 9: Confirm the claim has no ninth site -**Runs last.** The seven tasks above were found by grepping the claim. This re-runs that grep -against the finished files, so the plan's completeness is a check rather than an assertion. +**Runs last.** The eight tasks above were found by grepping the claim. This re-runs that grep +against the finished files, so completeness is a check rather than an assertion. - [ ] **No stale floor language survives in either file.** ```bash -if grep -nEi '3-pass|three passes|3 passes|3-passes-per-gate' README.md docs/getting-started.md; then - echo "A FLOOR CLAIM SURVIVES — read the hits printed above"; exit 1 +if grep -nEi '3-pass|three passes|3 passes|3-passes-per-gate|[0-9]/3' README.md docs/getting-started.md; then + echo "A NUMERIC FLOOR CLAIM SURVIVES — read the hits printed above"; exit 1 fi ``` -> **What this catches and what it does not.** It catches the wrong claim in the spellings this -> repo has actually used. It does **not** catch a sentence asserting a fixed floor without a -> number — *"the loop always runs the same number of times"* would pass. That residue is a -> reading, and it is named here rather than covered by implication. +> **Exactly one hit is expected and is not C1's:** the satisfied-message example at +> `docs/getting-started.md:58`, which prints `3/3 cycle` and belongs to **C2**. If the grep reports +> only that line, C1's numeric sites are complete and C2 is still owed. **Any other hit is C1's.** +> +> **This sweep covers the numeric spellings only.** Tasks 5 and 7 correct sentences that state the +> claim with no number in them — *"If the Gate-A floor wasn't met"* and *"Gate A's floor is +> unchanged at every level"* — and those two are covered by their own tasks' old-text-gone asserts, +> not by this grep. Said here because a sweep that looks complete and is not is worse than one +> whose boundary is written down. -- [ ] **The knob is described identically in both files.** Both say it moves the hook's - reminder threshold; neither says it moves the floor. +- [ ] **The knob's non-binding property holds at both sites**, which is a stronger check than the + two rows being worded identically — they are not, and are not meant to be. ```bash test "$(grep -cF -- "moves the hook's reminder threshold" README.md)" -eq 1 || { echo "README KNOB LINE WRONG"; exit 1; } test "$(grep -cF -- "moves the hook's reminder threshold" docs/getting-started.md)" -eq 1 || { echo "GETTING-STARTED KNOB LINE WRONG"; exit 1; } +if grep -nF -e "moves the 3-pass floor" -e "moves the 3-passes-per-gate floor" README.md docs/getting-started.md; then + echo "A KNOB-BINDS-THE-FLOOR CLAIM SURVIVES"; exit 1 +fi ``` -- [ ] **Amend** if either check produced a fix; otherwise nothing to stage. +> **Fixed strings, not a general pattern, and deliberately.** A regex broad enough to catch any +> re-binding of the floor was written first and **errored on this machine's `grep`** — +> *"exceeds complexity limits"* — which is worse than a narrow check, because a command that +> cannot run reports nothing and looks like a pass. These three catch the two phrasings that +> existed and confirm the replacement landed in both files; a *newly invented* way of saying the +> knob moves the floor is caught by a reader, not by this. + +- [ ] **If either check produced a fix**, amend as the tasks above do and re-run both checks. + A clean Task 9 stages nothing and amends nothing — the only task here that does not. + +> **What this catches and what it does not.** It catches every numeric spelling of the claim this +> repo has used, and the two knob phrasings that existed. It does **not** catch a fixed-floor +> assertion phrased without a number — *"the loop always runs the same number of times"* passes — +> nor a newly invented knob sentence. That residue is a reading, and it is named here rather than +> covered by implication. --- ## Self-Review -**Scope.** Seven sentences, two files, one claim. Every anchor verified against the current -tree — neither file is touched by Plan A or Plan B, so no simulation was needed and none is -claimed. +**Scope.** Eight sentences, two files, one claim. Every anchor verified against the current tree; +neither file is touched by Plan A or Plan B, so no simulation was needed and none is claimed. + +**Six asserts are whole-line equality (`grep -cxF`).** A replacement that lands but drops a clause +fails them. Pass 1 caught the substring-sentinel version, where Task 7 could have omitted half its +replacement and still gone green. Tasks 2 and 3 land mid-line, cannot use whole-line equality, and +say so rather than looking like an oversight. -**Every assert is two commands, not one:** the new text present exactly once, and the old text -gone. Plan C's Gate-A cycle spent a Major on the single-sided version of this, where a sentinel -counted `1` in a file that still carried the text it replaced. +**Every preflight reads both texts.** One old and zero new means apply; zero old and one new means +already replaced — and then it checks whether the edit actually reached the WIP, because an +interruption between Replace and Amend otherwise leaves the change stranded in the worktree while +the task reports itself done. Every other state stops. -**Task 8 is the completeness check, and it is deliberately weaker than the claim it checks.** -It greps the spellings this repo has used, and says so. A fixed-floor assertion phrased without -a number escapes it. That limit is stated rather than left for a reviewer to discover. +**What the accounting fixed.** Row 7 was written from a summary of the live sentence rather than +the sentence, and attributed a claim about baseline questions the original never made. It is now +read from the live text: three claims, two kept verbatim, one replaced, nothing added. -**What this plan does not do.** It ships no rule, touches no prompt, produces no evidence and -runs no Gate-B cycle. The evidence the story's mode requires is C3's, and one combined Gate-B -cycle covers A, B, C1, C2 and C3 together. +**Two rows in the scope table have no owner**, and that is stated as a gap in the split rather +than solved here. They block C3's close, not this plan. Absorbing them would rebuild Plan C. -**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's. +**Known limit.** Gate A reviews this plan, not the edits. What the edits do is Gate B's — C3's. From 7e42947a145167cfe860d1621c0e3d0800ef67c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:27:50 +0200 Subject: [PATCH 092/117] =?UTF-8?q?docs(plan):=20Plan=20C1=20revision=203?= =?UTF-8?q?=20=E2=80=94=20every=20replacement=20line-complete,=20the=20rul?= =?UTF-8?q?e=20quoted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 2: 12 findings, 1 BLOCKER, 7 MAJOR = 8 B+M. Total down 14 -> 12; B+M flat at 8. THE BLOCKER WAS A CHECK THAT COULD NEVER PASS. Task 9's sweep exited 1 on any hit, while the plan itself says C1 runs first and the C2-owned `3/3 cycle` line is still there when the check runs. The completeness task could not reach its passing state in the plan's own stated order. That one line is now filtered by exact text — filtered, not tolerated, and the reason is written down. THE DERIVATION RULE IS NOW QUOTED, NOT SUMMARISED. Two revisions summarised it and both summaries were wrong: revision 1 made the source a single story, revision 2 omitted the above-level-0 arm and two of the three stop conditions. Stated exactly now — floor 1 iff the set is non-empty and every member is profiled, resolvable and level 0; floor 3 for no story, any unprofiled member, or any resolvable member above level 0; and no value at all, stopping, for an unresolvable profile, disagreeing governing headers, or an unreadable Story: header. AND THE HEADLINE WAS WRONG. "Three passes is not the floor" — 3 remains the derived floor for this very story and for most states. The claim being corrected is that it is always the answer. Reworded. A NINTH SENTENCE, found by pass 2: docs/getting-started.md:7-8 says the hook "reminds both of you when a gate isn't satisfied", presenting the hook's counter as gate state. Under a floor-1 cycle it can warn after the owed floor is met, and after enough counted calls it can report its threshold satisfied with findings still open. Now Task 2. That is the second site a claim-grep missed — recorded in the plan rather than smoothed over. EVERY REPLACEMENT IS NOW LINE-COMPLETE. Pass 1 killed substring sentinels; pass 2 found the two mid-line replacements that still had the same hole, where Task 3 could drop "not against the floor §5 obliges" and still go green. The intro and the Gate-A paragraph were restructured to three and four whole lines, the Gate-A loop and its 1/3 example merged into one task since they share a paragraph and a claim, and every check is grep -cxF. That also removes the counting hazard pass 2 raised: two occurrences on one line count as one for grep -c, which cannot happen for whole-line equality. AMENDS GOT THREE THINGS. Identity is now branch + exact subject + parent-is-not-a-WIP, because a subject match alone does not identify this cycle's commit. The index must be empty before staging, since --amend commits the whole index and `git add ` does nothing about what is already staged. And the scoped read is `git diff HEAD -- `, which sees the index; plain `git diff` does not. Each amend then reads the blob back out of HEAD to prove the replacement landed. ROLLBACK IS A TASK. Task 0 establishes branch, WIP identity, no stacked WIP and a clean tree, then prints the SHA to record. The rollback validates the recorded value is a real commit and is the WIP before resetting, and the plan says --hard is safe only because of the clean-tree precondition — and to stop and inspect when it fails. 28 shell blocks, all parse under sh -n. The Task 9 sweep was executed live against the current files: it reports the six numeric sites and correctly filters line 58. All whole-line OLD anchors verified present exactly once; all changed NEW lines verified absent. Gate B: N/A — docs/superpowers/plans/**.md only, staged set verified. --- ...review-loop-economics-plan-c1-user-docs.md | 610 ++++++++++-------- 1 file changed, 346 insertions(+), 264 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md index 0e2f31f..ee93350 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md @@ -5,7 +5,8 @@ > checkbox (`- [ ]`) syntax for tracking. **Goal:** Correct every sentence in `README.md` and `docs/getting-started.md` that Plan A makes -false about **where the pass floor comes from**. Eight sentences, two files, one claim. +false about **where the pass floor comes from**. Nine sentences in eight replacements, two files, +one claim. **Spec:** `docs/superpowers/specs/2026-08-28-review-loop-economics-design.md` (revision 36). C1 implements the part of §7 that lands in these two user-facing docs. @@ -30,24 +31,37 @@ takes the evidence, the Gate-B cycle and the close. ### The one claim this plan owns -**"Three passes" is not the floor. §5 derives the floor from the profile and the cited set, and -the `codex-gate.floor` knob moves the hook's reminder threshold rather than that floor.** +**Three passes is not a universal or constant floor.** §5 derives the floor from the profile and +the cited set, and the `codex-gate.floor` knob moves the hook's **reminder threshold** rather than +that floor. -**The source is a set, not a story**, and the replacements say so in Plan A's own words. Plan A -derives one value from the current governing cited-story set: **1 only if every cited story is -level 0**; **3 if no story is cited or any cited story is unprofiled**; and a present-but- -unresolvable profile **stops** rather than defaulting. A phrase like *"the cited story's profile"* -is false on three of those four states, which is why it appears nowhere below. +**Three is still the right answer in most states — including this story's.** The claim being +corrected is that it is *always* the answer, and that the knob moves it. -Eight sentences state the claim wrongly. They are the complete set in these two files, found by -grepping the **claim** — every place a number of passes, a floor, a threshold or the knob is -described — not by grepping a phrase. Task 9 re-runs that grep against the finished files. +**The rule, exactly as Plan A ships it** (byte-frozen; quoted rather than summarised because a +summary of this is what pass 1 and pass 2 both caught): + +- **Floor 1 if and only if** the cited set is **non-empty** and **every** member is **profiled**, + **resolvable** and at **level 0** — all four conditions, since *every member* is vacuously true + of an empty set. +- **Floor 3** for: no story cited; any cited story unprofiled; or any resolvable member above + level 0. There are two levels, not three — `high` takes its rigor from lens sets and evidence + mode, not from extra passes. +- **No value, and the cycle stops** for: a cited profile present but **unresolvable**; governing + headers that **disagree**; a `Story:` header that **cannot be read**. Each stops rather than + defaulting, because reading a stop as 3 would turn a stop condition into a silent default. + +One derived value governs all three cycles, because they derive from the same cited-story set. + +Nine sentences state the claim wrongly, in eight replacements. They are the complete set in these +two files, found by grepping the **claim** — every place a number of passes, a floor, a threshold, +gate satisfaction, or the knob is described. Task 9 re-runs that grep against the finished files. ### What this plan does not own, so that nothing falls between the pieces | Not here | Where | Why not here | |---|---|---| -| `docs/getting-started.md:58`, the satisfied-message example | **C2** | It describes what the hook's message *reports* — three numbers, three meanings. A different claim in the same file. The below-floor examples at lines 35 and 44 are **not** that claim and are Tasks 3 and 5 here. | +| `docs/getting-started.md:58`, the satisfied-message example | **C2** | It describes what the hook's message *reports* — three numbers, three meanings. A different claim in the same file. The below-floor sites at lines 35 and 44 are **not** that claim and are Tasks 3 and 5 here. | | CHANGELOG entry, manifest bump, the item-1 n/a sentence | **C2** | Packaging. | | The parser claim, the findings-slot rule, spec §8, the evidence pack, the Gate-B cycle, the close, the field report | **C3** | C3's site list must name the two shipped prompt copies carrying *"because the deferred metrics work parses it"* — the two sites Plan C's own claim sweep missed. | | `docs/coding-workflow.md` — the axes sentence and the model-recording sentence | **UNASSIGNED** | Neither a C1 file, nor packaging, nor the close. | @@ -55,9 +69,8 @@ described — not by grepping a phrase. Task 9 re-runs that grep against the fin **The two unassigned rows are a real gap in the split, and they block the combined close rather than this plan.** The story requires every falsified shipped sentence corrected in the same -change, so C3 cannot close while they have no owner. They are named here, routed to Daniel, and -**not absorbed** — taking them would make C1 a second Plan C, which is the thing the split exists -to prevent. +change, so C3 cannot close while they have no owner. Named here, routed to Daniel, and **not +absorbed** — taking them would make C1 a second Plan C, which is what the split exists to prevent. --- @@ -69,47 +82,61 @@ to prevent. recognize as WIP is treated as a Gate-B cycle boundary and the cycle's Gate-B state is reset — regardless of whether the commit itself succeeded**, since that branch cannot observe exit status. Gate-A state is reset by skill events, not here. **Never `git commit --amend --no-edit`.** -- **Every task checks that HEAD is the WIP before amending.** Started out of order, resumed in - the wrong checkout, or run after the cycle closed, the first amend would rewrite an unrelated - commit. -- **`git add` names paths explicitly, never `-u`** — and explicit paths are **not** a scope - guard for content *inside* the named file. Each amend step reads the scoped diff first. +- **Every amend establishes identity first, statelessly:** the branch is `review-loop-economics`, + `HEAD`'s subject is exactly the WIP subject, and `HEAD^` is **not** itself a WIP. The three + together are what a subject match alone does not give. +- **Every amend requires an empty index before staging.** `git commit --amend` commits the whole + index, so a change staged by anything else would ride along, and `git add ` does nothing + to prevent that. The scoped read is `git diff HEAD -- `, which sees the index as well as + the worktree; plain `git diff` does not. +- **Every amend verifies the replacement reached `HEAD`** by reading the committed blob back. - **No ordinary commit until C3 closes the cycle.** - **Line numbers are provenance, never instructions.** -- **Preflight and assert are different commands and every task carries both, instantiated.** - The preflight is a state machine over the old and new texts where **`0` new means apply**; the - assert runs after the edit, where the new text must appear **exactly once** and the old text - **zero** times. Reversing them would abort every task that has not run yet. -- **Six of the eight replacements are whole lines and are asserted with `grep -cxF`** — exact - whole-line equality, so a replacement that drops a clause fails. Tasks 2 and 3 land mid-line and - are asserted by substring; each says so. -- **Record the WIP commit's SHA before Task 1.** Nothing here is destructive, but a half-executed - C1 is undone by resetting the WIP to that SHA and re-running, and without the value written down - an executor is left reconstructing it from the reflog. +- **Preflight and assert are different commands and every task carries both, instantiated.** The + preflight is a state machine over the changed old and new lines where **every-new-zero means + apply**; the assert runs after the edit, where every changed new line must appear **exactly + once** and every replaced old line **zero** times. Reversing them would abort every task that + has not run yet. +- **Every replacement is line-complete and every check is `grep -cxF`** — whole-line equality. A + replacement that lands but drops a clause fails. This also removes the counting hazard a + substring check has, where two occurrences on one line count as one. +- **A multi-line replacement asserts only the lines that change.** Task 2 carries one line + through unaltered; asserting it would assert the file's prior state. - **Neither file is touched by Plan A or Plan B**, so every anchor below was verified against the **current** tree. No simulation, and none claimed. --- -## Old-conditions accounting +## Task 0: Checkpoint -For each sentence: what it asserted, and what replaces it. Read from the **live** sentence, not -from its summary. +**Runs before Task 1.** It establishes the preconditions every later task assumes, and it records +the one value a rollback needs. -| # | Sentence | What it asserted | Disposition | -|---|---|---|---| -| 1 | `README.md:130` knob row | the knob moves the 3-passes-per-gate floor | **false as of Plan A** — it moves the hook's reminder threshold and never bound an agent. Replaced, with the source named as the profile and the cited set | -| 2 | `getting-started.md:34` Gate-A loop | three passes minimum | **replaced** by the derived floor. "final pass clean" and the zero-finding early exit are **kept** | -| 3 | `getting-started.md:35` counter example | `1/3` is "the counter" | **kept as an example, relabelled** — the literal is the hook's own ratio, so the sentence now says which number is whose. "not an error" is **kept** | -| 4 | `getting-started.md:40` plan loop | the same 3-pass loop | **replaced**; the rest of the sentence kept | -| 5 | `getting-started.md:44` below-floor message | the hook reports that the Gate-A floor wasn't met | **false** — it reports its own threshold, which at a derived floor of 1 announces a shortfall the cycle does not owe. Replaced | -| 6 | `getting-started.md:53` Gate-B loop | three passes, final clean | **replaced** by the derived floor; "final clean" **kept** | -| 7 | `getting-started.md:84` axes sentence | the profile supplies eligibility not the skip · the battery is still owed · Gate A's floor is unchanged at every level | first two **kept verbatim**; the third **deliberately dropped and replaced**, since this change is what makes the floor vary. **Nothing added** | -| 8 | `getting-started.md:86` second knob mention | the knob moves the 3-pass floor | **false**, same as row 1. Both sites corrected, because fixing one leaves the other teaching it | +- [ ] **Establish the starting state.** + +```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } +git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — collapse first"; exit 1; fi +test -z "$(git status --porcelain)" || { echo "TREE NOT CLEAN — resolve before starting C1"; exit 1; } +echo "PRE-C1 WIP: $(git rev-parse HEAD) <-- record this value" +``` + +- [ ] **Rollback, if C1 must be undone.** Only with the recorded SHA, and only while the tree is + clean: + +```bash +sha=PASTE_THE_RECORDED_PRE_C1_WIP_SHA +test -z "$(git status --porcelain)" || { echo "TREE NOT CLEAN — do not reset; inspect first"; exit 1; } +git rev-parse --verify "$sha^{commit}" >/dev/null 2>&1 || { echo "NOT A COMMIT — check the recorded value"; exit 1; } +git log -1 --pretty=%s "$sha" | grep -qx 'WIP: review-loop economics' || { echo "RECORDED SHA IS NOT THE WIP — stop"; exit 1; } +git reset --hard "$sha" +``` -**Rows 1 and 8 are one claim in two places; rows 2, 4 and 6 are one claim in three; rows 3 and 5 -are one claim in two.** That is why they are in one plan: a fix to any one alone leaves the others -contradicting it. +> **`--hard` is safe here only because of the clean-tree precondition**, which Task 0 established +> and every task below preserves — each ends with a commit and nothing uncommitted. If the tree is +> **not** clean, the reset would destroy work that is not C1's: **stop and inspect instead.** +> Plans A and B are behind the recorded commit and are untouched by this reset. --- @@ -126,26 +153,21 @@ README.md:130:| `codex-gate.floor` | a positive integer; moves the 3-passes-per- > The knob never bound an agent. `$floor` appears in the hook's control flow, but that flow only > selects which advisory message fires, and the hook exits 0 on every branch. This line was the > clearest statement of the wrong model anywhere in the docs. -> -> **The floor's source is a set, not a story.** Plan A derives one value from the current governing -> cited-story **set**: level 1 only if every cited story is level 0, and 3 if no story is cited or -> any cited story is unprofiled. The replacement uses Plan A's own words — *the profile and the -> cited set*. -- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** +- [ ] **Preflight — a state machine over both texts, where every-new-zero is the signal to apply.** ```sh -old=$(grep -cF -- 'moves the 3-passes-per-gate floor' README.md) -new=$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the hook'\''s reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. |' README.md) -if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then - : # not applied — apply it -elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then - if [ -n "$(git diff -- README.md)" ] || [ -n "$(git diff --cached -- README.md)" ]; then + o1=$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the 3-passes-per-gate floor. |' README.md) + n1=$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the hook'\''s reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. |' README.md) +if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then + : # not applied — apply it +elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then + if [ -n "$(git diff HEAD -- README.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else - echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 + echo "MIXED OR DUPLICATE STATE — stop, do not retry"; exit 1 fi ``` @@ -161,168 +183,201 @@ NEW: | `codex-gate.floor` | a positive integer; moves the hook's reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. | ``` -- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. +- [ ] **Assert** — every changed line present exactly once, whole; every replaced line gone. ```bash -test "$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the hook'\''s reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. |' README.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } -test "$(grep -cF -- 'moves the 3-passes-per-gate floor' README.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the hook'\''s reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. |' README.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the 3-passes-per-gate floor. |' README.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } ``` -- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. +- [ ] **Amend** — identity first, index empty, scoped diff read, then verify it reached `HEAD`. ```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } -git diff -- README.md -git add README.md -git commit --amend -m "WIP: review-loop economics" +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi +test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } +git diff HEAD -- README.md +git add README.md || exit 1 +git commit --amend -m "WIP: review-loop economics" || exit 1 +test "$(git show HEAD:README.md | grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the hook'\''s reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. |')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } ``` -**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing -about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed -into the shared WIP and closed under this story. +**Read that `git diff HEAD` before staging.** It covers the worktree *and* the index, which +`git diff` alone does not — and `git commit --amend` commits the whole index, so a staged change +this task never made would ride along. The empty-index check above is what makes that observable. --- -## Task 2: The Gate-A loop description +## Task 2: The intro's account of what the hook reminds about **File:** `docs/getting-started.md` **Site** — pasted `grep -n`: ``` -docs/getting-started.md:34:three passes minimum, final pass clean — the one early exit is a pass that comes +docs/getting-started.md:7:normally; the skills and gates structure *how Claude works*, and the hook reminds ``` -> **The replacement lands mid-line**, because the old sentence ends part-way through line 35 and -> the next task owns the rest of it. Its assert is therefore a substring count, not the whole-line -> equality the other tasks use — stated here rather than left as an inconsistency to notice. +> **The claim's earliest site, and the first sweep missed it.** "The hook reminds you when a gate +> isn't satisfied" presents the hook's counter as gate state. It is not: the hook reports its own +> threshold and fingerprint, and §5 decides satisfaction. Under a floor-1 cycle the hook can warn +> after the owed floor is already met; after enough counted calls it can report its threshold +> satisfied while findings are still open. +> +> **Line 1 of the replacement is unchanged** and carries no assert of its own — only the two lines +> that actually change do. -- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** +- [ ] **Preflight — a state machine over both texts, where every-new-zero is the signal to apply.** ```sh -old=$(grep -cF -- 'three passes minimum' docs/getting-started.md) -new=$(grep -cF -- 'the floor §5 derives, final pass clean' docs/getting-started.md) -if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then - : # not applied — apply it -elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then - if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + o1=$(grep -cxF -- 'both of you when a gate isn'\''t satisfied. Your job is the decision points —' docs/getting-started.md) + n1=$(grep -cxF -- 'both of you when its own counter or fingerprint says a gate may not have run —' docs/getting-started.md) + n2=$(grep -cxF -- '§5 decides whether one is satisfied. Your job is the decision points —' docs/getting-started.md) +if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ] && [ "$n2" -eq 0 ]; then + : # not applied — apply it +elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ] && [ "$n2" -eq 1 ]; then + if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else - echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 + echo "MIXED OR DUPLICATE STATE — stop, do not retry"; exit 1 fi ``` - [ ] **Replace.** OLD: ``` -three passes minimum, final pass clean — the one early exit is a pass that comes -back with zero findings. +normally; the skills and gates structure *how Claude works*, and the hook reminds +both of you when a gate isn't satisfied. Your job is the decision points — ``` NEW: ``` -the floor §5 derives, final pass clean — the one early exit is a pass that -comes back with zero findings. +normally; the skills and gates structure *how Claude works*, and the hook reminds +both of you when its own counter or fingerprint says a gate may not have run — +§5 decides whether one is satisfied. Your job is the decision points — ``` -- [ ] **Assert** — the new text (substring — this replacement lands mid-line) present exactly once, and the old text gone. +- [ ] **Assert** — every changed line present exactly once, whole; every replaced line gone. ```bash -test "$(grep -cF -- 'the floor §5 derives, final pass clean' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } -test "$(grep -cF -- 'three passes minimum' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'both of you when its own counter or fingerprint says a gate may not have run —' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- '§5 decides whether one is satisfied. Your job is the decision points —' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'both of you when a gate isn'\''t satisfied. Your job is the decision points —' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } ``` -- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. +- [ ] **Amend** — identity first, index empty, scoped diff read, then verify it reached `HEAD`. ```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } -git diff -- docs/getting-started.md -git add docs/getting-started.md -git commit --amend -m "WIP: review-loop economics" +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi +test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } +git diff HEAD -- docs/getting-started.md +git add docs/getting-started.md || exit 1 +git commit --amend -m "WIP: review-loop economics" || exit 1 +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'both of you when its own counter or fingerprint says a gate may not have run —')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- '§5 decides whether one is satisfied. Your job is the decision points —')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } ``` -**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing -about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed -into the shared WIP and closed under this story. +**Read that `git diff HEAD` before staging.** It covers the worktree *and* the index, which +`git diff` alone does not — and `git commit --amend` commits the whole index, so a staged change +this task never made would ride along. The empty-index check above is what makes that observable. --- -## Task 3: The below-floor counter example +## Task 3: The Gate-A loop and its counter example **File:** `docs/getting-started.md` **Site** — pasted `grep -n`: ``` -docs/getting-started.md:35:back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` are +docs/getting-started.md:34:three passes minimum, final pass clean — the one early exit is a pass that comes ``` -> **The eighth site, and it was missed by the first sweep.** This example prints a fixed `1/3` and -> calls it *the counter* without saying whose. The `3` is the hook's own threshold; after Task 2 -> the sentence beside it says the floor is derived, and the two would contradict each other on the -> same screen. -> -> **Not C2's.** C2 owns the *satisfied* message at line 58, which reports three different numbers. -> This is a below-floor message and carries the same threshold-versus-floor claim as Task 4, so it -> belongs to C1's one claim. +> **Two sentences, one task, on purpose.** The loop description and the `1/3` example sit in one +> paragraph and state the same claim; the first sweep split them, and a substring assert could then +> pass while a load-bearing clause was dropped. Replaced together, **every resulting line is a +> complete line** and each is asserted whole. > -> **Independent of Task 2** despite sharing line 35: Task 2's OLD ends at *"back with zero -> findings."* and this OLD begins at *"Hook messages"*. The two replacements touch disjoint text -> and may run in either order. +> `1/3` stays as an example — the literal is the hook's own ratio. What changes is that the +> sentence now says which number is whose. "final pass clean", the zero-finding early exit and +> "not an error" are all kept. -- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** +- [ ] **Preflight — a state machine over both texts, where every-new-zero is the signal to apply.** ```sh -old=$(grep -cF -- 'the counter, not an error' docs/getting-started.md) -new=$(grep -cF -- 'count the calls against the' docs/getting-started.md) -if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then - : # not applied — apply it -elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then - if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + o1=$(grep -cxF -- 'three passes minimum, final pass clean — the one early exit is a pass that comes' docs/getting-started.md) + o2=$(grep -cxF -- 'back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` are' docs/getting-started.md) + o3=$(grep -cxF -- 'the counter, not an error. Your job: arbitrate disputed findings — Codex is' docs/getting-started.md) + n1=$(grep -cxF -- 'the floor §5 derives, final pass clean — the one early exit is a pass that comes' docs/getting-started.md) + n2=$(grep -cxF -- 'back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` count' docs/getting-started.md) + n3=$(grep -cxF -- 'the calls against the hook'\''s own reminder threshold, not against the floor §5 obliges,' docs/getting-started.md) + n4=$(grep -cxF -- 'and are not an error. Your job: arbitrate disputed findings — Codex is' docs/getting-started.md) +if [ "$o1" -eq 1 ] && [ "$o2" -eq 1 ] && [ "$o3" -eq 1 ] && [ "$n1" -eq 0 ] && [ "$n2" -eq 0 ] && [ "$n3" -eq 0 ] && [ "$n4" -eq 0 ]; then + : # not applied — apply it +elif [ "$o1" -eq 0 ] && [ "$o2" -eq 0 ] && [ "$o3" -eq 0 ] && [ "$n1" -eq 1 ] && [ "$n2" -eq 1 ] && [ "$n3" -eq 1 ] && [ "$n4" -eq 1 ]; then + if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else - echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 + echo "MIXED OR DUPLICATE STATE — stop, do not retry"; exit 1 fi ``` - [ ] **Replace.** OLD: ``` -Hook messages like `⚠ Codex Gate A below floor (1/3)` are -the counter, not an error. +three passes minimum, final pass clean — the one early exit is a pass that comes +back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` are +the counter, not an error. Your job: arbitrate disputed findings — Codex is ``` NEW: ``` -Hook messages like `⚠ Codex Gate A below floor (1/3)` count the calls against the -hook's own reminder threshold, not against the floor §5 obliges, and are not an error. +the floor §5 derives, final pass clean — the one early exit is a pass that comes +back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` count +the calls against the hook's own reminder threshold, not against the floor §5 obliges, +and are not an error. Your job: arbitrate disputed findings — Codex is ``` -- [ ] **Assert** — the new text (substring — this replacement lands mid-line) present exactly once, and the old text gone. +- [ ] **Assert** — every changed line present exactly once, whole; every replaced line gone. ```bash -test "$(grep -cF -- 'count the calls against the' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } -test "$(grep -cF -- 'the counter, not an error' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'the floor §5 derives, final pass clean — the one early exit is a pass that comes' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` count' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'the calls against the hook'\''s own reminder threshold, not against the floor §5 obliges,' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'and are not an error. Your job: arbitrate disputed findings — Codex is' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'three passes minimum, final pass clean — the one early exit is a pass that comes' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } +test "$(grep -cxF -- 'back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` are' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } +test "$(grep -cxF -- 'the counter, not an error. Your job: arbitrate disputed findings — Codex is' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } ``` -- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. +- [ ] **Amend** — identity first, index empty, scoped diff read, then verify it reached `HEAD`. ```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } -git diff -- docs/getting-started.md -git add docs/getting-started.md -git commit --amend -m "WIP: review-loop economics" +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi +test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } +git diff HEAD -- docs/getting-started.md +git add docs/getting-started.md || exit 1 +git commit --amend -m "WIP: review-loop economics" || exit 1 +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'the floor §5 derives, final pass clean — the one early exit is a pass that comes')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` count')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'the calls against the hook'\''s own reminder threshold, not against the floor §5 obliges,')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'and are not an error. Your job: arbitrate disputed findings — Codex is')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } ``` -**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing -about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed -into the shared WIP and closed under this story. +**Read that `git diff HEAD` before staging.** It covers the worktree *and* the index, which +`git diff` alone does not — and `git commit --amend` commits the whole index, so a staged change +this task never made would ride along. The empty-index check above is what makes that observable. --- @@ -336,20 +391,20 @@ into the shared WIP and closed under this story. docs/getting-started.md:40:task-by-task plan (each task starts with a failing test); the same 3-pass loop runs ``` -- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** +- [ ] **Preflight — a state machine over both texts, where every-new-zero is the signal to apply.** ```sh -old=$(grep -cF -- 'the same 3-pass loop runs' docs/getting-started.md) -new=$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor' docs/getting-started.md) -if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then - : # not applied — apply it -elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then - if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + o1=$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same 3-pass loop runs' docs/getting-started.md) + n1=$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor' docs/getting-started.md) +if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then + : # not applied — apply it +elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then + if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else - echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 + echo "MIXED OR DUPLICATE STATE — stop, do not retry"; exit 1 fi ``` @@ -365,25 +420,29 @@ NEW: task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor ``` -- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. +- [ ] **Assert** — every changed line present exactly once, whole; every replaced line gone. ```bash -test "$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } -test "$(grep -cF -- 'the same 3-pass loop runs' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same 3-pass loop runs' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } ``` -- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. +- [ ] **Amend** — identity first, index empty, scoped diff read, then verify it reached `HEAD`. ```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } -git diff -- docs/getting-started.md -git add docs/getting-started.md -git commit --amend -m "WIP: review-loop economics" +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi +test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } +git diff HEAD -- docs/getting-started.md +git add docs/getting-started.md || exit 1 +git commit --amend -m "WIP: review-loop economics" || exit 1 +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } ``` -**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing -about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed -into the shared WIP and closed under this story. +**Read that `git diff HEAD` before staging.** It covers the worktree *and* the index, which +`git diff` alone does not — and `git commit --amend` commits the whole index, so a staged change +this task never made would ride along. The empty-index check above is what makes that observable. --- @@ -399,22 +458,22 @@ docs/getting-started.md:44:progress claims backed by test runs. If the Gate-A fl > The hook reports against its **own threshold**, not against what the cycle owes. At a derived > floor of 1 it announces a shortfall the cycle does not have — the named residual of this change, -> and this sentence is where a reader would otherwise learn the opposite. +> and the same claim Task 3's example carries. -- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** +- [ ] **Preflight — a state machine over both texts, where every-new-zero is the signal to apply.** ```sh -old=$(grep -cF -- 'If the Gate-A floor wasn'\''t met, the hook says' docs/getting-started.md) -new=$(grep -cxF -- 'progress claims backed by test runs. If the hook'\''s own threshold wasn'\''t met, it says' docs/getting-started.md) -if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then - : # not applied — apply it -elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then - if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + o1=$(grep -cxF -- 'progress claims backed by test runs. If the Gate-A floor wasn'\''t met, the hook says' docs/getting-started.md) + n1=$(grep -cxF -- 'progress claims backed by test runs. If the hook'\''s own threshold wasn'\''t met, it says' docs/getting-started.md) +if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then + : # not applied — apply it +elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then + if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else - echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 + echo "MIXED OR DUPLICATE STATE — stop, do not retry"; exit 1 fi ``` @@ -430,25 +489,29 @@ NEW: progress claims backed by test runs. If the hook's own threshold wasn't met, it says ``` -- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. +- [ ] **Assert** — every changed line present exactly once, whole; every replaced line gone. ```bash -test "$(grep -cxF -- 'progress claims backed by test runs. If the hook'\''s own threshold wasn'\''t met, it says' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } -test "$(grep -cF -- 'If the Gate-A floor wasn'\''t met, the hook says' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'progress claims backed by test runs. If the hook'\''s own threshold wasn'\''t met, it says' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'progress claims backed by test runs. If the Gate-A floor wasn'\''t met, the hook says' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } ``` -- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. +- [ ] **Amend** — identity first, index empty, scoped diff read, then verify it reached `HEAD`. ```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } -git diff -- docs/getting-started.md -git add docs/getting-started.md -git commit --amend -m "WIP: review-loop economics" +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi +test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } +git diff HEAD -- docs/getting-started.md +git add docs/getting-started.md || exit 1 +git commit --amend -m "WIP: review-loop economics" || exit 1 +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'progress claims backed by test runs. If the hook'\''s own threshold wasn'\''t met, it says')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } ``` -**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing -about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed -into the shared WIP and closed under this story. +**Read that `git diff HEAD` before staging.** It covers the worktree *and* the index, which +`git diff` alone does not — and `git commit --amend` commits the whole index, so a staged change +this task never made would ride along. The empty-index check above is what makes that observable. --- @@ -462,20 +525,20 @@ into the shared WIP and closed under this story. docs/getting-started.md:53:`mcp__codex__review` the same way: three passes, final clean. Verification is by ``` -- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** +- [ ] **Preflight — a state machine over both texts, where every-new-zero is the signal to apply.** ```sh -old=$(grep -cF -- 'the same way: three passes, final clean' docs/getting-started.md) -new=$(grep -cxF -- '`mcp__codex__review` the same way: the derived floor, final clean. Verification is by' docs/getting-started.md) -if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then - : # not applied — apply it -elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then - if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + o1=$(grep -cxF -- '`mcp__codex__review` the same way: three passes, final clean. Verification is by' docs/getting-started.md) + n1=$(grep -cxF -- '`mcp__codex__review` the same way: the derived floor, final clean. Verification is by' docs/getting-started.md) +if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then + : # not applied — apply it +elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then + if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else - echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 + echo "MIXED OR DUPLICATE STATE — stop, do not retry"; exit 1 fi ``` @@ -491,25 +554,29 @@ NEW: `mcp__codex__review` the same way: the derived floor, final clean. Verification is by ``` -- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. +- [ ] **Assert** — every changed line present exactly once, whole; every replaced line gone. ```bash -test "$(grep -cxF -- '`mcp__codex__review` the same way: the derived floor, final clean. Verification is by' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } -test "$(grep -cF -- 'the same way: three passes, final clean' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- '`mcp__codex__review` the same way: the derived floor, final clean. Verification is by' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- '`mcp__codex__review` the same way: three passes, final clean. Verification is by' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } ``` -- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. +- [ ] **Amend** — identity first, index empty, scoped diff read, then verify it reached `HEAD`. ```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } -git diff -- docs/getting-started.md -git add docs/getting-started.md -git commit --amend -m "WIP: review-loop economics" +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi +test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } +git diff HEAD -- docs/getting-started.md +git add docs/getting-started.md || exit 1 +git commit --amend -m "WIP: review-loop economics" || exit 1 +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- '`mcp__codex__review` the same way: the derived floor, final clean. Verification is by')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } ``` -**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing -about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed -into the shared WIP and closed under this story. +**Read that `git diff HEAD` before staging.** It covers the worktree *and* the index, which +`git diff` alone does not — and `git commit --amend` commits the whole index, so a staged change +this task never made would ride along. The empty-index check above is what makes that observable. --- @@ -526,23 +593,23 @@ docs/getting-started.md:84:is still owed and Gate A's floor is unchanged at ever > **Three claims share this sentence and only one is false.** That the profile supplies > eligibility rather than the skip: **kept**. That the battery is still owed: **kept**. That Gate > A's floor is unchanged at every level: **this change makes it false**, and it is the only clause -> replaced. Nothing is added — an earlier draft introduced a clause about baseline questions that -> the old sentence never made, which the accounting would then have had to explain. +> replaced. **Nothing is added** — an earlier draft introduced a clause about baseline questions +> the old sentence never made. -- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** +- [ ] **Preflight — a state machine over both texts, where every-new-zero is the signal to apply.** ```sh -old=$(grep -cF -- 'Gate A'\''s floor is unchanged at every level' docs/getting-started.md) -new=$(grep -cxF -- 'is still owed; Gate A'\''s floor derives from the profile and the cited set exactly as Gate B'\''s does. The caution bias is' docs/getting-started.md) -if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then - : # not applied — apply it -elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then - if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + o1=$(grep -cxF -- 'is still owed and Gate A'\''s floor is unchanged at every level. The caution bias is' docs/getting-started.md) + n1=$(grep -cxF -- 'is still owed; Gate A'\''s floor derives from the profile and the cited set exactly as Gate B'\''s does. The caution bias is' docs/getting-started.md) +if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then + : # not applied — apply it +elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then + if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else - echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 + echo "MIXED OR DUPLICATE STATE — stop, do not retry"; exit 1 fi ``` @@ -558,25 +625,29 @@ NEW: is still owed; Gate A's floor derives from the profile and the cited set exactly as Gate B's does. The caution bias is ``` -- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. +- [ ] **Assert** — every changed line present exactly once, whole; every replaced line gone. ```bash -test "$(grep -cxF -- 'is still owed; Gate A'\''s floor derives from the profile and the cited set exactly as Gate B'\''s does. The caution bias is' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } -test "$(grep -cF -- 'Gate A'\''s floor is unchanged at every level' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'is still owed; Gate A'\''s floor derives from the profile and the cited set exactly as Gate B'\''s does. The caution bias is' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'is still owed and Gate A'\''s floor is unchanged at every level. The caution bias is' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } ``` -- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. +- [ ] **Amend** — identity first, index empty, scoped diff read, then verify it reached `HEAD`. ```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } -git diff -- docs/getting-started.md -git add docs/getting-started.md -git commit --amend -m "WIP: review-loop economics" +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi +test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } +git diff HEAD -- docs/getting-started.md +git add docs/getting-started.md || exit 1 +git commit --amend -m "WIP: review-loop economics" || exit 1 +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'is still owed; Gate A'\''s floor derives from the profile and the cited set exactly as Gate B'\''s does. The caution bias is')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } ``` -**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing -about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed -into the shared WIP and closed under this story. +**Read that `git diff HEAD` before staging.** It covers the worktree *and* the index, which +`git diff` alone does not — and `git commit --amend` commits the whole index, so a staged change +this task never made would ride along. The empty-index check above is what makes that observable. --- @@ -594,20 +665,20 @@ docs/getting-started.md:86:positive integer) moves the 3-pass floor, and `touch > the same wrong thing**, and this repo's own record is that a fix to one has never implied a fix > to the other. -- [ ] **Preflight — a state machine over both texts, where `0` new is the signal to apply.** +- [ ] **Preflight — a state machine over both texts, where every-new-zero is the signal to apply.** ```sh -old=$(grep -cF -- 'moves the 3-pass floor' docs/getting-started.md) -new=$(grep -cxF -- 'positive integer) moves the hook'\''s reminder threshold, and `touch .context/codex-gate.off`' docs/getting-started.md) -if [ "$old" -eq 1 ] && [ "$new" -eq 0 ]; then - : # not applied — apply it -elif [ "$old" -eq 0 ] && [ "$new" -eq 1 ]; then - if [ -n "$(git diff -- docs/getting-started.md)" ] || [ -n "$(git diff --cached -- docs/getting-started.md)" ]; then + o1=$(grep -cxF -- 'positive integer) moves the 3-pass floor, and `touch .context/codex-gate.off`' docs/getting-started.md) + n1=$(grep -cxF -- 'positive integer) moves the hook'\''s reminder threshold, and `touch .context/codex-gate.off`' docs/getting-started.md) +if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then + : # not applied — apply it +elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then + if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else - echo "MIXED OR DUPLICATE STATE ($old old, $new new) — stop, do not retry"; exit 1 + echo "MIXED OR DUPLICATE STATE — stop, do not retry"; exit 1 fi ``` @@ -623,53 +694,59 @@ NEW: positive integer) moves the hook's reminder threshold, and `touch .context/codex-gate.off` ``` -- [ ] **Assert** — the complete new line, matched whole present exactly once, and the old text gone. +- [ ] **Assert** — every changed line present exactly once, whole; every replaced line gone. ```bash -test "$(grep -cxF -- 'positive integer) moves the hook'\''s reminder threshold, and `touch .context/codex-gate.off`' docs/getting-started.md)" -eq 1 || { echo "NEW TEXT NOT PRESENT EXACTLY ONCE"; exit 1; } -test "$(grep -cF -- 'moves the 3-pass floor' docs/getting-started.md)" -eq 0 || { echo "OLD TEXT SURVIVES"; exit 1; } +test "$(grep -cxF -- 'positive integer) moves the hook'\''s reminder threshold, and `touch .context/codex-gate.off`' docs/getting-started.md)" -eq 1 || { echo "NEW LINE MISSING OR DUPLICATED"; exit 1; } +test "$(grep -cxF -- 'positive integer) moves the 3-pass floor, and `touch .context/codex-gate.off`' docs/getting-started.md)" -eq 0 || { echo "OLD LINE SURVIVES"; exit 1; } ``` -- [ ] **Amend** — after confirming HEAD is the WIP and the file carries nothing else. +- [ ] **Amend** — identity first, index empty, scoped diff read, then verify it reached `HEAD`. ```bash +git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } -git diff -- docs/getting-started.md -git add docs/getting-started.md -git commit --amend -m "WIP: review-loop economics" +if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi +test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } +git diff HEAD -- docs/getting-started.md +git add docs/getting-started.md || exit 1 +git commit --amend -m "WIP: review-loop economics" || exit 1 +test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'positive integer) moves the hook'\''s reminder threshold, and `touch .context/codex-gate.off`')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } ``` -**Read that `git diff` before staging.** Explicit paths keep other *files* out; they do nothing -about other *content in this file*, so a concurrent or pre-existing edit here would be absorbed -into the shared WIP and closed under this story. +**Read that `git diff HEAD` before staging.** It covers the worktree *and* the index, which +`git diff` alone does not — and `git commit --amend` commits the whole index, so a staged change +this task never made would ride along. The empty-index check above is what makes that observable. --- -## Task 9: Confirm the claim has no ninth site +## Task 9: Confirm the claim has no tenth site -**Runs last.** The eight tasks above were found by grepping the claim. This re-runs that grep -against the finished files, so completeness is a check rather than an assertion. +**Runs last.** The eight replacements above were found by grepping the claim. This re-runs that +grep against the finished files, so completeness is a check rather than an assertion. -- [ ] **No stale floor language survives in either file.** +- [ ] **No numeric floor claim survives, except the one C2 owns.** ```bash -if grep -nEi '3-pass|three passes|3 passes|3-passes-per-gate|[0-9]/3' README.md docs/getting-started.md; then +if grep -nEi '3-pass|three passes|3 passes|3-passes-per-gate|[0-9]/3' README.md docs/getting-started.md \ + | grep -vF 'Codex Gate B satisfied (3/3 cycle'; then echo "A NUMERIC FLOOR CLAIM SURVIVES — read the hits printed above"; exit 1 fi ``` -> **Exactly one hit is expected and is not C1's:** the satisfied-message example at -> `docs/getting-started.md:58`, which prints `3/3 cycle` and belongs to **C2**. If the grep reports -> only that line, C1's numeric sites are complete and C2 is still owed. **Any other hit is C1's.** +> **The one expected hit is filtered, not tolerated.** The satisfied-message example at +> `docs/getting-started.md:58` prints `3/3 cycle` and belongs to **C2**; C1 runs first, so that +> line is still there when this check runs. Filtering it by exact text is what lets the check +> reach its passing state while C2 is still owed — an unfiltered version could never pass in the +> stated order, which pass 2 caught. > -> **This sweep covers the numeric spellings only.** Tasks 5 and 7 correct sentences that state the -> claim with no number in them — *"If the Gate-A floor wasn't met"* and *"Gate A's floor is -> unchanged at every level"* — and those two are covered by their own tasks' old-text-gone asserts, -> not by this grep. Said here because a sweep that looks complete and is not is worse than one -> whose boundary is written down. +> **This sweep covers the numeric spellings only.** Tasks 2, 5 and 7 correct sentences that state +> the claim with no number — *"when a gate isn't satisfied"*, *"If the Gate-A floor wasn't met"*, +> *"Gate A's floor is unchanged at every level"* — and those are covered by their own tasks' +> old-line-gone asserts, not by this grep. Said here because a sweep that looks complete and is +> not is worse than one whose boundary is written down. -- [ ] **The knob's non-binding property holds at both sites**, which is a stronger check than the - two rows being worded identically — they are not, and are not meant to be. +- [ ] **The knob's non-binding property holds at both sites.** ```bash test "$(grep -cF -- "moves the hook's reminder threshold" README.md)" -eq 1 || { echo "README KNOB LINE WRONG"; exit 1; } @@ -681,40 +758,45 @@ fi > **Fixed strings, not a general pattern, and deliberately.** A regex broad enough to catch any > re-binding of the floor was written first and **errored on this machine's `grep`** — -> *"exceeds complexity limits"* — which is worse than a narrow check, because a command that -> cannot run reports nothing and looks like a pass. These three catch the two phrasings that -> existed and confirm the replacement landed in both files; a *newly invented* way of saying the -> knob moves the floor is caught by a reader, not by this. - -- [ ] **If either check produced a fix**, amend as the tasks above do and re-run both checks. - A clean Task 9 stages nothing and amends nothing — the only task here that does not. +> *"exceeds complexity limits"*. A command that cannot run reports nothing and reads as a pass, +> which is worse than a narrow check. These three catch the two phrasings that existed and confirm +> the replacement landed in both files; a *newly invented* way of saying the knob moves the floor +> is caught by a reader, not by this. +> +> **The two knob rows are deliberately not identical** — `README.md` names the source, the +> getting-started line does not. The check is on the property, not on sameness. -> **What this catches and what it does not.** It catches every numeric spelling of the claim this -> repo has used, and the two knob phrasings that existed. It does **not** catch a fixed-floor -> assertion phrased without a number — *"the loop always runs the same number of times"* passes — -> nor a newly invented knob sentence. That residue is a reading, and it is named here rather than -> covered by implication. +- [ ] **If either check produced a fix**, amend as the tasks above do and re-run both. A clean + Task 9 stages nothing and amends nothing — the only task here that does not. --- ## Self-Review -**Scope.** Eight sentences, two files, one claim. Every anchor verified against the current tree; -neither file is touched by Plan A or Plan B, so no simulation was needed and none is claimed. +**Scope.** Nine sentences in eight replacements, two files, one claim. Every anchor verified +against the current tree; neither file is touched by Plan A or Plan B, so no simulation was needed +and none is claimed. + +**Every replacement is line-complete, and every check is whole-line equality.** Pass 1 caught +substring sentinels that could pass after a clause was dropped; pass 2 caught the two remaining +mid-line replacements with the same hole. Tasks 2 and 3 were restructured — the intro's three +lines and the Gate-A paragraph's four — so that no replacement lands mid-line anywhere in the +plan and `grep -cxF` covers all of them. + +**Task 3 merges two sentences the first sweep had split.** They sit in one paragraph and carry one +claim, and splitting them is what let a substring check look sufficient. -**Six asserts are whole-line equality (`grep -cxF`).** A replacement that lands but drops a clause -fails them. Pass 1 caught the substring-sentinel version, where Task 7 could have omitted half its -replacement and still gone green. Tasks 2 and 3 land mid-line, cannot use whole-line equality, and -say so rather than looking like an oversight. +**Two sites came from review, not from the original sweep**: the `1/3` counter example (pass 1) +and the intro's *"when a gate isn't satisfied"* (pass 2). Both were found by reading for the +claim, both are now owned, and the fact that a claim-grep missed them twice is recorded in the +plan rather than smoothed over. -**Every preflight reads both texts.** One old and zero new means apply; zero old and one new means -already replaced — and then it checks whether the edit actually reached the WIP, because an -interruption between Replace and Amend otherwise leaves the change stranded in the worktree while -the task reports itself done. Every other state stops. +**The derivation rule is quoted, not summarised.** Both earlier revisions summarised it and both +summaries were wrong — first by making the source a single story, then by omitting the +above-level-0 arm and two of the three stop conditions. -**What the accounting fixed.** Row 7 was written from a summary of the live sentence rather than -the sentence, and attributed a claim about baseline questions the original never made. It is now -read from the live text: three claims, two kept verbatim, one replaced, nothing added. +**Rollback is a task, not a sentence.** Task 0 records the SHA and establishes the clean-tree +precondition that makes `--hard` safe; the plan says what to do when that precondition fails. **Two rows in the scope table have no owner**, and that is stated as a gap in the split rather than solved here. They block C3's close, not this plan. Absorbing them would rebuild Plan C. From 52192d1490598d60eab950cfe09eab1e647d47b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:41:13 +0200 Subject: [PATCH 093/117] =?UTF-8?q?docs(plan):=20Plan=20C1=20revision=204?= =?UTF-8?q?=20=E2=80=94=20stops=20first,=20guards=20stop=20overclaiming?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass 3: 17 findings, 0 BLOCKER, 10 MAJOR. Curve 14/8, 12/8, 17/10 — total and B+M both up, Blockers 0 -> 1 -> 0. Daniel's prediction was <=3 passes; that is missed. THREE PRODUCT FIXES. - The header says the plan states no pass count derived from the story, and the headline then said three is this story's derived floor. Both cannot be true, and the written number goes stale the moment the header moves. Removed. - The derivation quote let a stop be outvoted. A mixed set — one unresolvable member beside several level-0 ones — satisfied both "stops" and "floor 3" as written. Stops are now stated first and every floor arm is qualified by them, which is Plan A's own stop-before-default semantics. - Task 9 was described as turning completeness into a check. It is not: it re-runs the numeric spellings and two knob phrasings, and a tenth sentence phrased without a number would pass it. Narrowed to what it does, in both places it was claimed — and the plan now records that two of its nine sites were found by review rather than by the claim-reading, which is the honest measure of what that reading is worth. TWO MECHANICAL BUGS. - `test -z "$(git status --porcelain)"` and the cached-name check read a FAILED git command's empty output as a clean result. A broken index or unreadable repository passed the safety precondition. Status is captured and checked before the value is tested, everywhere. - The resume path dead-ended: preflight said "run the amend step only", and the amend step rejected the now-nonempty index that an interrupted add-then-amend leaves. The index check now accepts empty OR exactly this task's own path, and stops on anything else. AND THREE OVERCLAIMS REMOVED RATHER THAN ARGUED WITH. The identity guards were described as establishing statelessly which commit HEAD is. They do not: a different commit with the same subject on the same branch passes all three, and every amend changes the SHA, so there is nothing stable to compare against without a lock this plan does not have. Single-executor, single-worktree is now stated as a PRECONDITION of the plan rather than something it verifies. The rollback likewise: a clean worktree says nothing about commits made since the checkpoint, and `git status` cannot see them. It now names the one state it covers, tells the executor to read `git log ..HEAD` first, and says plainly that every other state is a stop with no recovery supplied — because a recovery nobody has exercised is worse than an instruction to look. Two Majors remain unrepairable inside C1 and unchanged: docs/coding-workflow.md's two sentences and the five-file skipped-cycle claim are UNASSIGNED in the split, and they block C3's close rather than this plan. 28 shell blocks, all parse under sh -n. Gate B: N/A — docs/superpowers/plans/**.md only, staged set verified. --- ...review-loop-economics-plan-c1-user-docs.md | 173 +++++++++++++----- 1 file changed, 131 insertions(+), 42 deletions(-) diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md index ee93350..32e4229 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md @@ -35,27 +35,39 @@ takes the evidence, the Gate-B cycle and the close. the cited set, and the `codex-gate.floor` knob moves the hook's **reminder threshold** rather than that floor. -**Three is still the right answer in most states — including this story's.** The claim being -corrected is that it is *always* the answer, and that the knob moves it. +**Three is still the right answer in most states.** The claim being corrected is that it is +*always* the answer, and that the knob moves it. **This plan states no floor for this story** — +the value is recomputed from the story header at execution time, and a number written here would +be stale the moment that header moved. **The rule, exactly as Plan A ships it** (byte-frozen; quoted rather than summarised because a summary of this is what pass 1 and pass 2 both caught): -- **Floor 1 if and only if** the cited set is **non-empty** and **every** member is **profiled**, - **resolvable** and at **level 0** — all four conditions, since *every member* is vacuously true - of an empty set. -- **Floor 3** for: no story cited; any cited story unprofiled; or any resolvable member above - level 0. There are two levels, not three — `high` takes its rigor from lens sets and evidence - mode, not from extra passes. +**The stop conditions come first, and they are not outvoted by the rest of the set.** A mixed +set — one unresolvable member beside several level-0 ones — **stops**; it does not fall through to +a floor. Reading a stop as 3 would turn a stop condition into a silent default, which is the one +thing Plan A says the derivation must never do. + - **No value, and the cycle stops** for: a cited profile present but **unresolvable**; governing - headers that **disagree**; a `Story:` header that **cannot be read**. Each stops rather than - defaulting, because reading a stop as 3 would turn a stop condition into a silent default. + headers that **disagree**; a `Story:` header that **cannot be read**. +- **Otherwise, floor 1 if and only if** the cited set is **non-empty** and **every** member is + **profiled**, **resolvable** and at **level 0** — all four conditions, since *every member* is + vacuously true of an empty set. +- **Otherwise, floor 3**: no story cited; any cited story unprofiled; or any resolvable member + above level 0. There are two levels, not three — `high` takes its rigor from lens sets and + evidence mode, not from extra passes. One derived value governs all three cycles, because they derive from the same cited-story set. Nine sentences state the claim wrongly, in eight replacements. They are the complete set in these -two files, found by grepping the **claim** — every place a number of passes, a floor, a threshold, -gate satisfaction, or the knob is described. Task 9 re-runs that grep against the finished files. +two files **as of this revision**, found by reading for the claim — every place a number of +passes, a floor, a threshold, gate satisfaction, or the knob is described. **Two of the nine were +found by review rather than by that reading** (pass 1 and pass 2 each found one), which is the +measure of how much the reading is worth. + +**Task 9 is a bounded mechanical check, not a proof of completeness.** It re-runs the *numeric* +spellings and the two knob phrasings. A tenth sentence stating the claim without a number would +pass it. The plan says so at the task rather than implying otherwise here. ### What this plan does not own, so that nothing falls between the pieces @@ -82,13 +94,24 @@ absorbed** — taking them would make C1 a second Plan C, which is what the spli recognize as WIP is treated as a Gate-B cycle boundary and the cycle's Gate-B state is reset — regardless of whether the commit itself succeeded**, since that branch cannot observe exit status. Gate-A state is reset by skill events, not here. **Never `git commit --amend --no-edit`.** -- **Every amend establishes identity first, statelessly:** the branch is `review-loop-economics`, - `HEAD`'s subject is exactly the WIP subject, and `HEAD^` is **not** itself a WIP. The three - together are what a subject match alone does not give. -- **Every amend requires an empty index before staging.** `git commit --amend` commits the whole +- **Every amend runs three cheap guards before staging**, and it is worth being exact about what + they establish: the branch is `review-loop-economics`, `HEAD`'s subject is exactly the WIP + subject, and `HEAD^` is not itself a WIP. **They catch the wrong branch, a closed cycle, a + stacked WIP and a plain out-of-order run. They do not establish that `HEAD` is *this* cycle's + WIP** — a different commit with the same subject on the same branch passes all three, and every + amend changes the SHA, so there is no stable value to compare against without a lock this plan + does not have. **Single-executor, single-worktree is a precondition of this plan, not something + it verifies.** +- **Every amend inspects the index before staging.** `git commit --amend` commits the whole index, so a change staged by anything else would ride along, and `git add ` does nothing - to prevent that. The scoped read is `git diff HEAD -- `, which sees the index as well as - the worktree; plain `git diff` does not. + to prevent that. **Empty is fine and this task's own path already staged is fine** — that is + what an interrupted add-then-amend leaves behind, and rejecting it would dead-end the resume + path this plan advertises. **Any other staged path stops.** The scoped read is + `git diff HEAD -- `, which sees the index as well as the worktree; plain `git diff` does + not. +- **Every git command whose output is tested has its status checked first.** `test -z "$(cmd)"` + reads a *failed* command's empty output as a clean result, which would let a broken index or an + unreadable repository pass a safety precondition. - **Every amend verifies the replacement reached `HEAD`** by reading the committed blob back. - **No ordinary commit until C3 closes the cycle.** - **Line numbers are provenance, never instructions.** @@ -118,7 +141,8 @@ the one value a rollback needs. git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — collapse first"; exit 1; fi -test -z "$(git status --porcelain)" || { echo "TREE NOT CLEAN — resolve before starting C1"; exit 1; } +st=$(git status --porcelain) || { echo "git status FAILED — stop"; exit 1; } +test -z "$st" || { echo "TREE NOT CLEAN — resolve before starting C1"; exit 1; } echo "PRE-C1 WIP: $(git rev-parse HEAD) <-- record this value" ``` @@ -127,16 +151,24 @@ echo "PRE-C1 WIP: $(git rev-parse HEAD) <-- record this value" ```bash sha=PASTE_THE_RECORDED_PRE_C1_WIP_SHA -test -z "$(git status --porcelain)" || { echo "TREE NOT CLEAN — do not reset; inspect first"; exit 1; } +st=$(git status --porcelain) || { echo "git status FAILED — stop"; exit 1; } +test -z "$st" || { echo "TREE NOT CLEAN — do not reset; inspect first"; exit 1; } +git log --oneline "$sha"..HEAD # read it: every commit here must be C1's git rev-parse --verify "$sha^{commit}" >/dev/null 2>&1 || { echo "NOT A COMMIT — check the recorded value"; exit 1; } git log -1 --pretty=%s "$sha" | grep -qx 'WIP: review-loop economics' || { echo "RECORDED SHA IS NOT THE WIP — stop"; exit 1; } git reset --hard "$sha" ``` -> **`--hard` is safe here only because of the clean-tree precondition**, which Task 0 established -> and every task below preserves — each ends with a commit and nothing uncommitted. If the tree is -> **not** clean, the reset would destroy work that is not C1's: **stop and inspect instead.** -> Plans A and B are behind the recorded commit and are untouched by this reset. +> **What this rollback does and does not cover.** It restores the branch to the recorded commit, +> and Plans A and B are behind that commit and untouched. It is **not** a general safety net: a +> clean worktree says nothing about *commits* made after the checkpoint by anything else, and +> `git status` cannot see them — so a `--hard` here would discard them. It also races anything +> writing concurrently, which is why single-executor is a precondition above. +> +> **It covers exactly one state: a clean tree whose only commits since the checkpoint are C1's.** +> Establish that by reading `git log ..HEAD` before resetting. **Any other state — a dirty +> tree, a staged edit, an unfamiliar commit — is a stop, and this plan supplies no recovery for +> it**, because a recovery procedure nobody has exercised is worse than an instruction to look. --- @@ -164,6 +196,8 @@ if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then if [ -n "$(git diff HEAD -- README.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + # the amend step accepts this task's path already being staged, so an interrupted + # add-then-amend resumes there rather than dead-ending on a non-empty index fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else @@ -196,8 +230,13 @@ test "$(grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the 3-pas git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi -test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } -git diff HEAD -- README.md +staged=$(git diff --cached --name-only) || { echo "git diff --cached FAILED — stop"; exit 1; } +case "$staged" in + "") : ;; + "README.md") echo "NOTE: only this task's path is staged — an interrupted amend; continuing" ;; + *) echo "UNEXPECTED STAGED PATHS: $staged — inspect before staging"; exit 1 ;; +esac +git diff HEAD -- README.md || { echo "git diff FAILED — stop"; exit 1; } git add README.md || exit 1 git commit --amend -m "WIP: review-loop economics" || exit 1 test "$(git show HEAD:README.md | grep -cxF -- '| `codex-gate.floor` | a positive integer; moves the hook'\''s reminder threshold. It does not change the floor §5 obliges, which §5 derives from the profile and the cited set. |')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } @@ -239,6 +278,8 @@ if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ] && [ "$n2" -eq 0 ]; then elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ] && [ "$n2" -eq 1 ]; then if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + # the amend step accepts this task's path already being staged, so an interrupted + # add-then-amend resumes there rather than dead-ending on a non-empty index fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else @@ -275,8 +316,13 @@ test "$(grep -cxF -- 'both of you when a gate isn'\''t satisfied. Your job is th git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi -test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } -git diff HEAD -- docs/getting-started.md +staged=$(git diff --cached --name-only) || { echo "git diff --cached FAILED — stop"; exit 1; } +case "$staged" in + "") : ;; + "docs/getting-started.md") echo "NOTE: only this task's path is staged — an interrupted amend; continuing" ;; + *) echo "UNEXPECTED STAGED PATHS: $staged — inspect before staging"; exit 1 ;; +esac +git diff HEAD -- docs/getting-started.md || { echo "git diff FAILED — stop"; exit 1; } git add docs/getting-started.md || exit 1 git commit --amend -m "WIP: review-loop economics" || exit 1 test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'both of you when its own counter or fingerprint says a gate may not have run —')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } @@ -323,6 +369,8 @@ if [ "$o1" -eq 1 ] && [ "$o2" -eq 1 ] && [ "$o3" -eq 1 ] && [ "$n1" -eq 0 ] && [ elif [ "$o1" -eq 0 ] && [ "$o2" -eq 0 ] && [ "$o3" -eq 0 ] && [ "$n1" -eq 1 ] && [ "$n2" -eq 1 ] && [ "$n3" -eq 1 ] && [ "$n4" -eq 1 ]; then if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + # the amend step accepts this task's path already being staged, so an interrupted + # add-then-amend resumes there rather than dead-ending on a non-empty index fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else @@ -365,8 +413,13 @@ test "$(grep -cxF -- 'the counter, not an error. Your job: arbitrate disputed fi git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi -test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } -git diff HEAD -- docs/getting-started.md +staged=$(git diff --cached --name-only) || { echo "git diff --cached FAILED — stop"; exit 1; } +case "$staged" in + "") : ;; + "docs/getting-started.md") echo "NOTE: only this task's path is staged — an interrupted amend; continuing" ;; + *) echo "UNEXPECTED STAGED PATHS: $staged — inspect before staging"; exit 1 ;; +esac +git diff HEAD -- docs/getting-started.md || { echo "git diff FAILED — stop"; exit 1; } git add docs/getting-started.md || exit 1 git commit --amend -m "WIP: review-loop economics" || exit 1 test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'the floor §5 derives, final pass clean — the one early exit is a pass that comes')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } @@ -401,6 +454,8 @@ if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + # the amend step accepts this task's path already being staged, so an interrupted + # add-then-amend resumes there rather than dead-ending on a non-empty index fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else @@ -433,8 +488,13 @@ test "$(grep -cxF -- 'task-by-task plan (each task starts with a failing test); git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi -test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } -git diff HEAD -- docs/getting-started.md +staged=$(git diff --cached --name-only) || { echo "git diff --cached FAILED — stop"; exit 1; } +case "$staged" in + "") : ;; + "docs/getting-started.md") echo "NOTE: only this task's path is staged — an interrupted amend; continuing" ;; + *) echo "UNEXPECTED STAGED PATHS: $staged — inspect before staging"; exit 1 ;; +esac +git diff HEAD -- docs/getting-started.md || { echo "git diff FAILED — stop"; exit 1; } git add docs/getting-started.md || exit 1 git commit --amend -m "WIP: review-loop economics" || exit 1 test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } @@ -470,6 +530,8 @@ if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + # the amend step accepts this task's path already being staged, so an interrupted + # add-then-amend resumes there rather than dead-ending on a non-empty index fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else @@ -502,8 +564,13 @@ test "$(grep -cxF -- 'progress claims backed by test runs. If the Gate-A floor w git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi -test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } -git diff HEAD -- docs/getting-started.md +staged=$(git diff --cached --name-only) || { echo "git diff --cached FAILED — stop"; exit 1; } +case "$staged" in + "") : ;; + "docs/getting-started.md") echo "NOTE: only this task's path is staged — an interrupted amend; continuing" ;; + *) echo "UNEXPECTED STAGED PATHS: $staged — inspect before staging"; exit 1 ;; +esac +git diff HEAD -- docs/getting-started.md || { echo "git diff FAILED — stop"; exit 1; } git add docs/getting-started.md || exit 1 git commit --amend -m "WIP: review-loop economics" || exit 1 test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'progress claims backed by test runs. If the hook'\''s own threshold wasn'\''t met, it says')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } @@ -535,6 +602,8 @@ if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + # the amend step accepts this task's path already being staged, so an interrupted + # add-then-amend resumes there rather than dead-ending on a non-empty index fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else @@ -567,8 +636,13 @@ test "$(grep -cxF -- '`mcp__codex__review` the same way: three passes, final cle git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi -test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } -git diff HEAD -- docs/getting-started.md +staged=$(git diff --cached --name-only) || { echo "git diff --cached FAILED — stop"; exit 1; } +case "$staged" in + "") : ;; + "docs/getting-started.md") echo "NOTE: only this task's path is staged — an interrupted amend; continuing" ;; + *) echo "UNEXPECTED STAGED PATHS: $staged — inspect before staging"; exit 1 ;; +esac +git diff HEAD -- docs/getting-started.md || { echo "git diff FAILED — stop"; exit 1; } git add docs/getting-started.md || exit 1 git commit --amend -m "WIP: review-loop economics" || exit 1 test "$(git show HEAD:docs/getting-started.md | grep -cxF -- '`mcp__codex__review` the same way: the derived floor, final clean. Verification is by')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } @@ -606,6 +680,8 @@ if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + # the amend step accepts this task's path already being staged, so an interrupted + # add-then-amend resumes there rather than dead-ending on a non-empty index fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else @@ -638,8 +714,13 @@ test "$(grep -cxF -- 'is still owed and Gate A'\''s floor is unchanged at every git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi -test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } -git diff HEAD -- docs/getting-started.md +staged=$(git diff --cached --name-only) || { echo "git diff --cached FAILED — stop"; exit 1; } +case "$staged" in + "") : ;; + "docs/getting-started.md") echo "NOTE: only this task's path is staged — an interrupted amend; continuing" ;; + *) echo "UNEXPECTED STAGED PATHS: $staged — inspect before staging"; exit 1 ;; +esac +git diff HEAD -- docs/getting-started.md || { echo "git diff FAILED — stop"; exit 1; } git add docs/getting-started.md || exit 1 git commit --amend -m "WIP: review-loop economics" || exit 1 test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'is still owed; Gate A'\''s floor derives from the profile and the cited set exactly as Gate B'\''s does. The caution bias is')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } @@ -675,6 +756,8 @@ if [ "$o1" -eq 1 ] && [ "$n1" -eq 0 ]; then elif [ "$o1" -eq 0 ] && [ "$n1" -eq 1 ]; then if [ -n "$(git diff HEAD -- docs/getting-started.md)" ]; then echo "REPLACED BUT NOT YET IN THE WIP — run the amend step only, then skip"; exit 0 + # the amend step accepts this task's path already being staged, so an interrupted + # add-then-amend resumes there rather than dead-ending on a non-empty index fi echo "ALREADY APPLIED AND COMMITTED — skip"; exit 0 else @@ -707,8 +790,13 @@ test "$(grep -cxF -- 'positive integer) moves the 3-pass floor, and `touch .cont git symbolic-ref --short HEAD | grep -qx 'review-loop-economics' || { echo "WRONG BRANCH — stop"; exit 1; } git log -1 --pretty=%s | grep -qx 'WIP: review-loop economics' || { echo "HEAD IS NOT THE WIP — stop"; exit 1; } if git log -1 --pretty=%s HEAD^ | grep -q '^WIP:'; then echo "STACKED WIP — stop"; exit 1; fi -test -z "$(git diff --cached --name-only)" || { echo "INDEX NOT EMPTY — inspect it before staging"; exit 1; } -git diff HEAD -- docs/getting-started.md +staged=$(git diff --cached --name-only) || { echo "git diff --cached FAILED — stop"; exit 1; } +case "$staged" in + "") : ;; + "docs/getting-started.md") echo "NOTE: only this task's path is staged — an interrupted amend; continuing" ;; + *) echo "UNEXPECTED STAGED PATHS: $staged — inspect before staging"; exit 1 ;; +esac +git diff HEAD -- docs/getting-started.md || { echo "git diff FAILED — stop"; exit 1; } git add docs/getting-started.md || exit 1 git commit --amend -m "WIP: review-loop economics" || exit 1 test "$(git show HEAD:docs/getting-started.md | grep -cxF -- 'positive integer) moves the hook'\''s reminder threshold, and `touch .context/codex-gate.off`')" -eq 1 || { echo "REPLACEMENT NOT IN HEAD"; exit 1; } @@ -722,8 +810,9 @@ this task never made would ride along. The empty-index check above is what makes ## Task 9: Confirm the claim has no tenth site -**Runs last.** The eight replacements above were found by grepping the claim. This re-runs that -grep against the finished files, so completeness is a check rather than an assertion. +**Runs last.** This re-runs the mechanical part of the discovery search against the finished +files. **It is a bounded check, not a completeness proof** — see the boundary stated under the +first command, and the plan's own count of sites that only review found. - [ ] **No numeric floor claim survives, except the one C2 owns.** From 4e829576dbc3a42ed458cb43c1d9a7c7a8b38fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:46:50 +0200 Subject: [PATCH 094/117] docs(vision): Freigabe ladder on a rendered wave plan, plan-as-view, phase-0 inputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decision 7: Freigabe reads a rendered wave plan (can vs. should); granularity is a human-owned maturity knob (story -> batch -> wave -> standing auto), flags always escalate, the plan renders on every rung, rungs rise only on P8 evidence. Decision 6 gains the transient-marker clarification. §4: the execution plan is a computed view (wave dry-run), waves are milestones that also steer. §9: Phase 0 has two inputs (pool / existing codebase via workflow-init); tree v1 need only be good enough to judge with; a single story is a mini-wave. --- .../specs/2026-08-30-dark-factory-vision.md | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index fe02e30..c7e705c 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -23,8 +23,9 @@ Story-Pool — filling is always consequence-free [missing] classification + architecture verdict, debounced/batched; attaches the card, triggers NO production) [missing] ↳ reads the Architektur (AGENTS.md) [exists, needs one extension] - → Freigabe (human sets "freigegeben" — the ONLY - production trigger) [human] + → Freigabe (human — the ONLY production trigger; + reads a rendered wave plan, granularity knob: + decision 7) [human] → Takt-Loop (polls freigegebene stories of the active wave → wakes the orchestrator → a lane opens) [missing] → Spec-Loop (intake + design + Gate A) [exists] @@ -77,7 +78,22 @@ the inspiration: the adversarial verifier is a different model *family* by both writers on one field; and in Phase 0 the architecture verdict waits until tree v1 exists. The intake zone (pool, Intake-Loop, Architektur) is the factory's first stage-4 proactive loop — at the front - of the pipeline, not the end. + of the pipeline, not the end. "Unclassified" is a transient marker, never a + working state: the pool is the single entry, and the marker exists only as + the intake trigger, as visible backlog when the loop stalls, and for Phase + 0's deferred architecture verdicts. +7. **Freigabe reads a rendered wave plan; its granularity is a maturity knob.** + The card answers "can this be built" — Freigabe answers "should this be + built now": value and timing, profile confirmation, capacity, and a + spot-check of the card (a Freigabe that regularly needs deep thought means + the card is missing a dimension → labeled example, tighten intake). The + knob: per story → batch → wave ("Go" on the rendered wave plan) → standing + auto-Freigabe (notified, not asked). Three things hold on every rung: flags + always escalate (high profile, architecture ⚠, rückfragen, scope doubt); + the knob is human-owned and committed, changed only by defined operation + (like `lanes`); and the plan is rendered on every rung — as a question or + as a notice. Raising the rung follows measured P8 evidence, never precedes + it (non-goal 4). ## 3. Maturity ladder @@ -101,6 +117,14 @@ the inspiration: the adversarial verifier is a different model *family* - **A project's roadmap is a view, not a document**: pool items plus status, priority, dependencies and **wave** yield the order. The pool is the single source; a separate roadmap file would be a second copy that drifts. +- **The execution plan is a computed view too** — the wave dry-run: dependency + graph (classification) + tree topology (the parallelism map) + waves and + priorities (human) + lanes and throttles yield lane assignments and order. + Recomputed at wave opening and every tick, never stored; the human + intervenes through the inputs, never by editing the plan. It generalizes + the orchestrator dry-run (build step 3). Waves are milestones that also + *steer* — the clock pulls only the active wave — so the roadmap is the + milestone-level view and the wave plan its per-milestone detail. - **Waves structure a new project.** Phase 0 assigns every initial pool item a wave mark (wave 1, 2, … or named milestones) — the deliberate "these subareas develop together first, those later" decision, usually aligned with @@ -180,7 +204,12 @@ proceeds, the breaking part waits on the meta-story). - **Phase 0 exists once.** Initial brainstorming builds architecture tree v1 plus goals/out-of-scope before production starts. It is the only - everything-waits moment; after it, intake never freezes. + everything-waits moment; after it, intake never freezes. It has two inputs: + a green field derives tree v1 from the pool; an existing project reads it + from the codebase (natural home: a `/workflow-init` extension). Tree v1 need + only be good enough to judge with — meta-stories correct it in use + (decision 3). A single incoming story is simply a mini-wave: the views + collapse to one line, no stage is skipped. - **Architecture churn blocks branches, never the factory.** An architecture-relevant story is not rejected: classification parks it with warten-auf behind its meta-story, and the meta-story locks exactly the tree From 096dfa97f69eaa83ba954df91e3e10e9b3e0d2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:56:18 +0200 Subject: [PATCH 095/117] docs(vision): rename Intake-Loop to Klassifizierungs-Loop The zone and the loop carried the same name (intake); the loop's actual job is attaching the classification. Zone stays Intake-Zone; the loop now names its function, matching the existing vocabulary (Klassifizierungs-Karte, status klassifiziert). Kit skill name 'intake' is untouched until build step 4 extends it. --- docs/superpowers/specs/2026-08-30-dark-factory-vision.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index c7e705c..01e6e05 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -19,7 +19,7 @@ measurably matters — and, at full maturity, sampled rather than omnipresent: ``` Story-Pool — filling is always consequence-free [missing] - → Intake-Loop (PROACTIVE: a new story appearing triggers + → Klassifizierungs-Loop (PROACTIVE: a new story appearing triggers classification + architecture verdict, debounced/batched; attaches the card, triggers NO production) [missing] ↳ reads the Architektur (AGENTS.md) [exists, needs one extension] @@ -70,13 +70,13 @@ the inspiration: the adversarial verifier is a different model *family* gates. (Until step 6 of the build path matures, merge remains human.) 6. **Filling the pool is always consequence-free; classification is a proactive loop of its own.** A new story's appearance (debounced into - mini-batches) triggers exactly one thing: the Intake-Loop attaches the + mini-batches) triggers exactly one thing: the Klassifizierungs-Loop attaches the classification card and the architecture verdict. Production starts only through the human's Freigabe plus the clock. Two rules guard the seam: the - Intake-Loop only attaches cards, status changes only through defined + Klassifizierungs-Loop only attaches cards, status changes only through defined operations (human: freigeben; loop: klassifiziert) — never free-form edits by both writers on one field; and in Phase 0 the architecture verdict - waits until tree v1 exists. The intake zone (pool, Intake-Loop, + waits until tree v1 exists. The intake zone (pool, Klassifizierungs-Loop, Architektur) is the factory's first stage-4 proactive loop — at the front of the pipeline, not the end. "Unclassified" is a transient marker, never a working state: the pool is the single entry, and the marker exists only as From 0c0e4768de0b5ab0515adf25d16092f7711f3b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:56:41 +0200 Subject: [PATCH 096/117] Revert "docs(vision): rename Intake-Loop to Klassifizierungs-Loop" This reverts commit 096dfa97f69eaa83ba954df91e3e10e9b3e0d2c6. --- docs/superpowers/specs/2026-08-30-dark-factory-vision.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index 01e6e05..c7e705c 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -19,7 +19,7 @@ measurably matters — and, at full maturity, sampled rather than omnipresent: ``` Story-Pool — filling is always consequence-free [missing] - → Klassifizierungs-Loop (PROACTIVE: a new story appearing triggers + → Intake-Loop (PROACTIVE: a new story appearing triggers classification + architecture verdict, debounced/batched; attaches the card, triggers NO production) [missing] ↳ reads the Architektur (AGENTS.md) [exists, needs one extension] @@ -70,13 +70,13 @@ the inspiration: the adversarial verifier is a different model *family* gates. (Until step 6 of the build path matures, merge remains human.) 6. **Filling the pool is always consequence-free; classification is a proactive loop of its own.** A new story's appearance (debounced into - mini-batches) triggers exactly one thing: the Klassifizierungs-Loop attaches the + mini-batches) triggers exactly one thing: the Intake-Loop attaches the classification card and the architecture verdict. Production starts only through the human's Freigabe plus the clock. Two rules guard the seam: the - Klassifizierungs-Loop only attaches cards, status changes only through defined + Intake-Loop only attaches cards, status changes only through defined operations (human: freigeben; loop: klassifiziert) — never free-form edits by both writers on one field; and in Phase 0 the architecture verdict - waits until tree v1 exists. The intake zone (pool, Klassifizierungs-Loop, + waits until tree v1 exists. The intake zone (pool, Intake-Loop, Architektur) is the factory's first stage-4 proactive loop — at the front of the pipeline, not the end. "Unclassified" is a transient marker, never a working state: the pool is the single entry, and the marker exists only as From 2afe1d11b107262da1f394a6681c49f902fa051e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 21:03:07 +0200 Subject: [PATCH 097/117] =?UTF-8?q?docs(vision):=20decision=208=20?= =?UTF-8?q?=E2=80=94=20four-eyes=20principle=20with=20a=20scaling=20guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second pair of eyes: another model by default; availability emergency = same model, different agent, fresh context; never the author. Human eyes only at bounded frequency (O(waves + exceptions), never O(stories)), and every mandatory human touchpoint carries a maturity knob that lowers with P8 evidence. Concrete: architecture merges trigger mechanical re-classification of touched branches; the Sample-Gate draws architecture merges at 100% as a starting value with a downward knob. --- .../specs/2026-08-30-dark-factory-vision.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index c7e705c..ed07d8a 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -94,6 +94,20 @@ the inspiration: the adversarial verifier is a different model *family* (like `lanes`); and the plan is rendered on every rung — as a question or as a notice. Raising the rung follows measured P8 evidence, never precedes it (non-goal 4). +8. **Four-eyes principle, with a scaling guard.** No artifact passes only its + author. The second pair of eyes is another model by default; in the + availability emergency (reviewer down, tokens exhausted) the same model as + a **different agent with fresh context** — never the same agent (the kit's + shipped two-tier reviewer fallback embodies this). Human eyes only where + the frequency is bounded — O(waves + exceptions), never O(stories) — and + every mandatory human touchpoint carries a maturity knob that lowers with + P8 evidence: presence is a dial that falls with trust, never a ratchet. + Two concrete rules: an architecture merge triggers re-classification of + the cards on the touched branches (their architecture verdicts are stale — + mechanical, no human involved); and the Sample-Gate draws architecture + merges at 100% as the starting value, knob downward with evidence — + bounded, because architecture changes batch into one meta-story per wave + (§9). ## 3. Maturity ladder From 56657bcf8eb3207cb6b4b13031cfda99108b63b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 21:04:11 +0200 Subject: [PATCH 098/117] docs(vision): reviewers judge artifacts, the judge watches process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sharpens the artifact-only-handoff rule in §1: a reviewer judges only the result, never the process (separate eyes need separate heads, separate heads come from separate context); process facts reach a reviewer only reified as artifacts. Deliberate exception: the judge/watchdog reads process signals and judges only liveness, never quality. --- docs/superpowers/specs/2026-08-30-dark-factory-vision.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index ed07d8a..02049bd 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -37,7 +37,13 @@ Story-Pool — filling is always consequence-free [missing] ``` Every node is a loop with its own fresh context; only the artifact crosses an -edge (story, spec, plan, diff, PR) — never the intermediate steps. Mandatory +edge (story, spec, plan, diff, PR) — never the intermediate steps. A reviewer +therefore judges only the result, never the process: separate eyes need +separate heads, and separate heads come from separate context. Everything that +matters must be *in* the artifact; process facts reach a reviewer only reified +as artifacts (records, findings, evidence entries). The one deliberate +exception is the judge/watchdog: it watches process signals (idle, thrash, no +progress) and judges only liveness, never quality. Mandatory stops, scope questions and architecture re-evaluations escalate to the human; the factory stops and reports instead of spinning. A human override becomes a labeled example for tightening the rules. From 391ea0f47f754cbfa3096f73869c8f98493b23f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Sun, 30 Aug 2026 21:11:28 +0200 Subject: [PATCH 099/117] =?UTF-8?q?docs(vision):=20park=20three=20=C2=A711?= =?UTF-8?q?=20topics=20for=20the=202026-08-31=20session?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Process dashboard (when/where/what runs, on views + P8), pipeline hooks and shortcuts for flexible use cases, and the three-test-layer question (merge- queue smoke gate for lane composition, E2E as clock loop filing pool stories) — all parked 2026-08-30, owned by the next design session. --- .../specs/2026-08-30-dark-factory-vision.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index 02049bd..e996ae3 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -307,3 +307,14 @@ visibility is what makes a token furnace invisible (their P0 gap, our P8). builder) as a Gate-B supplement — candidate small story, unscheduled. - How the clock's platform mechanics (loop/schedule) are configured per project — step 5. +- Process dashboard / console status — "when is what running where"; builds + on the computed views plus P8 trace/analytics. Parked 2026-08-30, design + session planned 2026-08-31. +- Sensible hooks and shortcuts through the pipeline for flexible use cases. + Parked 2026-08-30, same session. +- Test layers beyond the lane battery: a smoke gate in the serial merge + queue (parallel lanes can each be green while their composition breaks — + currently unchecked), and the full E2E suite as a clock loop whose + failures are auto-filed as pool stories. Proposed 2026-08-30, to be + decided in the same session; concrete tools stay project truth in + AGENTS.md. From 6e37fdaf8e25b3085bce1753f54870e467a5f54f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 14:39:59 +0200 Subject: [PATCH 100/117] docs(vision): three test layers and the merge-queue station MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lane battery (seconds, every cycle) · smoke gate in the serial merge queue (minutes, every merge: rebase onto main → smoke on the composed candidate → green lands, red returns to the lane as an artifact; main is never red, no human involved) · full E2E suite as a stage-3 clock loop whose failures are auto-filed as pool stories with the suspect merge's trace ID. Closes the parallelism blind spot (disjoint lanes each green, composition broken) and owns the merge-coordinator mechanics. AGENTS.md gains the command roles smoke and e2e; cadence and flaky rules belong to step 5. §1 pipeline, §4, §7 step 5, §9 and §11 updated accordingly. --- .../specs/2026-08-30-dark-factory-vision.md | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index e996ae3..33d980c 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -33,7 +33,10 @@ Story-Pool — filling is always consequence-free [missing] → Bau-Loop (executing-plans, goal-based, TDD) [exists] → Verify (battery + Gate B — adversarial model) [exists] → Sample-Gate (draws x% of PRs for human audit) [missing] - → Audit (human, sampled) → Merge/Deploy [missing] + → Audit (human, sampled) [missing] + → Merge-Queue (serial: rebase onto main → smoke on the + candidate → green lands, red returns to the lane) [missing] + → Merge/Deploy [missing] ``` Every node is a loop with its own fresh context; only the artifact crosses an @@ -130,9 +133,10 @@ the inspiration: the adversarial verifier is a different model *family* profiles, loop rules, prompt standards, the classification card. Home: the kit's prompts. They are the product. - **Project conventions** (per target project): AGENTS.md — architecture tree, - invariants, verified commands, conventions, **plus one extension this vision + invariants, verified commands, conventions, **plus two extensions this vision requires: project goals and an explicit out-of-scope list**, which the - classification's reject verdict reads. One document, read by gates, bots and + classification's reject verdict reads, **and two verified command roles, + `smoke` and `e2e`**, beside quality/lint/test (test layers, §9). One document, read by gates, bots and triage alike. - **A project's roadmap is a view, not a document**: pool items plus status, priority, dependencies and **wave** yield the order. The pool is the single @@ -206,7 +210,9 @@ proceeds, the breaking part waits on the meta-story). before automating further. 3. Orchestrator story (codify the role as skill/agent per decision 1). 4. Story pool + status + classification + architecture tree (decisions 2 and 4). -5. Stage 3: clock and event loops (poll, audit, PR processing). +5. Stage 3: clock and event loops (poll, audit, PR processing) — including the + E2E clock loop (failures auto-filed as pool stories) and the merge-queue + station (rebase + smoke on the candidate), see §9. 6. Stage 4 + sampled audit (decision 5) — full automation first only for level-0 stories; merge stays human until this step ships and holds. @@ -254,6 +260,20 @@ proceeds, the breaking part waits on the meta-story). stands in a mandatory stop, and no new lane opens while more than N decisions are queued for the human — the measured bottleneck is decision bandwidth, not compute. +- **Three test layers, three cost classes** (decided 2026-08-31). The lane + battery (seconds, every cycle, exists) · a **smoke gate in the merge queue** + (minutes, every merge, new) · the **full E2E suite as a clock loop** (hours, + nightly or at wave close, stage 3). The merge queue is a station of its own: + serial, per candidate rebase onto current main → smoke on the composed + candidate → green lands, red returns to the lane as an artifact (like Gate-B + findings) while the queue continues with the next branch — main is never + red and no human is involved; repeated failure escalates via the judge. + This closes the parallelism blind spot (disjoint lanes each green, their + composition broken) and owns the merge-coordinator mechanics (rebase, + retry). An E2E failure becomes a pool story automatically, classified by + the normal intake, carrying the trace ID of the suspect merge. Concrete + tools stay project truth: AGENTS.md gains the command roles `smoke` and + `e2e`. Cadence and flaky rules belong to step 5. ## 10. Prior art: godarkfactory.com (reviewed 2026-08-30) @@ -312,9 +332,6 @@ visibility is what makes a token furnace invisible (their P0 gap, our P8). session planned 2026-08-31. - Sensible hooks and shortcuts through the pipeline for flexible use cases. Parked 2026-08-30, same session. -- Test layers beyond the lane battery: a smoke gate in the serial merge - queue (parallel lanes can each be green while their composition breaks — - currently unchecked), and the full E2E suite as a clock loop whose - failures are auto-filed as pool stories. Proposed 2026-08-30, to be - decided in the same session; concrete tools stay project truth in - AGENTS.md. +- Test layers are decided (§9); still open: whether a smoke failure that + returns to a lane is surfaced to the human (a dashboard question), and the + E2E cadence / flaky-handling rules — step 5. From 0b48976d1f7e8b880ae94339a4ebe91906174be3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 15:08:37 +0200 Subject: [PATCH 101/117] docs(vision): dark-factory map as an Excalidraw file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The visual companion to the vision doc, versioned beside it. Zones are Excalidraw frames, loop stations are ellipses, human stations orange, missing stations red-dashed; arrows are bound to their nodes. Edited by hand in ExcalidrawZ (folder linked) and regenerated from the node/edge data when the vision changes — node positions survive regeneration. --- .../2026-08-30-dark-factory-vision.excalidraw | 6141 +++++++++++++++++ 1 file changed, 6141 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw b/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw new file mode 100644 index 0000000..be89155 --- /dev/null +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw @@ -0,0 +1,6141 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "dev-workflow-kit/mkdiagram.py", + "elements": [ + { + "id": "f-pool", + "type": "frame", + "x": 164, + "y": 164, + "width": 1152, + "height": 292, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 324838976, + "version": 1, + "versionNonce": 1152379866, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "name": "Intake-Zone · Stufe 4 proaktiv" + }, + { + "id": "f-spec", + "type": "frame", + "x": 1324, + "y": 604, + "width": 1552, + "height": 691, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 217893071, + "version": 1, + "versionNonce": 2037851939, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "name": "Produktion · eine Lane pro Ast (×lanes) · Review: anderes Modell, notfalls anderer Agent — nie der Autor" + }, + { + "id": "f-sample", + "type": "frame", + "x": 3044, + "y": 604, + "width": 1052, + "height": 216, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 780846360, + "version": 1, + "versionNonce": 1317990737, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "name": "Abnahme · Smoke vor dem Landen, main ist nie rot" + }, + { + "id": "f-metrics", + "type": "frame", + "x": 1964, + "y": 164, + "width": 1092, + "height": 292, + "angle": 0, + "strokeColor": "#868e96", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 54762750, + "version": 1, + "versionNonce": 151001551, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "name": "Überwachung" + }, + { + "id": "h-title", + "type": "text", + "x": 200, + "y": -150, + "width": 369.6, + "height": 35.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1877614492, + "version": 1, + "versionNonce": 446574991, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Dark Factory · Landkarte", + "fontSize": 28, + "fontFamily": 2, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Dark Factory · Landkarte", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "h-sub", + "type": "text", + "x": 200, + "y": -108, + "width": 707.85, + "height": 32.5, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1318703119, + "version": 1, + "versionNonce": 807946406, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Mehrere Loops bilden den Graphen. Auf den Kanten wandert nur das Artefakt.\nRot gestrichelt = Eskalation zu dir · Kreise = interne Loops (iterieren bis clean) · Rahmen = Zonen", + "fontSize": 13, + "fontFamily": 2, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Mehrere Loops bilden den Graphen. Auf den Kanten wandert nur das Artefakt.\nRot gestrichelt = Eskalation zu dir · Kreise = interne Loops (iterieren bis clean) · Rahmen = Zonen", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lg-auto", + "type": "rectangle", + "x": 200, + "y": -56, + "width": 16, + "height": 16, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 319009743, + "version": 1, + "versionNonce": 1362384196, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "lgt-auto", + "type": "text", + "x": 224, + "y": -56, + "width": 171.60000000000002, + "height": 15.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 541719408, + "version": 1, + "versionNonce": 2051851967, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Loop / Station · existiert", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Loop / Station · existiert", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lg-human", + "type": "rectangle", + "x": 446, + "y": -56, + "width": 16, + "height": 16, + "angle": 0, + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 746013369, + "version": 1, + "versionNonce": 1293384712, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "lgt-human", + "type": "text", + "x": 470, + "y": -56, + "width": 39.6, + "height": 15.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 782035029, + "version": 1, + "versionNonce": 1018232522, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Mensch", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Mensch", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lg-missing", + "type": "rectangle", + "x": 552, + "y": -56, + "width": 16, + "height": 16, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 263801686, + "version": 1, + "versionNonce": 247719778, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "lgt-missing", + "type": "text", + "x": 576, + "y": -56, + "width": 66.0, + "height": 15.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1823078164, + "version": 1, + "versionNonce": 1048118163, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "fehlt noch", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "fehlt noch", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-phase0", + "type": "rectangle", + "x": 1080, + "y": -60, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-phase0" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 695425565, + "version": 1, + "versionNonce": 2035525363, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-phase0" + }, + { + "id": "e-phase0-triage", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-phase0", + "type": "text", + "x": 1090.8, + "y": -46, + "width": 158.4, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 323946140, + "version": 1, + "versionNonce": 847877000, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Phase 0 · einmalig", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-phase0", + "originalText": "Phase 0 · einmalig", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-phase0", + "type": "text", + "x": 1084.2, + "y": -20, + "width": 171.60000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-phase0" + ], + "frameId": null, + "roundness": null, + "seed": 1397871145, + "version": 1, + "versionNonce": 103694313, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "vor Produktionsstart: Baum\nv1 + Ziele/Out-of-Scope ·\nWellen zuweisen", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "vor Produktionsstart: Baum\nv1 + Ziele/Out-of-Scope ·\nWellen zuweisen", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-pool", + "type": "rectangle", + "x": 200, + "y": 200, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-pool" + ], + "frameId": "f-pool", + "roundness": { + "type": 3 + }, + "seed": 155555738, + "version": 1, + "versionNonce": 1763673107, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-pool" + }, + { + "id": "e-pool-pruef", + "type": "arrow" + }, + { + "id": "e-pruef-pool", + "type": "arrow" + }, + { + "id": "e-pool-frei", + "type": "arrow" + }, + { + "id": "e-e2e-pool", + "type": "arrow" + }, + { + "id": "e-orch-pool", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-pool", + "type": "text", + "x": 246.0, + "y": 214, + "width": 88.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-pool", + "roundness": null, + "seed": 1150797846, + "version": 1, + "versionNonce": 202142729, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Story-Pool", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-pool", + "originalText": "Story-Pool", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-pool", + "type": "text", + "x": 204.2, + "y": 240, + "width": 171.60000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-pool" + ], + "frameId": "f-pool", + "roundness": null, + "seed": 785310973, + "version": 1, + "versionNonce": 1251527727, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "befüllen jederzeit,\nfolgenlos · Wellen · Kopf:\nlanes-Regler (deiner)", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "befüllen jederzeit,\nfolgenlos · Wellen · Kopf:\nlanes-Regler (deiner)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-pruef", + "type": "ellipse", + "x": 620, + "y": 200, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-pruef" + ], + "frameId": "f-pool", + "roundness": null, + "seed": 124551739, + "version": 1, + "versionNonce": 1953574603, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-pruef" + }, + { + "id": "e-pool-pruef", + "type": "arrow" + }, + { + "id": "e-pruef-pool", + "type": "arrow" + }, + { + "id": "e-pruef-triage", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-pruef", + "type": "text", + "x": 681.6, + "y": 255.0, + "width": 96.80000000000001, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-pool", + "roundness": null, + "seed": 1089709947, + "version": 1, + "versionNonce": 461060839, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Intake-Loop", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-pruef", + "originalText": "Intake-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-pruef", + "type": "text", + "x": 650.8, + "y": 281.0, + "width": 158.4, + "height": 60.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-pruef" + ], + "frameId": "f-pool", + "roundness": null, + "seed": 80521325, + "version": 1, + "versionNonce": 184570286, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "neue Story (Batch) → vet\n+ Karte + Architektur-\nUrteil · löst KEINE\nProduktion aus", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "neue Story (Batch) → vet\n+ Karte + Architektur-\nUrteil · löst KEINE\nProduktion aus", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-pruef", + "type": "text", + "x": 642.275, + "y": 347.0, + "width": 175.45000000000002, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-pruef" + ], + "frameId": "f-pool", + "roundness": null, + "seed": 931247022, + "version": 1, + "versionNonce": 898017870, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⟳ Klassifizierung · pro Story", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Klassifizierung · pro Story", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-frei", + "type": "rectangle", + "x": 200, + "y": 640, + "width": 180, + "height": 144, + "angle": 0, + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-frei" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 150013384, + "version": 1, + "versionNonce": 516819859, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-frei" + }, + { + "id": "e-pool-frei", + "type": "arrow" + }, + { + "id": "e-frei-takt", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-frei", + "type": "text", + "x": 254.8, + "y": 654, + "width": 70.4, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 194804717, + "version": 1, + "versionNonce": 1183364968, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Freigabe", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-frei", + "originalText": "Freigabe", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-frei", + "type": "text", + "x": 204.2, + "y": 680, + "width": 171.60000000000002, + "height": 90.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-frei" + ], + "frameId": null, + "roundness": null, + "seed": 911648020, + "version": 1, + "versionNonce": 126938844, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "dein „Go\" auf den\ngerenderten Wellen-Plan —\nder einzige Produktions-\nAuslöser · Regler: Story →\nBatch → Welle → auto ·\nFlags kommen immer zu dir", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "dein „Go\" auf den\ngerenderten Wellen-Plan —\nder einzige Produktions-\nAuslöser · Regler: Story →\nBatch → Welle → auto ·\nFlags kommen immer zu dir", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-takt", + "type": "ellipse", + "x": 560, + "y": 640, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-takt" + ], + "frameId": null, + "roundness": null, + "seed": 1775651416, + "version": 1, + "versionNonce": 1214302568, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-takt" + }, + { + "id": "e-frei-takt", + "type": "arrow" + }, + { + "id": "e-takt-orch", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-takt", + "type": "text", + "x": 630.4, + "y": 695.0, + "width": 79.2, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 265862674, + "version": 1, + "versionNonce": 2034632751, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Takt-Loop", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-takt", + "originalText": "Takt-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-takt", + "type": "text", + "x": 607.3, + "y": 721.0, + "width": 125.4, + "height": 60.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-takt" + ], + "frameId": null, + "roundness": null, + "seed": 479402029, + "version": 1, + "versionNonce": 1354258845, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "pollt freigegebene\nStories der aktiven\nWelle → weckt den\nOrchestrator", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "pollt freigegebene\nStories der aktiven\nWelle → weckt den\nOrchestrator", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-takt", + "type": "text", + "x": 651.85, + "y": 787.0, + "width": 36.300000000000004, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-takt" + ], + "frameId": null, + "roundness": null, + "seed": 1347402587, + "version": 1, + "versionNonce": 1251976313, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⟳ Takt", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Takt", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-triage", + "type": "ellipse", + "x": 1060, + "y": 200, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-triage" + ], + "frameId": "f-pool", + "roundness": null, + "seed": 2035189461, + "version": 1, + "versionNonce": 132847737, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-triage" + }, + { + "id": "e-phase0-triage", + "type": "arrow" + }, + { + "id": "e-pruef-triage", + "type": "arrow" + }, + { + "id": "e-triage-esk", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-triage", + "type": "text", + "x": 1121.6, + "y": 255.0, + "width": 96.80000000000001, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-pool", + "roundness": null, + "seed": 1239319144, + "version": 1, + "versionNonce": 1257440635, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Architektur", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-triage", + "originalText": "Architektur", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-triage", + "type": "text", + "x": 1090.8, + "y": 281.0, + "width": 158.4, + "height": 60.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-triage" + ], + "frameId": "f-pool", + "roundness": null, + "seed": 851864843, + "version": 1, + "versionNonce": 106492239, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "AGENTS.md: Baum ·\nInvarianten · Ziele/Out-\nof-Scope · Änderung →\nÄste neu klassifiziert", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "AGENTS.md: Baum ·\nInvarianten · Ziele/Out-\nof-Scope · Änderung →\nÄste neu klassifiziert", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-triage", + "type": "text", + "x": 1118.575, + "y": 347.0, + "width": 102.85000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-triage" + ], + "frameId": "f-pool", + "roundness": null, + "seed": 2096491879, + "version": 1, + "versionNonce": 474769609, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⟳ Bewertungs-Loop", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Bewertungs-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-spec", + "type": "ellipse", + "x": 1360, + "y": 640, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-spec" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 100035545, + "version": 1, + "versionNonce": 1195428768, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-spec" + }, + { + "id": "e-orch-spec", + "type": "arrow" + }, + { + "id": "e-spec-plan", + "type": "arrow" + }, + { + "id": "e-spec-esk", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-spec", + "type": "text", + "x": 1430.4, + "y": 717.5, + "width": 79.2, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 1843546982, + "version": 1, + "versionNonce": 285990743, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Spec-Loop", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-spec", + "originalText": "Spec-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-spec", + "type": "text", + "x": 1390.8, + "y": 743.5, + "width": 158.4, + "height": 15.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-spec" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 621931212, + "version": 1, + "versionNonce": 900094242, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "intake + Design + Gate A", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "intake + Design + Gate A", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-spec", + "type": "text", + "x": 1430.675, + "y": 764.5, + "width": 78.65, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-spec" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 309785427, + "version": 1, + "versionNonce": 1161114103, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⟳ Gate-A-Loop", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Gate-A-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-plan", + "type": "ellipse", + "x": 1780, + "y": 640, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-plan" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 252956897, + "version": 1, + "versionNonce": 1226027821, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-plan" + }, + { + "id": "e-spec-plan", + "type": "arrow" + }, + { + "id": "e-plan-build", + "type": "arrow" + }, + { + "id": "e-plan-esk", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-plan", + "type": "text", + "x": 1850.4, + "y": 717.5, + "width": 79.2, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 662459677, + "version": 1, + "versionNonce": 1203143341, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Plan-Loop", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-plan", + "originalText": "Plan-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-plan", + "type": "text", + "x": 1847.1, + "y": 743.5, + "width": 85.80000000000001, + "height": 15.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-plan" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 1752618008, + "version": 1, + "versionNonce": 1464589643, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Plan + Gate A", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Plan + Gate A", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-plan", + "type": "text", + "x": 1850.675, + "y": 764.5, + "width": 78.65, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-plan" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 388106950, + "version": 1, + "versionNonce": 221310450, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⟳ Gate-A-Loop", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Gate-A-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-esk", + "type": "rectangle", + "x": 1560, + "y": 240, + "width": 180, + "height": 84, + "angle": 0, + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-esk" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 1248976841, + "version": 1, + "versionNonce": 1226652085, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-esk" + }, + { + "id": "e-triage-esk", + "type": "arrow" + }, + { + "id": "e-orch-esk", + "type": "arrow" + }, + { + "id": "e-spec-esk", + "type": "arrow" + }, + { + "id": "e-plan-esk", + "type": "arrow" + }, + { + "id": "e-build-esk", + "type": "arrow" + }, + { + "id": "e-verify-esk", + "type": "arrow" + }, + { + "id": "e-judge-esk", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-esk", + "type": "text", + "x": 1562.0, + "y": 254, + "width": 176.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1372056228, + "version": 1, + "versionNonce": 403449955, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⚠ Eskalation an dich", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-esk", + "originalText": "⚠ Eskalation an dich", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-esk", + "type": "text", + "x": 1560.9, + "y": 280, + "width": 178.20000000000002, + "height": 30.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-esk" + ], + "frameId": null, + "roundness": null, + "seed": 799717634, + "version": 1, + "versionNonce": 209230570, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "stoppt & meldet sich, statt\nendlos zu drehen", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "stoppt & meldet sich, statt\nendlos zu drehen", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-build", + "type": "ellipse", + "x": 2200, + "y": 640, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-build" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 1176272277, + "version": 1, + "versionNonce": 1529246226, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-build" + }, + { + "id": "e-plan-build", + "type": "arrow" + }, + { + "id": "e-build-verify", + "type": "arrow" + }, + { + "id": "e-verify-build", + "type": "arrow" + }, + { + "id": "e-build-esk", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-build", + "type": "text", + "x": 2274.8, + "y": 710.0, + "width": 70.4, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 134838300, + "version": 1, + "versionNonce": 1211971682, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Bau-Loop", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-build", + "originalText": "Bau-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-build", + "type": "text", + "x": 2267.1, + "y": 736.0, + "width": 85.80000000000001, + "height": 30.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-build" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 127992539, + "version": 1, + "versionNonce": 1329312985, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "goal-based ·\ntestgetrieben", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "goal-based ·\ntestgetrieben", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-build", + "type": "text", + "x": 2258.575, + "y": 772.0, + "width": 102.85000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-build" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 442292976, + "version": 1, + "versionNonce": 1066042003, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⟳ TDD: rot → grün", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ TDD: rot → grün", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-verify", + "type": "ellipse", + "x": 2620, + "y": 640, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-verify" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 1461147819, + "version": 1, + "versionNonce": 1141860530, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-verify" + }, + { + "id": "e-build-verify", + "type": "arrow" + }, + { + "id": "e-verify-sample", + "type": "arrow" + }, + { + "id": "e-mq-verify", + "type": "arrow" + }, + { + "id": "e-verify-build", + "type": "arrow" + }, + { + "id": "e-verify-esk", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-verify", + "type": "text", + "x": 2703.6, + "y": 687.5, + "width": 52.800000000000004, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 918247488, + "version": 1, + "versionNonce": 1669086093, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Verify", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-verify", + "originalText": "Verify", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-verify", + "type": "text", + "x": 2654.1, + "y": 713.5, + "width": 151.8, + "height": 75.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-verify" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 674625912, + "version": 1, + "versionNonce": 999872393, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Gate B + Battery ·\nanderes Modell ·\nNotfall: gleiches\nModell, anderer Agent ·\nnie der Autor", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Gate B + Battery ·\nanderes Modell ·\nNotfall: gleiches\nModell, anderer Agent ·\nnie der Autor", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-verify", + "type": "text", + "x": 2690.675, + "y": 794.5, + "width": 78.65, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-verify" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 1257484521, + "version": 1, + "versionNonce": 1983075268, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⟳ Gate-B-Loop", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Gate-B-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-lane2", + "type": "rectangle", + "x": 1360, + "y": 1000, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-lane2" + ], + "frameId": "f-spec", + "roundness": { + "type": 3 + }, + "seed": 973206041, + "version": 1, + "versionNonce": 776492205, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-lane2" + }, + { + "id": "e-orch-lane2", + "type": "arrow" + }, + { + "id": "e-lane2-sample", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-lane2", + "type": "text", + "x": 1375.2, + "y": 1014, + "width": 149.60000000000002, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 643744727, + "version": 1, + "versionNonce": 533492028, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Lane 2 · parallel", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-lane2", + "originalText": "Lane 2 · parallel", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-lane2", + "type": "text", + "x": 1364.2, + "y": 1040, + "width": 171.60000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-lane2" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 1705916948, + "version": 1, + "versionNonce": 386046158, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Spec → Plan → Bau → Verify\n· disjunkter Ast, eigener\nWorktree", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Spec → Plan → Bau → Verify\n· disjunkter Ast, eigener\nWorktree", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-lane3", + "type": "rectangle", + "x": 1360, + "y": 1160, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-lane3" + ], + "frameId": "f-spec", + "roundness": { + "type": 3 + }, + "seed": 1501079115, + "version": 1, + "versionNonce": 1674671377, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-lane3" + }, + { + "id": "e-orch-lane3", + "type": "arrow" + }, + { + "id": "e-lane3-sample", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-lane3", + "type": "text", + "x": 1375.2, + "y": 1174, + "width": 149.60000000000002, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 524193278, + "version": 1, + "versionNonce": 175782304, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Lane 3 · parallel", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-lane3", + "originalText": "Lane 3 · parallel", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-lane3", + "type": "text", + "x": 1360.9, + "y": 1200, + "width": 178.20000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-lane3" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 1233565528, + "version": 1, + "versionNonce": 644780075, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "gleiche Pipeline · so viele\nLanes, wie der lanes-Regler\nerlaubt", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "gleiche Pipeline · so viele\nLanes, wie der lanes-Regler\nerlaubt", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-sample", + "type": "rectangle", + "x": 3080, + "y": 640, + "width": 180, + "height": 144, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-sample" + ], + "frameId": "f-sample", + "roundness": { + "type": 3 + }, + "seed": 1127850897, + "version": 1, + "versionNonce": 1063254276, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-sample" + }, + { + "id": "e-lane2-sample", + "type": "arrow" + }, + { + "id": "e-lane3-sample", + "type": "arrow" + }, + { + "id": "e-verify-sample", + "type": "arrow" + }, + { + "id": "e-sample-audit", + "type": "arrow" + }, + { + "id": "e-sample-mq", + "type": "arrow" + }, + { + "id": "e-metrics-sample", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-sample", + "type": "text", + "x": 3121.6, + "y": 654, + "width": 96.80000000000001, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-sample", + "roundness": null, + "seed": 1879343460, + "version": 1, + "versionNonce": 737608423, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Sample-Gate", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-sample", + "originalText": "Sample-Gate", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-sample", + "type": "text", + "x": 3084.2, + "y": 680, + "width": 171.60000000000002, + "height": 90.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-sample" + ], + "frameId": "f-sample", + "roundness": null, + "seed": 1566471825, + "version": 1, + "versionNonce": 963864094, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "zieht x % der PRs ·\nmechanische Risiko-\nSchwellen (Zeilen/Dateien)\n· Architektur-Merges:\nZiehung 100 % (Startwert,\nRegler)", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "zieht x % der PRs ·\nmechanische Risiko-\nSchwellen (Zeilen/Dateien)\n· Architektur-Merges:\nZiehung 100 % (Startwert,\nRegler)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-audit", + "type": "rectangle", + "x": 3480, + "y": 640, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-audit" + ], + "frameId": "f-sample", + "roundness": { + "type": 3 + }, + "seed": 618341637, + "version": 1, + "versionNonce": 1307729535, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-audit" + }, + { + "id": "e-sample-audit", + "type": "arrow" + }, + { + "id": "e-audit-mq", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-audit", + "type": "text", + "x": 3548.0, + "y": 654, + "width": 44.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-sample", + "roundness": null, + "seed": 2104909469, + "version": 1, + "versionNonce": 157197672, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Audit", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-audit", + "originalText": "Audit", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-audit", + "type": "text", + "x": 3484.2, + "y": 680, + "width": 171.60000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-audit" + ], + "frameId": "f-sample", + "roundness": null, + "seed": 253544329, + "version": 1, + "versionNonce": 1099367391, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "du prüfst die Stichprobe ·\narbeitet mit generierter\nPunchlist", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "du prüfst die Stichprobe ·\narbeitet mit generierter\nPunchlist", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-mq", + "type": "rectangle", + "x": 3880, + "y": 640, + "width": 180, + "height": 144, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-mq" + ], + "frameId": "f-sample", + "roundness": { + "type": 3 + }, + "seed": 897911925, + "version": 1, + "versionNonce": 354253419, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-mq" + }, + { + "id": "e-sample-mq", + "type": "arrow" + }, + { + "id": "e-audit-mq", + "type": "arrow" + }, + { + "id": "e-mq-merge", + "type": "arrow" + }, + { + "id": "e-mq-verify", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-mq", + "type": "text", + "x": 3921.6, + "y": 654, + "width": 96.80000000000001, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-sample", + "roundness": null, + "seed": 1625947776, + "version": 1, + "versionNonce": 734559256, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Merge-Queue", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-mq", + "originalText": "Merge-Queue", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-mq", + "type": "text", + "x": 3880.9, + "y": 680, + "width": 178.20000000000002, + "height": 90.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-mq" + ], + "frameId": "f-sample", + "roundness": null, + "seed": 326384299, + "version": 1, + "versionNonce": 2004182514, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "seriell · pro Kandidat: auf\nmain rebasen → Smoke auf\nder Kombination → grün\nlandet · rot geht als\nArtefakt zurück in die Lane\n· main ist nie rot", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "seriell · pro Kandidat: auf\nmain rebasen → Smoke auf\nder Kombination → grün\nlandet · rot geht als\nArtefakt zurück in die Lane\n· main ist nie rot", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-merge", + "type": "rectangle", + "x": 3880, + "y": 640, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-merge" + ], + "frameId": "f-sample", + "roundness": { + "type": 3 + }, + "seed": 1050040258, + "version": 1, + "versionNonce": 905590325, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-merge" + }, + { + "id": "e-mq-merge", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-merge", + "type": "text", + "x": 3908.4, + "y": 654, + "width": 123.20000000000002, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-sample", + "roundness": null, + "seed": 84196940, + "version": 1, + "versionNonce": 2065920251, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Merge / Deploy", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-merge", + "originalText": "Merge / Deploy", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-merge", + "type": "text", + "x": 3880.9, + "y": 680, + "width": 178.20000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-merge" + ], + "frameId": "f-sample", + "roundness": null, + "seed": 1434982633, + "version": 1, + "versionNonce": 166688708, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "landet auf main, wenn beide\nGates + Smoke grün ·\nDeploy: Projekt-CI", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "landet auf main, wenn beide\nGates + Smoke grün ·\nDeploy: Projekt-CI", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-judge", + "type": "rectangle", + "x": 2400, + "y": 200, + "width": 180, + "height": 114, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-judge" + ], + "frameId": "f-metrics", + "roundness": { + "type": 3 + }, + "seed": 1641903440, + "version": 1, + "versionNonce": 1198458558, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-judge" + }, + { + "id": "e-judge-esk", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-judge", + "type": "text", + "x": 2419.6, + "y": 214, + "width": 140.8, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-metrics", + "roundness": null, + "seed": 1230563834, + "version": 1, + "versionNonce": 1694566832, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Judge / Watchdog", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-judge", + "originalText": "Judge / Watchdog", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-judge", + "type": "text", + "x": 2400.9, + "y": 240, + "width": 178.20000000000002, + "height": 60.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-judge" + ], + "frameId": "f-metrics", + "roundness": null, + "seed": 1880074284, + "version": 1, + "versionNonce": 1757400422, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "überwacht alle Loops: Idle\n· Thrash · No-Progress →\nkillt & eskaliert · nie das\nhängende Modell selbst", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "überwacht alle Loops: Idle\n· Thrash · No-Progress →\nkillt & eskaliert · nie das\nhängende Modell selbst", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-metrics", + "type": "rectangle", + "x": 2000, + "y": 200, + "width": 180, + "height": 114, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-metrics" + ], + "frameId": "f-metrics", + "roundness": { + "type": 3 + }, + "seed": 673767655, + "version": 1, + "versionNonce": 730407202, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-metrics" + }, + { + "id": "e-metrics-sample", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-metrics", + "type": "text", + "x": 2015.2, + "y": 214, + "width": 149.60000000000002, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-metrics", + "roundness": null, + "seed": 1493135432, + "version": 1, + "versionNonce": 752002366, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Analytics + Trace", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-metrics", + "originalText": "Analytics + Trace", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-metrics", + "type": "text", + "x": 2004.2, + "y": 240, + "width": 171.60000000000002, + "height": 60.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-metrics" + ], + "frameId": "f-metrics", + "roundness": null, + "seed": 1276399591, + "version": 1, + "versionNonce": 1066600998, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "SQLite: Kosten · Dauer ·\nRetries pro Schritt ·\nTrace-ID pro Story · spec-\ndelta (= P8)", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "SQLite: Kosten · Dauer ·\nRetries pro Schritt ·\nTrace-ID pro Story · spec-\ndelta (= P8)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-e2e", + "type": "ellipse", + "x": 2800, + "y": 200, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-e2e" + ], + "frameId": "f-metrics", + "roundness": null, + "seed": 1245315470, + "version": 1, + "versionNonce": 1711312495, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-e2e" + }, + { + "id": "e-e2e-pool", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-e2e", + "type": "text", + "x": 2874.8, + "y": 247.5, + "width": 70.4, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-metrics", + "roundness": null, + "seed": 979693494, + "version": 1, + "versionNonce": 147667305, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "E2E-Loop", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-e2e", + "originalText": "E2E-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-e2e", + "type": "text", + "x": 2834.1, + "y": 273.5, + "width": 151.8, + "height": 75.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-e2e" + ], + "frameId": "f-metrics", + "roundness": null, + "seed": 1803817088, + "version": 1, + "versionNonce": 200995868, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "volle Suite als Takt-\nLoop (nightly / Wellen-\nEnde) · Fehlschlag →\nautomatisch Story im\nPool, mit Trace-ID", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "volle Suite als Takt-\nLoop (nightly / Wellen-\nEnde) · Fehlschlag →\nautomatisch Story im\nPool, mit Trace-ID", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-e2e", + "type": "text", + "x": 2861.6, + "y": 354.5, + "width": 96.80000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-e2e" + ], + "frameId": "f-metrics", + "roundness": null, + "seed": 2028687212, + "version": 1, + "versionNonce": 579690177, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "⟳ Takt · Stufe 3", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Takt · Stufe 3", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-orch", + "type": "rectangle", + "x": 960, + "y": 640, + "width": 180, + "height": 189, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "#ffe3e3", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-orch" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 1018118421, + "version": 1, + "versionNonce": 1496886435, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-orch" + }, + { + "id": "e-takt-orch", + "type": "arrow" + }, + { + "id": "e-orch-spec", + "type": "arrow" + }, + { + "id": "e-orch-lane2", + "type": "arrow" + }, + { + "id": "e-orch-lane3", + "type": "arrow" + }, + { + "id": "e-orch-esk", + "type": "arrow" + }, + { + "id": "e-orch-pool", + "type": "arrow" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false + }, + { + "id": "t-orch", + "type": "text", + "x": 997.2, + "y": 654, + "width": 105.60000000000001, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1426256014, + "version": 1, + "versionNonce": 139586394, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Orchestrator", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-orch", + "originalText": "Orchestrator", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-orch", + "type": "text", + "x": 960.9, + "y": 680, + "width": 178.20000000000002, + "height": 135.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-orch" + ], + "frameId": null, + "roundness": null, + "seed": 130286598, + "version": 1, + "versionNonce": 1570152712, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "verteilt Lanes (aktive\nWelle, lanes-Regler) ·\nreicht Artefakte weiter ·\ndry-run vor Token-Spend ·\nFrischsession ab Versuch N\n· Drosseln: Pflicht-Stop\noffen, Entscheidungs-Stau —\nheute: Sparring-Session von\nHand", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "verteilt Lanes (aktive\nWelle, lanes-Regler) ·\nreicht Artefakte weiter ·\ndry-run vor Token-Spend ·\nFrischsession ab Versuch N\n· Drosseln: Pflicht-Stop\noffen, Entscheidungs-Stau —\nheute: Sparring-Session von\nHand", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-phase0-triage", + "type": "arrow", + "x": 1170.0, + "y": 39, + "width": 0.0, + "height": 161, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1506442652, + "version": 1, + "versionNonce": 664876774, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-phase0-triage" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + 0.0, + 161 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-phase0", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-triage", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-phase0-triage", + "type": "text", + "x": 1124.625, + "y": 111.5, + "width": 90.75000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1389698625, + "version": 1, + "versionNonce": 1241130074, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Baum v1 + Ziele", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-phase0-triage", + "originalText": "Baum v1 + Ziele", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-pool-pruef", + "type": "arrow", + "x": 380, + "y": 249.5, + "width": 240, + "height": 60.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 2132657286, + "version": 1, + "versionNonce": 1462945690, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-pool-pruef" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 240, + 60.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-pool", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-pruef", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-pool-pruef", + "type": "text", + "x": 448.575, + "y": 271.75, + "width": 102.85000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1765070035, + "version": 1, + "versionNonce": 957006265, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Event: neue Story", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-pool-pruef", + "originalText": "Event: neue Story", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-pruef-pool", + "type": "arrow", + "x": 620, + "y": 400, + "width": 240, + "height": 221, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 611164248, + "version": 1, + "versionNonce": 1538946474, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-pruef-pool" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -120.0, + 100 + ], + [ + -240, + -121 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-pruef", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-pool", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-pruef-pool", + "type": "text", + "x": 451.6, + "y": 492, + "width": 96.80000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 828480808, + "version": 1, + "versionNonce": 1904904517, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Karte angeheftet", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-pruef-pool", + "originalText": "Karte angeheftet", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-pruef-triage", + "type": "arrow", + "x": 840, + "y": 310.0, + "width": 220, + "height": 0.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1435920784, + "version": 1, + "versionNonce": 745188127, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-pruef-triage" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 220, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-pruef", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-triage", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-pruef-triage", + "type": "text", + "x": 901.6, + "y": 302.0, + "width": 96.80000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 48453508, + "version": 1, + "versionNonce": 2020027419, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "liest & bewertet", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-pruef-triage", + "originalText": "liest & bewertet", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-pool-frei", + "type": "arrow", + "x": 290.0, + "y": 299, + "width": 0.0, + "height": 341, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 991483082, + "version": 1, + "versionNonce": 763353365, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-pool-frei" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + 0.0, + 341 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-pool", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-frei", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-pool-frei", + "type": "text", + "x": 178.075, + "y": 461.5, + "width": 223.85000000000002, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 360881140, + "version": 1, + "versionNonce": 1311939741, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "klassifiziert · Wellen-Plan gerendert", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-pool-frei", + "originalText": "klassifiziert · Wellen-Plan gerendert", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-frei-takt", + "type": "arrow", + "x": 380, + "y": 712.0, + "width": 180, + "height": 38.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 251461309, + "version": 1, + "versionNonce": 1060197638, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-frei-takt" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 180, + 38.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-frei", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-takt", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-frei-takt", + "type": "text", + "x": 412.525, + "y": 723.0, + "width": 114.95, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 126603649, + "version": 1, + "versionNonce": 468597630, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Status: freigegeben", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-frei-takt", + "originalText": "Status: freigegeben", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-takt-orch", + "type": "arrow", + "x": 780, + "y": 750.0, + "width": 180, + "height": 15.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1649767777, + "version": 1, + "versionNonce": 617255373, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-takt-orch" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 180, + -15.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-takt", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-orch", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-takt-orch", + "type": "text", + "x": 833.7, + "y": 734.25, + "width": 72.60000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 277756008, + "version": 1, + "versionNonce": 1585623284, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "weckt (Tick)", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-takt-orch", + "originalText": "weckt (Tick)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-orch-spec", + "type": "arrow", + "x": 1140, + "y": 734.5, + "width": 220, + "height": 15.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 531748802, + "version": 1, + "versionNonce": 854478761, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-orch-spec" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 220, + 15.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-orch", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-spec", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-orch-spec", + "type": "text", + "x": 1162.275, + "y": 734.25, + "width": 175.45000000000002, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 839558095, + "version": 1, + "versionNonce": 1968847850, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "öffnet Lane 1 · Story + Karte", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-orch-spec", + "originalText": "öffnet Lane 1 · Story + Karte", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-orch-lane2", + "type": "arrow", + "x": 1140, + "y": 734.5, + "width": 220, + "height": 315.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1871364441, + "version": 1, + "versionNonce": 1066240031, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-orch-lane2" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 220, + 315.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-orch", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-lane2", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-orch-lane2", + "type": "text", + "x": 1210.675, + "y": 884.0, + "width": 78.65, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 173047028, + "version": 1, + "versionNonce": 357268878, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "öffnet Lane 2", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-orch-lane2", + "originalText": "öffnet Lane 2", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-orch-lane3", + "type": "arrow", + "x": 1140, + "y": 734.5, + "width": 220, + "height": 475.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 964622594, + "version": 1, + "versionNonce": 862524476, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-orch-lane3" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 220, + 475.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-orch", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-lane3", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-orch-lane3", + "type": "text", + "x": 1195.55, + "y": 964.0, + "width": 108.9, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1179913225, + "version": 1, + "versionNonce": 596654992, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "… bis lanes-Budget", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-orch-lane3", + "originalText": "… bis lanes-Budget", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-lane2-sample", + "type": "arrow", + "x": 1540, + "y": 1049.5, + "width": 1540, + "height": 285.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1897052333, + "version": 1, + "versionNonce": 294046656, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-lane2-sample" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 1420, + -4.5 + ], + [ + 1540, + -285.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-lane2", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-sample", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-lane2-sample", + "type": "text", + "x": 2953.95, + "y": 1037, + "width": 12.100000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1759390061, + "version": 1, + "versionNonce": 924538201, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "PR", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-lane2-sample", + "originalText": "PR", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-lane3-sample", + "type": "arrow", + "x": 1540, + "y": 1209.5, + "width": 1540, + "height": 445.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1855392517, + "version": 1, + "versionNonce": 1181587504, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 1440, + -4.5 + ], + [ + 1540, + -445.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-lane3", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-sample", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "e-spec-plan", + "type": "arrow", + "x": 1580, + "y": 750.0, + "width": 200, + "height": 0.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 597904679, + "version": 1, + "versionNonce": 1516975390, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-spec-plan" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 200, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-spec", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-plan", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-spec-plan", + "type": "text", + "x": 1667.9, + "y": 742.0, + "width": 24.200000000000003, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 891842471, + "version": 1, + "versionNonce": 2118421926, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Spec", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-spec-plan", + "originalText": "Spec", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-plan-build", + "type": "arrow", + "x": 2000, + "y": 750.0, + "width": 200, + "height": 0.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 770455201, + "version": 1, + "versionNonce": 1466136595, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-plan-build" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 200, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-plan", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-build", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-plan-build", + "type": "text", + "x": 2087.9, + "y": 742.0, + "width": 24.200000000000003, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1898789635, + "version": 1, + "versionNonce": 816991461, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Plan", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-plan-build", + "originalText": "Plan", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-build-verify", + "type": "arrow", + "x": 2420, + "y": 750.0, + "width": 200, + "height": 0.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 2056712111, + "version": 1, + "versionNonce": 495535104, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-build-verify" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 200, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-build", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-verify", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-build-verify", + "type": "text", + "x": 2507.9, + "y": 742.0, + "width": 24.200000000000003, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 324100191, + "version": 1, + "versionNonce": 178208278, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Diff", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-build-verify", + "originalText": "Diff", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-verify-sample", + "type": "arrow", + "x": 2840, + "y": 750.0, + "width": 240, + "height": 38.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 378424697, + "version": 1, + "versionNonce": 324910815, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-verify-sample" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 240, + -38.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-verify", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-sample", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-verify-sample", + "type": "text", + "x": 2953.95, + "y": 723.0, + "width": 12.100000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 498123580, + "version": 1, + "versionNonce": 1414153797, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "PR", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-verify-sample", + "originalText": "PR", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-sample-audit", + "type": "arrow", + "x": 3260, + "y": 712.0, + "width": 220, + "height": 22.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 501085430, + "version": 1, + "versionNonce": 25905232, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-sample-audit" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 220, + -22.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-sample", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-audit", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-sample-audit", + "type": "text", + "x": 3348.825, + "y": 692.75, + "width": 42.35, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1041449536, + "version": 1, + "versionNonce": 1784759832, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "gezogen", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-sample-audit", + "originalText": "gezogen", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-sample-mq", + "type": "arrow", + "x": 3170.0, + "y": 784, + "width": 800.0, + "height": 100, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1265133104, + "version": 1, + "versionNonce": 391578344, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-sample-mq" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + 400.0, + 100 + ], + [ + 800.0, + 0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-sample", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-mq", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-sample-mq", + "type": "text", + "x": 3530.675, + "y": 876, + "width": 78.65, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 564244067, + "version": 1, + "versionNonce": 605441631, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "nicht gezogen", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-sample-mq", + "originalText": "nicht gezogen", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-audit-mq", + "type": "arrow", + "x": 3660, + "y": 689.5, + "width": 220, + "height": 22.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 8790957, + "version": 1, + "versionNonce": 312837672, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-audit-mq" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 220, + 22.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-audit", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-mq", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-audit-mq", + "type": "text", + "x": 3763.95, + "y": 692.75, + "width": 12.100000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 899680760, + "version": 1, + "versionNonce": 1148025345, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "OK", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-audit-mq", + "originalText": "OK", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-mq-merge", + "type": "arrow", + "x": 4060, + "y": 712.0, + "width": 180, + "height": 22.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 792966007, + "version": 1, + "versionNonce": 1309562236, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-mq-merge" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + -180, + -22.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-mq", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-merge", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-mq-merge", + "type": "text", + "x": 3912.525, + "y": 692.75, + "width": 114.95, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1216208521, + "version": 1, + "versionNonce": 684213371, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Smoke grün · landet", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-mq-merge", + "originalText": "Smoke grün · landet", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-mq-verify", + "type": "arrow", + "x": 3880, + "y": 764, + "width": 1040, + "height": 176, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 2046762209, + "version": 1, + "versionNonce": 269490964, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-mq-verify" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -520.0, + 176 + ], + [ + -1040, + 76 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-mq", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-verify", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-mq-verify", + "type": "text", + "x": 3269.25, + "y": 932, + "width": 181.50000000000003, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1482823831, + "version": 1, + "versionNonce": 1845122138, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Smoke rot → zurück in die Lane", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-mq-verify", + "originalText": "Smoke rot → zurück in die Lane", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-verify-build", + "type": "arrow", + "x": 2620, + "y": 840, + "width": 200, + "height": 100, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 1107009420, + "version": 1, + "versionNonce": 2040590398, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-verify-build" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -100.0, + 100 + ], + [ + -200, + 0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-verify", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-build", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-verify-build", + "type": "text", + "x": 2474.625, + "y": 932, + "width": 90.75000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1326270331, + "version": 1, + "versionNonce": 1406529762, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Findings zurück", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-verify-build", + "originalText": "Findings zurück", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-triage-esk", + "type": "arrow", + "x": 1280, + "y": 310.0, + "width": 280, + "height": 28.0, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1452128622, + "version": 1, + "versionNonce": 1588675649, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-triage-esk" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 280, + -28.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-triage", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-esk", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-triage-esk", + "type": "text", + "x": 1383.7, + "y": 288.0, + "width": 72.60000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 115948851, + "version": 1, + "versionNonce": 980634927, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Neubewertung", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-triage-esk", + "originalText": "Neubewertung", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-orch-esk", + "type": "arrow", + "x": 1050.0, + "y": 640, + "width": 600.0, + "height": 316, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1931732424, + "version": 1, + "versionNonce": 1870414235, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-orch-esk" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + 600.0, + -316 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-orch", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-esk", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-orch-esk", + "type": "text", + "x": 1322.775, + "y": 474.0, + "width": 54.45, + "height": 13.75, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1674971721, + "version": 1, + "versionNonce": 2044160092, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "eskaliert", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-orch-esk", + "originalText": "eskaliert", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-spec-esk", + "type": "arrow", + "x": 1470.0, + "y": 640, + "width": 180.0, + "height": 316, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1878002762, + "version": 1, + "versionNonce": 1461523904, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + 180.0, + -316 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-spec", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-esk", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "e-plan-esk", + "type": "arrow", + "x": 1890.0, + "y": 640, + "width": 240.0, + "height": 316, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1713419474, + "version": 1, + "versionNonce": 1201026917, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + -240.0, + -316 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-plan", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-esk", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "e-build-esk", + "type": "arrow", + "x": 2310.0, + "y": 640, + "width": 660.0, + "height": 316, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 842627282, + "version": 1, + "versionNonce": 854848018, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + -660.0, + -316 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-build", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-esk", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "e-verify-esk", + "type": "arrow", + "x": 2730.0, + "y": 640, + "width": 1080.0, + "height": 316, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 856800515, + "version": 1, + "versionNonce": 846366295, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-verify-esk" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + -1080.0, + -316 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-verify", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-esk", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-verify-esk", + "type": "text", + "x": 2153.7, + "y": 474.0, + "width": 72.60000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 222344215, + "version": 1, + "versionNonce": 1034062383, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Pflicht-Stop", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-verify-esk", + "originalText": "Pflicht-Stop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-judge-esk", + "type": "arrow", + "x": 2400, + "y": 257.0, + "width": 660, + "height": 25.0, + "angle": 0, + "strokeColor": "#e03131", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1362126470, + "version": 1, + "versionNonce": 859944004, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-judge-esk" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + -660, + 25.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-judge", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-esk", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-judge-esk", + "type": "text", + "x": 2027.65, + "y": 261.5, + "width": 84.7, + "height": 13.75, + "angle": 0, + "strokeColor": "#c92a2a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 133676181, + "version": 1, + "versionNonce": 409330879, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "hängt / thrash", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-judge-esk", + "originalText": "hängt / thrash", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-metrics-sample", + "type": "arrow", + "x": 2090.0, + "y": 314, + "width": 1080.0, + "height": 326, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 144627903, + "version": 1, + "versionNonce": 2114557575, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-metrics-sample" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + 1080.0, + 326 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-metrics", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-sample", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-metrics-sample", + "type": "text", + "x": 2581.6, + "y": 469.0, + "width": 96.80000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 448315526, + "version": 1, + "versionNonce": 946239001, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "speist Schwellen", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-metrics-sample", + "originalText": "speist Schwellen", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-e2e-pool", + "type": "arrow", + "x": 2910.0, + "y": 200, + "width": 2620.0, + "height": 110, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 348543443, + "version": 1, + "versionNonce": 236069245, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-e2e-pool" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + 0.0, + -110 + ], + [ + -2620.0, + -110 + ], + [ + -2620.0, + 0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-e2e", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-pool", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-e2e-pool", + "type": "text", + "x": 178.075, + "y": 82, + "width": 223.85000000000002, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 730259659, + "version": 1, + "versionNonce": 1290051973, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "Fehlschlag → Story im Pool (Trace-ID)", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-e2e-pool", + "originalText": "Fehlschlag → Story im Pool (Trace-ID)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-orch-pool", + "type": "arrow", + "x": 1050.0, + "y": 640, + "width": 670.0, + "height": 361, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 2 + }, + "seed": 112905263, + "version": 1, + "versionNonce": 219858513, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-orch-pool" + } + ], + "updated": 1788179982887, + "link": null, + "locked": false, + "points": [ + [ + 0.0, + 0 + ], + [ + -290.0, + -140 + ], + [ + -670.0, + -361 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-orch", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-pool", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-orch-pool", + "type": "text", + "x": 626.9, + "y": 492, + "width": 266.20000000000005, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 500965, + "version": 1, + "versionNonce": 1217158540, + "isDeleted": false, + "boundElements": [], + "updated": 1788179982887, + "link": null, + "locked": false, + "text": "zieht freigegebene Stories der aktiven Welle", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-orch-pool", + "originalText": "zieht freigegebene Stories der aktiven Welle", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "viewBackgroundColor": "#ffffff" + }, + "files": {} +} \ No newline at end of file From 23d88894fdfd0a123e2780e64258034fcdaff08c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 15:28:17 +0200 Subject: [PATCH 102/117] docs(vision): decision 9 (AC read-only in a lane), second prior-art sweep, deploy non-goal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Decision 9: a builder never edits acceptance criteria; a wrong AC is a story mutation — lane stops, story back to the pool flagged, human decides, re-classification; descriptive details still update in the same commit (§5), every spec change captured as spec-delta and shown beside the diff; AC block fingerprint checked at Gate B. Second sweep (§10): 14 adoptions with owning steps and six added rules (first blocking hook must argue invariant 1; reviewer never weaker than builder; cheap review = INCOMPLETE pass; rejected PR always re-audited; caps apply to build loops not review loops; harness router only after P8 measures harness tokens), redirects, rollup branch rejected with reasons. §3 stage-4 wording and decision 7 aligned with decisions 4 and 6. Deployment declared a non-goal. --- .../specs/2026-08-30-dark-factory-vision.md | 85 ++++++++++++++++++- 1 file changed, 82 insertions(+), 3 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index 33d980c..d710725 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -55,7 +55,7 @@ The kit already holds the middle of this pipeline, and in one respect exceeds the inspiration: the adversarial verifier is a different model *family* (Codex), not merely a different context. -## 2. Decisions (all 2026-08-30, Daniel) +## 2. Decisions (Daniel; 2026-08-30 unless dated otherwise) 1. **Orchestrator lives hybrid.** The orchestrator is kit prompts (a skill / agent definition any session can load — the product stays prompts). Only the @@ -97,7 +97,9 @@ the inspiration: the adversarial verifier is a different model *family* spot-check of the card (a Freigabe that regularly needs deep thought means the card is missing a dimension → labeled example, tighten intake). The knob: per story → batch → wave ("Go" on the rendered wave plan) → standing - auto-Freigabe (notified, not asked). Three things hold on every rung: flags + auto-Freigabe (notified, not asked; the standing rule sets the status + *freigegeben* itself, so decision 4 holds — the clock still pulls only + *freigegeben*). Three things hold on every rung: flags always escalate (high profile, architecture ⚠, rückfragen, scope doubt); the knob is human-owned and committed, changed only by defined operation (like `lanes`); and the plan is rendered on every rung — as a question or @@ -117,6 +119,19 @@ the inspiration: the adversarial verifier is a different model *family* merges at 100% as the starting value, knob downward with evidence — bounded, because architecture changes batch into one meta-story per wave (§9). +9. **Acceptance criteria are read-only inside a lane** (2026-08-31). A + builder never edits the acceptance criteria of the story it builds — that + is the reward-hacking vector the inspiration warns about. An acceptance + criterion found wrong during build is a story mutation, not a spec edit: + the lane stops, the story returns to the pool flagged "AC change needed", + the human decides (an exception path, so O(exceptions)), and the story + re-enters classification. Descriptive spec details a fix changes are + still updated in the same commit (CLAUDE.md §5 stands); every spec change + is captured as spec-delta (§10, step 2) and shown to the reviewer beside + the diff, so baseline and change are both visible. Mechanically: + acceptance criteria carry IDs (§10 second sweep), the AC block's + fingerprint is compared at Gate B, and a changed block without the pool + round-trip is a Blocker. ## 3. Maturity ladder @@ -125,7 +140,7 @@ the inspiration: the adversarial verifier is a different model *family* | 1 | Turn-based — skills with self-checks, red-first tests | shipped | | 2 | Goal-based — acceptance criteria as target, gates loop to clean with mandatory stops | shipped | | 3 | Time-based — clock loops: poll the pool, drift audits, PR-bot processing | missing | -| 4 | Proactive — event-triggered: a spec turns "freigegeben" and the factory runs | missing | +| 4 | Proactive — event-triggered: first instance is the intake zone (a story appearing triggers classification, decision 6); later a story turning "freigegeben" wakes the orchestrator without waiting for the tick | missing | ## 4. Conventions and roadmap — where each truth lives @@ -225,6 +240,8 @@ proceeds, the breaking part waits on the meta-story). absence. - No autonomy expansion ahead of the measured evidence (P8) that the review economics support it. +- No deployment stage. The factory ends at the merge; what follows (release + tags, environments, direct deploy) is the project's own CI. ## 9. Parallelism and flow control (decisions 2026-08-30, Daniel) @@ -319,6 +336,68 @@ contaminate implementation branches; run artifacts need a retention stance from day one (their P0 gap: unbounded disk growth); absent real-time cost visibility is what makes a token furnace invisible (their P0 gap, our P8). +**Second sweep (2026-08-31), against their full docs and the inspiration +video; the gap lists live in `docs/research/2026-08-30-*-gap-sweep.md`.** +Adopted, with owning step: +- Mechanical write protection for pool status, the `lanes` knob and the + architecture section of AGENTS.md (their `protected_paths` / + `denied_commands`). This would be the kit's first *blocking* hook — + defensible only because it depends on nothing external, unlike the + advisory gate hook (invariant 1); the story must argue that explicitly. + [step 4/5] +- A run lock per orchestrator tick with stale-lock cleanup by the judge, so + a double-firing clock never runs two orchestrators. [step 5] +- Usage-limit hold/resume: a hit rate limit pauses the run with a countdown + and resumes — never counted as a failure (their v0.18 lesson). [step 5] +- Model strength per role as a project knob (cheap builder, strong + reviewer), with one asymmetry rule: the reviewer is never weaker than the + builder — cross-model stays. [step 1/3] +- Minimum review cost/duration as an INCOMPLETE signal: a pass under the + floor does not count toward the pass floor. No new gate — the kit's + existing INCOMPLETE semantics. [step 2, P8] +- Audit rejection flows back automatically: the human's "changes requested" + returns to the lane as an artifact (like a red smoke), and a PR once + rejected by the human is always re-audited by the human, never re-drawn. + [step 6] +- Environment preflight before every tick (`doctor`: required env, host + services) — a clock session on a broken environment only burns tokens. + [step 5] +- Phase 0 on an existing codebase also *reports* agent-hostile patterns + (codegen magic, implicit behaviour) — report only, never fixes. [Phase 0 / + `workflow-init`] +- Wave close renders an as-built view (what was actually built, grounded in + code) — a generated view like the roadmap, never maintained; the standing + defence against docs drift. [step 4/5] +- From the video: a hard round cap per Bau-Loop and a token budget per + story as preventive limits — reaching one is a mandatory stop, never a + silent kill; the caps apply to build loops, not to review loops, which + keep their floor and tells. [step 3/5] Acceptance criteria with IDs in + given/when/then form and a per-AC test report as a handoff artifact (the + machine-checkable goal condition; also what decision 9's fingerprint + protects). [step 1/4] Reverse traceability (code with no spec behind it) + as drift-audit content, report only. [step 5] Every clock loop starts + report-only; fix permission is a maturity knob per loop. [step 5] + Deployment is out of scope (§8). + +Redirected: their merge coordinator is our merge-queue station (§9); their +`report` (executive summary over the analytics window) belongs to the +dashboard topic (§11); their harness-as-router (CLAUDE.md compressed to a +20-line signpost) becomes a kit story after P8 measures harness tokens per +session — with fresh context per station, every story pays the harness +size times its stations. + +Not adopted: the rollup branch (issue PRs into a wave branch, one rollup PR +to main). It is a part of their stop-the-world milestone model, already +rejected: the merge-queue smoke and the wave-close E2E give the same +integration checkpoint, the as-built view gives the wave-level reading, and +a wave branch would break the kit's merge-base-with-main semantics and make +hotfixes a two-way merge — the opposite of flexible shortcuts (§11). A +project that needs "main = whole waves only" solves that with release tags +in its own CI (§8). Also not adopted, noted as a lesson only: hard byte +caps on verify output and PR diffs in prompts (unbounded diffs blow context +and cost). Irrelevant here: per-module monorepo batteries, LangGraph as a +framework, knowledge-graph disambiguation. + ## 11. Open questions (owned by the stories that will answer them) - Sample percentage and drawing rule for the Sample-Gate (step 6). From 8aedc0b08c06ce348c20b84978e2fd3519662e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 15:55:05 +0200 Subject: [PATCH 103/117] =?UTF-8?q?docs(vision):=20Gate=20A=20pass=201=20?= =?UTF-8?q?=E2=80=94=20bound=20the=20merge-queue=20claim,=20correct=20two?= =?UTF-8?q?=20stale=20claims=20about=20shipped=20kit=20behaviour,=20fix=20?= =?UTF-8?q?pipeline=20terminus=20and=20cross-references?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes 15 of 52 pass-1 findings that sit inside the document's own logic: the pipeline ended at Merge/Deploy against non-goal 5; the merge queue claimed 'main is never red' (the gate-overclaim class AGENTS.md forbids) and no human involvement one build step before decision 5 allows it; the classification card claimed the profile drives the pass floor, which shipped CLAUDE.md does not do; the second-sweep citation pointed at git-ignored files; stage labels put event loops in the stage-3 step; and five cross-references or counts did not resolve. Decision text is untouched — findings against decisions 1, 2, 5, 7 and 8 are routed to the author with the loop paused. --- .../specs/2026-08-30-dark-factory-vision.md | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index d710725..85bf906 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -33,14 +33,21 @@ Story-Pool — filling is always consequence-free [missing] → Bau-Loop (executing-plans, goal-based, TDD) [exists] → Verify (battery + Gate B — adversarial model) [exists] → Sample-Gate (draws x% of PRs for human audit) [missing] - → Audit (human, sampled) [missing] + ↳ drawn → Audit (human, sampled) [missing] + ↳ not drawn → straight on (the ordinary path) [missing] → Merge-Queue (serial: rebase onto main → smoke on the candidate → green lands, red returns to the lane) [missing] - → Merge/Deploy [missing] + → Merge — the factory ends here; release and deploy + are the project's own CI (§8) [missing] + + ⟳ E2E-Loop (clock: nightly or at wave close; a failure + is filed back into the Story-Pool, §9) [missing] ``` -Every node is a loop with its own fresh context; only the artifact crosses an -edge (story, spec, plan, diff, PR) — never the intermediate steps. A reviewer +Every *model-operated* node is a loop with its own fresh context — the pool is +a store, Freigabe and Audit are human, the merge queue is a mechanical +operation. Only the artifact crosses an edge (story, spec, plan, diff, PR) — +never the intermediate steps. A reviewer therefore judges only the result, never the process: separate eyes need separate heads, and separate heads come from separate context. Everything that matters must be *in* the artifact; process facts reach a reviewer only reified @@ -49,7 +56,10 @@ exception is the judge/watchdog: it watches process signals (idle, thrash, no progress) and judges only liveness, never quality. Mandatory stops, scope questions and architecture re-evaluations escalate to the human; the factory stops and reports instead of spinning. A human override becomes a -labeled example for tightening the rules. +labeled example for tightening the rules — and it moves only knobs the human +already owns: it never waives a mandatory stop, a gate obligation, profile +evidence or an AGENTS.md invariant (CLAUDE.md §5: such a record "supplies no +permission"). The kit already holds the middle of this pipeline, and in one respect exceeds the inspiration: the adversarial verifier is a different model *family* @@ -104,7 +114,7 @@ the inspiration: the adversarial verifier is a different model *family* the knob is human-owned and committed, changed only by defined operation (like `lanes`); and the plan is rendered on every rung — as a question or as a notice. Raising the rung follows measured P8 evidence, never precedes - it (non-goal 4). + it (§8, "No autonomy expansion ahead of the measured evidence"). 8. **Four-eyes principle, with a scaling guard.** No artifact passes only its author. The second pair of eyes is another model by default; in the availability emergency (reviewer down, tokens exhausted) the same model as @@ -177,11 +187,15 @@ the inspiration: the adversarial verifier is a different model *family* ## 5. The classification card Every new pool item is classified before anything else. Dimensions 1–4 exist -in the intake skill today; 5–7 are new: +in the intake skill today; 5–8 are new: 1. **Size** — story, or epic that must be split. -2. **Risk/security profile** — drives floor, lenses, evidence mode. -3. **Completeness** — too thin → one question back to the human; nothing is +2. **Risk/security profile** — drives lenses and evidence mode today; it + drives the *pass floor* only once build step 1 lands (shipped CLAUDE.md + has a fixed 3-pass floor, and lenses are different questions, not more + passes). +3. **Completeness** — too thin → one question round back to the human (several + targeted questions in it, as the shipped intake skill does); nothing is invented. 4. **Invariant touch-list** — which AGENTS.md invariants the item affects. 5. **Scope verdict** — inside project goals? Duplicate of a pooled or in-flight @@ -203,7 +217,7 @@ proceeds, the breaking part waits on the meta-story). 1. Story pool with status — the factory's trigger. *(missing)* 2. Classification station beyond intake's current card (scope, architecture, - dependency dimensions). *(missing)* + dependency and wave dimensions). *(missing)* 3. Architecture tree + Bewertungs-Loop in AGENTS.md. *(missing)* 4. Orchestrator as product — the sparring-session practice codified as skill/agent: question routing, artifact handoff, prediction ledger, batched @@ -224,12 +238,16 @@ proceeds, the breaking part waits on the meta-story). 2. Loop-rule consolidation (successor story) + P8 passive metrics — measure before automating further. 3. Orchestrator story (codify the role as skill/agent per decision 1). -4. Story pool + status + classification + architecture tree (decisions 2 and 4). -5. Stage 3: clock and event loops (poll, audit, PR processing) — including the +4. Story pool + status + classification + architecture tree (decisions 2 and + 4) — including the proactive Intake-Loop, which is the factory's first + stage-4 loop (decision 6), so stage 4 starts here rather than at step 6. +5. Stage 3: clock loops (poll, drift audits, PR processing) — including the E2E clock loop (failures auto-filed as pool stories) and the merge-queue station (rebase + smoke on the candidate), see §9. -6. Stage 4 + sampled audit (decision 5) — full automation first only for - level-0 stories; merge stays human until this step ships and holds. +6. Stage 4 for the orchestrator wake (a story turning *freigegeben* wakes it + without waiting for the tick) + sampled audit (decision 5) — full + automation first only for level-0 stories; merge stays human until this + step ships and holds. ## 8. Non-goals @@ -283,8 +301,13 @@ proceeds, the breaking part waits on the meta-story). nightly or at wave close, stage 3). The merge queue is a station of its own: serial, per candidate rebase onto current main → smoke on the composed candidate → green lands, red returns to the lane as an artifact (like Gate-B - findings) while the queue continues with the next branch — main is never - red and no human is involved; repeated failure escalates via the judge. + findings) while the queue continues with the next branch; repeated failure + escalates via the judge. The bounded claim: the queue never lands a + candidate whose configured smoke command fails. That is not a claim about + main's full-system health — only the E2E layer speaks to that, and it can + still find a failure on already-merged main. Landing is automatic from + build step 6 onward; until then the green outcome is a human merge + (decision 5). This closes the parallelism blind spot (disjoint lanes each green, their composition broken) and owns the merge-coordinator mechanics (rebase, retry). An E2E failure becomes a pool story automatically, classified by @@ -326,7 +349,8 @@ define-conventions↔Phase 0, watch↔clock loops, needs-human-review↔escalati review gates weaken as retries mount; the inverse of the clean-final-pass rule); same-model review (their implementer and reviewers share one model family — correlated blind spots; our adversarial gate stays cross-model); -daemon/Docker/GitHub as hard requirements (non-goal 1 stands); stop-the-world +daemon/Docker/GitHub as hard requirements (§8, "No daemon or server-side +runner", stands); stop-the-world sequential milestones (branch-scoped locks and the lanes budget replace it). **Their documented scars, kept as constraints here:** manual state mutation @@ -337,7 +361,9 @@ from day one (their P0 gap: unbounded disk growth); absent real-time cost visibility is what makes a token furnace invisible (their P0 gap, our P8). **Second sweep (2026-08-31), against their full docs and the inspiration -video; the gap lists live in `docs/research/2026-08-30-*-gap-sweep.md`.** +video.** The two sweep files (`docs/research/2026-08-30-*-gap-sweep.md`) are +local working notes under a git-ignored directory: they do not survive a +clone, so the durable record is the list below, not those paths. Adopted, with owning step: - Mechanical write protection for pool status, the `lanes` knob and the architecture section of AGENTS.md (their `protected_paths` / From 2121b25aa919c3a4b915b5b9cbc8f9dddbf37f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:03:07 +0200 Subject: [PATCH 104/117] =?UTF-8?q?docs(vision):=20Gate=20A=20pass=201=20r?= =?UTF-8?q?ound=202=20=E2=80=94=20close=20both=20blockers,=20split=20the?= =?UTF-8?q?=20two=20epic=20build=20steps,=20record=2021=20unclosed=20gaps?= =?UTF-8?q?=20with=20owners?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blocker 1: decision 8 claimed a shipped two-tier reviewer fallback with a same-family tier 2. The kit ships the opposite rule ('be gateless — not self-reviewed'), and that shape was formally closed with a negative answer after three design cycles, nine Gate-A passes and 303 findings. Decision 8 now states the gateless rule and says reopening it would be its own story. Blocker 2: the proposed mechanical write protection would be the kit's first blocking hook, which invariant 1 forbids. Arguing it depends on nothing external is an argument for amending the invariant, not an exemption from it, so an invariant-1 amendment via an architecture meta-story is now its named prerequisite, with a repo-owned command or CI check as the interim route. Build path: steps 4 and 5 were each an epic that the document's own split rule would reject. Split into 4a-4e and 5a-5d behind named interfaces, which also gives owners to two mechanisms nothing owned before: decision 7's Freigabe and wave control (4d), and decision 9's AC read-only enforcement (4e), ordered before any autonomous lane execution. Open questions: 21 gaps the document does not close are now named in §11 with the step that owns each, rather than specified here. Also reconciled four cross-decision contradictions: tree v1's source on an existing codebase (decision 2 vs §9), the stage-4 event wake against decision 1's platform boundary, the O(waves + exceptions) bound as an end-state target rather than a rule the bootstrap already obeys, and automatic wave opening as a production trigger that needs a standing rule. --- .../specs/2026-08-30-dark-factory-vision.md | 168 +++++++++++++++--- 1 file changed, 145 insertions(+), 23 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index 85bf906..564cdb1 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -70,12 +70,18 @@ the inspiration: the adversarial verifier is a different model *family* 1. **Orchestrator lives hybrid.** The orchestrator is kit prompts (a skill / agent definition any session can load — the product stays prompts). Only the *clock* uses platform mechanics: a scheduled/loop wake-up starts an - orchestrator session. No daemon, no server-side infrastructure. + orchestrator session. No daemon, no server-side infrastructure. The stage-4 + event wake (§3) is another adapter under this same boundary — a platform + trigger that *starts* a session, never a resident listener; where a platform + offers no such trigger, the event only shortens the next scheduled tick. 2. **Project truth is AGENTS.md, and the architecture tree is subordinate to - the pool.** The architecture is built *from* the pool initially and - continuously re-evaluated against it (Bewertungs-Loop), versioned, living in - AGENTS.md beside the invariants and conventions. No second architecture - document that could drift. + the pool.** On a green field the architecture is built *from* the pool + initially; on an existing codebase tree v1 is read from the code instead + (§9). Where the two disagree, the code is the fact and the pool the intent + — reconciling them is Phase 0 work rather than a silent choice. Either way + the tree is continuously re-evaluated against the pool (Bewertungs-Loop), + versioned, living in AGENTS.md beside the invariants and conventions. No + second architecture document that could drift. 3. **Architecture re-evaluation is a meta-story through the same factory.** When a story breaks the tree, classification produces a story "extend the architecture for X" with a high risk profile (heavy review, human in the @@ -116,12 +122,21 @@ the inspiration: the adversarial verifier is a different model *family* as a notice. Raising the rung follows measured P8 evidence, never precedes it (§8, "No autonomy expansion ahead of the measured evidence"). 8. **Four-eyes principle, with a scaling guard.** No artifact passes only its - author. The second pair of eyes is another model by default; in the - availability emergency (reviewer down, tokens exhausted) the same model as - a **different agent with fresh context** — never the same agent (the kit's - shipped two-tier reviewer fallback embodies this). Human eyes only where - the frequency is bounded — O(waves + exceptions), never O(stories) — and - every mandatory human touchpoint carries a maturity knob that lowers with + author. The second pair of eyes is another model *family*; where none is + available the honest answer is to be **gateless and say so** — never + self-reviewed, and not a same-family agent either. That question is closed + with a negative answer and this vision does not reopen it: the same-family + tier-2 reviewer was designed three times across nine Gate-A passes and 303 + findings and failed structurally + (`docs/superpowers/specs/2026-08-14-reviewer-availability-fallback-design.md`), + and what ships is "be gateless — not self-reviewed" + (`docs/coding-workflow.md`, `/workflow-init`). Reopening it would be its own + story. Human eyes only where the frequency is bounded — O(waves + + exceptions), never O(stories) — **as the end state**, which the rollout + deliberately does not satisfy yet: decision 7's lower rungs are per-story + and decision 5 keeps merge human until step 6. The bound is the target the + knobs move toward, not a rule the bootstrap already obeys. Every mandatory + human touchpoint carries a maturity knob that lowers with P8 evidence: presence is a dial that falls with trust, never a ratchet. Two concrete rules: an architecture merge triggers re-classification of the cards on the touched branches (their architecture verdicts are stale — @@ -179,8 +194,10 @@ the inspiration: the adversarial verifier is a different model *family* subareas develop together first, those later" decision, usually aligned with tree branches but not required to be. The orchestrator pulls only from the active wave (a focus throttle beside the lanes budget: lanes = how much at - once, wave = what at all). Opening the next wave is the human's call (or - automatic when the prior wave is fully merged — settled by build step 4). + once, wave = what at all). Opening the next wave is the human's call; it + becomes automatic on prior-wave completion only where the human has raised + decision 7's knob to a standing rule for it, because an unasked wave opening + is a production trigger like any other (settled by build step 4). Later stories get their wave mark at classification. - **The kit's own roadmap** to this vision is §7 of this document. @@ -238,12 +255,34 @@ proceeds, the breaking part waits on the meta-story). 2. Loop-rule consolidation (successor story) + P8 passive metrics — measure before automating further. 3. Orchestrator story (codify the role as skill/agent per decision 1). -4. Story pool + status + classification + architecture tree (decisions 2 and - 4) — including the proactive Intake-Loop, which is the factory's first - stage-4 loop (decision 6), so stage 4 starts here rather than at step 6. -5. Stage 3: clock loops (poll, drift audits, PR processing) — including the - E2E clock loop (failures auto-filed as pool stories) and the merge-queue - station (rebase + smoke on the candidate), see §9. +4. **Story pool and classification** (decisions 2 and 4). This is an epic, so + §5's own split rule applies to it; the ordered stories, behind named + interfaces: + - **4a** pool storage, item identity, and the status state machine — every + state, its authorizing operation, its precondition and its terminal or + resumable outcome. + - **4b** classification: the eight card dimensions, the verdicts, the split + and dependency rules — including the proactive Intake-Loop, which is the + factory's first stage-4 loop (decision 6), so stage 4 starts here rather + than at step 6. + - **4c** architecture bootstrap: Phase 0 and tree v1 (§9), plus the + machine-readable projection generated from AGENTS.md. + - **4d** Freigabe and wave control: the rendered wave plan, the granularity + knob and its standing rules, wave opening and closing (decision 7). No + other step owned this, and the pipeline cannot run without it. + - **4e** decision 9's mechanics: acceptance-criterion IDs, the read-only + rule inside a lane, the AC-block comparison at Gate B and the pool + round-trip. Ordered **before any autonomous lane execution** — it is the + reward-hacking guard, and a build path that ships lanes without it looks + complete while the guard is missing. +5. **Stage 3: clock loops** (poll, drift audits, PR processing). Also an epic; + its stories are separate failure domains behind one shared contract: + - **5a** the clock-loop contract itself: tick, run lock, environment + preflight, usage-limit hold/resume, the report-only default, and the + branch-claim and lane-budget rules the scheduler needs. + - **5b** the merge-queue station (rebase + smoke on the candidate, §9). + - **5c** the E2E clock loop (failures auto-filed as pool stories, §9). + - **5d** drift audits and PR-bot processing. 6. Stage 4 for the orchestrator wake (a story turning *freigegeben* wakes it without waiting for the tick) + sampled audit (decision 5) — full automation first only for level-0 stories; merge stays human until this @@ -367,10 +406,14 @@ clone, so the durable record is the list below, not those paths. Adopted, with owning step: - Mechanical write protection for pool status, the `lanes` knob and the architecture section of AGENTS.md (their `protected_paths` / - `denied_commands`). This would be the kit's first *blocking* hook — - defensible only because it depends on nothing external, unlike the - advisory gate hook (invariant 1); the story must argue that explicitly. - [step 4/5] + `denied_commands`). This would be the kit's first *blocking* hook, and + invariant 1 ("the hook always exits 0") forbids one. Arguing that a + local-state check carries none of the external dependency invariant 1 names + is an argument *for amending* it, not an exemption from it — so this item's + prerequisite is an **invariant-1 amendment through an architecture + meta-story** (decision 3). Until that lands, enforcement lives outside the + hook: a repo-owned command or a required CI check. + [prerequisite meta-story, then step 4/5] - A run lock per orchestrator tick with stale-lock cleanup by the judge, so a double-firing clock never runs two orchestrators. [step 5] - Usage-limit hold/resume: a hit rate limit pauses the run with a countdown @@ -440,3 +483,82 @@ framework, knowledge-graph disambiguation. - Test layers are decided (§9); still open: whether a smoke failure that returns to a lane is surfaced to the human (a dashboard question), and the E2E cadence / flaky-handling rules — step 5. + +**Recorded by Gate A pass 1 (2026-08-31).** Gaps this document does not close, +each with the step that owns it. They are named rather than specified: a +decomposition document that invented them would be taking design decisions +nobody took, and a gap named here cannot be silently invented later by whoever +writes the story. + +- *Pool and state* — cycles, self-dependencies and dangling targets; what a + dependency becomes when its target is split or rejected; the atomic + transition when the last dependency clears; which terminal statuses count as + resolved for wave closure and where carried-over items go; Phase 0's + empty-pool outcome; and whether an external board can satisfy the committed, + history-bearing, compare-and-set assumptions the `lanes` knob makes. [4a] +- *Intake concurrency* — stable item IDs and idempotent classification writes, + so a re-delivered or overlapping debounce batch cannot produce a second card + or a second meta-story. [4b] +- *The classification transaction* — the complete set of writes one + classification may make (card, status, meta-story, dependency link) and its + atomicity. Decision 6's "attaches cards only" and decision 3's meta-story + creation are the same act, so the seam guard needs the full list. [4b] +- *Naming* — the verdict `freigeben` and the status `freigegeben` are one + letter apart, and the first must never produce the second. The classification + outcome needs a non-authorizing name. [4b] +- *Architecture staleness* — an architecture merge makes verdicts stale, but + nothing yet revokes an already-*freigegeben* card or stops a running lane + whose verdict aged out mid-build. Binding cards and lanes to a tree version + is the candidate. [4c] +- *Projection freshness* — what the generated projection does on a stale + digest, a parse failure, or a detected cycle. [4c] +- *Approval binding* — the wave plan the human approves is recomputed every + tick from mutable inputs, so the clock can execute a materially different + plan than the one approved. Binding an approval to an input digest and + re-rendering when it moves is the candidate. [4d] +- *The decision-queue throttle's `N`* — a committed knob with a default, a + range and an exact comparison, distinct from `lanes`. [5a] +- *Branch-claim semantics* — "disjoint branches" is undefined for + ancestor/descendant overlap, shared roots, multi-branch stories, and a lane + whose touched set grows during the build. [5a] +- *Orchestrator exclusion* — stale-lock cleanup is not exclusion: a live owner + paused in a usage-limit hold is exactly what a staleness heuristic + misreads. A lease with a heartbeat and a fencing token that every mutating + write checks is the candidate. [5a] +- *Merge-queue terminal paths* — rebase conflict, unclean worktree, a branch + deleted while queued, a retry invalidated by newer main: each needs its + artifact, its return transition and a retry cap. [5b] +- *E2E attribution* — a nightly or wave-close run covers many merges, so "the + trace ID of the suspect merge" is not derivable from it. Carrying the tested + merge range and marking attribution unknown unless deterministically + isolated is the candidate. [5c] +- *Telemetry gaps* — analytics writes are non-fatal, yet review cost and + duration decide whether a pass counts. A missing or unattributable gate + measurement must read as INCOMPLETE, never as a cheap pass. [step 2] +- *Live cost visibility* — post-run analytics detect overspend after it + happened, which is not the control §10 keeps from their P0 gap. Live + counters, alerts and stop thresholds are a different mechanism from P8's + passive record. [step 2 / dashboard] +- *Retention* — run artifacts need a retention, compaction and deletion stance + from the first story that writes them; their P0 gap was exactly this. [step 2] +- *Planning-artifact home* — where plans and specs live so they never + contaminate an implementation branch, and which of them are tracked. [step 3] +- *Model strength order* — "the reviewer is never weaker than the builder" + needs a project-owned order across families and a fail-closed path for an + unknown or newly released model. [step 1/3] +- *The triviality gap in decision 5* — "no merge skips both gates" does not + hold today: the shipped Gate-B triviality skip plus an undrawn Sample-Gate + leaves a change with neither. Whether the end state removes that skip, or + makes every Gate-B-skipped change a mandatory draw, is open. [step 6] +- *Audit binding* — a draw and an audit verdict are not bound to the bytes + reviewed, so a later fix, rebase or Gate-B round can inherit an audit of + something else. Which mutations force resampling and re-audit is open. [step 6] +- *Autonomy downgrade* — every knob lowers human presence on good evidence and + nothing raises it back on bad (audit rejection, repeated smoke failure, + deteriorating metrics). Triggers, authority, hysteresis and the immediate + safe state are unowned; without them "a dial, never a ratchet" is only half + true. [step 6] +- *Meta-story batching versus the split rule* — one architecture meta-story per + wave can combine independent subsystems and mixed profiles, which §5 says + must split. Batching by compatible branch and profile group is the candidate, + and the O(waves) bound then counts batches rather than waves. [4c / step 6] From d479e833b12a9d5dc5550029d5f00e6aef151794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 16:28:27 +0200 Subject: [PATCH 105/117] =?UTF-8?q?docs(vision):=20Gate=20A=20pass=202=20?= =?UTF-8?q?=E2=80=94=20reviewer=20availability=20gates=20automatic=20merge?= =?UTF-8?q?,=20five=20missing=20stations,=20three=20more=20epic=20splits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blocker: the pass-1 gateless rule plus automatic merge left a path where an author-only candidate lands during a reviewer outage. Decision 5 now makes reviewer availability an input to merge authorization. Six pre-existing contradictions resolved: Freigabe described as human-only while a standing rule may write it; a maturity knob promised at every mandatory touchpoint against the four §8 protects; unbounded meta-story recursion; 'churn blocks branches, never the factory' against a global mandatory-stop throttle; 'every clock loop starts report-only' against the E2E loop filing a pool story; and 'filling the pool is always consequence-free', which is the totality overclaim AGENTS.md names. Six of this pass's findings corrected pass-1 corrections and were absorbed, including one of my own overclaims: the decision-8 rewrite said the same-family tier-2 reviewer was designed three times, where the record says three fallback designs failed and only the first had that shape. The pipeline gained five stations it named elsewhere but never drew: PR and bot processing, the Bewertungs-Loop, the drift audit, the vet preflight and the judge sidecar. Steps 2 and 6 split into leaves, which keeps the tracked P8 story read-only over the ledger and git — the vision's analytics, tracing, spec-delta capture and live cost counters move to a new 2c, and five stale references were repointed. Eight further gaps are recorded in §11 with owners. --- .../specs/2026-08-30-dark-factory-vision.md | 210 ++++++++++++++---- 1 file changed, 163 insertions(+), 47 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index 564cdb1..628fc52 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -1,7 +1,9 @@ # Dark Factory — target vision and decomposition **Date:** 2026-08-30 · **Kind:** decomposition document, not an implementation spec. -Each build step below becomes its own story through the normal workflow +Each *leaf* build item below becomes its own story through the normal workflow +— the numbered entries in §7 are ordering groups, and where one carries +lettered leaves (2a, 4a…, 5a…, 6a…) those leaves are the story owners (intake → spec → Gate A → plan → Gate A → execute → Gate B). Nothing in this document is executable on its own, and nothing here overrides a shipped rule. @@ -18,20 +20,26 @@ merged, reviewed software with the human concentrated where human judgement measurably matters — and, at full maturity, sampled rather than omnipresent: ``` -Story-Pool — filling is always consequence-free [missing] +Story-Pool — filling never triggers production [missing] → Intake-Loop (PROACTIVE: a new story appearing triggers classification + architecture verdict, debounced/batched; attaches the card, triggers NO production) [missing] ↳ reads the Architektur (AGENTS.md) [exists, needs one extension] - → Freigabe (human — the ONLY production trigger; - reads a rendered wave plan, granularity knob: - decision 7) [human] + ⟳ Bewertungs-Loop (continuously re-evaluates the tree + against the pool: produces the meta-stories of + decision 3, and re-classifies the cards an + architecture merge made stale) [missing] + → Freigabe (the ONLY production trigger; the human, or + a standing rule the human installed — decision 7; + reads a rendered wave plan) [human] → Takt-Loop (polls freigegebene stories of the active wave → wakes the orchestrator → a lane opens) [missing] → Spec-Loop (intake + design + Gate A) [exists] → Plan-Loop (writing-plans + Gate A) [exists] → Bau-Loop (executing-plans, goal-based, TDD) [exists] → Verify (battery + Gate B — adversarial model) [exists] + → PR (open it; wait for the routed review bots and + process their findings — docs/pr-review-bots.md) [exists] → Sample-Gate (draws x% of PRs for human audit) [missing] ↳ drawn → Audit (human, sampled) [missing] ↳ not drawn → straight on (the ordinary path) [missing] @@ -42,6 +50,13 @@ Story-Pool — filling is always consequence-free [missing] ⟳ E2E-Loop (clock: nightly or at wave close; a failure is filed back into the Story-Pool, §9) [missing] + ⟳ Drift-Audit (clock: docs drift, reverse traceability; + report-only, findings land in the Story-Pool) [missing] + + ┄ Vet preflight — mechanical checks run before every + model-operated node above (§10) [missing] + ┄ Judge/watchdog — sidecar over the model loops and + the queue; liveness only, escalates to the human [missing] ``` Every *model-operated* node is a loop with its own fresh context — the pool is @@ -72,35 +87,53 @@ the inspiration: the adversarial verifier is a different model *family* *clock* uses platform mechanics: a scheduled/loop wake-up starts an orchestrator session. No daemon, no server-side infrastructure. The stage-4 event wake (§3) is another adapter under this same boundary — a platform - trigger that *starts* a session, never a resident listener; where a platform - offers no such trigger, the event only shortens the next scheduled tick. + trigger that *starts* a session, never a resident listener. Where a platform + offers no such trigger there is no stage-4 wake at all: the story waits for + the next scheduled tick. That is stage-3 polling at the configured latency + and is named as such rather than counted as proactive. 2. **Project truth is AGENTS.md, and the architecture tree is subordinate to - the pool.** On a green field the architecture is built *from* the pool - initially; on an existing codebase tree v1 is read from the code instead - (§9). Where the two disagree, the code is the fact and the pool the intent - — reconciling them is Phase 0 work rather than a silent choice. Either way + the pool.** Three things can disagree and stay distinct: the **approved + tree** in AGENTS.md (normative — what classification reads), the **code** + (observed fact), and the **pool** (intent). On a green field the approved + tree is built *from* the pool; on an existing codebase tree v1 is read from + the code (§9). Phase 0's job is to make the approved tree agree with the + code before it becomes authoritative. Afterwards a code/tree divergence is + drift that the audit reports, and a pool/tree divergence is what produces a + meta-story (decision 3) — neither is resolved silently. Either way the tree is continuously re-evaluated against the pool (Bewertungs-Loop), versioned, living in AGENTS.md beside the invariants and conventions. No second architecture document that could drift. 3. **Architecture re-evaluation is a meta-story through the same factory.** When a story breaks the tree, classification produces a story "extend the architecture for X" with a high risk profile (heavy review, human in the - loop); the triggering story waits on it. One process for everything — the - factory rebuilds itself the same way it builds features. + loop); the triggering story waits on it. A meta-story *is* the architecture + amendment, so it is classified as one and never produces a further + meta-story — that is what stops the recursion — while keeping the branch + lock and the human escalation its high profile earns. One process for + everything — the factory rebuilds itself the same way it builds features. 4. **Classification is the mandatory first station.** Every new pool item gets the card (§5 below) before anything else; only "freigegeben" is pulled by the clock. 5. **End state is sampled audit, not per-merge approval.** The adversarial gate checks every merge; the human audits a sample. No merge skips both - gates. (Until step 6 of the build path matures, merge remains human.) -6. **Filling the pool is always consequence-free; classification is a - proactive loop of its own.** A new story's appearance (debounced into + gates — so **reviewer availability is an input to merge authorization**: + when no independent model family is available the cycle is gateless + (decision 8), and a gateless candidate never lands automatically. It waits, + or it goes to human audit; automatic landing resumes when an independent + reviewer does. (Until step 6 of the build path matures, merge remains human + anyway.) +6. **Filling the pool never triggers production; classification is a + proactive loop of its own.** (It is not free of *all* consequence — it + spends tokens, writes a card and a status, may create a meta-story and may + queue a question. What it cannot do is open a lane or merge code.) A new story's appearance (debounced into mini-batches) triggers exactly one thing: the Intake-Loop attaches the classification card and the architecture verdict. Production starts only through the human's Freigabe plus the clock. Two rules guard the seam: the Intake-Loop only attaches cards, status changes only through defined - operations (human: freigeben; loop: klassifiziert) — never free-form edits - by both writers on one field; and in Phase 0 the architecture verdict + operations (human: freigeben; loop: klassifiziert; a standing rule: + freigeben, carrying the committed rule as its authority and leaving its own + audit record — decision 7) — never free-form edits by several writers on + one field; and in Phase 0 the architecture verdict waits until tree v1 exists. The intake zone (pool, Intake-Loop, Architektur) is the factory's first stage-4 proactive loop — at the front of the pipeline, not the end. "Unclassified" is a transient marker, never a @@ -125,9 +158,10 @@ the inspiration: the adversarial verifier is a different model *family* author. The second pair of eyes is another model *family*; where none is available the honest answer is to be **gateless and say so** — never self-reviewed, and not a same-family agent either. That question is closed - with a negative answer and this vision does not reopen it: the same-family - tier-2 reviewer was designed three times across nine Gate-A passes and 303 - findings and failed structurally + with a negative answer and this vision does not reopen it: three fallback + designs across nine Gate-A passes and 303 findings all failed + structurally — the first of them being exactly this same-family tier-2 + shape — (`docs/superpowers/specs/2026-08-14-reviewer-availability-fallback-design.md`), and what ships is "be gateless — not self-reviewed" (`docs/coding-workflow.md`, `/workflow-init`). Reopening it would be its own @@ -137,7 +171,12 @@ the inspiration: the adversarial verifier is a different model *family* and decision 5 keeps merge human until step 6. The bound is the target the knobs move toward, not a rule the bootstrap already obeys. Every mandatory human touchpoint carries a maturity knob that lowers with - P8 evidence: presence is a dial that falls with trust, never a ratchet. + P8 evidence — but only at *routine* touchpoints: Freigabe granularity, + sample rates, wave opening. The four paths §8 protects (mandatory stops, + profile confirmations, scope changes, architecture meta-stories) carry no + such knob and stay human at every rung. Where a knob does exist, presence + is a dial that falls with trust and rises again on adverse evidence, never + a ratchet. Two concrete rules: an architecture merge triggers re-classification of the cards on the touched branches (their architecture verdicts are stale — mechanical, no human involved); and the Sample-Gate draws architecture @@ -152,7 +191,7 @@ the inspiration: the adversarial verifier is a different model *family* the human decides (an exception path, so O(exceptions)), and the story re-enters classification. Descriptive spec details a fix changes are still updated in the same commit (CLAUDE.md §5 stands); every spec change - is captured as spec-delta (§10, step 2) and shown to the reviewer beside + is captured as spec-delta (§10, step 2c) and shown to the reviewer beside the diff, so baseline and change are both visible. Mechanically: acceptance criteria carry IDs (§10 second sweep), the AC block's fingerprint is compared at Gate B, and a changed block without the pool @@ -252,10 +291,23 @@ proceeds, the breaking part waits on the meta-story). 1. Finish the review-economics story (in flight) — floors by profile, severity calibration, measurable records. Without calibrated review economics every factory is a token furnace. -2. Loop-rule consolidation (successor story) + P8 passive metrics — measure - before automating further. -3. Orchestrator story (codify the role as skill/agent per decision 1). -4. **Story pool and classification** (decisions 2 and 4). This is an epic, so +2. **Measure before automating further** — three ordered leaves, not one + story: + - **2a** loop-rule consolidation (the successor story to step 1). + - **2b** the tracked P8 passive-metrics story + (`docs/superpowers/stories/2026-08-04-passive-metrics-over-the-ledger-story.md`), + which is **read-only over the ledger and git** and stays that way. This + vision does not widen it. + - **2c** a separate telemetry story for everything this vision adds that P8 + does not do: run analytics with cost and duration per step, a trace ID + carried through every stage artifact, mechanical spec-delta capture, and + live cost counters. That is new state and new instrumentation, so it + cannot ride inside 2b. +3. Orchestrator story (codify the role as skill/agent per decision 1) — the + role, the artifact handoff, and the *interface* of the tick execution plan. + The working dry-run reads the pool, the projection, waves, lanes and the + tick, so it lands after 4d and 5a rather than here (§4). +4. **Story pool and classification** (decisions 2, 4, 6, 7 and 9). An epic, so §5's own split rule applies to it; the ordered stories, behind named interfaces: - **4a** pool storage, item identity, and the status state machine — every @@ -280,13 +332,27 @@ proceeds, the breaking part waits on the meta-story). - **5a** the clock-loop contract itself: tick, run lock, environment preflight, usage-limit hold/resume, the report-only default, and the branch-claim and lane-budget rules the scheduler needs. - - **5b** the merge-queue station (rebase + smoke on the candidate, §9). + - **5b** the merge-queue station (rebase + smoke on the candidate, §9). It + is **not** a clock loop: it is a serial mechanical station triggered by + queue entry, so it takes the run-lock and artifact halves of 5a's + contract and none of the tick, usage-hold or report-only halves. - **5c** the E2E clock loop (failures auto-filed as pool stories, §9). - **5d** drift audits and PR-bot processing. -6. Stage 4 for the orchestrator wake (a story turning *freigegeben* wakes it - without waiting for the tick) + sampled audit (decision 5) — full - automation first only for level-0 stories; merge stays human until this - step ships and holds. +6. **Stage 4 and sampled audit** (decision 5) — the highest-risk step, so it + splits like the others: + - **6a** the stage-4 orchestrator wake: a story turning *freigegeben* wakes + the orchestrator without waiting for the tick. + - **6b** the Sample-Gate and the audit state: the drawing rule, the audit + verdict's binding to the candidate it reviewed, and the rejection path + back to the lane. + - **6c** graduated automatic merge: mechanical risk thresholds beneath the + semantic profile, starting with level-0 stories only. + - **6d** the promotion path past level 0 — the profile threshold at which + automation widens to standard and high stories, the evidence that moves + it, and its downgrade triggers. Without this leaf every other story can + finish while most work still waits for per-merge human approval, and the + end state is never reached. + Merge stays human until 6c ships and holds. ## 8. Non-goals @@ -333,7 +399,10 @@ proceeds, the breaking part waits on the meta-story). drain). Two automatic throttles on top: no new lane opens while any lane stands in a mandatory stop, and no new lane opens while more than N decisions are queued for the human — the measured bottleneck is decision - bandwidth, not compute. + bandwidth, not compute. The first of those is a **global** brake, and it + narrows the branch-only guarantee above: architecture churn parks *stories* + per branch, but a mandatory stop in any lane halts *new lanes everywhere* + until it clears. Running lanes drain either way. - **Three test layers, three cost classes** (decided 2026-08-31). The lane battery (seconds, every cycle, exists) · a **smoke gate in the merge queue** (minutes, every merge, new) · the **full E2E suite as a clock loop** (hours, @@ -366,8 +435,8 @@ define-conventions↔Phase 0, watch↔clock loops, needs-human-review↔escalati **Adopted into the build path** (owning step in brackets): - Local SQLite analytics — cost/duration/retries per step, written non-fatally post-run; plus a trace ID per story propagated through every - stage artifact, and mechanical spec-delta capture. [step 2 — this is P8's - concrete shape] + stage artifact, and mechanical spec-delta capture. [step 2c — new state and + instrumentation, so not the read-only P8 story] - Orchestrator dry-run (print the tick's execution plan before spending tokens) and a forced-fresh-session knob (`max_resume_retries` analog) — turns gap 7 into a mechanism. [step 3] @@ -400,9 +469,9 @@ from day one (their P0 gap: unbounded disk growth); absent real-time cost visibility is what makes a token furnace invisible (their P0 gap, our P8). **Second sweep (2026-08-31), against their full docs and the inspiration -video.** The two sweep files (`docs/research/2026-08-30-*-gap-sweep.md`) are -local working notes under a git-ignored directory: they do not survive a -clone, so the durable record is the list below, not those paths. +video.** The sweep produced two working notes that live outside the repo and +are deliberately not cited by path, because they do not survive a clone. The +durable record is the list below. Adopted, with owning step: - Mechanical write protection for pool status, the `lanes` knob and the architecture section of AGENTS.md (their `protected_paths` / @@ -410,9 +479,14 @@ Adopted, with owning step: invariant 1 ("the hook always exits 0") forbids one. Arguing that a local-state check carries none of the external dependency invariant 1 names is an argument *for amending* it, not an exemption from it — so this item's - prerequisite is an **invariant-1 amendment through an architecture - meta-story** (decision 3). Until that lands, enforcement lives outside the - hook: a repo-owned command or a required CI check. + prerequisite is an **architecture meta-story amending invariant 1** + (decision 3). Invariant 1 is not the only one it meets: invariant 2 ("on + uncertainty, fire") would turn every parse or environment failure into a + denied write, and invariant 4 keeps the hook POSIX `sh` with `jq` optional. + The meta-story must decide fail-open or fail-closed for each local failure + mode and say which of the three invariants it amends and which it preserves. + Until it lands, enforcement lives outside the hook: a repo-owned command or + a required CI check. [prerequisite meta-story, then step 4/5] - A run lock per orchestrator tick with stale-lock cleanup by the judge, so a double-firing clock never runs two orchestrators. [step 5] @@ -423,7 +497,7 @@ Adopted, with owning step: builder — cross-model stays. [step 1/3] - Minimum review cost/duration as an INCOMPLETE signal: a pass under the floor does not count toward the pass floor. No new gate — the kit's - existing INCOMPLETE semantics. [step 2, P8] + existing INCOMPLETE semantics, fed by 2c's measurements. [step 2c] - Audit rejection flows back automatically: the human's "changes requested" returns to the lane as an artifact (like a red smoke), and a PR once rejected by the human is always re-audited by the human, never re-drawn. @@ -445,7 +519,10 @@ Adopted, with owning step: machine-checkable goal condition; also what decision 9's fingerprint protects). [step 1/4] Reverse traceability (code with no spec behind it) as drift-audit content, report only. [step 5] Every clock loop starts - report-only; fix permission is a maturity knob per loop. [step 5] + report-only — meaning it changes no product code and no pool *status*; + filing a new pool item is the one write it does make, because a report + nobody can act on is not a report (that is how the E2E loop files its + failure, §9). Fix permission is a maturity knob per loop. [step 5] Deployment is out of scope (§8). Redirected: their merge coordinator is our merge-queue station (§9); their @@ -467,7 +544,11 @@ caps on verify output and PR diffs in prompts (unbounded diffs blow context and cost). Irrelevant here: per-module monorepo batteries, LangGraph as a framework, knowledge-graph disambiguation. -## 11. Open questions (owned by the stories that will answer them) +## 11. Open questions + +Most carry the step that will answer them. Three do not — hidden verification +scenarios, the dashboard and the shortcuts topic are parked without a +numbered owner, and are marked as such rather than counted as decomposed. - Sample percentage and drawing rule for the Sample-Gate (step 6). - Storage form of the pool (files in-repo vs. external board) — step 4. @@ -534,13 +615,13 @@ writes the story. isolated is the candidate. [5c] - *Telemetry gaps* — analytics writes are non-fatal, yet review cost and duration decide whether a pass counts. A missing or unattributable gate - measurement must read as INCOMPLETE, never as a cheap pass. [step 2] + measurement must read as INCOMPLETE, never as a cheap pass. [2c] - *Live cost visibility* — post-run analytics detect overspend after it happened, which is not the control §10 keeps from their P0 gap. Live counters, alerts and stop thresholds are a different mechanism from P8's - passive record. [step 2 / dashboard] + passive record. [2c / dashboard] - *Retention* — run artifacts need a retention, compaction and deletion stance - from the first story that writes them; their P0 gap was exactly this. [step 2] + from the first story that writes them; their P0 gap was exactly this. [2c] - *Planning-artifact home* — where plans and specs live so they never contaminate an implementation branch, and which of them are tracked. [step 3] - *Model strength order* — "the reviewer is never weaker than the builder" @@ -562,3 +643,38 @@ writes the story. wave can combine independent subsystems and mixed profiles, which §5 says must split. Batching by compatible branch and profile group is the candidate, and the O(waves) bound then counts batches rather than waves. [4c / step 6] +- *The hardening ledger has no station* — the fingerprinted ledger and + `harden-finding` are one of the kit's three defining mechanisms (AGENTS.md), + and no station in §1 disposes of accepted gate or bot findings into it. An + autonomous factory that repairs instances without recording fingerprints + loses the escalation that turns a recurring finding into a rule. [4b / 5d] +- *Gate B and the rebase* — the queue rebases onto newer main after Gate B ran + and then runs only smoke, so what lands is not byte-for-byte what the + adversarial gate reviewed. Whether Gate B re-runs on the composed candidate, + or the claim is narrowed, is open — and until it is settled, "the adversarial + gate checks every merge" (decision 5) is a claim about the pre-rebase diff. + [5b] +- *PR readiness before the queue* — nothing sequences PR opening, the routed + review bots (`docs/pr-review-bots.md`) and required CI against Sample-Gate + entry and queue entry. [5d] +- *AC canonicalization* — decision 9's fingerprint needs a canonical + serialization of the AC block, a baseline captured at lane opening, a + durable record of the authorizing pool round-trip, and the exact evidence + Gate B compares. Without them the check cannot separate an authorized + correction from reward hacking. [4e] +- *The stopped lane* — when a lane stops for an AC change, whether its partial + work, plan, evidence and review counters are discarded, quarantined or + resumed after reclassification is undefined, and each choice changes the + Gate-B baseline. [4e] +- *Given/when/then and the per-AC test report* — §10 assigns these to steps 1 + and 4, but step 1's tracked story does not contain them and 4e covers only + IDs, immutability and the round-trip. The machine-checkable goal condition is + therefore unowned. [4e] +- *Promotion evidence* — "measured P8 evidence" is the condition on every + autonomy knob, but P8 measures ledger recurrence and review-severity mixes, + not Freigabe accuracy, audit escapes or merge safety. Each knob needs its own + outcome metric, adverse-event measure, window and threshold. [6d] +- *Wave-close ordering* — the order of the wave-close E2E run, the as-built + view, wave closure and any standing auto-open rule is undefined, so a wave can + open before the previous one's integration result exists, and a late E2E + failure has no defined wave. [4d] From 263be718e9635c1cd6d56bffdc1f02ba9fd698f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 17:07:58 +0200 Subject: [PATCH 106/117] =?UTF-8?q?docs(vision):=20Gate=20A=20pass=203=20?= =?UTF-8?q?=E2=80=94=20an=20outage=20makes=20work=20wait,=20fix=20permissi?= =?UTF-8?q?on=20never=20edits=20code,=20three=20shipped-behaviour=20correc?= =?UTF-8?q?tions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both open blockers resolved on the author's ruling. A runtime reviewer outage no longer produces a gateless cycle: the candidate waits in the merge queue until an independent reviewer returns, and no human substitutes for the gate. Sampled audit is QA, never authorization. 'Gateless' is only a declared project-level state (.context/codex-gate.off), never a runtime improvisation — which leaves the reviewer-availability question closed where its own record closed it. A clock loop's 'fix permission' means it may create and advance a normally classified pool story, and nothing else. It never edits code directly: every fix travels through classification, Freigabe and a wave, so decision 3's one process for everything covers the loops too. The PR poller therefore splits from PR processing — the poller is report-only clock work, while the shipped process-pr-review command fixes, commits and hardens inside a lane. Three descriptions of shipped behaviour corrected: the PR station now applies the routing table in docs/pr-review-bots.md, whose Wait-for list is empty, instead of telling an implementer to wait for bots that must never block; the hardening-ledger gap now records that process-pr-review already routes accepted bot findings, and narrows the gap to Gate A/B findings and sampled-audit findings; and live cost visibility plus harness-token measurement move from the read-only P8 story to 2c. --- .../specs/2026-08-30-dark-factory-vision.md | 67 +++++++++++++------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index 628fc52..c5c5902 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -38,8 +38,10 @@ Story-Pool — filling never triggers production [missing] → Plan-Loop (writing-plans + Gate A) [exists] → Bau-Loop (executing-plans, goal-based, TDD) [exists] → Verify (battery + Gate B — adversarial model) [exists] - → PR (open it; wait for the routed review bots and - process their findings — docs/pr-review-bots.md) [exists] + → PR (open it, then apply the routing table in + docs/pr-review-bots.md — its Wait-for list is empty, + so nothing blocks: read what the opportunistic bots + have posted, handle later posts as follow-ups) [exists] → Sample-Gate (draws x% of PRs for human audit) [missing] ↳ drawn → Audit (human, sampled) [missing] ↳ not drawn → straight on (the ordinary path) [missing] @@ -116,12 +118,16 @@ the inspiration: the adversarial verifier is a different model *family* the clock. 5. **End state is sampled audit, not per-merge approval.** The adversarial gate checks every merge; the human audits a sample. No merge skips both - gates — so **reviewer availability is an input to merge authorization**: - when no independent model family is available the cycle is gateless - (decision 8), and a gateless candidate never lands automatically. It waits, - or it goes to human audit; automatic landing resumes when an independent - reviewer does. (Until step 6 of the build path matures, merge remains human - anyway.) + gates — so **reviewer availability is an input to merge authorization**, + and the answer to an outage is to **wait**. A runtime outage never produces + a gateless cycle: the candidate stays in the merge queue until an + independent reviewer returns, and **no human substitutes for the gate**. + Sampled audit is QA, never authorization, and never stands in for an active + gate. "Gateless" exists only as a *declared project-level state* — the + kit's `.context/codex-gate.off`, chosen deliberately and visibly — never as + a runtime improvisation. That leaves the reviewer-availability question + closed where its own record closed it. (Until step 6 of the build path + matures, merge remains human anyway.) 6. **Filling the pool never triggers production; classification is a proactive loop of its own.** (It is not free of *all* consequence — it spends tokens, writes a card and a status, may create a meta-story and may @@ -155,9 +161,11 @@ the inspiration: the adversarial verifier is a different model *family* as a notice. Raising the rung follows measured P8 evidence, never precedes it (§8, "No autonomy expansion ahead of the measured evidence"). 8. **Four-eyes principle, with a scaling guard.** No artifact passes only its - author. The second pair of eyes is another model *family*; where none is - available the honest answer is to be **gateless and say so** — never - self-reviewed, and not a same-family agent either. That question is closed + author. The second pair of eyes is another model *family*; where a project + has none configured, the honest answer is to be **gateless and say so** — a + declared, visible project state, never self-reviewed and not a same-family + agent either. A *runtime* outage is a different thing and is no licence to + close a cycle: the work waits (decision 5). That question is closed with a negative answer and this vision does not reopen it: three fallback designs across nine Gate-A passes and 303 findings all failed structurally — the first of them being exactly this same-family tier-2 @@ -337,7 +345,13 @@ proceeds, the breaking part waits on the meta-story). queue entry, so it takes the run-lock and artifact halves of 5a's contract and none of the tick, usage-hold or report-only halves. - **5c** the E2E clock loop (failures auto-filed as pool stories, §9). - - **5d** drift audits and PR-bot processing. + - **5d** the drift audits: docs drift and reverse traceability, + report-only, findings filed as pool items. + - **5e** the PR poller: a report-only clock loop that notices a PR needing + attention and wakes the shipped `process-pr-review` station inside a + lane. It does not process the PR itself — that command fixes, commits and + hardens, which is lane work and not something a report-only clock loop + may do. 6. **Stage 4 and sampled audit** (decision 5) — the highest-risk step, so it splits like the others: - **6a** the stage-4 orchestrator wake: a story turning *freigegeben* wakes @@ -466,7 +480,9 @@ under an automated resolver breaks it — every hand-edit of pool status must be a defined operation; planning artifacts need a defined home so they never contaminate implementation branches; run artifacts need a retention stance from day one (their P0 gap: unbounded disk growth); absent real-time cost -visibility is what makes a token furnace invisible (their P0 gap, our P8). +visibility is what makes a token furnace invisible (their P0 gap; ours is +2c's live counters — not the read-only P8 story, which is forbidden to +instrument anything). **Second sweep (2026-08-31), against their full docs and the inspiration video.** The sweep produced two working notes that live outside the repo and @@ -522,13 +538,17 @@ Adopted, with owning step: report-only — meaning it changes no product code and no pool *status*; filing a new pool item is the one write it does make, because a report nobody can act on is not a report (that is how the E2E loop files its - failure, §9). Fix permission is a maturity knob per loop. [step 5] + failure, §9). "Fix permission" is a maturity knob per loop, and it means + exactly one thing: the loop may **create and advance a normally classified + pool story**. It never means editing code directly. Every fix a loop wants + travels through classification, Freigabe and a wave like any other story — + decision 3's one process for everything covers the loops too. [step 5] Deployment is out of scope (§8). Redirected: their merge coordinator is our merge-queue station (§9); their `report` (executive summary over the analytics window) belongs to the dashboard topic (§11); their harness-as-router (CLAUDE.md compressed to a -20-line signpost) becomes a kit story after P8 measures harness tokens per +20-line signpost) becomes a kit story once 2c measures harness tokens per session — with fresh context per station, every story pays the harness size times its stations. @@ -643,11 +663,16 @@ writes the story. wave can combine independent subsystems and mixed profiles, which §5 says must split. Batching by compatible branch and profile group is the candidate, and the O(waves) bound then counts batches rather than waves. [4c / step 6] -- *The hardening ledger has no station* — the fingerprinted ledger and - `harden-finding` are one of the kit's three defining mechanisms (AGENTS.md), - and no station in §1 disposes of accepted gate or bot findings into it. An - autonomous factory that repairs instances without recording fingerprints - loses the escalation that turns a recurring finding into a rule. [4b / 5d] +- *The hardening ledger covers only one of three finding sources* — the + fingerprinted ledger and `harden-finding` are one of the kit's three + defining mechanisms (AGENTS.md), and `process-pr-review` item 5 already + routes accepted actionable **bot** findings into it. Two sources have no + station: accepted **Gate A and Gate B** findings, and accepted findings from + a **sampled human audit** — the latter being the factory's highest-value + signal, since an audit finding is by definition something both gates let + through. Repairing those instances without recording fingerprints loses the + escalation that turns a recurring finding into a rule. + [gate findings 4b, audit findings 6b] - *Gate B and the rebase* — the queue rebases onto newer main after Gate B ran and then runs only smoke, so what lands is not byte-for-byte what the adversarial gate reviewed. Whether Gate B re-runs on the composed candidate, @@ -656,7 +681,7 @@ writes the story. [5b] - *PR readiness before the queue* — nothing sequences PR opening, the routed review bots (`docs/pr-review-bots.md`) and required CI against Sample-Gate - entry and queue entry. [5d] + entry and queue entry. [5e] - *AC canonicalization* — decision 9's fingerprint needs a canonical serialization of the AC block, a baseline captured at lane opening, a durable record of the authorizing pool round-trip, and the exact evidence From 6f67645bf0dfbf8ceb30cb2c4727a3b153200aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 17:30:41 +0200 Subject: [PATCH 107/117] =?UTF-8?q?docs(vision):=20Gate=20A=20pass=204=20?= =?UTF-8?q?=E2=80=94=20fix=20where=20the=20outage=20rule=20applies,=20corr?= =?UTF-8?q?ect=20the=20gate-off=20claim,=20retire=20four=20overclaims?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both blockers were my own errors implementing the pass-3 ruling, not new questions. The ruling said work waits during a reviewer outage; I wrote that as waiting in the merge queue, but Gate B runs at Verify, before PR, Sample-Gate and the queue, and a Gate-A outage precedes any candidate — so the wait now happens where the outage happens. And I described .context/codex-gate.off as a declared project-level gateless state, where shipped CLAUDE.md says it suppresses the hook reminder per workspace and 'the gates still apply', and .gitignore excludes it so a clone never sees it. The declaration is the tracked INACTIVE notice /workflow-init writes. Neither file authorizes anything. Five sentences retired from the overclaim class AGENTS.md forbids by name: smoke plus wave-close E2E called equivalent to a pre-main rollup branch; release tags said to solve 'main = whole waves only'; a repo-owned command and a CI check called enforcement where neither prevents an unauthorized local write; 'no stage is skipped' against the shipped triviality skip; and an audit finding called by definition something both gates let through. Four more internal contradictions: the architecture verdict had two possible producers, the Spec-Loop still ran intake after classification already had, story stages were said to fan out against an artifact-ordered pipeline, and decision 8 promised a knob at every mandatory touchpoint one sentence before naming four that carry none. Seven leaf-ownership findings recorded rather than specified: 4c gained the Bewertungs-Loop runtime, 4e the machine-checkable goal condition, and §11 gained a block naming six stations §1 draws that no leaf yet owns, each with a candidate owner. The gate is NOT closed: the closing condition was a pass returning only leaf-ownership findings, and this one did not. --- .../specs/2026-08-30-dark-factory-vision.md | 126 +++++++++++++----- 1 file changed, 95 insertions(+), 31 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index c5c5902..dc51fec 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -22,19 +22,28 @@ measurably matters — and, at full maturity, sampled rather than omnipresent: ``` Story-Pool — filling never triggers production [missing] → Intake-Loop (PROACTIVE: a new story appearing triggers - classification + architecture verdict, debounced/batched; - attaches the card, triggers NO production) [missing] + classification, debounced/batched; attaches the card, + triggers NO production) [missing] ↳ reads the Architektur (AGENTS.md) [exists, needs one extension] - ⟳ Bewertungs-Loop (continuously re-evaluates the tree - against the pool: produces the meta-stories of - decision 3, and re-classifies the cards an - architecture merge made stale) [missing] + ↳ asks the Bewertungs-Loop for the architecture + verdict, and attaches the answer it returns [missing] + ⟳ Bewertungs-Loop (the single producer of every + architecture verdict: answers Intake's request, + continuously re-evaluates the tree against the pool, + produces the meta-stories of decision 3, and + re-classifies the cards an architecture merge made + stale) [missing] → Freigabe (the ONLY production trigger; the human, or a standing rule the human installed — decision 7; reads a rendered wave plan) [human] → Takt-Loop (polls freigegebene stories of the active - wave → wakes the orchestrator → a lane opens) [missing] - → Spec-Loop (intake + design + Gate A) [exists] + wave → wakes the orchestrator → a lane opens). The + orchestrator is itself a model-operated node here, + so the vet preflight, fresh context, artifact-only + handoff and the judge all cover it [missing] + → Spec-Loop (design + Gate A — the item was already + intaken and profiled at classification, so intake + does not run twice) [exists] → Plan-Loop (writing-plans + Gate A) [exists] → Bau-Loop (executing-plans, goal-based, TDD) [exists] → Verify (battery + Gate B — adversarial model) [exists] @@ -118,15 +127,21 @@ the inspiration: the adversarial verifier is a different model *family* the clock. 5. **End state is sampled audit, not per-merge approval.** The adversarial gate checks every merge; the human audits a sample. No merge skips both - gates — so **reviewer availability is an input to merge authorization**, - and the answer to an outage is to **wait**. A runtime outage never produces - a gateless cycle: the candidate stays in the merge queue until an - independent reviewer returns, and **no human substitutes for the gate**. + gates — so **reviewer availability is an input to authorization**, and the + answer to an outage is to **wait where the outage happens**: a Gate-A + outage stops the lane before any candidate exists, a Gate-B outage holds + the work at Verify, and a candidate already queued stays queued. Nothing + advances past an unfinished gate, and **no human substitutes for the + gate**. Sampled audit is QA, never authorization, and never stands in for an active - gate. "Gateless" exists only as a *declared project-level state* — the - kit's `.context/codex-gate.off`, chosen deliberately and visibly — never as - a runtime improvisation. That leaves the reviewer-availability question - closed where its own record closed it. (Until step 6 of the build path + gate. "Gateless" exists only as a *declared project state*, and the shipped + declaration is the tracked **INACTIVE notice** `/workflow-init` writes into + the project's own CLAUDE.md ("the gates below do not run"). The + `.context/codex-gate.off` marker beside it is not that declaration: it + suppresses the hook reminder in one workspace, it is git-ignored so a clone + never sees it, and shipped CLAUDE.md says plainly that with it in place + "the gates still apply". Neither file authorizes anything. That leaves the + reviewer-availability question closed where its own record closed it. (Until step 6 of the build path matures, merge remains human anyway.) 6. **Filling the pool never triggers production; classification is a proactive loop of its own.** (It is not free of *all* consequence — it @@ -325,14 +340,20 @@ proceeds, the breaking part waits on the meta-story). and dependency rules — including the proactive Intake-Loop, which is the factory's first stage-4 loop (decision 6), so stage 4 starts here rather than at step 6. - - **4c** architecture bootstrap: Phase 0 and tree v1 (§9), plus the - machine-readable projection generated from AGENTS.md. + - **4c** architecture: Phase 0 and tree v1 (§9), the machine-readable + projection generated from AGENTS.md, **and the Bewertungs-Loop runtime** + — its continuous pool-versus-tree evaluation, its meta-story batching per + wave, and the re-classification an architecture merge triggers. The + bootstrap alone would leave §1's Bewertungs-Loop unbuilt. - **4d** Freigabe and wave control: the rendered wave plan, the granularity knob and its standing rules, wave opening and closing (decision 7). No other step owned this, and the pipeline cannot run without it. - **4e** decision 9's mechanics: acceptance-criterion IDs, the read-only rule inside a lane, the AC-block comparison at Gate B and the pool - round-trip. Ordered **before any autonomous lane execution** — it is the + round-trip — plus given/when/then normalization and the per-AC test + report as a handoff artifact, which is the machine-checkable goal + condition the fingerprint protects and which no other leaf owned. + Ordered **before any autonomous lane execution** — it is the reward-hacking guard, and a build path that ships lanes without it looks complete while the guard is missing. 5. **Stage 3: clock loops** (poll, drift audits, PR processing). Also an epic; @@ -401,8 +422,10 @@ proceeds, the breaking part waits on the meta-story). - **The tree is the parallelism map.** Disjoint branches run in parallel (N worktrees × one pipeline each — possible today; the record/nonce rules are the foundation); same branch serializes. Classification's dependency and - architecture verdicts yield the schedule. Within a story, stages fan out to - subagents; Gate B's two review branches already run in parallel. What does + architecture verdicts yield the schedule. Within a story the stages stay + serial — the plan needs the spec, the build needs the plan, Verify needs the + diff — but independent work *inside* one stage may fan out to subagents, as + Gate B's two review branches already do. What does not parallelize: the serial passes of one review loop, the merge queue to main, and the human's decisions. - **The parallelism budget is a user-owned knob.** It lives in the story @@ -501,8 +524,12 @@ Adopted, with owning step: denied write, and invariant 4 keeps the hook POSIX `sh` with `jq` optional. The meta-story must decide fail-open or fail-closed for each local failure mode and say which of the three invariants it amends and which it preserves. - Until it lands, enforcement lives outside the hook: a repo-owned command or - a required CI check. + Until it lands there is no mechanical write protection at all. What exists + is **authorized-write tooling** (a repo-owned command an agent may choose to + use) and **merge-time detection** (a required CI check). Neither prevents an + unauthorized local write, and the factory can schedule or build from one for + as long as it takes a commit to reach CI. That bypass window is the reason + the blocking mechanism is wanted, and the interim path does not close it. [prerequisite meta-story, then step 4/5] - A run lock per orchestrator tick with stale-lock cleanup by the judge, so a double-firing clock never runs two orchestrators. [step 5] @@ -554,12 +581,18 @@ size times its stations. Not adopted: the rollup branch (issue PRs into a wave branch, one rollup PR to main). It is a part of their stop-the-world milestone model, already -rejected: the merge-queue smoke and the wave-close E2E give the same -integration checkpoint, the as-built view gives the wave-level reading, and +rejected. What replaces it is weaker, and saying so is the point: incremental +smoke on each composed candidate *before* it lands, plus an E2E run over a +wave that is already on main. There is no atomic whole-wave candidate and no +pre-main whole-wave checkpoint, so some integration failures surface only +after a partial wave has shipped. The as-built view gives the wave-level +reading, and a wave branch would break the kit's merge-base-with-main semantics and make hotfixes a two-way merge — the opposite of flexible shortcuts (§11). A -project that needs "main = whole waves only" solves that with release tags -in its own CI (§8). Also not adopted, noted as a lesson only: hard byte +project that needs "main = whole waves only" does not get it here: the queue +lands individual candidates throughout a wave, so main is incremental by +construction. Release tags in its own CI (§8) give that project release +*boundaries*, which is a different thing and the most this design offers. Also not adopted, noted as a lesson only: hard byte caps on verify output and PR diffs in prompts (unbounded diffs blow context and cost). Irrelevant here: per-module monorepo batteries, LangGraph as a framework, knowledge-graph disambiguation. @@ -669,10 +702,12 @@ writes the story. routes accepted actionable **bot** findings into it. Two sources have no station: accepted **Gate A and Gate B** findings, and accepted findings from a **sampled human audit** — the latter being the factory's highest-value - signal, since an audit finding is by definition something both gates let - through. Repairing those instances without recording fingerprints loses the - escalation that turns a recurring finding into a rule. - [gate findings 4b, audit findings 6b] + signal, since an audit finding is something the gates that ran did not + catch, or that no gate saw at all where Gate B was legitimately skipped and + Gate A reviewed only the spec and the plan. Repairing those instances without recording fingerprints loses the + escalation that turns a recurring finding into a rule. Gate findings belong + to the loop-rule owner rather than to the classifier — 4b is pre-production + and never sees a review loop. [gate findings 2a, audit findings 6b] - *Gate B and the rebase* — the queue rebases onto newer main after Gate B ran and then runs only smoke, so what lands is not byte-for-byte what the adversarial gate reviewed. Whether Gate B re-runs on the composed candidate, @@ -682,6 +717,35 @@ writes the story. - *PR readiness before the queue* — nothing sequences PR opening, the routed review bots (`docs/pr-review-bots.md`) and required CI against Sample-Gate entry and queue entry. [5e] + +**Stations drawn in §1 that no leaf yet owns.** Each needs a leaf assigned +before step 4 or step 5 planning begins; naming them here is what stops a +roadmap from reading complete while a drawn station is unbuilt. + +- *The vet preflight* — §10 assigns it to step 4 as a whole, but 4a, 4b and 4c + divide storage, classification and architecture between them and none owns a + runner that fires before **every** model-operated node, including nodes that + do not exist yet. Candidate: its own leaf under the shared contract, with + its pool, split-threshold and projection checks mapped to 4a, 4b and 4c. +- *The judge / watchdog* — §10 assigns it to step 5 as a whole, and it appears + in no leaf. It supervises the model loops **and** the merge queue, which is + not a clock loop, so it does not fall out of 5a either. Candidate: its own + leaf covering every supervised node plus the push notifications. +- *The working orchestrator dry-run* — step 3 owns the plan interface and + says the runnable dry-run lands after 4d and 5a, but no later leaf owns it + and §10 still points at step 3. Candidate: a leaf after 5a, with §10 + repointed to it. +- *The as-built view at wave close* — §10 assigns it to steps 4 and 5, it is + in no leaf, and it is not drawn in §1. Candidate: 5d generates it and 4d + requires it in the wave-close transition. +- *Punchlist generation* — §10 promises it as the artifact a sampled audit + reads, assigned to step 6 as a whole; 6b owns the draw and the audit state, + 6c owns the merge thresholds, and neither owns producing the punchlist. + Candidate: 6b, since it is what the audit consumes. +- *Model strength per role* — §10 assigns it to step 1/3, but the tracked + review-economics story contains no model-strength knob and step 3's + definition omits it, so both cited owners can finish without it. Candidate: + 2a with the other loop rules, with §10 repointed. - *AC canonicalization* — decision 9's fingerprint needs a canonical serialization of the AC block, a baseline captured at lane opening, a durable record of the authorizing pool round-trip, and the exact evidence From 97bf0f4dd9b8c471337e2fa011e759dbd5b54b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:32:04 +0200 Subject: [PATCH 108/117] =?UTF-8?q?docs(vision):=20Gate=20A=20closed=20?= =?UTF-8?q?=E2=80=94=20dark-factory=20decomposition,=20five=20passes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gate A (spec) is closed on the author's scope disposition: leaf-level mechanics are outside this decomposition document; each remaining finding is owned by the named leaf story and its own Gate A. This is a scope disposition, not a clean pass. Five passes: 52, 34, 32, 26, 15 findings; 41, 26, 24, 16, 7 Blocker+Major; 2, 1, 2, 2, 0 Blockers. The loop converged rather than plateaued — worth recording, because the pre-agreed exit anticipated the opposite and the closing rationale should not carry a reason the data contradicts. Pass 5 changed the document in two ways beyond recording. First, two factual errors about tracked repository files, both introduced by earlier passes of mine. Decision 8 said the same-family tier-2 reviewer question was closed with a negative answer; it is not. What closed negatively was a safe sanctioned zero-pass closure. Tier-2 is a different question and remains open as a tracked, unshipped story, which the fallback design's own section 7 routes to. And 'git-ignored so a clone never sees it' held only for this repo, since /workflow-init has target projects ignore /.context/codex-reviews/ specifically, leaving the marker visible and committable in a scaffolded project. Neither correction changes what the author decided. Second, seven edits that the pass-4 report claimed were applied had never reached disk: the edit script aborted on a non-matching string before its write, and the report was written from the per-edit log rather than from the file. Pass 5 re-reported all seven, which is how the miss surfaced. They are applied now — the section 7 heading, step 5's title, decision 8's 'every mandatory touchpoint', the autonomy-downgrade owner, the section 11 opening count, the dashboard's P8 attribution, and the mini-wave 'no stage is skipped'. The pass-4 dispositions file carries the correction. Five findings are recorded in section 11 under 'Unresolved at close', each with its owning leaf, so a later reader meets them as known rather than as oversights. What the cycle produced: two false claims about shipped kit behaviour removed, eight sentences retired from the overclaim class AGENTS.md forbids by name, six stations drawn that had existed only in prose, four epic build steps split into seventeen leaves, and forty titled gaps recorded with owners in a section 11 that had seven bullets before. The document went from 416 to 816 lines. --- .../specs/2026-08-30-dark-factory-vision.md | 103 +++++++++++++----- 1 file changed, 75 insertions(+), 28 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index dc51fec..fafe46e 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -138,10 +138,13 @@ the inspiration: the adversarial verifier is a different model *family* declaration is the tracked **INACTIVE notice** `/workflow-init` writes into the project's own CLAUDE.md ("the gates below do not run"). The `.context/codex-gate.off` marker beside it is not that declaration: it - suppresses the hook reminder in one workspace, it is git-ignored so a clone - never sees it, and shipped CLAUDE.md says plainly that with it in place - "the gates still apply". Neither file authorizes anything. That leaves the - reviewer-availability question closed where its own record closed it. (Until step 6 of the build path + suppresses the hook reminder in one workspace, and shipped CLAUDE.md says + plainly that with it in place "the gates still apply". (Whether a clone + sees the marker depends on the repo: this kit ignores all of `.context/`, + while `/workflow-init` has target projects ignore `/.context/codex-reviews/` + specifically — so in a scaffolded project the marker is visible and + committable.) Neither file authorizes anything. That leaves the + reviewer-availability question exactly where its own record left it. (Until step 6 of the build path matures, merge remains human anyway.) 6. **Filling the pool never triggers production; classification is a proactive loop of its own.** (It is not free of *all* consequence — it @@ -180,22 +183,26 @@ the inspiration: the adversarial verifier is a different model *family* has none configured, the honest answer is to be **gateless and say so** — a declared, visible project state, never self-reviewed and not a same-family agent either. A *runtime* outage is a different thing and is no licence to - close a cycle: the work waits (decision 5). That question is closed - with a negative answer and this vision does not reopen it: three fallback - designs across nine Gate-A passes and 303 findings all failed - structurally — the first of them being exactly this same-family tier-2 - shape — - (`docs/superpowers/specs/2026-08-14-reviewer-availability-fallback-design.md`), - and what ships is "be gateless — not self-reviewed" - (`docs/coding-workflow.md`, `/workflow-init`). Reopening it would be its own - story. Human eyes only where the frequency is bounded — O(waves + + close a cycle: the work waits (decision 5). What ships today is "be + gateless — not self-reviewed" (`docs/coding-workflow.md`, + `/workflow-init`), and this vision assumes nothing beyond it. What was + closed with a negative answer is narrower than "a same-family reviewer": + three fallback designs across nine Gate-A passes and 303 findings failed to + produce a safe *sanctioned zero-pass closure* + (`docs/superpowers/specs/2026-08-14-reviewer-availability-fallback-design.md`). + A same-family tier-2 reviewer is a different question and is **still open** + as a tracked, unshipped story + (`docs/superpowers/stories/2026-08-14-tier-2-same-family-reviewer-story.md`) + — "a weaker review is still a review", per that design's §7, if its + containment proves buildable. If it ever ships, this decision follows it; + until then the second pair of eyes is another family, or none. Human eyes only where the frequency is bounded — O(waves + exceptions), never O(stories) — **as the end state**, which the rollout deliberately does not satisfy yet: decision 7's lower rungs are per-story and decision 5 keeps merge human until step 6. The bound is the target the knobs move toward, not a rule the bootstrap already obeys. Every mandatory human touchpoint carries a maturity knob that lowers with - P8 evidence — but only at *routine* touchpoints: Freigabe granularity, - sample rates, wave opening. The four paths §8 protects (mandatory stops, + P8 evidence — but a knob exists only at *routine* touchpoints: Freigabe + granularity, sample rates, wave opening. The four paths §8 protects (mandatory stops, profile confirmations, scope changes, architecture meta-stories) carry no such knob and stay human at every rung. Where a knob does exist, presence is a dial that falls with trust and rises again on adverse evidence, never @@ -309,7 +316,7 @@ proceeds, the breaking part waits on the meta-story). rules of the in-flight review-economics story are the foundation for more. *(in flight)* -## 7. Build path (each step = one story through the normal workflow) +## 7. Build path (each *leaf* is one story; numbered entries are ordering groups) 1. Finish the review-economics story (in flight) — floors by profile, severity calibration, measurable records. Without calibrated review economics every @@ -356,8 +363,9 @@ proceeds, the breaking part waits on the meta-story). Ordered **before any autonomous lane execution** — it is the reward-hacking guard, and a build path that ships lanes without it looks complete while the guard is missing. -5. **Stage 3: clock loops** (poll, drift audits, PR processing). Also an epic; - its stories are separate failure domains behind one shared contract: +5. **Stage 3, plus the merge queue** (polling, drift audits, PR polling — and + one station, 5b, that is not a clock loop at all). Also an epic; its + stories are separate failure domains behind one shared contract: - **5a** the clock-loop contract itself: tick, run lock, environment preflight, usage-limit hold/resume, the report-only default, and the branch-claim and lane-budget rules the scheduler needs. @@ -410,7 +418,10 @@ proceeds, the breaking part waits on the meta-story). from the codebase (natural home: a `/workflow-init` extension). Tree v1 need only be good enough to judge with — meta-stories correct it in use (decision 3). A single incoming story is simply a mini-wave: the views - collapse to one line, no stage is skipped. + collapse to one line and no *station* is bypassed — though a behaviourally + trivial change can still take the shipped Gate-B triviality skip, and an + undrawn PR does not reach Audit; §11 records the first as an open end-state + question. - **Architecture churn blocks branches, never the factory.** An architecture-relevant story is not rejected: classification parks it with warten-auf behind its meta-story, and the meta-story locks exactly the tree @@ -599,9 +610,10 @@ framework, knowledge-graph disambiguation. ## 11. Open questions -Most carry the step that will answer them. Three do not — hidden verification -scenarios, the dashboard and the shortcuts topic are parked without a -numbered owner, and are marked as such rather than counted as decomposed. +Most carry the leaf or step that will answer them. Some do not: the three +parked topics below — hidden verification scenarios, the dashboard, the +shortcuts — and the stations near the end of this section that §1 draws but no +leaf yet owns. Those are marked as such rather than counted as decomposed. - Sample percentage and drawing rule for the Sample-Gate (step 6). - Storage form of the pool (files in-repo vs. external board) — step 4. @@ -610,7 +622,8 @@ numbered owner, and are marked as such rather than counted as decomposed. - How the clock's platform mechanics (loop/schedule) are configured per project — step 5. - Process dashboard / console status — "when is what running where"; builds - on the computed views plus P8 trace/analytics. Parked 2026-08-30, design + on the computed views plus 2c's traces and analytics — not P8, which is + read-only over the ledger and git. Parked 2026-08-30, design session planned 2026-08-31. - Sensible hooks and shortcuts through the pipeline for flexible use cases. Parked 2026-08-30, same session. @@ -690,8 +703,8 @@ writes the story. - *Autonomy downgrade* — every knob lowers human presence on good evidence and nothing raises it back on bad (audit rejection, repeated smoke failure, deteriorating metrics). Triggers, authority, hysteresis and the immediate - safe state are unowned; without them "a dial, never a ratchet" is only half - true. [step 6] + safe state belong to 6d, which owns the promotion path in both directions; + until 6d defines them "a dial, never a ratchet" is only half true. [6d] - *Meta-story batching versus the split rule* — one architecture meta-story per wave can combine independent subsystems and mixed profiles, which §5 says must split. Batching by compatible branch and profile group is the candidate, @@ -746,6 +759,41 @@ roadmap from reading complete while a drawn station is unbuilt. review-economics story contains no model-strength knob and step 3's definition omits it, so both cited owners can finish without it. Candidate: 2a with the other loop rules, with §10 repointed. + +**Unresolved at close (Gate A pass 5, 2026-08-31).** The gate was closed on a +scope disposition rather than a clean pass: leaf-level mechanics are outside +this decomposition document, and each item below is owned by the named leaf +story and its own Gate A. Pass 5 returned 15 findings and **zero Blockers** +(the series ran 52, 34, 32, 26, 15). These went unfixed by decision, not by +oversight, and a later reader should treat them as known: + +- *Labeled examples have no owner* — §1 turns a human override into a labeled + example, and decision 7 does the same for a Freigabe that repeatedly needs + deep thought, but no leaf captures, stores, routes or consumes them. + Candidate: 2a with the loop rules. [unowned] +- *The orchestrator product is wider than step 3* — §6 gap 4 names question + routing, artifact handoff, a prediction ledger and batched human decisions; + step 3 owns the role, the handoff and the plan interface only. Three + capabilities have no leaf. [3, needs widening] +- *"This closes the parallelism blind spot"* (§9) overstates what smoke + proves: it shows the configured smoke command passed on the composed + candidate, not that independently green lanes compose correctly outside + smoke coverage. The E2E layer is what reaches the rest, later. [5b / 5c] +- *"Promotion evidence" names one owner for several knobs* — 6d owns the + automatic-merge threshold, but the Freigabe and wave-opening knobs belong to + 4d and the sample rate to 6b, so the entry's single [6d] owner is too + narrow. [4d, 6b, 6d] +- *Coarse owners remain on four top-level questions* — the Sample-Gate rule, + pool storage, clock platform configuration and E2E cadence still cite step + 6, step 4 and step 5 rather than leaves, although this document makes the + leaf the unit of ownership. [6b, 4a, 5a, 5c] +These are the whole remainder. Six further pass-5 findings named wordings that +pass 4 had reported as fixed but had not written to disk — the §7 heading, step +5's title, decision 8's "every mandatory touchpoint", the autonomy-downgrade +owner, the §11 opening count, the dashboard's P8 attribution and the mini-wave +"no stage is skipped". Those were applied at close rather than recorded, and +the miss is noted here because a reader comparing the pass-4 record against the +document would otherwise find them inconsistent. - *AC canonicalization* — decision 9's fingerprint needs a canonical serialization of the AC block, a baseline captured at lane opening, a durable record of the authorizing pool round-trip, and the exact evidence @@ -756,9 +804,8 @@ roadmap from reading complete while a drawn station is unbuilt. resumed after reclassification is undefined, and each choice changes the Gate-B baseline. [4e] - *Given/when/then and the per-AC test report* — §10 assigns these to steps 1 - and 4, but step 1's tracked story does not contain them and 4e covers only - IDs, immutability and the round-trip. The machine-checkable goal condition is - therefore unowned. [4e] + and 4, but step 1's tracked story does not contain them. 4e now owns them + explicitly (see §7), so what remains is repointing §10 away from step 1. [4e] - *Promotion evidence* — "measured P8 evidence" is the condition on every autonomy knob, but P8 measures ledger recurrence and review-severity mixes, not Freigabe accuracy, audit escapes or merge safety. Each knob needs its own From b9254f2fd73a10e15405c0ab707e1d3b15d752db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:35:33 +0200 Subject: [PATCH 109/117] docs(vision): dashboard package and role-separation rules (Daniel, 2026-08-31) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The live status becomes the third computed view (§4): per-tick snapshot rendered by deterministic code — zero tokens per tick — into status.html, a status-line ticker and an optional menu-bar script, console command on demand; decision queue first, staleness visible, push stays separate, LLM summaries only on order; daemon/TUI and hosted artifact pages rejected. Role separation (§9): star not mesh, role = task (artifacts in and out, never chat history), identity in artifacts rather than session names — subagents separate automatically, long-lived sessions need the rules. §11 dashboard bullet resolved to a pointer; parked topics now two. --- .../specs/2026-08-30-dark-factory-vision.md | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index fafe46e..90b2ff7 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -258,6 +258,19 @@ the inspiration: the adversarial verifier is a different model *family* the orchestrator dry-run (build step 3). Waves are milestones that also *steer* — the clock pulls only the active wave — so the roadmap is the milestone-level view and the wave plan its per-milestone detail. +- **The live status is a computed view too — the dashboard (decided + 2026-08-31).** The orchestrator writes a state snapshot every tick; + deterministic code — never a model, so a tick costs no tokens — renders + it into a local `status.html` (auto-refresh, with a visible staleness + stamp: "Stand 17:03 · Tick 42" — a dashboard that silently ages is worse + than none), a status-line ticker, and optionally a menu-bar script; a + console command renders the same snapshot on demand. The top element is + always the human's decision queue (flags, rückfragen, mandatory stops, + drawn audits) — the queue that throttles the factory. Events that need + the human push (§10); the dashboard is for looking, never for polling. + LLM summaries only on demand. Rejected: a daemon/TUI and a hosted + artifact page (account-bound). The wave plan and the dashboard share one + renderer — the plan is the forecast, the dashboard is the now. - **Waves structure a new project.** Phase 0 assigns every initial pool item a wave mark (wave 1, 2, … or named milestones) — the deliberate "these subareas develop together first, those later" decision, usually aligned with @@ -470,6 +483,20 @@ proceeds, the breaking part waits on the meta-story). the normal intake, carrying the trace ID of the suspect merge. Concrete tools stay project truth: AGENTS.md gains the command roles `smoke` and `e2e`. Cadence and flaky rules belong to step 5. +- **Role separation across running agents (decided 2026-08-31).** Spawned + subagents separate automatically — one task, fresh context, no shared + chat. Long-lived interactive sessions do not, so three rules hold: + **star, not mesh** — lanes never talk to each other or sideways; + everything crosses the orchestrator or is an artifact. **Role = task** — + an input artifact and an output artifact define a role, never chat + history; only the orchestrator lives long, and even it wakes fresh per + tick with the pool as its memory. **Identity lives in artifacts, not + session names** (names proved unstable across a restart on 2026-08-31): + lock files, slot infixes, status fields with defined writers. Enforced + by mechanics already decided: one worktree per lane, the run lock, + defined status operations, the record/nonce rules. The human-facing + sparring chat is the one persistent conversation; its boundary is + decisions and questions, never production. [owner: step 3] ## 10. Prior art: godarkfactory.com (reviewed 2026-08-30) @@ -610,9 +637,9 @@ framework, knowledge-graph disambiguation. ## 11. Open questions -Most carry the leaf or step that will answer them. Some do not: the three -parked topics below — hidden verification scenarios, the dashboard, the -shortcuts — and the stations near the end of this section that §1 draws but no +Most carry the leaf or step that will answer them. Some do not: the two +parked topics below — hidden verification scenarios and the shortcuts — and +the stations near the end of this section that §1 draws but no leaf yet owns. Those are marked as such rather than counted as decomposed. - Sample percentage and drawing rule for the Sample-Gate (step 6). @@ -621,10 +648,11 @@ leaf yet owns. Those are marked as such rather than counted as decomposed. builder) as a Gate-B supplement — candidate small story, unscheduled. - How the clock's platform mechanics (loop/schedule) are configured per project — step 5. -- Process dashboard / console status — "when is what running where"; builds - on the computed views plus 2c's traces and analytics — not P8, which is - read-only over the ledger and git. Parked 2026-08-30, design - session planned 2026-08-31. +- Process dashboard / console status — decided 2026-08-31 and recorded as + the live-status view in §4 (snapshot per tick, rendered by code into + status.html / status line / optional menu bar, decision queue first, + staleness visible); still open: the snapshot schema and the owning leaf, + fed by 2c's traces and analytics. [2c / dashboard] - Sensible hooks and shortcuts through the pipeline for flexible use cases. Parked 2026-08-30, same session. - Test layers are decided (§9); still open: whether a smoke failure that From 8bdb7cfc291de2be3b1441335a93596b2cebc593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 18:58:30 +0200 Subject: [PATCH 110/117] docs(vision): shortcuts and hooks (Daniel, 2026-08-31) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shortcuts are shorter lanes, never side doors: depth per card (trivial lane collapses spec/plan), speed per priority (standing hotfix wave, full Verify), consequence-freedom per report (experiments never merge), and hand work passes the same gates — four eyes has no owner exception. Hooks are subscribers to station-boundary events the trace layer emits anyway: passive ones notify/render/log, active ones may only create a pool story; the E2E loop is the first active hook. §11's last parked pair resolved to a pointer; one parked topic remains (hidden verification scenarios). --- .../specs/2026-08-30-dark-factory-vision.md | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index 90b2ff7..181be84 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -497,6 +497,31 @@ proceeds, the breaking part waits on the meta-story). defined status operations, the record/nonce rules. The human-facing sparring chat is the one persistent conversation; its boundary is decisions and questions, never production. [owner: step 3] +- **Shortcuts are shorter lanes, never side doors (decided 2026-08-31).** + Everything enters through the pool (seconds) and leaves through Verify + and the merge-queue smoke; what shrinks in between is decided by the + card, never by the builder (decision 8: an author never rates itself + trivial). Four cases: a **trivial lane** collapses Spec- and Plan-Loop — + the story text is the spec, no plan — with the light review the profile + rules already grant; a **hotfix lane** gets speed from priority, not + from skipped review — a standing "hotfix" wave bypasses wave steering + and jumps the queue, Verify stays full; an **experiment** runs in a + throwaway worktree and never merges — its artifact is a report, not a + diff, so no gates apply where nothing lands, and learnings become pool + stories; and **hand work by the human** is always allowed, but what + wants onto main passes the same gates — four eyes has no owner + exception. [owner: step 4 (card-driven depth), step 5 (hotfix wave)] +- **Hooks are subscribers to station-boundary events (decided + 2026-08-31).** The factory emits an event at every station boundary + (story arrived, classified, freigegeben, lane opened, merge landed, + escalation, audit drawn) — the trace infrastructure (2c) produces these + anyway. Project-owned hooks subscribe in two classes: **passive** hooks + notify, render or log (push, status line, dashboard and analytics are + the first four subscribers) and may change nothing; **active** hooks may + do exactly one thing — put a story into the pool (per the fix-permission + rule: never a direct fix), so everything mutating passes classification + and Freigabe. The E2E loop is the first active hook. [owner: 2c + (events), step 5 (subscriptions)] ## 10. Prior art: godarkfactory.com (reviewed 2026-08-30) @@ -637,8 +662,8 @@ framework, knowledge-graph disambiguation. ## 11. Open questions -Most carry the leaf or step that will answer them. Some do not: the two -parked topics below — hidden verification scenarios and the shortcuts — and +Most carry the leaf or step that will answer them. Some do not: the one +parked topic below — hidden verification scenarios — and the stations near the end of this section that §1 draws but no leaf yet owns. Those are marked as such rather than counted as decomposed. @@ -653,8 +678,10 @@ leaf yet owns. Those are marked as such rather than counted as decomposed. status.html / status line / optional menu bar, decision queue first, staleness visible); still open: the snapshot schema and the owning leaf, fed by 2c's traces and analytics. [2c / dashboard] -- Sensible hooks and shortcuts through the pipeline for flexible use cases. - Parked 2026-08-30, same session. +- Hooks and shortcuts — decided 2026-08-31 and recorded in §9 (shortcuts + are shorter lanes, never side doors; hooks are subscribers to + station-boundary events, passive or story-creating). Still open: the + event schema, with 2c. [2c / step 5] - Test layers are decided (§9); still open: whether a smoke failure that returns to a lane is surfaced to the human (a dashboard question), and the E2E cadence / flaky-handling rules — step 5. From 92b072a92026f52af9bcb7ad6f288225c822af45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 19:10:27 +0200 Subject: [PATCH 111/117] docs(vision): map rebuilt as a road, synced to the gated doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One thick left-to-right road: Eingang → Pool → Freigabe → Takt → Orchestrator → Lane (Spec→Plan→Bau→Verify) → PR → Sample-Gate → Merge-Queue → main, with an exit arrow to the project CI. Branches off the road: Audit (drawn/not drawn, yes/no return), escalation ramp to the human, findings return lanes underneath. New gated stations drawn: Bewertungs-Loop as the single verdict producer, PR, Drift-Audit, Vet-Preflight, the computed-views card feeding Freigabe, an entry symbol. Encodings: color = operator (teal model, gray mechanics, orange human), stroke = status (dashed missing), ellipse = loop, hachure = store/view; thick arrows = road. Zones reduced to three that don't distort the road. --- .../2026-08-30-dark-factory-vision.excalidraw | 5846 +++++++++++------ 1 file changed, 3807 insertions(+), 2039 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw b/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw index be89155..1a6f3d4 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw @@ -6,10 +6,10 @@ { "id": "f-pool", "type": "frame", - "x": 164, - "y": 164, - "width": 1152, - "height": 292, + "x": 124, + "y": 104, + "width": 1132, + "height": 606, "angle": 0, "strokeColor": "#868e96", "backgroundColor": "transparent", @@ -21,51 +21,23 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 324838976, + "seed": 1582884707, "version": 1, - "versionNonce": 1152379866, + "versionNonce": 1583016050, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "name": "Intake-Zone · Stufe 4 proaktiv" + "name": "① INTAKE — klassifizieren, nie produzieren" }, { "id": "f-spec", "type": "frame", - "x": 1324, - "y": 604, - "width": 1552, - "height": 691, - "angle": 0, - "strokeColor": "#868e96", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 217893071, - "version": 1, - "versionNonce": 2037851939, - "isDeleted": false, - "boundElements": [], - "updated": 1788179982887, - "link": null, - "locked": false, - "name": "Produktion · eine Lane pro Ast (×lanes) · Review: anderes Modell, notfalls anderer Agent — nie der Autor" - }, - { - "id": "f-sample", - "type": "frame", - "x": 3044, - "y": 604, - "width": 1052, - "height": 216, + "x": 2504, + "y": 524, + "width": 1932, + "height": 591, "angle": 0, "strokeColor": "#868e96", "backgroundColor": "transparent", @@ -77,22 +49,22 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 780846360, + "seed": 1511164101, "version": 1, - "versionNonce": 1317990737, + "versionNonce": 278194589, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "name": "Abnahme · Smoke vor dem Landen, main ist nie rot" + "name": "② PRODUKTION — eine Lane pro Ast (×lanes) · Review: anderes Modell, nie der Autor" }, { - "id": "f-metrics", + "id": "f-vet", "type": "frame", - "x": 1964, - "y": 164, - "width": 1092, + "x": 2084, + "y": 1214, + "width": 1972, "height": 292, "angle": 0, "strokeColor": "#868e96", @@ -105,21 +77,21 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 54762750, + "seed": 1703527210, "version": 1, - "versionNonce": 151001551, + "versionNonce": 2068841389, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "name": "Überwachung" + "name": "③ ÜBERWACHUNG — beobachten, melden, nie selbst fixen" }, { "id": "h-title", "type": "text", "x": 200, - "y": -150, + "y": -170, "width": 369.6, "height": 35.0, "angle": 0, @@ -133,12 +105,12 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1877614492, + "seed": 1964953076, "version": 1, - "versionNonce": 446574991, + "versionNonce": 733610633, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "text": "Dark Factory · Landkarte", @@ -155,8 +127,8 @@ "id": "h-sub", "type": "text", "x": 200, - "y": -108, - "width": 707.85, + "y": -128, + "width": 1115.4, "height": 32.5, "angle": 0, "strokeColor": "#495057", @@ -169,29 +141,29 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1318703119, + "seed": 755557971, "version": 1, - "versionNonce": 807946406, + "versionNonce": 182378959, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Mehrere Loops bilden den Graphen. Auf den Kanten wandert nur das Artefakt.\nRot gestrichelt = Eskalation zu dir · Kreise = interne Loops (iterieren bis clean) · Rahmen = Zonen", + "text": "Lies die Straße von links nach rechts: Eingang → Pool → Freigabe (deine Schranke) → Takt → Orchestrator → Lane → PR → Stichprobe → Merge-Queue → main.\nTürkis arbeitet ein Modell, Grau Mechanik, Orange bist du · gestrichelt = fehlt noch · dicke Pfeile = die Straße, dünne = Nebenwege, rote = Ausfahrt zu dir.", "fontSize": 13, "fontFamily": 2, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "Mehrere Loops bilden den Graphen. Auf den Kanten wandert nur das Artefakt.\nRot gestrichelt = Eskalation zu dir · Kreise = interne Loops (iterieren bis clean) · Rahmen = Zonen", + "originalText": "Lies die Straße von links nach rechts: Eingang → Pool → Freigabe (deine Schranke) → Takt → Orchestrator → Lane → PR → Stichprobe → Merge-Queue → main.\nTürkis arbeitet ein Modell, Grau Mechanik, Orange bist du · gestrichelt = fehlt noch · dicke Pfeile = die Straße, dünne = Nebenwege, rote = Ausfahrt zu dir.", "autoResize": true, "lineHeight": 1.25 }, { - "id": "lg-auto", + "id": "lg-0", "type": "rectangle", "x": 200, - "y": -56, + "y": -66, "width": 16, "height": 16, "angle": 0, @@ -207,21 +179,21 @@ "roundness": { "type": 3 }, - "seed": 319009743, + "seed": 1471339731, "version": 1, - "versionNonce": 1362384196, + "versionNonce": 1016357713, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "lgt-auto", + "id": "lgt-0", "type": "text", "x": 224, - "y": -56, - "width": 171.60000000000002, + "y": -66, + "width": 85.80000000000001, "height": 15.0, "angle": 0, "strokeColor": "#495057", @@ -234,34 +206,34 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 541719408, + "seed": 1934530462, "version": 1, - "versionNonce": 2051851967, + "versionNonce": 166793565, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Loop / Station · existiert", + "text": "Modell-Knoten", "fontSize": 12, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "Loop / Station · existiert", + "originalText": "Modell-Knoten", "autoResize": true, "lineHeight": 1.25 }, { - "id": "lg-human", + "id": "lg-1", "type": "rectangle", - "x": 446, - "y": -56, + "x": 355, + "y": -66, "width": 16, "height": 16, "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -272,21 +244,21 @@ "roundness": { "type": 3 }, - "seed": 746013369, + "seed": 1870675710, "version": 1, - "versionNonce": 1293384712, + "versionNonce": 1841028242, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "lgt-human", + "id": "lgt-1", "type": "text", - "x": 470, - "y": -56, - "width": 39.6, + "x": 379, + "y": -66, + "width": 85.80000000000001, "height": 15.0, "angle": 0, "strokeColor": "#495057", @@ -299,37 +271,37 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 782035029, + "seed": 895829542, "version": 1, - "versionNonce": 1018232522, + "versionNonce": 2027738559, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Mensch", + "text": "Mechanik/Code", "fontSize": 12, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "Mensch", + "originalText": "Mechanik/Code", "autoResize": true, "lineHeight": 1.25 }, { - "id": "lg-missing", + "id": "lg-2", "type": "rectangle", - "x": 552, - "y": -56, + "x": 510, + "y": -66, "width": 16, "height": 16, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], @@ -337,21 +309,21 @@ "roundness": { "type": 3 }, - "seed": 263801686, + "seed": 1697654549, "version": 1, - "versionNonce": 247719778, + "versionNonce": 65003596, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "lgt-missing", + "id": "lgt-2", "type": "text", - "x": 576, - "y": -56, - "width": 66.0, + "x": 534, + "y": -66, + "width": 72.60000000000001, "height": 15.0, "angle": 0, "strokeColor": "#495057", @@ -364,73 +336,127 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1823078164, + "seed": 1854293451, "version": 1, - "versionNonce": 1048118163, + "versionNonce": 1073582196, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "fehlt noch", + "text": "Mensch — du", "fontSize": 12, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "fehlt noch", + "originalText": "Mensch — du", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-phase0", + "id": "lg-3", "type": "rectangle", - "x": 1080, - "y": -60, - "width": 180, - "height": 99, + "x": 651, + "y": -66, + "width": 16, + "height": 16, "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "hachure", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 1230094741, + "version": 1, + "versionNonce": 31208372, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "lgt-3", + "type": "text", + "x": 675, + "y": -66, + "width": 105.60000000000001, + "height": 15.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1342092557, + "version": 1, + "versionNonce": 1422203800, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Speicher & Views", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "Speicher & Views", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lg-4", + "type": "rectangle", + "x": 827, + "y": -66, + "width": 16, + "height": 16, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, - "groupIds": [ - "g-phase0" - ], + "groupIds": [], "frameId": null, "roundness": { "type": 3 }, - "seed": 695425565, + "seed": 821055000, "version": 1, - "versionNonce": 2035525363, + "versionNonce": 814275163, "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-phase0" - }, - { - "id": "e-phase0-triage", - "type": "arrow" - } - ], - "updated": 1788179982887, + "boundElements": [], + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-phase0", + "id": "lgt-4", "type": "text", - "x": 1090.8, - "y": -46, + "x": 851, + "y": -66, "width": 158.4, - "height": 20.0, + "height": 15.0, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#495057", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -440,124 +466,106 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 323946140, + "seed": 1251293402, "version": 1, - "versionNonce": 847877000, + "versionNonce": 26730098, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Phase 0 · einmalig", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", + "text": "gestrichelt = fehlt noch", + "fontSize": 12, + "fontFamily": 3, + "textAlign": "left", "verticalAlign": "top", - "containerId": "n-phase0", - "originalText": "Phase 0 · einmalig", + "containerId": null, + "originalText": "gestrichelt = fehlt noch", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-phase0", + "id": "lg-loop", "type": "text", - "x": 1084.2, - "y": -20, - "width": 171.60000000000002, - "height": 45.0, + "x": 1059, + "y": -66, + "width": 184.8, + "height": 15.0, "angle": 0, - "strokeColor": "#495057", + "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, - "groupIds": [ - "g-phase0" - ], + "groupIds": [], "frameId": null, "roundness": null, - "seed": 1397871145, + "seed": 1307531725, "version": 1, - "versionNonce": 103694313, + "versionNonce": 155153760, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "vor Produktionsstart: Baum\nv1 + Ziele/Out-of-Scope ·\nWellen zuweisen", + "text": "⟳ = Loop, iteriert bis clean", "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", + "fontFamily": 3, + "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "vor Produktionsstart: Baum\nv1 + Ziele/Out-of-Scope ·\nWellen zuweisen", + "originalText": "⟳ = Loop, iteriert bis clean", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-pool", + "id": "n-inbox", "type": "rectangle", - "x": 200, - "y": 200, + "x": 40, + "y": 560, "width": 180, "height": 99, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [ - "g-pool" + "g-inbox" ], - "frameId": "f-pool", + "frameId": null, "roundness": { "type": 3 }, - "seed": 155555738, + "seed": 971477687, "version": 1, - "versionNonce": 1763673107, + "versionNonce": 1859167399, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-pool" - }, - { - "id": "e-pool-pruef", - "type": "arrow" - }, - { - "id": "e-pruef-pool", - "type": "arrow" - }, - { - "id": "e-pool-frei", - "type": "arrow" - }, - { - "id": "e-e2e-pool", - "type": "arrow" + "id": "t-inbox" }, { - "id": "e-orch-pool", + "id": "e-inbox-pool", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-pool", + "id": "t-inbox", "type": "text", - "x": 246.0, - "y": 214, - "width": 88.0, + "x": 90.4, + "y": 574, + "width": 79.2, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -568,31 +576,31 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-pool", + "frameId": null, "roundness": null, - "seed": 1150797846, + "seed": 1202102036, "version": 1, - "versionNonce": 202142729, + "versionNonce": 1840099286, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Story-Pool", + "text": "💡 Eingang", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-pool", - "originalText": "Story-Pool", + "containerId": "n-inbox", + "originalText": "💡 Eingang", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-pool", + "id": "s-inbox", "type": "text", - "x": 204.2, - "y": 240, + "x": 44.19999999999999, + "y": 600, "width": 171.60000000000002, "height": 45.0, "angle": 0, @@ -604,80 +612,74 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-pool" + "g-inbox" ], - "frameId": "f-pool", + "frameId": null, "roundness": null, - "seed": 785310973, + "seed": 1984727111, "version": 1, - "versionNonce": 1251527727, + "versionNonce": 1677652995, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "befüllen jederzeit,\nfolgenlos · Wellen · Kopf:\nlanes-Regler (deiner)", + "text": "Ideen · Stories · Bugs —\nvon dir, vom Team, von der\nFabrik selbst", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "befüllen jederzeit,\nfolgenlos · Wellen · Kopf:\nlanes-Regler (deiner)", + "originalText": "Ideen · Stories · Bugs —\nvon dir, vom Team, von der\nFabrik selbst", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-pruef", - "type": "ellipse", - "x": 620, - "y": 200, - "width": 220, - "height": 220, + "id": "n-phase0", + "type": "rectangle", + "x": 1040, + "y": -100, + "width": 180, + "height": 99, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [ - "g-pruef" + "g-phase0" ], - "frameId": "f-pool", - "roundness": null, - "seed": 124551739, + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 999975905, "version": 1, - "versionNonce": 1953574603, + "versionNonce": 970302524, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-pruef" - }, - { - "id": "e-pool-pruef", - "type": "arrow" - }, - { - "id": "e-pruef-pool", - "type": "arrow" + "id": "t-phase0" }, { - "id": "e-pruef-triage", + "id": "e-phase0-arch", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-pruef", + "id": "t-phase0", "type": "text", - "x": 681.6, - "y": 255.0, - "width": 96.80000000000001, + "x": 1050.8, + "y": -86, + "width": 158.4, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -688,33 +690,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-pool", + "frameId": null, "roundness": null, - "seed": 1089709947, + "seed": 1090580831, "version": 1, - "versionNonce": 461060839, + "versionNonce": 1836196521, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Intake-Loop", + "text": "Phase 0 · einmalig", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-pruef", - "originalText": "Intake-Loop", + "containerId": "n-phase0", + "originalText": "Phase 0 · einmalig", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-pruef", + "id": "s-phase0", "type": "text", - "x": 650.8, - "y": 281.0, - "width": 158.4, - "height": 60.0, + "x": 1044.2, + "y": -60, + "width": 171.60000000000002, + "height": 45.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -724,116 +726,106 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-pruef" + "g-phase0" ], - "frameId": "f-pool", + "frameId": null, "roundness": null, - "seed": 80521325, + "seed": 1261399322, "version": 1, - "versionNonce": 184570286, + "versionNonce": 407811517, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "neue Story (Batch) → vet\n+ Karte + Architektur-\nUrteil · löst KEINE\nProduktion aus", + "text": "Baum v1 + Ziele/Out-of-\nScope · Greenfield aus dem\nPool, Bestand aus dem Code", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "neue Story (Batch) → vet\n+ Karte + Architektur-\nUrteil · löst KEINE\nProduktion aus", + "originalText": "Baum v1 + Ziele/Out-of-\nScope · Greenfield aus dem\nPool, Bestand aus dem Code", "autoResize": true, "lineHeight": 1.25 }, { - "id": "b-pruef", - "type": "text", - "x": 642.275, - "y": 347.0, - "width": 175.45000000000002, - "height": 13.75, + "id": "n-pool", + "type": "rectangle", + "x": 340, + "y": 560, + "width": 180, + "height": 114, "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "hachure", "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, + "strokeStyle": "dashed", + "roughness": 1, "opacity": 100, "groupIds": [ - "g-pruef" + "g-pool" ], "frameId": "f-pool", - "roundness": null, - "seed": 931247022, + "roundness": { + "type": 3 + }, + "seed": 396555069, "version": 1, - "versionNonce": 898017870, - "isDeleted": false, - "boundElements": [], - "updated": 1788179982887, - "link": null, - "locked": false, - "text": "⟳ Klassifizierung · pro Story", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ Klassifizierung · pro Story", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-frei", - "type": "rectangle", - "x": 200, - "y": 640, - "width": 180, - "height": 144, - "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-frei" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 150013384, - "version": 1, - "versionNonce": 516819859, + "versionNonce": 1726362773, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-frei" + "id": "t-pool" + }, + { + "id": "e-inbox-pool", + "type": "arrow" }, { "id": "e-pool-frei", "type": "arrow" }, { - "id": "e-frei-takt", + "id": "e-pool-intake", + "type": "arrow" + }, + { + "id": "e-intake-pool", + "type": "arrow" + }, + { + "id": "e-bew-pool", + "type": "arrow" + }, + { + "id": "e-pool-views", + "type": "arrow" + }, + { + "id": "e-orch-pool", + "type": "arrow" + }, + { + "id": "e-e2e-pool", + "type": "arrow" + }, + { + "id": "e-drift-pool", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-frei", + "id": "t-pool", "type": "text", - "x": 254.8, - "y": 654, - "width": 70.4, + "x": 386.0, + "y": 574, + "width": 88.0, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -844,33 +836,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": null, + "frameId": "f-pool", "roundness": null, - "seed": 194804717, + "seed": 1099315426, "version": 1, - "versionNonce": 1183364968, + "versionNonce": 1021693764, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Freigabe", + "text": "Story-Pool", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-frei", - "originalText": "Freigabe", + "containerId": "n-pool", + "originalText": "Story-Pool", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-frei", + "id": "s-pool", "type": "text", - "x": 204.2, - "y": 680, - "width": 171.60000000000002, - "height": 90.0, + "x": 350.8, + "y": 600, + "width": 158.4, + "height": 60.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -880,76 +872,84 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-frei" + "g-pool" ], - "frameId": null, + "frameId": "f-pool", "roundness": null, - "seed": 911648020, + "seed": 1352662845, "version": 1, - "versionNonce": 126938844, + "versionNonce": 1318466551, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "dein „Go\" auf den\ngerenderten Wellen-Plan —\nder einzige Produktions-\nAuslöser · Regler: Story →\nBatch → Welle → auto ·\nFlags kommen immer zu dir", + "text": "Projekteingang & einzige\nWahrheit · befüllen ist\nfolgenlos · Kopf: lanes-\nRegler (deiner)", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "dein „Go\" auf den\ngerenderten Wellen-Plan —\nder einzige Produktions-\nAuslöser · Regler: Story →\nBatch → Welle → auto ·\nFlags kommen immer zu dir", + "originalText": "Projekteingang & einzige\nWahrheit · befüllen ist\nfolgenlos · Kopf: lanes-\nRegler (deiner)", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-takt", + "id": "n-intake", "type": "ellipse", - "x": 560, - "y": 640, + "x": 160, + "y": 140, "width": 220, "height": 220, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-takt" + "g-intake" ], - "frameId": null, + "frameId": "f-pool", "roundness": null, - "seed": 1775651416, + "seed": 1702904874, "version": 1, - "versionNonce": 1214302568, + "versionNonce": 399804947, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-takt" + "id": "t-intake" }, { - "id": "e-frei-takt", + "id": "e-pool-intake", "type": "arrow" }, { - "id": "e-takt-orch", + "id": "e-intake-pool", + "type": "arrow" + }, + { + "id": "e-intake-bew", + "type": "arrow" + }, + { + "id": "e-bew-intake", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-takt", + "id": "t-intake", "type": "text", - "x": 630.4, - "y": 695.0, - "width": 79.2, + "x": 221.6, + "y": 202.5, + "width": 96.80000000000001, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -960,33 +960,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": null, + "frameId": "f-pool", "roundness": null, - "seed": 265862674, + "seed": 202128586, "version": 1, - "versionNonce": 2034632751, + "versionNonce": 959051492, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Takt-Loop", + "text": "Intake-Loop", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-takt", - "originalText": "Takt-Loop", + "containerId": "n-intake", + "originalText": "Intake-Loop", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-takt", + "id": "s-intake", "type": "text", - "x": 607.3, - "y": 721.0, - "width": 125.4, - "height": 60.0, + "x": 197.39999999999998, + "y": 228.5, + "width": 145.20000000000002, + "height": 45.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -996,34 +996,34 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-takt" + "g-intake" ], - "frameId": null, + "frameId": "f-pool", "roundness": null, - "seed": 479402029, + "seed": 651549249, "version": 1, - "versionNonce": 1354258845, + "versionNonce": 304502735, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "pollt freigegebene\nStories der aktiven\nWelle → weckt den\nOrchestrator", + "text": "heftet die Karte an (8\nDimensionen) · löst\nKEINE Produktion aus", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "pollt freigegebene\nStories der aktiven\nWelle → weckt den\nOrchestrator", + "originalText": "heftet die Karte an (8\nDimensionen) · löst\nKEINE Produktion aus", "autoResize": true, "lineHeight": 1.25 }, { - "id": "b-takt", + "id": "b-intake", "type": "text", - "x": 651.85, - "y": 787.0, - "width": 36.300000000000004, + "x": 218.575, + "y": 279.5, + "width": 102.85000000000001, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -1034,80 +1034,84 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-takt" + "g-intake" ], - "frameId": null, + "frameId": "f-pool", "roundness": null, - "seed": 1347402587, + "seed": 194713491, "version": 1, - "versionNonce": 1251976313, + "versionNonce": 1156835504, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "⟳ Takt", + "text": "⟳ pro Story-Batch", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "⟳ Takt", + "originalText": "⟳ pro Story-Batch", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-triage", + "id": "n-bew", "type": "ellipse", - "x": 1060, - "y": 200, + "x": 600, + "y": 140, "width": 220, "height": 220, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-triage" + "g-bew" ], "frameId": "f-pool", "roundness": null, - "seed": 2035189461, + "seed": 1738698400, "version": 1, - "versionNonce": 132847737, + "versionNonce": 1912077537, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-triage" + "id": "t-bew" }, { - "id": "e-phase0-triage", + "id": "e-intake-bew", "type": "arrow" }, { - "id": "e-pruef-triage", + "id": "e-bew-intake", "type": "arrow" }, { - "id": "e-triage-esk", + "id": "e-bew-arch", + "type": "arrow" + }, + { + "id": "e-bew-pool", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-triage", + "id": "t-bew", "type": "text", - "x": 1121.6, - "y": 255.0, - "width": 96.80000000000001, + "x": 644.0, + "y": 202.5, + "width": 132.0, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -1120,31 +1124,31 @@ "groupIds": [], "frameId": "f-pool", "roundness": null, - "seed": 1239319144, + "seed": 1489147796, "version": 1, - "versionNonce": 1257440635, + "versionNonce": 1362476978, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Architektur", + "text": "Bewertungs-Loop", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-triage", - "originalText": "Architektur", + "containerId": "n-bew", + "originalText": "Bewertungs-Loop", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-triage", + "id": "s-bew", "type": "text", - "x": 1090.8, - "y": 281.0, - "width": 158.4, - "height": 60.0, + "x": 640.7, + "y": 228.5, + "width": 138.60000000000002, + "height": 45.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -1154,33 +1158,33 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-triage" + "g-bew" ], "frameId": "f-pool", "roundness": null, - "seed": 851864843, + "seed": 89937332, "version": 1, - "versionNonce": 106492239, + "versionNonce": 1278690150, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "AGENTS.md: Baum ·\nInvarianten · Ziele/Out-\nof-Scope · Änderung →\nÄste neu klassifiziert", + "text": "einziger Erzeuger des\nArchitektur-Urteils ·\nerzeugt Meta-Stories", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "AGENTS.md: Baum ·\nInvarianten · Ziele/Out-\nof-Scope · Änderung →\nÄste neu klassifiziert", + "originalText": "einziger Erzeuger des\nArchitektur-Urteils ·\nerzeugt Meta-Stories", "autoResize": true, "lineHeight": 1.25 }, { - "id": "b-triage", + "id": "b-bew", "type": "text", - "x": 1118.575, - "y": 347.0, + "x": 658.575, + "y": 279.5, "width": 102.85000000000001, "height": 13.75, "angle": 0, @@ -1192,16 +1196,16 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-triage" + "g-bew" ], "frameId": "f-pool", "roundness": null, - "seed": 2096491879, + "seed": 2109244315, "version": 1, - "versionNonce": 474769609, + "versionNonce": 850747066, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "text": "⟳ Bewertungs-Loop", @@ -1215,57 +1219,55 @@ "lineHeight": 1.25 }, { - "id": "n-spec", - "type": "ellipse", - "x": 1360, - "y": 640, - "width": 220, - "height": 220, + "id": "n-arch", + "type": "rectangle", + "x": 1040, + "y": 160, + "width": 180, + "height": 99, "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [ - "g-spec" + "g-arch" ], - "frameId": "f-spec", - "roundness": null, - "seed": 100035545, + "frameId": "f-pool", + "roundness": { + "type": 3 + }, + "seed": 2071801565, "version": 1, - "versionNonce": 1195428768, + "versionNonce": 972799062, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-spec" + "id": "t-arch" }, { - "id": "e-orch-spec", - "type": "arrow" - }, - { - "id": "e-spec-plan", + "id": "e-bew-arch", "type": "arrow" }, { - "id": "e-spec-esk", + "id": "e-phase0-arch", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-spec", + "id": "t-arch", "type": "text", - "x": 1430.4, - "y": 717.5, - "width": 79.2, + "x": 1081.6, + "y": 174, + "width": 96.80000000000001, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -1276,33 +1278,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-spec", + "frameId": "f-pool", "roundness": null, - "seed": 1843546982, + "seed": 1404287948, "version": 1, - "versionNonce": 285990743, + "versionNonce": 1586809770, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Spec-Loop", + "text": "Architektur", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-spec", - "originalText": "Spec-Loop", + "containerId": "n-arch", + "originalText": "Architektur", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-spec", + "id": "s-arch", "type": "text", - "x": 1390.8, - "y": 743.5, - "width": 158.4, - "height": 15.0, + "x": 1040.9, + "y": 200, + "width": 178.20000000000002, + "height": 45.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -1312,118 +1314,94 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-spec" + "g-arch" ], - "frameId": "f-spec", + "frameId": "f-pool", "roundness": null, - "seed": 621931212, + "seed": 1321910848, "version": 1, - "versionNonce": 900094242, + "versionNonce": 1396016797, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "intake + Design + Gate A", + "text": "AGENTS.md: Baum ·\nInvarianten · Ziele/Out-of-\nScope — von allen gelesen", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "intake + Design + Gate A", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-spec", - "type": "text", - "x": 1430.675, - "y": 764.5, - "width": 78.65, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-spec" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 309785427, - "version": 1, - "versionNonce": 1161114103, - "isDeleted": false, - "boundElements": [], - "updated": 1788179982887, - "link": null, - "locked": false, - "text": "⟳ Gate-A-Loop", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ Gate-A-Loop", + "originalText": "AGENTS.md: Baum ·\nInvarianten · Ziele/Out-of-\nScope — von allen gelesen", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-plan", - "type": "ellipse", - "x": 1780, - "y": 640, - "width": 220, - "height": 220, + "id": "n-esk", + "type": "rectangle", + "x": 3060, + "y": 80, + "width": 180, + "height": 84, "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [ - "g-plan" + "g-esk" ], - "frameId": "f-spec", - "roundness": null, - "seed": 252956897, + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 338215988, "version": 1, - "versionNonce": 1226027821, + "versionNonce": 1338158164, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-plan" + "id": "t-esk" }, { - "id": "e-spec-plan", + "id": "e-orch-esk", "type": "arrow" }, { - "id": "e-plan-build", + "id": "e-spec-esk", "type": "arrow" }, { "id": "e-plan-esk", "type": "arrow" + }, + { + "id": "e-build-esk", + "type": "arrow" + }, + { + "id": "e-verify-esk", + "type": "arrow" + }, + { + "id": "e-judge-esk", + "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-plan", + "id": "t-esk", "type": "text", - "x": 1850.4, - "y": 717.5, - "width": 79.2, + "x": 3062.0, + "y": 94, + "width": 176.0, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -1434,33 +1412,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-spec", + "frameId": null, "roundness": null, - "seed": 662459677, + "seed": 32213827, "version": 1, - "versionNonce": 1203143341, + "versionNonce": 1786065641, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Plan-Loop", + "text": "⚠ Eskalation an dich", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-plan", - "originalText": "Plan-Loop", + "containerId": "n-esk", + "originalText": "⚠ Eskalation an dich", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-plan", + "id": "s-esk", "type": "text", - "x": 1847.1, - "y": 743.5, - "width": 85.80000000000001, - "height": 15.0, + "x": 3077.4, + "y": 120, + "width": 145.20000000000002, + "height": 30.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -1470,37 +1448,113 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-plan" + "g-esk" ], - "frameId": "f-spec", + "frameId": null, "roundness": null, - "seed": 1752618008, + "seed": 1134690140, "version": 1, - "versionNonce": 1464589643, + "versionNonce": 135645638, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Plan + Gate A", + "text": "stoppt & meldet, statt\nendlos zu drehen", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "Plan + Gate A", + "originalText": "stoppt & meldet, statt\nendlos zu drehen", "autoResize": true, "lineHeight": 1.25 }, { - "id": "b-plan", + "id": "n-metrics", + "type": "rectangle", + "x": 2540, + "y": 1250, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-metrics" + ], + "frameId": "f-vet", + "roundness": { + "type": 3 + }, + "seed": 127885034, + "version": 1, + "versionNonce": 76563763, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-metrics" + }, + { + "id": "e-metrics-sample", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-metrics", "type": "text", - "x": 1850.675, - "y": 764.5, - "width": 78.65, - "height": 13.75, + "x": 2555.2, + "y": 1264, + "width": 149.60000000000002, + "height": 20.0, "angle": 0, - "strokeColor": "#0b7285", + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-vet", + "roundness": null, + "seed": 408469126, + "version": 1, + "versionNonce": 1888851400, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Analytics + Trace", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-metrics", + "originalText": "Analytics + Trace", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-metrics", + "type": "text", + "x": 2544.2, + "y": 1290, + "width": 171.60000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -1508,98 +1562,74 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-plan" + "g-metrics" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 388106950, + "seed": 519568669, "version": 1, - "versionNonce": 221310450, + "versionNonce": 1287670035, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "⟳ Gate-A-Loop", - "fontSize": 11, - "fontFamily": 3, + "text": "SQLite: Kosten · Dauer ·\nRetries · Trace-ID · spec-\ndelta (2c)", + "fontSize": 12, + "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "⟳ Gate-A-Loop", + "originalText": "SQLite: Kosten · Dauer ·\nRetries · Trace-ID · spec-\ndelta (2c)", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-esk", + "id": "n-judge", "type": "rectangle", - "x": 1560, - "y": 240, + "x": 2960, + "y": 1250, "width": 180, - "height": 84, + "height": 99, "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-esk" + "g-judge" ], - "frameId": null, + "frameId": "f-vet", "roundness": { "type": 3 }, - "seed": 1248976841, + "seed": 64601867, "version": 1, - "versionNonce": 1226652085, + "versionNonce": 1670975073, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-esk" - }, - { - "id": "e-triage-esk", - "type": "arrow" - }, - { - "id": "e-orch-esk", - "type": "arrow" - }, - { - "id": "e-spec-esk", - "type": "arrow" - }, - { - "id": "e-plan-esk", - "type": "arrow" - }, - { - "id": "e-build-esk", - "type": "arrow" - }, - { - "id": "e-verify-esk", - "type": "arrow" + "id": "t-judge" }, { "id": "e-judge-esk", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-esk", + "id": "t-judge", "type": "text", - "x": 1562.0, - "y": 254, - "width": 176.0, + "x": 2979.6, + "y": 1264, + "width": 140.8, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -1610,33 +1640,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": null, + "frameId": "f-vet", "roundness": null, - "seed": 1372056228, + "seed": 996291672, "version": 1, - "versionNonce": 403449955, + "versionNonce": 700709642, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "⚠ Eskalation an dich", + "text": "Judge / Watchdog", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-esk", - "originalText": "⚠ Eskalation an dich", + "containerId": "n-judge", + "originalText": "Judge / Watchdog", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-esk", + "id": "s-judge", "type": "text", - "x": 1560.9, - "y": 280, + "x": 2960.9, + "y": 1290, "width": 178.20000000000002, - "height": 30.0, + "height": 45.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -1646,83 +1676,71 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-esk" + "g-judge" ], - "frameId": null, + "frameId": "f-vet", "roundness": null, - "seed": 799717634, + "seed": 946033373, "version": 1, - "versionNonce": 209230570, + "versionNonce": 1269075730, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "stoppt & meldet sich, statt\nendlos zu drehen", + "text": "Idle · Thrash · No-Progress\n→ killt & eskaliert ·\nurteilt nie über Qualität", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "stoppt & meldet sich, statt\nendlos zu drehen", + "originalText": "Idle · Thrash · No-Progress\n→ killt & eskaliert ·\nurteilt nie über Qualität", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-build", + "id": "n-e2e", "type": "ellipse", - "x": 2200, - "y": 640, + "x": 3380, + "y": 1250, "width": 220, "height": 220, "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-build" + "g-e2e" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 1176272277, + "seed": 1809098487, "version": 1, - "versionNonce": 1529246226, + "versionNonce": 419449462, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-build" - }, - { - "id": "e-plan-build", - "type": "arrow" - }, - { - "id": "e-build-verify", - "type": "arrow" - }, - { - "id": "e-verify-build", - "type": "arrow" + "id": "t-e2e" }, { - "id": "e-build-esk", + "id": "e-e2e-pool", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-build", + "id": "t-e2e", "type": "text", - "x": 2274.8, - "y": 710.0, + "x": 3454.8, + "y": 1312.5, "width": 70.4, "height": 20.0, "angle": 0, @@ -1734,33 +1752,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 134838300, + "seed": 1114810545, "version": 1, - "versionNonce": 1211971682, + "versionNonce": 501750180, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Bau-Loop", + "text": "E2E-Loop", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-build", - "originalText": "Bau-Loop", + "containerId": "n-e2e", + "originalText": "E2E-Loop", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-build", + "id": "s-e2e", "type": "text", - "x": 2267.1, - "y": 736.0, - "width": 85.80000000000001, - "height": 30.0, + "x": 3410.8, + "y": 1338.5, + "width": 158.4, + "height": 45.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -1770,37 +1788,37 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-build" + "g-e2e" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 127992539, + "seed": 1375015975, "version": 1, - "versionNonce": 1329312985, + "versionNonce": 631685691, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "goal-based ·\ntestgetrieben", + "text": "volle Suite · Fehlschlag\n→ Story in den Pool\n(Trace-ID)", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "goal-based ·\ntestgetrieben", + "originalText": "volle Suite · Fehlschlag\n→ Story in den Pool\n(Trace-ID)", "autoResize": true, "lineHeight": 1.25 }, { - "id": "b-build", + "id": "b-e2e", "type": "text", - "x": 2258.575, - "y": 772.0, - "width": 102.85000000000001, + "x": 3420.425, + "y": 1389.5, + "width": 139.15, "height": 13.75, "angle": 0, - "strokeColor": "#0b7285", + "strokeColor": "#495057", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -1808,33 +1826,33 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-build" + "g-e2e" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 442292976, + "seed": 1073254677, "version": 1, - "versionNonce": 1066042003, + "versionNonce": 9865044, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "⟳ TDD: rot → grün", + "text": "⟳ nightly / Wellen-Ende", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "⟳ TDD: rot → grün", + "originalText": "⟳ nightly / Wellen-Ende", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-verify", + "id": "n-drift", "type": "ellipse", - "x": 2620, - "y": 640, + "x": 3800, + "y": 1250, "width": 220, "height": 220, "angle": 0, @@ -1842,54 +1860,38 @@ "backgroundColor": "#c3fae8", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-verify" + "g-drift" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 1461147819, + "seed": 1422599544, "version": 1, - "versionNonce": 1141860530, + "versionNonce": 182511984, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-verify" - }, - { - "id": "e-build-verify", - "type": "arrow" - }, - { - "id": "e-verify-sample", - "type": "arrow" - }, - { - "id": "e-mq-verify", - "type": "arrow" - }, - { - "id": "e-verify-build", - "type": "arrow" + "id": "t-drift" }, { - "id": "e-verify-esk", + "id": "e-drift-pool", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-verify", + "id": "t-drift", "type": "text", - "x": 2703.6, - "y": 687.5, - "width": 52.800000000000004, + "x": 3861.6, + "y": 1312.5, + "width": 96.80000000000001, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -1900,33 +1902,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 918247488, + "seed": 982108444, "version": 1, - "versionNonce": 1669086093, + "versionNonce": 1406175817, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Verify", + "text": "Drift-Audit", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-verify", - "originalText": "Verify", + "containerId": "n-drift", + "originalText": "Drift-Audit", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-verify", + "id": "s-drift", "type": "text", - "x": 2654.1, - "y": 713.5, - "width": 151.8, - "height": 75.0, + "x": 3837.4, + "y": 1338.5, + "width": 145.20000000000002, + "height": 45.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -1936,33 +1938,33 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-verify" + "g-drift" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 674625912, + "seed": 597350287, "version": 1, - "versionNonce": 999872393, + "versionNonce": 873550682, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Gate B + Battery ·\nanderes Modell ·\nNotfall: gleiches\nModell, anderer Agent ·\nnie der Autor", + "text": "Docs-Drift · Code ohne\nSpec → nur melden,\nStories in den Pool", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "Gate B + Battery ·\nanderes Modell ·\nNotfall: gleiches\nModell, anderer Agent ·\nnie der Autor", + "originalText": "Docs-Drift · Code ohne\nSpec → nur melden,\nStories in den Pool", "autoResize": true, "lineHeight": 1.25 }, { - "id": "b-verify", + "id": "b-drift", "type": "text", - "x": 2690.675, - "y": 794.5, + "x": 3870.675, + "y": 1389.5, "width": 78.65, "height": 13.75, "angle": 0, @@ -1974,78 +1976,70 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-verify" + "g-drift" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 1257484521, + "seed": 2142450373, "version": 1, - "versionNonce": 1983075268, + "versionNonce": 1183838446, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "⟳ Gate-B-Loop", + "text": "⟳ Takt: Audit", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "⟳ Gate-B-Loop", + "originalText": "⟳ Takt: Audit", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-lane2", + "id": "n-vet", "type": "rectangle", - "x": 1360, - "y": 1000, + "x": 2120, + "y": 1250, "width": 180, - "height": 99, + "height": 114, "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-lane2" + "g-vet" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": { "type": 3 }, - "seed": 973206041, + "seed": 2138231514, "version": 1, - "versionNonce": 776492205, + "versionNonce": 2000778784, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-lane2" - }, - { - "id": "e-orch-lane2", - "type": "arrow" - }, - { - "id": "e-lane2-sample", - "type": "arrow" + "id": "t-vet" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-lane2", + "id": "t-vet", "type": "text", - "x": 1375.2, - "y": 1014, - "width": 149.60000000000002, + "x": 2152.8, + "y": 1264, + "width": 114.4, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -2056,33 +2050,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 643744727, + "seed": 1804349166, "version": 1, - "versionNonce": 533492028, + "versionNonce": 178678102, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Lane 2 · parallel", + "text": "Vet-Preflight", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-lane2", - "originalText": "Lane 2 · parallel", + "containerId": "n-vet", + "originalText": "Vet-Preflight", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-lane2", + "id": "s-vet", "type": "text", - "x": 1364.2, - "y": 1040, - "width": 171.60000000000002, - "height": 45.0, + "x": 2137.4, + "y": 1290, + "width": 145.20000000000002, + "height": 60.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -2092,77 +2086,77 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-lane2" + "g-vet" ], - "frameId": "f-spec", + "frameId": "f-vet", "roundness": null, - "seed": 1705916948, + "seed": 1520009599, "version": 1, - "versionNonce": 386046158, + "versionNonce": 545449556, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Spec → Plan → Bau → Verify\n· disjunkter Ast, eigener\nWorktree", + "text": "mechanische Checks vor\njedem Modell-Knoten:\nArtefakte · Formate ·\nUmgebung", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "Spec → Plan → Bau → Verify\n· disjunkter Ast, eigener\nWorktree", + "originalText": "mechanische Checks vor\njedem Modell-Knoten:\nArtefakte · Formate ·\nUmgebung", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-lane3", + "id": "n-views", "type": "rectangle", - "x": 1360, - "y": 1160, + "x": 760, + "y": 900, "width": 180, - "height": 99, + "height": 114, "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "hachure", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-lane3" + "g-views" ], - "frameId": "f-spec", + "frameId": null, "roundness": { "type": 3 }, - "seed": 1501079115, + "seed": 677052671, "version": 1, - "versionNonce": 1674671377, + "versionNonce": 1627771234, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-lane3" + "id": "t-views" }, { - "id": "e-orch-lane3", + "id": "e-pool-views", "type": "arrow" }, { - "id": "e-lane3-sample", + "id": "e-views-frei", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-lane3", + "id": "t-views", "type": "text", - "x": 1375.2, - "y": 1174, + "x": 775.2, + "y": 914, "width": 149.60000000000002, "height": 20.0, "angle": 0, @@ -2174,33 +2168,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-spec", + "frameId": null, "roundness": null, - "seed": 524193278, + "seed": 493203764, "version": 1, - "versionNonce": 175782304, + "versionNonce": 1101411390, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Lane 3 · parallel", + "text": "Views · gerechnet", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-lane3", - "originalText": "Lane 3 · parallel", + "containerId": "n-views", + "originalText": "Views · gerechnet", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-lane3", + "id": "s-views", "type": "text", - "x": 1360.9, - "y": 1200, - "width": 178.20000000000002, - "height": 45.0, + "x": 764.2, + "y": 940, + "width": 171.60000000000002, + "height": 60.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -2210,94 +2204,82 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-lane3" + "g-views" ], - "frameId": "f-spec", + "frameId": null, "roundness": null, - "seed": 1233565528, + "seed": 620708569, "version": 1, - "versionNonce": 644780075, + "versionNonce": 63891758, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "gleiche Pipeline · so viele\nLanes, wie der lanes-Regler\nerlaubt", + "text": "Roadmap · Wellen-Plan ·\nDashboard · As-built — aus\ndem Pool gerechnet, nie\ngepflegt", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "gleiche Pipeline · so viele\nLanes, wie der lanes-Regler\nerlaubt", + "originalText": "Roadmap · Wellen-Plan ·\nDashboard · As-built — aus\ndem Pool gerechnet, nie\ngepflegt", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-sample", + "id": "n-frei", "type": "rectangle", - "x": 3080, - "y": 640, + "x": 1340, + "y": 560, "width": 180, - "height": 144, + "height": 129, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [ - "g-sample" + "g-frei" ], - "frameId": "f-sample", + "frameId": null, "roundness": { "type": 3 }, - "seed": 1127850897, + "seed": 150803809, "version": 1, - "versionNonce": 1063254276, + "versionNonce": 1209338792, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-sample" - }, - { - "id": "e-lane2-sample", - "type": "arrow" - }, - { - "id": "e-lane3-sample", - "type": "arrow" - }, - { - "id": "e-verify-sample", - "type": "arrow" + "id": "t-frei" }, { - "id": "e-sample-audit", + "id": "e-pool-frei", "type": "arrow" }, { - "id": "e-sample-mq", + "id": "e-frei-takt", "type": "arrow" }, { - "id": "e-metrics-sample", + "id": "e-views-frei", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-sample", + "id": "t-frei", "type": "text", - "x": 3121.6, - "y": 654, - "width": 96.80000000000001, + "x": 1394.8, + "y": 574, + "width": 70.4, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -2308,33 +2290,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-sample", + "frameId": null, "roundness": null, - "seed": 1879343460, + "seed": 1645590713, "version": 1, - "versionNonce": 737608423, + "versionNonce": 231771288, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Sample-Gate", + "text": "Freigabe", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-sample", - "originalText": "Sample-Gate", + "containerId": "n-frei", + "originalText": "Freigabe", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-sample", + "id": "s-frei", "type": "text", - "x": 3084.2, - "y": 680, + "x": 1344.2, + "y": 600, "width": 171.60000000000002, - "height": 90.0, + "height": 75.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -2344,78 +2326,76 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-sample" + "g-frei" ], - "frameId": "f-sample", + "frameId": null, "roundness": null, - "seed": 1566471825, + "seed": 859852041, "version": 1, - "versionNonce": 963864094, + "versionNonce": 231505395, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "zieht x % der PRs ·\nmechanische Risiko-\nSchwellen (Zeilen/Dateien)\n· Architektur-Merges:\nZiehung 100 % (Startwert,\nRegler)", + "text": "dein „Go\" auf den Wellen-\nPlan — der einzige\nProduktions-Auslöser ·\nRegler bis Standing-Auto ·\nFlags immer zu dir", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "zieht x % der PRs ·\nmechanische Risiko-\nSchwellen (Zeilen/Dateien)\n· Architektur-Merges:\nZiehung 100 % (Startwert,\nRegler)", + "originalText": "dein „Go\" auf den Wellen-\nPlan — der einzige\nProduktions-Auslöser ·\nRegler bis Standing-Auto ·\nFlags immer zu dir", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-audit", - "type": "rectangle", - "x": 3480, - "y": 640, - "width": 180, - "height": 99, + "id": "n-takt", + "type": "ellipse", + "x": 1700, + "y": 560, + "width": 220, + "height": 220, "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-audit" + "g-takt" ], - "frameId": "f-sample", - "roundness": { - "type": 3 - }, - "seed": 618341637, + "frameId": null, + "roundness": null, + "seed": 1818024495, "version": 1, - "versionNonce": 1307729535, + "versionNonce": 624709210, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-audit" + "id": "t-takt" }, { - "id": "e-sample-audit", + "id": "e-frei-takt", "type": "arrow" }, { - "id": "e-audit-mq", + "id": "e-takt-orch", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-audit", + "id": "t-takt", "type": "text", - "x": 3548.0, - "y": 654, - "width": 44.0, + "x": 1770.4, + "y": 622.5, + "width": 79.2, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -2426,32 +2406,32 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-sample", + "frameId": null, "roundness": null, - "seed": 2104909469, + "seed": 830031502, "version": 1, - "versionNonce": 157197672, + "versionNonce": 143508822, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Audit", + "text": "Takt-Loop", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-audit", - "originalText": "Audit", + "containerId": "n-takt", + "originalText": "Takt-Loop", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-audit", + "id": "s-takt", "type": "text", - "x": 3484.2, - "y": 680, - "width": 171.60000000000002, + "x": 1734.1, + "y": 648.5, + "width": 151.8, "height": 45.0, "angle": 0, "strokeColor": "#495057", @@ -2462,86 +2442,130 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-audit" + "g-takt" ], - "frameId": "f-sample", + "frameId": null, "roundness": null, - "seed": 253544329, + "seed": 2057380367, "version": 1, - "versionNonce": 1099367391, + "versionNonce": 36272003, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "du prüfst die Stichprobe ·\narbeitet mit generierter\nPunchlist", + "text": "die Uhr: weckt den\nOrchestrator · einziges\nStück Plattform", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "du prüfst die Stichprobe ·\narbeitet mit generierter\nPunchlist", + "originalText": "die Uhr: weckt den\nOrchestrator · einziges\nStück Plattform", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-mq", - "type": "rectangle", - "x": 3880, - "y": 640, - "width": 180, - "height": 144, + "id": "b-takt", + "type": "text", + "x": 1791.85, + "y": 699.5, + "width": 36.300000000000004, + "height": 13.75, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-takt" + ], + "frameId": null, + "roundness": null, + "seed": 1819583883, + "version": 1, + "versionNonce": 1470806136, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "⟳ Takt", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Takt", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-orch", + "type": "ellipse", + "x": 2120, + "y": 560, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-mq" + "g-orch" ], - "frameId": "f-sample", - "roundness": { - "type": 3 - }, - "seed": 897911925, + "frameId": null, + "roundness": null, + "seed": 1170242, "version": 1, - "versionNonce": 354253419, + "versionNonce": 458491376, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-mq" + "id": "t-orch" }, { - "id": "e-sample-mq", + "id": "e-takt-orch", "type": "arrow" }, { - "id": "e-audit-mq", + "id": "e-orch-spec", "type": "arrow" }, { - "id": "e-mq-merge", + "id": "e-orch-pool", "type": "arrow" }, { - "id": "e-mq-verify", + "id": "e-orch-lane2", + "type": "arrow" + }, + { + "id": "e-orch-lane3", + "type": "arrow" + }, + { + "id": "e-orch-esk", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-mq", + "id": "t-orch", "type": "text", - "x": 3921.6, - "y": 654, - "width": 96.80000000000001, + "x": 2177.2, + "y": 615.0, + "width": 105.60000000000001, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -2552,33 +2576,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-sample", + "frameId": null, "roundness": null, - "seed": 1625947776, + "seed": 450364715, "version": 1, - "versionNonce": 734559256, + "versionNonce": 1990807841, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Merge-Queue", + "text": "Orchestrator", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-mq", - "originalText": "Merge-Queue", + "containerId": "n-orch", + "originalText": "Orchestrator", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-mq", + "id": "s-orch", "type": "text", - "x": 3880.9, - "y": 680, - "width": 178.20000000000002, - "height": 90.0, + "x": 2154.1, + "y": 641.0, + "width": 151.8, + "height": 60.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -2588,188 +2612,118 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-mq" + "g-orch" ], - "frameId": "f-sample", + "frameId": null, "roundness": null, - "seed": 326384299, + "seed": 1954794096, "version": 1, - "versionNonce": 2004182514, + "versionNonce": 112361914, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "seriell · pro Kandidat: auf\nmain rebasen → Smoke auf\nder Kombination → grün\nlandet · rot geht als\nArtefakt zurück in die Lane\n· main ist nie rot", + "text": "liest alles frisch ·\nrechnet den Plan (dry-\nrun) · prüft Drosseln ·\nöffnet Lanes", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "seriell · pro Kandidat: auf\nmain rebasen → Smoke auf\nder Kombination → grün\nlandet · rot geht als\nArtefakt zurück in die Lane\n· main ist nie rot", + "originalText": "liest alles frisch ·\nrechnet den Plan (dry-\nrun) · prüft Drosseln ·\nöffnet Lanes", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-merge", - "type": "rectangle", - "x": 3880, - "y": 640, - "width": 180, - "height": 99, + "id": "b-orch", + "type": "text", + "x": 2178.575, + "y": 707.0, + "width": 102.85000000000001, + "height": 13.75, "angle": 0, "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", + "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", - "roughness": 1, + "roughness": 0, "opacity": 100, "groupIds": [ - "g-merge" + "g-orch" ], - "frameId": "f-sample", - "roundness": { - "type": 3 - }, - "seed": 1050040258, + "frameId": null, + "roundness": null, + "seed": 1009289989, "version": 1, - "versionNonce": 905590325, + "versionNonce": 806301091, "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-merge" - }, - { - "id": "e-mq-merge", - "type": "arrow" - } - ], - "updated": 1788179982887, + "boundElements": [], + "updated": 1788196217117, "link": null, - "locked": false + "locked": false, + "text": "⟳ pro Tick frisch", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ pro Tick frisch", + "autoResize": true, + "lineHeight": 1.25 }, { - "id": "t-merge", - "type": "text", - "x": 3908.4, - "y": 654, - "width": 123.20000000000002, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-sample", - "roundness": null, - "seed": 84196940, - "version": 1, - "versionNonce": 2065920251, - "isDeleted": false, - "boundElements": [], - "updated": 1788179982887, - "link": null, - "locked": false, - "text": "Merge / Deploy", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-merge", - "originalText": "Merge / Deploy", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-merge", - "type": "text", - "x": 3880.9, - "y": 680, - "width": 178.20000000000002, - "height": 45.0, + "id": "n-spec", + "type": "ellipse", + "x": 2540, + "y": 560, + "width": 220, + "height": 220, "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-merge" - ], - "frameId": "f-sample", - "roundness": null, - "seed": 1434982633, - "version": 1, - "versionNonce": 166688708, - "isDeleted": false, - "boundElements": [], - "updated": 1788179982887, - "link": null, - "locked": false, - "text": "landet auf main, wenn beide\nGates + Smoke grün ·\nDeploy: Projekt-CI", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "landet auf main, wenn beide\nGates + Smoke grün ·\nDeploy: Projekt-CI", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-judge", - "type": "rectangle", - "x": 2400, - "y": 200, - "width": 180, - "height": 114, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [ - "g-judge" + "g-spec" ], - "frameId": "f-metrics", - "roundness": { - "type": 3 - }, - "seed": 1641903440, + "frameId": "f-spec", + "roundness": null, + "seed": 2105304789, "version": 1, - "versionNonce": 1198458558, + "versionNonce": 1522349327, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-judge" + "id": "t-spec" }, { - "id": "e-judge-esk", + "id": "e-orch-spec", + "type": "arrow" + }, + { + "id": "e-spec-plan", + "type": "arrow" + }, + { + "id": "e-spec-esk", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-judge", + "id": "t-spec", "type": "text", - "x": 2419.6, - "y": 214, - "width": 140.8, + "x": 2610.4, + "y": 630.0, + "width": 79.2, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -2780,33 +2734,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-metrics", + "frameId": "f-spec", "roundness": null, - "seed": 1230563834, + "seed": 853462374, "version": 1, - "versionNonce": 1694566832, + "versionNonce": 901495176, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Judge / Watchdog", + "text": "Spec-Loop", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-judge", - "originalText": "Judge / Watchdog", + "containerId": "n-spec", + "originalText": "Spec-Loop", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-judge", + "id": "s-spec", "type": "text", - "x": 2400.9, - "y": 240, - "width": 178.20000000000002, - "height": 60.0, + "x": 2574.1, + "y": 656.0, + "width": 151.8, + "height": 30.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -2816,113 +2770,37 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-judge" + "g-spec" ], - "frameId": "f-metrics", + "frameId": "f-spec", "roundness": null, - "seed": 1880074284, + "seed": 156848651, "version": 1, - "versionNonce": 1757400422, + "versionNonce": 1215994888, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "überwacht alle Loops: Idle\n· Thrash · No-Progress →\nkillt & eskaliert · nie das\nhängende Modell selbst", + "text": "Design + Gate A (Intake\nlief schon)", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "überwacht alle Loops: Idle\n· Thrash · No-Progress →\nkillt & eskaliert · nie das\nhängende Modell selbst", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-metrics", - "type": "rectangle", - "x": 2000, - "y": 200, - "width": 180, - "height": 114, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-metrics" - ], - "frameId": "f-metrics", - "roundness": { - "type": 3 - }, - "seed": 673767655, - "version": 1, - "versionNonce": 730407202, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-metrics" - }, - { - "id": "e-metrics-sample", - "type": "arrow" - } - ], - "updated": 1788179982887, - "link": null, - "locked": false - }, - { - "id": "t-metrics", - "type": "text", - "x": 2015.2, - "y": 214, - "width": 149.60000000000002, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-metrics", - "roundness": null, - "seed": 1493135432, - "version": 1, - "versionNonce": 752002366, - "isDeleted": false, - "boundElements": [], - "updated": 1788179982887, - "link": null, - "locked": false, - "text": "Analytics + Trace", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-metrics", - "originalText": "Analytics + Trace", + "originalText": "Design + Gate A (Intake\nlief schon)", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-metrics", + "id": "b-spec", "type": "text", - "x": 2004.2, - "y": 240, - "width": 171.60000000000002, - "height": 60.0, + "x": 2610.675, + "y": 692.0, + "width": 78.65, + "height": 13.75, "angle": 0, - "strokeColor": "#495057", + "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -2930,72 +2808,80 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-metrics" + "g-spec" ], - "frameId": "f-metrics", + "frameId": "f-spec", "roundness": null, - "seed": 1276399591, + "seed": 1351744129, "version": 1, - "versionNonce": 1066600998, + "versionNonce": 426254319, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "SQLite: Kosten · Dauer ·\nRetries pro Schritt ·\nTrace-ID pro Story · spec-\ndelta (= P8)", - "fontSize": 12, - "fontFamily": 2, + "text": "⟳ Gate-A-Loop", + "fontSize": 11, + "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "SQLite: Kosten · Dauer ·\nRetries pro Schritt ·\nTrace-ID pro Story · spec-\ndelta (= P8)", + "originalText": "⟳ Gate-A-Loop", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-e2e", + "id": "n-plan", "type": "ellipse", - "x": 2800, - "y": 200, + "x": 2960, + "y": 560, "width": 220, "height": 220, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [ - "g-e2e" + "g-plan" ], - "frameId": "f-metrics", + "frameId": "f-spec", "roundness": null, - "seed": 1245315470, + "seed": 1671839336, "version": 1, - "versionNonce": 1711312495, + "versionNonce": 1449356410, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-e2e" + "id": "t-plan" }, { - "id": "e-e2e-pool", + "id": "e-spec-plan", + "type": "arrow" + }, + { + "id": "e-plan-build", + "type": "arrow" + }, + { + "id": "e-plan-esk", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-e2e", + "id": "t-plan", "type": "text", - "x": 2874.8, - "y": 247.5, - "width": 70.4, + "x": 3030.4, + "y": 637.5, + "width": 79.2, "height": 20.0, "angle": 0, "strokeColor": "#1e1e1e", @@ -3006,33 +2892,33 @@ "roughness": 0, "opacity": 100, "groupIds": [], - "frameId": "f-metrics", + "frameId": "f-spec", "roundness": null, - "seed": 979693494, + "seed": 579338669, "version": 1, - "versionNonce": 147667305, + "versionNonce": 723471863, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "E2E-Loop", + "text": "Plan-Loop", "fontSize": 16, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-e2e", - "originalText": "E2E-Loop", + "containerId": "n-plan", + "originalText": "Plan-Loop", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-e2e", + "id": "s-plan", "type": "text", - "x": 2834.1, - "y": 273.5, - "width": 151.8, - "height": 75.0, + "x": 3027.1, + "y": 663.5, + "width": 85.80000000000001, + "height": 15.0, "angle": 0, "strokeColor": "#495057", "backgroundColor": "transparent", @@ -3042,34 +2928,34 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-e2e" + "g-plan" ], - "frameId": "f-metrics", + "frameId": "f-spec", "roundness": null, - "seed": 1803817088, + "seed": 187140746, "version": 1, - "versionNonce": 200995868, + "versionNonce": 668317600, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "volle Suite als Takt-\nLoop (nightly / Wellen-\nEnde) · Fehlschlag →\nautomatisch Story im\nPool, mit Trace-ID", + "text": "Plan + Gate A", "fontSize": 12, "fontFamily": 2, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "volle Suite als Takt-\nLoop (nightly / Wellen-\nEnde) · Fehlschlag →\nautomatisch Story im\nPool, mit Trace-ID", + "originalText": "Plan + Gate A", "autoResize": true, "lineHeight": 1.25 }, { - "id": "b-e2e", + "id": "b-plan", "type": "text", - "x": 2861.6, - "y": 354.5, - "width": 96.80000000000001, + "x": 3030.675, + "y": 684.5, + "width": 78.65, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3080,97 +2966,1751 @@ "roughness": 0, "opacity": 100, "groupIds": [ - "g-e2e" + "g-plan" ], - "frameId": "f-metrics", + "frameId": "f-spec", "roundness": null, - "seed": 2028687212, + "seed": 714222201, "version": 1, - "versionNonce": 579690177, + "versionNonce": 32533313, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "⟳ Takt · Stufe 3", + "text": "⟳ Gate-A-Loop", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "containerId": null, - "originalText": "⟳ Takt · Stufe 3", + "originalText": "⟳ Gate-A-Loop", "autoResize": true, "lineHeight": 1.25 }, { - "id": "n-orch", - "type": "rectangle", - "x": 960, - "y": 640, - "width": 180, - "height": 189, + "id": "n-build", + "type": "ellipse", + "x": 3380, + "y": 560, + "width": 220, + "height": 220, "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "#ffe3e3", + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [ - "g-orch" + "g-build" ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1018118421, + "frameId": "f-spec", + "roundness": null, + "seed": 2070337917, "version": 1, - "versionNonce": 1496886435, + "versionNonce": 880577645, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "t-orch" + "id": "t-build" }, { - "id": "e-takt-orch", + "id": "e-plan-build", "type": "arrow" }, { - "id": "e-orch-spec", + "id": "e-build-verify", "type": "arrow" }, { - "id": "e-orch-lane2", + "id": "e-audit-build", "type": "arrow" }, { - "id": "e-orch-lane3", + "id": "e-verify-build", "type": "arrow" }, { - "id": "e-orch-esk", + "id": "e-build-esk", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-build", + "type": "text", + "x": 3454.8, + "y": 630.0, + "width": 70.4, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 1627879603, + "version": 1, + "versionNonce": 1981800359, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Bau-Loop", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-build", + "originalText": "Bau-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-build", + "type": "text", + "x": 3424.0, + "y": 656.0, + "width": 132.0, + "height": 30.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-build" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 253385209, + "version": 1, + "versionNonce": 289043725, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "goal-based · AC sind\nread-only", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "goal-based · AC sind\nread-only", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-build", + "type": "text", + "x": 3438.575, + "y": 692.0, + "width": 102.85000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-build" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 529114096, + "version": 1, + "versionNonce": 1518305368, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "⟳ TDD: rot → grün", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ TDD: rot → grün", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-verify", + "type": "ellipse", + "x": 3800, + "y": 560, + "width": 220, + "height": 220, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-verify" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 216995297, + "version": 1, + "versionNonce": 23525720, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-verify" + }, + { + "id": "e-build-verify", "type": "arrow" }, { - "id": "e-orch-pool", + "id": "e-verify-pr", + "type": "arrow" + }, + { + "id": "e-verify-build", + "type": "arrow" + }, + { + "id": "e-mq-verify", + "type": "arrow" + }, + { + "id": "e-verify-esk", "type": "arrow" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false }, { - "id": "t-orch", + "id": "t-verify", + "type": "text", + "x": 3883.6, + "y": 622.5, + "width": 52.800000000000004, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 128620107, + "version": 1, + "versionNonce": 998450711, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Verify", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-verify", + "originalText": "Verify", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-verify", + "type": "text", + "x": 3834.1, + "y": 648.5, + "width": 151.8, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-verify" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 1711590672, + "version": 1, + "versionNonce": 1045445060, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Battery + Gate B ·\nanderes Modell, nie der\nAutor", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Battery + Gate B ·\nanderes Modell, nie der\nAutor", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "b-verify", + "type": "text", + "x": 3870.675, + "y": 699.5, + "width": 78.65, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-verify" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 381560869, + "version": 1, + "versionNonce": 1464750150, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "⟳ Gate-B-Loop", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "⟳ Gate-B-Loop", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-pr", + "type": "rectangle", + "x": 4220, + "y": 560, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-pr" + ], + "frameId": "f-spec", + "roundness": { + "type": 3 + }, + "seed": 1201077169, + "version": 1, + "versionNonce": 404554714, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-pr" + }, + { + "id": "e-verify-pr", + "type": "arrow" + }, + { + "id": "e-pr-sample", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-pr", + "type": "text", + "x": 4301.2, + "y": 574, + "width": 17.6, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 960837608, + "version": 1, + "versionNonce": 1092780211, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "PR", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-pr", + "originalText": "PR", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-pr", + "type": "text", + "x": 4230.8, + "y": 600, + "width": 158.4, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-pr" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 409491659, + "version": 1, + "versionNonce": 2115875710, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "PR auf · Bots sind\nopportunistisch — nichts\nblockiert", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "PR auf · Bots sind\nopportunistisch — nichts\nblockiert", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-sample", + "type": "rectangle", + "x": 4580, + "y": 560, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-sample" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 1571730853, + "version": 1, + "versionNonce": 1652535920, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-sample" + }, + { + "id": "e-pr-sample", + "type": "arrow" + }, + { + "id": "e-sample-mq", + "type": "arrow" + }, + { + "id": "e-sample-audit", + "type": "arrow" + }, + { + "id": "e-lane2-sample", + "type": "arrow" + }, + { + "id": "e-lane3-sample", + "type": "arrow" + }, + { + "id": "e-metrics-sample", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-sample", + "type": "text", + "x": 4621.6, + "y": 574, + "width": 96.80000000000001, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 281249670, + "version": 1, + "versionNonce": 900290631, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Sample-Gate", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-sample", + "originalText": "Sample-Gate", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-sample", + "type": "text", + "x": 4584.2, + "y": 600, + "width": 171.60000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-sample" + ], + "frameId": null, + "roundness": null, + "seed": 1382367706, + "version": 1, + "versionNonce": 824100700, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "mechanische Ziehung: x % ·\nRisiko-Schwellen ·\nArchitektur immer", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "mechanische Ziehung: x % ·\nRisiko-Schwellen ·\nArchitektur immer", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-audit", + "type": "rectangle", + "x": 4540, + "y": 240, + "width": 180, + "height": 84, + "angle": 0, + "strokeColor": "#e8590c", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-audit" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 250199008, + "version": 1, + "versionNonce": 847941153, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-audit" + }, + { + "id": "e-sample-audit", + "type": "arrow" + }, + { + "id": "e-audit-mq", + "type": "arrow" + }, + { + "id": "e-audit-build", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-audit", + "type": "text", + "x": 4608.0, + "y": 254, + "width": 44.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 903565808, + "version": 1, + "versionNonce": 2126121013, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Audit", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-audit", + "originalText": "Audit", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-audit", + "type": "text", + "x": 4544.2, + "y": 280, + "width": 171.60000000000002, + "height": 30.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-audit" + ], + "frameId": null, + "roundness": null, + "seed": 457125559, + "version": 1, + "versionNonce": 1011272, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "du, mit Punchlist · einmal\n„Nein\" = immer wieder du", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "du, mit Punchlist · einmal\n„Nein\" = immer wieder du", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-mq", + "type": "rectangle", + "x": 5000, + "y": 560, + "width": 180, + "height": 84, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-mq" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 579380293, + "version": 1, + "versionNonce": 1856143004, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-mq" + }, + { + "id": "e-sample-mq", + "type": "arrow" + }, + { + "id": "e-mq-merge", + "type": "arrow" + }, + { + "id": "e-audit-mq", + "type": "arrow" + }, + { + "id": "e-mq-verify", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-mq", + "type": "text", + "x": 5041.6, + "y": 574, + "width": 96.80000000000001, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 2085054259, + "version": 1, + "versionNonce": 2093489594, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Merge-Queue", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-mq", + "originalText": "Merge-Queue", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-mq", + "type": "text", + "x": 5004.2, + "y": 600, + "width": 171.60000000000002, + "height": 30.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-mq" + ], + "frameId": null, + "roundness": null, + "seed": 1725313060, + "version": 1, + "versionNonce": 1272900802, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "seriell: rebase auf main →\nSmoke → nur Grünes landet", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "seriell: rebase auf main →\nSmoke → nur Grünes landet", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-merge", + "type": "rectangle", + "x": 5420, + "y": 560, + "width": 180, + "height": 84, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "#e9ecef", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "dashed", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-merge" + ], + "frameId": null, + "roundness": { + "type": 3 + }, + "seed": 653146725, + "version": 1, + "versionNonce": 2142981916, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-merge" + }, + { + "id": "e-mq-merge", + "type": "arrow" + }, + { + "id": "e-exit", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-merge", + "type": "text", + "x": 5488.0, + "y": 574, + "width": 44.0, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1900233367, + "version": 1, + "versionNonce": 42156640, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Merge", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-merge", + "originalText": "Merge", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-merge", + "type": "text", + "x": 5420.9, + "y": 600, + "width": 178.20000000000002, + "height": 30.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-merge" + ], + "frameId": null, + "roundness": null, + "seed": 452496762, + "version": 1, + "versionNonce": 402239492, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Fabrik endet hier ·\nRelease/Deploy = Projekt-CI", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Fabrik endet hier ·\nRelease/Deploy = Projekt-CI", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-lane2", + "type": "rectangle", + "x": 2540, + "y": 980, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-lane2" + ], + "frameId": "f-spec", + "roundness": { + "type": 3 + }, + "seed": 846698336, + "version": 1, + "versionNonce": 2138540917, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-lane2" + }, + { + "id": "e-orch-lane2", + "type": "arrow" + }, + { + "id": "e-lane2-sample", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-lane2", + "type": "text", + "x": 2555.2, + "y": 994, + "width": 149.60000000000002, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 1834760427, + "version": 1, + "versionNonce": 1292824872, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Lane 2 · parallel", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-lane2", + "originalText": "Lane 2 · parallel", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-lane2", + "type": "text", + "x": 2544.2, + "y": 1020, + "width": 171.60000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-lane2" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 1378331574, + "version": 1, + "versionNonce": 1239011821, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Spec → Plan → Bau → Verify\n→ PR · disjunkter Ast,\neigener Worktree", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "Spec → Plan → Bau → Verify\n→ PR · disjunkter Ast,\neigener Worktree", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "n-lane3", + "type": "rectangle", + "x": 2960, + "y": 980, + "width": 180, + "height": 99, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "#c3fae8", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "g-lane3" + ], + "frameId": "f-spec", + "roundness": { + "type": 3 + }, + "seed": 215462959, + "version": 1, + "versionNonce": 90425851, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "t-lane3" + }, + { + "id": "e-orch-lane3", + "type": "arrow" + }, + { + "id": "e-lane3-sample", + "type": "arrow" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false + }, + { + "id": "t-lane3", + "type": "text", + "x": 2975.2, + "y": 994, + "width": 149.60000000000002, + "height": 20.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": "f-spec", + "roundness": null, + "seed": 2124509219, + "version": 1, + "versionNonce": 314302711, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Lane 3 · parallel", + "fontSize": 16, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "n-lane3", + "originalText": "Lane 3 · parallel", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "s-lane3", + "type": "text", + "x": 2960.9, + "y": 1020, + "width": 178.20000000000002, + "height": 45.0, + "angle": 0, + "strokeColor": "#495057", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [ + "g-lane3" + ], + "frameId": "f-spec", + "roundness": null, + "seed": 457936654, + "version": 1, + "versionNonce": 948203538, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "gleiche Pipeline · so\nviele, wie der lanes-Regler\nerlaubt", + "fontSize": 12, + "fontFamily": 2, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "gleiche Pipeline · so\nviele, wie der lanes-Regler\nerlaubt", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-inbox-pool", + "type": "arrow", + "x": 220, + "y": 609.5, + "width": 120, + "height": 7.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 554646833, + "version": 1, + "versionNonce": 20562661, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-inbox-pool" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 120, + 7.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-inbox", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-pool", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-inbox-pool", + "type": "text", + "x": 216.475, + "y": 605.25, + "width": 127.05000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1659338477, + "version": 1, + "versionNonce": 1310680491, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "jederzeit · folgenlos", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-inbox-pool", + "originalText": "jederzeit · folgenlos", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-pool-frei", + "type": "arrow", + "x": 520, + "y": 617.0, + "width": 820, + "height": 7.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 706426385, + "version": 1, + "versionNonce": 1783033082, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-pool-frei" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 820, + 7.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-pool", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-frei", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-pool-frei", + "type": "text", + "x": 863.45, + "y": 612.75, + "width": 133.10000000000002, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 636352585, + "version": 1, + "versionNonce": 829293676, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "klassifiziert & bereit", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-pool-frei", + "originalText": "klassifiziert & bereit", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-frei-takt", + "type": "arrow", + "x": 1520, + "y": 624.5, + "width": 180, + "height": 45.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 157622201, + "version": 1, + "versionNonce": 159517226, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-frei-takt" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 180, + 45.5 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-frei", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-takt", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-frei-takt", + "type": "text", + "x": 1552.525, + "y": 639.25, + "width": 114.95, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 193525164, + "version": 1, + "versionNonce": 448251369, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Status: freigegeben", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-frei-takt", + "originalText": "Status: freigegeben", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-takt-orch", + "type": "arrow", + "x": 1920, + "y": 670.0, + "width": 200, + "height": 0.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1251413446, + "version": 1, + "versionNonce": 1367061054, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-takt-orch" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 200, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-takt", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-orch", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-takt-orch", + "type": "text", + "x": 1983.7, + "y": 662.0, + "width": 72.60000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 521866285, + "version": 1, + "versionNonce": 33305479, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "weckt (Tick)", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-takt-orch", + "originalText": "weckt (Tick)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-orch-spec", + "type": "arrow", + "x": 2340, + "y": 670.0, + "width": 200, + "height": 0.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1291247220, + "version": 1, + "versionNonce": 791723976, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-orch-spec" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 200, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-orch", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-spec", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-orch-spec", + "type": "text", + "x": 2352.275, + "y": 662.0, + "width": 175.45000000000002, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 798228357, + "version": 1, + "versionNonce": 1336055300, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "öffnet Lane 1 · Story + Karte", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-orch-spec", + "originalText": "öffnet Lane 1 · Story + Karte", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-spec-plan", + "type": "arrow", + "x": 2760, + "y": 670.0, + "width": 200, + "height": 0.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 973256993, + "version": 1, + "versionNonce": 273205204, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-spec-plan" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 200, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-spec", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-plan", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-spec-plan", "type": "text", - "x": 997.2, - "y": 654, - "width": 105.60000000000001, - "height": 20.0, + "x": 2847.9, + "y": 662.0, + "width": 24.200000000000003, + "height": 13.75, "angle": 0, - "strokeColor": "#1e1e1e", + "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, @@ -3180,101 +4720,157 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1426256014, + "seed": 2059726030, "version": 1, - "versionNonce": 139586394, + "versionNonce": 1261155441, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Orchestrator", - "fontSize": 16, - "fontFamily": 2, + "text": "Spec", + "fontSize": 11, + "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "n-orch", - "originalText": "Orchestrator", + "containerId": "e-spec-plan", + "originalText": "Spec", "autoResize": true, "lineHeight": 1.25 }, { - "id": "s-orch", + "id": "e-plan-build", + "type": "arrow", + "x": 3180, + "y": 670.0, + "width": 200, + "height": 0.0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1038790516, + "version": 1, + "versionNonce": 1787477839, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-plan-build" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 200, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-plan", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": { + "elementId": "n-build", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "l-plan-build", "type": "text", - "x": 960.9, - "y": 680, - "width": 178.20000000000002, - "height": 135.0, + "x": 3267.9, + "y": 662.0, + "width": 24.200000000000003, + "height": 13.75, "angle": 0, - "strokeColor": "#495057", + "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, - "groupIds": [ - "g-orch" - ], + "groupIds": [], "frameId": null, "roundness": null, - "seed": 130286598, + "seed": 1233882367, "version": 1, - "versionNonce": 1570152712, + "versionNonce": 291495025, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "verteilt Lanes (aktive\nWelle, lanes-Regler) ·\nreicht Artefakte weiter ·\ndry-run vor Token-Spend ·\nFrischsession ab Versuch N\n· Drosseln: Pflicht-Stop\noffen, Entscheidungs-Stau —\nheute: Sparring-Session von\nHand", - "fontSize": 12, - "fontFamily": 2, + "text": "Plan", + "fontSize": 11, + "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": null, - "originalText": "verteilt Lanes (aktive\nWelle, lanes-Regler) ·\nreicht Artefakte weiter ·\ndry-run vor Token-Spend ·\nFrischsession ab Versuch N\n· Drosseln: Pflicht-Stop\noffen, Entscheidungs-Stau —\nheute: Sparring-Session von\nHand", + "containerId": "e-plan-build", + "originalText": "Plan", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-phase0-triage", + "id": "e-build-verify", "type": "arrow", - "x": 1170.0, - "y": 39, - "width": 0.0, - "height": 161, + "x": 3600, + "y": 670.0, + "width": 200, + "height": 0.0, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", - "strokeWidth": 2, + "strokeWidth": 4, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, "roundness": null, - "seed": 1506442652, + "seed": 1860849707, "version": 1, - "versionNonce": 664876774, + "versionNonce": 829137059, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-phase0-triage" + "id": "l-build-verify" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0.0, - 0 + 0, + 0.0 ], [ - 0.0, - 161 + 200, + 0.0 ] ], "startArrowhead": null, @@ -3282,24 +4878,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-phase0", + "elementId": "n-build", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-triage", + "elementId": "n-verify", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-phase0-triage", + "id": "l-build-verify", "type": "text", - "x": 1124.625, - "y": 111.5, - "width": 90.75000000000001, + "x": 3687.9, + "y": 662.0, + "width": 24.200000000000003, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3312,53 +4908,53 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1389698625, + "seed": 392619526, "version": 1, - "versionNonce": 1241130074, + "versionNonce": 1346998123, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Baum v1 + Ziele", + "text": "Diff", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-phase0-triage", - "originalText": "Baum v1 + Ziele", + "containerId": "e-build-verify", + "originalText": "Diff", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-pool-pruef", + "id": "e-verify-pr", "type": "arrow", - "x": 380, - "y": 249.5, - "width": 240, + "x": 4020, + "y": 670.0, + "width": 200, "height": 60.5, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", - "strokeWidth": 2, + "strokeWidth": 4, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, "roundness": null, - "seed": 2132657286, + "seed": 331003081, "version": 1, - "versionNonce": 1462945690, + "versionNonce": 667462151, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-pool-pruef" + "id": "l-verify-pr" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -3367,8 +4963,8 @@ 0.0 ], [ - 240, - 60.5 + 200, + -60.5 ] ], "startArrowhead": null, @@ -3376,24 +4972,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-pool", + "elementId": "n-verify", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-pruef", + "elementId": "n-pr", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-pool-pruef", + "id": "l-verify-pr", "type": "text", - "x": 448.575, - "y": 271.75, - "width": 102.85000000000001, + "x": 4092.775, + "y": 631.75, + "width": 54.45, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3406,69 +5002,116 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1765070035, + "seed": 1950825112, "version": 1, - "versionNonce": 957006265, + "versionNonce": 490449539, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Event: neue Story", + "text": "grün → PR", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-pool-pruef", - "originalText": "Event: neue Story", + "containerId": "e-verify-pr", + "originalText": "grün → PR", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-pruef-pool", + "id": "e-pr-sample", "type": "arrow", - "x": 620, - "y": 400, - "width": 240, - "height": 221, + "x": 4400, + "y": 609.5, + "width": 180, + "height": 0.0, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeWidth": 4, + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, - "roundness": { - "type": 2 + "roundness": null, + "seed": 1756216314, + "version": 1, + "versionNonce": 1310920283, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0.0 + ], + [ + 180, + 0.0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-pr", + "focus": 0, + "gap": 6, + "fixedPoint": null }, - "seed": 611164248, + "endBinding": { + "elementId": "n-sample", + "focus": 0, + "gap": 6, + "fixedPoint": null + } + }, + { + "id": "e-sample-mq", + "type": "arrow", + "x": 4760, + "y": 609.5, + "width": 240, + "height": 7.5, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 535794085, "version": 1, - "versionNonce": 1538946474, + "versionNonce": 1557765238, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-pruef-pool" + "id": "l-sample-mq" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ 0, - 0 - ], - [ - -120.0, - 100 + 0.0 ], [ - -240, - -121 + 240, + -7.5 ] ], "startArrowhead": null, @@ -3476,24 +5119,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-pruef", + "elementId": "n-sample", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-pool", + "elementId": "n-mq", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-pruef-pool", + "id": "l-sample-mq", "type": "text", - "x": 451.6, - "y": 492, - "width": 96.80000000000001, + "x": 4789.25, + "y": 597.75, + "width": 181.50000000000003, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3506,53 +5149,53 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 828480808, + "seed": 407593829, "version": 1, - "versionNonce": 1904904517, + "versionNonce": 340323999, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Karte angeheftet", + "text": "nicht gezogen — der Normalfall", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-pruef-pool", - "originalText": "Karte angeheftet", + "containerId": "e-sample-mq", + "originalText": "nicht gezogen — der Normalfall", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-pruef-triage", + "id": "e-mq-merge", "type": "arrow", - "x": 840, - "y": 310.0, - "width": 220, + "x": 5180, + "y": 602.0, + "width": 240, "height": 0.0, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", - "strokeWidth": 2, + "strokeWidth": 4, "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, "roundness": null, - "seed": 1435920784, + "seed": 1587901785, "version": 1, - "versionNonce": 745188127, + "versionNonce": 1350718411, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-pruef-triage" + "id": "l-mq-merge" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -3561,7 +5204,7 @@ 0.0 ], [ - 220, + 240, 0.0 ] ], @@ -3570,24 +5213,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-pruef", + "elementId": "n-mq", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-triage", + "elementId": "n-merge", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-pruef-triage", + "id": "l-mq-merge", "type": "text", - "x": 901.6, - "y": 302.0, - "width": 96.80000000000001, + "x": 5242.525, + "y": 594.0, + "width": 114.95, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3600,31 +5243,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 48453508, + "seed": 2019504135, "version": 1, - "versionNonce": 2020027419, + "versionNonce": 1189558186, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "liest & bewertet", + "text": "Smoke grün → landet", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-pruef-triage", - "originalText": "liest & bewertet", + "containerId": "e-mq-merge", + "originalText": "Smoke grün → landet", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-pool-frei", + "id": "e-pool-intake", "type": "arrow", - "x": 290.0, - "y": 299, - "width": 0.0, - "height": 341, + "x": 360, + "y": 560, + "width": 90.0, + "height": 200, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -3636,27 +5279,27 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 991483082, + "seed": 422173362, "version": 1, - "versionNonce": 763353365, + "versionNonce": 1475265434, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-pool-frei" + "id": "l-pool-intake" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0.0, + 0, 0 ], [ - 0.0, - 341 + -90.0, + -200 ] ], "startArrowhead": null, @@ -3670,18 +5313,18 @@ "fixedPoint": null }, "endBinding": { - "elementId": "n-frei", + "elementId": "n-intake", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-pool-frei", + "id": "l-pool-intake", "type": "text", - "x": 178.075, - "y": 461.5, - "width": 223.85000000000002, + "x": 263.575, + "y": 452.0, + "width": 102.85000000000001, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3694,31 +5337,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 360881140, + "seed": 2040401216, "version": 1, - "versionNonce": 1311939741, + "versionNonce": 833674493, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "klassifiziert · Wellen-Plan gerendert", + "text": "Event: neue Story", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-pool-frei", - "originalText": "klassifiziert · Wellen-Plan gerendert", + "containerId": "e-pool-intake", + "originalText": "Event: neue Story", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-frei-takt", + "id": "e-intake-pool", "type": "arrow", "x": 380, - "y": 712.0, - "width": 180, - "height": 38.0, + "y": 340, + "width": 50.0, + "height": 220, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -3730,27 +5373,27 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 251461309, + "seed": 1894488072, "version": 1, - "versionNonce": 1060197638, + "versionNonce": 1036195671, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-frei-takt" + "id": "l-intake-pool" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ 0, - 0.0 + 0 ], [ - 180, - 38.0 + 50.0, + 220 ] ], "startArrowhead": null, @@ -3758,24 +5401,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-frei", + "elementId": "n-intake", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-takt", + "elementId": "n-pool", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-frei-takt", + "id": "l-intake-pool", "type": "text", - "x": 412.525, - "y": 723.0, - "width": 114.95, + "x": 356.6, + "y": 442.0, + "width": 96.80000000000001, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3788,31 +5431,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 126603649, + "seed": 1296079861, "version": 1, - "versionNonce": 468597630, + "versionNonce": 168572768, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Status: freigegeben", + "text": "Karte angeheftet", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-frei-takt", - "originalText": "Status: freigegeben", + "containerId": "e-intake-pool", + "originalText": "Karte angeheftet", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-takt-orch", + "id": "e-intake-bew", "type": "arrow", - "x": 780, - "y": 750.0, - "width": 180, - "height": 15.5, + "x": 380, + "y": 250.0, + "width": 220, + "height": 0.0, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -3824,17 +5467,17 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1649767777, + "seed": 905072500, "version": 1, - "versionNonce": 617255373, + "versionNonce": 101794100, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-takt-orch" + "id": "l-intake-bew" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -3843,8 +5486,8 @@ 0.0 ], [ - 180, - -15.5 + 220, + 0.0 ] ], "startArrowhead": null, @@ -3852,24 +5495,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-takt", + "elementId": "n-intake", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-orch", + "elementId": "n-bew", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-takt-orch", + "id": "l-intake-bew", "type": "text", - "x": 833.7, - "y": 734.25, - "width": 72.60000000000001, + "x": 444.625, + "y": 242.0, + "width": 90.75000000000001, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3882,63 +5525,69 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 277756008, + "seed": 222994022, "version": 1, - "versionNonce": 1585623284, + "versionNonce": 234177226, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "weckt (Tick)", + "text": "fragt Urteil an", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-takt-orch", - "originalText": "weckt (Tick)", + "containerId": "e-intake-bew", + "originalText": "fragt Urteil an", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-orch-spec", + "id": "e-bew-intake", "type": "arrow", - "x": 1140, - "y": 734.5, + "x": 600, + "y": 340, "width": 220, - "height": 15.5, + "height": 100, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, - "roundness": null, - "seed": 531748802, + "roundness": { + "type": 2 + }, + "seed": 83100045, "version": 1, - "versionNonce": 854478761, + "versionNonce": 1100275758, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-orch-spec" + "id": "l-bew-intake" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ 0, - 0.0 + 0 ], [ - 220, - 15.5 + -110.0, + 100 + ], + [ + -220, + 0 ] ], "startArrowhead": null, @@ -3946,24 +5595,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-orch", + "elementId": "n-bew", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-spec", + "elementId": "n-intake", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-orch-spec", + "id": "l-bew-intake", "type": "text", - "x": 1162.275, - "y": 734.25, - "width": 175.45000000000002, + "x": 435.55, + "y": 432, + "width": 108.9, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -3976,31 +5625,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 839558095, + "seed": 2067342801, "version": 1, - "versionNonce": 1968847850, + "versionNonce": 547991986, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "öffnet Lane 1 · Story + Karte", + "text": "Architektur-Urteil", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-orch-spec", - "originalText": "öffnet Lane 1 · Story + Karte", + "containerId": "e-bew-intake", + "originalText": "Architektur-Urteil", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-orch-lane2", + "id": "e-bew-arch", "type": "arrow", - "x": 1140, - "y": 734.5, + "x": 820, + "y": 250.0, "width": 220, - "height": 315.0, + "height": 40.5, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -4012,17 +5661,17 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1871364441, + "seed": 511975550, "version": 1, - "versionNonce": 1066240031, + "versionNonce": 1588769950, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-orch-lane2" + "id": "l-bew-arch" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -4032,7 +5681,7 @@ ], [ 220, - 315.0 + -40.5 ] ], "startArrowhead": null, @@ -4040,24 +5689,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-orch", + "elementId": "n-bew", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-lane2", + "elementId": "n-arch", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-orch-lane2", + "id": "l-bew-arch", "type": "text", - "x": 1210.675, - "y": 884.0, - "width": 78.65, + "x": 860.425, + "y": 221.75, + "width": 139.15, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4070,31 +5719,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 173047028, + "seed": 1513072866, "version": 1, - "versionNonce": 357268878, + "versionNonce": 840978158, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "öffnet Lane 2", + "text": "liest & pflegt den Baum", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-orch-lane2", - "originalText": "öffnet Lane 2", + "containerId": "e-bew-arch", + "originalText": "liest & pflegt den Baum", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-orch-lane3", + "id": "e-bew-pool", "type": "arrow", - "x": 1140, - "y": 734.5, - "width": 220, - "height": 475.0, + "x": 710.0, + "y": 360, + "width": 210.0, + "height": 200, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -4106,27 +5755,27 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 964622594, + "seed": 551863352, "version": 1, - "versionNonce": 862524476, + "versionNonce": 903693969, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-orch-lane3" + "id": "l-bew-pool" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0, - 0.0 + 0.0, + 0 ], [ - 220, - 475.0 + -210.0, + 200 ] ], "startArrowhead": null, @@ -4134,24 +5783,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-orch", + "elementId": "n-bew", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-lane3", + "elementId": "n-pool", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-orch-lane3", + "id": "l-bew-pool", "type": "text", - "x": 1195.55, - "y": 964.0, - "width": 108.9, + "x": 505.175, + "y": 452.0, + "width": 199.65, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4164,31 +5813,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1179913225, + "seed": 1768920593, "version": 1, - "versionNonce": 596654992, + "versionNonce": 1943094879, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "… bis lanes-Budget", + "text": "Meta-Stories · Re-Klassifizierung", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-orch-lane3", - "originalText": "… bis lanes-Budget", + "containerId": "e-bew-pool", + "originalText": "Meta-Stories · Re-Klassifizierung", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-lane2-sample", + "id": "e-phase0-arch", "type": "arrow", - "x": 1540, - "y": 1049.5, - "width": 1540, - "height": 285.5, + "x": 1130.0, + "y": -1, + "width": 0.0, + "height": 161, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -4199,34 +5848,28 @@ "opacity": 100, "groupIds": [], "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1897052333, + "roundness": null, + "seed": 1280901641, "version": 1, - "versionNonce": 294046656, + "versionNonce": 1054413414, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-lane2-sample" + "id": "l-phase0-arch" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0, - 0.0 - ], - [ - 1420, - -4.5 + 0.0, + 0 ], [ - 1540, - -285.5 + 0.0, + 161 ] ], "startArrowhead": null, @@ -4234,24 +5877,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-lane2", + "elementId": "n-phase0", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-sample", + "elementId": "n-arch", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-lane2-sample", + "id": "l-phase0-arch", "type": "text", - "x": 2953.95, - "y": 1037, - "width": 12.100000000000001, + "x": 1084.625, + "y": 71.5, + "width": 90.75000000000001, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4264,31 +5907,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1759390061, + "seed": 630147616, "version": 1, - "versionNonce": 924538201, + "versionNonce": 1116850875, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "PR", + "text": "Baum v1 + Ziele", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-lane2-sample", - "originalText": "PR", + "containerId": "e-phase0-arch", + "originalText": "Baum v1 + Ziele", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-lane3-sample", + "id": "e-pool-views", "type": "arrow", - "x": 1540, - "y": 1209.5, - "width": 1540, - "height": 445.5, + "x": 430.0, + "y": 674, + "width": 420.0, + "height": 226, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -4299,29 +5942,28 @@ "opacity": 100, "groupIds": [], "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1855392517, + "roundness": null, + "seed": 376740337, "version": 1, - "versionNonce": 1181587504, + "versionNonce": 1995519512, "isDeleted": false, - "boundElements": [], - "updated": 1788179982887, + "boundElements": [ + { + "type": "text", + "id": "l-pool-views" + } + ], + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0, - 0.0 - ], - [ - 1440, - -4.5 + 0.0, + 0 ], [ - 1540, - -445.5 + 420.0, + 226 ] ], "startArrowhead": null, @@ -4329,25 +5971,61 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-lane3", + "elementId": "n-pool", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-sample", + "elementId": "n-views", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "e-spec-plan", + "id": "l-pool-views", + "type": "text", + "x": 594.625, + "y": 779.0, + "width": 90.75000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1546946991, + "version": 1, + "versionNonce": 2098474247, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Status + Karten", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-pool-views", + "originalText": "Status + Karten", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-views-frei", "type": "arrow", - "x": 1580, - "y": 750.0, - "width": 200, - "height": 0.0, + "x": 940, + "y": 957.0, + "width": 490.0, + "height": 268.0, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -4359,17 +6037,17 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 597904679, + "seed": 147695603, "version": 1, - "versionNonce": 1516975390, + "versionNonce": 271442309, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-spec-plan" + "id": "l-views-frei" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -4378,8 +6056,8 @@ 0.0 ], [ - 200, - 0.0 + 490.0, + -268.0 ] ], "startArrowhead": null, @@ -4387,24 +6065,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-spec", + "elementId": "n-views", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-plan", + "elementId": "n-frei", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-spec-plan", + "id": "l-views-frei", "type": "text", - "x": 1667.9, - "y": 742.0, - "width": 24.200000000000003, + "x": 1115.425, + "y": 815.0, + "width": 139.15, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4417,31 +6095,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 891842471, + "seed": 490477406, "version": 1, - "versionNonce": 2118421926, + "versionNonce": 1029288706, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Spec", + "text": "gerenderter Wellen-Plan", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-spec-plan", - "originalText": "Spec", + "containerId": "e-views-frei", + "originalText": "gerenderter Wellen-Plan", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-plan-build", + "id": "e-orch-pool", "type": "arrow", - "x": 2000, - "y": 750.0, - "width": 200, - "height": 0.0, + "x": 2230.0, + "y": 560, + "width": 1710.0, + "height": 254, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -4452,28 +6130,34 @@ "opacity": 100, "groupIds": [], "frameId": null, - "roundness": null, - "seed": 770455201, + "roundness": { + "type": 2 + }, + "seed": 1201230074, "version": 1, - "versionNonce": 1466136595, + "versionNonce": 1403396873, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-plan-build" + "id": "l-orch-pool" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0, - 0.0 + 0.0, + 0 ], [ - 200, - 0.0 + -990.0, + -160 + ], + [ + -1710.0, + 94 ] ], "startArrowhead": null, @@ -4481,24 +6165,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-plan", + "elementId": "n-orch", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-build", + "elementId": "n-pool", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-plan-build", + "id": "l-orch-pool", "type": "text", - "x": 2087.9, - "y": 742.0, - "width": 24.200000000000003, + "x": 1161.35, + "y": 392, + "width": 157.3, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4511,31 +6195,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1898789635, + "seed": 1830514796, "version": 1, - "versionNonce": 816991461, + "versionNonce": 1322571943, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Plan", + "text": "zieht freigegebene Stories", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-plan-build", - "originalText": "Plan", + "containerId": "e-orch-pool", + "originalText": "zieht freigegebene Stories", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-build-verify", + "id": "e-sample-audit", "type": "arrow", - "x": 2420, - "y": 750.0, - "width": 200, - "height": 0.0, + "x": 4670.0, + "y": 560, + "width": 40.0, + "height": 236, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -4547,27 +6231,27 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 2056712111, + "seed": 1319205689, "version": 1, - "versionNonce": 495535104, + "versionNonce": 159214594, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-build-verify" + "id": "l-sample-audit" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0, - 0.0 + 0.0, + 0 ], [ - 200, - 0.0 + -40.0, + -236 ] ], "startArrowhead": null, @@ -4575,24 +6259,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-build", + "elementId": "n-sample", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-verify", + "elementId": "n-audit", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-build-verify", + "id": "l-sample-audit", "type": "text", - "x": 2507.9, - "y": 742.0, - "width": 24.200000000000003, + "x": 4553.2, + "y": 434.0, + "width": 193.60000000000002, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4605,31 +6289,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 324100191, + "seed": 601766565, "version": 1, - "versionNonce": 178208278, + "versionNonce": 456126060, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Diff", + "text": "gezogen: x % · Architektur immer", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-build-verify", - "originalText": "Diff", + "containerId": "e-sample-audit", + "originalText": "gezogen: x % · Architektur immer", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-verify-sample", + "id": "e-audit-mq", "type": "arrow", - "x": 2840, - "y": 750.0, - "width": 240, - "height": 38.0, + "x": 4720, + "y": 282.0, + "width": 370.0, + "height": 278.0, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -4641,17 +6325,17 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 378424697, + "seed": 1970015975, "version": 1, - "versionNonce": 324910815, + "versionNonce": 1964835650, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-verify-sample" + "id": "l-audit-mq" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -4660,8 +6344,8 @@ 0.0 ], [ - 240, - -38.0 + 370.0, + 278.0 ] ], "startArrowhead": null, @@ -4669,24 +6353,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-verify", + "elementId": "n-audit", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-sample", + "elementId": "n-mq", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-verify-sample", + "id": "l-audit-mq", "type": "text", - "x": 2953.95, - "y": 723.0, - "width": 12.100000000000001, + "x": 4865.675, + "y": 413.0, + "width": 78.65, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4699,53 +6383,55 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 498123580, + "seed": 438041302, "version": 1, - "versionNonce": 1414153797, + "versionNonce": 1608433291, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "PR", + "text": "„Ja\" → weiter", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-verify-sample", - "originalText": "PR", + "containerId": "e-audit-mq", + "originalText": "„Ja\" → weiter", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-sample-audit", + "id": "e-audit-build", "type": "arrow", - "x": 3260, - "y": 712.0, - "width": 220, - "height": 22.5, + "x": 4540, + "y": 282.0, + "width": 1050.0, + "height": 400, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, - "roundness": null, - "seed": 501085430, + "roundness": { + "type": 2 + }, + "seed": 35594107, "version": 1, - "versionNonce": 25905232, + "versionNonce": 148507777, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-sample-audit" + "id": "l-audit-build" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -4754,8 +6440,12 @@ 0.0 ], [ - 220, - -22.5 + -840, + -122.0 + ], + [ + -1050.0, + 278.0 ] ], "startArrowhead": null, @@ -4763,24 +6453,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-sample", + "elementId": "n-audit", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-audit", + "elementId": "n-build", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-sample-audit", + "id": "l-audit-build", "type": "text", - "x": 3348.825, - "y": 692.75, - "width": 42.35, + "x": 3624.375, + "y": 152, + "width": 151.25, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4793,37 +6483,37 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1041449536, + "seed": 578089555, "version": 1, - "versionNonce": 1784759832, + "versionNonce": 883531813, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "gezogen", + "text": "„Nein\" → Punchlist zurück", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-sample-audit", - "originalText": "gezogen", + "containerId": "e-audit-build", + "originalText": "„Nein\" → Punchlist zurück", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-sample-mq", + "id": "e-verify-build", "type": "arrow", - "x": 3170.0, - "y": 784, - "width": 800.0, + "x": 3800, + "y": 760, + "width": 200, "height": 100, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [], @@ -4831,30 +6521,30 @@ "roundness": { "type": 2 }, - "seed": 1265133104, + "seed": 957145326, "version": 1, - "versionNonce": 391578344, + "versionNonce": 534860933, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-sample-mq" + "id": "l-verify-build" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0.0, + 0, 0 ], [ - 400.0, + -100.0, 100 ], [ - 800.0, + -200, 0 ] ], @@ -4863,24 +6553,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-sample", + "elementId": "n-verify", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-mq", + "elementId": "n-build", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-sample-mq", + "id": "l-verify-build", "type": "text", - "x": 3530.675, - "y": 876, - "width": 78.65, + "x": 3654.625, + "y": 852, + "width": 90.75000000000001, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4893,63 +6583,69 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 564244067, + "seed": 129827362, "version": 1, - "versionNonce": 605441631, + "versionNonce": 100068740, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "nicht gezogen", + "text": "Findings zurück", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-sample-mq", - "originalText": "nicht gezogen", + "containerId": "e-verify-build", + "originalText": "Findings zurück", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-audit-mq", + "id": "e-mq-verify", "type": "arrow", - "x": 3660, - "y": 689.5, - "width": 220, - "height": 22.5, + "x": 5000, + "y": 624, + "width": 980, + "height": 236, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "solid", + "strokeStyle": "dashed", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, - "roundness": null, - "seed": 8790957, + "roundness": { + "type": 2 + }, + "seed": 378502112, "version": 1, - "versionNonce": 312837672, + "versionNonce": 605508529, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-audit-mq" + "id": "l-mq-verify" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ 0, - 0.0 + 0 ], [ - 220, - 22.5 + -490.0, + 236 + ], + [ + -980, + 136 ] ], "startArrowhead": null, @@ -4957,24 +6653,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-audit", + "elementId": "n-mq", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-mq", + "elementId": "n-verify", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-audit-mq", + "id": "l-mq-verify", "type": "text", - "x": 3763.95, - "y": 692.75, - "width": 12.100000000000001, + "x": 4419.25, + "y": 852, + "width": 181.50000000000003, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -4987,31 +6683,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 899680760, + "seed": 791960550, "version": 1, - "versionNonce": 1148025345, + "versionNonce": 1140321590, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "OK", + "text": "Smoke rot → zurück in die Lane", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-audit-mq", - "originalText": "OK", + "containerId": "e-mq-verify", + "originalText": "Smoke rot → zurück in die Lane", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-mq-merge", + "id": "e-orch-lane2", "type": "arrow", - "x": 4060, - "y": 712.0, - "width": 180, - "height": 22.5, + "x": 2230.0, + "y": 780, + "width": 310.0, + "height": 249.5, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -5023,27 +6719,27 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 792966007, + "seed": 1228724469, "version": 1, - "versionNonce": 1309562236, + "versionNonce": 2089641415, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-mq-merge" + "id": "l-orch-lane2" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0, - 0.0 + 0.0, + 0 ], [ - -180, - -22.5 + 310.0, + 249.5 ] ], "startArrowhead": null, @@ -5051,24 +6747,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-mq", + "elementId": "n-orch", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-merge", + "elementId": "n-lane2", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-mq-merge", + "id": "l-orch-lane2", "type": "text", - "x": 3912.525, - "y": 692.75, - "width": 114.95, + "x": 2345.675, + "y": 896.75, + "width": 78.65, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -5081,69 +6777,63 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1216208521, + "seed": 282562726, "version": 1, - "versionNonce": 684213371, + "versionNonce": 197981077, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Smoke grün · landet", + "text": "öffnet Lane 2", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-mq-merge", - "originalText": "Smoke grün · landet", + "containerId": "e-orch-lane2", + "originalText": "öffnet Lane 2", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-mq-verify", + "id": "e-orch-lane3", "type": "arrow", - "x": 3880, - "y": 764, - "width": 1040, - "height": 176, + "x": 2230.0, + "y": 780, + "width": 730.0, + "height": 249.5, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 2046762209, + "roundness": null, + "seed": 777700808, "version": 1, - "versionNonce": 269490964, + "versionNonce": 297229584, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-mq-verify" + "id": "l-orch-lane3" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ - 0, + 0.0, 0 ], [ - -520.0, - 176 - ], - [ - -1040, - 76 + 730.0, + 249.5 ] ], "startArrowhead": null, @@ -5151,24 +6841,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-mq", + "elementId": "n-orch", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-verify", + "elementId": "n-lane3", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-mq-verify", + "id": "l-orch-lane3", "type": "text", - "x": 3269.25, - "y": 932, - "width": 181.50000000000003, + "x": 2540.55, + "y": 896.75, + "width": 108.9, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -5181,37 +6871,37 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1482823831, + "seed": 1913279574, "version": 1, - "versionNonce": 1845122138, + "versionNonce": 967110115, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Smoke rot → zurück in die Lane", + "text": "… bis lanes-Budget", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-mq-verify", - "originalText": "Smoke rot → zurück in die Lane", + "containerId": "e-orch-lane3", + "originalText": "… bis lanes-Budget", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-verify-build", + "id": "e-lane2-sample", "type": "arrow", - "x": 2620, - "y": 840, - "width": 200, - "height": 100, + "x": 2720, + "y": 1029.5, + "width": 1950, + "height": 371, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], @@ -5219,31 +6909,31 @@ "roundness": { "type": 2 }, - "seed": 1107009420, + "seed": 2105593595, "version": 1, - "versionNonce": 2040590398, + "versionNonce": 710741819, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-verify-build" + "id": "l-lane2-sample" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ [ 0, - 0 + 0.0 ], [ - -100.0, - 100 + 1950, + 0.5 ], [ - -200, - 0 + 1880, + -370.5 ] ], "startArrowhead": null, @@ -5251,24 +6941,24 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-verify", + "elementId": "n-lane2", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-build", + "elementId": "n-sample", "focus": 0, "gap": 6, "fixedPoint": null } }, { - "id": "l-verify-build", + "id": "l-lane2-sample", "type": "text", - "x": 2474.625, - "y": 932, - "width": 90.75000000000001, + "x": 4663.95, + "y": 1022, + "width": 12.100000000000001, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -5281,53 +6971,50 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1326270331, + "seed": 1410750823, "version": 1, - "versionNonce": 1406529762, + "versionNonce": 1573901718, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Findings zurück", + "text": "PR", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-verify-build", - "originalText": "Findings zurück", + "containerId": "e-lane2-sample", + "originalText": "PR", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-triage-esk", + "id": "e-lane3-sample", "type": "arrow", - "x": 1280, - "y": 310.0, - "width": 280, - "height": 28.0, + "x": 3140, + "y": 1029.5, + "width": 1550, + "height": 431, "angle": 0, - "strokeColor": "#e03131", + "strokeColor": "#0b7285", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, - "strokeStyle": "dashed", + "strokeStyle": "solid", "roughness": 1, "opacity": 100, "groupIds": [], "frameId": null, - "roundness": null, - "seed": 1452128622, + "roundness": { + "type": 2 + }, + "seed": 1484387071, "version": 1, - "versionNonce": 1588675649, + "versionNonce": 1120855397, "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-triage-esk" - } - ], - "updated": 1788179982887, + "boundElements": [], + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5336,8 +7023,12 @@ 0.0 ], [ - 280, - -28.0 + 1550, + 60.5 + ], + [ + 1530.0, + -370.5 ] ], "startArrowhead": null, @@ -5345,61 +7036,25 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-triage", + "elementId": "n-lane3", "focus": 0, "gap": 6, "fixedPoint": null }, "endBinding": { - "elementId": "n-esk", + "elementId": "n-sample", "focus": 0, "gap": 6, "fixedPoint": null } }, - { - "id": "l-triage-esk", - "type": "text", - "x": 1383.7, - "y": 288.0, - "width": 72.60000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#c92a2a", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 115948851, - "version": 1, - "versionNonce": 980634927, - "isDeleted": false, - "boundElements": [], - "updated": 1788179982887, - "link": null, - "locked": false, - "text": "Neubewertung", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-triage-esk", - "originalText": "Neubewertung", - "autoResize": true, - "lineHeight": 1.25 - }, { "id": "e-orch-esk", "type": "arrow", - "x": 1050.0, - "y": 640, - "width": 600.0, - "height": 316, + "x": 2230.0, + "y": 560, + "width": 920.0, + "height": 396, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -5411,9 +7066,9 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1931732424, + "seed": 1255075915, "version": 1, - "versionNonce": 1870414235, + "versionNonce": 2033354169, "isDeleted": false, "boundElements": [ { @@ -5421,7 +7076,7 @@ "id": "l-orch-esk" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5430,8 +7085,8 @@ 0 ], [ - 600.0, - -316 + 920.0, + -396 ] ], "startArrowhead": null, @@ -5454,8 +7109,8 @@ { "id": "l-orch-esk", "type": "text", - "x": 1322.775, - "y": 474.0, + "x": 2662.775, + "y": 354.0, "width": 54.45, "height": 13.75, "angle": 0, @@ -5469,12 +7124,12 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1674971721, + "seed": 301393283, "version": 1, - "versionNonce": 2044160092, + "versionNonce": 1266748287, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "text": "eskaliert", @@ -5490,10 +7145,10 @@ { "id": "e-spec-esk", "type": "arrow", - "x": 1470.0, - "y": 640, - "width": 180.0, - "height": 316, + "x": 2650.0, + "y": 560, + "width": 500.0, + "height": 396, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -5505,12 +7160,12 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1878002762, + "seed": 75334865, "version": 1, - "versionNonce": 1461523904, + "versionNonce": 1983388773, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5519,8 +7174,8 @@ 0 ], [ - 180.0, - -316 + 500.0, + -396 ] ], "startArrowhead": null, @@ -5543,10 +7198,10 @@ { "id": "e-plan-esk", "type": "arrow", - "x": 1890.0, - "y": 640, - "width": 240.0, - "height": 316, + "x": 3070.0, + "y": 560, + "width": 80.0, + "height": 396, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -5558,12 +7213,12 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 1713419474, + "seed": 38427498, "version": 1, - "versionNonce": 1201026917, + "versionNonce": 1019609984, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5572,8 +7227,8 @@ 0 ], [ - -240.0, - -316 + 80.0, + -396 ] ], "startArrowhead": null, @@ -5596,10 +7251,10 @@ { "id": "e-build-esk", "type": "arrow", - "x": 2310.0, - "y": 640, - "width": 660.0, - "height": 316, + "x": 3490.0, + "y": 560, + "width": 340.0, + "height": 396, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -5611,12 +7266,12 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 842627282, + "seed": 1954666366, "version": 1, - "versionNonce": 854848018, + "versionNonce": 767665384, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5625,8 +7280,8 @@ 0 ], [ - -660.0, - -316 + -340.0, + -396 ] ], "startArrowhead": null, @@ -5649,10 +7304,10 @@ { "id": "e-verify-esk", "type": "arrow", - "x": 2730.0, - "y": 640, - "width": 1080.0, - "height": 316, + "x": 3910.0, + "y": 560, + "width": 760.0, + "height": 396, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -5664,9 +7319,9 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 856800515, + "seed": 1505321654, "version": 1, - "versionNonce": 846366295, + "versionNonce": 669595491, "isDeleted": false, "boundElements": [ { @@ -5674,7 +7329,7 @@ "id": "l-verify-esk" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5683,8 +7338,8 @@ 0 ], [ - -1080.0, - -316 + -760.0, + -396 ] ], "startArrowhead": null, @@ -5707,8 +7362,8 @@ { "id": "l-verify-esk", "type": "text", - "x": 2153.7, - "y": 474.0, + "x": 3493.7, + "y": 354.0, "width": 72.60000000000001, "height": 13.75, "angle": 0, @@ -5722,12 +7377,12 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 222344215, + "seed": 2067535009, "version": 1, - "versionNonce": 1034062383, + "versionNonce": 72030578, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "text": "Pflicht-Stop", @@ -5743,10 +7398,10 @@ { "id": "e-judge-esk", "type": "arrow", - "x": 2400, - "y": 257.0, - "width": 660, - "height": 25.0, + "x": 2960, + "y": 1299.5, + "width": 630, + "height": 1178.0, "angle": 0, "strokeColor": "#e03131", "backgroundColor": "transparent", @@ -5757,10 +7412,12 @@ "opacity": 100, "groupIds": [], "frameId": null, - "roundness": null, - "seed": 1362126470, + "roundness": { + "type": 2 + }, + "seed": 45653732, "version": 1, - "versionNonce": 859944004, + "versionNonce": 1284995613, "isDeleted": false, "boundElements": [ { @@ -5768,7 +7425,7 @@ "id": "l-judge-esk" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5777,8 +7434,16 @@ 0.0 ], [ - -660, - 25.0 + -530, + 0.5 + ], + [ + -530, + -1159.5 + ], + [ + 100, + -1177.5 ] ], "startArrowhead": null, @@ -5801,8 +7466,8 @@ { "id": "l-judge-esk", "type": "text", - "x": 2027.65, - "y": 261.5, + "x": 2387.65, + "y": 132, "width": 84.7, "height": 13.75, "angle": 0, @@ -5816,12 +7481,12 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 133676181, + "seed": 1366195859, "version": 1, - "versionNonce": 409330879, + "versionNonce": 160704061, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "text": "hängt / thrash", @@ -5837,10 +7502,10 @@ { "id": "e-metrics-sample", "type": "arrow", - "x": 2090.0, - "y": 314, - "width": 1080.0, - "height": 326, + "x": 2630.0, + "y": 1250, + "width": 2110.0, + "height": 591, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -5851,10 +7516,12 @@ "opacity": 100, "groupIds": [], "frameId": null, - "roundness": null, - "seed": 144627903, + "roundness": { + "type": 2 + }, + "seed": 1035593341, "version": 1, - "versionNonce": 2114557575, + "versionNonce": 144462451, "isDeleted": false, "boundElements": [ { @@ -5862,7 +7529,7 @@ "id": "l-metrics-sample" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5871,8 +7538,16 @@ 0 ], [ - 1080.0, - 326 + 0.0, + -70 + ], + [ + 2110.0, + -70 + ], + [ + 2110.0, + -591 ] ], "startArrowhead": null, @@ -5895,8 +7570,8 @@ { "id": "l-metrics-sample", "type": "text", - "x": 2581.6, - "y": 469.0, + "x": 4691.6, + "y": 1172, "width": 96.80000000000001, "height": 13.75, "angle": 0, @@ -5910,12 +7585,12 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 448315526, + "seed": 1568732475, "version": 1, - "versionNonce": 946239001, + "versionNonce": 668002832, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "text": "speist Schwellen", @@ -5931,10 +7606,10 @@ { "id": "e-e2e-pool", "type": "arrow", - "x": 2910.0, - "y": 200, - "width": 2620.0, - "height": 110, + "x": 3490.0, + "y": 1470, + "width": 3190.0, + "height": 906, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -5948,9 +7623,9 @@ "roundness": { "type": 2 }, - "seed": 348543443, + "seed": 684842185, "version": 1, - "versionNonce": 236069245, + "versionNonce": 293358725, "isDeleted": false, "boundElements": [ { @@ -5958,7 +7633,7 @@ "id": "l-e2e-pool" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -5968,15 +7643,15 @@ ], [ 0.0, - -110 + 90 ], [ - -2620.0, - -110 + -3190.0, + 90 ], [ - -2620.0, - 0 + -3150.0, + -816 ] ], "startArrowhead": null, @@ -5999,9 +7674,9 @@ { "id": "l-e2e-pool", "type": "text", - "x": 178.075, - "y": 82, - "width": 223.85000000000002, + "x": 212.27499999999998, + "y": 1552, + "width": 175.45000000000002, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -6014,31 +7689,31 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 730259659, + "seed": 2146104173, "version": 1, - "versionNonce": 1290051973, + "versionNonce": 155546662, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "Fehlschlag → Story im Pool (Trace-ID)", + "text": "Fehlschlag → Story (Trace-ID)", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "containerId": "e-e2e-pool", - "originalText": "Fehlschlag → Story im Pool (Trace-ID)", + "originalText": "Fehlschlag → Story (Trace-ID)", "autoResize": true, "lineHeight": 1.25 }, { - "id": "e-orch-pool", + "id": "e-drift-pool", "type": "arrow", - "x": 1050.0, - "y": 640, - "width": 670.0, - "height": 361, + "x": 3910.0, + "y": 1470, + "width": 3650.0, + "height": 926, "angle": 0, "strokeColor": "#0b7285", "backgroundColor": "transparent", @@ -6052,17 +7727,17 @@ "roundness": { "type": 2 }, - "seed": 112905263, + "seed": 161625853, "version": 1, - "versionNonce": 219858513, + "versionNonce": 973005003, "isDeleted": false, "boundElements": [ { "type": "text", - "id": "l-orch-pool" + "id": "l-drift-pool" } ], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, "points": [ @@ -6071,12 +7746,16 @@ 0 ], [ - -290.0, - -140 + 0.0, + 130 + ], + [ + -3650.0, + 130 ], [ - -670.0, - -361 + -3480.0, + -796 ] ], "startArrowhead": null, @@ -6084,7 +7763,7 @@ "elbowed": false, "lastCommittedPoint": null, "startBinding": { - "elementId": "n-orch", + "elementId": "n-drift", "focus": 0, "gap": 6, "fixedPoint": null @@ -6097,11 +7776,11 @@ } }, { - "id": "l-orch-pool", + "id": "l-drift-pool", "type": "text", - "x": 626.9, - "y": 492, - "width": 266.20000000000005, + "x": 205.55, + "y": 1592, + "width": 108.9, "height": 13.75, "angle": 0, "strokeColor": "#0b7285", @@ -6114,21 +7793,110 @@ "groupIds": [], "frameId": null, "roundness": null, - "seed": 500965, + "seed": 1172730855, "version": 1, - "versionNonce": 1217158540, + "versionNonce": 789792721, "isDeleted": false, "boundElements": [], - "updated": 1788179982887, + "updated": 1788196217117, "link": null, "locked": false, - "text": "zieht freigegebene Stories der aktiven Welle", + "text": "Findings → Stories", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", - "containerId": "e-orch-pool", - "originalText": "zieht freigegebene Stories der aktiven Welle", + "containerId": "e-drift-pool", + "originalText": "Findings → Stories", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "e-exit", + "type": "arrow", + "x": 5600, + "y": 602.0, + "width": 200, + "height": 0, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 4, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1582707250, + "version": 1, + "versionNonce": 95487716, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "l-exit" + } + ], + "updated": 1788196217117, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 200, + 0 + ] + ], + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "lastCommittedPoint": null, + "startBinding": { + "elementId": "n-merge", + "focus": 0, + "gap": 6, + "fixedPoint": null + }, + "endBinding": null + }, + { + "id": "l-exit", + "type": "text", + "x": 5636.475, + "y": 576.0, + "width": 127.05000000000001, + "height": 13.75, + "angle": 0, + "strokeColor": "#0b7285", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "frameId": null, + "roundness": null, + "seed": 1933155933, + "version": 1, + "versionNonce": 2010580388, + "isDeleted": false, + "boundElements": [], + "updated": 1788196217117, + "link": null, + "locked": false, + "text": "Ausfahrt → Projekt-CI", + "fontSize": 11, + "fontFamily": 3, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "e-exit", + "originalText": "Ausfahrt → Projekt-CI", "autoResize": true, "lineHeight": 1.25 } From 7e29cb43616882f62447fd6f2644fabe1d5a7bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Mon, 31 Aug 2026 19:31:40 +0200 Subject: [PATCH 112/117] docs(vision): map redrawn with the excalidraw-diagram skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hand-crafted JSON (no generator), skill methodology: one thick left-to-right road with an entry symbol and a Projekt-CI exit, side streets instead of sprawl (intake stamping cluster, views feeding Freigabe, audit branch with yes/no return, orthogonal return lanes for findings and red smoke, a shared Rückführ-Spur from monitoring into the pool), semantic palette (violet = model, blue = mechanics, orange = human, green = goal, dashed = missing), roughness 0, monospace, three quiet zone frames. Validated through the skill's render-view-fix loop (4 iterations, PNG-inspected). --- .../2026-08-30-dark-factory-vision.excalidraw | 7910 +---------------- 1 file changed, 1 insertion(+), 7909 deletions(-) diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw b/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw index 1a6f3d4..aa68110 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.excalidraw @@ -1,7909 +1 @@ -{ - "type": "excalidraw", - "version": 2, - "source": "dev-workflow-kit/mkdiagram.py", - "elements": [ - { - "id": "f-pool", - "type": "frame", - "x": 124, - "y": 104, - "width": 1132, - "height": 606, - "angle": 0, - "strokeColor": "#868e96", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1582884707, - "version": 1, - "versionNonce": 1583016050, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "name": "① INTAKE — klassifizieren, nie produzieren" - }, - { - "id": "f-spec", - "type": "frame", - "x": 2504, - "y": 524, - "width": 1932, - "height": 591, - "angle": 0, - "strokeColor": "#868e96", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1511164101, - "version": 1, - "versionNonce": 278194589, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "name": "② PRODUKTION — eine Lane pro Ast (×lanes) · Review: anderes Modell, nie der Autor" - }, - { - "id": "f-vet", - "type": "frame", - "x": 2084, - "y": 1214, - "width": 1972, - "height": 292, - "angle": 0, - "strokeColor": "#868e96", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 1, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1703527210, - "version": 1, - "versionNonce": 2068841389, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "name": "③ ÜBERWACHUNG — beobachten, melden, nie selbst fixen" - }, - { - "id": "h-title", - "type": "text", - "x": 200, - "y": -170, - "width": 369.6, - "height": 35.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1964953076, - "version": 1, - "versionNonce": 733610633, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Dark Factory · Landkarte", - "fontSize": 28, - "fontFamily": 2, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Dark Factory · Landkarte", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "h-sub", - "type": "text", - "x": 200, - "y": -128, - "width": 1115.4, - "height": 32.5, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 755557971, - "version": 1, - "versionNonce": 182378959, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Lies die Straße von links nach rechts: Eingang → Pool → Freigabe (deine Schranke) → Takt → Orchestrator → Lane → PR → Stichprobe → Merge-Queue → main.\nTürkis arbeitet ein Modell, Grau Mechanik, Orange bist du · gestrichelt = fehlt noch · dicke Pfeile = die Straße, dünne = Nebenwege, rote = Ausfahrt zu dir.", - "fontSize": 13, - "fontFamily": 2, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Lies die Straße von links nach rechts: Eingang → Pool → Freigabe (deine Schranke) → Takt → Orchestrator → Lane → PR → Stichprobe → Merge-Queue → main.\nTürkis arbeitet ein Modell, Grau Mechanik, Orange bist du · gestrichelt = fehlt noch · dicke Pfeile = die Straße, dünne = Nebenwege, rote = Ausfahrt zu dir.", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "lg-0", - "type": "rectangle", - "x": 200, - "y": -66, - "width": 16, - "height": 16, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1471339731, - "version": 1, - "versionNonce": 1016357713, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "lgt-0", - "type": "text", - "x": 224, - "y": -66, - "width": 85.80000000000001, - "height": 15.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1934530462, - "version": 1, - "versionNonce": 166793565, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Modell-Knoten", - "fontSize": 12, - "fontFamily": 3, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Modell-Knoten", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "lg-1", - "type": "rectangle", - "x": 355, - "y": -66, - "width": 16, - "height": 16, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1870675710, - "version": 1, - "versionNonce": 1841028242, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "lgt-1", - "type": "text", - "x": 379, - "y": -66, - "width": 85.80000000000001, - "height": 15.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 895829542, - "version": 1, - "versionNonce": 2027738559, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Mechanik/Code", - "fontSize": 12, - "fontFamily": 3, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Mechanik/Code", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "lg-2", - "type": "rectangle", - "x": 510, - "y": -66, - "width": 16, - "height": 16, - "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1697654549, - "version": 1, - "versionNonce": 65003596, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "lgt-2", - "type": "text", - "x": 534, - "y": -66, - "width": 72.60000000000001, - "height": 15.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1854293451, - "version": 1, - "versionNonce": 1073582196, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Mensch — du", - "fontSize": 12, - "fontFamily": 3, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Mensch — du", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "lg-3", - "type": "rectangle", - "x": 651, - "y": -66, - "width": 16, - "height": 16, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1230094741, - "version": 1, - "versionNonce": 31208372, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "lgt-3", - "type": "text", - "x": 675, - "y": -66, - "width": 105.60000000000001, - "height": 15.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1342092557, - "version": 1, - "versionNonce": 1422203800, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Speicher & Views", - "fontSize": 12, - "fontFamily": 3, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "Speicher & Views", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "lg-4", - "type": "rectangle", - "x": 827, - "y": -66, - "width": 16, - "height": 16, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 821055000, - "version": 1, - "versionNonce": 814275163, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "lgt-4", - "type": "text", - "x": 851, - "y": -66, - "width": 158.4, - "height": 15.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1251293402, - "version": 1, - "versionNonce": 26730098, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "gestrichelt = fehlt noch", - "fontSize": 12, - "fontFamily": 3, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "gestrichelt = fehlt noch", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "lg-loop", - "type": "text", - "x": 1059, - "y": -66, - "width": 184.8, - "height": 15.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1307531725, - "version": 1, - "versionNonce": 155153760, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ = Loop, iteriert bis clean", - "fontSize": 12, - "fontFamily": 3, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ = Loop, iteriert bis clean", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-inbox", - "type": "rectangle", - "x": 40, - "y": 560, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-inbox" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 971477687, - "version": 1, - "versionNonce": 1859167399, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-inbox" - }, - { - "id": "e-inbox-pool", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-inbox", - "type": "text", - "x": 90.4, - "y": 574, - "width": 79.2, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1202102036, - "version": 1, - "versionNonce": 1840099286, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "💡 Eingang", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-inbox", - "originalText": "💡 Eingang", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-inbox", - "type": "text", - "x": 44.19999999999999, - "y": 600, - "width": 171.60000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-inbox" - ], - "frameId": null, - "roundness": null, - "seed": 1984727111, - "version": 1, - "versionNonce": 1677652995, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Ideen · Stories · Bugs —\nvon dir, vom Team, von der\nFabrik selbst", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Ideen · Stories · Bugs —\nvon dir, vom Team, von der\nFabrik selbst", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-phase0", - "type": "rectangle", - "x": 1040, - "y": -100, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-phase0" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 999975905, - "version": 1, - "versionNonce": 970302524, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-phase0" - }, - { - "id": "e-phase0-arch", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-phase0", - "type": "text", - "x": 1050.8, - "y": -86, - "width": 158.4, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1090580831, - "version": 1, - "versionNonce": 1836196521, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Phase 0 · einmalig", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-phase0", - "originalText": "Phase 0 · einmalig", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-phase0", - "type": "text", - "x": 1044.2, - "y": -60, - "width": 171.60000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-phase0" - ], - "frameId": null, - "roundness": null, - "seed": 1261399322, - "version": 1, - "versionNonce": 407811517, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Baum v1 + Ziele/Out-of-\nScope · Greenfield aus dem\nPool, Bestand aus dem Code", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Baum v1 + Ziele/Out-of-\nScope · Greenfield aus dem\nPool, Bestand aus dem Code", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-pool", - "type": "rectangle", - "x": 340, - "y": 560, - "width": 180, - "height": 114, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-pool" - ], - "frameId": "f-pool", - "roundness": { - "type": 3 - }, - "seed": 396555069, - "version": 1, - "versionNonce": 1726362773, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-pool" - }, - { - "id": "e-inbox-pool", - "type": "arrow" - }, - { - "id": "e-pool-frei", - "type": "arrow" - }, - { - "id": "e-pool-intake", - "type": "arrow" - }, - { - "id": "e-intake-pool", - "type": "arrow" - }, - { - "id": "e-bew-pool", - "type": "arrow" - }, - { - "id": "e-pool-views", - "type": "arrow" - }, - { - "id": "e-orch-pool", - "type": "arrow" - }, - { - "id": "e-e2e-pool", - "type": "arrow" - }, - { - "id": "e-drift-pool", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-pool", - "type": "text", - "x": 386.0, - "y": 574, - "width": 88.0, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-pool", - "roundness": null, - "seed": 1099315426, - "version": 1, - "versionNonce": 1021693764, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Story-Pool", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-pool", - "originalText": "Story-Pool", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-pool", - "type": "text", - "x": 350.8, - "y": 600, - "width": 158.4, - "height": 60.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-pool" - ], - "frameId": "f-pool", - "roundness": null, - "seed": 1352662845, - "version": 1, - "versionNonce": 1318466551, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Projekteingang & einzige\nWahrheit · befüllen ist\nfolgenlos · Kopf: lanes-\nRegler (deiner)", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Projekteingang & einzige\nWahrheit · befüllen ist\nfolgenlos · Kopf: lanes-\nRegler (deiner)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-intake", - "type": "ellipse", - "x": 160, - "y": 140, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-intake" - ], - "frameId": "f-pool", - "roundness": null, - "seed": 1702904874, - "version": 1, - "versionNonce": 399804947, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-intake" - }, - { - "id": "e-pool-intake", - "type": "arrow" - }, - { - "id": "e-intake-pool", - "type": "arrow" - }, - { - "id": "e-intake-bew", - "type": "arrow" - }, - { - "id": "e-bew-intake", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-intake", - "type": "text", - "x": 221.6, - "y": 202.5, - "width": 96.80000000000001, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-pool", - "roundness": null, - "seed": 202128586, - "version": 1, - "versionNonce": 959051492, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Intake-Loop", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-intake", - "originalText": "Intake-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-intake", - "type": "text", - "x": 197.39999999999998, - "y": 228.5, - "width": 145.20000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-intake" - ], - "frameId": "f-pool", - "roundness": null, - "seed": 651549249, - "version": 1, - "versionNonce": 304502735, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "heftet die Karte an (8\nDimensionen) · löst\nKEINE Produktion aus", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "heftet die Karte an (8\nDimensionen) · löst\nKEINE Produktion aus", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-intake", - "type": "text", - "x": 218.575, - "y": 279.5, - "width": 102.85000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-intake" - ], - "frameId": "f-pool", - "roundness": null, - "seed": 194713491, - "version": 1, - "versionNonce": 1156835504, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ pro Story-Batch", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ pro Story-Batch", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-bew", - "type": "ellipse", - "x": 600, - "y": 140, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-bew" - ], - "frameId": "f-pool", - "roundness": null, - "seed": 1738698400, - "version": 1, - "versionNonce": 1912077537, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-bew" - }, - { - "id": "e-intake-bew", - "type": "arrow" - }, - { - "id": "e-bew-intake", - "type": "arrow" - }, - { - "id": "e-bew-arch", - "type": "arrow" - }, - { - "id": "e-bew-pool", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-bew", - "type": "text", - "x": 644.0, - "y": 202.5, - "width": 132.0, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-pool", - "roundness": null, - "seed": 1489147796, - "version": 1, - "versionNonce": 1362476978, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Bewertungs-Loop", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-bew", - "originalText": "Bewertungs-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-bew", - "type": "text", - "x": 640.7, - "y": 228.5, - "width": 138.60000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-bew" - ], - "frameId": "f-pool", - "roundness": null, - "seed": 89937332, - "version": 1, - "versionNonce": 1278690150, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "einziger Erzeuger des\nArchitektur-Urteils ·\nerzeugt Meta-Stories", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "einziger Erzeuger des\nArchitektur-Urteils ·\nerzeugt Meta-Stories", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-bew", - "type": "text", - "x": 658.575, - "y": 279.5, - "width": 102.85000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-bew" - ], - "frameId": "f-pool", - "roundness": null, - "seed": 2109244315, - "version": 1, - "versionNonce": 850747066, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ Bewertungs-Loop", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ Bewertungs-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-arch", - "type": "rectangle", - "x": 1040, - "y": 160, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-arch" - ], - "frameId": "f-pool", - "roundness": { - "type": 3 - }, - "seed": 2071801565, - "version": 1, - "versionNonce": 972799062, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-arch" - }, - { - "id": "e-bew-arch", - "type": "arrow" - }, - { - "id": "e-phase0-arch", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-arch", - "type": "text", - "x": 1081.6, - "y": 174, - "width": 96.80000000000001, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-pool", - "roundness": null, - "seed": 1404287948, - "version": 1, - "versionNonce": 1586809770, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Architektur", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-arch", - "originalText": "Architektur", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-arch", - "type": "text", - "x": 1040.9, - "y": 200, - "width": 178.20000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-arch" - ], - "frameId": "f-pool", - "roundness": null, - "seed": 1321910848, - "version": 1, - "versionNonce": 1396016797, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "AGENTS.md: Baum ·\nInvarianten · Ziele/Out-of-\nScope — von allen gelesen", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "AGENTS.md: Baum ·\nInvarianten · Ziele/Out-of-\nScope — von allen gelesen", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-esk", - "type": "rectangle", - "x": 3060, - "y": 80, - "width": 180, - "height": 84, - "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-esk" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 338215988, - "version": 1, - "versionNonce": 1338158164, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-esk" - }, - { - "id": "e-orch-esk", - "type": "arrow" - }, - { - "id": "e-spec-esk", - "type": "arrow" - }, - { - "id": "e-plan-esk", - "type": "arrow" - }, - { - "id": "e-build-esk", - "type": "arrow" - }, - { - "id": "e-verify-esk", - "type": "arrow" - }, - { - "id": "e-judge-esk", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-esk", - "type": "text", - "x": 3062.0, - "y": 94, - "width": 176.0, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 32213827, - "version": 1, - "versionNonce": 1786065641, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⚠ Eskalation an dich", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-esk", - "originalText": "⚠ Eskalation an dich", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-esk", - "type": "text", - "x": 3077.4, - "y": 120, - "width": 145.20000000000002, - "height": 30.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-esk" - ], - "frameId": null, - "roundness": null, - "seed": 1134690140, - "version": 1, - "versionNonce": 135645638, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "stoppt & meldet, statt\nendlos zu drehen", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "stoppt & meldet, statt\nendlos zu drehen", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-metrics", - "type": "rectangle", - "x": 2540, - "y": 1250, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-metrics" - ], - "frameId": "f-vet", - "roundness": { - "type": 3 - }, - "seed": 127885034, - "version": 1, - "versionNonce": 76563763, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-metrics" - }, - { - "id": "e-metrics-sample", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-metrics", - "type": "text", - "x": 2555.2, - "y": 1264, - "width": 149.60000000000002, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-vet", - "roundness": null, - "seed": 408469126, - "version": 1, - "versionNonce": 1888851400, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Analytics + Trace", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-metrics", - "originalText": "Analytics + Trace", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-metrics", - "type": "text", - "x": 2544.2, - "y": 1290, - "width": 171.60000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-metrics" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 519568669, - "version": 1, - "versionNonce": 1287670035, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "SQLite: Kosten · Dauer ·\nRetries · Trace-ID · spec-\ndelta (2c)", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "SQLite: Kosten · Dauer ·\nRetries · Trace-ID · spec-\ndelta (2c)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-judge", - "type": "rectangle", - "x": 2960, - "y": 1250, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-judge" - ], - "frameId": "f-vet", - "roundness": { - "type": 3 - }, - "seed": 64601867, - "version": 1, - "versionNonce": 1670975073, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-judge" - }, - { - "id": "e-judge-esk", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-judge", - "type": "text", - "x": 2979.6, - "y": 1264, - "width": 140.8, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-vet", - "roundness": null, - "seed": 996291672, - "version": 1, - "versionNonce": 700709642, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Judge / Watchdog", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-judge", - "originalText": "Judge / Watchdog", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-judge", - "type": "text", - "x": 2960.9, - "y": 1290, - "width": 178.20000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-judge" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 946033373, - "version": 1, - "versionNonce": 1269075730, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Idle · Thrash · No-Progress\n→ killt & eskaliert ·\nurteilt nie über Qualität", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Idle · Thrash · No-Progress\n→ killt & eskaliert ·\nurteilt nie über Qualität", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-e2e", - "type": "ellipse", - "x": 3380, - "y": 1250, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-e2e" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 1809098487, - "version": 1, - "versionNonce": 419449462, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-e2e" - }, - { - "id": "e-e2e-pool", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-e2e", - "type": "text", - "x": 3454.8, - "y": 1312.5, - "width": 70.4, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-vet", - "roundness": null, - "seed": 1114810545, - "version": 1, - "versionNonce": 501750180, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "E2E-Loop", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-e2e", - "originalText": "E2E-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-e2e", - "type": "text", - "x": 3410.8, - "y": 1338.5, - "width": 158.4, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-e2e" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 1375015975, - "version": 1, - "versionNonce": 631685691, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "volle Suite · Fehlschlag\n→ Story in den Pool\n(Trace-ID)", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "volle Suite · Fehlschlag\n→ Story in den Pool\n(Trace-ID)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-e2e", - "type": "text", - "x": 3420.425, - "y": 1389.5, - "width": 139.15, - "height": 13.75, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-e2e" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 1073254677, - "version": 1, - "versionNonce": 9865044, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ nightly / Wellen-Ende", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ nightly / Wellen-Ende", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-drift", - "type": "ellipse", - "x": 3800, - "y": 1250, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-drift" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 1422599544, - "version": 1, - "versionNonce": 182511984, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-drift" - }, - { - "id": "e-drift-pool", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-drift", - "type": "text", - "x": 3861.6, - "y": 1312.5, - "width": 96.80000000000001, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-vet", - "roundness": null, - "seed": 982108444, - "version": 1, - "versionNonce": 1406175817, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Drift-Audit", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-drift", - "originalText": "Drift-Audit", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-drift", - "type": "text", - "x": 3837.4, - "y": 1338.5, - "width": 145.20000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-drift" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 597350287, - "version": 1, - "versionNonce": 873550682, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Docs-Drift · Code ohne\nSpec → nur melden,\nStories in den Pool", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Docs-Drift · Code ohne\nSpec → nur melden,\nStories in den Pool", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-drift", - "type": "text", - "x": 3870.675, - "y": 1389.5, - "width": 78.65, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-drift" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 2142450373, - "version": 1, - "versionNonce": 1183838446, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ Takt: Audit", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ Takt: Audit", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-vet", - "type": "rectangle", - "x": 2120, - "y": 1250, - "width": 180, - "height": 114, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-vet" - ], - "frameId": "f-vet", - "roundness": { - "type": 3 - }, - "seed": 2138231514, - "version": 1, - "versionNonce": 2000778784, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-vet" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-vet", - "type": "text", - "x": 2152.8, - "y": 1264, - "width": 114.4, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-vet", - "roundness": null, - "seed": 1804349166, - "version": 1, - "versionNonce": 178678102, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Vet-Preflight", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-vet", - "originalText": "Vet-Preflight", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-vet", - "type": "text", - "x": 2137.4, - "y": 1290, - "width": 145.20000000000002, - "height": 60.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-vet" - ], - "frameId": "f-vet", - "roundness": null, - "seed": 1520009599, - "version": 1, - "versionNonce": 545449556, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "mechanische Checks vor\njedem Modell-Knoten:\nArtefakte · Formate ·\nUmgebung", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "mechanische Checks vor\njedem Modell-Knoten:\nArtefakte · Formate ·\nUmgebung", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-views", - "type": "rectangle", - "x": 760, - "y": 900, - "width": 180, - "height": 114, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "hachure", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-views" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 677052671, - "version": 1, - "versionNonce": 1627771234, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-views" - }, - { - "id": "e-pool-views", - "type": "arrow" - }, - { - "id": "e-views-frei", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-views", - "type": "text", - "x": 775.2, - "y": 914, - "width": 149.60000000000002, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 493203764, - "version": 1, - "versionNonce": 1101411390, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Views · gerechnet", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-views", - "originalText": "Views · gerechnet", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-views", - "type": "text", - "x": 764.2, - "y": 940, - "width": 171.60000000000002, - "height": 60.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-views" - ], - "frameId": null, - "roundness": null, - "seed": 620708569, - "version": 1, - "versionNonce": 63891758, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Roadmap · Wellen-Plan ·\nDashboard · As-built — aus\ndem Pool gerechnet, nie\ngepflegt", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Roadmap · Wellen-Plan ·\nDashboard · As-built — aus\ndem Pool gerechnet, nie\ngepflegt", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-frei", - "type": "rectangle", - "x": 1340, - "y": 560, - "width": 180, - "height": 129, - "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-frei" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 150803809, - "version": 1, - "versionNonce": 1209338792, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-frei" - }, - { - "id": "e-pool-frei", - "type": "arrow" - }, - { - "id": "e-frei-takt", - "type": "arrow" - }, - { - "id": "e-views-frei", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-frei", - "type": "text", - "x": 1394.8, - "y": 574, - "width": 70.4, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1645590713, - "version": 1, - "versionNonce": 231771288, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Freigabe", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-frei", - "originalText": "Freigabe", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-frei", - "type": "text", - "x": 1344.2, - "y": 600, - "width": 171.60000000000002, - "height": 75.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-frei" - ], - "frameId": null, - "roundness": null, - "seed": 859852041, - "version": 1, - "versionNonce": 231505395, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "dein „Go\" auf den Wellen-\nPlan — der einzige\nProduktions-Auslöser ·\nRegler bis Standing-Auto ·\nFlags immer zu dir", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "dein „Go\" auf den Wellen-\nPlan — der einzige\nProduktions-Auslöser ·\nRegler bis Standing-Auto ·\nFlags immer zu dir", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-takt", - "type": "ellipse", - "x": 1700, - "y": 560, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-takt" - ], - "frameId": null, - "roundness": null, - "seed": 1818024495, - "version": 1, - "versionNonce": 624709210, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-takt" - }, - { - "id": "e-frei-takt", - "type": "arrow" - }, - { - "id": "e-takt-orch", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-takt", - "type": "text", - "x": 1770.4, - "y": 622.5, - "width": 79.2, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 830031502, - "version": 1, - "versionNonce": 143508822, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Takt-Loop", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-takt", - "originalText": "Takt-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-takt", - "type": "text", - "x": 1734.1, - "y": 648.5, - "width": 151.8, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-takt" - ], - "frameId": null, - "roundness": null, - "seed": 2057380367, - "version": 1, - "versionNonce": 36272003, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "die Uhr: weckt den\nOrchestrator · einziges\nStück Plattform", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "die Uhr: weckt den\nOrchestrator · einziges\nStück Plattform", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-takt", - "type": "text", - "x": 1791.85, - "y": 699.5, - "width": 36.300000000000004, - "height": 13.75, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-takt" - ], - "frameId": null, - "roundness": null, - "seed": 1819583883, - "version": 1, - "versionNonce": 1470806136, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ Takt", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ Takt", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-orch", - "type": "ellipse", - "x": 2120, - "y": 560, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-orch" - ], - "frameId": null, - "roundness": null, - "seed": 1170242, - "version": 1, - "versionNonce": 458491376, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-orch" - }, - { - "id": "e-takt-orch", - "type": "arrow" - }, - { - "id": "e-orch-spec", - "type": "arrow" - }, - { - "id": "e-orch-pool", - "type": "arrow" - }, - { - "id": "e-orch-lane2", - "type": "arrow" - }, - { - "id": "e-orch-lane3", - "type": "arrow" - }, - { - "id": "e-orch-esk", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-orch", - "type": "text", - "x": 2177.2, - "y": 615.0, - "width": 105.60000000000001, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 450364715, - "version": 1, - "versionNonce": 1990807841, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Orchestrator", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-orch", - "originalText": "Orchestrator", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-orch", - "type": "text", - "x": 2154.1, - "y": 641.0, - "width": 151.8, - "height": 60.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-orch" - ], - "frameId": null, - "roundness": null, - "seed": 1954794096, - "version": 1, - "versionNonce": 112361914, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "liest alles frisch ·\nrechnet den Plan (dry-\nrun) · prüft Drosseln ·\nöffnet Lanes", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "liest alles frisch ·\nrechnet den Plan (dry-\nrun) · prüft Drosseln ·\nöffnet Lanes", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-orch", - "type": "text", - "x": 2178.575, - "y": 707.0, - "width": 102.85000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-orch" - ], - "frameId": null, - "roundness": null, - "seed": 1009289989, - "version": 1, - "versionNonce": 806301091, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ pro Tick frisch", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ pro Tick frisch", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-spec", - "type": "ellipse", - "x": 2540, - "y": 560, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-spec" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 2105304789, - "version": 1, - "versionNonce": 1522349327, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-spec" - }, - { - "id": "e-orch-spec", - "type": "arrow" - }, - { - "id": "e-spec-plan", - "type": "arrow" - }, - { - "id": "e-spec-esk", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-spec", - "type": "text", - "x": 2610.4, - "y": 630.0, - "width": 79.2, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-spec", - "roundness": null, - "seed": 853462374, - "version": 1, - "versionNonce": 901495176, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Spec-Loop", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-spec", - "originalText": "Spec-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-spec", - "type": "text", - "x": 2574.1, - "y": 656.0, - "width": 151.8, - "height": 30.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-spec" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 156848651, - "version": 1, - "versionNonce": 1215994888, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Design + Gate A (Intake\nlief schon)", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Design + Gate A (Intake\nlief schon)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-spec", - "type": "text", - "x": 2610.675, - "y": 692.0, - "width": 78.65, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-spec" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 1351744129, - "version": 1, - "versionNonce": 426254319, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ Gate-A-Loop", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ Gate-A-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-plan", - "type": "ellipse", - "x": 2960, - "y": 560, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-plan" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 1671839336, - "version": 1, - "versionNonce": 1449356410, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-plan" - }, - { - "id": "e-spec-plan", - "type": "arrow" - }, - { - "id": "e-plan-build", - "type": "arrow" - }, - { - "id": "e-plan-esk", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-plan", - "type": "text", - "x": 3030.4, - "y": 637.5, - "width": 79.2, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-spec", - "roundness": null, - "seed": 579338669, - "version": 1, - "versionNonce": 723471863, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Plan-Loop", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-plan", - "originalText": "Plan-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-plan", - "type": "text", - "x": 3027.1, - "y": 663.5, - "width": 85.80000000000001, - "height": 15.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-plan" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 187140746, - "version": 1, - "versionNonce": 668317600, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Plan + Gate A", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Plan + Gate A", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-plan", - "type": "text", - "x": 3030.675, - "y": 684.5, - "width": 78.65, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-plan" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 714222201, - "version": 1, - "versionNonce": 32533313, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ Gate-A-Loop", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ Gate-A-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-build", - "type": "ellipse", - "x": 3380, - "y": 560, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-build" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 2070337917, - "version": 1, - "versionNonce": 880577645, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-build" - }, - { - "id": "e-plan-build", - "type": "arrow" - }, - { - "id": "e-build-verify", - "type": "arrow" - }, - { - "id": "e-audit-build", - "type": "arrow" - }, - { - "id": "e-verify-build", - "type": "arrow" - }, - { - "id": "e-build-esk", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-build", - "type": "text", - "x": 3454.8, - "y": 630.0, - "width": 70.4, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-spec", - "roundness": null, - "seed": 1627879603, - "version": 1, - "versionNonce": 1981800359, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Bau-Loop", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-build", - "originalText": "Bau-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-build", - "type": "text", - "x": 3424.0, - "y": 656.0, - "width": 132.0, - "height": 30.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-build" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 253385209, - "version": 1, - "versionNonce": 289043725, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "goal-based · AC sind\nread-only", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "goal-based · AC sind\nread-only", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-build", - "type": "text", - "x": 3438.575, - "y": 692.0, - "width": 102.85000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-build" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 529114096, - "version": 1, - "versionNonce": 1518305368, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ TDD: rot → grün", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ TDD: rot → grün", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-verify", - "type": "ellipse", - "x": 3800, - "y": 560, - "width": 220, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-verify" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 216995297, - "version": 1, - "versionNonce": 23525720, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-verify" - }, - { - "id": "e-build-verify", - "type": "arrow" - }, - { - "id": "e-verify-pr", - "type": "arrow" - }, - { - "id": "e-verify-build", - "type": "arrow" - }, - { - "id": "e-mq-verify", - "type": "arrow" - }, - { - "id": "e-verify-esk", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-verify", - "type": "text", - "x": 3883.6, - "y": 622.5, - "width": 52.800000000000004, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-spec", - "roundness": null, - "seed": 128620107, - "version": 1, - "versionNonce": 998450711, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Verify", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-verify", - "originalText": "Verify", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-verify", - "type": "text", - "x": 3834.1, - "y": 648.5, - "width": 151.8, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-verify" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 1711590672, - "version": 1, - "versionNonce": 1045445060, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Battery + Gate B ·\nanderes Modell, nie der\nAutor", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Battery + Gate B ·\nanderes Modell, nie der\nAutor", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "b-verify", - "type": "text", - "x": 3870.675, - "y": 699.5, - "width": 78.65, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-verify" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 381560869, - "version": 1, - "versionNonce": 1464750150, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "⟳ Gate-B-Loop", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "⟳ Gate-B-Loop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-pr", - "type": "rectangle", - "x": 4220, - "y": 560, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-pr" - ], - "frameId": "f-spec", - "roundness": { - "type": 3 - }, - "seed": 1201077169, - "version": 1, - "versionNonce": 404554714, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-pr" - }, - { - "id": "e-verify-pr", - "type": "arrow" - }, - { - "id": "e-pr-sample", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-pr", - "type": "text", - "x": 4301.2, - "y": 574, - "width": 17.6, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-spec", - "roundness": null, - "seed": 960837608, - "version": 1, - "versionNonce": 1092780211, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "PR", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-pr", - "originalText": "PR", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-pr", - "type": "text", - "x": 4230.8, - "y": 600, - "width": 158.4, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-pr" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 409491659, - "version": 1, - "versionNonce": 2115875710, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "PR auf · Bots sind\nopportunistisch — nichts\nblockiert", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "PR auf · Bots sind\nopportunistisch — nichts\nblockiert", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-sample", - "type": "rectangle", - "x": 4580, - "y": 560, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-sample" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 1571730853, - "version": 1, - "versionNonce": 1652535920, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-sample" - }, - { - "id": "e-pr-sample", - "type": "arrow" - }, - { - "id": "e-sample-mq", - "type": "arrow" - }, - { - "id": "e-sample-audit", - "type": "arrow" - }, - { - "id": "e-lane2-sample", - "type": "arrow" - }, - { - "id": "e-lane3-sample", - "type": "arrow" - }, - { - "id": "e-metrics-sample", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-sample", - "type": "text", - "x": 4621.6, - "y": 574, - "width": 96.80000000000001, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 281249670, - "version": 1, - "versionNonce": 900290631, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Sample-Gate", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-sample", - "originalText": "Sample-Gate", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-sample", - "type": "text", - "x": 4584.2, - "y": 600, - "width": 171.60000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-sample" - ], - "frameId": null, - "roundness": null, - "seed": 1382367706, - "version": 1, - "versionNonce": 824100700, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "mechanische Ziehung: x % ·\nRisiko-Schwellen ·\nArchitektur immer", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "mechanische Ziehung: x % ·\nRisiko-Schwellen ·\nArchitektur immer", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-audit", - "type": "rectangle", - "x": 4540, - "y": 240, - "width": 180, - "height": 84, - "angle": 0, - "strokeColor": "#e8590c", - "backgroundColor": "#ffec99", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-audit" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 250199008, - "version": 1, - "versionNonce": 847941153, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-audit" - }, - { - "id": "e-sample-audit", - "type": "arrow" - }, - { - "id": "e-audit-mq", - "type": "arrow" - }, - { - "id": "e-audit-build", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-audit", - "type": "text", - "x": 4608.0, - "y": 254, - "width": 44.0, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 903565808, - "version": 1, - "versionNonce": 2126121013, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Audit", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-audit", - "originalText": "Audit", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-audit", - "type": "text", - "x": 4544.2, - "y": 280, - "width": 171.60000000000002, - "height": 30.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-audit" - ], - "frameId": null, - "roundness": null, - "seed": 457125559, - "version": 1, - "versionNonce": 1011272, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "du, mit Punchlist · einmal\n„Nein\" = immer wieder du", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "du, mit Punchlist · einmal\n„Nein\" = immer wieder du", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-mq", - "type": "rectangle", - "x": 5000, - "y": 560, - "width": 180, - "height": 84, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-mq" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 579380293, - "version": 1, - "versionNonce": 1856143004, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-mq" - }, - { - "id": "e-sample-mq", - "type": "arrow" - }, - { - "id": "e-mq-merge", - "type": "arrow" - }, - { - "id": "e-audit-mq", - "type": "arrow" - }, - { - "id": "e-mq-verify", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-mq", - "type": "text", - "x": 5041.6, - "y": 574, - "width": 96.80000000000001, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 2085054259, - "version": 1, - "versionNonce": 2093489594, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Merge-Queue", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-mq", - "originalText": "Merge-Queue", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-mq", - "type": "text", - "x": 5004.2, - "y": 600, - "width": 171.60000000000002, - "height": 30.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-mq" - ], - "frameId": null, - "roundness": null, - "seed": 1725313060, - "version": 1, - "versionNonce": 1272900802, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "seriell: rebase auf main →\nSmoke → nur Grünes landet", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "seriell: rebase auf main →\nSmoke → nur Grünes landet", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-merge", - "type": "rectangle", - "x": 5420, - "y": 560, - "width": 180, - "height": 84, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "#e9ecef", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-merge" - ], - "frameId": null, - "roundness": { - "type": 3 - }, - "seed": 653146725, - "version": 1, - "versionNonce": 2142981916, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-merge" - }, - { - "id": "e-mq-merge", - "type": "arrow" - }, - { - "id": "e-exit", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-merge", - "type": "text", - "x": 5488.0, - "y": 574, - "width": 44.0, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1900233367, - "version": 1, - "versionNonce": 42156640, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Merge", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-merge", - "originalText": "Merge", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-merge", - "type": "text", - "x": 5420.9, - "y": 600, - "width": 178.20000000000002, - "height": 30.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-merge" - ], - "frameId": null, - "roundness": null, - "seed": 452496762, - "version": 1, - "versionNonce": 402239492, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Fabrik endet hier ·\nRelease/Deploy = Projekt-CI", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Fabrik endet hier ·\nRelease/Deploy = Projekt-CI", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-lane2", - "type": "rectangle", - "x": 2540, - "y": 980, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-lane2" - ], - "frameId": "f-spec", - "roundness": { - "type": 3 - }, - "seed": 846698336, - "version": 1, - "versionNonce": 2138540917, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-lane2" - }, - { - "id": "e-orch-lane2", - "type": "arrow" - }, - { - "id": "e-lane2-sample", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-lane2", - "type": "text", - "x": 2555.2, - "y": 994, - "width": 149.60000000000002, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-spec", - "roundness": null, - "seed": 1834760427, - "version": 1, - "versionNonce": 1292824872, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Lane 2 · parallel", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-lane2", - "originalText": "Lane 2 · parallel", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-lane2", - "type": "text", - "x": 2544.2, - "y": 1020, - "width": 171.60000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-lane2" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 1378331574, - "version": 1, - "versionNonce": 1239011821, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Spec → Plan → Bau → Verify\n→ PR · disjunkter Ast,\neigener Worktree", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "Spec → Plan → Bau → Verify\n→ PR · disjunkter Ast,\neigener Worktree", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "n-lane3", - "type": "rectangle", - "x": 2960, - "y": 980, - "width": 180, - "height": 99, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "#c3fae8", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [ - "g-lane3" - ], - "frameId": "f-spec", - "roundness": { - "type": 3 - }, - "seed": 215462959, - "version": 1, - "versionNonce": 90425851, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "t-lane3" - }, - { - "id": "e-orch-lane3", - "type": "arrow" - }, - { - "id": "e-lane3-sample", - "type": "arrow" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false - }, - { - "id": "t-lane3", - "type": "text", - "x": 2975.2, - "y": 994, - "width": 149.60000000000002, - "height": 20.0, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": "f-spec", - "roundness": null, - "seed": 2124509219, - "version": 1, - "versionNonce": 314302711, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Lane 3 · parallel", - "fontSize": 16, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "n-lane3", - "originalText": "Lane 3 · parallel", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "s-lane3", - "type": "text", - "x": 2960.9, - "y": 1020, - "width": 178.20000000000002, - "height": 45.0, - "angle": 0, - "strokeColor": "#495057", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [ - "g-lane3" - ], - "frameId": "f-spec", - "roundness": null, - "seed": 457936654, - "version": 1, - "versionNonce": 948203538, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "gleiche Pipeline · so\nviele, wie der lanes-Regler\nerlaubt", - "fontSize": 12, - "fontFamily": 2, - "textAlign": "center", - "verticalAlign": "top", - "containerId": null, - "originalText": "gleiche Pipeline · so\nviele, wie der lanes-Regler\nerlaubt", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-inbox-pool", - "type": "arrow", - "x": 220, - "y": 609.5, - "width": 120, - "height": 7.5, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 554646833, - "version": 1, - "versionNonce": 20562661, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-inbox-pool" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 120, - 7.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-inbox", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-inbox-pool", - "type": "text", - "x": 216.475, - "y": 605.25, - "width": 127.05000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1659338477, - "version": 1, - "versionNonce": 1310680491, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "jederzeit · folgenlos", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-inbox-pool", - "originalText": "jederzeit · folgenlos", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-pool-frei", - "type": "arrow", - "x": 520, - "y": 617.0, - "width": 820, - "height": 7.5, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 706426385, - "version": 1, - "versionNonce": 1783033082, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-pool-frei" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 820, - 7.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-frei", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-pool-frei", - "type": "text", - "x": 863.45, - "y": 612.75, - "width": 133.10000000000002, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 636352585, - "version": 1, - "versionNonce": 829293676, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "klassifiziert & bereit", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-pool-frei", - "originalText": "klassifiziert & bereit", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-frei-takt", - "type": "arrow", - "x": 1520, - "y": 624.5, - "width": 180, - "height": 45.5, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 157622201, - "version": 1, - "versionNonce": 159517226, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-frei-takt" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 180, - 45.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-frei", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-takt", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-frei-takt", - "type": "text", - "x": 1552.525, - "y": 639.25, - "width": 114.95, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 193525164, - "version": 1, - "versionNonce": 448251369, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Status: freigegeben", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-frei-takt", - "originalText": "Status: freigegeben", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-takt-orch", - "type": "arrow", - "x": 1920, - "y": 670.0, - "width": 200, - "height": 0.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1251413446, - "version": 1, - "versionNonce": 1367061054, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-takt-orch" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 200, - 0.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-takt", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-orch", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-takt-orch", - "type": "text", - "x": 1983.7, - "y": 662.0, - "width": 72.60000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 521866285, - "version": 1, - "versionNonce": 33305479, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "weckt (Tick)", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-takt-orch", - "originalText": "weckt (Tick)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-orch-spec", - "type": "arrow", - "x": 2340, - "y": 670.0, - "width": 200, - "height": 0.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1291247220, - "version": 1, - "versionNonce": 791723976, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-orch-spec" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 200, - 0.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-orch", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-spec", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-orch-spec", - "type": "text", - "x": 2352.275, - "y": 662.0, - "width": 175.45000000000002, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 798228357, - "version": 1, - "versionNonce": 1336055300, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "öffnet Lane 1 · Story + Karte", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-orch-spec", - "originalText": "öffnet Lane 1 · Story + Karte", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-spec-plan", - "type": "arrow", - "x": 2760, - "y": 670.0, - "width": 200, - "height": 0.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 973256993, - "version": 1, - "versionNonce": 273205204, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-spec-plan" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 200, - 0.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-spec", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-plan", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-spec-plan", - "type": "text", - "x": 2847.9, - "y": 662.0, - "width": 24.200000000000003, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 2059726030, - "version": 1, - "versionNonce": 1261155441, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Spec", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-spec-plan", - "originalText": "Spec", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-plan-build", - "type": "arrow", - "x": 3180, - "y": 670.0, - "width": 200, - "height": 0.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1038790516, - "version": 1, - "versionNonce": 1787477839, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-plan-build" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 200, - 0.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-plan", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-build", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-plan-build", - "type": "text", - "x": 3267.9, - "y": 662.0, - "width": 24.200000000000003, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1233882367, - "version": 1, - "versionNonce": 291495025, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Plan", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-plan-build", - "originalText": "Plan", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-build-verify", - "type": "arrow", - "x": 3600, - "y": 670.0, - "width": 200, - "height": 0.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1860849707, - "version": 1, - "versionNonce": 829137059, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-build-verify" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 200, - 0.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-build", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-verify", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-build-verify", - "type": "text", - "x": 3687.9, - "y": 662.0, - "width": 24.200000000000003, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 392619526, - "version": 1, - "versionNonce": 1346998123, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Diff", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-build-verify", - "originalText": "Diff", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-verify-pr", - "type": "arrow", - "x": 4020, - "y": 670.0, - "width": 200, - "height": 60.5, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 331003081, - "version": 1, - "versionNonce": 667462151, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-verify-pr" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 200, - -60.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-verify", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-pr", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-verify-pr", - "type": "text", - "x": 4092.775, - "y": 631.75, - "width": 54.45, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1950825112, - "version": 1, - "versionNonce": 490449539, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "grün → PR", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-verify-pr", - "originalText": "grün → PR", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-pr-sample", - "type": "arrow", - "x": 4400, - "y": 609.5, - "width": 180, - "height": 0.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1756216314, - "version": 1, - "versionNonce": 1310920283, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 180, - 0.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-pr", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-sample", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "e-sample-mq", - "type": "arrow", - "x": 4760, - "y": 609.5, - "width": 240, - "height": 7.5, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 535794085, - "version": 1, - "versionNonce": 1557765238, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-sample-mq" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 240, - -7.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-sample", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-mq", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-sample-mq", - "type": "text", - "x": 4789.25, - "y": 597.75, - "width": 181.50000000000003, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 407593829, - "version": 1, - "versionNonce": 340323999, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "nicht gezogen — der Normalfall", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-sample-mq", - "originalText": "nicht gezogen — der Normalfall", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-mq-merge", - "type": "arrow", - "x": 5180, - "y": 602.0, - "width": 240, - "height": 0.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1587901785, - "version": 1, - "versionNonce": 1350718411, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-mq-merge" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 240, - 0.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-mq", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-merge", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-mq-merge", - "type": "text", - "x": 5242.525, - "y": 594.0, - "width": 114.95, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 2019504135, - "version": 1, - "versionNonce": 1189558186, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Smoke grün → landet", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-mq-merge", - "originalText": "Smoke grün → landet", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-pool-intake", - "type": "arrow", - "x": 360, - "y": 560, - "width": 90.0, - "height": 200, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 422173362, - "version": 1, - "versionNonce": 1475265434, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-pool-intake" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -90.0, - -200 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-intake", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-pool-intake", - "type": "text", - "x": 263.575, - "y": 452.0, - "width": 102.85000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 2040401216, - "version": 1, - "versionNonce": 833674493, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Event: neue Story", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-pool-intake", - "originalText": "Event: neue Story", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-intake-pool", - "type": "arrow", - "x": 380, - "y": 340, - "width": 50.0, - "height": 220, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1894488072, - "version": 1, - "versionNonce": 1036195671, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-intake-pool" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 50.0, - 220 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-intake", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-intake-pool", - "type": "text", - "x": 356.6, - "y": 442.0, - "width": 96.80000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1296079861, - "version": 1, - "versionNonce": 168572768, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Karte angeheftet", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-intake-pool", - "originalText": "Karte angeheftet", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-intake-bew", - "type": "arrow", - "x": 380, - "y": 250.0, - "width": 220, - "height": 0.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 905072500, - "version": 1, - "versionNonce": 101794100, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-intake-bew" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 220, - 0.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-intake", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-bew", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-intake-bew", - "type": "text", - "x": 444.625, - "y": 242.0, - "width": 90.75000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 222994022, - "version": 1, - "versionNonce": 234177226, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "fragt Urteil an", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-intake-bew", - "originalText": "fragt Urteil an", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-bew-intake", - "type": "arrow", - "x": 600, - "y": 340, - "width": 220, - "height": 100, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 83100045, - "version": 1, - "versionNonce": 1100275758, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-bew-intake" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -110.0, - 100 - ], - [ - -220, - 0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-bew", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-intake", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-bew-intake", - "type": "text", - "x": 435.55, - "y": 432, - "width": 108.9, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 2067342801, - "version": 1, - "versionNonce": 547991986, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Architektur-Urteil", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-bew-intake", - "originalText": "Architektur-Urteil", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-bew-arch", - "type": "arrow", - "x": 820, - "y": 250.0, - "width": 220, - "height": 40.5, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 511975550, - "version": 1, - "versionNonce": 1588769950, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-bew-arch" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 220, - -40.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-bew", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-arch", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-bew-arch", - "type": "text", - "x": 860.425, - "y": 221.75, - "width": 139.15, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1513072866, - "version": 1, - "versionNonce": 840978158, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "liest & pflegt den Baum", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-bew-arch", - "originalText": "liest & pflegt den Baum", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-bew-pool", - "type": "arrow", - "x": 710.0, - "y": 360, - "width": 210.0, - "height": 200, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 551863352, - "version": 1, - "versionNonce": 903693969, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-bew-pool" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - -210.0, - 200 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-bew", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-bew-pool", - "type": "text", - "x": 505.175, - "y": 452.0, - "width": 199.65, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1768920593, - "version": 1, - "versionNonce": 1943094879, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Meta-Stories · Re-Klassifizierung", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-bew-pool", - "originalText": "Meta-Stories · Re-Klassifizierung", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-phase0-arch", - "type": "arrow", - "x": 1130.0, - "y": -1, - "width": 0.0, - "height": 161, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1280901641, - "version": 1, - "versionNonce": 1054413414, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-phase0-arch" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 0.0, - 161 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-phase0", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-arch", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-phase0-arch", - "type": "text", - "x": 1084.625, - "y": 71.5, - "width": 90.75000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 630147616, - "version": 1, - "versionNonce": 1116850875, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Baum v1 + Ziele", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-phase0-arch", - "originalText": "Baum v1 + Ziele", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-pool-views", - "type": "arrow", - "x": 430.0, - "y": 674, - "width": 420.0, - "height": 226, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 376740337, - "version": 1, - "versionNonce": 1995519512, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-pool-views" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 420.0, - 226 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-views", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-pool-views", - "type": "text", - "x": 594.625, - "y": 779.0, - "width": 90.75000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1546946991, - "version": 1, - "versionNonce": 2098474247, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Status + Karten", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-pool-views", - "originalText": "Status + Karten", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-views-frei", - "type": "arrow", - "x": 940, - "y": 957.0, - "width": 490.0, - "height": 268.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 147695603, - "version": 1, - "versionNonce": 271442309, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-views-frei" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 490.0, - -268.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-views", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-frei", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-views-frei", - "type": "text", - "x": 1115.425, - "y": 815.0, - "width": 139.15, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 490477406, - "version": 1, - "versionNonce": 1029288706, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "gerenderter Wellen-Plan", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-views-frei", - "originalText": "gerenderter Wellen-Plan", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-orch-pool", - "type": "arrow", - "x": 2230.0, - "y": 560, - "width": 1710.0, - "height": 254, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1201230074, - "version": 1, - "versionNonce": 1403396873, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-orch-pool" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - -990.0, - -160 - ], - [ - -1710.0, - 94 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-orch", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-orch-pool", - "type": "text", - "x": 1161.35, - "y": 392, - "width": 157.3, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1830514796, - "version": 1, - "versionNonce": 1322571943, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "zieht freigegebene Stories", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-orch-pool", - "originalText": "zieht freigegebene Stories", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-sample-audit", - "type": "arrow", - "x": 4670.0, - "y": 560, - "width": 40.0, - "height": 236, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1319205689, - "version": 1, - "versionNonce": 159214594, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-sample-audit" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - -40.0, - -236 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-sample", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-audit", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-sample-audit", - "type": "text", - "x": 4553.2, - "y": 434.0, - "width": 193.60000000000002, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 601766565, - "version": 1, - "versionNonce": 456126060, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "gezogen: x % · Architektur immer", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-sample-audit", - "originalText": "gezogen: x % · Architektur immer", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-audit-mq", - "type": "arrow", - "x": 4720, - "y": 282.0, - "width": 370.0, - "height": 278.0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1970015975, - "version": 1, - "versionNonce": 1964835650, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-audit-mq" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 370.0, - 278.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-audit", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-mq", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-audit-mq", - "type": "text", - "x": 4865.675, - "y": 413.0, - "width": 78.65, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 438041302, - "version": 1, - "versionNonce": 1608433291, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "„Ja\" → weiter", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-audit-mq", - "originalText": "„Ja\" → weiter", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-audit-build", - "type": "arrow", - "x": 4540, - "y": 282.0, - "width": 1050.0, - "height": 400, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 35594107, - "version": 1, - "versionNonce": 148507777, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-audit-build" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - -840, - -122.0 - ], - [ - -1050.0, - 278.0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-audit", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-build", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-audit-build", - "type": "text", - "x": 3624.375, - "y": 152, - "width": 151.25, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 578089555, - "version": 1, - "versionNonce": 883531813, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "„Nein\" → Punchlist zurück", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-audit-build", - "originalText": "„Nein\" → Punchlist zurück", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-verify-build", - "type": "arrow", - "x": 3800, - "y": 760, - "width": 200, - "height": 100, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 957145326, - "version": 1, - "versionNonce": 534860933, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-verify-build" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -100.0, - 100 - ], - [ - -200, - 0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-verify", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-build", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-verify-build", - "type": "text", - "x": 3654.625, - "y": 852, - "width": 90.75000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 129827362, - "version": 1, - "versionNonce": 100068740, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Findings zurück", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-verify-build", - "originalText": "Findings zurück", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-mq-verify", - "type": "arrow", - "x": 5000, - "y": 624, - "width": 980, - "height": 236, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 378502112, - "version": 1, - "versionNonce": 605508529, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-mq-verify" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -490.0, - 236 - ], - [ - -980, - 136 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-mq", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-verify", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-mq-verify", - "type": "text", - "x": 4419.25, - "y": 852, - "width": 181.50000000000003, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 791960550, - "version": 1, - "versionNonce": 1140321590, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Smoke rot → zurück in die Lane", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-mq-verify", - "originalText": "Smoke rot → zurück in die Lane", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-orch-lane2", - "type": "arrow", - "x": 2230.0, - "y": 780, - "width": 310.0, - "height": 249.5, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1228724469, - "version": 1, - "versionNonce": 2089641415, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-orch-lane2" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 310.0, - 249.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-orch", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-lane2", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-orch-lane2", - "type": "text", - "x": 2345.675, - "y": 896.75, - "width": 78.65, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 282562726, - "version": 1, - "versionNonce": 197981077, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "öffnet Lane 2", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-orch-lane2", - "originalText": "öffnet Lane 2", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-orch-lane3", - "type": "arrow", - "x": 2230.0, - "y": 780, - "width": 730.0, - "height": 249.5, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 777700808, - "version": 1, - "versionNonce": 297229584, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-orch-lane3" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 730.0, - 249.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-orch", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-lane3", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-orch-lane3", - "type": "text", - "x": 2540.55, - "y": 896.75, - "width": 108.9, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1913279574, - "version": 1, - "versionNonce": 967110115, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "… bis lanes-Budget", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-orch-lane3", - "originalText": "… bis lanes-Budget", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-lane2-sample", - "type": "arrow", - "x": 2720, - "y": 1029.5, - "width": 1950, - "height": 371, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 2105593595, - "version": 1, - "versionNonce": 710741819, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-lane2-sample" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 1950, - 0.5 - ], - [ - 1880, - -370.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-lane2", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-sample", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-lane2-sample", - "type": "text", - "x": 4663.95, - "y": 1022, - "width": 12.100000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1410750823, - "version": 1, - "versionNonce": 1573901718, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "PR", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-lane2-sample", - "originalText": "PR", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-lane3-sample", - "type": "arrow", - "x": 3140, - "y": 1029.5, - "width": 1550, - "height": 431, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1484387071, - "version": 1, - "versionNonce": 1120855397, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - 1550, - 60.5 - ], - [ - 1530.0, - -370.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-lane3", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-sample", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "e-orch-esk", - "type": "arrow", - "x": 2230.0, - "y": 560, - "width": 920.0, - "height": 396, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1255075915, - "version": 1, - "versionNonce": 2033354169, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-orch-esk" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 920.0, - -396 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-orch", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-esk", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-orch-esk", - "type": "text", - "x": 2662.775, - "y": 354.0, - "width": 54.45, - "height": 13.75, - "angle": 0, - "strokeColor": "#c92a2a", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 301393283, - "version": 1, - "versionNonce": 1266748287, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "eskaliert", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-orch-esk", - "originalText": "eskaliert", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-spec-esk", - "type": "arrow", - "x": 2650.0, - "y": 560, - "width": 500.0, - "height": 396, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 75334865, - "version": 1, - "versionNonce": 1983388773, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 500.0, - -396 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-spec", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-esk", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "e-plan-esk", - "type": "arrow", - "x": 3070.0, - "y": 560, - "width": 80.0, - "height": 396, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 38427498, - "version": 1, - "versionNonce": 1019609984, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 80.0, - -396 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-plan", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-esk", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "e-build-esk", - "type": "arrow", - "x": 3490.0, - "y": 560, - "width": 340.0, - "height": 396, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1954666366, - "version": 1, - "versionNonce": 767665384, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - -340.0, - -396 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-build", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-esk", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "e-verify-esk", - "type": "arrow", - "x": 3910.0, - "y": 560, - "width": 760.0, - "height": 396, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1505321654, - "version": 1, - "versionNonce": 669595491, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-verify-esk" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - -760.0, - -396 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-verify", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-esk", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-verify-esk", - "type": "text", - "x": 3493.7, - "y": 354.0, - "width": 72.60000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#c92a2a", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 2067535009, - "version": 1, - "versionNonce": 72030578, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Pflicht-Stop", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-verify-esk", - "originalText": "Pflicht-Stop", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-judge-esk", - "type": "arrow", - "x": 2960, - "y": 1299.5, - "width": 630, - "height": 1178.0, - "angle": 0, - "strokeColor": "#e03131", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "dashed", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 45653732, - "version": 1, - "versionNonce": 1284995613, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-judge-esk" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0.0 - ], - [ - -530, - 0.5 - ], - [ - -530, - -1159.5 - ], - [ - 100, - -1177.5 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-judge", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-esk", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-judge-esk", - "type": "text", - "x": 2387.65, - "y": 132, - "width": 84.7, - "height": 13.75, - "angle": 0, - "strokeColor": "#c92a2a", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1366195859, - "version": 1, - "versionNonce": 160704061, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "hängt / thrash", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-judge-esk", - "originalText": "hängt / thrash", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-metrics-sample", - "type": "arrow", - "x": 2630.0, - "y": 1250, - "width": 2110.0, - "height": 591, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 1035593341, - "version": 1, - "versionNonce": 144462451, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-metrics-sample" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 0.0, - -70 - ], - [ - 2110.0, - -70 - ], - [ - 2110.0, - -591 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-metrics", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-sample", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-metrics-sample", - "type": "text", - "x": 4691.6, - "y": 1172, - "width": 96.80000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1568732475, - "version": 1, - "versionNonce": 668002832, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "speist Schwellen", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-metrics-sample", - "originalText": "speist Schwellen", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-e2e-pool", - "type": "arrow", - "x": 3490.0, - "y": 1470, - "width": 3190.0, - "height": 906, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 684842185, - "version": 1, - "versionNonce": 293358725, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-e2e-pool" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 0.0, - 90 - ], - [ - -3190.0, - 90 - ], - [ - -3150.0, - -816 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-e2e", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-e2e-pool", - "type": "text", - "x": 212.27499999999998, - "y": 1552, - "width": 175.45000000000002, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 2146104173, - "version": 1, - "versionNonce": 155546662, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Fehlschlag → Story (Trace-ID)", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-e2e-pool", - "originalText": "Fehlschlag → Story (Trace-ID)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-drift-pool", - "type": "arrow", - "x": 3910.0, - "y": 1470, - "width": 3650.0, - "height": 926, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": { - "type": 2 - }, - "seed": 161625853, - "version": 1, - "versionNonce": 973005003, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-drift-pool" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0.0, - 0 - ], - [ - 0.0, - 130 - ], - [ - -3650.0, - 130 - ], - [ - -3480.0, - -796 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-drift", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": { - "elementId": "n-pool", - "focus": 0, - "gap": 6, - "fixedPoint": null - } - }, - { - "id": "l-drift-pool", - "type": "text", - "x": 205.55, - "y": 1592, - "width": 108.9, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1172730855, - "version": 1, - "versionNonce": 789792721, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Findings → Stories", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-drift-pool", - "originalText": "Findings → Stories", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "e-exit", - "type": "arrow", - "x": 5600, - "y": 602.0, - "width": 200, - "height": 0, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 4, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1582707250, - "version": 1, - "versionNonce": 95487716, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "l-exit" - } - ], - "updated": 1788196217117, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - 200, - 0 - ] - ], - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false, - "lastCommittedPoint": null, - "startBinding": { - "elementId": "n-merge", - "focus": 0, - "gap": 6, - "fixedPoint": null - }, - "endBinding": null - }, - { - "id": "l-exit", - "type": "text", - "x": 5636.475, - "y": 576.0, - "width": 127.05000000000001, - "height": 13.75, - "angle": 0, - "strokeColor": "#0b7285", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 0, - "opacity": 100, - "groupIds": [], - "frameId": null, - "roundness": null, - "seed": 1933155933, - "version": 1, - "versionNonce": 2010580388, - "isDeleted": false, - "boundElements": [], - "updated": 1788196217117, - "link": null, - "locked": false, - "text": "Ausfahrt → Projekt-CI", - "fontSize": 11, - "fontFamily": 3, - "textAlign": "center", - "verticalAlign": "top", - "containerId": "e-exit", - "originalText": "Ausfahrt → Projekt-CI", - "autoResize": true, - "lineHeight": 1.25 - } - ], - "appState": { - "gridSize": 20, - "viewBackgroundColor": "#ffffff" - }, - "files": {} -} \ No newline at end of file +{"type": "excalidraw", "version": 2, "source": "https://excalidraw.com", "appState": {"viewBackgroundColor": "#ffffff", "gridSize": 20}, "files": {}, "elements": [{"type": "text", "id": "hd_title", "x": 140, "y": 30, "width": 420, "height": 35, "text": "Dark Factory · Landkarte", "originalText": "Dark Factory · Landkarte", "fontSize": 28, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#1e40af", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 101, "version": 1, "versionNonce": 102, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "hd_sub", "x": 140, "y": 80, "width": 900, "height": 34, "text": "Die Straße läuft von links nach rechts. Violett arbeitet ein Modell, Blau Mechanik, Orange bist du, Grün ist das Ziel.\nGestrichelt = fehlt noch · Ellipsen = Loops (iterieren bis clean) · dick = Hauptstraße · rot = Ausfahrt zu dir · orange = Audit-Wege & Rückführ-Spur", "originalText": "Die Straße läuft von links nach rechts. Violett arbeitet ein Modell, Blau Mechanik, Orange bist du, Grün ist das Ziel.\nGestrichelt = fehlt noch · Ellipsen = Loops (iterieren bis clean) · dick = Hauptstraße · rot = Ausfahrt zu dir · orange = Audit-Wege & Rückführ-Spur", "fontSize": 13, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 103, "version": 1, "versionNonce": 104, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "hd_summary", "x": 140, "y": 136, "width": 880, "height": 18, "text": "Auf einen Blick: Einwurf → Karte → dein Go → Takt → bauen & prüfen (×lanes) → Stichprobe → main", "originalText": "Auf einen Blick: Einwurf → Karte → dein Go → Takt → bauen & prüfen (×lanes) → Stichprobe → main", "fontSize": 14, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#1e40af", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 105, "version": 1, "versionNonce": 106, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "rectangle", "id": "z_intake", "x": 380, "y": 245, "width": 900, "height": 170, "strokeColor": "#94a3b8", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 111, "version": 1, "versionNonce": 112, "isDeleted": false, "groupIds": [], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "z_intake_t", "x": 396, "y": 253, "width": 300, "height": 18, "text": "① INTAKE — Karte statt Produktion", "originalText": "① INTAKE — Karte statt Produktion", "fontSize": 13, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#1e40af", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 113, "version": 1, "versionNonce": 114, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "rectangle", "id": "z_prod", "x": 1670, "y": 445, "width": 1550, "height": 560, "strokeColor": "#94a3b8", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 115, "version": 1, "versionNonce": 116, "isDeleted": false, "groupIds": [], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "z_prod_t", "x": 1686, "y": 453, "width": 560, "height": 18, "text": "② PRODUKTION — eine Lane pro Ast (×lanes) · Reviewer nie der Autor", "originalText": "② PRODUKTION — eine Lane pro Ast (×lanes) · Reviewer nie der Autor", "fontSize": 13, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#1e40af", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 117, "version": 1, "versionNonce": 118, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "rectangle", "id": "z_abn", "x": 3290, "y": 445, "width": 900, "height": 300, "strokeColor": "#94a3b8", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 119, "version": 1, "versionNonce": 120, "isDeleted": false, "groupIds": [], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "z_abn_t", "x": 3306, "y": 453, "width": 300, "height": 18, "text": "③ ABNAHME — Stichprobe & Smoke", "originalText": "③ ABNAHME — Stichprobe & Smoke", "fontSize": 13, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#1e40af", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 121, "version": 1, "versionNonce": 122, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "rectangle", "id": "z_mon", "x": 1670, "y": 1080, "width": 1540, "height": 220, "strokeColor": "#94a3b8", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 123, "version": 1, "versionNonce": 124, "isDeleted": false, "groupIds": [], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "z_mon_t", "x": 1686, "y": 1088, "width": 520, "height": 18, "text": "④ ÜBERWACHUNG — beobachtet & meldet, fixt nie selbst", "originalText": "④ ÜBERWACHUNG — beobachtet & meldet, fixt nie selbst", "fontSize": 13, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#1e40af", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 125, "version": 1, "versionNonce": 126, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "ellipse", "id": "st_in", "x": 75, "y": 510, "width": 150, "height": 100, "strokeColor": "#c2410c", "backgroundColor": "#fed7aa", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 201, "version": 1, "versionNonce": 202, "isDeleted": false, "groupIds": ["g_in"], "boundElements": [{"id": "r_1", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_in", "x": 115, "y": 551, "width": 70, "height": 18, "text": "Einwurf", "originalText": "Einwurf", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 203, "version": 1, "versionNonce": 204, "isDeleted": false, "groupIds": ["g_in"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_in", "x": 50, "y": 625, "width": 200, "height": 30, "text": "Ideen · Stories · Bugs\njederzeit, folgenlos", "originalText": "Ideen · Stories · Bugs\njederzeit, folgenlos", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 205, "version": 1, "versionNonce": 206, "isDeleted": false, "groupIds": ["g_in"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_pool", "x": 405, "y": 525, "width": 170, "height": 70, "strokeColor": "#1e3a5f", "backgroundColor": "#93c5fd", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 211, "version": 1, "versionNonce": 212, "isDeleted": false, "groupIds": ["g_pool"], "boundElements": [{"id": "r_1", "type": "arrow"}, {"id": "r_2", "type": "arrow"}], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_pool", "x": 442, "y": 551, "width": 96, "height": 18, "text": "Story-Pool", "originalText": "Story-Pool", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 213, "version": 1, "versionNonce": 214, "isDeleted": false, "groupIds": ["g_pool"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_pool", "x": 385, "y": 625, "width": 210, "height": 30, "text": "einzige Wahrheit · Status-Spalte\nlanes-Regler: deiner", "originalText": "einzige Wahrheit · Status-Spalte\nlanes-Regler: deiner", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 215, "version": 1, "versionNonce": 216, "isDeleted": false, "groupIds": ["g_pool"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_frei", "x": 745, "y": 525, "width": 170, "height": 70, "strokeColor": "#c2410c", "backgroundColor": "#fed7aa", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 221, "version": 1, "versionNonce": 222, "isDeleted": false, "groupIds": ["g_frei"], "boundElements": [{"id": "r_2", "type": "arrow"}, {"id": "r_3", "type": "arrow"}], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_frei", "x": 791, "y": 551, "width": 78, "height": 18, "text": "Freigabe", "originalText": "Freigabe", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 223, "version": 1, "versionNonce": 224, "isDeleted": false, "groupIds": ["g_frei"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_frei", "x": 715, "y": 625, "width": 230, "height": 30, "text": "dein „Go“ auf den Wellen-Plan\nRegler: Story → Welle → Standing-Auto", "originalText": "dein „Go“ auf den Wellen-Plan\nRegler: Story → Welle → Standing-Auto", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 225, "version": 1, "versionNonce": 226, "isDeleted": false, "groupIds": ["g_frei"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "ellipse", "id": "st_takt", "x": 1060, "y": 515, "width": 140, "height": 95, "strokeColor": "#1e3a5f", "backgroundColor": "#93c5fd", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 231, "version": 1, "versionNonce": 232, "isDeleted": false, "groupIds": ["g_takt"], "boundElements": [{"id": "r_3", "type": "arrow"}, {"id": "r_4", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_takt", "x": 1087, "y": 553, "width": 86, "height": 18, "text": "Takt-Loop", "originalText": "Takt-Loop", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 233, "version": 1, "versionNonce": 234, "isDeleted": false, "groupIds": ["g_takt"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_takt", "x": 1078, "y": 576, "width": 104, "height": 30, "text": "die Uhr — Loop", "originalText": "die Uhr — Loop", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 235, "version": 1, "versionNonce": 236, "isDeleted": false, "groupIds": ["g_takt"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "ellipse", "id": "st_orch", "x": 1370, "y": 505, "width": 160, "height": 110, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 241, "version": 1, "versionNonce": 242, "isDeleted": false, "groupIds": ["g_orch"], "boundElements": [{"id": "r_4", "type": "arrow"}, {"id": "r_5", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_orch", "x": 1392, "y": 551, "width": 116, "height": 18, "text": "Orchestrator", "originalText": "Orchestrator", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 243, "version": 1, "versionNonce": 244, "isDeleted": false, "groupIds": ["g_orch"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_orch", "x": 1385, "y": 574, "width": 130, "height": 30, "text": "plant · öffnet Lanes\npro Tick frisch", "originalText": "plant · öffnet Lanes\npro Tick frisch", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 245, "version": 1, "versionNonce": 246, "isDeleted": false, "groupIds": ["g_orch"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "ellipse", "id": "st_spec", "x": 1710, "y": 505, "width": 160, "height": 110, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 251, "version": 1, "versionNonce": 252, "isDeleted": false, "groupIds": ["g_spec"], "boundElements": [{"id": "r_5", "type": "arrow"}, {"id": "r_6", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_spec", "x": 1747, "y": 551, "width": 86, "height": 18, "text": "Spec-Loop", "originalText": "Spec-Loop", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 253, "version": 1, "versionNonce": 254, "isDeleted": false, "groupIds": ["g_spec"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_spec", "x": 1725, "y": 580, "width": 130, "height": 16, "text": "Design + Gate A", "originalText": "Design + Gate A", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 255, "version": 1, "versionNonce": 256, "isDeleted": false, "groupIds": ["g_spec"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "ellipse", "id": "st_plan", "x": 2030, "y": 505, "width": 160, "height": 110, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 261, "version": 1, "versionNonce": 262, "isDeleted": false, "groupIds": ["g_plan"], "boundElements": [{"id": "r_6", "type": "arrow"}, {"id": "r_7", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_plan", "x": 2067, "y": 551, "width": 86, "height": 18, "text": "Plan-Loop", "originalText": "Plan-Loop", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 263, "version": 1, "versionNonce": 264, "isDeleted": false, "groupIds": ["g_plan"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_plan", "x": 2045, "y": 580, "width": 130, "height": 16, "text": "Plan + Gate A", "originalText": "Plan + Gate A", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 265, "version": 1, "versionNonce": 266, "isDeleted": false, "groupIds": ["g_plan"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "ellipse", "id": "st_bau", "x": 2350, "y": 505, "width": 160, "height": 110, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 271, "version": 1, "versionNonce": 272, "isDeleted": false, "groupIds": ["g_bau"], "boundElements": [{"id": "r_7", "type": "arrow"}, {"id": "r_8", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_bau", "x": 2394, "y": 551, "width": 72, "height": 18, "text": "Bau-Loop", "originalText": "Bau-Loop", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 273, "version": 1, "versionNonce": 274, "isDeleted": false, "groupIds": ["g_bau"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_bau", "x": 2365, "y": 574, "width": 130, "height": 30, "text": "TDD rot bis grün\nAC read-only", "originalText": "TDD rot bis grün\nAC read-only", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 275, "version": 1, "versionNonce": 276, "isDeleted": false, "groupIds": ["g_bau"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "ellipse", "id": "st_verify", "x": 2670, "y": 505, "width": 160, "height": 110, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 281, "version": 1, "versionNonce": 282, "isDeleted": false, "groupIds": ["g_verify"], "boundElements": [{"id": "r_8", "type": "arrow"}, {"id": "r_9", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_verify", "x": 2721, "y": 551, "width": 58, "height": 18, "text": "Verify", "originalText": "Verify", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 283, "version": 1, "versionNonce": 284, "isDeleted": false, "groupIds": ["g_verify"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_verify", "x": 2685, "y": 574, "width": 130, "height": 30, "text": "Battery + Gate B\nnie der Autor", "originalText": "Battery + Gate B\nnie der Autor", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 285, "version": 1, "versionNonce": 286, "isDeleted": false, "groupIds": ["g_verify"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_pr", "x": 2985, "y": 525, "width": 170, "height": 70, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 291, "version": 1, "versionNonce": 292, "isDeleted": false, "groupIds": ["g_pr"], "boundElements": [{"id": "r_9", "type": "arrow"}, {"id": "r_10", "type": "arrow"}], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_pr", "x": 3056, "y": 551, "width": 28, "height": 18, "text": "PR", "originalText": "PR", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 293, "version": 1, "versionNonce": 294, "isDeleted": false, "groupIds": ["g_pr"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_pr", "x": 2960, "y": 625, "width": 220, "height": 30, "text": "Bots: opportunistisch\nnichts blockiert", "originalText": "Bots: opportunistisch\nnichts blockiert", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 295, "version": 1, "versionNonce": 296, "isDeleted": false, "groupIds": ["g_pr"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "diamond", "id": "st_sample", "x": 3325, "y": 495, "width": 170, "height": 130, "strokeColor": "#b45309", "backgroundColor": "#fef3c7", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 301, "version": 1, "versionNonce": 302, "isDeleted": false, "groupIds": ["g_sample"], "boundElements": [{"id": "r_10", "type": "arrow"}, {"id": "r_11", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_sample", "x": 3360, "y": 551, "width": 100, "height": 18, "text": "Stichprobe?", "originalText": "Stichprobe?", "fontSize": 14, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 303, "version": 1, "versionNonce": 304, "isDeleted": false, "groupIds": ["g_sample"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_sample", "x": 3300, "y": 640, "width": 220, "height": 30, "text": "Sample-Gate: zieht x %\nArchitektur-Merges: immer", "originalText": "Sample-Gate: zieht x %\nArchitektur-Merges: immer", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 305, "version": 1, "versionNonce": 306, "isDeleted": false, "groupIds": ["g_sample"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_mq", "x": 3665, "y": 525, "width": 170, "height": 70, "strokeColor": "#1e3a5f", "backgroundColor": "#93c5fd", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 311, "version": 1, "versionNonce": 312, "isDeleted": false, "groupIds": ["g_mq"], "boundElements": [{"id": "r_11", "type": "arrow"}, {"id": "r_12", "type": "arrow"}], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_mq", "x": 3696, "y": 551, "width": 108, "height": 18, "text": "Merge-Queue", "originalText": "Merge-Queue", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 313, "version": 1, "versionNonce": 314, "isDeleted": false, "groupIds": ["g_mq"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_mq", "x": 3640, "y": 625, "width": 220, "height": 30, "text": "seriell: rebase → Smoke\nnur Grünes landet", "originalText": "seriell: rebase → Smoke\nnur Grünes landet", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 315, "version": 1, "versionNonce": 316, "isDeleted": false, "groupIds": ["g_mq"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "ellipse", "id": "st_main", "x": 4015, "y": 510, "width": 150, "height": 100, "strokeColor": "#047857", "backgroundColor": "#a7f3d0", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 321, "version": 1, "versionNonce": 322, "isDeleted": false, "groupIds": ["g_main"], "boundElements": [{"id": "r_12", "type": "arrow"}, {"id": "r_exit", "type": "arrow"}], "link": null, "locked": false}, {"type": "text", "id": "t_main", "x": 4070, "y": 551, "width": 40, "height": 18, "text": "main", "originalText": "main", "fontSize": 15, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 323, "version": 1, "versionNonce": 324, "isDeleted": false, "groupIds": ["g_main"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "text", "id": "s_main", "x": 4010, "y": 625, "width": 160, "height": 16, "text": "Fabrik endet hier", "originalText": "Fabrik endet hier", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 325, "version": 1, "versionNonce": 326, "isDeleted": false, "groupIds": ["g_main"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "r_1", "x": 229, "y": 560, "width": 172, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 401, "version": 1, "versionNonce": 402, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [172, 0]], "startBinding": {"elementId": "st_in", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_pool", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "arrow", "id": "r_2", "x": 579, "y": 560, "width": 162, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 403, "version": 1, "versionNonce": 404, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [162, 0]], "startBinding": {"elementId": "st_pool", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_frei", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_2", "x": 590, "y": 536, "width": 140, "height": 15, "text": "klassifiziert", "originalText": "klassifiziert", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 405, "version": 1, "versionNonce": 406, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_3", "x": 919, "y": 560, "width": 137, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 407, "version": 1, "versionNonce": 408, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [137, 0]], "startBinding": {"elementId": "st_frei", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_takt", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_3", "x": 920, "y": 536, "width": 135, "height": 15, "text": "freigegeben", "originalText": "freigegeben", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 409, "version": 1, "versionNonce": 410, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_4", "x": 1204, "y": 560, "width": 162, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 411, "version": 1, "versionNonce": 412, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [162, 0]], "startBinding": {"elementId": "st_takt", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_orch", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_4", "x": 1230, "y": 536, "width": 110, "height": 15, "text": "weckt", "originalText": "weckt", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 413, "version": 1, "versionNonce": 414, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_5", "x": 1534, "y": 560, "width": 172, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 415, "version": 1, "versionNonce": 416, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [172, 0]], "startBinding": {"elementId": "st_orch", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_spec", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_5", "x": 1540, "y": 536, "width": 160, "height": 15, "text": "Story + Karte", "originalText": "Story + Karte", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 417, "version": 1, "versionNonce": 418, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_6", "x": 1874, "y": 560, "width": 152, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 419, "version": 1, "versionNonce": 420, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [152, 0]], "startBinding": {"elementId": "st_spec", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_plan", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_6", "x": 1910, "y": 536, "width": 80, "height": 15, "text": "Spec", "originalText": "Spec", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 421, "version": 1, "versionNonce": 422, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_7", "x": 2194, "y": 560, "width": 152, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 423, "version": 1, "versionNonce": 424, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [152, 0]], "startBinding": {"elementId": "st_plan", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_bau", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_7", "x": 2230, "y": 536, "width": 80, "height": 15, "text": "Plan", "originalText": "Plan", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 425, "version": 1, "versionNonce": 426, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_8", "x": 2514, "y": 560, "width": 152, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 427, "version": 1, "versionNonce": 428, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [152, 0]], "startBinding": {"elementId": "st_bau", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_verify", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_8", "x": 2550, "y": 536, "width": 80, "height": 15, "text": "Diff", "originalText": "Diff", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 429, "version": 1, "versionNonce": 430, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_9", "x": 2834, "y": 560, "width": 147, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 431, "version": 1, "versionNonce": 432, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [147, 0]], "startBinding": {"elementId": "st_verify", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_pr", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_9", "x": 2870, "y": 536, "width": 80, "height": 15, "text": "grün", "originalText": "grün", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 433, "version": 1, "versionNonce": 434, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_10", "x": 3159, "y": 560, "width": 162, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 435, "version": 1, "versionNonce": 436, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [162, 0]], "startBinding": {"elementId": "st_pr", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_sample", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "arrow", "id": "r_11", "x": 3499, "y": 560, "width": 162, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 439, "version": 1, "versionNonce": 440, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [162, 0]], "startBinding": {"elementId": "st_sample", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_mq", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_11", "x": 3495, "y": 536, "width": 170, "height": 15, "text": "nicht gezogen", "originalText": "nicht gezogen", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 441, "version": 1, "versionNonce": 442, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_12", "x": 3839, "y": 560, "width": 172, "height": 0, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 443, "version": 1, "versionNonce": 444, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [172, 0]], "startBinding": {"elementId": "st_mq", "focus": 0, "gap": 4}, "endBinding": {"elementId": "st_main", "focus": 0, "gap": 4}, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_12", "x": 3850, "y": 536, "width": 150, "height": 15, "text": "Smoke grün", "originalText": "Smoke grün", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 445, "version": 1, "versionNonce": 446, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "arrow", "id": "r_exit", "x": 4169, "y": 560, "width": 170, "height": 0, "strokeColor": "#047857", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 3, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 447, "version": 1, "versionNonce": 448, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [170, 0]], "startBinding": {"elementId": "st_main", "focus": 0, "gap": 4}, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow"}, {"type": "text", "id": "rl_exit", "x": 4180, "y": 536, "width": 170, "height": 15, "text": "→ Projekt-CI", "originalText": "→ Projekt-CI", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#047857", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 449, "version": 1, "versionNonce": 450, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.25}, {"type": "ellipse", "id": "st_intake", "x": 410, "y": 275, "width": 160, "height": 110, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9002, "version": 1, "versionNonce": 9003, "isDeleted": false, "groupIds": ["g_intake"], "boundElements": [], "link": null, "locked": false}, {"type": "text", "id": "t_intake", "x": 442, "y": 314, "width": 96, "height": 15, "text": "Intake-Loop", "originalText": "Intake-Loop", "fontSize": 14, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9004, "version": 1, "versionNonce": 9005, "isDeleted": false, "groupIds": ["g_intake"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_intake", "x": 400, "y": 391, "width": 180, "height": 15, "text": "Karte: 8 Dimensionen · Batch", "originalText": "Karte: 8 Dimensionen · Batch", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9006, "version": 1, "versionNonce": 9007, "isDeleted": false, "groupIds": ["g_intake"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "ellipse", "id": "st_bew", "x": 750, "y": 275, "width": 160, "height": 110, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9008, "version": 1, "versionNonce": 9009, "isDeleted": false, "groupIds": ["g_bew"], "boundElements": [], "link": null, "locked": false}, {"type": "text", "id": "t_bew", "x": 765, "y": 314, "width": 130, "height": 15, "text": "Bewertungs-Loop", "originalText": "Bewertungs-Loop", "fontSize": 12.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9010, "version": 1, "versionNonce": 9011, "isDeleted": false, "groupIds": ["g_bew"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_bew", "x": 730, "y": 391, "width": 200, "height": 15, "text": "Architektur-Urteile · Meta-Stories", "originalText": "Architektur-Urteile · Meta-Stories", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9012, "version": 1, "versionNonce": 9013, "isDeleted": false, "groupIds": ["g_bew"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_arch", "x": 1085, "y": 295, "width": 170, "height": 70, "strokeColor": "#1e3a5f", "backgroundColor": "#93c5fd", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9014, "version": 1, "versionNonce": 9015, "isDeleted": false, "groupIds": ["g_arch"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_arch", "x": 1117, "y": 321, "width": 106, "height": 15, "text": "Architektur", "originalText": "Architektur", "fontSize": 14, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9016, "version": 1, "versionNonce": 9017, "isDeleted": false, "groupIds": ["g_arch"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_arch", "x": 1080, "y": 375, "width": 180, "height": 15, "text": "AGENTS.md: Baum · Ziele", "originalText": "AGENTS.md: Baum · Ziele", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9018, "version": 1, "versionNonce": 9019, "isDeleted": false, "groupIds": ["g_arch"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_ph0", "x": 1085, "y": 150, "width": 170, "height": 60, "strokeColor": "#c2410c", "backgroundColor": "#fed7aa", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9020, "version": 1, "versionNonce": 9021, "isDeleted": false, "groupIds": [], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_ph0", "x": 1136, "y": 171, "width": 68, "height": 15, "text": "Phase 0", "originalText": "Phase 0", "fontSize": 14, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9022, "version": 1, "versionNonce": 9023, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_ph0", "x": 1270, "y": 152, "width": 210, "height": 45, "text": "einmalig: Baum v1 + Ziele\nGreenfield aus dem Pool,\nBestand aus dem Code", "originalText": "einmalig: Baum v1 + Ziele\nGreenfield aus dem Pool,\nBestand aus dem Code", "fontSize": 10.5, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9024, "version": 1, "versionNonce": 9025, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_pool_intake", "x": 455, "y": 520, "width": 0, "height": 128, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9026, "version": 1, "versionNonce": 9027, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [0, -128]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "arrow", "id": "a_intake_pool", "x": 525, "y": 392, "width": 0, "height": 128, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9028, "version": 1, "versionNonce": 9029, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [0, 128]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_newstory", "x": 330, "y": 448, "width": 110, "height": 15, "text": "neue Story", "originalText": "neue Story", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9030, "version": 1, "versionNonce": 9031, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "l_karte", "x": 543, "y": 448, "width": 60, "height": 15, "text": "Karte", "originalText": "Karte", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9032, "version": 1, "versionNonce": 9033, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_intake_bew", "x": 573, "y": 318, "width": 174, "height": 0, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9034, "version": 1, "versionNonce": 9035, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [174, 0]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "arrow", "id": "a_bew_intake", "x": 747, "y": 346, "width": 174, "height": 0, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9036, "version": 1, "versionNonce": 9037, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [-174, 0]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_urteil", "x": 590, "y": 296, "width": 140, "height": 15, "text": "fragt Urteil an", "originalText": "fragt Urteil an", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9038, "version": 1, "versionNonce": 9039, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "l_urteil2", "x": 585, "y": 352, "width": 150, "height": 15, "text": "Architektur-Urteil", "originalText": "Architektur-Urteil", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9040, "version": 1, "versionNonce": 9041, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_bew_arch", "x": 912, "y": 330, "width": 171, "height": 0, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9042, "version": 1, "versionNonce": 9043, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [171, 0]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_pflegt", "x": 915, "y": 306, "width": 170, "height": 15, "text": "liest & pflegt Baum", "originalText": "liest & pflegt Baum", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9044, "version": 1, "versionNonce": 9045, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_bew_pool", "x": 790, "y": 388, "width": 225, "height": 134, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9046, "version": 1, "versionNonce": 9047, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [-225, 134]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_meta", "x": 618, "y": 470, "width": 190, "height": 15, "text": "Meta-Stories in den Pool", "originalText": "Meta-Stories in den Pool", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9048, "version": 1, "versionNonce": 9049, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_ph0_arch", "x": 1170, "y": 212, "width": 0, "height": 80, "strokeColor": "#c2410c", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9050, "version": 1, "versionNonce": 9051, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [0, 80]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_baumv1", "x": 1185, "y": 240, "width": 70, "height": 15, "text": "Baum v1", "originalText": "Baum v1", "fontSize": 10.5, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9052, "version": 1, "versionNonce": 9053, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_views", "x": 405, "y": 790, "width": 200, "height": 70, "strokeColor": "#1e3a5f", "backgroundColor": "#93c5fd", "fillStyle": "hachure", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9054, "version": 1, "versionNonce": 9055, "isDeleted": false, "groupIds": ["g_views"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_views", "x": 430, "y": 815, "width": 150, "height": 15, "text": "Views · gerechnet", "originalText": "Views · gerechnet", "fontSize": 13, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9056, "version": 1, "versionNonce": 9057, "isDeleted": false, "groupIds": ["g_views"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_views", "x": 330, "y": 870, "width": 350, "height": 30, "text": "Roadmap · Wellen-Plan · Dashboard · As-built\naus dem Pool gerechnet — nie gepflegt", "originalText": "Roadmap · Wellen-Plan · Dashboard · As-built\naus dem Pool gerechnet — nie gepflegt", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9058, "version": 1, "versionNonce": 9059, "isDeleted": false, "groupIds": ["g_views"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_pool_views", "x": 480, "y": 597, "width": 0, "height": 191, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9060, "version": 1, "versionNonce": 9061, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [0, 191]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "arrow", "id": "a_views_frei", "x": 607, "y": 808, "width": 193, "height": 210, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9062, "version": 1, "versionNonce": 9063, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [193, -210]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_wplan", "x": 655, "y": 712, "width": 110, "height": 15, "text": "Wellen-Plan", "originalText": "Wellen-Plan", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9064, "version": 1, "versionNonce": 9065, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_audit", "x": 3495, "y": 270, "width": 170, "height": 70, "strokeColor": "#c2410c", "backgroundColor": "#fed7aa", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9066, "version": 1, "versionNonce": 9067, "isDeleted": false, "groupIds": ["g_audit"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_audit", "x": 3552, "y": 296, "width": 56, "height": 15, "text": "Audit", "originalText": "Audit", "fontSize": 14, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9068, "version": 1, "versionNonce": 9069, "isDeleted": false, "groupIds": ["g_audit"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_audit", "x": 3680, "y": 278, "width": 220, "height": 30, "text": "du, mit Punchlist\n„Nein“ = immer wieder du", "originalText": "du, mit Punchlist\n„Nein“ = immer wieder du", "fontSize": 10.5, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9070, "version": 1, "versionNonce": 9071, "isDeleted": false, "groupIds": ["g_audit"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_sample_audit", "x": 3425, "y": 505, "width": 120, "height": 161, "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9072, "version": 1, "versionNonce": 9073, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [120, -161]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_gezogen", "x": 3400, "y": 412, "width": 80, "height": 15, "text": "gezogen", "originalText": "gezogen", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9074, "version": 1, "versionNonce": 9075, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_audit_mq", "x": 3667, "y": 325, "width": 90, "height": 197, "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9076, "version": 1, "versionNonce": 9077, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [90, 197]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_ja", "x": 3735, "y": 410, "width": 50, "height": 15, "text": "„Ja“", "originalText": "„Ja“", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9078, "version": 1, "versionNonce": 9079, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_audit_nein", "x": 3493, "y": 300, "width": 1063, "height": 200, "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9080, "version": 1, "versionNonce": 9081, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [-1063, 0], [-1063, 200]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_nein", "x": 2760, "y": 280, "width": 340, "height": 15, "text": "„Nein“ → Punchlist zurück in die Lane", "originalText": "„Nein“ → Punchlist zurück in die Lane", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9082, "version": 1, "versionNonce": 9083, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "ret_verify_bau", "x": 2740, "y": 613, "width": 300, "height": 87, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9084, "version": 1, "versionNonce": 9085, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [0, 87], [-300, 87], [-300, 0]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_findings", "x": 2520, "y": 706, "width": 150, "height": 15, "text": "Findings zurück", "originalText": "Findings zurück", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9086, "version": 1, "versionNonce": 9087, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "ret_mq_verify", "x": 3650, "y": 597, "width": 888, "height": 163, "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9088, "version": 1, "versionNonce": 9089, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [0, 163], [-888, 163], [-888, 17]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_smokerot", "x": 3130, "y": 766, "width": 280, "height": 15, "text": "Smoke rot → zurück in die Lane", "originalText": "Smoke rot → zurück in die Lane", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#1e3a5f", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9090, "version": 1, "versionNonce": 9091, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_lane2", "x": 1720, "y": 830, "width": 300, "height": 54, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9092, "version": 1, "versionNonce": 9093, "isDeleted": false, "groupIds": ["g_l2"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_lane2", "x": 1740, "y": 849, "width": 262, "height": 15, "text": "Lane 2 · Spec→Plan→Bau→Verify→PR", "originalText": "Lane 2 · Spec→Plan→Bau→Verify→PR", "fontSize": 11.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9094, "version": 1, "versionNonce": 9095, "isDeleted": false, "groupIds": ["g_l2"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_lane3", "x": 1720, "y": 904, "width": 300, "height": 54, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9096, "version": 1, "versionNonce": 9097, "isDeleted": false, "groupIds": ["g_l3"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_lane3", "x": 1745, "y": 923, "width": 250, "height": 15, "text": "Lane 3 … bis zum lanes-Budget", "originalText": "Lane 3 … bis zum lanes-Budget", "fontSize": 11.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9098, "version": 1, "versionNonce": 9099, "isDeleted": false, "groupIds": ["g_l3"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_orch_l2", "x": 1462, "y": 608, "width": 256, "height": 247, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9100, "version": 1, "versionNonce": 9101, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [256, 247]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "arrow", "id": "a_orch_l3", "x": 1450, "y": 613, "width": 268, "height": 316, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9102, "version": 1, "versionNonce": 9103, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [268, 316]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "arrow", "id": "a_l2_out", "x": 2022, "y": 857, "width": 1376, "height": 227, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9104, "version": 1, "versionNonce": 9105, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [1323, 0], [1376, -227]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "arrow", "id": "a_l3_out", "x": 2022, "y": 931, "width": 1399, "height": 293, "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9106, "version": 1, "versionNonce": 9107, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [1356, 0], [1399, -293]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_l2pr", "x": 3280, "y": 838, "width": 40, "height": 15, "text": "PR", "originalText": "PR", "fontSize": 10.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#6d28d9", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9108, "version": 1, "versionNonce": 9109, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_esk", "x": 2140, "y": 120, "width": 280, "height": 70, "strokeColor": "#dc2626", "backgroundColor": "#fee2e2", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9110, "version": 1, "versionNonce": 9111, "isDeleted": false, "groupIds": ["g_esk"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_esk", "x": 2185, "y": 146, "width": 190, "height": 15, "text": "Eskalation zu dir", "originalText": "Eskalation zu dir", "fontSize": 14, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9112, "version": 1, "versionNonce": 9113, "isDeleted": false, "groupIds": ["g_esk"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_esk", "x": 2440, "y": 128, "width": 300, "height": 30, "text": "Pflicht-Stop · Rückfrage · hängt\nstoppt & meldet, statt zu drehen", "originalText": "Pflicht-Stop · Rückfrage · hängt\nstoppt & meldet, statt zu drehen", "fontSize": 10.5, "fontFamily": 3, "textAlign": "left", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9114, "version": 1, "versionNonce": 9115, "isDeleted": false, "groupIds": ["g_esk"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "a_esk", "x": 1480, "y": 502, "width": 710, "height": 307, "strokeColor": "#dc2626", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9116, "version": 1, "versionNonce": 9117, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [710, -307]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "rectangle", "id": "st_vet", "x": 1720, "y": 1130, "width": 170, "height": 54, "strokeColor": "#1e3a5f", "backgroundColor": "#93c5fd", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9118, "version": 1, "versionNonce": 9119, "isDeleted": false, "groupIds": ["g_st_vet"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_st_vet", "x": 1754, "y": 1149, "width": 102, "height": 15, "text": "Vet-Preflight", "originalText": "Vet-Preflight", "fontSize": 12.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9120, "version": 1, "versionNonce": 9121, "isDeleted": false, "groupIds": ["g_st_vet"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_st_vet", "x": 1705, "y": 1196, "width": 200, "height": 30, "text": "mechanisch, vor jedem\nModell-Knoten", "originalText": "mechanisch, vor jedem\nModell-Knoten", "fontSize": 10, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9122, "version": 1, "versionNonce": 9123, "isDeleted": false, "groupIds": ["g_st_vet"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_ana", "x": 1990, "y": 1130, "width": 170, "height": 54, "strokeColor": "#1e3a5f", "backgroundColor": "#93c5fd", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9124, "version": 1, "versionNonce": 9125, "isDeleted": false, "groupIds": ["g_st_ana"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_st_ana", "x": 2016, "y": 1149, "width": 118, "height": 15, "text": "Analytics+Trace", "originalText": "Analytics+Trace", "fontSize": 12.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9126, "version": 1, "versionNonce": 9127, "isDeleted": false, "groupIds": ["g_st_ana"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_st_ana", "x": 1975, "y": 1196, "width": 200, "height": 30, "text": "Kosten · Trace-ID\nspeist Sample-Schwellen", "originalText": "Kosten · Trace-ID\nspeist Sample-Schwellen", "fontSize": 10, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9128, "version": 1, "versionNonce": 9129, "isDeleted": false, "groupIds": ["g_st_ana"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_judge", "x": 2260, "y": 1130, "width": 170, "height": 54, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9130, "version": 1, "versionNonce": 9131, "isDeleted": false, "groupIds": ["g_st_judge"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_st_judge", "x": 2290, "y": 1149, "width": 110, "height": 15, "text": "Judge/Watchdog", "originalText": "Judge/Watchdog", "fontSize": 12.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9132, "version": 1, "versionNonce": 9133, "isDeleted": false, "groupIds": ["g_st_judge"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_st_judge", "x": 2245, "y": 1196, "width": 200, "height": 30, "text": "hängt? thrash? → rote\nAusfahrt zu dir", "originalText": "hängt? thrash? → rote\nAusfahrt zu dir", "fontSize": 10, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9134, "version": 1, "versionNonce": 9135, "isDeleted": false, "groupIds": ["g_st_judge"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_e2e", "x": 2530, "y": 1130, "width": 170, "height": 54, "strokeColor": "#1e3a5f", "backgroundColor": "#93c5fd", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9136, "version": 1, "versionNonce": 9137, "isDeleted": false, "groupIds": ["g_st_e2e"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_st_e2e", "x": 2584, "y": 1149, "width": 63, "height": 15, "text": "E2E-Loop", "originalText": "E2E-Loop", "fontSize": 12.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9138, "version": 1, "versionNonce": 9139, "isDeleted": false, "groupIds": ["g_st_e2e"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_st_e2e", "x": 2515, "y": 1196, "width": 200, "height": 30, "text": "nightly · Fehlschlag\nwird Story", "originalText": "nightly · Fehlschlag\nwird Story", "fontSize": 10, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9140, "version": 1, "versionNonce": 9141, "isDeleted": false, "groupIds": ["g_st_e2e"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "rectangle", "id": "st_drift", "x": 2800, "y": 1130, "width": 170, "height": 54, "strokeColor": "#6d28d9", "backgroundColor": "#ddd6fe", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "dashed", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9142, "version": 1, "versionNonce": 9143, "isDeleted": false, "groupIds": ["g_st_drift"], "boundElements": [], "link": null, "locked": false, "roundness": {"type": 3}}, {"type": "text", "id": "t_st_drift", "x": 2842, "y": 1149, "width": 86, "height": 15, "text": "Drift-Audit", "originalText": "Drift-Audit", "fontSize": 12.5, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#374151", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9144, "version": 1, "versionNonce": 9145, "isDeleted": false, "groupIds": ["g_st_drift"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "text", "id": "s_st_drift", "x": 2785, "y": 1196, "width": 200, "height": 15, "text": "Code↔Spec · meldet nur", "originalText": "Code↔Spec · meldet nur", "fontSize": 10, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#64748b", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9146, "version": 1, "versionNonce": 9147, "isDeleted": false, "groupIds": ["g_st_drift"], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "spur", "x": 2900, "y": 1040, "width": 2580, "height": 440, "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9148, "version": 1, "versionNonce": 9149, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [-2580, 0], [-2580, -400], [-2502, -440]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "text", "id": "l_spur", "x": 1050, "y": 1014, "width": 560, "height": 15, "text": "Rückführ-Spur: Fehlschläge & Funde werden neue Stories (mit Trace-ID)", "originalText": "Rückführ-Spur: Fehlschläge & Funde werden neue Stories (mit Trace-ID)", "fontSize": 11, "fontFamily": 3, "textAlign": "center", "verticalAlign": "top", "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 1, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9150, "version": 1, "versionNonce": 9151, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "containerId": null, "lineHeight": 1.3}, {"type": "arrow", "id": "feed_e2e", "x": 2615, "y": 1128, "width": 0, "height": 82, "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9152, "version": 1, "versionNonce": 9153, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [0, -82]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}, {"type": "arrow", "id": "feed_drift", "x": 2885, "y": 1128, "width": 0, "height": 82, "strokeColor": "#b45309", "backgroundColor": "transparent", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", "roughness": 0, "opacity": 100, "angle": 0, "seed": 9154, "version": 1, "versionNonce": 9155, "isDeleted": false, "groupIds": [], "boundElements": null, "link": null, "locked": false, "points": [[0, 0], [0, -82]], "startBinding": null, "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "roundness": null, "lastCommittedPoint": null}]} \ No newline at end of file From ab8ac98ef7eccf06d9d24a74f201b06016228dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Tue, 1 Sep 2026 12:56:47 +0200 Subject: [PATCH 113/117] docs(field-report): Plan C's closing figures and Plan C1's stopped curve Plan C's record stood at five passes with a note that its closing figures belonged in a later revision. It closed as not converged at seven: findings 18, 20, 20, 23, 22, 19, 29, with Blocker+Major never leaving the 15-21 band and both the highest total and the highest B+M falling on the last pass. Plan C1 is added as the fourth cycle. Three passes, stopped on the two-tell rule and never closed: findings 14, 12, 17 with Blocker+Major 8, 8, 10 -- the count rising and B+M failing to fall. Daniel's decision of 2026-09-01 is recorded beside it: do not resume, dissolve C1, review its payload at Gate B on the artifact. Every number was extracted mechanically from the findings files, per this file's own rule, and they match the Plan C closure record exactly. Two lessons recorded. The C1 slots carry no revision infix, so with four committed revisions and three recorded passes it is no longer recoverable which pass reviewed which revision -- a dispositions file must carry its revision. And C1 was itself the remedy for Plan C's non-convergence, so its rising curve says the cost is not carried by plan size: both artifacts are prose describing replacements of prose, which leaves a reviewer no decidable question, where Plan B's grammars could be checked against themselves. A plan made of prose about prose has now failed to converge under Gate A twice, at two very different sizes. --- .../2026-08-30-gate-a-rle-plan-cycles.md | 54 ++++++++++++++++--- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md index de30bb6..f88fa10 100644 --- a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md +++ b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md @@ -1,7 +1,7 @@ -# Gate-A plan cycles `rle` — A, B and C, the record preserved +# Gate-A plan cycles `rle` — A, B, C and C1, the record preserved Companion to `2026-08-29-gate-a-rle-cycle-evidence.md`, which holds the **spec** cycle. This file -holds the **three plan** cycles for the same change. Their validated findings files live under +holds the **four plan** cycles for the same change. Their validated findings files live under `.context/codex-reviews/`, which is gitignored and whose slots are reused, so this is the durable record — the same reason the spec file and `2026-08-26-fic2-cycle-evidence.md` exist. @@ -43,16 +43,38 @@ Blockers 2, 0, 0, 0, 0, 0, 0 Majors 7, 3, 4, 7, 5, 6, 0 ``` -**Plan C — rollout, packaging, evidence, the close.** Open at 5 passes when this file was written. -Its closing figures belong in a later revision of this file, and its absence from the list below -is a statement that the cycle had not closed, not that it closed at five. +**Plan C — rollout, packaging, evidence, the close.** 7 passes, **closed as not converged** +(Daniel, 2026-08-30). No clean pass was reached and none is claimed anywhere. The record is +`.context/codex-reviews/gate-a-plan-planc-CLOSURE.md`. ``` -Findings 18, 20, 20, 23, 22 -Blockers 5, 4, 2, 4, 5 -Majors 11, 13, 13, 16, 13 +Findings 18, 20, 20, 23, 22, 19, 29 +Blockers 5, 4, 2, 4, 5, 2, 2 +Majors 11, 13, 13, 16, 13, 16, 19 ``` +Blocker/Major never left the 15–21 band, and both the highest finding total and the highest +Blocker+Major of the cycle are **pass 7 — the last one**. + +**Plan C1 — the user-facing floor description.** The first carve-out of the split. 3 passes, +**stopped on the two-tell rule**, never closed. + +``` +Findings 14, 12, 17 +Blockers 0, 1, 0 +Majors 8, 7, 10 +``` + +The finding count rose and Blocker+Major rose (8, 8, 10) — two of §5's five tells, which makes +stop-and-surface mandatory rather than discretionary. **Daniel's decision, 2026-09-01: do not +resume the loop. C1 is dissolved and its payload is reviewed at Gate B, on the artifact.** + +One operational note the cycle paid for: the slots are +`gate-a-plan-planc1-pass-{1,2,3}.md`, with **no revision infix**. Four plan revisions were +committed (bb358c2, 476236b, 7e42947, 52192d1) and three passes recorded, and which pass ran +against which revision is not recoverable from the artifacts. A dispositions file must carry the +revision it reviewed; this record cannot reconstruct it. + ## What the three cost, and why they differ **Plan A's shape is the ordinary one**: Blockers exhausted in three passes, then a long tail of @@ -85,6 +107,22 @@ remedy was the same one the spec cycle recorded for restatements generally — * restatement, a deletion** — but deleting a restatement leaves a gap where the plan-specific facts were tangled up in it, and pass 5 is a list of those. +**C1 turns that into a second data point, and it generalizes further than the first.** C1 *was* +the remedy for Plan C's non-convergence: take one statement site out, make the plan small, give it +its own cycle. C1 is nine sentences in eight replacements across two files — about as small as a +plan of this kind gets — and its curve rose anyway. So the cost is not carried by the plan's +**size**. + +What both artifacts share is that they are prose describing replacements of prose, and a reviewer +reading one has no decidable question to answer. Plan B is the contrast that makes this visible: +its grammars can be checked against themselves, so a pass either finds an unreachable production +or it does not. A sentence-replacement list can only be checked against a fresh reading of two +other documents, and every pass brings a fresh reading. **A plan made of prose about prose has +now failed to converge under Gate A twice, at two very different sizes.** + +Where that evidence points is Gate B: the same eight replacements, read as a diff against the +files they changed, are a question with an answer. + ## The superseded single-plan artifact `docs/superpowers/plans/2026-08-29-review-loop-economics.md` is the single-plan version of this From b3bcb18041ff41a71335ce7a9315de536e8798c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Tue, 1 Sep 2026 17:04:21 +0200 Subject: [PATCH 114/117] feat(workflow): derive the pass floor from the cited story's profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Gate-A and Gate-B pass floor stops being a fixed 3 and becomes a function of the cited story's profile: max(risk, security), where level 0 gives a floor of 1 and every resolvable profile above that — and an artifact citing no story — gives 3. The hook's ratio becomes a reminder threshold that controls nothing. Finding severity turns on whether something in the system takes a different decision. Two pinned commit-body records ship with a cycle nonce and a slot-naming rule: a provenance line and a per-pass curve. Plans A (15 tasks), B (6) and C (tasks 1-18 and 21) in both prompt copies, plus the user-facing sentences they falsify. Manifest 0.10.0 -> 0.11.0. cycle none (pre-rule); floor 3 per {docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md (level 2)}; hook reminder threshold absent cycle none (pre-rule); Gate B (passes 1-5, codex): Findings 16,29,25,25,23. Blockers 4,15,6,5,10. Majors 5,2,9,10,6. Evidence entry — Story: docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md Battery: the full AGENTS.md § Commands chain green, with the version-bump checker given HEAD^ as its base ref — both hook suites under sh and dash, 148 + 36 assertions, invariant checks ok, claude plugin validate --strict passed. Re-run green after every round of this cycle. Check that fails without the change: four assertions over the pass-1-Minor sentence. The base carries "Blocker/Major-free pass 1 carrying a Minor" and not "a Blocker/Major-free pass below the floor"; HEAD carries the reverse. Assertions 3 and 4 fail against the base, and 1 and 2 establish that the wiring could produce that failure. Named verification of the risk path: the union of the three plans' Story headers is that one story; its profile gives max(risk, security) = high, level 2, floor 3. The provenance line above matches the pinned grammar and its floor is licensed by that level. The falsifying observation would be a line whose floor the cited profile does not license. CLOSED ON THE CLEARLY-STUCK EXIT, not on a clean pass. Daniel's decision, 2026-09-02, answering a surfaced mandatory stop with all three §5 conditions affirmed: - Plateau across passes: 16/9, 29/17 (discounted), 25/15, 25/15, 23/16 findings over Blocker+Major. B+M never returned to its pass-1 level and rose on the last pass. - Coverage affirmatively sufficient: across five passes the reviewers covered both prompt copies, the spec, the story, all three plans, the hook source and the user docs. No materially unreviewed area is known. - Blocker/Major regenerating across genuine repair attempts: each round's fix produced the next round's findings on the same mechanism. The regeneration history, which is why the exit was taken. One mechanism — what the hook does with the floor knob — was described wrongly four times running. Pass 1 invented a maximum the hook does not define and a trailing-newline rule it does not use. Pass 2 rewrote it from the source and still missed that the hook gates on -f before reading. Pass 3 was told to delete it; the walkthrough went and a one-sentence summary stayed, and the summary was false too: a file of 1, NUL, 2 is accepted as twelve in sh, dash and bash alike. Pass 5's cut removed the claim entirely — and found that the note explaining the deletion is itself a description of the hook. No version of that paragraph survives its own rule. docs/prompt-standards.md item 11 names this shape and prescribes deletion after a fourth correction; the field report carries the tested counter-example. Decisions recorded, all Daniel's: - 2026-09-01: C1 dissolved into the rollout after its own Gate A stopped on two tells; no third prose plan; plan-level Gate A skipped for the sentence replacements after two non-convergences, with verification moved to the artifact; execution ordered A -> B -> C. - 2026-09-02: Plan C Tasks 19 and 20 dropped — the deterministic slot discriminator is not shipped, and a general production is deferred to the loop-rule consolidation story. - 2026-09-02: this cycle's findings slots use the rle infix as a RECORDED plan-local naming exception under the old rules that govern it. No shipped rule admits the form. Reason: the cycle is pre-rule and cannot mint a nonce, and the bare family already held 30 files that delete-before-call would have destroyed. - 2026-09-02: the knob-cause vocabulary and the model-cause obligation are withdrawn from the grammar and from both prompt copies. Accepted capability cost, stated: the record says THAT a knob was unusable and no longer WHY. Whoever needs why reads the file and the hook. - 2026-09-02: this close. Pass 2 of this cycle is DISCOUNTED and not counted toward the floor. Both branch files were structurally valid, but the reply contradicted itself — each reviewer reported the other branch INCOMPLETE, mistaking its counterpart's legitimate file for a foreign write. The findings were acted on because they were provably complete rather than partial; the pass was not credited. A protocol note fixed the collision and it did not recur. WHAT NO PASS REVIEWED. The four closing repairs — the skip-record link, the two remaining model-failure descriptions in the spec and Plan B, the Task 23 vs Task 24 closing-body contradiction, and Plan C's half-done propagation of the dropped tasks — were made AFTER pass 5 and were not reviewed by any pass. The gate hook says so at this commit, and it is right. They are repairs of defects pass 5 itself named, they are small, and the battery is green over them; none of that is a review. A reader comparing this commit to the reviewed content should know the difference. Open findings and their dispositions: .context/codex-reviews/gate-b-rle-pass-5-dispositions.md — including the two that demand back what Daniel withdrew, marked as a chosen cost rather than a missed defect. --- CLAUDE.md | 501 ++++++++++++++++- README.md | 2 +- docs/coding-workflow.md | 15 +- docs/getting-started.md | 25 +- ...8-29-review-loop-economics-plan-a-rules.md | 8 + ...30-review-loop-economics-plan-b-records.md | 20 +- ...30-review-loop-economics-plan-c-rollout.md | 66 ++- ...2026-08-28-review-loop-economics-design.md | 53 +- ...-review-loop-economics-pass-floor-story.md | 8 +- .../dev-workflow/.claude-plugin/plugin.json | 2 +- plugins/dev-workflow/CHANGELOG.md | 41 ++ .../commands/process-pr-review.md | 24 +- .../dev-workflow/commands/workflow-init.md | 506 +++++++++++++++++- 13 files changed, 1150 insertions(+), 121 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index c78de2c..57476ef 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -69,16 +69,129 @@ yours — a non-blocking hook (shipped by the `dev-workflow` plugin) reminds you each. Opt out per-workspace with `.context/codex-gate.off` (delete to re-enable); the gates still apply. -**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major -only), counted by the hook.** The hook counts passes but can't read findings or -tell the spec run from the plan run (it resets at `writing-plans`), so Gate A — -the spec run especially — is instruction-backed: a satisfied count is not a clean -review. Open a TodoWrite "Codex pass N" per pass; fix Blocker/Major after each. Your -final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +**Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run +(Blocker/Major only), derived from the cited story's profile.** +The derivation is max(risk, security): a value of 0 gives a floor of 1; every +resolvable profile above that, and an artifact citing no story, gives 3. Two levels, +not three — `high` takes its rigor from lens sets and evidence mode, not from extra +passes. A cited story whose profile is present but unresolvable stops and surfaces +under the existing rule; it does not fall through to 3, because reading it as 3 would +turn a stop condition into a silent default. Across a cited set the floor is 1 if and +only if the set is non-empty and every member is profiled, resolvable and at level 0 +— all four conditions, since "every cited story" is vacuously true of an empty set; +no story cited, or any cited story unprofiled, gives 3. One derived value governs all +three cycle *kinds*: the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle. Not +because they are one cycle — they are separate cycles, and a change carrying several plans runs +a Gate-A plan cycle per plan — but because they derive from the same +cited-story set. That value is a function of the current confirmed profiles of that set, +read fresh wherever this section already requires them to be read, so wherever a value can be +derived at all there is exactly one, because there is one source. Some states derive **no** value +rather than a second one, and each stops rather than defaulting: governing headers that +disagree; a cited profile that is present but unresolvable; and a `Story:` header that cannot +be read. A change to a profile or to the set +therefore binds every open and future cycle — a raise costs an affected open cycle a +further pass under the current profile, as the profile-change rule below requires — while a +cycle that has already closed +stands, its close having been valid under the profile current when it closed, which is the +cycle-level form of passes already run keeping their count. **The set has one authority: +the artifact's `Story:` header, which carries the path of every cited story.** Nothing else +is a citation. A story path appearing anywhere else in an artifact's body — including a +sentence placing a story *outside* this change's scope — **contributes nothing to the cited +set and nothing to the floor derivation**, which is the only claim made about it; it may still +be a perfectly good cross-reference for any other purpose. And **an agent +deriving the set reads that header and does not grep the body for story paths**, because a +grep finds mentions and cannot tell a citation from a disclaimer. **Each cycle's governing header is the +header of the artifact it reviews**: the spec's for the Gate-A spec loop, the plan's for the +Gate-A plan loop, and — since a Gate-B cycle reviews a diff and has no header of its own — +**the union of the `Story:` headers of every plan contributing to that diff, which the Gate-B +call must carry in full**, as this section already requires of every cited path. **Every expected artifact contributes a set — a spec, and every plan contributing to the +reviewed diff — and an expected artifact whose `Story:` header is absent contributes the empty +set rather than dropping out of the comparison.** **One path per entry**, and "entry" is +decidable against the form these artifacts actually carry: a single line beginning `**Story:**`, +then one or more paths, **each wrapped in backticks**, separated by `, `. Trailing prose after +the last path is allowed and contributes nothing — several headers carry a reminder to read the +profile fresh, and a reminder is not a citation. So a header citing several stories carries several entries, +one path each. Exact duplicate paths are one member; entries naming different stories are +different members; and a header that does not parse as that line is malformed and stops, +reporting that as the cause rather than as a disagreement. **Before each pass the deriving agent +compares every such set, and again before a clean pass is accepted as the cycle's final pass.** +A header or profile that changed during that pass means the pass is not final — the same +answer a change gets at every other read point. Where they name different sets the premise of a single value has +failed: **stop and surface the disagreement** rather than deriving from either, exactly as an +unresolvable profile stops rather than defaulting. + +**The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder +threshold that controls nothing.** The hook still counts passes, and it still can't read +findings or tell the spec run from the plan run (it resets at `writing-plans`), so +Gate A — the spec run especially — is instruction-backed: a satisfied count is not a +clean review, and a below-threshold reminder is noted in the pass report and disregarded +where the cycle's own closure rules are satisfied. This replaces the pass-count number +and nothing else. Every other rule stated here about how a cycle closes stands as +written, and none of them is restated — a summary is where their conditions would get +dropped. Nothing here writes the floor knob: it stays the user's, never written, never +removed, never read for this derivation. Open a TodoWrite "Codex pass N" per pass; fix Blocker/Major after each. Your +final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until clean or clearly stuck → then STOP and surface to the user. The only early exit -below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is advisory — validate before applying; dismissed finding → one-line why. +**Named residual:** the hook's messages state its own threshold as an obligation, so at a +floor of 1 they report a shortfall the cycle does not owe. Hook text is out of scope here +by decision; what makes that tolerable is the precedence rule above plus the hook exiting +0 on every branch, not the reminder being harmless. + +**The gate-off surface — routes known today, not a complete list**, since an enumeration +read as complete guarantees the routes it omits. One route is created here: a stated floor +the cited set does not license, which could not exist before there was a derived floor to +state. Pre-existing and unchanged: omitting a higher-risk cited story; minting or editing a +profile to level 0; presenting an incomplete cited set; falsifying evidence entries; +silencing reminders; or not running a pass and reporting that it ran. A user-set floor is +not the lever — it moves what the hook says, not what the cycle owes. +None of this is a guard: the floor is produced by the agent and nothing checks it against +the cited profiles. + +**When these rules bind.** From the commit that ships them, and a cycle already running +finishes under the rules it started with. Where a cycle's starting rules cannot be +established it takes the stricter reading of every part this change touches — at minimum +floor 3, severity classified without the demotion, the provenance-line duty owed, the curve +duty owed, and the nonce duties at their strictest — the cycle is treated as post-rule, so it +owes a nonce, owes its provenance line and its curve or skip record, and uses that nonce in every +cycle record it does write — which changes what a record is named, never whether one is owed, so +the working record stays optional and a skipped cycle still writes no findings slots. Where it +cannot recover a nonce it starts a new cycle rather than claiming `none (pre-rule)`, that reserved +field being unavailable to a cycle whose start cannot be established. Each further rule this change ships adds its own strict +reading to this list. Not a re-derivation, which could hand a level-0 +cycle a floor of 1 and skip passes on the strength of not knowing when it started. A user +knob set above 3 is not lowered by this fallback. A revert is itself a shipping commit for +the old rules, and the activation rule wins wherever the start is determinable; the +fallback covers only where it is not. + +**Downstream has no shipping commit.** Adoption binds from the `/workflow-init` run that +actually writes the text — which may write nothing, be declined, or be merged in part — +so these rules bind only over the text a project's `CLAUDE.md` actually contains, and a +partial adoption can persist undetected. A project taking the floor rule without the +severity test gets a floor whose docs-only question the severity test is what settles. +**A partial adoption can leave a project's floor undefined or self-contradictory.** The rule +is a coherence requirement, stated semantically rather than as a list of spellings, and it +runs in **both** directions: **exactly one definition of the floor must be present, and every +statement that defines or constrains the floor, or makes closing depend on it, must resolve to +that one definition.** **The unknown-start fallback is not a second definition**: it is +explicitly conditional on a cycle's starting rules being undeterminable and governs only that +state, so it coexists with the predicate rather than competing with it. Everything else +likewise keeps its own footing and is **not** required to derive from the floor: **the other closure and stop predicates** — assigned-fix-set membership, a new +structural question, an accepted Blocker or Major, the tell thresholds; **independent reporting +and diagnostic ordinals**, such as a duty owed from a given pass onward; and **the hook's +reminder threshold together with any descriptive or historical pass number**, which say what a +tool reports or what once happened rather than what a cycle owes. Four states break it, and the list is **not exhaustive**: a fixed-number or +specific-pass obligation surviving beside the derived predicate; a claim or dependency on a +derived floor with no predicate to define it; **no definition at all**; and **two definitions +at once**. A +merge can produce any of them: the Gate-A loop description, the pass-1 closure rule and the +re-review rationale each carry a fixed-three claim and can be taken or left independently of +the predicate itself. In any such state nothing here resolves which rule governs: **stop, and +have a human complete or revert the adoption, before running a gate under it.** What prompt text can do about downstream +adoption is limited, and that limit is what this paragraph states. + **What a loop absorbs, and what stops it — a question of scope, not of action.** A finding that corrects the correction you just made **and stays inside the assigned fix set** is **inside this loop's scope**: keep it here rather than handing it back, then act on it by its @@ -123,13 +236,22 @@ finish, and it is why **a clean completion takes precedence over this exit**: a Blocker/Major-free pass **at or above the floor** has satisfied the clean-final-pass rule — collect the Minors and Nits and close — and reporting "will not converge" on a converged loop is a false report. **Below the floor nothing closes**, and a zero-finding pass remains -the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps +the only exception, exactly as above; a Blocker/Major-free pass below the floor +carrying a Minor keeps looping. **Surfacing does not close the cycle, and that is what makes this reachable.** You surface *with the finding still open* — the resolve rule is not waived, no pass is credited as clean, and the loop resumes on whatever the user decides. Reading it as "stop instead of fixing" would put the exit in competition with the rule that every Blocker and Major resolves, and then nothing could satisfy both. +**Every pass report states three things about the floor**, from pass 1 onward: the +derived floor, the risk and security values read, and the cited stories they were read +from. A report giving the number alone leaves a reader unable to check the derivation +while passes are still being spent — which is the only time checking it is cheap. Where +no story is cited, or a cited story is unprofiled, the report says so in place of axis +values; a multi-story set names each story and its values. This is owed by every pass; +the three lines below are owed from pass 4 and are a different obligation. + **From pass 4 onward every pass report carries three lines.** The carrier is **your own status report to the user** — never the Codex reply, which stays exactly one line per branch, and never the findings file, which admits no line that is not a finding or the terminator. @@ -177,7 +299,26 @@ Append to the gate prompt: > `.context/codex-reviews/.md` (create the directory if needed; the path is > relative to that root — Codex resolves writes against its working directory, so > without this a valid file can land in a different checkout). `` is -> `gate-a-spec-pass-

`, `gate-a-plan-pass-

`, or `gate-b--pass-

`. +> `gate-a-spec-pass-

`, `gate-a-plan-pass-

`, or `gate-b--pass-

` for a +> cycle with no nonce; a cycle that has one writes `gate-a-spec--pass-

`, +> `gate-a-plan--pass-

` or `gate-b---pass-

` instead, and uses +> the nonce in every slot more than one cycle could write. The bare names are reserved for the +> legacy single-cycle case they already serve. **Distinct-nonce paths coexist by construction and +> are never in conflict** — a sibling cycle's slot is simply a different file. +> +> **The rule binds the deletion step, which is where the damage is done.** This section already +> requires every target to be deleted and confirmed gone before a call. A cycle holding a nonce +> **deletes only paths carrying its own nonce**; it never deletes a bare path or one carrying a +> different nonce, and an attempt to do either **stops and names the path** instead of removing +> it. That is reachable and observable: the step operates on a path it computed, and the check is +> whether that path is the cycle's own. **The case it exists for is a nonce-holding cycle +> computing a bare path** — the legacy spelling — **and deleting a file that belongs to somebody +> else**, which is exactly what happened once. **Two cycles that drew the same nonce compute the +> same paths and are indistinguishable to this rule**; what makes that unlikely is the width of +> the draw, not this rule — this section already stops on a +> target that survives deletion, and this extends that to a target that must not be deleted at +> all. That rule exists because a bare slot was in fact overwritten once, destroying a previous +> cycle's findings file. > > One finding per line in the format above; escape a literal pipe inside a field as > `\|`. @@ -223,6 +364,114 @@ terminator remain the only hard requirement, and a zero-finding pass needs no co Whoever runs the cycle writes it when useful, replaces it as the cycle moves, and deletes it once the cycle closes. Nothing depends on it existing. +**The cycle nonce.** Both shipped records below carry a **cycle field**, because a record that +cannot be attributed to a cycle cannot be told apart from another cycle's when several are read +together. That is a limitation rather than a disqualification — a human reading one cycle's +records knows which cycle they came from; what attribution buys is that a *later* reader +**usually** does not have to. Usually, not always: the guarantee is probabilistic, for the two +reasons stated at the end of this block. This section defines three **kinds** of cycle — the Gate-A spec loop, the Gate-A +plan loop and the Gate-B cycle — and **one cycle field is produced per cycle run, not per +kind**: a change carrying several plans runs a Gate-A plan cycle for each, and each of those is +its own cycle with its own nonce. + +Generated once at cycle start, immutable, and collision-resistant operationally: **8 to 16 +characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — 8 being where +collision resistance starts and 16 where the field stops being a usable infix. **Never derived +from a name, a timestamp or a commit**, each of which collides exactly where sibling cycles do, +which is the one thing the nonce exists to prevent. The character set keeps it safe as a slot +infix and a path component. + +**It appears in every record the cycle writes** — which keeps records apart **as far as distinct +nonces allow**, and no further — **and that set is named rather than left open**: +the provenance line, the per-pass curve (including a skip record standing in for one), the +cycle's findings slots, and its advisory working record. **The working record is a cycle record +too**: a cycle holding a nonce names it `gate-a-spec--resume.md`, +`gate-a-plan--resume.md` or `gate-b--resume.md`, and the bare names above stay +reserved for the legacy single-cycle case, exactly as the findings slots do. **Because recovery +scopes candidates by artifact as well as by kind, the record's contents name that artifact**, and +what counts as the artifact depends on the cycle kind: for a Gate-A cycle it is the reviewed +document's path, quoted by the same rule the provenance line uses where quoting is needed; for a +Gate-B cycle, which reviews a diff rather than a file, it is the **base commit's full +40-character hex object name**, the same value the cycle's reviews are run against. The filename +carries kind and nonce; the artifact key lives inside, where neither a path nor a hex name has to +survive a filename. The nonce is not +required in records this change neither introduces nor keys to a cycle — the evidence entry and +a human-exception record among them. + +**A nonce is a candidate for recovery only if** it is keyed to this cycle's kind — Gate-A spec, +Gate-A plan, or Gate B — **and** this cycle's artifact, **and** that cycle is still open. **Those +three are necessary and not sufficient, and the difference matters**: two Gate-A cycles can review +the same document and two Gate-B cycles commonly share a base commit, so a sole match on kind and +artifact is **not** identity. **A candidate is adopted only if it is positively linked to this +run** — the working record this run itself wrote. A match that is merely consistent is treated as +no identity, and the cycle starts fresh; adopting a sibling on a shared key would merge two +cycles under one nonce, which is the failure this rule exists to prevent. +History normally holds many closed cycles' nonces and they are not candidates; a working record +left by a closed cycle is not one either, which is why that record is **retired at closure** +rather than left to be found later. **Recovery has two sources, and they answer different questions.** The **working record** is the +source while the cycle runs, and it is the one the candidate rules above apply to — several files +may be present and the run must decide which, if any, is its own. **History is the source once +the cycle's own commit exists**, and there is no search there: the cycle is reading **its own +commit body**, so kind and artifact are settled by which commit is being read, and the nonce is +taken from the provenance line and the curve, which must agree. A Gate-A cycle mid-run has no +such commit and therefore has only the working record. Recovering a single candidate from +**either** keeps identity **as far as the field can distinguish cycles** — two cycles sharing a +nonce are one cycle to it. **No candidate, +disagreeing sources, or more than one candidate → no identity: start a new cycle**, which costs +passes rather than letting one cycle's records read as another's — again, as far as distinct +nonces allow. **Starting a new cycle does +not close, adopt or retire the cycles those candidates belong to** — they stay open, keep their +own nonces, and are a human's to resolve; the new cycle simply does not claim them. + +**A cycle does not start without a valid nonce, unique among the cycles open when it was +generated.** That is the requirement. **What the check can establish is narrower** — it compares +against the cycles it can observe — and the gap between the two is the residual set out below. +Where generation fails, make **at most three attempts in total**, then stop and +surface, **naming which of the three causes occurred**; each has its own check and its own fix, +and one token would name a symptom rather than a cause: + +The three are distinguished by **where** the attempt stopped, so they cannot both apply: the +source failed to produce bytes; or it produced bytes that are not a well-formed nonce; or it +produced a well-formed nonce that is already in use. An empty result is the first, never the +second. + +- **randomness unavailable** — the source errors or produces no bytes. *Fix:* retry, since the + condition can be transient; if it persists across the attempts, make a source available or run + where one is, which is a change to the environment rather than another draw. +- **an invalid value** — the drawn value is not 8 to 16 characters from `[a-z0-9]`. *Fix:* + redraw. Repeated invalid output points at the generator rather than at luck, and the report + says which. +- **a collision with a known-open cycle** — the value equals a nonce on a cycle still open. + *Fix:* redraw. A second collision at this width is possible but unlikely enough to be worth + reporting as a possible source defect, which the report states as a suspicion rather than a + finding. + +**Report every distinct cause observed across the attempts, in the order they occurred** — the +attempts can fail for different reasons, and naming only the last would describe the tail of the +sequence rather than what happened. + +**No deterministic fallback.** + +**Residuals, disclosed rather than guarded, and this list is not exhaustive.** The check compares +against cycles *known to be open*, so a nonce can repeat one belonging to a cycle nobody can see; +two cycles starting at the same moment can each check before either has published, so neither +observes the other; and the check deliberately ignores **closed** cycles, so a new cycle can +redraw a closed one's value and then write to its surviving findings slots and working record. +**What makes both unlikely is the width of the draw, not the check** — and unlikely is the +honest word. Neither is a guard. + +**What follows from that, said here rather than left to be discovered.** The nonce is +collision-**resistant**, not collision-**proof**, so everything built on it inherits that bound: +two cycles sharing a nonce write to the same slots and are not refused, their records read as +one cycle's, and a later reader cannot separate them. Attribution is therefore a strong default +rather than a guarantee, and any reading of these records that would be wrong if two cycles +shared a field should say so rather than assume they did not. + +**A cycle that began before these rules shipped has no nonce and cannot acquire one.** Its +records carry the reserved `cycle none (pre-rule)` field and are, by construction, not +cycle-attributable. That exception is bounded and self-terminating: it reaches only cycles +already running when the rules land, and no later cycle can enter the state. + (Field practice, infinite-portfolio-canvas: 7 dispositions files and a Gate-A resume note had been invented per-session there before the protocol knew about them.) @@ -233,7 +482,7 @@ the file" would accept a truncated file padded with fragments); and, for a `full pass, both branch files satisfy all of that. Anything else — missing, unreadable or empty file, wrong path, malformed terminator, count mismatch, extra lines, one branch file, an `INCOMPLETE` reply — is an **INCOMPLETE pass**, which is not a review: don't -act on the partial list, don't count it toward the 3-pass floor, and don't read "no +act on the partial list, don't count it toward the floor, and don't read "no Blocker/Major visible" as clean. **Reader:** the severity field is taken by splitting the line on **unescaped** pipes and @@ -297,7 +546,7 @@ number of lines, nor a stale file if you skip the delete. invalidate the review they document. Ignore `/.context/codex-reviews/` specifically — not all of `.context/`, which would strip the committed `codex-gate.on` adoption marker. -- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the +- **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the **spec** right after brainstorming (before `writing-plans`), then on the **plan** before `executing-plans`/`subagent-driven-development` — catching a spec flaw before it's baked into the plan. Tool: `mcp__codex__exec` (raw; @@ -331,8 +580,8 @@ not all of `.context/`, which would strip the committed `codex-gate.on` adoption - **Gate B — Code.** Tests green, before `git commit`. Tool: `mcp__codex__review` (args `instruction`, `whatWasImplemented`, `baseSha`; `reviewType: full` runs spec + quality in parallel). Skip ONLY trivial changes. Check against - @AGENTS.md. Re-review after every fix — a fix changes the diff and the hook - invalidates the prior pass, which is where the 3 come from. + @AGENTS.md. Re-review after every fix — a fix changes the artifact, so the prior + review no longer covers it. The hook merely notices, at commit time. **A fix that changes specified behaviour updates the spec in the same commit.** If a Gate-B fix alters something the approved spec pins down — an ordering, a terminal @@ -400,16 +649,31 @@ the reviewer, the other obliges the author. it; risk's *abuse* and security's *abuse paths* are **one lens carrying both labels**, not two questions. -Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major -filter, the file-first findings protocol and the clean-final-pass rule are unchanged. +Lenses are **different questions, not more passes** — they change what a pass asks, never +how many a cycle owes. The Blocker/Major filter, the file-first findings protocol and the +clean-final-pass rule are unchanged. The floor is not among them: it is no longer a fixed +number but derives from the profile and the cited set. -**Reading the profile — three cases, three answers:** +**Reading the profile — five cases, five answers:** +0. **The ordinary case**: every cited story is readable and its profile resolves → derive the + floor from it and run. Stated first because a partition of failures alone is not a + partition, and an earlier revision of this list omitted it. 1. The artifact **cites no story** → run unprofiled and **say so** in the pass. Artifacts predating this rule are the common case; stopping on them would halt in-flight work. 2. The cited story has **no profile line** → same: today's behaviour. -3. A profile is **present but unresolvable** → **stop and surface the cause**. That covers - the syntactic failures — unparseable line, a value outside the enums, two profile - blocks, a citation resolving to nothing — **and the semantic ones**: a `**Validation:**` +3. The cited path **does not yield a readable story file** → **stop and surface which of + these it was**, because each has a different fix: the path does not exist (a typo, or a + file moved or deleted); it exists but is not a regular file, a directory being the common + case; it is a symlink that does not resolve; or it exists and is a regular file but cannot + be read for permissions. **Report what you observed; no test order is prescribed here**, + because the obvious one is wrong — an ordinary existence or regular-file test follows a + symlink, so a dangling link reads as absent rather than as a broken link. + This case exists because none of the answers above is available to an agent that never + obtained the file: it can establish neither that a profile is absent nor that one is + present but unresolvable. +4. The story **is readable** and a profile is **present but unresolvable** → **stop and + surface the cause**. That covers the syntactic failures — unparseable line, a value + outside the enums, two profile blocks — **and the semantic ones**: a `**Validation:**` value disagreeing with `max(risk, security)`, or `+abuse-path` present without security `high` or absent with it. Only the **latest `mode override`** in the log, moving in a direction compatible with the current value, can explain such a mismatch — and if the @@ -429,8 +693,9 @@ trivial** (the pre-existing judgement, unchanged by profiles), **and** for a pro profile *changes*, and a skip changes no profile value. **A skip removes the review, never the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the battery and lands its evidence entry beside the reason; a skipped **unprofiled** story -records the reason and the battery result and nothing more, because it owes no mode-derived -entry and keeps exactly today's judgement-based skip. +records the reason and the battery result, because it owes no mode-derived entry and keeps +exactly today's judgement-based skip. **Neither is excused the records every cycle owes** — +the provenance line, and a skip record in place of the curve. **A cycle citing several stories** aggregates along separate dimensions, never through one winning mode: the **battery runs once** for the cycle; **each cited _profiled_ story @@ -487,6 +752,29 @@ the current profile. Inside an active Gate-B cycle, fold the edit into the activ snapshot by amend — a non-`WIP` commit reads to the hook as the cycle closing and would discard the accumulated passes. +**While a gate is running, the floor derives from the current profile at each pass.** +Passes already run keep counting; closing requires the floor as currently derived. These +are pass-count rules, so they apply while a gate is running and are silent otherwise — +what governs when a gate runs is unchanged and deliberately not summarised here. + +**Any profile change costs at least one further pass**, in either direction and whether or +not the floor number moves, because the final clean pass must run under the current +profile — so no already-banked pass can be it. That further pass must itself be clean and +every other closure duty must be satisfied; it is one more pass, not a licence to close on +the next one. What a lowering drops is whatever the changed values drop, not a fixed pair: +a mode-only override changes the evidence obligations while leaving the axis-derived lens +sets alone, and security `high` → `standard` keeps the security lens set while changing +what evidence is owed. Every derived obligation is recomputed from the current profile. + +**The cited set is re-read at each pass, and the final clean pass runs against the current +set** — whenever its membership changes, not only when the floor number moves. Adding a +high-risk story to a set already at floor 3 leaves the number alone while adding that +story's lens set, its evidence obligations and its review scope; a pass run before it +joined did not cover them. Removing a story recomputes obligations from the current set +and so does remove that story's lenses and evidence duty — but it never discharges an +accepted in-set Blocker or Major: the acceptance put that finding in the fix set, not the +citation. + **What this does not do:** nothing checks which file a model actually read, whether the header changed mid-call, or whether the lens sets were appended. This is instruction-backed like the rest of §5; the detection is a reader comparing the pass against the story. @@ -494,10 +782,43 @@ like the rest of §5; the detection is a reader comparing the pass against the s ### Mechanics (reference) - **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → rework) → both must resolve. Minor · Nit → collect, never iterate. + + **Deciding severity — one procedure. The subject list is illustration, not a second + rule.** Name what in the system consumes this text — whatever *acts* on it — and the + decision that act takes differently if the text is wrong. Both are required. If you + cannot name both, the finding is Minor or below: collect, never iterate. + + The exclusions are contract, not commentary. The reader must consume the text in the + system's *operation*, not in reviewing it — the review pass raising the finding is not + an in-system reader of the text it reviews; without this the test demotes nothing. + Gates remain legitimate readers of rule text they will later apply. A human reader never + satisfies the test — the prose exemption already prices that cost as non-gating. The + list of reader kinds is illustrative, not closed, because this ships into projects whose + readers we have never seen. The test sets a ceiling, not a floor, and never chooses + between Blocker and Major — the four definitions above still decide that. The instrument + carve-out is symmetric: an instrument finding keeps its severity whenever it shows the + instrument changes what a gate concludes about product behaviour — a false green, and + equally a false red or a check blocking a valid change. Rationale prose is Minor only + when no rule's application depends on it, not categorically: `docs/prompt-standards.md` + requires rules to carry their why, so rationale a reader must consult to apply a rule + passes the test. This removes arbitrariness, not judgement. Coverage-first is unchanged + — the reviewer reports every finding with severity and confidence; the filter is ours. + + This is the finding-level analog of the path-level prose exemption: one principle at two + granularities — text that *describes* the product versus text that *is* the product. + + **How this demotion bears on the loop-health measures — the per-pass counts, the finding + clusters and the stop thresholds — is not settled here, and this change does not settle it. + Until it is, a pass whose outcome would turn on that question reports the question and + stops rather than deciding it** — the same answer any unresolved gate question gets. + That question is owned by the loop-rule consolidation work in + `docs/superpowers/stories/2026-08-29-loop-rule-consolidation-story.md`. - **Tool routing:** docs (spec/plan, incl. code snippets) → `mcp__codex__exec`; implemented diff → `mcp__codex__review`. Never `review` a doc — it reads the git range, not the text. -- **`baseSha`:** against main = merge-base with main (`headSha` = HEAD); +- **`baseSha`:** against main = merge-base with main (`headSha` = the full 40-character + object name `HEAD` resolves to at that moment, never the symbolic `HEAD` — see the + branch-agreement rule below for why); pre-commit, `baseSha` = HEAD is an empty range (HEAD..HEAD) — make a WIP commit and set `baseSha` to its parent. **Name that commit `WIP: …`** — the hook treats a `wip`-prefixed commit message as cycle-internal, so it neither fires a Gate-B STOP @@ -516,7 +837,141 @@ like the rest of §5; the detection is a reader comparing the pass against the s destroyed exactly when the cycle closes. The final commit body is the durable record; a PR shows commit messages, so there is no second home to keep in sync. - **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** + **Every cycle records one provenance line in its closing commit body** — default floor or + not, so an absent line is never ambiguous between "the default applied" and "someone forgot". + **One line per cycle**, so a change running five cycles records five. There is no informal + variant; anything quoting this form elsewhere quotes an instance of it, because the deferred + metrics work is intended to parse it — that consumer does not exist yet, and the form is pinned + now so that it can. + + ; floor per ; hook reminder threshold + + := "cycle " | "cycle none (pre-rule)" + := [a-z0-9]{8,16} + := [1-9][0-9]* + := "none" | "{" ("," )* "}" + each appears at most once; a repeated path, + with or without conflicting levels, is malformed + := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" + := | + := [A-Za-z0-9._/-]+ contains no delimiter, quote or whitespace + := a double-quoted string, non-empty, whose only escapes are \" and \\ ; + a path containing a newline or other control + character is NOT representable — the cycle stops + and surfaces rather than emitting one + := "absent" | [1-9][0-9]* | "unusable" + + A filled instance, so the form is shown and not only described: + + cycle none (pre-rule); floor 3 per {docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md (level 2)}; hook reminder threshold absent + + It carries that cycle's **cycle field** — the nonce for any cycle started after these rules + ship, `none (pre-rule)` only for one that began before them — the **derived floor**, and **the + cited set that produced it**, each member with its level as a numeral. One floor and one set, + not an entry per story, since unanimity makes the floor a property of the set. It + distinguishes **a cited story with no profile** from **no story cited**. It records the + **workspace knob whenever the file exists**: the value if the observer read one, otherwise + `unusable`. **Nothing here describes what the hook does with that file, and the record does not + say why a value was unusable** — four successive attempts to state either were each wrong in a + different way, the last of them demonstrably so, and the rule for a claim needing a fourth + correction is to delete it. Whoever needs to know why reads the file and the hook. + + **These records are one contract, and a partial adoption breaks it.** The nonce, the slot + naming, the provenance line, the curve, this carry rule **and the unknown-start activation + semantics that say what a cycle owes when its starting rules cannot be established** depend on + one another, and the requirement is that the adopted definitions **agree**, not merely that all + of them are present: a curve + without a cycle field cannot be attributed, a slot rule without a nonce has nothing to key on, + and a carry rule naming records a project does not produce is inert. **A project whose text + carries some of them and not others, or carries all of them in versions that disagree, stops + and has a human complete, revert or reconcile the adoption before running a gate under it** — + disagreement is the harder case and gets the same stop, because a project holding two + definitions of a record has no single answer to what it owes — the same answer, and for the same reason, as a partial + adoption of the floor rule. + + **On squash-merge, copy every evidence entry, every human-exception record, the provenance lines, the curves and any skipped cycle's skip record TOGETHER WITH THE SKIP REASON IT POINTS AT in the squash range into the squash body — a skip record carried without its reason is a pointer into a body the squash has made unreachable — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** + + **Every cycle records its own per-pass curve in its own commit body.** Gate B alone would + leave the dominant cost unrecorded — the loops this rule was built from are Gate-A loops. + + ; (passes , ): Findings . Blockers . Majors . + + := "Gate-A spec" | "Gate-A plan" | "Gate B" + := ("," )* strictly ascending, non-overlapping + :=

|

"-"

+

:= [1-9][0-9]* + := ("," )* exactly as many entries as enumerates + := 0 | [1-9][0-9]* | "?" "?" = the count is unrecoverable for that pass + := | ("; " )* + := "pass "

" " ("+" )* + := | | "undetermined" + "undetermined" means the model could not be determined; + a real model so named is written as + := [!-~]{1,} minus ; : , ( ) + " and space, and not the + literal "undetermined", which is reserved + printable ASCII only; a control character makes the + identifier unrepresentable, handled below + := a non-empty double-quoted string, same two escapes as ; + an identifier that cannot be determined, or cannot be + represented, is written `undetermined`, and the raw + value is NOT reproduced anywhere in the body, since a + commit message cannot safely carry one (NUL cannot + appear at all). The record does not say why a pass + reached `undetermined`, and nothing here describes how + a model identifier fails — same rule, same reason as + the knob above + + Two filled instances, one ordinary and one with a split logical pass: + + cycle none (pre-rule); Gate B (passes 1-3, codex): Findings 16,29,4. Blockers 4,15,0. Majors 5,2,1. + cycle 7b2q9xk4; Gate-A spec (passes 1,2, pass 1 codex+claude; pass 2 codex): Findings 5,0. Blockers 1,0. Majors 2,0. + + A skipped cycle writes `; : skipped (see skip reason)` and no counts. **The skip + reason it points at is the text immediately following it in the same commit body** — + adjacency is the link. The cycle field is not: every pre-rule cycle writes + `cycle none (pre-rule)`, so it identifies nothing when a body carries more than one. + **`` keys must be exactly the passes `` expands to, each once, ascending** — a + list that omits or repeats a pass is malformed, not partially informative — and **every model + contributing to a split logical pass is listed**, joined by `+`, since recording one of two is + the same loss as recording none. + + **Majors are recorded as well as Findings and Blockers**, because the severity rule moves the + Blocker/Major line rather than the total, so totals and Blockers alone could not show even a + change in the mix. **Subject categories are deliberately not recorded** — they are a judgement + per finding rather than a count, and the findings files carry the material. + + **One entry per valid pass**, and since incomplete passes are excluded while still consuming + pass numbers, the record **states which pass numbers it covers**. A valid zero-finding pass is + recorded as zero, never omitted. **A count that cannot be recovered is written `?`, never + guessed and never written as `0`** — a cycle keeps its identity through the nonce rather than + through its pass files, as far as distinct nonces allow, so a resumed cycle may know a pass happened and not what it found, and + zero and unknown are different facts. **`?` is per series**: a pass whose Findings are unknown + may still have usable Blocker and Major counts, and a reader excludes the unknown value from + the comparisons that read that series while keeping the pass's other series. + + A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are + **branches of one logical pass** contributing one summed entry — **the curve counts logical + passes; the hook counts calls**, and where they differ the body says so **as prose beside the + curve**: neither grammar has a field for a call count, deliberately, since the count is a + property of how the pass was invoked rather than of what it found. **Both branches must be + issued against the same commit**, and that — not what they read — is what this rule + establishes. The result reports no reviewed revision, so there is nothing to read back and no + way to confirm from the reply what either branch actually looked at. What is available is the + request: **resolve `HEAD` to its full 40-character object name before each call and pass that + explicit value as `headSha`**, never the symbolic `HEAD`, which two calls can resolve + differently if a `WIP:` amend lands between them. Keep the value you passed **with that + branch's result**, and require the two kept values to be **exactly equal** before summing the + branches. Equal values mean the two calls were aimed at one commit; they are not evidence that + either branch reviewed it, and nothing available here would be. Record it as the **full 40-character hex object name**, since abbreviations are + ambiguous across repositories and across time; if it changed between them they are not one + pass, the completed branch is recorded as incomplete and excluded, and the later branch begins + a new one. Ending the pass is the conservative direction; merging two revisions would produce + one entry describing two different artifacts. + + **What the curve is worth, stated rather than implied.** Durable **across** cycles; **not + within** a running one, since the commit does not exist until the cycle closes. And + **author-written and unchecked** — nothing compares it against the validated pass files, so + whatever reads it reads a self-reported curve and must not present it as measurement. **Recording a human exception.** Where a human decides that something **no applicable rule required** was nonetheless worth skipping — an optional check this environment cannot run, a diff --git a/README.md b/README.md index 5e8dc14..f4e3103 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ plugin is installed once per machine; every other repo you open hears nothing fr Per-workspace knobs, all files under `.context/`: -| `codex-gate.floor` | a positive integer; moves the 3-passes-per-gate floor. | +| `codex-gate.floor` | a positive integer; moves the hook's reminder threshold. It does not change the floor §5 obliges, which is derived from the cited story's profile. | | `codex-gate.off` | silences the reminders; classification and state tracking keep running, so re-enabling lands on counters carrying the same semantics as gate-on — which is not the same as evidence that a review happened. | | `codex-gate.tools` | `execTool=` and/or `reviewTool=` — counts a Codex server whose tools aren't named `exec`/`review`, and only worth it if that server really does separate text-review from diff-review; aiming both gates at one general-purpose tool moves the counters while neither gate means what it says. Each mapped name must itself lie in `mcp__codex__*`: the hook's `hooks.json` matcher is `^(Bash\|Skill\|mcp__codex__.*)$`, so an out-of-namespace name is either never delivered (the mapping looks applied and does nothing) or, for the reserved names `Bash`/`Skill`, hijacks a lifecycle event; the hook refuses both — register the server as `codex` to place its tools there. Unparseable, out-of-namespace and reserved (`Bash`/`Skill`) lines are ignored, and the gate keeps its default `exec`/`review` name. A typo **inside** the namespace — `mcp__codex__exce` — is still honoured: the hook does not check that a mapped tool exists, so the gate now counts that name and nothing else. Whether it ever counts depends on whether a tool by that name is actually invoked; for a typo, normally never. | diff --git a/docs/coding-workflow.md b/docs/coding-workflow.md index c3b520c..ee3d018 100644 --- a/docs/coding-workflow.md +++ b/docs/coding-workflow.md @@ -76,8 +76,9 @@ story that captures *what* and defers *how*: the problem, the desired outcome, t acceptance criteria, which core invariants the change touches, the open questions, a rough size, and a **profile** — risk and security relevance, confirmed by the human, with a validation mode derived from the two. The two axes **add** review lenses at the -gates for a risky or security-relevant change (they never subtract any: Gate A's floor and -the baseline questions are the same at every level), while the derived mode calibrates +gates for a risky or security-relevant change (they never subtract a baseline question; the +floor itself derives from the profile, so it is not the same at every level), while the derived +mode calibrates what evidence the author owes before Gate B. The design ("how") is deliberately left out — it belongs to the next stage. The value here is a shared, reviewable definition of done before anyone argues about approach. @@ -127,8 +128,9 @@ prior review. Trivial changes may skip it, on terms that depend on the story: an unprofiled one keeps the judgement call, while a profiled one qualifies only at effective level 0 — trivial risk *and* no security relevance — so a trivial-looking change on security-relevant surface is not eligible. A skip removes the review, never -the evidence: the battery still runs, the reason is recorded in the commit body, and -so is one evidence entry per cited profiled story. **Explanatory** +the evidence: the battery still runs, and the commit body carries the reason, the battery +result, the cycle's provenance line, a skip record in place of the curve, and one evidence +entry per cited profiled story. **Explanatory** documentation carries no gate at all — a wrong sentence there costs a confused reader rather than broken behaviour. Prompt artifacts are not explanatory prose: in a project whose product is prompts, the text *is* the behaviour, so the review policy requires Gate @@ -276,8 +278,9 @@ top-level field alone is the wrong answer precisely where the override documente use, since `CODEX_DEV_REVIEW_MODEL` is stored at `tools.review.model`. If neither level names a model the probe establishes nothing — the CLI then picks its own default, and the only honest record is to set an explicit model or record the model as undetermined. Record the result beside -the finding count in the pass record: the commit body's evidence entry, or the slot's -dispositions file. This is +the finding count in **the cycle's per-pass curve**, which pins a field for it — not the +evidence entry and not the dispositions file, neither of which is keyed to a pass. The health +probe above is how the value is established; the curve is where it goes. This is bookkeeping, not enforcement: nothing checks it, and a wrong entry looks exactly like a right one. diff --git a/docs/getting-started.md b/docs/getting-started.md index 0ec38c9..e5456e1 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -31,17 +31,17 @@ settled decisions with rationale, not a wish list. **3. Gate A on the spec.** Claude sends the spec text to Codex (`mcp__codex__exec`) — a different model family, so it doesn't share Claude's blind spots. Blocker/Major findings get fixed, the review reruns on the revised spec: -three passes minimum, final pass clean — the one early exit is a pass that comes -back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` are +the floor its profile derives, final pass clean — the one early exit is a pass that +comes back with zero findings. Hook messages like `⚠ Codex Gate A below floor (1/3)` are the counter, not an error. Your job: arbitrate disputed findings — Codex is advisory, and a dismissed finding needs a one-line reason. **4. Plan, and Gate A again.** `superpowers:writing-plans` turns the spec into a -task-by-task plan (each task starts with a failing test); the same 3-pass loop runs +task-by-task plan (each task starts with a failing test); the same loop runs at the derived floor on the plan. A flaw caught here never reaches code. **5. Implement.** `superpowers:executing-plans` works through the plan, test-first, -progress claims backed by test runs. If the Gate-A floor wasn't met, the hook says +progress claims backed by test runs. If the hook's own threshold wasn't met, it says so right when execution starts. **6. Quality battery.** The one command you wired at init (typecheck + lint + dead @@ -50,12 +50,14 @@ skipping locally only postpones the red. **7. Gate B on the diff.** Claude makes a `WIP:`-prefixed commit (gives Codex a range to read; the hook knows WIP doesn't end the cycle), then loops -`mcp__codex__review` the same way: three passes, final clean. Verification is by +`mcp__codex__review` the same way: the derived floor, final clean. Invalidation is by **content** — any change to included content present when the hook runs, even from a -formatter, flips it back to unsatisfied; `.context/` and untracked ignored paths are +formatter, flips it back to unsatisfied. What that proves is bounded, and the hook's own +source says so: the current fingerprint matches the one recorded on a counted call, which +is not evidence that Codex read those bytes; `.context/` and untracked ignored paths are excluded, and staging counts, because the fingerprint covers the index and that is what a commit carries. On -`✓ Codex Gate B satisfied (3/3 cycle, 3 on current fingerprint)`, the real commit replaces +`✓ Codex Gate B satisfied (/ cycle, on current fingerprint)` — three different numbers: the calls the hook counted this cycle, the hook's own reminder threshold, and the **consecutive** counted calls on the current fingerprint since it last changed. The first is not the calls you made: the hook withholds the count for a recognized failure envelope, the backgrounding notice, and a result it can get no text from. The third is a streak, not a tally — the hook keeps the last fingerprint and that streak, so a pass on a changed fingerprint restarts it and an earlier matching pass separated by a different fingerprint is not counted. None of the three is the floor §5 obliges — the real commit replaces the WIP via `git commit --amend`. **8. PR and bots.** Open the PR as usual; once the bots have commented, run @@ -81,9 +83,9 @@ close-out (8–9). Trivial changes travel lighter, within limits: **Gate B** may only when the change is behaviourally trivial **and** the story is eligible — a profiled one at effective level 0 (risk `trivial` *and* security `none`), an unprofiled one by the prior judgement call. The profile supplies eligibility, never the skip itself; the battery -is still owed and Gate A's floor is unchanged at every level. The caution bias is +is still owed; Gate A's floor derives from the profile exactly as Gate B's does, and what the axes never subtract is the baseline questions. The caution bias is for non-trivial work, judgment is allowed. Two knobs: `.context/codex-gate.floor` (any -positive integer) moves the 3-pass floor, and `touch .context/codex-gate.off` +positive integer) moves the hook's reminder threshold, and `touch .context/codex-gate.off` silences the reminders in a scratch workspace (delete to re-enable; state keeps tracking while off, so nothing goes stale). @@ -102,8 +104,9 @@ smallest that matches your intent: 2. **One trivial change:** the hook warns, it never blocks. What §5 permits depends on the story: an **unprofiled** one keeps the old judgement call, while a **profiled** one may skip Gate B only at effective level 0 (risk `trivial` *and* security `none`), - still owes the battery, and records both the skip reason and its evidence entry in the - commit body. Gate A is not + still owes the battery, and records the skip reason, the battery result, the cycle's + provenance line, a skip record in place of the curve, and one evidence entry per cited + profiled story, in the commit body. Gate A is not skippable at any level. 3. **Pause a project:** `touch .context/codex-gate.off` (delete to re-enable; state keeps tracking, so nothing goes stale). diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index 89a10b8..ca813d6 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -102,6 +102,14 @@ Two passages diverge between the copies and get a row each; both divergences wer **Nothing in §5 outside these eleven passages is edited.** Gate B, reviewing the combined diff, is what confirms that against this table. +**Two rows added during the Gate-B cycle**, because two decision procedures were rewritten by +its findings and this table is where old conditions are accounted for: + +| # | Passage | Copy | What the pre-fix prose required | Disposition | +|---|---|---|---|---| +| 12 | profile-reading cases | both | a. cites no story → unprofiled · b. no profile line → unprofiled · c. present but unresolvable → stop, covering unparseable lines, values outside the enums, two profile blocks, a citation resolving to nothing, and the semantic mismatches | a and b **kept verbatim**. c **split**: the failure-to-obtain-the-file half — including "a citation resolving to nothing" — becomes its own case 3 with four named sub-causes and a decidable order of tests, because an agent that never read the file can answer neither a nor b; the remaining syntactic and semantic failures stay as case 4, **unchanged in substance**. Nothing dropped | +| 13 | `headSha` / branch agreement | both | a. both branches must have reviewed the same commit · b. take it from the head commit each call reports · c. capture it with the branch's result, not later · d. require exact equality before summing · e. record the full 40-character name | **b deliberately dropped**: the reviewer tool reports no head commit, so the condition was unsatisfiable as written. **a narrowed to what the mechanism supports** — both branches are *issued against* the same commit, stated with the explicit note that this is not evidence either reviewed it. c, d, e **kept**, now applied to the value passed rather than a value read back | + --- ## Task 1: The floor predicate diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md index d0fda4d..030f014 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-b-records.md @@ -378,8 +378,10 @@ NEW: a path containing a newline or other control character is NOT representable — the cycle stops and surfaces rather than emitting one - := "absent" | [1-9][0-9]* | "unusable(" ")" - := "unreadable" | "empty" | "non-numeric" | "out-of-range" + := "absent" | [1-9][0-9]* | "unusable" + (narrowed 2026-09-02: the cause token this plan + originally shipped was withdrawn — see the spec's + §2.3 note and the field report) It carries that cycle's **cycle field** — the nonce for any cycle started after these rules ship, `none (pre-rule)` only for one that began before them — the **derived floor**, and **the @@ -460,9 +462,9 @@ NEW: written `undetermined` — and the raw value is NOT reproduced anywhere in the body, since a commit message cannot safely carry one (NUL cannot appear at all). - What the body records instead is where the value came - from and which bytes were rejected, described rather - than embedded + (the source-and-rejected-bytes obligation this line + carried was withdrawn 2026-09-02 with the cause + vocabulary; nothing replaces it) A skipped cycle writes `; : skipped (see skip reason)` and no counts. **`` keys must be exactly the passes `` expands to, each once, ascending** — a @@ -486,9 +488,11 @@ NEW: A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are **branches of one logical pass** contributing one summed entry — **the curve counts logical - passes; the hook counts calls**, and where they differ the body says so. **Both branches must - have reviewed the same tracked reviewed commit.** Take it from **the head commit each call - reports having reviewed**, capture it **with that branch's result** rather than re-reading it + passes; the hook counts calls**, and where they differ the body says so. **Both branches must be + issued against the same tracked reviewed commit** — which is not a claim that either reviewed + it. Resolve `HEAD` to its full object name **before** each call and pass it explicitly; the + reviewer reports no reviewed head, so there is nothing to take from the result. Keep the value + you passed **with that branch's result** rather than re-reading it later — an intervening `WIP:` amend moves `HEAD`, so a value read afterwards is a different commit — and require the two captured values to be **exactly equal** before the branches are summed. Record it as the **full 40-character hex object name**, since abbreviations are diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md index c47fe5b..0c5f520 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -122,7 +122,7 @@ and what replaces it. | 16 | scaffolded template skip duties | as row 14, in the mirror | same disposition (Task 16) | | 17 | `getting-started.md` skip duties | as row 14, in an explanatory duty summary | same disposition (Task 17) | | 18 | `coding-workflow.md` skip duties | as row 14, in the other explanatory duty summary | same disposition (Task 18) | -| 19 | §5 findings-slot rule, as Plan B leaves it | the bare names serve every cycle with no nonce | **kept and extended**: the bare-name reservation stands and the deletion binding stands; a no-nonce cycle writing where bare-slot files already exist takes a deterministic discriminator instead. Nothing is dropped — the exception is added because this cycle is that case (Task 19) | +| 19 | §5 findings-slot rule, as Plan B leaves it | the bare names serve every cycle with no nonce | **kept, unchanged.** The extension this row originally described — a deterministic discriminator for a no-nonce cycle where bare-slot files already exist — was **dropped on 2026-09-02** with Tasks 19 and 20. The rule ships as Plan B leaves it; this cycle's own non-bare slots are a recorded plan-local exception, not a shipped production | | 20 | the same rule in the scaffolded mirror | as row 19 | same disposition, second copy (Task 20) | | 21 | spec §8 parse-check item | the evidence is a **parse** check over both grammars | **kept, and its mechanism corrected**: the coverage requirement — constructed valid and invalid strings, features recorded per grammar — is **unchanged**; the word `parse` is replaced by the comparison that exists, a `grep -E` match against each grammar's productions, with the cardinality rule named as the one case decided by counting instead. §5's own rule sends a fix that changes specified behaviour into the same commit (Task 21) | @@ -648,13 +648,13 @@ NEW: rather than implying a guarantee. - **Findings slots take a per-cycle infix**, and the deletion step §5 already requires deletes only paths carrying the cycle's own infix. A cycle that holds a **nonce** uses it; a cycle with - **no** nonce keeps the **bare** names, *except* when the workspace already holds bare-slot files - from earlier cycles — then it takes a **short, deterministic discriminator** in the nonce's - position, which is not a nonce and claims none of a nonce's properties. Either way a cycle - deletes only its own paths, never a bare path belonging to somebody else and never another - cycle's. That rule exists because a bare slot was overwritten during this change's own - development, destroying a previous cycle's findings file — and this release's own Gate-B cycle - is the discriminator case, in the workspace where it happened. + **no** nonce keeps the **bare** names. A cycle deletes only its own paths, never a bare path + belonging to somebody else and never another cycle's. That rule exists because a bare slot was + overwritten during this change's own development, destroying a previous cycle's findings file. + **What this release does not ship** is a rule for the remaining case — a nonce-less cycle where + bare-slot files already exist. This release's own Gate-B cycle is that case, and it used a + recorded plan-local naming exception rather than a shipped rule; a general production is + deferred to the loop-rule consolidation story. - **What this change does not settle** is how demotion bears on the loop-health measures — the per-pass counts, the clusters and the stop thresholds. That is the loop-rule consolidation story's, and both documents say so, so the obligation cannot fall between them. @@ -967,7 +967,17 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 19: The slot rule admits a deterministic discriminator — 1 of 2, `CLAUDE.md` §5 +## Task 19: DROPPED — the slot rule admits a deterministic discriminator — 1 of 2, `CLAUDE.md` + +> **DROPPED by author decision, 2026-09-02. Do not execute this task or Task 20.** Shipping the +> discriminator would have put a slot production into both prompt copies that the approved spec's +> slot rules do not contain, leaving the source-of-truth section stale — Gate-A pass 7's first +> BLOCKER. The general production is deferred to the loop-rule consolidation successor story. +> **What replaces it for this cycle:** the `rle` slot names are a recorded plan-local naming +> exception under the old rules, approved by the author and recorded in the closing commit body +> and the field report. Task 23's second point cites Tasks 19 and 20 as admitting the name; read +> that citation as superseded by this exception. Task 21 is unaffected: it never touched the slot +> section, which is why dropping these two leaves nothing stale. §5 **File:** `CLAUDE.md` @@ -1034,7 +1044,12 @@ git commit --amend -m "WIP: review-loop economics" --- -## Task 20: The slot rule admits a deterministic discriminator — 2 of 2, the scaffolded template +## Task 20: DROPPED — the slot rule admits a deterministic discriminator — 2 of 2, the scaffolded template + +> **DROPPED by author decision, 2026-09-02, together with Task 19. Do not execute it.** See the +> note under Task 19 for the reason and for what replaces it. Task 23's second point and this +> plan's Self-Review both still say these two tasks admit the `rle` name into the shipped rule; +> read both as superseded by the recorded plan-local naming exception. **File:** `plugins/dev-workflow/commands/workflow-init.md` @@ -1203,9 +1218,10 @@ scaffolded mirror carries this rule too and is covered by item 6, which compares - **absent** — no path. Record not-applicable with that reason. **Do not create one**; a fixture supplying its own input proves nothing. - **present but not a readable regular file** — a directory, a device, an unreadable file, **or a - symlink whose target does not resolve**. Record `unusable(unreadable)`. A broken symlink is + symlink whose target does not resolve**. Record `unusable`. A broken symlink is *present and unusable*, never absent — the state the stripped machinery got wrong and the one - this rule exists for. + this rule exists for. (The cause token this step originally recorded was withdrawn on + 2026-09-02; `unusable` no longer carries one.) - **a readable regular file** — record bytes and digest, and classify the value as numeric or as one of the pinned unusable causes. @@ -1215,7 +1231,7 @@ scaffolded mirror carries this rule too and is covered by item 6, which compares Construct strings and match each against that grammar's own productions with `grep -E`: valid ones must match, invalid ones must not. - **Provenance:** a quoted path; each `unusable()` value; **a valid numeric knob value**; + **Provenance:** a quoted path; the `unusable` value; **a valid numeric knob value**; a cited-story-with-no-profile entry; `none` for no story cited; **a real `cycle ` field**. **Curve:** a gapped `` such as `1,2,4`; a split-model pass; a `?` count; a skipped cycle's skip record; **a `cycle ` field, the same nonce as the provenance instance**, so @@ -1279,10 +1295,10 @@ should report success, and under `set -e` the first negative guard aborts before The third asks about a **different** commit, which is why it can fail. A stacked WIP would silently leave the earlier snapshot out of the only review. -2. **The slots are `gate-b--rle-pass-

.md`**, and `rle` is the deterministic - discriminator **Tasks 19 and 20 admit into the shipped rule** — without them this form is - admitted by neither the bare names nor the nonce grammar, and the plan would contradict the - prompt it ships. **§8 states the same thing** — *"its slot discriminator is short and +2. **The slots are `gate-b--rle-pass-

.md`**, and since Tasks 19 and 20 were + dropped, `rle` is admitted by **no shipped rule at all**: it is a **recorded plan-local naming + exception** under the old rules, approved by the author on 2026-09-02 and recorded in the + closing commit body and the field report. That is the whole of its authority. **§8 states the same thing** — *"its slot discriminator is short and deterministic, so it is not a nonce"*. The reason it is needed here: this cycle is pre-rule and **cannot mint a nonce**, so the @@ -1319,7 +1335,9 @@ blocks are what it compares against. cycle's own output land here, then the body is composed from them. - [ ] **3 — A named verification of the risk path.** This cycle emits **one** provenance line. - Recompute its floor from the `Story:` header of the artifact this cycle reviewed. **The + Recompute its floor from the **union of the `Story:` headers of every plan contributing to the + reviewed diff** — Gate B reviews a diff and has no header of its own, which is what the shipped + rule says. (For this cycle all three plans cite one story, so the union is one path.) **The observation that would exist if the claim were false is a line whose floor the cited profile does not license.** @@ -1339,6 +1357,12 @@ cycle's own output land here, then the body is composed from them. 2. **This Gate-B cycle's provenance line.** 3. **This Gate-B cycle's per-pass curve.** 4. **Decline records**, if the cycle produced any. +5. **The `rle` naming exception**, which Task 23 requires be recorded here — a plan-local + exception under the old rules, approved by the author 2026-09-02, with no shipped rule + behind it. +6. **The standing decision record** for this cycle: the pass-2 discount, the dropped Tasks 19 + and 20, the withdrawn knob-cause and model-cause vocabulary with its accepted capability + cost, and the closing disposition. **Nothing else, and the exclusions are the point.** No records for the four Gate-A cycles, no reconstruction, no reconstruction marker, no adjacent prose explaining one, and no line about the @@ -1421,9 +1445,9 @@ every occurrence fixed in one pass: `AGENTS.md`'s own recipe, *search for the cl phrase*, used as the method rather than as a warning about it. The claim-to-site map is in the commit body. -**Two of those claims changed what the plan ships**, and neither was a wording fix. The **slot -rule gains a production** (Tasks 19 and 20), because the plan's own slot form was admitted by -nothing the plan ships — the plan and the prompt it ships could not both be followed. And **§8's +**One of those claims changed what the plan ships**, and it was not a wording fix. The slot rule +was to gain a production (Tasks 19 and 20) — **that was dropped on 2026-09-02**, and the plan's +own slot form is now a recorded plan-local exception instead of a shipped rule. And **§8's mechanism names four constraints a grep cannot decide instead of one**, because revision 6's correction of an overclaim was itself an overclaim. diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index db17ac5..5f97318 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -10,7 +10,8 @@ Prompt-only, in two mirrored copies: `CLAUDE.md` §5 and `/workflow-init`'s inli **This document states rules and decisions.** Concrete formats, per-site wordings, the old-conditions passage list, and every procedure live in the plan, reviewed there against this spec. Two things stay pinned as *required properties* because a shipped criterion reads them and a -program parses them: the provenance line (§2.3) and the per-pass curve (§4). +program is intended to parse them — P8's deferred measurement, which does not exist yet: the +provenance line (§2.3) and the per-pass curve (§4). **One constraint on the edit itself:** `codex-gate.sh:94` greps `CLAUDE.md` for the §5 heading to build every reminder's citation, so **the heading must keep matching @@ -90,7 +91,8 @@ passes are still being spent, which is the only time checking it is cheap. ### 2.3 The provenance line — required properties One line per cycle, in its closing commit body. **The grammar is pinned here, not in the plan** — -the parent story requires one form and says this spec states it, and P8 parses it. (Revision 15 +the parent story requires one form and says this spec states it, and P8 — deferred, and not +built yet — is intended to parse it. (Revision 15 moved it out and was wrong to; pass 9 had already settled this, and the test it settled on is whether anything but a person parses it.) @@ -110,24 +112,29 @@ whether anything but a person parses it.) a path containing a newline or other control character is NOT representable — the cycle stops and surfaces rather than emitting one - := "absent" | [1-9][0-9]* | "unusable(" ")" - := "unreadable" | "empty" | "non-numeric" | "out-of-range" + := "absent" | [1-9][0-9]* | "unusable" ``` Everything that quotes this line elsewhere quotes an instance of it; there is no informal variant. The properties the grammar exists to satisfy: - **Every cycle records it**, default floor or not, so an absent line is never ambiguous between - "the default applied" and "someone forgot". **Three cycles means three lines**, one per cycle. + "the default applied" and "someone forgot". **One line per cycle run**, so a change running + five cycles records five — the count is of cycle runs, not of the three cycle kinds. - It carries that cycle's **cycle field** — **the nonce (§5) for any cycle started after these rules ship, and `none (pre-rule)` only for a cycle that began before them** — the **derived floor**, and **the cited set that produced it with each member's level as a numeral** — one floor and one set, not an entry per story, since unanimity makes the floor a property of the set. - It distinguishes **a cited story with no profile** from **no story cited**. -- It records the **workspace knob whenever the file exists**, including when present but unusable, - **naming the cause** — unreadable, empty, non-numeric, out-of-range — because those need - different fixes and one token names a symptom rather than a cause. +- It records the **workspace knob whenever the file exists**: the value if one was read, + otherwise `unusable`. **It does not name why.** An earlier revision required a cause token — + unreadable, empty, non-numeric, out-of-range — on the reasoning that those need different + fixes. That was withdrawn on 2026-09-02, knowingly and at a cost: four successive attempts to + state the classification behind those tokens were each wrong in a different way, the last one + demonstrably (a file of `1`, NUL, `2` is accepted by the hook as twelve, in `sh`, `dash` and + `bash` alike). The record now says **that** a knob was unusable, no longer **why**; whoever + needs why reads the file and the hook. - It is **machine-extractable and has one form covering every case**, because the deferred P8 measurement parses it. @@ -248,9 +255,9 @@ reason as the provenance line:** written `undetermined` — and the raw value is NOT reproduced anywhere in the body, since a commit message cannot safely carry one (NUL cannot appear at all). - What the body records instead is where the value came - from and which bytes were rejected, described rather - than embedded + (the source-and-rejected-bytes obligation this line + carried was withdrawn 2026-09-02 with the cause + vocabulary; nothing replaces it) ``` **`` keys must be exactly the passes `` expands to, each once, ascending** — a @@ -306,9 +313,11 @@ here keeps a later reader from computing a demotion figure the baseline cannot b - A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are **branches of one logical pass** contributing one summed entry. **The curve counts logical passes; the hook counts calls**, and where they differ the body says so. -- **Both branches of one logical pass must have reviewed the same artifact revision**, identified - by the **tracked reviewed commit** — the plan fixes how it is encoded, but which thing is - compared is a decision, not a format. **If it changed between them they are not one pass**: the +- **Both branches of one logical pass must be issued against the same artifact revision**, + identified by the **tracked reviewed commit** passed to each call — the plan fixes how it is + encoded, but which thing is compared is a decision, not a format. **What this establishes is + that the two calls were aimed at one revision, not that either branch read it**: the reviewer + reports no reviewed revision, so no stronger claim is available. **If it changed between them they are not one pass**: the completed branch is recorded as an incomplete pass and excluded, and the later branch begins a new one. Ending the pass is the conservative direction; merging two revisions would produce one entry describing two different artifacts. @@ -464,12 +473,20 @@ Mode `battery+check+verification` (no `+abuse-path`; security is `none`). - **A conditional verification that a user's knob survives untouched** — byte-identical across a cycle where one exists; **recorded not-applicable with its reason where none does**, since creating one would be a fixture supplying its own input. -- **A parse check over both pinned grammars.** The branch's own instances cannot exercise them: +- **A grammar check over both pinned grammars.** The branch's own instances cannot exercise them: three lines cannot cover quoted paths, each unusable-knob cause, gapped pass ranges, split-model passes, a skipped cycle, or the cardinality rule that `` matches ``. **The check - reads a set of constructed strings — valid ones that must parse and invalid ones that must be - rejected** — and records which grammar features each exercises. A grammar nothing ever parsed is - a format claim, not a format. + matches a set of constructed strings against each grammar's own productions with `grep -E` — + valid ones must match, invalid ones must not** — and records which grammar features each + exercises. **It is a grep plus field comparisons, not a parser**: these forms are pinned as + greppable field grammars and this repo ships no parser for either, so naming a parser would name + a check nobody can run. **A match decides the lexical productions and nothing else.** Every + constraint it cannot decide is checked by a comparison over the extracted fields: `` + having as many entries as `` enumerates; a `` occurring more than once; `` + ranges ascending and non-overlapping; a per-pass key present in one field and absent from its + partner. **That list is what reading both grammars for non-regular constraints produced, and it + is not proven complete** — a further one is checked the same way rather than folded into the + match. A grammar nothing was ever matched against is a format claim, not a format. - **Parity across every changed rule**, in both copies, since they already diverge and parity cannot be asserted from a whole-section diff. - **A fresh twelve-item `docs/prompt-standards.md` pass.** Invariant 11 binds **each changed diff --git a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md index 9148a47..43acb8e 100644 --- a/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md +++ b/docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md @@ -168,9 +168,11 @@ would separate a rule from the argument that settles it. recorded as unknown rather than as zero, per series** — a pass may have one series lost and the others intact — and a reader excludes an unknown value only from the comparisons that read it. Both copies also state when separate calls - are **one logical pass**: only when they reviewed the **same tracked revision** — and that a - revision mismatch **ends the first as an incomplete pass** before the later one starts a new - pass, rather than merging two revisions into a single entry. Checkable: the requirement is + are **one logical pass**: only when they were **issued against the same tracked revision**, + which is what the retained request values establish and is not a claim that either branch + read it — the reviewer reports no reviewed revision. A mismatch **ends the first as an + incomplete pass** before the later one starts a new pass, rather than merging two + revisions into a single entry. Checkable: the requirement is stated in both copies, and this story's own commits carry it for each cycle that ran — **every field of the pinned form except the identifier, which no cycle on this branch can supply.** Those records carry the reserved pre-rule value, which by construction **does not diff --git a/plugins/dev-workflow/.claude-plugin/plugin.json b/plugins/dev-workflow/.claude-plugin/plugin.json index c7b63d7..5599cbd 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.10.0", + "version": "0.11.0", "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 02eac0f..526f194 100644 --- a/plugins/dev-workflow/CHANGELOG.md +++ b/plugins/dev-workflow/CHANGELOG.md @@ -22,6 +22,47 @@ 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.11.0 + +- **The mandatory pass floor is now a function of the cited story's profile**, not the constant 3. + `max(risk, security) == 0` gives a floor of **1**; every resolvable profile above that, and an + artifact citing no story, gives **3**. A cited story whose profile is present but unresolvable + **stops and surfaces** rather than defaulting. Across a cited set the floor is 1 only if the set + is non-empty and every member is profiled, resolvable and at level 0. +- **The hook's ratio is a reminder threshold and controls nothing.** It always did; the text now + says so, and the `codex-gate.floor` knob is described as moving that threshold rather than the + obligation. `README.md` and `docs/getting-started.md` carried the old description and are + corrected. +- **Finding severity is decided by whether something in the system takes a different decision.** + Name what consumes the text and the decision that changes if it is wrong; if you cannot name + both, the finding is Minor or below. The review pass raising a finding is not an in-system + reader of the text it reviews, gates remain readers of rule text they will later apply, and a + human reader never satisfies the test. It sets a ceiling, never a floor, and never chooses + between Blocker and Major. +- **Two commit-body records are pinned**, so that a program can parse them — P8's deferred + measurement is the intended consumer, and **no parser for either form ships today**; the + evidence for this release matched constructed strings against the grammars instead: a + **provenance line** + carrying the cycle field, the derived floor and the cited set that produced it, and a + **per-pass curve** carrying Findings, Blockers and Majors per pass. One of each per cycle — a + change running five cycles records five. +- **A cycle nonce** attributes those records. Eight to sixteen characters from `[a-z0-9]`, from a + source of randomness, never derived from a name, timestamp or commit. It is + collision-**resistant**, not collision-proof, and the shipped text says where that bound bites + rather than implying a guarantee. +- **Findings slots take a per-cycle infix**, and the deletion step §5 already requires deletes + only paths carrying the cycle's own infix. A cycle that holds a **nonce** uses it; a cycle with + **no** nonce keeps the **bare** names. That rule exists because a bare slot was overwritten + during this change's own development, destroying a previous cycle's findings file. + **What this release does not ship** is a rule for the remaining case — a nonce-less cycle in a + workspace that already holds bare-slot files, where the bare names would collide with somebody + else's. That case is real: this release's own Gate-B cycle is one, and it used a recorded + plan-local naming exception rather than a shipped rule. A general slot production for it is + deferred to the loop-rule consolidation story. +- **What this change does not settle** is how demotion bears on the loop-health measures — the + per-pass counts, the clusters and the stop thresholds. That is the loop-rule consolidation + story's, and both documents say so, so the obligation cannot fall between them. + ## 0.10.0 - §5's gate loop gained two rules it was missing, both mirrored into `workflow-init`'s scaffolded diff --git a/plugins/dev-workflow/commands/process-pr-review.md b/plugins/dev-workflow/commands/process-pr-review.md index ced1a7b..46bdf72 100644 --- a/plugins/dev-workflow/commands/process-pr-review.md +++ b/plugins/dev-workflow/commands/process-pr-review.md @@ -148,18 +148,30 @@ be ambiguous, which is not an instruction. - **every cited story is eligible.** Resolve the cited story path(s) first, looking in **both** the PR body and the **commit bodies in the range** — the workflow puts the story path in the closing commit message, so a PR that cites nothing in its - description may still be profiled. Only when neither carries a citation is this §5's - "no story cited" case, which takes the unprofiled judgement call. A profiled story is + description may still be profiled. **This reads a record, and is not §5's + governing-set derivation**, which takes the cited set from a reviewed artifact's own + `Story:` header and forbids grepping an artifact body for story paths. A closing + commit body carries the path deliberately, as an evidence entry; an artifact body + mentioning one may be a disclaimer. Different question, different source — and this one + never relaxes the other: where no record carries a citation, that is an absence **in the + PR record**, and it takes the unprofiled judgement call for *this command's* skip + decision only. It is not §5's "no story cited" case, it cannot set a floor, and it can + neither override nor substitute for a governing set derived from a reviewed artifact's + `Story:` header. Where neither carries a citation, take the unprofiled judgement call + for this decision — without calling it §5's "no story cited" case, which is about a + reviewed artifact's header and not about this record. A profiled story is eligible only at effective level 0 (risk `trivial` *and* security `none`); an unprofiled story is eligible on the old judgement call. With several cited stories, each must be eligible on its own; one eligible story does not carry the rest. A cited profile that - is **present but unresolvable** (§5's third case) stops the run — surface the cause; + is **present but unresolvable** (§5's fourth case) stops the run — surface the cause; it is never treated as unprofiled. A skip removes the review and never the evidence. Every skipped cycle runs the battery - and records, in the commit body, **the skip reason and the battery result**. On top of - that: one mode-derived evidence entry per cited **profiled** story, and none for an - unprofiled one — which owes the reason and battery result and nothing further. + and records, in the commit body, **the skip reason and the battery result** — and, like + every other cycle, **its provenance line and, in place of a curve, its skip record**. On + top of that: one mode-derived evidence entry per cited **profiled** story, and none for an + unprofiled one — which owes the reason, the battery result, the provenance line and the + skip record, and nothing further. 4. Stop and ask the user for: every `escalate-to-user` verdict, and every accepted finding that contradicts a settled decision. Do not implement these. A finding already recorded as out of scope by item 2 does **not** come here — it is terminal diff --git a/plugins/dev-workflow/commands/workflow-init.md b/plugins/dev-workflow/commands/workflow-init.md index 3895394..391ca17 100644 --- a/plugins/dev-workflow/commands/workflow-init.md +++ b/plugins/dev-workflow/commands/workflow-init.md @@ -189,6 +189,13 @@ If a `CLAUDE.md` already exists with unrelated project content, do not overwrite offer to **append** sections §1–§5 (renumbering only if the file already uses those numbers) and say so in the report. +> **Prompt-standards item 1 for the scaffolded `CLAUDE.md`: n/a, and why.** The file this +> template writes is model-agnostic by design — its executing model is whatever the reader of +> that project runs — so a `Target model:` line inside it would be false in every repo it lands +> in. Recorded as a reasoned n/a rather than skipped: the item is answered. **This note sits +> outside the fence** so it never scaffolds, and is deliberately **not** a `Target model:` line, +> which would make this file's declaration count 2 and fail `scripts/check-invariants.sh`. + ````markdown # @@ -269,16 +276,129 @@ counters and Gate B reports "not run" forever. A mapped name must itself start w to the hook or, for `Bash`/`Skill`, hijacks a lifecycle event. Register the server as `codex` to place its tools there. -**Both gates are a LOOP with a HARD FLOOR: min 3 passes per run (Blocker/Major -only), counted by the hook.** The hook counts passes but can't read findings or -tell the spec run from the plan run (it resets at `writing-plans`), so Gate A — -the spec run especially — is instruction-backed: a satisfied count is not a clean -review. Open a TodoWrite "Codex pass N" per pass; fix Blocker/Major after each. Your -final pass must be clean — if pass 3 still finds Blocker/Major, keep going until +**Both gates are a LOOP with a HARD FLOOR: a minimum number of passes per run +(Blocker/Major only), derived from the cited story's profile.** +The derivation is max(risk, security): a value of 0 gives a floor of 1; every +resolvable profile above that, and an artifact citing no story, gives 3. Two levels, +not three — `high` takes its rigor from lens sets and evidence mode, not from extra +passes. A cited story whose profile is present but unresolvable stops and surfaces +under the existing rule; it does not fall through to 3, because reading it as 3 would +turn a stop condition into a silent default. Across a cited set the floor is 1 if and +only if the set is non-empty and every member is profiled, resolvable and at level 0 +— all four conditions, since "every cited story" is vacuously true of an empty set; +no story cited, or any cited story unprofiled, gives 3. One derived value governs all +three cycle *kinds*: the Gate-A spec loop, the Gate-A plan loop and the Gate-B cycle. Not +because they are one cycle — they are separate cycles, and a change carrying several plans runs +a Gate-A plan cycle per plan — but because they derive from the same +cited-story set. That value is a function of the current confirmed profiles of that set, +read fresh wherever this section already requires them to be read, so wherever a value can be +derived at all there is exactly one, because there is one source. Some states derive **no** value +rather than a second one, and each stops rather than defaulting: governing headers that +disagree; a cited profile that is present but unresolvable; and a `Story:` header that cannot +be read. A change to a profile or to the set +therefore binds every open and future cycle — a raise costs an affected open cycle a +further pass under the current profile, as the profile-change rule below requires — while a +cycle that has already closed +stands, its close having been valid under the profile current when it closed, which is the +cycle-level form of passes already run keeping their count. **The set has one authority: +the artifact's `Story:` header, which carries the path of every cited story.** Nothing else +is a citation. A story path appearing anywhere else in an artifact's body — including a +sentence placing a story *outside* this change's scope — **contributes nothing to the cited +set and nothing to the floor derivation**, which is the only claim made about it; it may still +be a perfectly good cross-reference for any other purpose. And **an agent +deriving the set reads that header and does not grep the body for story paths**, because a +grep finds mentions and cannot tell a citation from a disclaimer. **Each cycle's governing header is the +header of the artifact it reviews**: the spec's for the Gate-A spec loop, the plan's for the +Gate-A plan loop, and — since a Gate-B cycle reviews a diff and has no header of its own — +**the union of the `Story:` headers of every plan contributing to that diff, which the Gate-B +call must carry in full**, as this section already requires of every cited path. **Every expected artifact contributes a set — a spec, and every plan contributing to the +reviewed diff — and an expected artifact whose `Story:` header is absent contributes the empty +set rather than dropping out of the comparison.** **One path per entry**, and "entry" is +decidable against the form these artifacts actually carry: a single line beginning `**Story:**`, +then one or more paths, **each wrapped in backticks**, separated by `, `. Trailing prose after +the last path is allowed and contributes nothing — several headers carry a reminder to read the +profile fresh, and a reminder is not a citation. So a header citing several stories carries several entries, +one path each. Exact duplicate paths are one member; entries naming different stories are +different members; and a header that does not parse as that line is malformed and stops, +reporting that as the cause rather than as a disagreement. **Before each pass the deriving agent +compares every such set, and again before a clean pass is accepted as the cycle's final pass.** +A header or profile that changed during that pass means the pass is not final — the same +answer a change gets at every other read point. Where they name different sets the premise of a single value has +failed: **stop and surface the disagreement** rather than deriving from either, exactly as an +unresolvable profile stops rather than defaulting. + +**The derived floor is the pass count a cycle owes, and the hook's ratio is a reminder +threshold that controls nothing.** The hook still counts passes, and it still can't read +findings or tell the spec run from the plan run (it resets at `writing-plans`), so +Gate A — the spec run especially — is instruction-backed: a satisfied count is not a +clean review, and a below-threshold reminder is noted in the pass report and disregarded +where the cycle's own closure rules are satisfied. This replaces the pass-count number +and nothing else. Every other rule stated here about how a cycle closes stands as +written, and none of them is restated — a summary is where their conditions would get +dropped. Nothing here writes the floor knob: it stays the user's, never written, never +removed, never read for this derivation. Open a TodoWrite "Codex pass N" per pass; fix Blocker/Major after each. Your +final pass must be clean — if the pass at the floor still finds Blocker/Major, keep going until clean or clearly stuck → then STOP and surface to the user. The only early exit -below 3 is a pass with **zero** findings; don't manufacture findings to pad. Codex is +below the floor is a pass with **zero** findings; don't manufacture findings to pad. Codex is advisory — validate before applying; dismissed finding → one-line why. +**Named residual:** the hook's messages state its own threshold as an obligation, so at a +floor of 1 they report a shortfall the cycle does not owe. Hook text is out of scope here +by decision; what makes that tolerable is the precedence rule above plus the hook exiting +0 on every branch, not the reminder being harmless. + +**The gate-off surface — routes known today, not a complete list**, since an enumeration +read as complete guarantees the routes it omits. One route is created here: a stated floor +the cited set does not license, which could not exist before there was a derived floor to +state. Pre-existing and unchanged: omitting a higher-risk cited story; minting or editing a +profile to level 0; presenting an incomplete cited set; falsifying evidence entries; +silencing reminders; or not running a pass and reporting that it ran. A user-set floor is +not the lever — it moves what the hook says, not what the cycle owes. +None of this is a guard: the floor is produced by the agent and nothing checks it against +the cited profiles. + +**When these rules bind.** From the commit that ships them, and a cycle already running +finishes under the rules it started with. Where a cycle's starting rules cannot be +established it takes the stricter reading of every part this change touches — at minimum +floor 3, severity classified without the demotion, the provenance-line duty owed, the curve +duty owed, and the nonce duties at their strictest — the cycle is treated as post-rule, so it +owes a nonce, owes its provenance line and its curve or skip record, and uses that nonce in every +cycle record it does write — which changes what a record is named, never whether one is owed, so +the working record stays optional and a skipped cycle still writes no findings slots. Where it +cannot recover a nonce it starts a new cycle rather than claiming `none (pre-rule)`, that reserved +field being unavailable to a cycle whose start cannot be established. Each further rule this change ships adds its own strict +reading to this list. Not a re-derivation, which could hand a level-0 +cycle a floor of 1 and skip passes on the strength of not knowing when it started. A user +knob set above 3 is not lowered by this fallback. A revert is itself a shipping commit for +the old rules, and the activation rule wins wherever the start is determinable; the +fallback covers only where it is not. + +**Downstream has no shipping commit.** Adoption binds from the `/workflow-init` run that +actually writes the text — which may write nothing, be declined, or be merged in part — +so these rules bind only over the text a project's `CLAUDE.md` actually contains, and a +partial adoption can persist undetected. A project taking the floor rule without the +severity test gets a floor whose docs-only question the severity test is what settles. +**A partial adoption can leave a project's floor undefined or self-contradictory.** The rule +is a coherence requirement, stated semantically rather than as a list of spellings, and it +runs in **both** directions: **exactly one definition of the floor must be present, and every +statement that defines or constrains the floor, or makes closing depend on it, must resolve to +that one definition.** **The unknown-start fallback is not a second definition**: it is +explicitly conditional on a cycle's starting rules being undeterminable and governs only that +state, so it coexists with the predicate rather than competing with it. Everything else +likewise keeps its own footing and is **not** required to derive from the floor: **the other closure and stop predicates** — assigned-fix-set membership, a new +structural question, an accepted Blocker or Major, the tell thresholds; **independent reporting +and diagnostic ordinals**, such as a duty owed from a given pass onward; and **the hook's +reminder threshold together with any descriptive or historical pass number**, which say what a +tool reports or what once happened rather than what a cycle owes. Four states break it, and the list is **not exhaustive**: a fixed-number or +specific-pass obligation surviving beside the derived predicate; a claim or dependency on a +derived floor with no predicate to define it; **no definition at all**; and **two definitions +at once**. A +merge can produce any of them: the Gate-A loop description, the pass-1 closure rule and the +re-review rationale each carry a fixed-three claim and can be taken or left independently of +the predicate itself. In any such state nothing here resolves which rule governs: **stop, and +have a human complete or revert the adoption, before running a gate under it.** What prompt text can do about downstream +adoption is limited, and that limit is what this paragraph states. + **What a loop absorbs, and what stops it — a question of scope, not of action.** A finding that corrects the correction you just made **and stays inside the assigned fix set** is **inside this loop's scope**: keep it here rather than handing it back, then act on it by its @@ -319,7 +439,8 @@ finish, and it is why **a clean completion takes precedence over this exit**: a Blocker/Major-free pass **at or above the floor** has satisfied the clean-final-pass rule — collect the Minors and Nits and close — and reporting "will not converge" on a converged loop is a false report. **Below the floor nothing closes**, and a zero-finding pass remains -the only exception, exactly as above; a Blocker/Major-free pass 1 carrying a Minor keeps +the only exception, exactly as above; a Blocker/Major-free pass below the floor +carrying a Minor keeps looping. **Surfacing does not close the cycle, and that is what makes this reachable.** You surface *with the finding still open* — the resolve rule is not waived, no pass is credited as @@ -327,6 +448,14 @@ clean, and the loop resumes on whatever the user decides. Reading it as "stop in fixing" would put the exit in competition with the rule that every Blocker and Major resolves, and then nothing could satisfy both. +**Every pass report states three things about the floor**, from pass 1 onward: the +derived floor, the risk and security values read, and the cited stories they were read +from. A report giving the number alone leaves a reader unable to check the derivation +while passes are still being spent — which is the only time checking it is cheap. Where +no story is cited, or a cited story is unprofiled, the report says so in place of axis +values; a multi-story set names each story and its values. This is owed by every pass; +the three lines below are owed from pass 4 and are a different obligation. + **From pass 4 onward every pass report carries three lines.** The carrier is **your own status report to the user** — never the Codex reply, which stays exactly one line per branch, and never the findings file, which admits no line that is not a finding or the terminator. @@ -364,7 +493,26 @@ because the response stops carrying the findings at all. Append to the gate prom > `.context/codex-reviews/.md` (create the directory if needed; the path is > relative to that root — Codex resolves writes against its working directory, so > without this a valid file can land in a different checkout). `` is -> `gate-a-spec-pass-

`, `gate-a-plan-pass-

`, or `gate-b--pass-

`. +> `gate-a-spec-pass-

`, `gate-a-plan-pass-

`, or `gate-b--pass-

` for a +> cycle with no nonce; a cycle that has one writes `gate-a-spec--pass-

`, +> `gate-a-plan--pass-

` or `gate-b---pass-

` instead, and uses +> the nonce in every slot more than one cycle could write. The bare names are reserved for the +> legacy single-cycle case they already serve. **Distinct-nonce paths coexist by construction and +> are never in conflict** — a sibling cycle's slot is simply a different file. +> +> **The rule binds the deletion step, which is where the damage is done.** This section already +> requires every target to be deleted and confirmed gone before a call. A cycle holding a nonce +> **deletes only paths carrying its own nonce**; it never deletes a bare path or one carrying a +> different nonce, and an attempt to do either **stops and names the path** instead of removing +> it. That is reachable and observable: the step operates on a path it computed, and the check is +> whether that path is the cycle's own. **The case it exists for is a nonce-holding cycle +> computing a bare path** — the legacy spelling — **and deleting a file that belongs to somebody +> else**, which is exactly what happened once. **Two cycles that drew the same nonce compute the +> same paths and are indistinguishable to this rule**; what makes that unlikely is the width of +> the draw, not this rule — this section already stops on a +> target that survives deletion, and this extends that to a target that must not be deleted at +> all. That rule exists because a bare slot was in fact overwritten once, destroying a previous +> cycle's findings file. > > One finding per line in the format above; escape a literal pipe inside a field as > `\|`. @@ -410,6 +558,114 @@ terminator remain the only hard requirement, and a zero-finding pass needs no co Whoever runs the cycle writes it when useful, replaces it as the cycle moves, and deletes it once the cycle closes. Nothing depends on it existing. +**The cycle nonce.** Both shipped records below carry a **cycle field**, because a record that +cannot be attributed to a cycle cannot be told apart from another cycle's when several are read +together. That is a limitation rather than a disqualification — a human reading one cycle's +records knows which cycle they came from; what attribution buys is that a *later* reader +**usually** does not have to. Usually, not always: the guarantee is probabilistic, for the two +reasons stated at the end of this block. This section defines three **kinds** of cycle — the Gate-A spec loop, the Gate-A +plan loop and the Gate-B cycle — and **one cycle field is produced per cycle run, not per +kind**: a change carrying several plans runs a Gate-A plan cycle for each, and each of those is +its own cycle with its own nonce. + +Generated once at cycle start, immutable, and collision-resistant operationally: **8 to 16 +characters drawn uniformly from `[a-z0-9]`, from a source of randomness** — 8 being where +collision resistance starts and 16 where the field stops being a usable infix. **Never derived +from a name, a timestamp or a commit**, each of which collides exactly where sibling cycles do, +which is the one thing the nonce exists to prevent. The character set keeps it safe as a slot +infix and a path component. + +**It appears in every record the cycle writes** — which keeps records apart **as far as distinct +nonces allow**, and no further — **and that set is named rather than left open**: +the provenance line, the per-pass curve (including a skip record standing in for one), the +cycle's findings slots, and its advisory working record. **The working record is a cycle record +too**: a cycle holding a nonce names it `gate-a-spec--resume.md`, +`gate-a-plan--resume.md` or `gate-b--resume.md`, and the bare names above stay +reserved for the legacy single-cycle case, exactly as the findings slots do. **Because recovery +scopes candidates by artifact as well as by kind, the record's contents name that artifact**, and +what counts as the artifact depends on the cycle kind: for a Gate-A cycle it is the reviewed +document's path, quoted by the same rule the provenance line uses where quoting is needed; for a +Gate-B cycle, which reviews a diff rather than a file, it is the **base commit's full +40-character hex object name**, the same value the cycle's reviews are run against. The filename +carries kind and nonce; the artifact key lives inside, where neither a path nor a hex name has to +survive a filename. The nonce is not +required in records this change neither introduces nor keys to a cycle — the evidence entry and +a human-exception record among them. + +**A nonce is a candidate for recovery only if** it is keyed to this cycle's kind — Gate-A spec, +Gate-A plan, or Gate B — **and** this cycle's artifact, **and** that cycle is still open. **Those +three are necessary and not sufficient, and the difference matters**: two Gate-A cycles can review +the same document and two Gate-B cycles commonly share a base commit, so a sole match on kind and +artifact is **not** identity. **A candidate is adopted only if it is positively linked to this +run** — the working record this run itself wrote. A match that is merely consistent is treated as +no identity, and the cycle starts fresh; adopting a sibling on a shared key would merge two +cycles under one nonce, which is the failure this rule exists to prevent. +History normally holds many closed cycles' nonces and they are not candidates; a working record +left by a closed cycle is not one either, which is why that record is **retired at closure** +rather than left to be found later. **Recovery has two sources, and they answer different questions.** The **working record** is the +source while the cycle runs, and it is the one the candidate rules above apply to — several files +may be present and the run must decide which, if any, is its own. **History is the source once +the cycle's own commit exists**, and there is no search there: the cycle is reading **its own +commit body**, so kind and artifact are settled by which commit is being read, and the nonce is +taken from the provenance line and the curve, which must agree. A Gate-A cycle mid-run has no +such commit and therefore has only the working record. Recovering a single candidate from +**either** keeps identity **as far as the field can distinguish cycles** — two cycles sharing a +nonce are one cycle to it. **No candidate, +disagreeing sources, or more than one candidate → no identity: start a new cycle**, which costs +passes rather than letting one cycle's records read as another's — again, as far as distinct +nonces allow. **Starting a new cycle does +not close, adopt or retire the cycles those candidates belong to** — they stay open, keep their +own nonces, and are a human's to resolve; the new cycle simply does not claim them. + +**A cycle does not start without a valid nonce, unique among the cycles open when it was +generated.** That is the requirement. **What the check can establish is narrower** — it compares +against the cycles it can observe — and the gap between the two is the residual set out below. +Where generation fails, make **at most three attempts in total**, then stop and +surface, **naming which of the three causes occurred**; each has its own check and its own fix, +and one token would name a symptom rather than a cause: + +The three are distinguished by **where** the attempt stopped, so they cannot both apply: the +source failed to produce bytes; or it produced bytes that are not a well-formed nonce; or it +produced a well-formed nonce that is already in use. An empty result is the first, never the +second. + +- **randomness unavailable** — the source errors or produces no bytes. *Fix:* retry, since the + condition can be transient; if it persists across the attempts, make a source available or run + where one is, which is a change to the environment rather than another draw. +- **an invalid value** — the drawn value is not 8 to 16 characters from `[a-z0-9]`. *Fix:* + redraw. Repeated invalid output points at the generator rather than at luck, and the report + says which. +- **a collision with a known-open cycle** — the value equals a nonce on a cycle still open. + *Fix:* redraw. A second collision at this width is possible but unlikely enough to be worth + reporting as a possible source defect, which the report states as a suspicion rather than a + finding. + +**Report every distinct cause observed across the attempts, in the order they occurred** — the +attempts can fail for different reasons, and naming only the last would describe the tail of the +sequence rather than what happened. + +**No deterministic fallback.** + +**Residuals, disclosed rather than guarded, and this list is not exhaustive.** The check compares +against cycles *known to be open*, so a nonce can repeat one belonging to a cycle nobody can see; +two cycles starting at the same moment can each check before either has published, so neither +observes the other; and the check deliberately ignores **closed** cycles, so a new cycle can +redraw a closed one's value and then write to its surviving findings slots and working record. +**What makes both unlikely is the width of the draw, not the check** — and unlikely is the +honest word. Neither is a guard. + +**What follows from that, said here rather than left to be discovered.** The nonce is +collision-**resistant**, not collision-**proof**, so everything built on it inherits that bound: +two cycles sharing a nonce write to the same slots and are not refused, their records read as +one cycle's, and a later reader cannot separate them. Attribution is therefore a strong default +rather than a guarantee, and any reading of these records that would be wrong if two cycles +shared a field should say so rather than assume they did not. + +**A cycle that began before these rules shipped has no nonce and cannot acquire one.** Its +records carry the reserved `cycle none (pre-rule)` field and are, by construction, not +cycle-attributable. That exception is bounded and self-terminating: it reaches only cycles +already running when the rules land, and no later cycle can enter the state. + **Accept a pass only when** the file exists and is readable; its last line is exactly `END OF FINDINGS ( total)`; it contains exactly `` finding lines *and nothing @@ -418,7 +674,7 @@ the file" would accept a truncated file padded with fragments); and, for a `full pass, both branch files satisfy all of that. Anything else — missing, unreadable or empty file, wrong path, malformed terminator, count mismatch, extra lines, one branch file, an `INCOMPLETE` reply — is an **INCOMPLETE pass**, which is not a review: don't -act on the partial list, don't count it toward the 3-pass floor, and don't read "no +act on the partial list, don't count it toward the floor, and don't read "no Blocker/Major visible" as clean. **Reader:** the severity field is taken by splitting the line on **unescaped** pipes and @@ -482,7 +738,7 @@ fingerprint components, so review artifacts cannot invalidate the review they do Add `/.context/codex-reviews/` to `.gitignore` — that entry specifically, not all of `.context/`, which would strip the committed `codex-gate.on` adoption marker. -- **Gate A — Spec, then plan (TWO runs, each its own 3-pass loop).** Run on the +- **Gate A — Spec, then plan (TWO runs, each its own loop at the derived floor).** Run on the **spec** right after brainstorming (before `writing-plans`), then on the **plan** before `executing-plans`/`subagent-driven-development` — catching a spec flaw before it's baked into the plan. Tool: `mcp__codex__exec` (raw; @@ -515,8 +771,8 @@ Add `/.context/codex-reviews/` to `.gitignore` — that entry specifically, not - **Gate B — Code.** Tests green, before `git commit`. Tool: `mcp__codex__review` (args `instruction`, `whatWasImplemented`, `baseSha`; `reviewType: full` runs spec + quality in parallel). Skip ONLY trivial changes. Check against - @AGENTS.md. Re-review after every fix — a fix changes the diff and the hook - invalidates the prior pass, which is where the 3 come from. + @AGENTS.md. Re-review after every fix — a fix changes the artifact, so the prior + review no longer covers it. The hook merely notices, at commit time. **A fix that changes specified behaviour updates the spec in the same commit.** If a Gate-B fix alters something the approved spec pins down — an ordering, a terminal @@ -579,16 +835,31 @@ the reviewer, the other obliges the author. it; risk's *abuse* and security's *abuse paths* are **one lens carrying both labels**, not two questions. -Lenses are **different questions, not more passes.** The 3-pass floor, the Blocker/Major -filter, the file-first findings protocol and the clean-final-pass rule are unchanged. +Lenses are **different questions, not more passes** — they change what a pass asks, never +how many a cycle owes. The Blocker/Major filter, the file-first findings protocol and the +clean-final-pass rule are unchanged. The floor is not among them: it is no longer a fixed +number but derives from the profile and the cited set. -**Reading the profile — three cases, three answers:** +**Reading the profile — five cases, five answers:** +0. **The ordinary case**: every cited story is readable and its profile resolves → derive the + floor from it and run. Stated first because a partition of failures alone is not a + partition, and an earlier revision of this list omitted it. 1. The artifact **cites no story** → run unprofiled and **say so** in the pass. Artifacts predating this rule are the common case; stopping on them would halt in-flight work. 2. The cited story has **no profile line** → same: today's behaviour. -3. A profile is **present but unresolvable** → **stop and surface the cause**. That covers - the syntactic failures — unparseable line, a value outside the enums, two profile - blocks, a citation resolving to nothing — **and the semantic ones**: a `**Validation:**` +3. The cited path **does not yield a readable story file** → **stop and surface which of + these it was**, because each has a different fix: the path does not exist (a typo, or a + file moved or deleted); it exists but is not a regular file, a directory being the common + case; it is a symlink that does not resolve; or it exists and is a regular file but cannot + be read for permissions. **Report what you observed; no test order is prescribed here**, + because the obvious one is wrong — an ordinary existence or regular-file test follows a + symlink, so a dangling link reads as absent rather than as a broken link. + This case exists because none of the answers above is available to an agent that never + obtained the file: it can establish neither that a profile is absent nor that one is + present but unresolvable. +4. The story **is readable** and a profile is **present but unresolvable** → **stop and + surface the cause**. That covers the syntactic failures — unparseable line, a value + outside the enums, two profile blocks — **and the semantic ones**: a `**Validation:**` value disagreeing with `max(risk, security)`, or `+abuse-path` present without security `high` or absent with it. Only the **latest `mode override`** in the log, moving in a direction compatible with the current value, can explain such a mismatch — and if the @@ -608,8 +879,9 @@ trivial** (the pre-existing judgement, unchanged by profiles), **and** for a pro profile *changes*, and a skip changes no profile value. **A skip removes the review, never the evidence**, and what is owed follows the profile: a skipped **profiled** story runs the battery and lands its evidence entry beside the reason; a skipped **unprofiled** story -records the reason and the battery result and nothing more, because it owes no mode-derived -entry and keeps exactly today's judgement-based skip. +records the reason and the battery result, because it owes no mode-derived entry and keeps +exactly today's judgement-based skip. **Neither is excused the records every cycle owes** — +the provenance line, and a skip record in place of the curve. **A cycle citing several stories** aggregates along separate dimensions, never through one winning mode: the **battery runs once** for the cycle; **each cited _profiled_ story @@ -666,6 +938,29 @@ the current profile. Inside an active Gate-B cycle, fold the edit into the activ snapshot by amend — a non-`WIP` commit reads to the hook as the cycle closing and would discard the accumulated passes. +**While a gate is running, the floor derives from the current profile at each pass.** +Passes already run keep counting; closing requires the floor as currently derived. These +are pass-count rules, so they apply while a gate is running and are silent otherwise — +what governs when a gate runs is unchanged and deliberately not summarised here. + +**Any profile change costs at least one further pass**, in either direction and whether or +not the floor number moves, because the final clean pass must run under the current +profile — so no already-banked pass can be it. That further pass must itself be clean and +every other closure duty must be satisfied; it is one more pass, not a licence to close on +the next one. What a lowering drops is whatever the changed values drop, not a fixed pair: +a mode-only override changes the evidence obligations while leaving the axis-derived lens +sets alone, and security `high` → `standard` keeps the security lens set while changing +what evidence is owed. Every derived obligation is recomputed from the current profile. + +**The cited set is re-read at each pass, and the final clean pass runs against the current +set** — whenever its membership changes, not only when the floor number moves. Adding a +high-risk story to a set already at floor 3 leaves the number alone while adding that +story's lens set, its evidence obligations and its review scope; a pass run before it +joined did not cover them. Removing a story recomputes obligations from the current set +and so does remove that story's lenses and evidence duty — but it never discharges an +accepted in-set Blocker or Major: the acceptance put that finding in the fix set, not the +citation. + **What this does not do:** nothing checks which file a model actually read, whether the header changed mid-call, or whether the lens sets were appended. This is instruction-backed like the rest of §5; the detection is a reader comparing the pass against the story. @@ -673,10 +968,41 @@ like the rest of §5; the detection is a reader comparing the pass against the s ### Mechanics (reference) - **Severity:** Blocker (wrong/unsafe/breaks invariant) · Major (design flaw → rework) → both must resolve. Minor · Nit → collect, never iterate. + + **Deciding severity — one procedure. The subject list is illustration, not a second + rule.** Name what in the system consumes this text — whatever *acts* on it — and the + decision that act takes differently if the text is wrong. Both are required. If you + cannot name both, the finding is Minor or below: collect, never iterate. + + The exclusions are contract, not commentary. The reader must consume the text in the + system's *operation*, not in reviewing it — the review pass raising the finding is not + an in-system reader of the text it reviews; without this the test demotes nothing. + Gates remain legitimate readers of rule text they will later apply. A human reader never + satisfies the test — the prose exemption already prices that cost as non-gating. The + list of reader kinds is illustrative, not closed, because this ships into projects whose + readers we have never seen. The test sets a ceiling, not a floor, and never chooses + between Blocker and Major — the four definitions above still decide that. The instrument + carve-out is symmetric: an instrument finding keeps its severity whenever it shows the + instrument changes what a gate concludes about product behaviour — a false green, and + equally a false red or a check blocking a valid change. Rationale prose is Minor only + when no rule's application depends on it, not categorically: `docs/prompt-standards.md` + requires rules to carry their why, so rationale a reader must consult to apply a rule + passes the test. This removes arbitrariness, not judgement. Coverage-first is unchanged + — the reviewer reports every finding with severity and confidence; the filter is ours. + + This is the finding-level analog of the path-level prose exemption: one principle at two + granularities — text that *describes* the product versus text that *is* the product. + + **How this demotion bears on the loop-health measures — the per-pass counts, the finding + clusters and the stop thresholds — is not settled here, and this change does not settle it. + Until it is, a pass whose outcome would turn on that question reports the question and + stops rather than deciding it** — the same answer any unresolved gate question gets. - **Tool routing:** docs (spec/plan, incl. code snippets) → `mcp__codex__exec`; implemented diff → `mcp__codex__review`. Never `review` a doc — it reads the git range, not the text. -- **`baseSha`:** against main = merge-base with main (`headSha` = HEAD); +- **`baseSha`:** against main = merge-base with main (`headSha` = the full 40-character + object name `HEAD` resolves to at that moment, never the symbolic `HEAD` — see the + branch-agreement rule below for why); pre-commit, `baseSha` = HEAD is an empty range (HEAD..HEAD) — make a WIP commit and set `baseSha` to its parent. **Name that commit `WIP: …`** — the hook treats a `wip`-prefixed commit message as cycle-internal, so it neither fires a Gate-B STOP @@ -695,7 +1021,141 @@ like the rest of §5; the detection is a reader comparing the pass against the s destroyed exactly when the cycle closes. The final commit body is the durable record; a PR shows commit messages, so there is no second home to keep in sync. - **On squash-merge, copy every evidence entry and every human-exception record in the squash range into the squash body — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** + **Every cycle records one provenance line in its closing commit body** — default floor or + not, so an absent line is never ambiguous between "the default applied" and "someone forgot". + **One line per cycle**, so a change running five cycles records five. There is no informal + variant; anything quoting this form elsewhere quotes an instance of it, because the deferred + metrics work is intended to parse it — that consumer does not exist yet, and the form is pinned + now so that it can. + + ; floor per ; hook reminder threshold + + := "cycle " | "cycle none (pre-rule)" + := [a-z0-9]{8,16} + := [1-9][0-9]* + := "none" | "{" ("," )* "}" + each appears at most once; a repeated path, + with or without conflicting levels, is malformed + := " (level " ("0"|"1"|"2") ")" | " (unprofiled)" + := | + := [A-Za-z0-9._/-]+ contains no delimiter, quote or whitespace + := a double-quoted string, non-empty, whose only escapes are \" and \\ ; + a path containing a newline or other control + character is NOT representable — the cycle stops + and surfaces rather than emitting one + := "absent" | [1-9][0-9]* | "unusable" + + A filled instance, so the form is shown and not only described: + + cycle none (pre-rule); floor 3 per {docs/superpowers/stories/2026-08-28-review-loop-economics-pass-floor-story.md (level 2)}; hook reminder threshold absent + + It carries that cycle's **cycle field** — the nonce for any cycle started after these rules + ship, `none (pre-rule)` only for one that began before them — the **derived floor**, and **the + cited set that produced it**, each member with its level as a numeral. One floor and one set, + not an entry per story, since unanimity makes the floor a property of the set. It + distinguishes **a cited story with no profile** from **no story cited**. It records the + **workspace knob whenever the file exists**: the value if the observer read one, otherwise + `unusable`. **Nothing here describes what the hook does with that file, and the record does not + say why a value was unusable** — four successive attempts to state either were each wrong in a + different way, the last of them demonstrably so, and the rule for a claim needing a fourth + correction is to delete it. Whoever needs to know why reads the file and the hook. + + **These records are one contract, and a partial adoption breaks it.** The nonce, the slot + naming, the provenance line, the curve, this carry rule **and the unknown-start activation + semantics that say what a cycle owes when its starting rules cannot be established** depend on + one another, and the requirement is that the adopted definitions **agree**, not merely that all + of them are present: a curve + without a cycle field cannot be attributed, a slot rule without a nonce has nothing to key on, + and a carry rule naming records a project does not produce is inert. **A project whose text + carries some of them and not others, or carries all of them in versions that disagree, stops + and has a human complete, revert or reconcile the adoption before running a gate under it** — + disagreement is the harder case and gets the same stop, because a project holding two + definitions of a record has no single answer to what it owes — the same answer, and for the same reason, as a partial + adoption of the floor rule. + + **On squash-merge, copy every evidence entry, every human-exception record, the provenance lines, the curves and any skipped cycle's skip record TOGETHER WITH THE SKIP REASON IT POINTS AT in the squash range into the squash body — a skip record carried without its reason is a pointer into a body the squash has made unreachable — the squash commit is the only body the merge carries into `main`'s history, so anything left behind is unreachable from it.** + + **Every cycle records its own per-pass curve in its own commit body.** Gate B alone would + leave the dominant cost unrecorded — the loops this rule was built from are Gate-A loops. + + ; (passes , ): Findings . Blockers . Majors . + + := "Gate-A spec" | "Gate-A plan" | "Gate B" + := ("," )* strictly ascending, non-overlapping + :=

|

"-"

+

:= [1-9][0-9]* + := ("," )* exactly as many entries as enumerates + := 0 | [1-9][0-9]* | "?" "?" = the count is unrecoverable for that pass + := | ("; " )* + := "pass "

" " ("+" )* + := | | "undetermined" + "undetermined" means the model could not be determined; + a real model so named is written as + := [!-~]{1,} minus ; : , ( ) + " and space, and not the + literal "undetermined", which is reserved + printable ASCII only; a control character makes the + identifier unrepresentable, handled below + := a non-empty double-quoted string, same two escapes as ; + an identifier that cannot be determined, or cannot be + represented, is written `undetermined`, and the raw + value is NOT reproduced anywhere in the body, since a + commit message cannot safely carry one (NUL cannot + appear at all). The record does not say why a pass + reached `undetermined`, and nothing here describes how + a model identifier fails — same rule, same reason as + the knob above + + Two filled instances, one ordinary and one with a split logical pass: + + cycle none (pre-rule); Gate B (passes 1-3, codex): Findings 16,29,4. Blockers 4,15,0. Majors 5,2,1. + cycle 7b2q9xk4; Gate-A spec (passes 1,2, pass 1 codex+claude; pass 2 codex): Findings 5,0. Blockers 1,0. Majors 2,0. + + A skipped cycle writes `; : skipped (see skip reason)` and no counts. **The skip + reason it points at is the text immediately following it in the same commit body** — + adjacency is the link. The cycle field is not: every pre-rule cycle writes + `cycle none (pre-rule)`, so it identifies nothing when a body carries more than one. + **`` keys must be exactly the passes `` expands to, each once, ascending** — a + list that omits or repeats a pass is malformed, not partially informative — and **every model + contributing to a split logical pass is listed**, joined by `+`, since recording one of two is + the same loss as recording none. + + **Majors are recorded as well as Findings and Blockers**, because the severity rule moves the + Blocker/Major line rather than the total, so totals and Blockers alone could not show even a + change in the mix. **Subject categories are deliberately not recorded** — they are a judgement + per finding rather than a count, and the findings files carry the material. + + **One entry per valid pass**, and since incomplete passes are excluded while still consuming + pass numbers, the record **states which pass numbers it covers**. A valid zero-finding pass is + recorded as zero, never omitted. **A count that cannot be recovered is written `?`, never + guessed and never written as `0`** — a cycle keeps its identity through the nonce rather than + through its pass files, as far as distinct nonces allow, so a resumed cycle may know a pass happened and not what it found, and + zero and unknown are different facts. **`?` is per series**: a pass whose Findings are unknown + may still have usable Blocker and Major counts, and a reader excludes the unknown value from + the comparisons that read that series while keeping the pass's other series. + + A `full` Gate-B pass, separate `spec`/`quality` calls, and a single-branch recovery are + **branches of one logical pass** contributing one summed entry — **the curve counts logical + passes; the hook counts calls**, and where they differ the body says so **as prose beside the + curve**: neither grammar has a field for a call count, deliberately, since the count is a + property of how the pass was invoked rather than of what it found. **Both branches must be + issued against the same commit**, and that — not what they read — is what this rule + establishes. The result reports no reviewed revision, so there is nothing to read back and no + way to confirm from the reply what either branch actually looked at. What is available is the + request: **resolve `HEAD` to its full 40-character object name before each call and pass that + explicit value as `headSha`**, never the symbolic `HEAD`, which two calls can resolve + differently if a `WIP:` amend lands between them. Keep the value you passed **with that + branch's result**, and require the two kept values to be **exactly equal** before summing the + branches. Equal values mean the two calls were aimed at one commit; they are not evidence that + either branch reviewed it, and nothing available here would be. Record it as the **full 40-character hex object name**, since abbreviations are + ambiguous across repositories and across time; if it changed between them they are not one + pass, the completed branch is recorded as incomplete and excluded, and the later branch begins + a new one. Ending the pass is the conservative direction; merging two revisions would produce + one entry describing two different artifacts. + + **What the curve is worth, stated rather than implied.** Durable **across** cycles; **not + within** a running one, since the commit does not exist until the cycle closes. And + **author-written and unchecked** — nothing compares it against the validated pass files, so + whatever reads it reads a self-reported curve and must not present it as measurement. **Recording a human exception.** Where a human decides that something **no applicable rule required** was nonetheless worth skipping — an optional check this environment cannot run, a From 4760f4a78dbdd9a7c7cdce491dbdda62b4838306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Wed, 2 Sep 2026 16:19:19 +0200 Subject: [PATCH 115/117] docs(field-report): the Gate-B cycle, and the failure shape that closed it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file held the four Gate-A plan cycles. It now also holds the single Gate-B cycle over their combined diff, which closed the same way Plan C's did: on the clearly-stuck exit, with no clean pass and none claimed. Curve, extracted mechanically like the rest: findings 16, 29, 25, 25, 23; Blockers 4, 15, 6, 5, 10; Blocker+Major 9, 17, 15, 15, 16. Two things are recorded because nothing else would carry them. The pass-2 failure shape. Both branch files were structurally valid, but each of the two parallel reviewers reported the OTHER branch INCOMPLETE, mistaking its counterpart's legitimate file for a foreign write. The findings were acted on and the pass was not credited. A note in the next call's additionalContext fixed it and it did not recur. Nothing in the file protocol anticipates this: reviewType full runs two writers, and the rule protecting them from racing on one path never tells either that the other exists. The regeneration history. One mechanism — what the hook does with the floor knob — was described wrongly four rounds running, each fix a subtler version of the last. The tested counter-example that killed the third attempt is in the file: a knob file of 1, NUL, 2 is accepted as twelve in sh, dash and bash, because command substitution drops the NUL. The fourth round deleted the claim and found that the note explaining the deletion is itself a description of the hook. There is no version of that paragraph that survives its own rule, which is why the explanation lives here rather than in the product. Also recorded: the three clearly-stuck conditions as affirmed rather than assumed, and the require-withdraw pair that made the stop mandatory — pass 5 returned as Blockers the requirement the human had withdrawn the day before. --- .../2026-08-30-gate-a-rle-plan-cycles.md | 81 ++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md index f88fa10..5e711d9 100644 --- a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md +++ b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md @@ -1,7 +1,8 @@ -# Gate-A plan cycles `rle` — A, B, C and C1, the record preserved +# Review cycles `rle` — the four Gate-A plan cycles and the Gate-B cycle, preserved Companion to `2026-08-29-gate-a-rle-cycle-evidence.md`, which holds the **spec** cycle. This file -holds the **four plan** cycles for the same change. Their validated findings files live under +holds the **four plan** cycles for the same change, and — since 2026-09-02 — the single +**Gate-B** cycle that reviewed their combined diff. Their validated findings files live under `.context/codex-reviews/`, which is gitignored and whose slots are reused, so this is the durable record — the same reason the spec file and `2026-08-26-fic2-cycle-evidence.md` exist. @@ -123,6 +124,82 @@ now failed to converge under Gate A twice, at two very different sizes.** Where that evidence points is Gate B: the same eight replacements, read as a diff against the files they changed, are a question with an answer. +## The Gate-B cycle — five passes, closed as not converged + +The four cycles above are Gate-A. This one is the single Gate-B cycle over the combined +A+B+C diff, and it closed the same way Plan C's did: **on the clearly-stuck exit, with no +clean pass and none claimed.** Numbers extracted mechanically from +`.context/codex-reviews/gate-b-{spec,quality}-rle-pass-{1..5}.md`, the same way the rest of +this file was taken. + +``` +pass 1 2 3 4 5 +Findings 16 29 25 25 23 +Blockers 4 15 6 5 10 +Majors 5 2 9 10 6 +B+M 9 17 15 15 16 +``` + +**Pass 2 is discounted and not counted toward the floor.** Both branch files were +structurally valid — correct terminators, exact counts, six fields, no stray lines — but the +reply contradicted itself: each of the two parallel reviewers reported *the other* branch +`INCOMPLETE`, having mistaken its counterpart's legitimate file for a foreign write. The +findings were acted on, because a file that passes every structural check is provably not the +partial list the rule guards against; the pass was not credited, because an `INCOMPLETE` reply +is an incomplete pass by rule. **A protocol note in the next call's `additionalContext` — +"finding the other branch's file present is EXPECTED and is not a collision" — fixed it, and +it did not recur in passes 3, 4 or 5.** This failure shape is worth naming because nothing in +the file protocol anticipates it: `reviewType: full` runs two writers, and the rule that +protects them from racing on one path does not tell either that the other exists. + +### What made it stick: one mechanism, four wrong descriptions + +Every round's Blocker/Major cluster traced to prose describing **what the hook does with the +`.context/codex-gate.floor` knob**. The corrections, in order: + +| round | what was written | why it was wrong | +|---|---|---| +| 1 | "trims trailing newlines"; "exceeds the hook's accepted maximum" | the hook runs `tr -d '[:space:]'`, and it defines no maximum | +| 2 | rewritten against the hook source, cause by cause | it gates on `-f` before reading, so a broken symlink never reaches the read, and a failed `cat` is indistinguishable from an empty file | +| 3 | walkthrough deleted, one summary sentence kept: "an unusable value leaves the hook's default standing" | false. Tested: `printf '1\0002' > knob.bin` is **accepted as twelve** in `sh`, `dash` and `bash` — command substitution drops the NUL, and `1` `2` becomes `12` | +| 4 | the claim deleted entirely, `` dropped from the grammar | the note explaining *why* the description was deleted is itself a description of the hook | + +That last row is the one to remember. **There is no version of that paragraph that survives +its own rule** — the remedy consumes any explanation of why the remedy was applied. +`docs/prompt-standards.md` item 11 already prescribes deletion after a fourth correction; what +this cycle adds is that the deletion has to include its own rationale, and the rationale then +lives here, in a field report, where describing the hook is the point rather than a claim the +product makes. + +The capability cost was accepted knowingly on 2026-09-02: the provenance record now says +**that** a knob was unusable and no longer **why**. Whoever needs why reads the file and the +hook. + +### Why the exit was taken rather than a sixth round + +All three §5 conditions were affirmed, not assumed: + +- **A plateau across passes.** Blocker+Major never returned to its pass-1 level of 9 and rose + on the last pass. +- **Coverage affirmatively sufficient.** Across five passes the reviewers covered both prompt + copies, the spec, the story, all three plans, the hook source and the user docs. No + materially unreviewed area is known — and this file states that as a judgement, which the + exit requires, rather than inferring it from a low count. +- **Blocker/Major regenerating across genuine repair attempts.** Each round's fix produced the + next round's findings on the same mechanism, four times. + +One further signal, and it is the one that settled it: **pass 5 returned as Blockers the very +requirement the human had withdrawn the day before** — that `unusable` and `undetermined` +distinguish their causes. The reviewer is not wrong that a collapsed record is less useful. +But a gate cannot clear a finding whose resolution the human has already declined, and a loop +that re-raises a decided question is no longer measuring the artifact. That is a +require↔withdraw pair in the §5 sense, and it is what made the stop mandatory rather than +discretionary. + +Open findings and their dispositions, including the two marked as a chosen cost rather than a +missed defect, are in `.context/codex-reviews/gate-b-rle-pass-5-dispositions.md` — which is +git-ignored, so what survives a clone is this section. + ## The superseded single-plan artifact `docs/superpowers/plans/2026-08-29-review-loop-economics.md` is the single-plan version of this From f387ac87535803e0fe6261b2289e45f17aa5ed91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Wed, 2 Sep 2026 18:12:47 +0200 Subject: [PATCH 116/117] fix(review): six findings from PR #26's bot round MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit posted fourteen claims. Six were true and actionable here, three were true but out of scope, three were dismissed, and two are escalated to the human because they contradict settled decisions. Greptile posted a summary with no defect claim. Every thread has a reply carrying its verdict. Validated by me rather than by a subagent: the PR touches CLAUDE.md and plugins/**, so process-pr-review's instruction-path precheck fires and subagent triage is skipped for the whole PR. WHAT REVIEWED THESE FIXES: nothing did. They are ordinary commits on a closed cycle, per the author's instruction for this round, and the disclosure is the substitute for a review. The battery is green over them, which is not a review. One of the six changes shipped prompt text and is named below so a reader knows which. The six: - The dark-factory vision document, on this same branch, still said the kit "has a fixed 3-pass floor" and listed the review-economics story as "in flight", twice. This PR ships the derived floor, so all three sentences were false on merge. Found by the bot, missed by me: I ran AGENTS.md's "which existing statements does this diff falsify?" lens against the changed paths, and the falsified file is not one of them. - SHIPPED PROMPT TEXT: the branch-agreement rule bound only headSha, so two branch calls could share a head over different bases and still be summed. It now requires baseSha and headSha both. CLAUDE.md and the scaffolded template, in parity. - Plan A said "Three Gate-A cycles", which is wrong in every counting: one per artifact, five in the end once Plan C was split and C1 got its own. It now points at the field report, which carries them all. - The spec said running the three cycle kinds produces three records. A multi-plan change runs one Gate-A plan cycle per plan, so it produces 2 + number_of_plans. Same defect the prompts carried; missed with them. - Plan C task 25's extraction loop was `while [ -f ... ]`, stopping at the first gap, while the curve grammar explicitly permits a gapped SPEC. It now enumerates and sorts. - Task 25's completion assert tested only that the provisional wording was gone. A replacement that deleted it and wrote no closed record would have passed. A positive assertion with a cardinality floor now runs beside it. Two hardening rows appended. docs-drift reaches its seventh occurrence with a new sub-shape: the standing lens searches the diff, and a document on the same branch can be falsified by a change it does not contain. The lens now names the branch as its surface. verification-masks-failure reaches its fifth: an absence assert is half a check whenever the edit it guards is a replacement, and the missing half is always the same one. Three valid findings are recorded in todos.md rather than fixed: two in the superseded single-plan artifact and one in the dissolved Plan C1, none of which is executed. Also queued there: shipping the both-branches- misread-each-other note as standing prompt text, and the prompt-standards item-11 amendment for a deletion that must take its rationale with it. One dismissal worth naming: the bot reported that a multi-file `grep -cF` wrapped in a `test` could pass with only one file matching. There is no `test` at either cited site — both are bare greps printing two per-file counts, which the plans state as "1 and 1" for a human to read. --- CLAUDE.md | 5 +-- docs/hardening-log.md | 2 ++ ...8-29-review-loop-economics-plan-a-rules.md | 5 ++- ...30-review-loop-economics-plan-c-rollout.md | 9 +++-- ...2026-08-28-review-loop-economics-design.md | 7 ++-- .../specs/2026-08-30-dark-factory-vision.md | 19 ++++++----- .../dev-workflow/commands/workflow-init.md | 5 +-- todos.md | 34 +++++++++++++++++++ 8 files changed, 67 insertions(+), 19 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 57476ef..4458020 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -960,8 +960,9 @@ like the rest of §5; the detection is a reader comparing the pass against the s request: **resolve `HEAD` to its full 40-character object name before each call and pass that explicit value as `headSha`**, never the symbolic `HEAD`, which two calls can resolve differently if a `WIP:` amend lands between them. Keep the value you passed **with that - branch's result**, and require the two kept values to be **exactly equal** before summing the - branches. Equal values mean the two calls were aimed at one commit; they are not evidence that + branch's result**, and require the two kept values — **`baseSha` and `headSha` both**, since a + range is selected by both ends and two calls can share a head over different bases — to be + **exactly equal** before summing the branches. Equal values mean the two calls were aimed at one commit; they are not evidence that either branch reviewed it, and nothing available here would be. Record it as the **full 40-character hex object name**, since abbreviations are ambiguous across repositories and across time; if it changed between them they are not one pass, the completed branch is recorded as incomplete and excluded, and the later branch begins diff --git a/docs/hardening-log.md b/docs/hardening-log.md index fbf874a..45b182c 100644 --- a/docs/hardening-log.md +++ b/docs/hardening-log.md @@ -123,3 +123,5 @@ escape `\|`, one line), `source` (gate-a|gate-b|bot|manual), | 2026-08-16 | docs-drift | sixth occurrence: PR #24 (CodeRabbit) — `docs/coding-workflow.md` and `docs/sparring-briefing.md` both told the reader to record the reviewer model by reading "the configured value at that moment", while the same section's own timing facts said `mcp-codex-dev` caches its model chain per project root. Two sentences in one document disagreeing, and the wrong one was the actionable instruction: after any model edit the config names a model the running server is not using, so a pass record built from it misstates who reviewed — which is the only thing that makes reviewer-family independence checkable | bot | major | 1 prose | Both documents now name the model the pass *ran under* as the thing recorded, state the cache boundary exactly (launch root at startup, any other root on its first call, cached until restart, and an unseen root is the exception where a post-startup edit does take effect), and name a DETERMINISTIC probe rather than "check the config": `mcp__codex__health` with the same `workingDirectory` as the gate call, reading `checks.config.effective.model`, which is the cached per-root resolution the gate call itself uses. NOT ESCALATED past the 2026-07-26 `2 lint` row: that check guards prose count claims and cannot reach a stale mechanism description, the over-escalation those rows warn about. NO DETERMINISTIC RUNG EXISTS for this: nothing can tell that a sentence about a cache is stale, and the probe is a recipe a human runs, not a check — it raises the floor by making the right value obtainable, and does not close the class. Sibling row this same date under `unverified-enforcement-claim` covers the empty-commit half of the same PR review | | 2026-08-17 | prompt-missing-stop-condition | first row of this base class here: §5's loop told the reader to keep looping and never said what a loop may swallow, so absorbing a finding that opened a new contract question and handing back a three-line repair-of-a-repair were equally defensible readings — field-minted by the kit's heaviest consumer across ~150 gate passes, and its own reason for the rule is that absorbing a contract question spends a decision the loop was never given | manual | major | P std | CLAUDE.md §5 loop paragraph + the same block in the workflow-init inline template. Guard: the absorb-vs-stop rule and the sentence bounding it — a finding correcting the correction just made is absorbed, one opening a new structural or contract question stops the loop, novelty and not size decides, and the stop is NOT an exit from the gate (floor, Blocker/Major filter and clean-final-pass all stand). Does NOT guard which findings count as structural, and says nothing about pass counts. | | 2026-08-17 | prompt-vague-criteria | first row of this base class here: §5 named "clearly stuck" as the loop's terminal state and never said how to recognize one, so a plateauing artifact could burn passes with nobody entitled to call it — in the field one 2848-line spec ran 34 Gate-A spec passes, and over the 19 that were measured the count fell from 43 to an oscillation between 2 and 7 while Blockers fell from 8 to 0-1, i.e. the substance converged and the number never did | manual | major | P std | CLAUDE.md §5 loop paragraph + the same block in the workflow-init inline template. Guard: the recognition rule — after about six passes read the Blocker curve rather than the total, and Blockers at 0-1 with every remaining finding individually fixable IS the stuck state — plus the smaller-specs guidance that follows from the same measurement. Does NOT guard a size threshold (deliberately unmeasured and shipped as guidance), and does not say where a plateau begins. | +| 2026-09-02 | docs-drift | seventh occurrence: PR #26 (CodeRabbit) — the dark-factory vision document, sitting on the same branch, still said the kit "has a fixed 3-pass floor", listed the review-economics story as "in flight" twice, and made both claims about the very change the branch ships. Every sentence was true when written and false the moment the branch merged. NEW SUB-SHAPE, and it is why this row exists rather than a note: the standing lens was carried on this cycle and the falsified file is one the diff never touches — it is not in the changed-path set, so nothing scoped to the diff could reach it, and the lens's own recipe (grep for where each changed value is described elsewhere) was run against `CLAUDE.md`'s vocabulary and not against the branch's other documents | bot | major | 1 prose | The lens now names the branch, not the diff, as its search surface: a document added or edited **anywhere on the same branch** can be falsified by a change it does not contain, and a co-shipped design document describing the current state of the thing being changed is the likeliest instance. PRIOR ROW: 2026-08-16 docs-drift (1 prose), and 2026-08-04 (P std) whose guard asks what the diff changes the size, value or position of — this finding is INSIDE that guard and outside its reach at once: the value did change and was described elsewhere, but "elsewhere" was scoped to the changed paths by everyone who ran it, including me. Repaired at the same rung, not escalated. NO DETERMINISTIC RUNG EXISTS: nothing can tell that a design document's description of current state went stale, and widening the grep to the whole branch is a recipe a human runs. It raises the floor and does not close the class | +| 2026-09-02 | verification-masks-failure | fifth occurrence: PR #26 (CodeRabbit) — Plan C task 25's completion assert tested only that the provisional wording was ABSENT (`grep -c … -eq 0`). A replacement that deleted the provisional passage and wrote no closed record at all would have passed it, which is precisely the outcome the task exists to prevent. The task did run correctly this cycle, so the mask never fired; it was found by reading, not by failing | bot | major | 4 test | The assert gained a positive arm beside the negative one: the closed curves must be PRESENT, with a cardinality floor (`grep -cE '^Findings( +[0-9]+,?)+' … -ge 3`). PRIOR ROWS under this fingerprint all share one shape — a check whose only assertion is that something is gone. WHAT GENERALIZES: an absence assert is half a check whenever the edit it guards is a replacement rather than a deletion, and the missing half is always the same one. This row's remedy is specific to task 25; the general form belongs to the loop-rule consolidation story, which owns the plan-assert conventions | diff --git a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md index ca813d6..3b28f5b 100644 --- a/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md +++ b/docs/superpowers/plans/2026-08-29-review-loop-economics-plan-a-rules.md @@ -26,7 +26,10 @@ Plan A implements §2, §2.1, §2.2, §2.4, §3, and §10 in part. | **B** | the provenance line, the per-pass curve, the cycle nonce, slot naming | §2.3, §4, §5, §6 | | **C** | rollout: falsified sentences, packaging, the evidence pack, the review loop | §7, §8 | -**Three Gate-A cycles, one Gate-B cycle** over the combined A+B+C diff, run and closed by Plan C. +**One Gate-B cycle** over the combined A+B+C diff, run and closed by Plan C. The Gate-A +cycles behind it are one per artifact: the spec's, and one per plan — five in the end, since +Plan C was split and C1 got its own before being dissolved. Their record is +`docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md`. **Execution order A → B → C; Plan B may not open before Plan A's Gate-A loop closes.** **Plan C inherits these obligations**, which lived in a Gate-B section Plan A no longer has. A diff --git a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md index 0c5f520..948a439 100644 --- a/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md +++ b/docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c-rollout.md @@ -1406,8 +1406,10 @@ explanatory prose, so Gate B is N/A. - [ ] **Extract Plan C's final curve mechanically**, the way that file's own numbers were taken: ```bash -n=1 -while [ -f ".context/codex-reviews/gate-a-plan-planc-pass-$n.md" ]; do +# Enumerate rather than counting up: an incomplete pass can leave a gap, and the curve +# grammar permits a gapped , so stopping at the first missing file drops every +# later pass from the record. +for n in $(ls .context/codex-reviews/ | sed -n 's/^gate-a-plan-planc-pass-\([0-9]*\)\.md$/\1/p' | sort -n); do f=$(grep -cE '^(BLOCKER|MAJOR|MINOR|NIT) \|' ".context/codex-reviews/gate-a-plan-planc-pass-$n.md") b=$(grep -c '^BLOCKER' ".context/codex-reviews/gate-a-plan-planc-pass-$n.md") m=$(grep -c '^MAJOR' ".context/codex-reviews/gate-a-plan-planc-pass-$n.md") @@ -1424,6 +1426,9 @@ done ```bash test "$(grep -cF -- "Open at 5 passes when this file was written" docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md)" -eq 0 || { echo "PROVISIONAL WORDING SURVIVES"; exit 1; } +# Absence is half the check: a replacement that deletes the provisional text and writes no +# closed record would pass it. Assert the closed record positively, and its cardinality. +test "$(grep -cE '^Findings( +[0-9]+,?)+' docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md)" -ge 3 || { echo "CLOSED CURVES MISSING"; exit 1; } ``` - [ ] **Commit it alone**, ordinary message, no `WIP:` prefix — the cycle is already closed and diff --git a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md index 5f97318..5020c06 100644 --- a/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md +++ b/docs/superpowers/specs/2026-08-28-review-loop-economics-design.md @@ -343,9 +343,10 @@ Both shipped records carry a **cycle field**, because a record that cannot be at cannot be told apart from another cycle's when several are read together. **That is a limitation, not a disqualification** — the `fic2` baseline is entirely pre-rule and is used precisely because a human knows which cycle it came from. What attribution buys is that a *later* reader does not have -to. **§5 defines three cycles — the Gate-A spec loop, the -Gate-A plan loop and the Gate-B cycle — so a run of all three produces three cycle fields, and for -post-rule cycles three distinct nonces.** A **pre-rule** cycle has no nonce — §10's activation rule is what makes a cycle pre-rule; its field is the +to. **§5 defines three cycle *kinds* — the Gate-A spec loop, the +Gate-A plan loop and the Gate-B cycle — and one cycle field is produced per cycle *run*, not per +kind: a change carrying several plans runs a Gate-A plan cycle for each, so it produces +`2 + number_of_plans` records, and for post-rule cycles that many distinct nonces.** A **pre-rule** cycle has no nonce — §10's activation rule is what makes a cycle pre-rule; its field is the reserved `none (pre-rule)` and its records are not cycle-attributable; the activation rule that creates that case is §10's. Everything below describes **post-rule cycles**, which is every cycle started after the implementation commit. diff --git a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md index 181be84..843ecbc 100644 --- a/docs/superpowers/specs/2026-08-30-dark-factory-vision.md +++ b/docs/superpowers/specs/2026-08-30-dark-factory-vision.md @@ -289,10 +289,10 @@ Every new pool item is classified before anything else. Dimensions 1–4 exist in the intake skill today; 5–8 are new: 1. **Size** — story, or epic that must be split. -2. **Risk/security profile** — drives lenses and evidence mode today; it - drives the *pass floor* only once build step 1 lands (shipped CLAUDE.md - has a fixed 3-pass floor, and lenses are different questions, not more - passes). +2. **Risk/security profile** — drives lenses, evidence mode **and the pass + floor**. The floor became profile-derived when build step 1 shipped + (0.11.0); before that the kit had a fixed 3-pass floor, and this line said + so. Lenses remain different questions rather than more passes. 3. **Completeness** — too thin → one question round back to the human (several targeted questions in it, as the shipped intake skill does); nothing is invented. @@ -326,14 +326,15 @@ proceeds, the breaking part waits on the meta-story). 7. Fresh context per stage is convention, not enforced — long sessions measurably degrade. *(partial)* 8. Same-repo parallelism — N worktrees × 1 agent works today; the record/nonce - rules of the in-flight review-economics story are the foundation for more. - *(in flight)* + rules the review-economics story shipped in 0.11.0 are the foundation for + more. *(foundation shipped; the parallelism itself is not)* ## 7. Build path (each *leaf* is one story; numbered entries are ordering groups) -1. Finish the review-economics story (in flight) — floors by profile, severity - calibration, measurable records. Without calibrated review economics every - factory is a token furnace. +1. ~~Finish the review-economics story~~ — **shipped in 0.11.0**: floors by + profile, severity by reachable consequence, and the two measurable records. + Without calibrated review economics every factory is a token furnace, which + is why this was step 1. 2. **Measure before automating further** — three ordered leaves, not one story: - **2a** loop-rule consolidation (the successor story to step 1). diff --git a/plugins/dev-workflow/commands/workflow-init.md b/plugins/dev-workflow/commands/workflow-init.md index 391ca17..d72087e 100644 --- a/plugins/dev-workflow/commands/workflow-init.md +++ b/plugins/dev-workflow/commands/workflow-init.md @@ -1144,8 +1144,9 @@ like the rest of §5; the detection is a reader comparing the pass against the s request: **resolve `HEAD` to its full 40-character object name before each call and pass that explicit value as `headSha`**, never the symbolic `HEAD`, which two calls can resolve differently if a `WIP:` amend lands between them. Keep the value you passed **with that - branch's result**, and require the two kept values to be **exactly equal** before summing the - branches. Equal values mean the two calls were aimed at one commit; they are not evidence that + branch's result**, and require the two kept values — **`baseSha` and `headSha` both**, since a + range is selected by both ends and two calls can share a head over different bases — to be + **exactly equal** before summing the branches. Equal values mean the two calls were aimed at one commit; they are not evidence that either branch reviewed it, and nothing available here would be. Record it as the **full 40-character hex object name**, since abbreviations are ambiguous across repositories and across time; if it changed between them they are not one pass, the completed branch is recorded as incomplete and excluded, and the later branch begins diff --git a/todos.md b/todos.md index fde5722..1133bb9 100644 --- a/todos.md +++ b/todos.md @@ -767,3 +767,37 @@ backlog. fixtures. Part of that story: `ci.yml`'s `koalaman/shellcheck:v0.11.0` is tag-pinned by luck, not by the gate — a tag can be repointed, so digest-pinning it belongs to whoever takes the Docker surface on. + +## From PR #26 — backlog only, nothing implemented here + +- **Both-branches-misread-each-other.** `mcp__codex__review` with `reviewType: full` runs two + reviewers in parallel from one call. §5's file protocol keeps them from racing on a single + path, but never tells either that the other exists — so on PR #26's Gate-B pass 2 each read + its counterpart's legitimate findings file as a foreign write and reported the other branch + `INCOMPLETE`. Both files were structurally valid; the pass was discounted anyway, because an + `INCOMPLETE` reply is an incomplete pass by rule. One line of `additionalContext` fixed it + and it did not recur across three further passes. **Backlog:** ship that line as standing + prompt text in §5's Gate-B section and the scaffolded template, so it is not rediscovered per + cycle. Record: `docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md`. + +- **The self-consuming deletion — `prompt-standards.md` item 11 amendment.** Item 11 says to + delete a claim about a mechanism after a fourth correction rather than refine it a fifth + time. It does not say what happens to the *rationale* for the deletion — and on PR #26 that + rationale was itself a description of the mechanism (the tested NUL counter-example), so it + fell to the same rule. There is no version of that paragraph that survives its own rule. The + explanation was moved to a field report, where describing the hook is the point rather than + a claim the product makes. **Backlog:** amend item 11 to say the deletion takes its rationale + with it, and name the field report as the rationale's home. + +- **Valid findings from PR #26 recorded as out of scope** (per `process-pr-review` item 2 — + pre-existing and larger than the code this PR touches, so terminal there, not hardened): + - `docs/superpowers/plans/2026-08-29-review-loop-economics.md` is the **superseded** + single-plan artifact. CodeRabbit found two real defects in it — an impossible cycle + classification across Tasks 1–9, and `git commit --amend --no-edit` in Tasks 2–5, which + the hook does not recognize as a WIP amend and which therefore resets the cycle. Both are + true. The file is recorded as history and is not executed; marking superseded artifacts in + place is a convention gap this repo already owns and defers. + - `docs/superpowers/plans/2026-08-30-review-loop-economics-plan-c1-user-docs.md` — commands + whose output is tested do not check status, so a failed `git diff` with empty output can + select "ALREADY APPLIED AND COMMITTED". True, and C1 was dissolved into the rollout on + 2026-09-01 without being executed. Fix it if C1 is ever revived. From 1b07c7b0d39a5dbf85eab91c121cb172886834a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20S=C3=A4nger?= <20968534+dsnger@users.noreply.github.com> Date: Wed, 2 Sep 2026 18:32:17 +0200 Subject: [PATCH 117/117] docs(field-report): the seven decisions behind these cycles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two of them — citation completeness accepted as a residual risk, and no sixth Gate-B pass — answer findings a reviewer raised and Daniel declined. Recorded so a later reader meets them as decisions rather than as defects nobody noticed. The other five were scattered across commit bodies; a squash carries those, this file is carried by git on its own. Docs-only, prose-exempt per CLAUDE.md §5. Nothing reviewed it. --- .../2026-08-30-gate-a-rle-plan-cycles.md | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md index 5e711d9..f272c5a 100644 --- a/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md +++ b/docs/field-reports/2026-08-30-gate-a-rle-plan-cycles.md @@ -200,6 +200,42 @@ Open findings and their dispositions, including the two marked as a chosen cost missed defect, are in `.context/codex-reviews/gate-b-rle-pass-5-dispositions.md` — which is git-ignored, so what survives a clone is this section. +## The decisions behind these cycles + +All Daniel's, in order. Recorded here because commit bodies are carried by the squash and +this file is carried by git on its own, and because two of them are choices a later reader +would otherwise read as oversights. + +1. **2026-09-01 — C1 dissolved** into the rollout after its own Gate A stopped on two tells; + no third prose plan; **plan-level Gate A skipped** for the sentence replacements after two + non-convergences, with verification moved to the artifact (asserts plus the combined Gate + B); execution ordered **A → B → C** on the sequencing finding. +2. **2026-09-02 — Plan C Tasks 19 and 20 dropped.** The deterministic slot discriminator is + not shipped; a general production goes to the loop-rule consolidation story. +3. **2026-09-02 — the `rle` slot infix is a recorded plan-local naming exception** under the + old rules that govern this cycle. No shipped rule admits the form. The cycle is pre-rule + and cannot mint a nonce, and the bare family already held 30 files that delete-before-call + would have destroyed. +4. **2026-09-02 — the knob-cause vocabulary and the model-cause obligation withdrawn** from + the grammar and both prompt copies. Accepted capability cost, stated: the record says + **that** a knob was unusable, no longer **why**. Whoever needs why reads the file and the + hook. +5. **2026-09-02 — close on the clearly-stuck exit** rather than a sixth round. +6. **2026-09-02 — citation completeness accepted as a documented residual risk.** A + contributor who omits a high-risk story and cites only a level-0 one gets a floor of 1, and + nothing verifies the header is complete. That is the settled header-is-sole-authority + design rather than an oversight: a completeness check would need an independent source of + truth for what *should* have been cited, and none exists. **The countermeasure is the + sampled human audit, not a parser.** No mechanism was built. +7. **2026-09-02 — no sixth Gate-B pass** after the closing repairs. What the post-close + commits carry instead: the honesty disclosure naming exactly which changes no pass + reviewed, a green battery over them, and two independent bot reviews across the whole PR. + None of that is a Gate-B pass, and the commit bodies say so. + +Decisions 6 and 7 both answer findings a reviewer raised and the human declined. They are +recorded as chosen costs so that a later reader meets them as decisions rather than as +defects nobody noticed. + ## The superseded single-plan artifact `docs/superpowers/plans/2026-08-29-review-loop-economics.md` is the single-plan version of this