Skip to content

feat: terminal Run Worktree reconciliation - #39

Merged
marcioaltoe merged 17 commits into
mainfrom
ma/terminal-run-worktree-reconciliation
Jul 27, 2026
Merged

feat: terminal Run Worktree reconciliation#39
marcioaltoe merged 17 commits into
mainfrom
ma/terminal-run-worktree-reconciliation

Conversation

@marcioaltoe

@marcioaltoe marcioaltoe commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Implements spec 0038-terminal-run-worktree-reconciliation (archived in this branch after a passing QA gate).

What changed

  • Positive-proof classification — terminal Run Worktrees are classified from ancestry and cleanliness evidence instead of the old reaper's reflog-creation-base heuristic, which had neither target-ancestry evidence nor a freshness check between inspection and mutation.
  • Stale-proof cleanup — cleanup revalidates its inspection before mutating, and the existing terminal reaping paths migrate onto the shared classifier so automatic and explicit cleanup cannot disagree about safety.
  • Integration Pending reconciliation — recovery records durable evidence through spec 0037's guarded ReconcileIntegration, which until now had no production caller.
  • Reconcile Commandroundfix reconcile inspects retained terminal Run Worktrees, with dry-run and apply semantics.
  • Runs List surfaces retained worktreesroundfix runs list reports retained terminal Run Worktrees and points at the command to inspect them.
  • Docs, glossary, and the Roundfix Skill pair aligned with the new contracts.

Verification

  • QA gate executed the full matrix on build 3744d03: verdict pass (docs/specs/_archived/0038-terminal-run-worktree-reconciliation/qa/qa-report-2026-07-27.md). Earlier failing reports are preserved in history.
  • Every Task settled completed with its declared Verification passing.
  • make verify green on the delivered tree (2,563 tests across 23 packages).

Findings recorded from this cycle

Five pending findings under docs/findings/, three of them defects in code shipped earlier today:

  • Force Stop reads owner identity by forking ps, so a host that cannot fork makes the escape hatch refuse exactly when a runaway Run is exhausting process slots.
  • Derived Skill digest pins have no regeneration path, blocking their own Task three times in one day.
  • go build ./cmd/roundfix without -o writes an unignored 20 MiB binary at the repository root; the one-line fix needs maintainer authorization for a protected ignore file.
  • Sandboxed Agents cannot always reach the default GOCACHE, which blocked this Spec's QA gate before compilation.
  • The Claude adapter should standardize on the official @agentclientprotocol/claude-agent-acp, which also surfaced that Roundfix misreads advertised model ids like opus[1m] as a reasoning effort.

https://claude.ai/code/session_01KwYiSLnmzdHmrmV4XWmQge

Summary by CodeRabbit

  • New Features
    • Added reconcile command for inspecting retained terminal Run Worktrees, with dry-run reports in text or JSON and guarded cleanup via --apply.
    • Introduced reconciliation state classification (safe, unintegrated, dirty, unknown, released) with cleanup limited to freshly revalidated safe items (no force bypass).
    • runs list now highlights retained terminal worktrees and recommends reconciliation.
  • Documentation
    • Expanded reconciliation workflow guidance and clarified safety rules and state meanings.
  • Bug Fixes
    • Improved terminal worktree handling to preserve eligible runs and avoid unsafe/incorrect cleanup.
  • Chores
    • Updated QA fixtures generation and refreshed baseline/corpus digests.

Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
Roundfix-Task: task_01
Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
Roundfix-Task: task_02
Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
Roundfix-Task: task_03
Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
Roundfix-Task: task_04
Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
Roundfix-Task: task_05
Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
Roundfix-Task: task_06
Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
Roundfix-Task: task_07
The 2026-07-26 tooling authorization covered exactly the two roundfix
SKILL.md files, so task_07's authorized Skill edit could not carry its
own deterministic digest fallout and settled failed for the third time
today. Record the maintainer's 2026-07-27 express authorization of the
five derived pins in both active Spec artifacts, as a supervisor-owned
boundary commit rather than inside the Task commit it would authorize.

Add three findings from this dogfood cycle:

- derived Skill digest pins have no regeneration path, so every Skill
  edit needs five hashes transcribed by hand and fails its own Task's
  changed-path check
- Force Stop reads owner identity by forking ps, so a host that cannot
  fork makes the escape hatch refuse exactly when a runaway Run is
  exhausting process slots, and a failed capture at Run creation leaves
  a Run silently without reuse protection
