pi-gauntlet 4.4.1 → 4.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## v4.4.2 - 2026-07-15
4
+
5
+ Condense the finish-time conformance disposition gate to a dense, human-readable
6
+ decision list - the corrective second pass over v4.4.1's disposition work, which
7
+ delivered the right behavior but bloated the render and the freshness protocol.
8
+
9
+ - **Finish gate (`finishing-a-development-branch`):** carried-open conformance
10
+ findings now render as a dense one-line-per-decision list - each line carries a
11
+ plain-language title, the unresolved point, its question, and an inline recommended
12
+ choice - with a two-option reply (`1` = every recommendation; `2:` =
13
+ recommended-except comma-separated `<handle>=<choice>` overrides). Replaces the
14
+ terse `Gn` tokens and the flat D1-D5 per-gap menu. Single-concern gaps show no
15
+ `/Cn` symbol; `UNAUTHORIZED` items note availability deviations inline; the
16
+ zero-gap path still goes straight to branch options with no sign-off.
17
+ - **Freshness (`verification-before-completion/reference/conformance-check.md`,
18
+ `finishing-a-development-branch`, `subagent-driven-development`):** the four-field
19
+ audit-identity/hash protocol (duplicated across three files) collapses to a
20
+ `status`/`audited-base` sentinel plus a two-command working-tree drift check;
21
+ any change or doubt re-audits. The durable `## Closure / conformance` handoff
22
+ header drops `audited-spec-path`/`audited-diff-hash`/`audited-spec-hash`.
23
+ - **De-duplication:** the disposition-availability table, the `UNAUTHORIZED`
24
+ question, the `recommended: none` preflight, and the fix loop now live only in
25
+ `conformance-check.md`; the finish gate and SDD consume them by link.
26
+
3
27
  ## v4.4.1 - 2026-07-13
4
28
 
5
29
  Gates ratify, not ceremony: two gauntlet gates now auto-apply mechanical/drafting
package/README.md CHANGED
@@ -38,7 +38,7 @@ Concretely, one change through the gauntlet:
38
38
  1. You describe the change. **`brainstorming`** sets up an isolated worktree, explores the codebase, and turns your description into a written spec. A multi-model critique runs on it automatically. **You read and approve the spec - human gate 1.** No implementation code exists yet.
39
39
  2. **`writing-plans`** decomposes the approved spec into atomic, independently-verifiable tasks, grouped into parallel waves where they don't touch the same files.
40
40
  3. **`subagent-driven-development`** executes the plan one task at a time, each in a fresh subagent, behind spec-compliance review then code-quality review. TDD-locked: red, green, refactor.
41
- 4. **verify**: a whole-diff code review, then the **conformance gate** - a subagent reads the finished code and docs against your *original words* from step 1, not the plan, and reports per-requirement: delivered, partial, missing, drifted, or unauthorized. This gate is machine-blocked from being skipped. Requirement-restoring gaps (code drifted from the approved spec) auto-close through an isolated fix-and-re-audit loop with no prompt; only decisions that would *rewrite* your approved spec - accept, rescope, or removing unrequested code - are deferred to the finishing gate for your call.
41
+ 4. **verify**: a whole-diff code review, then the **conformance gate** - a subagent reads the finished code and docs against your *original words* from step 1, not the plan, and reports per-requirement: delivered, partial, missing, drifted, or unauthorized. This gate is machine-blocked from being skipped. Compatible executable recommendations auto-run through an isolated fix-and-re-audit loop with no prompt; anything still open surfaces as a dense list - one line per decision, plain-language, with its recommended choice inline. Reply `1` to take every recommendation, or `2:` with per-item overrides; a current `CONFORMS` / no-concerns result goes straight to the branch options with no extra conformance sign-off.
42
42
  5. **`finishing-a-development-branch`**: squash, PR, keep, or discard. **Human gate 2** - the only other decision you make.
43
43
 
