Skip to content

porch: state-bookkeeping commits accumulate as orphan artifacts on builder branches when not auto-deleted on PR merge #802

@amrmelsayed

Description

@amrmelsayed

Problem

porch's writeStateAndCommit (packages/codev/src/commands/porch/state.ts:184-210) commits and pushes a status.yaml-only update to the project's tracking branch on every state-machine transition: phase transitions, gate requests, gate approvals, verify skips, and merge recordings. The intent is explicit in the function's docstring (per Spec 653 §B.3):

"every phase transition, gate request, gate approval, and verify skip must commit and push status.yaml. Zero gaps."

This is correct behavior while the project is active. The problem is post-PR-merge transitions — architect-side gate approvals, builder-side porch done --merged, automatic phase advancement to verified, etc. — that happen after the project's tracking branch's content has already been merged into the integration branch.

When GitHub's "auto-delete branch on merge" repo setting fires, these post-merge pushes silently fail (sibling defect linked below). When it does not fire (because the setting is off, or it didn't fire for the merge in question), the opposite outcome occurs: each post-merge state transition lands a chore(porch): ... commit on the source branch, accumulating as dead orphan artifacts that:

  • Will never be merged back to the integration branch (the PR was already merged at an earlier point).
  • Reference deleted/orphan worktrees and out-of-date project state.
  • Live on the remote until the branch is manually deleted, polluting branch listings.

Observed (concrete example)

After a project completed and its PR was merged, the source branch retained three post-merge commits, all status.yaml-only:

chore(porch): <id> PR #<N> merged       ← from builder's `porch done --merged <N>`
chore(porch): <id> protocol complete    ← porch's automatic phase-transition to `verified`
chore(porch): <id> pr gate-approved     ← from architect's `porch approve <id> pr ...`

All three landed after the PR merge commit on the integration branch. None will ever be merged back. None reference anything that's still actively reachable from the integration branch's history. The branch is in a state where its HEAD is a post-merge bookkeeping commit, but its substantive content was already merged at an earlier point.

Why it happens

writeStateAndCommit has no awareness of whether the project is pre-merge or post-merge. It unconditionally commits and pushes on every state-machine event. The state machine itself continues to transition past PR merge (architect approves the pr gate → porch advances phase → builder records merge → porch advances to verified), each transition firing another commit-and-push.

There is no flag, no protocol guard, and no branch-state check that distinguishes "pre-merge: push needed for state-sync to other worktrees" from "post-merge: push is a dead-letter to a branch nobody will read."

Impact

  • Branch listings accumulate stale builder/* branches with post-merge bookkeeping commits, even after their PRs have shipped — visual noise + audit-trail-search noise.
  • The bookkeeping commits themselves are dead — they reference porch state that nothing consumes downstream (the merged-into-integration version of status.yaml is what gets carried forward; the post-merge updates land on an unreached branch).
  • Operators see contradictory states: the branch's HEAD says chore(porch): <id> PR #N merged, but the branch is otherwise functionally dead post-merge. Easy to mistake for in-progress work.
  • Combined with the sibling defect, both outcomes — silent push-fail when the branch is deleted, dead commits when it persists — reflect the same misuse: porch using ephemeral PR source-branches as durable state storage. Two flip-side failure modes of one underlying design choice.

Related

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions