feat(ab-3238): integrate origin/<target> drift before opening feature PR - #471
Merged
Merged
Conversation
Adds a new entry-point node to feature-pr.yaml that detects and resolves
drift between the feature branch and its target before the PR is opened.
Drift in this context = commits that landed on origin/<target_branch>
after feature/<id> was forked from it. Without integration, the feature
PR diff shows those drift commits as "removals" from the feature branch,
fooling LLM reviewers into flagging unrelated files (compounding with
AB#3236's revise_counter loop to burn unbounded LLM tokens).
Why this matters now:
Apex 62286666's most recent dogfood run (2026-05-18) hit exactly this:
main advanced 8 commits while the apex was running (USSec/USNat config
landed in #15615238). The feature PR opened with those 8 commits' worth
of files presented as "missing" from the feature branch. pr_initial_reviewer
flagged them as drift. pr_fixer iter 1 dutifully recreated them. Iters
2-7 each concluded "already fixed" without committing — with AB#3236's
revise_counter stuck at 1, the loop never terminated.
AB#3236 caps the runaway (no-commit fast-fail). This fix prevents the
runaway from being triggered in the first place.
What changed:
* New script .conductor/registry/scripts/integrate-target-drift.ps1.
- Fetches origin/<target> and origin/<feature>.
- Refuses to proceed if local <feature> has diverged from
origin/<feature> (both sides have unique commits).
- If behind_by > 0: git merge --no-ff origin/<target> and pushes.
- On conflict: aborts cleanly, captures conflicted_files, surfaces
merge_conflict for the gate to handle.
- Routing-style envelope (always exits 0; error_code drives routes).
* feature-pr.yaml: integrate_target_drift is now the entry_point.
Routes: merge_conflict -> conflict gate; other error -> failed gate;
success -> pr_platform_router (existing flow continues unchanged).
* Two new human gates:
- integrate_target_drift_conflict_gate (retry / abort).
- integrate_target_drift_failed_gate (retry / abort).
Retry semantics: operator resolves by hand (cd into worktree, merge,
push), then clicks Retry; the script re-checks behind_by and falls
through to PR creator if now in sync.
No "skip" option intentionally — skipping reproduces the bug.
* Merge (not rebase) chosen to preserve SHAs of upstream MG -> feature
merge commits, avoid --force-with-lease, and leave an obvious
"drift integration" commit visible in PR review.
Lint additions (lint-feature-pr.ps1):
* missing-drift-integrator - integrate_target_drift node exists
* drift-integrator-not-entry - entry_point is integrate_target_drift
* missing-drift-gate - both gates exist
* drift-routes-malformed - routes branch on error_code correctly
Tests:
* integrate-target-drift.Tests.ps1 — 5 script-level Pester tests against
a synthetic two-clone git fixture: no drift, clean drift, merge
conflict (worktree clean after abort, conflicted_files captured),
feature_branch_diverged, envelope contract.
* lint-feature-pr.Tests.ps1 — 6 new mutation tests + valid-fixture
updated to include integrate_target_drift as entry point.
Scope:
The fix lives in feature-pr.yaml because it's the single chokepoint
through which every feature PR flows (apex-driver -> feature-pr.yaml
for apex->main; apex-item-dispatch -> feature-pr.yaml for
child->feature/<apex>). Both GitHub and ADO platform legs are
protected by the same entry node.
Rubber-duck pass adopted: feature-pr boundary placement, drift script
owns its push (not strip), removed skip option (would reproduce bug),
guarded merge --abort with MERGE_HEAD check, added
feature_branch_diverged distinct error path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PolyphonyRequiem
added a commit
that referenced
this pull request
May 19, 2026
PR #471 shipped AB#3238 with git merge --no-ff origin/<target> as the drift-integration strategy. User feedback: clean linear feature-PR history is preferred over preserving the upstream MG-merge SHA chain; the extra integration merge commit polluted the PR's Commits tab. This change: * Script (integrate-target-drift.ps1): replaces git merge --no-ff with git rebase origin/<target>, replaces git push origin <feature> with git push --force-with-lease=<feature>:<expected-sha> (the expected SHA is captured BEFORE rebase to defeat lease-weakening from background fetches). * Conflict path: replaces git merge --abort (guarded by MERGE_HEAD) with git rebase --abort (guarded by Test-RebaseInProgress, which checks both ebase-merge and ebase-apply dirs via git rev-parse --git-path). * New pre-flight: Test-WorktreeBlocked refuses to proceed if any in-progress merge/rebase/cherry-pick/revert OR uncommitted changes are detected, surfaced as new error_code worktree_dirty. * New envelope fields: strategy: 'rebase', old_head_sha, ew_head_sha. Dropped merge_commit_sha (rebase has no single integration SHA). * Edge case: rebase-no-op (HEAD didn't move despite behind_by>0) exits success without force-push. * Initial-push path: if origin/<feature> doesn't exist yet, falls back to git push --set-upstream (no lease — nothing to overwrite). * error_code value merge_conflict retained for workflow-route compatibility (semantic meaning is still 'drift integration conflicted'); only the operator-instructions in the conflict gate prompt change (rebase + force-with-lease, not merge + plain push). * Workflow YAML: version 2.4.9 -> 2.4.10. Updates agent comment, conflict-gate operator instructions (full rebase/continue/force-push recipe with explicit git rev-parse refs/remotes/origin/<feature> for the lease), failed-gate prompt (documents new worktree_dirty and updated push_failed semantics). * Tests: clean-drift test asserts strategy='rebase', new_head_sha != old_head_sha, and verifies the force-pushed SHA landed on origin/<feature>. Conflict test asserts no rebase-merge or rebase-apply dir remains after abort. New worktree_dirty test. Envelope-contract test updated for new field set. 6/6 pass. Lint (lint-feature-pr.ps1) contract unchanged — routes still check �rror_code == 'merge_conflict', gate names + entry-point invariant preserved. 28/29 lint tests pass (1 pre-existing missing-ado-remediation-route failure unrelated to this change). Co-authored-by: Daniel Green <dangreen@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PolyphonyRequiem
added a commit
that referenced
this pull request
May 19, 2026
4 root causes traced from PR #474/#475 admin-merges: 1. Console.Out parallel-test race in StringBoolArgTests (caused the 2 net11.0 xUnit failures: Parse_RejectsAnythingElse('no') and NextReady_TwigShowThrows). Take ConsoleTestLock.AsyncLock like every other Console-redirecting test class does. 2. Install Pester/Install powershell-yaml lacked if: always() — so when (1) failed dotnet test, modules never installed and if: always() lints false-positive'd 'module required'. Add if: always() to both install steps. 3. lint-version-drift red on main: feature-pr.yaml (2.4.10) and implement-merge-group.yaml (2.4.9) drifted from index.yaml (2.4.8). PRs #471 and #469 bumped one workflow each without cutting a tag or bumping siblings — per docs/decisions/versioning-strategy.md, bumps happen at release-cut time, not PR-merge time. Roll both back to 2.4.8. 4. lint-primary-completer-trust-chain + lint-pwsh-jinja-bareword failures were pure consequences of (2). Resolved transitively. Verified: dotnet test --filter 'Category!=Slow&Category!=Integration' Passed: 3638, Failed: 0, Skipped: 4. lint-version-drift PASS for all 15 workflow YAMLs at 2.4.8. Co-authored-by: Daniel Green <dangreen@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes AB#3238.
Problem
strip_planning_artifacts (apex-driver) pushed the feature branch and triggered feature-PR creation against
mainwithout first integrating drift from origin/main. If main had advanced since the apex was dispatched, the feature PR's diff included that drift, presenting unrelated changes as if the feature was "removing" them. LLM reviewer flagged them;pr_fixer"fixed" by recreating; AB#3236's revise_counter loop kept it spinning.Live repro: apex 62286666 dogfood (2026-05-18) — main advanced 8 commits while the apex ran; USSec/USNat config files showed as missing from the feature branch; fixer iter 1 recreated them; iters 2-7 each concluded "already fixed" without committing; loop never terminated.
Fix
New
integrate_target_driftnode at the entry point offeature-pr.yaml. It runs once per feature PR (covering apex-driver → main AND apex-item-dispatch → feature/; both GitHub and ADO legs). Strategy:git merge --no-ff origin/<target>on the feature branch + push. Merge (not rebase) preserves upstream MG → feature merge-commit SHAs, avoids force-push, and leaves the drift integration visible in PR review.On conflict: script aborts cleanly, captures
conflicted_files, surfacesmerge_conflictto a human gate. Operator resolves by hand, pushes, clicks Retry; script re-checksbehind_byand falls through if now in sync.Pairs with AB#3236
AB#3236 (already merged) caps the runaway. This PR prevents the runaway from being triggered in the first place. The two together address the dogfood failure mode end-to-end.
Tests
lint-strict-undefined(15 workflows) ✓lint-apex-driver✓lint-feature-prclean for AB#3238 checks (1 pre-existing unrelatedmissing-ado-remediation-routefailure unchanged from main; tracked separately).Rubber-duck pass adopted
git merge --abortwithMERGE_HEADcheck.feature_branch_divergederror path.