44
44
  ```mermaid
@@ -74,7 +74,7 @@ pi-gauntlet is **opinionated**: every non-trivial change rides this one pipeline
74
74
  | --- | --- |
75
75
  | Gate | A machine-enforced checkpoint between phases (e.g. `complete verify` is blocked until conformance review has run). Not a suggestion. |
76
76
  | Spec council | Multi-model critique of the spec before you see it (`roasting-the-spec`); falls back to a single-model critique if no council is configured. |
77
- | Conformance gate | The closing check: does the delivered code + docs match your *original prompt*, not the derived plan? Per-requirement verdict; auto-fixes requirement-restoring gaps, defers spec-rewriting decisions (accept/rescope/unauthorized) to the finishing gate. |
77
+ | Conformance gate | The closing check: does the delivered code + docs match your *original prompt*, not the derived plan? Compatible executable recommendations auto-fix first; anything still open renders as a dense one-line-per-decision list with each recommended choice inline. Reply `1` to accept all recommendations or `2:` with per-item overrides; a current `CONFORMS` / no-concerns handoff goes straight to branch options with no extra sign-off. |
78
78
  | Wave | A batch of plan tasks that don't touch the same files, dispatched to implementers in parallel. |
79
79
  | Overrides file | `.pi/gauntlet-overrides.md` - where you put project-specific detail the generic skills don't know (CI command, worktree wrapper, routing rules). |
80
80
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "4.4.1",
3
+ "version": "4.4.2",
4
4
  "description": "Opinionated, gated workflow skills, subagent personas, and runtime extensions for the pi coding agent.",
5
5
  "author": "Jacek Juraszek",
6
6
  "type": "module",
@@ -72,46 +72,91 @@ Or ask: "This branch split from main - is that correct?"
72
72
 
73
73
  ### Step 3.5: Closure / Conformance Disposition Gate
74
74
 
75
- This is an **enforced per-gap disposition gate**, not a surface-only notice. The user is about to choose how to ship; every carried-open gap must get an explicit disposition here, before Step 4's menu. Tests prove the code runs; conformance proves it does what was requested different gates.
75
+ This is an **enforced disposition gate**, not a surface-only notice. The user is about to choose how to ship; every carried-open decision must get an explicit disposition here, before Step 4's menu. Tests prove the code runs; conformance proves it does what was requested - different gates.
76
76
 
77
- **If no conformance check has run in this flow** (e.g., ad-hoc work that landed without an execution skill): say so, then dispatch a fresh-context `conformance-reviewer` against the origin (spec + verbatim prompt + full diff vs base) per `verification-before-completion/reference/conformance-check.md`. Closing the loop is cheap relative to shipping unverified intent. Then proceed below with its verdict.
77
+ `verification-before-completion/reference/conformance-check.md` is **canonical** for the durable handoff schema, concern-decomposition rules, the single disposition-availability table, the `UNAUTHORIZED` question text, the `recommended: none` preflight, the freshness rule, and the concern-scoped fix projection. This step owns only **render, response, and execute-order** and consumes the rest by link - it does not restate the availability table, the `UNAUTHORIZED` question, or the preflight prose.
78
78
 
79
- **If verdict is `CONFORMS`** (no carried-open gaps): report `Closure / conformance: CONFORMS`. If the run auto-applied any fixes, also surface the flat `auto-applied fix commits: <Gn: SHA>, ...` index from the `## Closure / conformance` block with a one-line revert offer (see "Revert semantics") a gap that auto-converged to `CONFORMS` mid-verify has no per-gap line above, so this index is the only place its fix commit stays revertable. No per-gap menu. Continue to Step 4.
79
+ **If no conformance check has run in this flow** (e.g. ad-hoc work that landed without an execution skill): say so, then dispatch a fresh-context `conformance-reviewer` against the origin (spec + verbatim prompt + full diff vs base) per that reference - it owns the audit-time input rule (stage/commit untracked deliverables before auditing). Closing the loop is cheap relative to shipping unverified intent. Route the raw reviewer verdict through the reference's canonical pipeline (gap/concern partition, auto-fix where eligible, concern decomposition, emission of a durable `## Closure / conformance` block), then consume that block through the branching below exactly as a carried handoff.
80
80
 
81
- **If gaps were carried open:** read the `## Closure / conformance` block from the verify completion summary (schema and field names defined once in `verification-before-completion/reference/conformance-check.md` do not re-derive them here). For each `Gn` line, render it verbatim and offer this ordered menu the human must pick one entry per gap before Step 4:
81
+ **Freshness precondition - before any verdict branch, including `CONFORMS`.** The durable block opens with a two-line sentinel: `status: CONFORMS (0 open)` or `status: GAPS (N open)`, then `audited-base: <full HEAD SHA at audit time>`. Read the sentinel, then apply the reference's freshness rule (its `## Closure / conformance` block is the single source): compare `audited-base` to the current working tree; any change, doubt, missing/mismatched sentinel, legacy terse row, or malformed structured reviewer block triggers a fresh audit and replacement of the closure block. Never infer `CONFORMS` from the absence of bullets. Only a clean, valid `status: CONFORMS (0 open)` handoff enters the zero-gap fast path.
82
82
 
83
+ **Zero-gap fast path:** print exactly
84
+
85
+ ```
86
+ Closure / conformance: CONFORMS
83
87
  ```
84
- G<n>: <verdict> — recommended: <fix|accept|rescope> — touched-files: <paths>
85
- round history: R1 ..., R2 ...
86
88
 
87
- D1. Apply now: accept-into-spec fold a dated decision into the spec now (main session edits the spec directly, never a subagent)
88
- D2. Apply now: rescope-into-spec — same, recorded as reduced/changed scope
89
- D3. Apply now: fix-now run the fix loop now (worktree finish paths only, see below)
90
- D4. Custom disposition — e.g. capture as a follow-up ticket per this project's issue-tracker convention (see `.pi/gauntlet-overrides.md`)
91
- D5. Revert an auto-applied change for this gap (see "Revert semantics" below)
89
+ then continue directly to Step 4. No approval prompt, no menu, no shared options line, no sign-off. If the run auto-applied fixes, surface the flat `auto-applied fix commits: <Gn: SHA>, ...` index from the durable block as **one informational, non-blocking line** with a one-line revert offer (see "Revert semantics") - a gap that auto-converged mid-verify has no bullet, so this index is the only place its fix commit stays revertable. Do not wait for acknowledgment.
90
+
91
+ **Carried-open render (dense).** Read the `## Closure / conformance` block. Render a header with the decision count, one bullet per decision unit (a gap by default; a `Gn/Cn` concern only where the reference split it), then the shared options line, then the recommended-set reply. Never show durable-card internals (ownership, evidence tokens, identity, hashes) in the render.
92
+
93
+ Each bullet:
92
94
 