- the Claude adapter should standardize on the official
  @agentclientprotocol/claude-agent-acp, which also exposes that
  Roundfix misreads advertised ids like opus[1m] as a reasoning effort

Claude-Session: https://claude.ai/code/session_01KwYiSLnmzdHmrmV4XWmQge
QA finding RFQA-0038-01: task_04's commit added a 20.1 MiB Mach-O
executable at the repository root. It came from running
`go build ./cmd/roundfix` without -o, which writes ./roundfix, a path
.gitignore did not cover because it only ignored /bin/.

Remove the tracked binary and ignore that path so a bare build can no
longer be swept into a commit. The Makefile keeps writing the real
build output to the ignored bin/roundfix.

Claude-Session: https://claude.ai/code/session_01KwYiSLnmzdHmrmV4XWmQge
Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
The Spec 0038 QA gate flagged repair commit af03b5f for editing
.gitignore, which docs/agents/agent-instructions.md prohibits without
express maintainer authorization. The gate is right: removing the stray
root binary was the required repair; adding the ignore entry exceeded
it. Restore .gitignore and record the still-open recurrence risk as a
finding instead.

Claude-Session: https://claude.ai/code/session_01KwYiSLnmzdHmrmV4XWmQge
QA finding RFQA-0038-02: the previous revert restored .gitignore's
content but added a trailing newline the base blob did not have, so the
protected ignore file was still byte-modified without authorization.
Restore blob 498dd1c exactly.

Also record that sandboxed Agents cannot always reach the default
GOCACHE, which blocked this Spec's QA gate before compilation and made
part of a verdict rest on checks that never ran.

Claude-Session: https://claude.ai/code/session_01KwYiSLnmzdHmrmV4XWmQge
Roundfix-Spec: 0038-terminal-run-worktree-reconciliation
Project Config change: review_source.include_nitpicks now true, so
nitpick-severity findings become Review Issues that watch Runs triage
and resolve instead of ignoring.

Claude-Session: https://claude.ai/code/session_01KwYiSLnmzdHmrmV4XWmQge
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bd3c184f-8503-4411-aaaf-4f23944a1fc3

📥 Commits

Reviewing files that changed from the base of the PR and between 44fa422 and b9102d8.

⛔ Files ignored due to path filters (8)
  • docs/specs/_reviews/pr-39/round-001/issue_001.md is excluded by none and included by none
  • docs/specs/_reviews/pr-39/round-001/issue_002.md is excluded by none and included by none
  • docs/specs/_reviews/pr-39/round-001/issue_003.md is excluded by none and included by none
  • docs/specs/_reviews/pr-39/round-001/issue_004.md is excluded by none and included by none
  • docs/specs/_reviews/pr-39/round-001/issue_005.md is excluded by none and included by none
  • docs/specs/_reviews/pr-39/round-001/issue_006.md is excluded by none and included by none
  • docs/specs/_reviews/pr-39/round-001/issue_007.md is excluded by none and included by none
  • docs/specs/_reviews/pr-39/round-001/round.md is excluded by none and included by none
📒 Files selected for processing (7)
  • internal/cli/cli.go
  • internal/cli/cli_test.go
  • internal/cli/implement.go
  • internal/cli/runs.go
  • internal/store/store.go
  • internal/worktree/worktree.go
  • internal/worktree/worktree_test.go

Walkthrough

This change adds terminal Run Worktree reconciliation with five-state inspection, evidence-backed cleanup, persisted reconciliation events, a new reconcile CLI command, retained-worktree guidance, updated pruning behavior, QA fixtures, and related tests and baselines.

Changes

Terminal Run Worktree Reconciliation

Layer / File(s) Summary
Inspection and guarded cleanup
internal/worktree/worktree.go, internal/worktree/worktree_test.go
Terminal runs are classified as safe, unintegrated, dirty, unknown, or released; safe cleanup rechecks evidence before removing worktrees and branches.
Reconciliation evidence persistence
internal/store/store.go, internal/store/store_test.go
Reconciliation records expanded Git/worktree evidence, validates invariants, journals terminal outcomes, and handles identical replays idempotently.
Reconcile command and reporting
internal/cli/reconcile.go, internal/cli/cli.go, internal/cli/runs.go, internal/cli/cli_test.go
The CLI supports read-only text or JSON inspection and --apply cleanup of safe runs, while runs list reports retained worktrees.
Pruning integration and regression coverage
internal/worktree/worktree.go, internal/cli/implement.go, internal/cli/implement_test.go, internal/cli/settle_test.go, internal/worktree/worktree_test.go
Terminal pruning now uses run lookup and reconciliation evidence, preserving branches and worktrees without cleanliness or integration proof.
Documentation, QA fixtures, and baselines
.agents/skills/..., CONTEXT.md, docs/specs/..., internal/baseline/..., .roundfixrc.yml
Documentation, QA fixture tooling, and generated baseline digests are updated for reconciliation coverage.

