Fix #2367: pass pipeline_branch into _sync_worktree_with_remote - #2369
Conversation
The orchestrator-side worktree runs on egg/<pid>/work but the agent-facing remote branch is egg/<pid>. Reading the local branch via `git branch --show-current` and looking up `origin/<that>` always missed, exiting at case=no_remote_tracking before the rebase fallback (#2352) or the step-4 reset could run — stranding plan-phase commits on origin while the loud-fail populator marked the pipeline FAILED. Add a keyword-only pipeline_branch arg and use it for every remote-side ref (rev-parse, rev-list, gateway push target, divergence-rebase target, reset target). The local branch name is still used for detached-HEAD detection and is logged alongside remote_branch in worktree_sync_outcome so operators can grep both names. Both pipelines.py call sites now pass pipeline.branch. Also fixes the latent companion bug at the local-ahead push: the gateway builds HEAD:refs/heads/{branch} from its `branch` argument, so passing the /work-suffixed local name would have pushed to origin/egg/<pid>/work. Masked today by the step-3 early-out, but the regression guard test pins the correct target.
There was a problem hiding this comment.
Verdict
The fix is correct and minimal. Root cause is identified, both production call sites are updated, all five remote-side git operations now use remote_branch, and the latent push-target bug is fixed in the same patch. Approving with one non-blocking note on a test that doesn't actually guard what its name claims.
What I verified
- Root cause: orchestrator worktrees check out
egg/<container_id>/work(gateway/worktree_manager.py:297), butpipeline.branchisegg/issue-Nfor issue-driven pipelines. The pre-PRgit branch --show-currentlookup always missedorigin/<that-name>and bailed atcase=no_remote_tracking, leaving full BRC plan output stranded. - All five remote-side ops now use
remote_branch:rev-parse --verify(5349),rev-list --left-right --count(5386),push_worktree_branch(5446),_rebase_with_agent_output_autoresolve(branch=...)(5537),reset --hard(5569). - Local-vs-remote separation preserved correctly:
branchis still used for detached-HEAD detection and is logged alongsideremote_branchon every outcome — operators reading logs can distinguish the two. - Push semantics:
gateway_client.py:803buildsHEAD:refs/heads/{branch}from this argument, so passingbranch=remote_branchmakes the worktree HEAD land onorigin/<pipeline_branch>(the agent-facing ref). Confirmed. - Rebase semantics:
_rebase_with_agent_output_autoresolveuses itsbrancharg asorigin/{branch}(gateway_client.py:2056-2067), sobranch=remote_branchis correct. - Backward compat:
remote_branch = pipeline_branch or branch→ omission preserves pre-PR behavior. No regression for the script-style use case. - Both production call sites updated:
_run_pipelinephase startup (14275) and post-phase sync (15503). Grep confirmed there are no other production callers. - Pipeline.branch=None path: For prompt-driven pipelines pre-spawn (14553-14565),
pipeline.branchis set before_sync_worktree_with_remoteruns at the next phase boundary, and for fully unset cases the fallback tobranchkeeps things working.
Non-blocking
test_no_remote_tracking_does_not_fire_when_pipeline_branch_resolves doesn't actually guard the regression
orchestrator/tests/test_sync_worktree.py:902-932 — the mocked subprocess.run returns success regardless of which origin/<X> is queried. Under the buggy code (which would query origin/egg/issue-42/work), the mock would still return 0, rev_list would still produce 0\t3\n, and the function would still reach reset_succeeded — so the assertions "no_remote_tracking" not in cases and "reset_succeeded" in cases pass under both fixed and buggy code.
The bug condition IS verified by sibling tests (test_pipeline_branch_used_for_remote_lookup_not_local and test_pipeline_branch_used_for_reset_target both assert specific argv strings), so the fix isn't actually under-covered. But this particular test, which is named and docstring'd as the regression guard, provides zero regression value.
To make it a real guard, the rev-parse mock should return returncode=0 only when the argv contains origin/egg/issue-42 and returncode=128 when it contains origin/egg/issue-42/work — e.g. via a side_effect callable that inspects the argv. Then under buggy code the function would emit no_remote_tracking and the test would fail.
Not blocking — the fix is exercised by the siblings — but worth tightening so the test actually does what it advertises.
Minor: diff hunk header is misleading
The @@ header for the second call-site change reads def _health_monitor_poll(...) because that's the lexically closest preceding def, but the call at line 15503 is in _run_pipeline's body, not in _health_monitor_poll's body. Cosmetic; nothing to do.
— Authored by egg
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The reviewer flagged that test_no_remote_tracking_does_not_fire_when_ pipeline_branch_resolves accepted any rev-parse target as success — so under the buggy code (which would query origin/<local_branch>) the mock would still return 0 and the test would still pass. The named regression guard provided zero regression value. Replace the linear side_effect with an argv-aware callable: rev-parse returns 0 only when origin/egg/issue-42 is in the argv, and 128 otherwise. Under the pre-fix code path (querying origin/egg/issue-42/work) the mock now returns 128 → no_remote_tracking fires → the test fails. The fix path (querying origin/egg/issue-42) still passes.
Response to reviewThanks for the careful read — the verification trace is appreciated, and you correctly identified that Per-item disposition
— Authored by egg |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Verdict
The single new commit (12ed649) correctly addresses the only non-blocking concern from my prior review. The regression-guard test now actually guards the regression. Approving.
What I verified on the delta
- Mock now differentiates by argv, not by call ordinal.
orchestrator/tests/test_sync_worktree.py:917-929replaces the linearside_effectlist with a routing callable keyed on argv membership. - Fix path resolves correctly. Production calls
rev-parse --verify origin/egg/issue-42(routes/pipelines.py:5349); the mock's"origin/egg/issue-42" in argv_listmatches the element-wise comparison and returns rc=0.rev-listreturns0\t3\n→ fall-through to step 4 →reset --hard origin/egg/issue-42→ rc=0 →reset_succeededemitted. Both assertions hold. - Buggy path fails as designed. Without
pipeline_branch=,remote_branchfalls back tobranch(pipelines.py:5344), so step 3 callsrev-parse --verify origin/egg/issue-42/work. The argv element"origin/egg/issue-42/work"is a distinct string from"origin/egg/issue-42", soin argv_listreturns False → rc=128 →no_remote_trackingemitted →assert "no_remote_tracking" not in casesfails. The test now has real teeth. - Catch-all branch is safe. All four git invocations on the success path (
branch,rev-parse,rev-list,reset) are explicitly matched; the trailingreturn _make_subprocess_result(returncode=0)is unreachable on this path and harmless if a future refactor adds a fifth call (it would just behave as a no-op success). Sibling tests with stricter argv assertions still cover the bug condition independently. - No production code changed.
routes/pipelines.pyand the call sites are unchanged from5af3784— the prior approval still stands for those.
Non-blocking
None. The diff hunk header note from the prior review was correctly disposed of as a git diff artifact, not something the patch can control.
— Authored by egg
|
egg review completed. View run logs 3 previous review(s) hidden. |
* Fix #2367: pass pipeline_branch into _sync_worktree_with_remote The orchestrator-side worktree runs on egg/<pid>/work but the agent-facing remote branch is egg/<pid>. Reading the local branch via `git branch --show-current` and looking up `origin/<that>` always missed, exiting at case=no_remote_tracking before the rebase fallback (#2352) or the step-4 reset could run — stranding plan-phase commits on origin while the loud-fail populator marked the pipeline FAILED. Add a keyword-only pipeline_branch arg and use it for every remote-side ref (rev-parse, rev-list, gateway push target, divergence-rebase target, reset target). The local branch name is still used for detached-HEAD detection and is logged alongside remote_branch in worktree_sync_outcome so operators can grep both names. Both pipelines.py call sites now pass pipeline.branch. Also fixes the latent companion bug at the local-ahead push: the gateway builds HEAD:refs/heads/{branch} from its `branch` argument, so passing the /work-suffixed local name would have pushed to origin/egg/<pid>/work. Masked today by the step-3 early-out, but the regression guard test pins the correct target. * Tighten #2367 regression guard with argv-aware rev-parse mock The reviewer flagged that test_no_remote_tracking_does_not_fire_when_ pipeline_branch_resolves accepted any rev-parse target as success — so under the buggy code (which would query origin/<local_branch>) the mock would still return 0 and the test would still pass. The named regression guard provided zero regression value. Replace the linear side_effect with an argv-aware callable: rev-parse returns 0 only when origin/egg/issue-42 is in the argv, and 128 otherwise. Under the pre-fix code path (querying origin/egg/issue-42/work) the mock now returns 128 → no_remote_tracking fires → the test fails. The fix path (querying origin/egg/issue-42) still passes. --------- Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com>
…2395) * Fix #2393: push slice integration branch by SHA, not parent ref name The orchestrator's per-pipeline worktree is checked out on `<branch>/work` and has no local ref matching `<parent_branch>` — only `refs/remotes/origin/<parent_branch>` after a fetch. The old refspec `<parent_branch>:refs/heads/<integration_branch>` resolved the source side against the worktree's local refs and failed every slice push with `src refspec X does not match any` (the fourth latent regression in the slice-DAG creation chain after #2369, #2370, #2372). Fix: fetch the parent into the local odb, resolve to a SHA on origin via `git ls-remote`, then push `<sha>:refs/heads/<integration_branch>`. Pushing an explicit SHA bypasses local ref-name resolution entirely and surfaces "parent missing on origin" as a clear failure instead of git's confusing src-refspec error. * Rename test_fetch_failure_is_non_fatal to be precise about what it pins The reviewer on #2395 noted the original name over-promised: it implies fetch failure is recoverable in production, but the test only verifies that create_slice_integration_branch doesn't short-circuit when fetch_branch returns False — the SHA must still happen to be in the local odb for the subsequent push to succeed. Rename to test_fetch_returning_false_does_not_short_circuit and clarify the docstring with the production-failure note. --------- Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com>
Summary
Fixes #2367 — plan-phase pipelines were stranded with full BRC consensus on origin and no recovery path because
_sync_worktree_with_remoteexited atcase=no_remote_trackingbefore reaching the divergence/reset paths.pipeline_branchto_sync_worktree_with_remote. The orchestrator-side worktree runs onegg/<pid>/workbut the agent-facing remote isegg/<pid>— readinggit branch --show-currentand looking uporigin/<that>always missed. Both call sites (_run_pipelinephase startup + post-phase sync) now threadpipeline.branchthrough, so the lookup hits and the function reconciles correctly.remote_branchthrough every remote-side reference: rev-parse, rev-list divergence check, gateway push target, divergence-rebase target, and step-4 reset. Localbranchis still kept for detached-HEAD detection and is logged alongsideremote_branchin everyworktree_sync_outcomeline.HEAD:refs/heads/{branch}from itsbranchargument, so passing the/work-suffixed local name would have pushed toorigin/egg/<pid>/work. Masked today by the step-3 early-out, but the regression-guard test pins the correct target.Test plan
make lint— clean (only pre-existing soft-cap warnings on unrelated files)pytest orchestrator/tests/test_sync_worktree.py— 44 passed (37 pre-existing + 7 new)pytest orchestrator/tests/test_advance_phase_thread.py orchestrator/tests/test_advance_phase_populate_on_plan_exit.py orchestrator/tests/test_pipeline_failure_path.py— 43 passedmake test— 2,189 passed in 4:51New tests in
TestSyncWorktreePipelineBranchcover:origin/<pipeline_branch>, notorigin/<local_branch>case=no_remote_trackingdoes NOT fire whenorigin/<pipeline_branch>resolvespipeline_branchfalls back to the local branch (preserves the script-style use case)Refs: #2337 (silent-demotion bug #2352 closed), #2352 (added the loud-fail this issue surfaced).