Skip to content

porch: builder branches carry many chore commits per cycle across all protocols — discuss approaches to reduce noise without losing audit value #994

@amrmelsayed

Description

@amrmelsayed

Umbrella / discussion issue. Frames the porch-chore-commit volume on builder branches across all porch-driven protocols (SPIR, ASPIR, PIR, AIR, BUGFIX), the design rationale for the current shape, and several approaches to reducing it. The purpose is team discussion of trade-offs, not a committed plan. Each option preserves or trades audit value differently; the right answer depends on which trade-offs the team finds acceptable.

Observed shape

Every porch-driven protocol writes chore commits as the state machine advances. The volume varies by gate count and phase count, but the pattern is universal: every builder branch carries a meaningful number of chore(porch): commits alongside the substantive work.

Sampling eleven recently-merged builders spanning all five porch-driven protocols:

Builder Protocol Total commits Implementation + plan / thread / review Porch chores
spir-927 SPIR 32 12 20
spir-823 SPIR 55 32 23
spir-786 SPIR 65 33 32
aspir-694 ASPIR 25 10 15
pir-967 PIR 15 4 11
pir-971 PIR 20 8 12
pir-812 PIR 18 7 11
bugfix-974 BUGFIX 8 2 6
bugfix-985 BUGFIX 7 2 5
bugfix-966 BUGFIX 9 4 5
air-943 AIR 6 2 4

ASPIR data caveat: aspir-694 (last merged 2026-04-22) predates the universal pr-gate mechanic added via #927 / #872. Modern ASPIR would inherit pr-gate chores (pr gate-requested + pr gate-approved), pushing the count up by ~2. ASPIR has been dormant in usage since April; the most recent merges from March (aspir-650, aspir-637) shipped with zero chore commits, predating the porch chore-commit shape entirely.

Per-protocol typical proportions (chore count scales with gates, phases, and plan-phase iteration):

  • SPIR (4 gates, 5+ phases, per-plan-phase iteration): ~20-32 porch chores depending on plan-phase count. spir-786 with 7 plan phases hit 32 chores; spir-927 with 4 plan phases hit 20. Plan-phase iteration is the dominant scaling factor; SPIR's implement phase is itself a loop over the plan's phases, and each iteration adds advance plan phase, implement build-complete, and implement review-recorded chores.
  • ASPIR (SPIR minus spec-approval and plan-approval gates): in current porch, ~16-28 porch chores (SPIR count minus 4 for the dropped autonomous gates). Same plan-phase iteration as SPIR. The single recent sample (aspir-694) hit 15 chores under an older porch shape; modern equivalent would be ~17-18.
  • PIR (3 gates, 3 phases, no plan-phase loop): 11-12 porch chores per cycle, ±1 for the optional protocol complete event.
  • BUGFIX (1 gate, 2 phases): 5-6 porch chores per cycle.
  • AIR (1 gate, 2 phases, no plan): 3-4 porch chores per cycle.

The mechanical formula for the gate-driven portion: 1 (init) + (gates × 2) + (phase_transitions) + 3 (record PR + review build-complete + optional protocol-complete). SPIR and ASPIR add a multiplier for plan-phase iteration that the others don't have.

For the more gate-heavy protocols (SPIR, ASPIR, PIR), roughly two-thirds of the merged commits on a typical builder branch are porch bookkeeping rather than substantive work. For BUGFIX and AIR the ratio is smaller but still meaningful relative to a typical 1-3 implementation commits.

What's in a porch chore commit

Each commit message follows the shape chore(porch): <id> <event-name>. The event types (consistent across protocols):

  • init <protocol>
  • <phase> phase-transition (one per phase entered)
  • <gate> gate-requested / <gate> gate-approved (two per gate)
  • record PR #<n>
  • review build-complete
  • protocol complete

The count scales mechanically: every gate adds two chore commits, every phase adds one. SPIR (with specify, plan, implement, review, verify phases and spec-approval, plan-approval, pr, verify-approval gates) is the heaviest by design; AIR (with only implement and pr phases and one gate) is the lightest.

The commits typically modify only codev/projects/<id>/status.yaml, recording the new state in YAML form. Some also touch the builder thread file.

Why the design is this way

The single-commit-per-event model exists so that a future reader can reconstruct the full protocol history of a project from git log alone, without needing to read interleaved YAML snapshots. The audit trail is the load-bearing rationale:

  • "When did the plan-approval gate get requested?" → git log --grep="plan-approval gate-requested"
  • "What did status.yaml look like at the moment dev-approval was approved?" → git show <commit>:codev/projects/<id>/status.yaml
  • "Did porch advance phases in the expected order?" → git log --grep="phase-transition" --oneline

This is genuinely valuable when debugging a porch state-machine bug or reconstructing what happened during a builder's run after the fact.

The trade-off is signal-to-noise on the merged branch's history: substantive work commits are buried among bookkeeping commits.

The choice axis

Every approach below sits somewhere on a single trade-off axis:

Less porch granularity ↔ Less audit value

Approaches that reduce commit count typically reduce the granularity of the audit trail, or move the audit trail to a different surface, or both. Approaches that preserve full audit value tend to either keep the noise or address it cosmetically through filtering.

Approaches for discussion

Sorted from lightest intervention (no porch change, no audit loss) to heaviest (porch refactor with audit trade-offs).

Option 1: Tooling only, no porch change

Improve the affordances for filtering porch chores out of git log views without touching what porch writes.

  • A standard git substantive-log alias defined in repo docs or .gitconfig examples (git log --invert-grep --grep="^chore(porch)" --no-merges --oneline)
  • VS Code Git Graph filter recommendations
  • Architect docs pointing at the alias when scanning branch history

Preserves: 100% of the audit trail. No protocol change.

Costs: zero. The chores are still there; they're just hidden in filtered views.

Doesn't help with: cases where the full unfiltered git log is what's being read (release-notes assembly, post-merge audit by hand, etc.). The chore commits still ship to main and still appear in the merge commit's first-parent history.

Verdict pre-discussion: probably worth doing regardless of whether anything heavier ships. Cheap, useful, no downside.

Option 2: Batch porch chores per phase

Instead of writing one commit per event (phase-transition, gate-requested, gate-approved, etc.), porch buffers the state changes within a phase and commits once at each natural phase boundary.

Across protocols this might cut chore counts by roughly two-thirds: SPIR/ASPIR from ~14-16 to ~6-7, PIR from ~12 to ~4-5, BUGFIX from ~5-6 to ~3, AIR from ~3-4 to ~2. One commit per phase transition plus terminal events, instead of one commit per gate-request, per gate-approval, and per phase-transition.

Preserves: the high-level audit (you can still see "when did each phase end?"). Loses the fine-grained (you can't distinguish a gate-requested event from a gate-approved event by commit alone; have to read status.yaml diffs).

Costs: real porch refactor. Phase-boundary detection has edge cases (what about events that fire outside any phase? what if a phase aborts mid-stream?). Mid-range engineering work.

Doesn't help with: the plan/thread/review architect-artifact commits (those are intentional narrative).

Open question: is the per-event granularity ever the thing being read, or is per-phase always sufficient?

Option 3: Compress porch state into git notes

Git notes attach metadata to commits without appearing in the commit graph. Porch could write state-transition records as notes on whatever the last substantive commit is, rather than as new commits on the branch.

Preserves: full per-event audit (notes can be as granular as commits). Moves the audit to a parallel git surface that doesn't pollute git log.

Costs: notes don't ship with merges by default. The team would have to push and pull a separate ref (refs/notes/<namespace>) to maintain the audit trail. Tooling for reading notes (git log --show-notes) is less ergonomic than for commits.

Open question: how often is the audit trail actually consulted? If rarely, the notes-ergonomics cost may be acceptable. If often, the cost compounds.

Option 4: Replace chore commits with status.yaml version history

Don't commit state changes at all. Porch updates status.yaml in the working tree; the file's own version history (via the substantive commits that touch it) carries the record. Read the audit by walking the file's git blame.

Preserves: a reconstructable but lower-resolution audit (only states observed at commit boundaries, not events in between).

Costs: lose the per-event audit entirely. "Did the gate-requested event fire before or after the implement-phase transition?" becomes unanswerable without external logging. Reconstructing event ordering from status.yaml snapshots only is harder than reading event commits directly.

Heaviest in audit-value terms. Lightest in commit-count terms.

Option 5: Selective merge / per-branch porch chores

Porch chores live on the builder branch but are filtered out at merge time, so they don't ship to main. Substantive commits land on main via the merge; chore commits remain on the builder branch and get deleted with cleanup.

Preserves: full audit while the builder is active (on the builder branch). Loses post-cleanup audit (the chore branch is gone).

Costs: requires changes to the merge tooling (the current gh pr merge --merge includes the full branch history). Probably needs gh pr merge --squash for the chore-only portion and --merge for the substantive portion, which doesn't compose. Alternative: rebase the builder branch to drop chore commits before merging, which contradicts the no-squash, no-rewrite conventions.

Doesn't compose with the established no-squash policy. Probably off the table unless that policy is revisited.

Option 6: Compress chore commits into per-gate or per-phase summary commits

Variant of Option 2 with different granularity. Instead of per-phase, compress to per-gate (one commit per gate's lifecycle: gate-X opened-and-closed) or per-protocol-completion (one commit at the end summarizing all transitions).

Preserves: depending on the level, intermediate to high audit. Per-gate keeps gate-level audit; per-completion loses everything but final state.

Costs: same porch refactor cost as Option 2.

Trade-off: granularity vs commit count. Could pick a level that matches how the audit is actually used.

Open questions for the discussion

  1. How often is the audit trail read? If once a quarter, less frequently than the noise is felt, the cost-benefit favours noise reduction. If daily (e.g. by automated tooling), preserving audit matters more.
  2. Is per-event granularity ever the right resolution? Or is per-phase / per-gate always enough? This determines how much Option 2 / 6 can compress without losing what people actually use.
  3. Is the noise actually a problem in practice? The numbers look high but most architects use gh pr view <n> or filtered git log invocations rather than scanning raw history. Maybe Option 1 (tooling) addresses the felt experience and the deeper options aren't worth the cost.
  4. What downstream tooling depends on the current commit shape? If anything (CI, release scripts, analytics) parses commits via chore(porch) prefix matching, changing the shape requires coordinated updates.
  5. Is the no-squash convention itself the right one? The policy values traceability; if the audit is actually carried by porch chores rather than substantive commits, maybe squashing substantive commits IS acceptable as long as the chore commits' content is preserved elsewhere. Or vice versa.

What this issue isn't

  • Not a committed plan to do any of the above. Each option needs its own follow-up issue if the discussion converges on it.
  • Not a critique of the porch design. The current shape is the result of a deliberate trade-off. This issue surfaces the trade-off for re-examination, not its reversal.
  • Not asking for squash merges. The no-squash convention is unchanged unless explicitly revisited.

Related

  • codev/resources/arch.md and codev/resources/lessons-learned.md — the durable narrative surfaces that exist alongside git log.
  • BUGFIX, AIR protocol definitions in codev-skeleton/protocols/ — they already demonstrate that fewer gates produce fewer chore commits, validating that the count is mechanically driven by the gate set.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/porchArea: Porch state machine / protocol orchestrationprojectNew project or feature

    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