93
- Which option for G<n>?
95
+ `* <handle> - <plain title>: <what's unresolved, one clause>. <short question> Recommended: <choice> (<one-clause why>).`
96
+
97
+ - `<handle>` leads the bullet and is a short unique human word derived from the title (`Cache coverage` -> `cache`); on collision append a digit. It is the token option 2 targets. When a gap split and no clean word fits, use the bare `Gn/Cn`; a single-concern gap uses its gap ID `Gn`.
98
+ - The shared options line sits below the bullets: `Other options per item: fix-now / accept / rescope / follow-up / custom`, listing the options **generally available across items**. When a specific item's availability deviates - an option unavailable for it, or an `UNAUTHORIZED` item whose `rescope` is unavailable and whose `fix-now` means removal - note that deviation as a short parenthetical on **that item's bullet** (one clause, not a block), e.g. `(rescope N/A: scope creep)`. The shared line appears **only in the carried-open render**, never in the zero-gap path. Full per-option effects only on request, or when option 2 targets an unclear choice.
99
+ - Group items under one recommended line only when they share a disposition and rationale; each grouped handle repeats its title.
100
+ - Availability per concern comes from the reference's single availability table - apply it against current context (worktree state, `maxFixRounds`, ownership, resource accessibility), do not restate it. `UNAUTHORIZED` bullets ask the reference's question verbatim (`Should this unrequested behavior become part of the current workflow?`); `rescope-into-spec` is shown **unavailable** (not dropped) and `fix-now` means **removal** of the unrequested code.
101
+ - `revert conformance fix Gn`, when the gap has an auto-applied fix, renders on the shared options line as a **separate one-off action** - never inside a bullet's recommendation and never in the option-2 list. Name the parent gap and warn that revert undoes the entire gap-level commit (see "Revert semantics").
102
+
103
+ Representative carried-open render (multi-concern gap split to `e2e`; single-concern gap `cache`; `UNAUTHORIZED` gap `auth`):
104
+
105
+ ```
106
+ Conformance: 3 decisions needed before shipping.
107
+
108
+ * e2e - Source-image E2E validation: not run; blocked (HYDRA1.png, HTTP 401).
109
+ Still in scope for this branch? Recommended: rescope (defer until image available) (fix-now N/A: needs HYDRA1.png).
110
+ * cache - Cache coverage: implemented but the spec is silent on it.
111
+ In scope? Recommended: accept into spec (behavior is intentional).
112
+ * auth - Unrequested admin bypass: adds an unlisted route. Should this unrequested behavior become part of the current workflow? Recommended: fix-now = remove it (rescope N/A: scope creep).
113
+
114
+ Other options per item: fix-now / accept / rescope / follow-up / custom.
115
+
116
+ 1. Go with recommended
117
+ 2. Recommended except <handle>=<choice> e.g. "2: cache=follow-up"
118
+ ```
119
+
120
+ A single-concern render is identical minus the split: one bullet whose handle is the gap ID or word, no sibling.
121
+
122
+ #### Response grammar
123
+
124
+ ```
125
+ 1 -> apply every recommendation
126
+ 2: cache=follow-up
127
+ 2: e2e=custom(open ticket after image lands), cache=follow-up
94
128
  ```
95
129
 
96
- Also surface the flat `auto-applied fix commits: <Gn: SHA>, ...` index from the same block — this is the revert candidate list for D5, covering every `conformance fix Gn` commit this run (open and already-closed gaps alike).
130
+ - `1` (or `apply recommended`) applies all recommendations.
131
+ - `2:` takes a comma-separated override list, each `<handle>=<choice>`; omitted items keep their recommendation. A handle may appear at most once (repeat = invalid).
132
+ - `custom(<concrete effect>)` supplies an inline effect. Manual fix-in-place is expressed only as `custom(...)` where isolated `fix-now` is unavailable.
133
+ - `recommended: none` items follow the reference's preflight (linked, not restated): the item needs a `<handle>=custom(...)` decision, and option 1 is withheld until every open item has an executable recommendation; after the custom decision the menu re-renders for the remainder.
134
+ - `revert conformance fix Gn` is a valid standalone reply, mutually exclusive with `1` and `2:`; it never appears inside a `2:` override list.
135
+ - Invalid handle or choice -> focused reprompt naming only that item, retaining every valid pick and never reopening the gate. Unknown token: list the valid titled handles. Known item, bad choice: repeat its title + its available choices.
97
136
 
98
- **`fix-now` loop scope (worktree finish paths only):**
137
+ #### Execute order
99
138
 
100
- 1. Commit any `accept-into-spec` / `rescope-into-spec` edits picked in this gate **before** dispatching a dirty tree rejects `worktree: true`, and the re-audit must read the amended spec.
101
- 2. Run the **full** loop from `verification-before-completion/reference/conformance-check.md` "Fix loop" (implementer → integrate → test → `code-reviewer` → re-audit). Do not re-describe the loop steps here — that file is the single source.
102
- 3. Re-run Step 1's test verification on the result.
103
- 4. Re-enter this step (3.5) with the re-audited `## Closure / conformance` block before re-presenting ship options.
139
+ Take **no** disposition action before the reply. Then, once, in order:
104
140
 
105
- **Non-worktree precondition (unchanged):** on a normal-repo finish (`GIT_DIR == GIT_COMMON`) or detached HEAD there is no worktree to dispatch fix waves into the menu above offers D1, D2, D4, and D5 (manual fix-in-place substitutes for D3) but never dispatches `fix-now`.
141
+ 1. **Normalize** every `custom(...)` into explicit operations; classify state-changing (edits code or spec) vs not. Clarify only an ambiguous or unexecutable effect.
142
+ 2. **Commit spec edits** (`accept-into-spec`, `rescope-into-spec`, state-changing spec `custom`) - the main session edits the spec directly, before any fix dispatch (a dirty tree rejects `worktree: true`, and the re-audit must read the amended spec).
143
+ 3. **Re-audit if step 2 changed the spec**; regenerate the inventory and re-render if it changed. Project `fix-now` only from the refreshed inventory.
144
+ 4. **fix-now + code-changing custom:** project the selected concerns per gap into the reference's concern-scoped fix contract (excluding accepted/rescoped/followed-up siblings); run the reference "Fix loop" (unchanged - do not re-describe it). A code-changing `custom` runs the project's tests + `code-reviewer` on its delta before proceeding. Re-run Step 1's canonical tests.
145
+ 5. **Re-audit after all state-changing work;** obtain fresh decisions **only if** the refreshed inventory differs from the approved one, else proceed.
146
+ 6. **follow-up** from the current inventory: create the item via the project's issue-tracker convention (`.pi/gauntlet-overrides.md`), record the ticket ID/URL; on failure keep the concern open.
147
+ 7. **Non-state-changing custom:** execute and record the result.
148
+ 8. **revert** (`revert conformance fix Gn`): light-revert the indexed commit, re-run Step 1's canonical tests; on failure stop; on pass re-audit and regenerate.
149
+ 9. Re-enter Step 3.5 with the re-audited block if any concern remains open.
106
150
 
107
- No auto-proceed: every carried-open gap needs an explicit answer from the list above before Step 4 renders. Once all gaps are dispositioned, report the final state as a distinct line:
151
+ Record every final disposition with its **stable ID** as `Gn/Cn - <title>: <disposition>` (or `Gn - <title>: <disposition>` for a single-concern gap) - ticket ID/URL for `follow-up`, result for `custom`; never relabel a `custom` result as a recommendation. This durable record is the machine/audit surface; the interactive render stays handle-based. Then continue to Step 4:
108
152
 
109
153
  ```
110
154
  Closure / conformance: CONFORMS
111
- (or: GAPS <n> dispositioned: G1 accept-into-spec, G2 fix-now CONFORMS, ...)
155
+ (or: GAPS resolved - G1/C1 - Source-image E2E validation: rescope-into-spec;
156
+ G2 - Cache coverage: follow-up (PROJ-123); ...)
112
157
  ```
113
158
 
114
- Then continue to Step 4.
159
+ No auto-proceed: every carried-open decision needs an explicit disposition before Step 4 renders.
115
160
 
116
161
  ### Revert semantics
117
162
 
@@ -190,9 +190,9 @@ For the fan-out + worktree + patch-integration + conflict mechanics, see `dispat
190
190
 
191
191
  0. Call `phase_tracker({ action: "start", phase: "verify" })`. (The `implement` phase was started at execution start and auto-completes from `plan_tracker` once all tasks are done; this flow runs its own verify gate instead of `/skill:verification-before-completion`, so it must mark verify itself.)
192
192
  1. **Run the whole-diff code review.** Dispatch `/skill:requesting-code-review` against the worktree's full diff vs `main` (already covered in [The Process](#the-process) step "After all tasks"). Address Critical and Moderate findings before handoff. (Consumers wanting an in-flow project-specific audit re-add it as an explicit step in `.pi/gauntlet-overrides.md`, or run `/self-audit` manually.)
193
- 2. **Close the loop — conformance check.** The review in step 1 is plan-vs-code (single-step); it inherits any requirement the plan already dropped. Before marking verify complete, dispatch a fresh-context **`conformance-reviewer`** — its **own** dispatch, never fused into the step-1 review — to confront the deliverable (code **and** docs) against the *origin* — the spec **and** the original prompt — per `verification-before-completion/reference/conformance-check.md`. Pass the spec path, the verbatim original prompt, and the full diff. On `GAPS`, this skill does not choose a disposition or stop for one: `conformance-check.md` "When the check finds gaps" auto-runs the fix loop for every `recommended: fix` gap (no menu, no stop) and carries any `accept`/`rescope`/`UNAUTHORIZED` gap OPEN to the finish gate — follow that file for the partition rule and the fix-loop mechanics; do not reimplement them here. The fix loop may drive `plan_tracker` to surface fix-wave progress (task name = `Gn: <short desc>`, lifecycle `pending → in_progress → complete` per gap); it never calls `phase_tracker`. Call `phase_tracker({ action: "complete", phase: "verify" })` once every gap is either fixed (`CONFORMS`) or carried OPEN as a deferred `accept`/`rescope`/`UNAUTHORIZED` gap escalation (fix-round cap reached with an open `fix` gap) is the sole non-completing terminal state; on escalation, do not complete verify, stop and report.
194
- 3. Summarize what was implemented (tasks completed, files changed, test counts, code-review verdict). Emit the `## Closure / conformance` block verbatim per the schema defined in `verification-before-completion/reference/conformance-check.md` (do not rename or reformat its fields); `finishing-a-development-branch` Step 3.5 consumes it by those exact field names.
195
- 4. **Proceed to finishing — no confirmation prompt.** Once verify is complete per step 2's criterion, invoke `/skill:finishing-a-development-branch` immediately. Its Step 4 menu (squash / PR / keep / discard) is the human gate; a separate "ready to finish?" prompt only stacks a second stop in front of it. Carried-open gaps are resolved at Step 3.5 of that skill via the `## Closure / conformance` block from step 3 — nothing is left to decide here. Manual testing is a follow-up after the finishing choice (on `<base-branch>` after a squash-merge, or on the PR branch), never a reason to hold this gate.
193
+ 2. **Close the loop — conformance check.** The review in step 1 is plan-vs-code (single-step); it inherits any requirement the plan already dropped. Before marking verify complete, dispatch a fresh-context **`conformance-reviewer`** — its **own** dispatch, never fused into the step-1 review — to confront the deliverable (code **and** docs) against the *origin* — the spec **and** the original prompt — per `verification-before-completion/reference/conformance-check.md`. Pass the spec path, the verbatim original prompt, and the full diff. Follow that reference for the partition rule, concern decomposition, and fix-loop mechanics; do not reimplement them here. The fix loop may drive `plan_tracker` to surface fix-wave progress (task name = `Gn: <short desc>`, lifecycle `pending → in_progress → complete` per gap); it never calls `phase_tracker`. Call `phase_tracker({ action: "complete", phase: "verify" })` only when the reference says the handoff is durably complete: either a current `CONFORMS` result, or a current `## Closure / conformance` inventory whose carried-open concerns all come from valid deferred gaps, including `recommended: fix` gaps carried open because a declared precondition made the fix loop unavailable (`maxFixRounds: 0`, or no eligible named-branch worktree). A started positive-cap fix loop that blocks, fails, or exhausts its rounds with an open `fix` gap is escalation, not completion; on escalation, do not complete verify, stop and report.
194
+ 3. Summarize what was implemented (tasks completed, files changed, test counts, code-review verdict). Emit the `## Closure / conformance` block exactly as defined in `verification-before-completion/reference/conformance-check.md`: it must open with the two-line sentinel (`status: CONFORMS (0 open)` or `status: GAPS (N open)`, then `audited-base: <full HEAD SHA>`), then carry the exact durable concern schema by reference with no renamed or reformatted fields. `finishing-a-development-branch` Step 3.5 consumes that block verbatim.
195
+ 4. **Proceed to finishing — no confirmation prompt.** Once verify is complete per step 2's criterion, invoke `/skill:finishing-a-development-branch` immediately. Its Step 4 menu (squash / PR / keep / discard) is the human gate; a separate "ready to finish?" prompt only stacks a second stop in front of it. Carried-open concerns are resolved there per concern via the `## Closure / conformance` block from step 3. Manual testing is a follow-up after the finishing choice (on `<base-branch>` after a squash-merge, or on the PR branch), never a reason to hold this gate.
196
196
 