Estimated code review effort: 5 (Critical) | ~90+ minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant RunStore
  participant InspectTerminalRun
  participant ApplyTerminalRun
  participant Git
  CLI->>RunStore: load terminal implementation runs
  CLI->>InspectTerminalRun: classify Git and worktree state
  InspectTerminalRun->>Git: inspect heads, ancestry, registration, and cleanliness
  CLI->>ApplyTerminalRun: apply cleanup for safe runs
  ApplyTerminalRun->>RunStore: persist reconciliation evidence
  ApplyTerminalRun->>Git: remove worktree and run branches
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.72% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the main change: terminal Run Worktree reconciliation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ma/terminal-run-worktree-reconciliation

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/store/store.go (1)

481-650: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

ReconcileIntegration is ~170 lines and mixes validation, replay detection, transition, and journaling.

The guideline limit is 120 lines per function. Extracting the stored-invariant checks (lines 508-540), the replay lookup (562-589), and the Integration Pending compare-and-set (606-630) into helpers would keep each concern independently testable without changing behavior.

As per coding guidelines: "Keep cyclomatic complexity at or below 13, avoid deeply nested conditionals, limit functions to 120 lines and 80 statements".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/store/store.go` around lines 481 - 650, Refactor
ReconcileIntegration to stay under the 120-line guideline by extracting the
stored-invariant checks, replay lookup, and Integration Pending compare-and-set
into focused helpers. Preserve all existing validation, error messages,
transaction behavior, replay handling, and state-transition semantics while
keeping ReconcileIntegration responsible for orchestration and journaling.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/cli/runs.go`:
- Line 114: Update countRetainedTerminalRunWorktrees and its InspectTerminalRun
loop to avoid inspecting every terminal Run from the unbounded query: skip empty
WorkDir values and bound inspections to Runs relevant to the requested
state/limit, or otherwise enforce a safe cap. In the inspection error path
around the retained-worktree count, report the failure with diagnostic context
instead of silently treating it as retained.
- Around line 121-127: Update the note-rendering logic in the runs-list flow so
runsListRetainedWorktreeNote and runsListHiddenNote can both be emitted when
applicable, instead of the retained-worktree note suppressing hidden/older
guidance. Preserve the existing note formatting and update the exact stderr
expectations in the related CLI tests.

In `@internal/store/store.go`:
- Around line 532-540: The Run Branch prefix is duplicated in the reconciliation
check and worktree naming logic. Extract the prefix into a dependency-safe
shared symbol, or make it a store-owned exported constant consumed by
worktree.BranchName, then update the reconciliation validation around
expectedRunBranch to derive the expected name through that single source instead
of concatenating "roundfix/run-" directly.

In `@internal/worktree/worktree_test.go`:
- Around line 1552-1562: Remove the hand-rolled slicesEqual helper and import
the standard library slices package. Replace all three slicesEqual call sites in
the affected tests with slices.Equal, preserving the existing comparison
arguments and behavior.
- Around line 867-895: Add goleak coverage for the goroutines introduced by
TestInspectTerminalRunConcurrentSafe, using goleak.VerifyNone around the
concurrent test or goleak.VerifyTestMain via a package TestMain. Ensure leaked
goroutines cause the internal/worktree tests to fail while preserving the
existing inspection assertions.

In `@internal/worktree/worktree.go`:
- Around line 218-222: Update the worktree reconciliation flow around
recordedWorktree so an existing directory that is not registered in the Git root
produces reconciliationReasonWorktreeUnregistered, while genuinely unsafe paths
continue using reconciliationReasonWorktreePath. Distinguish these outcomes
using the recordedWorktree result and preserve the existing return behavior.
- Around line 849-857: Update the TerminalRunLookup function type to accept
context.Context as its first parameter, then pass the PruneTerminal ctx through
every lookup invocation within PruneTerminal and related reconciliation paths.
Update all implementations and callers to use the context-aware Run Store read,
preserving existing lookup behavior while propagating cancellation and
deadlines.

