Skip to content

fix(pr-autotrack): harden daemon PR-branch auto-tracking#377

Merged
ScriptedAlchemy merged 1 commit into
masterfrom
codex/autotrack-hardening
Jul 9, 2026
Merged

fix(pr-autotrack): harden daemon PR-branch auto-tracking#377
ScriptedAlchemy merged 1 commit into
masterfrom
codex/autotrack-hardening

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Fixes 7 adversarially-confirmed findings in daemon PR-branch auto-tracking (from the adversarial review of #364 / #367). Each has a regression test.

Findings & fixes

  1. [major] Failed discovery indistinguishable from zero open PRs → mass-untrack. discover_open_prs now returns Result. A git/gh command failure surfaces as Err, and poll_project skips the entire reconcile cycle (logs action=poll outcome=error) instead of reconciling against an empty discovery. Untrack happens only on positive evidence a PR closed.
  2. [major] Daemon death mid-track + advanced head wedged the PR forever and leaked its worktree. prepare_pr_worktree drops the erroring show-ref guard and adopts/resets the collision-proof synthetic branch with git worktree add -B. A stale-worktree sweep on each (complete) reconcile removes leaked pr-worktrees/pr-<N> checkouts.
  3. [major] remove_tracked_branch_store mutated branch-meta.json without the branch-add lock. It now serializes on the same lock prepare_branch_tracking_in_layout and gc_dead_branch_stores hold; skips (returns false) on sustained contention so the daemon retries.
  4. [minor] Per-cycle cap break skipped head-SHA updates for managed PRs. Changed to continue — the cap bounds only new tracks; refreshes always run.
  5. [minor] No GIT_TERMINAL_PROMPT=0 on daemon git/gh subprocesses. Every spawned git/gh command now sets GIT_TERMINAL_PROMPT=0 and GIT_ASKPASS=echo, so a credential prompt can't hang the single sequential poll loop.
  6. [minor] gh --limit truncation treated PR refactor: split tracedecay.rs into focused submodules #201+ as closed. Limit raised and truncation detected (count == limit → partial); a partial discovery suppresses removals so unlisted-but-open PRs aren't untracked.
  7. [minor] Disabling the setting stranded all managed state. The daemon now runs a one-shot removals-only reconcile (teardown_disabled_project) when the feature is off but managed entries remain; the branch autotrack disable output documents it.

Tests

Extended tests/daemon_suite/pr_autotrack_test.rs and src/daemon/pr_autotrack/tests.rs:

  • failed_discovery_returns_error_and_never_mass_untracks
  • interrupted_track_with_advanced_head_recovers_instead_of_wedging
  • cap_does_not_starve_head_updates_for_managed_prs
  • teardown_removes_all_managed_state_on_disable
  • remove_tracked_branch_store_respects_branch_add_lock
  • partial_discovery_suppresses_removals, gh_pr_list_flags_partial_when_result_reaches_limit

Gates

cargo fmt --check, cargo clippy --workspace --all-targets --locked -- -D warnings, and nextest daemon_suite + branch/config lib tests all green (full lib suite running).

🤖 Generated with Claude Code

Address 7 adversarially-confirmed findings in daemon PR-branch
auto-tracking (adversarial review on #364/#367):

1. [major] Failed discovery is now distinguishable from zero open PRs.
   discover_open_prs returns Result; a git/gh command failure surfaces as
   Err and poll_project skips the whole reconcile cycle (logging
   outcome=error) instead of reconciling against an empty discovery and
   mass-untracking every managed PR.

2. [major] Interrupted track + advanced head no longer wedges forever.
   prepare_pr_worktree drops the erroring show-ref guard and resets the
   collision-proof synthetic branch with `worktree add -B`, adopting an
   orphan left by a mid-track crash. Orphan worktrees are swept on each
   (complete) reconcile so stale checkouts don't leak.

3. [major] remove_tracked_branch_store now serializes on the branch-add
   lock (matching prepare/gc) so it can't race a concurrent `branch add`
   and clobber or resurrect a branch-meta entry.

4. [minor] Per-cycle new-track cap uses `continue` not `break`, so an
   already-managed PR whose head advanced is still refreshed even when
   new tracks are capped.

5. [minor] All spawned git/gh subprocesses set GIT_TERMINAL_PROMPT=0 and
   GIT_ASKPASS=echo so a credential prompt can't hang the single poll loop.

6. [minor] gh discovery raises --limit and flags the result `partial` when
   it reaches the limit; partial discoveries suppress removals so PRs
   beyond the page aren't untracked as closed.

7. [minor] Disabling the setting now tears down managed PR state: the
   daemon runs a removals-only reconcile once (teardown_disabled_project)
   and the disable CLI output documents it.

Tests: extend daemon_suite/pr_autotrack_test.rs and pr_autotrack/tests.rs
covering failed discovery, the wedged-track recovery, the cap head-update
starvation, disable teardown, the branch-add lock, and partial-discovery
removal suppression.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 52b306e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 52b306ee72

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/branch.rs
Comment on lines +604 to +605
let Some(_lock) = acquire_branch_add_lock_blocking(tracedecay_dir) else {
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve PR state when branch-store removal is contended

When the branch-add lock is held longer than these retries (for example, a user is running branch add while the daemon is closing/refreshing a PR or doing disabled teardown), this returns false without removing branch-meta.json or the DB. The PR autotrack callers ignore that false and reconcile_project still removes the managed state/worktree/ref, so no later cycle retries and the stale branch store remains visible indefinitely; keep the managed state until removal succeeds or propagate a distinct contention result.

Useful? React with 👍 / 👎.

@ScriptedAlchemy ScriptedAlchemy merged commit 085d439 into master Jul 9, 2026
9 of 14 checks passed
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