Background
v3.1.4 (PR #874) added a canonical pr_ready_for_human signal so Needs Attention could gate on a single boolean instead of protocol-specific derivations. The setter design baked in a special case for BUGFIX: every other protocol (SPIR / ASPIR / PIR / AIR) has a pr gate that fires the signal via index.ts:418 when the builder calls porch done after CMAP; BUGFIX has no pr gate, so the signal was wired to fire at the advanceProtocolPhase terminal-exit setter (index.ts:453) instead.
That set-point is too late: it only fires when porch done runs on the pr phase. BUGFIX builders only call porch done after merging, by which time the human has already acted and the "PR is ready for human" signal is meaningless. Verified empirically: BUGFIX projects sit at phase: pr, pr_ready_for_human: false indefinitely after their PR is opened and CMAP completes, and never surface in Needs Attention until after merge (when the PR is closed anyway and the row no longer matters).
The architect originally chose "leave BUGFIX gateless" without considering this interaction. Adding a pr gate to BUGFIX makes it semantically identical to AIR — and AIR's flow demonstrably gets the signal timing right.
Fix shape (Option G)
Add "gate": "pr" to BUGFIX's pr phase. The existing index.ts:418 gate-request setter then fires automatically when the builder calls porch done after CMAP. Architect approves the gate via porch approve <id> pr. Builder merges. Builder calls porch done again to advance to verified.
No new porch commands. No semantic shift in verified (still means "merged, work complete"). The BUGFIX-without-gate branches added in v3.1.4 become redundant and can be cleaned up.
Scope
Protocol JSON
codev/protocols/bugfix/protocol.json — add "gate": "pr" to the pr phase
codev-skeleton/protocols/bugfix/protocol.json — same
Porch code cleanup (BUGFIX-no-gate branches now redundant)
packages/codev/src/commands/porch/next.ts line ~617 — comment that references the "BUGFIX no gate" case is now stale; update or remove
packages/codev/src/commands/porch/index.ts lines 443-465 (advanceProtocolPhase): the advancingFromPrPhase snapshot + the terminal-exit setter was added specifically for the BUGFIX-no-gate case. With BUGFIX having a gate, this code path is no longer reachable for any current protocol. Recommend dropping the snapshot + the conditional setter (lines 448, 452-453, 463-464) and the JSDoc comment that calls out BUGFIX as the no-gate case. Keep the advanceProtocolPhase function itself; just remove the BUGFIX-specific conditional.
packages/codev/src/commands/porch/protocol.ts — isPrCreatingPhase() currently checks gate === 'pr' || hasPrConsultation. With BUGFIX getting the gate, every PR-creating phase has gate === 'pr' and the hasPrConsultation branch is dead. Drop the branch AND the hasPrConsultation field on ProtocolPhase AND its computation in normalizePhase. Tighter type, fewer moving parts. The JSDoc on isPrCreatingPhase needs updating to reflect the simpler invariant.
Builder prompt
- BUGFIX builder prompt (template in
codev-skeleton/protocols/bugfix/builder-prompt.md, or wherever the BUGFIX prompt lives) — update the pr-phase guidance: "After CMAP completes, run porch done <id> to request the pr gate. Wait for the architect to call porch approve <id> pr. Then merge the PR. Then run porch done <id> again to advance to verified."
- Match the prompt language to AIR's pr-phase prompt for consistency. The AIR prompt is the gold reference here.
Tests
packages/codev/src/commands/porch/__tests__/pr-ready-872.test.ts — the BUGFIX test case exercises the now-removed terminal-exit setter. Rewrite it to exercise the gate-request setter (line 418), matching how the AIR test case is written. The architect's iter-2 fix to that file (the RESEARCH false-classification regression test) stays in.
- Search the repo for tests that assert BUGFIX's final
gates: {} and update — BUGFIX terminal state now has gates: { pr: { status: 'approved', requested_at, approved_at } }.
derivePrReady in overview.ts carries a BUGFIX special case (phase === 'verified' && protocol === 'bugfix') as a v3.1.4-compat fallback. Once the new code rolls out and existing in-flight BUGFIX projects cycle through, this fallback path becomes dead code — but leave it for now as a graceful-degradation path for projects mid-flight when the upgrade lands. Flag for removal in a future cleanup pass (v3.2+).
Docs
CLAUDE.md and AGENTS.md — anywhere they describe BUGFIX as gateless or list BUGFIX's flow. Update to reflect the gate.
docs/releases/v3.1.4-jacobean.md — note the BUGFIX-specific set-point was an interim design; v3.1.5 supersedes it with the gate. Consider adding a "Known issue / superseded by v3.1.5" callout. Optional but useful for someone reading the v3.1.4 notes after the fact.
Acceptance criteria
Out of scope
- Removing the
derivePrReady BUGFIX-fallback in overview.ts — leave for graceful degradation of in-flight v3.1.4 projects. Track for v3.2+ removal.
- Re-examining whether AIR's terminal-exit also has the timing wrong — out of scope; AIR's setter is at the gate-request point which is correct.
References
Background
v3.1.4 (PR #874) added a canonical
pr_ready_for_humansignal so Needs Attention could gate on a single boolean instead of protocol-specific derivations. The setter design baked in a special case for BUGFIX: every other protocol (SPIR / ASPIR / PIR / AIR) has aprgate that fires the signal viaindex.ts:418when the builder callsporch doneafter CMAP; BUGFIX has noprgate, so the signal was wired to fire at theadvanceProtocolPhaseterminal-exit setter (index.ts:453) instead.That set-point is too late: it only fires when
porch doneruns on the pr phase. BUGFIX builders only callporch doneafter merging, by which time the human has already acted and the "PR is ready for human" signal is meaningless. Verified empirically: BUGFIX projects sit atphase: pr, pr_ready_for_human: falseindefinitely after their PR is opened and CMAP completes, and never surface in Needs Attention until after merge (when the PR is closed anyway and the row no longer matters).The architect originally chose "leave BUGFIX gateless" without considering this interaction. Adding a
prgate to BUGFIX makes it semantically identical to AIR — and AIR's flow demonstrably gets the signal timing right.Fix shape (Option G)
Add
"gate": "pr"to BUGFIX's pr phase. The existingindex.ts:418gate-request setter then fires automatically when the builder callsporch doneafter CMAP. Architect approves the gate viaporch approve <id> pr. Builder merges. Builder callsporch doneagain to advance toverified.No new porch commands. No semantic shift in
verified(still means "merged, work complete"). The BUGFIX-without-gate branches added in v3.1.4 become redundant and can be cleaned up.Scope
Protocol JSON
codev/protocols/bugfix/protocol.json— add"gate": "pr"to the pr phasecodev-skeleton/protocols/bugfix/protocol.json— samePorch code cleanup (BUGFIX-no-gate branches now redundant)
packages/codev/src/commands/porch/next.tsline ~617 — comment that references the "BUGFIX no gate" case is now stale; update or removepackages/codev/src/commands/porch/index.tslines 443-465 (advanceProtocolPhase): theadvancingFromPrPhasesnapshot + the terminal-exit setter was added specifically for the BUGFIX-no-gate case. With BUGFIX having a gate, this code path is no longer reachable for any current protocol. Recommend dropping the snapshot + the conditional setter (lines 448, 452-453, 463-464) and the JSDoc comment that calls out BUGFIX as the no-gate case. Keep theadvanceProtocolPhasefunction itself; just remove the BUGFIX-specific conditional.packages/codev/src/commands/porch/protocol.ts—isPrCreatingPhase()currently checksgate === 'pr' || hasPrConsultation. With BUGFIX getting the gate, every PR-creating phase hasgate === 'pr'and thehasPrConsultationbranch is dead. Drop the branch AND thehasPrConsultationfield onProtocolPhaseAND its computation innormalizePhase. Tighter type, fewer moving parts. The JSDoc onisPrCreatingPhaseneeds updating to reflect the simpler invariant.Builder prompt
codev-skeleton/protocols/bugfix/builder-prompt.md, or wherever the BUGFIX prompt lives) — update the pr-phase guidance: "After CMAP completes, runporch done <id>to request the pr gate. Wait for the architect to callporch approve <id> pr. Then merge the PR. Then runporch done <id>again to advance to verified."Tests
packages/codev/src/commands/porch/__tests__/pr-ready-872.test.ts— the BUGFIX test case exercises the now-removed terminal-exit setter. Rewrite it to exercise the gate-request setter (line 418), matching how the AIR test case is written. The architect's iter-2 fix to that file (the RESEARCH false-classification regression test) stays in.gates: {}and update — BUGFIX terminal state now hasgates: { pr: { status: 'approved', requested_at, approved_at } }.derivePrReadyinoverview.tscarries a BUGFIX special case (phase === 'verified' && protocol === 'bugfix') as a v3.1.4-compat fallback. Once the new code rolls out and existing in-flight BUGFIX projects cycle through, this fallback path becomes dead code — but leave it for now as a graceful-degradation path for projects mid-flight when the upgrade lands. Flag for removal in a future cleanup pass (v3.2+).Docs
CLAUDE.mdandAGENTS.md— anywhere they describe BUGFIX as gateless or list BUGFIX's flow. Update to reflect the gate.docs/releases/v3.1.4-jacobean.md— note the BUGFIX-specific set-point was an interim design; v3.1.5 supersedes it with the gate. Consider adding a "Known issue / superseded by v3.1.5" callout. Optional but useful for someone reading the v3.1.4 notes after the fact.Acceptance criteria
"gate": "pr"on the pr phasephase: prwithgates: { pr: { status: 'pending', requested_at: <timestamp> } }andpr_ready_for_human: trueafter runningporch donepost-CMAP, mirroring AIR's behaviorporch approve <id> prsetsgates.pr.status: 'approved'andpr_ready_for_human: falseporch doneadvances toverified(terminal state); the PR is by then mergedporch done(gate-request) until the moment ofporch approve(architect action)advanceProtocolPhaseandisPrCreatingPhase;hasPrConsultationfield removedpr-ready-872.test.tsBUGFIX case rewritten to test gate-request path; passesporch doneafter CMAP)porch done. Verify status.yaml at each step; verify Needs Attention surfaces the PR at the right momentOut of scope
derivePrReadyBUGFIX-fallback inoverview.ts— leave for graceful degradation of in-flight v3.1.4 projects. Track for v3.2+ removal.References
index.ts:418(gate-request, correct for AIR),index.ts:453(terminal-exit, the BUGFIX special case being removed)codev-skeleton/protocols/air/protocol.json