---

Outside diff comments:
In `@internal/store/store.go`:
- Around line 481-650: Refactor ReconcileIntegration to stay under the 120-line
guideline by extracting the stored-invariant checks, replay lookup, and
Integration Pending compare-and-set into focused helpers. Preserve all existing
validation, error messages, transaction behavior, replay handling, and
state-transition semantics while keeping ReconcileIntegration responsible for
orchestration and journaling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2548c3ce-5441-4a82-a37d-3f5db6149845

📥 Commits

Reviewing files that changed from the base of the PR and between 8ec92ad and 44fa422.

⛔ Files ignored due to path filters (29)
  • docs/findings/2026-07-27-bare-go-build-writes-an-untracked-root-binary.md is excluded by none and included by none
  • docs/findings/2026-07-27-claude-adapter-standardization.md is excluded by none and included by none
  • docs/findings/2026-07-27-derived-skill-digest-pins-have-no-regeneration-path.md is excluded by none and included by none
  • docs/findings/2026-07-27-owner-identity-forks-ps-and-fails-closed-under-load.md is excluded by none and included by none
  • docs/findings/2026-07-27-sandboxed-agents-cannot-reach-the-default-go-cache.md is excluded by none and included by none
  • docs/specs/0038-terminal-run-worktree-reconciliation/task_01.md is excluded by none and included by none
  • docs/specs/0038-terminal-run-worktree-reconciliation/task_02.md is excluded by none and included by none
  • docs/specs/0038-terminal-run-worktree-reconciliation/task_03.md is excluded by none and included by none
  • docs/specs/0038-terminal-run-worktree-reconciliation/task_04.md is excluded by none and included by none
  • docs/specs/0038-terminal-run-worktree-reconciliation/task_05.md is excluded by none and included by none
  • docs/specs/0038-terminal-run-worktree-reconciliation/task_06.md is excluded by none and included by none
  • docs/specs/0038-terminal-run-worktree-reconciliation/task_07.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/_prd.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/_tasks.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/_techspec.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/qa/evidence/2026-07-27-terminal-reconciliation-rerun/run-summary.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/qa/evidence/2026-07-27-terminal-reconciliation/run-summary.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/qa/qa-report-2026-07-27.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/task_01.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/task_02.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/task_03.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/task_04.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/task_05.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/task_06.md is excluded by none and included by none
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/task_07.md is excluded by none and included by none
  • docs/user-guide/commands.md is excluded by none and included by none
  • docs/user-guide/usage.md is excluded by none and included by none
  • internal/baseline/testdata/catalog.digest is excluded by !**/*.digest and included by none
  • skills/roundfix/SKILL.md is excluded by !skills/** and included by none
📒 Files selected for processing (19)
  • .agents/skills/roundfix/SKILL.md
  • .roundfixrc.yml
  • CONTEXT.md
  • docs/specs/_archived/0038-terminal-run-worktree-reconciliation/qa/evidence/2026-07-27-terminal-reconciliation-rerun/fixture.go
  • internal/baseline/assets/setups/typescript-bun.json
  • internal/baseline/testdata/catalog.normalized.json
  • internal/baseline/testdata/parity-corpus/v1/fixtures/asset-sync.json
  • internal/baseline/testdata/parity-corpus/v1/manifest.json
  • internal/cli/cli.go
  • internal/cli/cli_test.go
  • internal/cli/implement.go
  • internal/cli/implement_test.go
  • internal/cli/reconcile.go
  • internal/cli/runs.go
  • internal/cli/settle_test.go
  • internal/store/store.go
  • internal/store/store_test.go
  • internal/worktree/worktree.go
  • internal/worktree/worktree_test.go

Comment thread internal/cli/runs.go Outdated
Comment thread internal/cli/runs.go
Comment thread internal/store/store.go Outdated
Comment thread internal/worktree/worktree_test.go
Comment thread internal/worktree/worktree_test.go Outdated
Comment thread internal/worktree/worktree.go Outdated
Comment thread internal/worktree/worktree.go Outdated
@marcioaltoe
marcioaltoe merged commit 146faa1 into main Jul 27, 2026
3 checks passed
@marcioaltoe
marcioaltoe deleted the ma/terminal-run-worktree-reconciliation branch July 27, 2026 23:22
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