You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Porch's final state commits — chore(porch): <id> protocol complete, and for verify-phase protocols every verify-phase transition and the verify-approval gate record — systemically fail to reach main. They are committed and pushed to the builder branch after the PR merge, so the merge never carries them, and afx cleanup then deletes the branch (local and remote), leaving main's codev/projects/<id>/status.yaml permanently frozen mid-phase.
This is not occasional. 106 of 179 projects on main (59%) have status.yaml stranded in a non-terminal phase, the overwhelming majority of them long-merged. porch pending run from a main checkout currently reports 140 "pending" gates, including pr gates for PRs merged in April.
Observed workarounds today (both manual, both partial):
The human tells the architect "before merging the PR, ensure the builder pushes the chore(porch): protocol complete commit" — fixes the race for BUGFIX/AIR/PIR, does nothing for SPIR/ASPIR (see below).
writeStateAndCommit (packages/codev/src/commands/porch/state.ts:184-210) commits status.yaml and pushes to the current builder branch on every state-machine event (Spec 653 §B.3: "zero gaps"). Spec 653 assumed: "When the branch merges, status.yaml lands on main naturally." That holds for every mid-protocol transition — and fails precisely at the end of the lifecycle, because the terminal transitions happen after the merge. Two distinct strata:
Porch's designed ordering is actually safe: when the builder polls porch next after pr gate approval, porch commits+pushes protocol completefirst and only then emits the merge task in the same invocation (packages/codev/src/commands/porch/next.ts:344-351 → recursion → merge task at next.ts:262-281). If the builder follows that ordering, the complete-state commit is on the branch head before gh pr merge and rides the merge into main. That's how the 73 projects that did reach terminal state got there.
The race: the merge is usually triggered out-of-band. Porch's own gate-approval notification says "Gate pr approved — please run porch next to advance" (porch/notify.ts:37-39), but the architect role doc instructs afx send <id> "PR approved, please merge". Builders receiving that message merge immediately and only afterwards poll porch — so protocol complete lands on the branch seconds after the merge and is stranded. Sometimes the builder never polls again at all and the commit is never created.
Evidence:
AIR 1220 (PR [AIR #1220] arch-init: architect auto-state-saving lifecycle #1221): merged 2026-07-22T11:51:54Z; chore(porch): 1220 protocol complete (36c51ed7) committed 11:52:30Z — 36 seconds after the merge. Not an ancestor of main; reachable only from origin/builder/air-1220. Main's status.yaml: phase: pr, gates: {pr: approved}.
For protocols with a verify phase, the merge is Step 1 of the verify phase (next.ts:810-826: "Verify phase: merge PR first, then verify"). Everything after — verify phase-transition, verify build-complete, verify-approval gate-requested, verify-approval gate-approved, protocol complete — is post-merge by design. No ordering discipline can fix this: main structurally cannot receive SPIR/ASPIR terminal state through the PR.
Evidence — ASPIR 1210 (PR #1223, merged 2026-07-22T14:00:18Z), all on origin/builder/aspir-1210, none ancestors of main:
commit
subject
when (UTC)
ee3adc92
verify phase-transition
14:00:47 (29s post-merge)
bfb910d7
verify-approval gate-requested
14:02:27
75ebb7ac
verify build-complete (auto)
21:53:18
fc088c4e
verify-approval gate-approved
21:53:20
41a4e5b2
protocol complete
21:53:22
Main's snapshot: phase: review, verify-approval: pending — a gate the human in fact approved. PR #1228 is the hand-made restore for exactly this.
This gap is already acknowledged in code as deferred work — packages/codev/src/commands/porch/index.ts:778-782: "The 'verified' state is committed to the builder branch, which may not be merged back to main. The closed GitHub Issue serves as the canonical 'done' signal on main. State alignment (making status.yaml on main authoritative) is tracked as future work per spec 653." This issue is that future work, now with data.
Stratum 3 — cleanup destroys the only surviving copy
afx cleanup deletes the local branch (agent-farm/commands/cleanup.ts:322) and the remote branch (cleanup.ts:167). After cleanup, the stranded terminal-state commits are unreachable and a #1228-style byte-for-byte restore becomes impossible. Main's stale snapshot is all that remains.
Quantified impact (audit of main, 2026-07-22)
179 codev/projects/*/status.yaml on main; 106 not in verified/complete (a handful are genuinely in-flight; the overwhelming majority correspond to merged PRs — spot-verified: 1210, 1220, bugfix-1224, 671, 761, 787, 987, …):
protocol
stranded mid-phase
reached terminal state
spir
20
1
aspir
3
0
air
12
1
bugfix
38
27
pir
33
41
The SPIR/ASPIR column is the structural stratum (near-total loss); BUGFIX/PIR/AIR is the race stratum (roughly a coin flip).
Consequences:
porch pending on main reports 140 phantom gates (some duplicated), oldest from 2026-04-15 — the human-facing gate queue is mostly noise.
Any consumer of main's status.yaml (dashboard Work view, Needs Attention–style derivations, future tooling per Spec 653's "status.yaml is the project's history") sees merged, verified work as blocked at a gate.
The archaeological record is wrong for 59% of projects: approvals the human actually granted (e.g. 1210's verify) are recorded nowhere reachable.
This issue documents the main-side consequence — the terminal state never becoming visible where every consumer reads it — and quantifies it. #801/#802 describe where the commits go wrong; this one is about main never being told the protocol finished.
Possible directions (not prescriptive)
Fix the race (stratum 1) cheaply: change the architect flow/role docs so the post-approval message is "run porch next to continue" (matching porch's own notify text) instead of "please merge"; optionally have the merge task itself assert that the protocol complete commit is on the PR head before merging.
Give post-merge state a designed path to main (stratum 2): e.g. porch approve verify-approval / terminal porch done, when the PR is already merged, writes status.yaml to the main checkout and commits it there (a small bookkeeping commit, analogous to what PR chore(porch): 1210 verify-approval gate-approved (restore stranded state) #1228 did by hand); or afx cleanup ports final state to main before deleting the branch; or a porch reconcile that sweeps merged projects and syncs terminal state.
Or declare main's status.yaml non-authoritative post-merge and make consumers honor that: porch pending (and any dashboard derivation) cross-checks the PR/issue state and suppresses gates for merged work — aligning with the existing "closed GitHub Issue is the canonical done signal" stance in index.ts:778-782.
Whichever direction: the acceptance bar is that porch pending on a fresh main checkout reports no phantom gates for merged work, and a completed project's terminal state is recorded somewhere durable and reachable.
Summary
Porch's final state commits —
chore(porch): <id> protocol complete, and for verify-phase protocols every verify-phase transition and theverify-approvalgate record — systemically fail to reachmain. They are committed and pushed to the builder branch after the PR merge, so the merge never carries them, andafx cleanupthen deletes the branch (local and remote), leaving main'scodev/projects/<id>/status.yamlpermanently frozen mid-phase.This is not occasional. 106 of 179 projects on main (59%) have
status.yamlstranded in a non-terminal phase, the overwhelming majority of them long-merged.porch pendingrun from a main checkout currently reports 140 "pending" gates, includingprgates for PRs merged in April.Observed workarounds today (both manual, both partial):
chore(porch): protocol completecommit" — fixes the race for BUGFIX/AIR/PIR, does nothing for SPIR/ASPIR (see below).verify-approvalapproval.How state flows (and where it breaks)
writeStateAndCommit(packages/codev/src/commands/porch/state.ts:184-210) commitsstatus.yamland pushes to the current builder branch on every state-machine event (Spec 653 §B.3: "zero gaps"). Spec 653 assumed: "When the branch merges, status.yaml lands on main naturally." That holds for every mid-protocol transition — and fails precisely at the end of the lifecycle, because the terminal transitions happen after the merge. Two distinct strata:Stratum 1 — merge-ordering race (BUGFIX, AIR, PIR, MAINTAIN)
Porch's designed ordering is actually safe: when the builder polls
porch nextafterprgate approval, porch commits+pushesprotocol completefirst and only then emits the merge task in the same invocation (packages/codev/src/commands/porch/next.ts:344-351→ recursion → merge task atnext.ts:262-281). If the builder follows that ordering, the complete-state commit is on the branch head beforegh pr mergeand rides the merge into main. That's how the 73 projects that did reach terminal state got there.The race: the merge is usually triggered out-of-band. Porch's own gate-approval notification says "Gate pr approved — please run
porch nextto advance" (porch/notify.ts:37-39), but the architect role doc instructsafx send <id> "PR approved, please merge". Builders receiving that message merge immediately and only afterwards poll porch — soprotocol completelands on the branch seconds after the merge and is stranded. Sometimes the builder never polls again at all and the commit is never created.Evidence:
2026-07-22T11:51:54Z;chore(porch): 1220 protocol complete(36c51ed7) committed11:52:30Z— 36 seconds after the merge. Not an ancestor of main; reachable only fromorigin/builder/air-1220. Main's status.yaml:phase: pr, gates: {pr: approved}.2026-07-22T22:50:35Z; noprotocol completecommit exists anywhere — the builder merged and never re-polled. Main:phase: pr, gates: {pr: approved}.pr: pending(e.g. bugfix-1110, bugfix-1122) show the harder variant: even thepr gate-approvedcommit missed the merge (approval ran after merge — the porch approve state-push silently fails when project tracking branch was auto-deleted on PR merge; breaks state-sync to builder worktree #801 scenario).Stratum 2 — structurally post-merge verify phase (SPIR, ASPIR)
For protocols with a
verifyphase, the merge is Step 1 of the verify phase (next.ts:810-826: "Verify phase: merge PR first, then verify"). Everything after —verify phase-transition,verify build-complete,verify-approval gate-requested,verify-approval gate-approved,protocol complete— is post-merge by design. No ordering discipline can fix this: main structurally cannot receive SPIR/ASPIR terminal state through the PR.Evidence — ASPIR 1210 (PR #1223, merged
2026-07-22T14:00:18Z), all onorigin/builder/aspir-1210, none ancestors of main:ee3adc92bfb910d775ebb7acfc088c4e41a4e5b2Main's snapshot:
phase: review,verify-approval: pending— a gate the human in fact approved. PR #1228 is the hand-made restore for exactly this.This gap is already acknowledged in code as deferred work —
packages/codev/src/commands/porch/index.ts:778-782: "The 'verified' state is committed to the builder branch, which may not be merged back to main. The closed GitHub Issue serves as the canonical 'done' signal on main. State alignment (making status.yaml on main authoritative) is tracked as future work per spec 653." This issue is that future work, now with data.Stratum 3 — cleanup destroys the only surviving copy
afx cleanupdeletes the local branch (agent-farm/commands/cleanup.ts:322) and the remote branch (cleanup.ts:167). After cleanup, the stranded terminal-state commits are unreachable and a #1228-style byte-for-byte restore becomes impossible. Main's stale snapshot is all that remains.Quantified impact (audit of main, 2026-07-22)
179
codev/projects/*/status.yamlon main; 106 not inverified/complete(a handful are genuinely in-flight; the overwhelming majority correspond to merged PRs — spot-verified: 1210, 1220, bugfix-1224, 671, 761, 787, 987, …):The SPIR/ASPIR column is the structural stratum (near-total loss); BUGFIX/PIR/AIR is the race stratum (roughly a coin flip).
Consequences:
porch pendingon main reports 140 phantom gates (some duplicated), oldest from 2026-04-15 — the human-facing gate queue is mostly noise.status.yaml(dashboard Work view, Needs Attention–style derivations, future tooling per Spec 653's "status.yaml is the project's history") sees merged, verified work as blocked at a gate.Relationship to existing issues
porch approvestate-push silently fails when the branch was auto-deleted: the push-side failure of the same design.This issue documents the main-side consequence — the terminal state never becoming visible where every consumer reads it — and quantifies it. #801/#802 describe where the commits go wrong; this one is about main never being told the protocol finished.
Possible directions (not prescriptive)
porch nextto continue" (matching porch's own notify text) instead of "please merge"; optionally have the merge task itself assert that theprotocol completecommit is on the PR head before merging.porch approve verify-approval/ terminalporch done, when the PR is already merged, writesstatus.yamlto the main checkout and commits it there (a small bookkeeping commit, analogous to what PR chore(porch): 1210 verify-approval gate-approved (restore stranded state) #1228 did by hand); orafx cleanupports final state to main before deleting the branch; or aporch reconcilethat sweeps merged projects and syncs terminal state.porch pending(and any dashboard derivation) cross-checks the PR/issue state and suppresses gates for merged work — aligning with the existing "closed GitHub Issue is the canonical done signal" stance inindex.ts:778-782.Whichever direction: the acceptance bar is that
porch pendingon a fresh main checkout reports no phantom gates for merged work, and a completed project's terminal state is recorded somewhere durable and reachable.