197
197
  ## Red Flags — STOP
198
198
 
@@ -57,7 +57,7 @@ Skip any step = lying, not verifying
57
57
  | Regression test works | Red-green cycle verified | Test passes once |
58
58
  | Agent completed | VCS diff shows changes | Agent reports "success" |
59
59
  | Requirements met | Line-by-line checklist | Tests passing |
60
- | Intent delivered (loop closed) | Fresh reviewer audits code+doc vs origin spec + prompt; verify completes when every gap is `CONFORMS` or carried-open as a deferred `accept`/`rescope`/`UNAUTHORIZED` gap - the sole non-completing terminal state is escalation (cap reached with an unresolved `fix` gap). Partition, fix loop, and `## Closure / conformance` schema: `reference/conformance-check.md` | Plan-vs-code review, tests passing |
60
+ | Intent delivered (loop closed) | Fresh reviewer audits code+doc vs origin spec + prompt; verify completes only on a current `CONFORMS` result or a current durable carried-open closure inventory permitted by `reference/conformance-check.md` (including declared-precondition-unavailable `recommended: fix` gaps). A started positive-cap fix loop that blocks, fails, or exhausts rounds with an open `fix` gap is escalation, not completion. | Plan-vs-code review, tests passing |
61
61
 
62
62
  ## Rationalization Prevention
63
63
 
@@ -101,17 +101,18 @@ No prompt, no menu: this partition is deterministic and exhaustive.
101
101
  regardless of its `recommended` value. Never auto-remove or auto-accept
102
102
  unrequested code here.
103
103
  3. **Every remaining `PARTIAL`/`MISSING`/`DRIFTED` gap**:
104
- - `recommended: fix` → auto-run the fix loop below — **unless `maxFixRounds:
105
- 0`**, in which case carry the gap **OPEN** (see the fix loop's
106
- `maxFixRounds: 0` note).
104
+ - `recommended: fix` → auto-run the fix loop below — **unless a declared
105
+ fix-loop precondition is unavailable** (`maxFixRounds: 0`, or no eligible
106
+ named-branch worktree), in which case carry the gap **OPEN** (see the fix
107
+ loop's precondition and `maxFixRounds: 0` notes).
107
108
  - `recommended: accept` or `recommended: rescope` → carry the gap **OPEN**,
108
109
  deferred to the finish gate. Do not apply a spec edit here — the finish
109
110
  gate owns disposition of deferred gaps.
110
111
 
111
- So the fast path (all gaps `recommended: fix`, none `UNAUTHORIZED`, cap > 0)
112
- therefore auto-runs the fix loop with no menu, stop, or confirmation; any other
113
- mix carries the `accept`/`rescope`/`UNAUTHORIZED` gaps OPEN while the `fix` gaps
114
- run. Record every gap's outcome (`CONFORMS`-closed or carried OPEN) in the
112
+ So the fast path (all gaps `recommended: fix`, none `UNAUTHORIZED`, cap > 0,
113
+ eligible named-branch worktree) therefore auto-runs the fix loop with no menu,
114
+ stop, or confirmation; any other mix carries the
115
+ `accept`/`rescope`/`UNAUTHORIZED` gaps OPEN while the `fix` gaps run. Record every gap's outcome (`CONFORMS`-closed or carried OPEN) in the
115
116
  `## Closure / conformance` block (schema below).
116
117
 
117
118
  **Re-partition after every re-audit.** A re-audit can introduce `Gn+1` or flip a
@@ -130,8 +131,10 @@ Only the fan-out/integrate/review shape and `plan_tracker` are reused.
130
131
  **Precondition — worktree required.** The loop needs a worktree HEAD to branch
131
132
  fixes from. On the ad-hoc `finishing-a-development-branch` paths that run in a
132
133
  normal repo (`GIT_DIR == GIT_COMMON`) or detached HEAD, there is no such HEAD:
133
- skip this loop, carry every `fix` gap OPEN, and resolve manually at finish
134
- (`accept`/`rescope`/manual fix-in-place only).
134
+ skip this loop, carry every `fix` gap OPEN, and resolve it at finish via the
135
+ canonical Disposition catalog and availability table below. `fix-now` is
136
+ unavailable there; any other disposition is offered only when its table
137
+ prerequisites hold.
135
138
 
136
139
  Per round:
137
140
 
@@ -203,31 +206,262 @@ per-gap dispatch against the **gap block**, not a plan task:
203
206
 
204
207
  This is a task-framing contract in the dispatch, not a new persona.
205
208
 
209
+ ## Concern decomposition
210
+
211
+ The main verification orchestrator — **not** `conformance-reviewer` — decomposes
212
+ each carried-open gap into concerns when it writes the completion summary. A
213
+ **gap** is the reviewer's overall finding; a **concern** (`Gn/Cn`) is one
214
+ consequence within it that could reasonably receive a *different* disposition.
215
+ Observations that necessarily move together stay one concern.
216
+
217
+ The orchestrator reasons from:
218
+
219
+ - The final reviewer coverage row and structured gap block.
220
+ - The origin spec and original prompt already supplied to verification.
221
+ - Session-observed blockers, prerequisite checks, and fix-loop outcomes.
222
+ - The round history and current touched-file/resource ownership.
223
+
224
+ Per gap, apply these rules **in order**:
225
+
226
+ 1. Extract atomic unmet clauses and remediation actions from `origin`,
227
+ `evidence`, and `remediation`. Add a session-observed blocker **only** when a
228
+ tool result or fix-loop outcome established it.
229
+ 2. **Split** two points when either could be fixed, accepted, rescoped, or
230
+ followed up without imposing the same disposition on the other.
231
+ 3. **Keep together** when one action is meaningful only with the other, or no
232
+ different disposition could be executed independently.
233
+ 4. Map **every** unmet clause, remediation action, and established blocker to at
234
+ least one concern. Do **not** add a requirement absent from the origin or
235
+ prompt — invent nothing.
236
+ 5. Order concerns by first appearance in the origin/remediation and assign
237
+ `C1`, `C2`, and so on (source order).
238
+
239
+ A valid decomposition has no omitted source point, no invented requirement or
240
+ evidence, and a concern-scoped remediation plus ownership estimate for every
241
+ `fix-now` candidate. Shared evidence may appear in multiple concerns. When the
242
+ source cannot support a safe split, the fallback is **one indivisible `Gn/C1`**
243
+ concern carrying the complete gap and the known blocker; decomposition never
244
+ blocks on obtaining richer prose from the unchanged reviewer.
245
+
246
+ Field derivation is explicit: `title` and `unresolved` summarize the unmet
247
+ clause; `origin` narrows the gap `origin` to this concern's specific clause
248
+ without inventing a new requirement; `remediation` states the concern-scoped
249
+ remediation action drawn from the gap `remediation`; `impact` states the
250
+ consequence already implied by the origin requirement; `evidence` copies
251
+ reviewer evidence plus verified session observations;
252
+ `touched-files`/`touched-resources` narrow the gap ownership where supported,
253
+ otherwise `unknown`/`none`. For an `UNAUTHORIZED` concern there is no origin
254
+ requirement to narrow: keep `origin: none (scope creep)` verbatim — never invent
255
+ an origin clause; derive `unresolved` from the reviewer's `evidence`/`remediation`
256
+ as a plain description of the unrequested behavior; and derive `impact` from the
257
+ consequence of retaining, removing, or ratifying that behavior, not from an origin
258
+ requirement. These per-concern fields persist the exact contract
259
+ the concern-scoped fix projection consumes after pruning. **`evidence: absent`
260
+ is an unmet-delivery fact, not
261
+ an external blocker** — never relabel missing implementation evidence as a
262
+ blocker. A malformed structured reviewer gap block — missing its stable `Gn`
263
+ label or any required field (`verdict`, `origin`, `evidence`, `remediation`,
264
+ `touched-files`, `touched-resources`, `recommended`) — triggers a **fresh
265
+ audit**; a complete structured reviewer gap block does not — the orchestrator
266
+ decomposes it or emits the indivisible fallback.
267
+
268
+ ## Disposition catalog and availability
269
+
270
+ Each concern lists **every** supported disposition, its concrete effect, and
271
+ current availability. This table is the single availability contract, referenced
272
+ by both this verify gate and `finishing-a-development-branch`:
273
+
274
+ |Disposition|Ordinary gap effect|`UNAUTHORIZED` effect|Available when|
275
+ |---|---|---|---|
276
+ |`fix-now`|Complete missing implementation or validation.|Remove the unrequested code or behavior.|Named-branch worktree; `maxFixRounds > 0`; concern ownership is known; every required local/external resource is accessible.|
277
+ |`accept-into-spec`|Ratify intentional implemented behavior when the written contract is stale.|Ratify the unrequested behavior as approved scope.|A writable spec exists and there is concrete behavior to ratify.|
278
+ |`rescope-into-spec`|Explicitly remove or defer the origin requirement from this workflow.|Unavailable: scope creep has no origin requirement to defer.|The verdict is not `UNAUTHORIZED` and a writable spec exists.|
279
+ |`follow-up`|Keep the concern valid but transfer it to separately owned work.|Transfer a separately valid decision or removal task.|The concern can stand alone and the project defines an executable issue-tracker convention. Without one, mark unavailable and direct the user to `custom` to name another durable owner.|
280
+ |`custom`|Execute another user-defined disposition after its effect is clarified.|Same.|Always visible; executable only after the user supplies a concrete effect. It is never an automatic recommendation.|
281
+
282
+ A failed condition leaves the disposition visible and names the exact missing
283
+ prerequisite. `touched-files: unknown` makes `fix-now` unavailable until
284
+ ownership is established. A normal checkout (`GIT_DIR == GIT_COMMON`) or detached
285
+ HEAD cannot dispatch the isolated fix loop at all - it has no named-branch
286
+ worktree to branch fixes from - so `fix-now` stays unavailable there regardless
287
+ of ownership; resolve those concerns manually at finish. `maxFixRounds: 0`
288
+ (audit-only) likewise leaves `fix-now` visible but unavailable — the user
289
+ configured no auto-fix loop, and finish-time selection never bypasses or resets
290
+ that cap; a concrete `custom`/manual-fix disposition remains possible. `UNAUTHORIZED` cards
291
+ replace the primary question with
292
+ `Should this unrequested behavior become part of the current workflow?`, so
293
+ removal and ratification are not presented as missing-feature choices.
294
+
295
+ Reviewer tokens map to executable concern recommendations:
296
+
297
+ - `fix` maps to `fix-now`; for `UNAUTHORIZED`, that means removal.
298
+ - `accept` maps to `accept-into-spec`; for `UNAUTHORIZED`, that means ratification.
299
+ - `rescope` maps to `rescope-into-spec` and is invalid for `UNAUTHORIZED`.
300
+ - `follow-up` may replace an unavailable `fix-now`/`rescope-into-spec` **only**
301
+ when the concern remains valid, has separate ownership, and the issue-tracker
302
+ action is executable.
303
+
304
+ The orchestrator may choose another named disposition only when current evidence
305
+ supports its stated effect. If no named executable disposition is available, the
306
+ concern serializes `recommended: none` - the durable state for "no named
307
+ executable choice exists" - and its `rationale` must name the exact missing
308
+ prerequisites plus the concrete custom effect still required. `custom` is never
309
+ auto-recommended, so `none` never silently resolves to `custom`.
310
+
311
+ A concern with `recommended: none` is excluded from the recommended set. Before
312
+ any exhaustive `Apply recommended set`, the finish gate asks for a targeted
313
+ custom approval that supplies a concrete effect for that one concern (finishing
314
+ owns the exact reply syntax - a per-item `custom(...)` decision - not an `apply
315
+ recommended` variant). That reply approves only that concrete custom effect for
316
+ that concern. Validate and normalize it immediately: if the effect is ambiguous or
317
+ unexecutable, clarify and keep the concern open; if executable, record it as a
318
+ **pending approved custom decision** and remove that concern from the still-open
319
+ inventory used to build the recommended set. If other open concerns remain,
320
+ render the exhaustive recommended set for them and visibly carry the pending
321
+ approved custom decision through the existing action-order and freshness
322
+ barriers; `apply recommended` approves only the remaining recommendations. If no
323
+ other concerns remain, execute the approved custom directly through the existing
324
+ mechanics. The final disposition record keeps this concern as a `custom` result;
325
+ never relabel it as a model recommendation.
326
+
327
+ **Never recommend an unavailable disposition or an unexecutable `custom`
328
+ placeholder.**
329
+
330
+ `revert` is a separate action, not a generic concern disposition. The commit
331
+ index stays gap-granular (`conformance fix Gn`). A listed gap-level commit makes
332
+ revert visible on every concern card under that gap, with an explicit warning
333
+ that the **entire gap commit** is reverted. Existing light-revert semantics
334
+ remain: revert the indexed commit, re-audit, and return to the decision gate
335
+ only if a concern remains open.
336
+
206
337
  ## Closure / conformance
207
338
 
208
339
  Emit this block in the verify completion summary. It is the durable handoff
209
340
  `finishing-a-development-branch` Step 3.5 consumes — parseable even if session
210
341
  context was pruned. Verify completes when every gap is either fixed
211
- (`CONFORMS`) or carried OPEN as a deferred `accept`/`rescope`/`UNAUTHORIZED`
212
- gap; escalation (cap reached with an open `fix` gap) is the one
213
- non-completing terminal state.
342
+ (`CONFORMS`) or carried OPEN as a deferred gap - `accept`/`rescope`/`UNAUTHORIZED`,
343
+ or a `recommended: fix` gap carried open because a declared fix-loop precondition
344
+ was unavailable so the loop never started (`maxFixRounds: 0`, or no eligible
345
+ named-branch worktree - normal checkout / detached HEAD). Escalation - a started
346
+ positive-cap loop that exhausted its rounds or blocked/failed with an open `fix`
347
+ gap - is the one non-completing terminal state; the precondition-unavailable
348
+ carried-open `fix` state is valid closure inventory, not escalation.
349
+
350
+ ### Handoff sentinel and freshness anchor - every handoff
351
+
352
+ Every `## Closure / conformance` block - a `CONFORMS` no-card handoff and a
353
+ carried-open GAPS handoff alike - **opens with a two-line sentinel** that lets
354
+ the finish gate re-verify freshness after context pruning, with no session
355
+ history:
356
+
357
+ ```text
358
+ status: CONFORMS (0 open) # or: status: GAPS (N open)
359
+ audited-base: <full HEAD SHA at audit time>
360
+ ```
214
361
 
215
- For each carried-open gap:
362
+ `N` = count of open concerns (decision units), matching the number of emitted
363
+ concern cards. Record `audited-base` as the full 40-char HEAD SHA at audit time;
364
+ never abbreviate. This block is the **single source** for the freshness rule;
365
+ `finishing-a-development-branch` links here rather than restating it.
216
366
 
367
+ **Freshness rule.** The audit-input rule requires deliverables committed before
368
+ auditing, so `audited-base` captures the audited state; freshness compares that
369
+ commit to the **current working tree**, not to HEAD (a commit-to-commit diff
370
+ misses staged/unstaged edits when HEAD has not moved). Run two cheap commands:
371
+
372
+ ```bash
373
+ ROOT=$(git rev-parse --show-toplevel)
374
+ git -C "$ROOT" diff --stat <audited-base> -- . # tracked changes since the audited commit (staged + unstaged)
375
+ git -C "$ROOT" status --porcelain --untracked-files=all # new/untracked deliverables
217
376
  ```
218
- Gn: <verdict> — recommended: <fix|accept|rescope> — touched-files: <paths>
377
+
378
+ Any output from either command, any doubt, a missing/mismatched sentinel, or any
379
+ closure block not opening with the two-line sentinel above (e.g. a legacy
380
+ `Gn: PARTIAL - recommended: ...` row) triggers a fresh audit - never infer
381
+ `CONFORMS` from the absence of cards. This is a lightweight freshness check (two
382
+ git commands, no hashing or identity fields).
383
+
384
+ **Sentinel validation.** `status: CONFORMS` requires `N = 0` and **no** emitted
385
+ concern cards; `status: GAPS` requires `N > 0` exactly matching the emitted card
386
+ count (`Gn/Cn` blocks), not merely gap headers. A mismatch is stale -> re-audit.
387
+
388
+ **Audit-time input rule.** Stage or commit untracked deliverables before
389
+ auditing, since `git diff <base> -- .` omits untracked files from the reviewer
390
+ payload.
391
+
392
+ For each carried-open gap, emit a durable gap header plus one card per concern
393
+ (decomposition and derivation rules above). In rendered cards, recommendation
394
+ lists, clarification lists, and final disposition records, every user-facing
395
+ identifier carries both its ID **and** title; bare `Gn`/`Cn` or grouped
396
+ identifier lists without titles are prohibited. The only exceptions are the
397
+ machine/interaction tokens the grammar requires: bare `Gn/Cn` inside typed
398
+ response tokens, the literal `conformance fix Gn` commit/action identifiers, and
399
+ the flat `auto-applied fix commits` index below. The surrounding prompt or list
400
+ must map each token to its titled concern or gap before asking for input.
401
+
402
+ ```text
403
+ G1 - Source-image validation is incomplete
404
+ verdict: PARTIAL
405
+ origin: <requirement source and clause>
406
+ evidence: <current file:line or observed state>
407
+ blocker: <specific blocker, or none>
408
+ touched-files: <paths or unknown>
409
+ touched-resources: <resources or none>
219
410
  round history: R1 <verdict/action>, R2 <verdict/action>, ...
411
+
412
+ G1/C1 - End-to-end OCR output has not been validated
413
+ unresolved: <plain statement of the concern>
414
+ impact: <why it matters to the current workflow>
415
+ origin: <requirement source and clause, narrowed from the gap origin; or none (scope creep) for UNAUTHORIZED>
416
+ remediation: <concern-scoped remediation action>
417
+ evidence: <concern-specific evidence or blocker>
418
+ touched-files: <concern-scoped paths, narrowed from the gap; or unknown>
419
+ touched-resources: <concern-scoped resources, narrowed from the gap; or none>
420
+ available dispositions:
421
+ fix-now: <effect and availability>
422
+ accept-into-spec: <effect and availability>
423
+ rescope-into-spec: <effect and availability>
424
+ follow-up: <effect and availability>
425
+ custom: <effect and availability>
426
+ recommended: <available named disposition, or none>
427
+ rationale: <why this is the best current choice; for none, the missing prerequisites and the concrete custom effect required>
220
428
  ```
221
429
 
430
+ All concerns in a gap must be represented.
431
+
222
432
  Then a single flat revert index of **every** `conformance fix Gn` commit the fix
223
433
  loop produced this run — including gaps that later converged to `CONFORMS`
224
- (a closed gap has no block above, so its commit lives only here) — since the
434
+ (a closed gap has no card above, so its commit lives only here) — since the
225
435
  finish gate's revert option needs them all:
226
436
 
227
437
  ```
228
438
  auto-applied fix commits: <Gn: SHA>, <Gm: SHA>, ... (revertable)
229
439
  ```
230
440
 
441
+ ## Concern-scoped fix projection
442
+
443
+ When the finish gate selects `fix-now` on some concerns of a gap, the fix loop
444
+ above runs against a **projected** contract, not the original whole-gap block.
445
+ For each parent gap with selected `fix-now` concerns, project only those
446
+ concerns into one gap-scoped fix contract:
447
+
448
+ - The parent gap ID.
449
+ - The selected concern IDs and titles.
450
+ - Their origin clauses, concern remediations, and evidence.
451
+ - The **union** of the selected concerns' touched files/resources (ownership
452
+ boundary).
453
+
454
+ Rescoped, accepted, and followed-up sibling concerns are **excluded** from the
455
+ projection. The `implementer` and the pre-integration `spec-reviewer` receive
456
+ this projected contract in place of the original whole-gap block.
457
+
458
+ The projected task runs the existing full loop above: it retains the gap-level
459
+ `conformance fix Gn` commit name, reruns the project's tests, runs
460
+ `code-reviewer`, re-audits against the amended spec, and reenters the gate only
461
+ if concerns remain. Gap-level revert stays available through the flat commit
462
+ index. The gate records the final result by concern ID and title before showing
463
+ branch integration options.
464
+
231
465
  ## Checklist
232
466
 
233
467
  - [ ] Located canonical requirements (spec → prompt → ticket fallback)