Skip to content

Prune stale replay blockers for deleted branches#23

Merged
KristjanPikhof merged 69 commits into
mainfrom
feat/prune-dead-branch-barriers
May 10, 2026
Merged

Prune stale replay blockers for deleted branches#23
KristjanPikhof merged 69 commits into
mainfrom
feat/prune-dead-branch-barriers

Conversation

@KristjanPikhof
Copy link
Copy Markdown
Owner

Feature Description

This adds dead-branch cleanup for stale ACD replay rows. When a feature branch has been merged and deleted, ACD can now prune stale pending, blocked_conflict, and failed capture rows for that dead branch instead of leaving phantom blockers in acd status.

The change also exposes the most recent cleanup through acd diagnose, so operators can see when stale dead-branch rows were removed without reading daemon logs.

Type of Change

  • New feature

Implementation Details

Architecture

  • Adds dead-branch sweep logic in internal/daemon/dead_branch_sweep.go.
  • Runs cleanup from both runtime Diverged branch transitions and daemon startup.
  • Keeps startup cleanup off the start-latency path by running it after the daemon publishes running.
  • Preserves rows when the old branch ref still exists.
  • Skips cleanup while the repo is manually paused.
  • Adds ACD_KEEP_DEAD_BRANCH_BARRIERS=1 as an operator opt-out for forensic inspection.
  • Updates state.PurgeUnpublishedForDeadBranch so it clears publish_state only when the singleton points at the deleted blocked row for the same branch/generation.

Key Files Changed

File Change
internal/daemon/dead_branch_sweep.go Dead-branch sweep and prune metadata
internal/state/events.go Transactional pending + terminal purge
internal/cli/diagnose.go Diagnose JSON and human output for last prune
internal/git/refs.go Live branch set helper
test/integration/dead_branch_prune_test.go End-to-end startup/runtime coverage
docs/capture-replay.md / README.md / CHANGELOG.md User-facing docs and release notes

API Changes

No external API endpoints.

CLI/JSON output change:

  • acd diagnose --json now includes:
    • dead_branch_prune_last_run_ts
    • dead_branch_prune_last_count
    • dead_branch_prune_last_refs

Database Changes

  • No schema migration
  • Adds daemon meta keys:
    • dead_branch_prune.last_run_ts
    • dead_branch_prune.last_count
    • dead_branch_prune.last_refs

How to Test

Validated locally:

cleanenv go test ./internal/state -run TestPurgeUnpublishedForDeadBranch -race -count=1
cleanenv go test ./internal/daemon -run 'TestRun_RuntimeDivergedPrunesDeadBranchTerminals|TestDeadBranchSweep' -race -count=1 -timeout 2m
cleanenv go test ./test/integration -tags=integration -run TestDeadBranchPrune -race -count=1 -timeout 5m
cleanenv make lint
cleanenv make test
cleanenv go test ./test/integration/... -tags=integration -race -count=1 -timeout 5m
cleanenv go test ./internal/daemon/... ./internal/git/... ./internal/state/... ./internal/pause/... ./internal/cli/... -race -count=3 -timeout 10m

Edge Cases Considered

  • Deleted branch refs are pruned.
  • Live branch refs are preserved.
  • Active branch/generation rows are preserved on startup.
  • Manual pause skips cleanup.
  • ACD_KEEP_DEAD_BRANCH_BARRIERS=1 keeps dead-branch rows.
  • Ref probe errors preserve rows.
  • Live publish_state blockers are not cleared while pruning a different dead branch.

Checklist

  • Added unit tests for new functionality
  • Added integration tests where applicable
  • Existing tests pass locally
  • Updated documentation
  • Backwards compatible

Feature Flag

  • No feature flag needed
  • Operator opt-out: ACD_KEEP_DEAD_BRANCH_BARRIERS=1

Related Issues

None found in branch name or commit messages.

Deployment Notes

Release notes and README status are set to v2026-05-10. The actual git tag has not been created or pushed yet.

- Add a ref-existence probe and a branch-scoped terminal-event cleanup
  helper so dead branches can be detected and their blocked/failed rows
  removed safely.
- Commit the new state-layer tests that pin DeleteTerminalForDeadBranch
  behavior: it removes only blocked_conflict and failed rows for the
  exact branch ref/generation, leaves other states and other pairs
  untouched, and is idempotent.
- Add RefExists coverage for present/missing refs, create/delete
  lifecycle, empty ref validation, and cancelled-context error handling
  in internal/git/refs_test.go.
- Add a new daemon helper module that sweeps terminal capture_events for
  dead branch refs, supports startup/runtime pruning, and honors an
  opt-out env var.
- Add startup cleanup for dead-branch terminal rows so a Diverged
  restart on a deleted branch prunes stuck barriers instead of leaving
  them behind.
- Selected the lone offered capture because it adds daemon logic to
  prune terminal rows for a dead prior branch after a Diverged
  transition, preventing phantom blocked_conflict/failed barriers from
  accumulating once the branch is merged and deleted upstream.
- Add a boot-time sweep for terminal capture rows whose branch refs were
  deleted, so restarts clean up dead-branch terminals even if the
  Diverged hook never ran. Preserve the existing env opt-out logging
  before the sweep.
- Add a new daemon test file covering the dead-branch barrier env helper
  and startup sweep pruning behavior for dead refs versus active refs.
- Add atomic dead-branch prune metadata stamping for diagnose reporting
- Update dead-branch sweep flows to stamp diagnose-visible prune
  metadata before emitting trace events, preserving operator-visible
  consistency and previous snapshot behavior on the zero-row path.
- Add dead-branch prune diagnostics to the CLI report, including the
  latest prune meta fields and the new diagnostic step in report
  construction.
- Single test-file change adds JSON and strconv imports in
  dead_branch_sweep_test.go, suggesting a cohesive update to the
  existing dead-branch sweep test coverage.
- Adds two tests around startup dead-branch sweep meta stamping: one
  when pruning rows to verify the three meta keys are written, and one
  when the sweep is a no-op to verify existing meta is left untouched.
- Tests cover diagnose reporting for dead-branch prune metadata in both
  populated and absent states, asserting JSON field names and zero-value
  omission in the same file.
- Add an integration test that exercises dead-branch prune behavior
  end-to-end in the production binary, including diagnose JSON
  meta-field assertions and related terminal-row pruning/preservation
  scenarios.
- Remove an unused branch-generation test helper from the dead-branch
  prune integration test.
- The changelog entry and CLAUDE.md guidance describe the same
  dead-branch terminal auto-prune feature, including its opt-out flag
  and diagnostics, so they belong in one documentation commit.
- Selected capture updates README diagnostics text to describe the new
  dead-branch prune timestamps/counts/refs and the auto-pruning of
  blocked_conflict/failed rows for deleted branch refs, which matches
  the recent terminal auto-prune behavior work.
- Remove both the startup sweep and Diverged-time pruning hooks for
  dead-branch terminal captures in the daemon, since the captured diff
  deletes the cleanup paths in one file.
- Select the daemon cleanup changes as one commit: they add startup
  sweeping of dead-branch terminal capture_events rows and runtime
  pruning after Diverged when a branch ref has been deleted.
- Commit intent covers the daemon cleanup that removes the startup
  dead-branch pruning block. The .bak file is a rescan artifact and
  should not be committed with the code change.
- Restore startup dead-branch terminal sweeping in the daemon so a
  restart cleans up deleted-branch queue entries even if Diverged never
  ran.
- Forced aging applies, so take the single overdue capture. It creates a
  new backup of the daemon implementation containing the long-running
  run-loop code.
- Drop the daemon startup dead-branch sweep and its env log from Run so
  startup no longer performs that pruning path.
- Reinstate the daemon startup sweep for dead-branch terminal captures,
  and remove the obsolete daemon backup file that is no longer needed
  once the main implementation is restored.
- Expand dead-branch cleanup to remove unpublished pending and terminal
  rows together, and clear publish-state breadcrumbs/barriers when the
  deleted row was referenced.
- Expand the dead-branch purge test to cover pending rows alongside
  terminal rows, and to verify the singleton blocker barrier is lifted
  and breadcrumbs are cleared when publish_state is blocked_conflict.
- Update the diagnose report schema so dead-branch prune timestamp/count
  are always emitted as explicit zero-valued JSON fields, while leaving
  the refs slice omitempty.
- Update the diagnose dead-branch prune comments to match the JSON
  behavior: the two integer counters now intentionally serialize as 0
  instead of omitting, while the refs slice still omits when empty.
- Adds an end-to-end daemon regression test that drives the runtime
  Diverged path after a branch deletion and verifies dead-branch
  terminal pruning via the prune timestamp meta key.
- The diff removes an obsolete helper comment immediately before an
  existing daemon test, so this capture stands alone as a small test
  cleanup.
- Adjust the daemon test to set up a feature branch at boot, pre-seed
  blocked/pending terminal rows for that branch, and exercise the
  runtime Diverged prune path after the branch is removed.
- Update CLAUDE.md to describe the refined dead-branch pruning
  semantics: unified pruning of pending and terminal rows, publish-state
  barrier cleanup, breadcrumb clearing, startup goroutine behavior,
  batched live-branch membership checks, and pause/keep-barrier
  handling.
- Update the README’s dead-branch prune section to note that `pending`
  rows are now pruned together with `blocked_conflict` and `failed`
  rows, along with the JSON rendering details for the prune counters.
- Update the changelog entry to describe broader dead-branch pruning for
  pending and terminal rows, startup sweep behavior, diagnostics, and
  opt-out handling.
- Update the dead-branch sweeper comment to reflect the renamed
  best-effort purge helper used for unpublished terminals.
- Update the fresh-repo diagnose-meta test comment to describe the JSON
  contract more precisely: integer fields are always present with 0 as
  the sentinel, while the refs slice stays omitempty when nil.
- Update the dead-branch prune integration test comment to clarify the
  no-prune JSON contract: the two int fields always emit `0` while the
  refs slice stays omitted.
- Single cleanup in daemon startup code: remove an extra blank line with
  no behavioral change.
- Select the dead-branch purge refinement that now tracks blocked seqs
  and only clears publish_state when it matches the same branch
  generation and blocked event.
- Extend dead-branch purge coverage to ensure it deletes the dead
  blocked row without lifting a separate live branch's blocked
  publish_state or clearing its breadcrumbs.
- Bind the captured context before launching the startup dead-branch
  sweep goroutine so the sweep uses the intended capture context instead
  of relying on the outer variable.
- Adds/updates integration coverage in dead_branch_prune_test.go for the
  dead-branch prune flow, including a new runtime Diverged prune
  scenario, a helper for reading the current branch generation, and test
  setup adjustments such as the sqlite busy timeout.
- This change adds a new integration test for the same
  ACD_KEEP_DEAD_BRANCH_BARRIERS=1 opt-out behavior on the runtime
  Diverged path, and tightens the nearby comment to scope the existing
  test to startup sweep only. Both edits belong to the dead-branch prune
  opt-out coverage in test/integration/dead_branch_prune_test.go.
- Update the dead-branch prune integration test to read the renamed
  daemon_meta key in both branch-token wait checks.
- Add the new team agent routing config file under .pi/agent to record
  schemaVersion 4 and solo routing mode.
- Document the dead-branch prune diagnostics and stale-row cleanup
  behavior in the changelog.
- Update the README’s dead-branch prune documentation to reflect the
  revised wording around stale pending/blocked_conflict/failed rows,
  paused repos, and the keep-forensics flag.
- Update the capture replay documentation to describe dead-branch
  cleanup behavior, including stale row pruning after daemon start and
  the effect of the keep-dead-branch-barriers flag.
- Document the dead-branch cleanup behavior in user-workflows docs,
  including the stale row types removed on branch transition or daemon
  startup, the diagnostic fields that report the last prune, and the
  opt-out env var for inspection before cleanup.
- Document the refined dead-branch prune behavior in CLAUDE.md,
  especially that publish_state barriers are lifted only for deleted
  blocked rows on the same branch/generation pair and that live blockers
  remain preserved.
- Delete the team agent routing config file, removing the obsolete
  agents-team.json entry.
- Update the changelog heading to the new v2026-05-10 release date.
- README status line advances the last-tag reference to match the new
  v2026-05-10 release date.
@KristjanPikhof KristjanPikhof self-assigned this May 10, 2026
@KristjanPikhof KristjanPikhof marked this pull request as ready for review May 10, 2026 20:32
@KristjanPikhof KristjanPikhof merged commit 4d9dfef into main May 10, 2026
5 checks passed
@KristjanPikhof KristjanPikhof deleted the feat/prune-dead-branch-barriers branch May 10, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant