Skip to content

fix: linearize against bundle prerequisite SHA, not moving origin/<base> tip - #49002

Merged
pelikhan merged 6 commits into
mainfrom
copilot/fix-linearizer-rewrite
Jul 30, 2026
Merged

fix: linearize against bundle prerequisite SHA, not moving origin/<base> tip#49002
pelikhan merged 6 commits into
mainfrom
copilot/fix-linearizer-rewrite

Conversation

Copilot AI commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

When base branch advances after agent checkout, rewriteBundleBranchAsSingleCommit's git reset --soft origin/<base> computes diff(new_tip_tree, agent_head_tree) — absorbing base drift into the synthesized commit and deleting files added to base after checkout.

Changes

  • git_helpers.cjs: Export getBundlePrerequisites (was internal-only).

  • create_pull_request.cjsrewriteBundleBranchAsSingleCommit: Accept optional bundleFilePath; use getBundlePrerequisites to resolve the prerequisite SHA recorded in the bundle (the agent's actual base at checkout time) as the linearizeRangeAsCommit base. Falls back to origin/<baseBranch> when the bundle has 0 or multiple prerequisites. Update call site to pass bundleFilePath.

    // Before: always resets to possibly-moved remote tip
    const baseRef = `origin/${baseBranch}`;
    
    // After: prefer the exact commit the agent started from
    const prereqs = await getBundlePrerequisites(execApi, bundleFilePath);
    const baseRef = prereqs.length === 1 ? prereqs[0] : `origin/${baseBranch}`;

    pushSignedCommits's existing rebase path (lines 463–536) then handles rebasing the prerequisite-anchored commit onto the current origin/<base> tip without absorbing drift.

  • create_pull_request.cjs: Export rewriteBundleBranchAsSingleCommit for direct testing.

  • git_helpers.test.cjs: 7 unit tests for getBundlePrerequisites.

  • create_pull_request_bundle_integration.test.cjs: Integration test — simulates base advancing after agent checkout, agent creating a merge commit, and the rewrite path — asserts synthesized commit contains only agent's file and does not delete the base-drift file.


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 7.12 AIC · ⌖ 6.35 AIC · ⊞ 8.7K ·
Comment /souschef to run again

… to avoid base-branch drift

When the base branch advances after the agent's checkout, soft-resetting
to origin/<base> tip computes diff(new_tip_tree, agent_head_tree) which
reverses unrelated base commits in the synthesized commit.

Fix: export getBundlePrerequisites from git_helpers.cjs, accept
bundleFilePath in rewriteBundleBranchAsSingleCommit, and use the bundle's
prerequisite SHA as the linearization base when exactly one prerequisite
is recorded. Falls back to origin/<baseBranch> for bundles with zero or
multiple prerequisites.

Also export rewriteBundleBranchAsSingleCommit for direct testing, and
add 7 unit tests for getBundlePrerequisites plus an integration test that
simulates a moving-base-branch scenario with a merge commit forcing the
rewrite path.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix linearizeRangeAsCommit to use agent's base commit fix: linearize against bundle prerequisite SHA, not moving origin/<base> tip Jul 29, 2026
Copilot AI requested a review from pelikhan July 29, 2026 22:52
@github-actions

Copy link
Copy Markdown
Contributor

Triage: fix: linearize against bundle prerequisite SHA

  • Category: bug | Risk: high | Score: 72/100 (Impact 40, Urgency 20, Quality 12)
  • Recommendation: fast_track — fixes data-loss bug where base-branch drift could delete files from generated PRs. Touches core PR-creation logic (create_pull_request.cjs, git_helpers.cjs) — needs careful human review despite high urgency. Draft/CI pending.

Generated by 🔧 PR Triage Agent · aut00 · 52.7 AIC · ⌖ 9.26 AIC · ⊞ 7.9K ·

@pelikhan
pelikhan marked this pull request as ready for review July 30, 2026 06:12
Copilot AI review requested due to automatic review settings July 30, 2026 06:12
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (0 additions in default business logic dirs).

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR Code Quality Reviewer failed during code quality review.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates bundle linearization to use the agent’s recorded prerequisite commit rather than a moving base-branch tip.

Changes:

  • Exports and tests bundle prerequisite extraction.
  • Uses the prerequisite SHA during signed-push rewrite recovery.
  • Adds moving-base integration coverage.
Show a summary per file
File Description
actions/setup/js/git_helpers.cjs Exports prerequisite extraction.
actions/setup/js/git_helpers.test.cjs Tests prerequisite parsing.
actions/setup/js/create_pull_request.cjs Linearizes against the bundle prerequisite.
actions/setup/js/create_pull_request_bundle_integration.test.cjs Adds moving-base regression coverage.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment on lines +540 to +542
// The diff introduced by HEAD must NOT show base-drift.txt as deleted.
const diffStat = execGit(["diff", "--name-status", "HEAD^", "HEAD"], { cwd: safeOutputsRepo }).stdout;
expect(diffStat).not.toMatch(/^D\s+base-drift\.txt/m);
Comment on lines +498 to +500
// 4. Agent (unaware of drift) merges main to reconcile — creates merge commit.
execGit(["checkout", branchName], { cwd: agentRepo });
execGit(["merge", "--no-ff", "main", "-m", "reconcile: merge updated main"], { cwd: agentRepo });

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — the fix is well-scoped and the test coverage is strong. Leaving as COMMENT (not APPROVE) due to two targeted observations worth addressing.

📋 Key Themes & Highlights

Issues Raised

  1. Silent degradation path (create_pull_request.cjs ~line 340): When bundleFilePath is not passed, the fallback to origin/<baseBranch> is completely silent — the original bug reappears without any log signal. A core.warning call when bundleFilePath is absent would make this visible in Action logs.

  2. Fragile regex in getBundlePrerequisites (git_helpers.cjs line 322): The pattern includes records which is not a standard git output token. A silent parse failure here returns [] and triggers the fallback (i.e., the original bug). The integration test catches the happy path, but a unit test asserting on the exact git-emitted strings would pin this contract.

Positive Highlights

  • ✅ Root cause is correctly identified and addressed at the right layer (linearization base, not the rebase path)
  • ✅ Fallback strategy is sound — multiple prerequisites is a legitimate edge case and deserves the conservative origin/<base> fallback
  • rewriteBundleBranchAsSingleCommit is now exported, enabling direct unit testing — good testability improvement
  • ✅ Integration test scenario is thorough: seeds bare remote, simulates collaborator drift, creates bundle, applies, rewrites, and asserts at both file-system and git-stat level
  • ✅ 7 unit tests for getBundlePrerequisites cover the key edge cases including errors, empty bundles, multiple prereqs, and deduplication

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 32.5 AIC · ⌖ 5.06 AIC · ⊞ 7K
Comment /matt to run again

Comments that could not be inline-anchored

actions/setup/js/create_pull_request.cjs:340

[/diagnosing-bugs] The fallback to origin/&lt;baseBranch&gt; degrades silently when bundleFilePath is absent — the original bug reappears with no log signal.

<details>
<summary>💡 Suggestion</summary>

Add a warning when bundleFilePath is not supplied so future call sites that forget the argument are immediately visible in Action logs:

if (!bundleFilePath) {
  core.warning(`rewriteBundleBranchAsSingleCommit: no bundleFilePath provided; falling back to ${fallbackBaseRef} (base-drift …

</details>

<details><summary>actions/setup/js/git_helpers.cjs:322</summary>

**[/diagnosing-bugs]** The regex `/the bundle (requires|records) (this|these)/i` matches on `records` which is not standard git output. If git ever changes wording, silently returning `[]` would reactivate the original bug with no error.

&lt;details&gt;
&lt;summary&gt;💡 Suggestion&lt;/summary&gt;

A test against the actual `git bundle verify` output format would guard this — the integration test partially covers it, but a unit test asserting on the exact literal strings git emits (e.g. `&#39;The bundle requires th…

</details>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The fix is correct and well-tested.

Root cause addressed: rewriteBundleBranchAsSingleCommit previously always soft-reset to origin/<base>, which absorbs any base-branch commits added after the agent checkout. The fix uses the bundle prerequisite SHA (the exact commit the agent started from) as the linearization base.

Code quality:

  • Proper fallback to origin/<baseBranch> for bundles with 0 or multiple prerequisites — covers degenerate cases safely.
  • Error path uses core.warning (non-fatal) with clear fallback, which is appropriate.
  • getBundlePrerequisites was already implemented; the integration test clearly demonstrates the bug and verifies the fix.

Test coverage: 7 unit tests for getBundlePrerequisites and one integration test that reproduces the exact race condition (base advancing after agent checkout).

No blocking issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 19.8 AIC · ⌖ 4.8 AIC · ⊞ 5.3K

@github-actions

Copy link
Copy Markdown
Contributor

Warning

threat detection engine error
The threat detection engine encountered an error and could not complete analysis. This is a tooling failure, not a security finding.

Details

The threat detection engine failed to produce results.

Review the workflow run logs for details.

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 69/100 — Acceptable

Analyzed 8 test(s): 6 design, 2 implementation, 0 violation(s).

📊 Metrics (8 tests)
Metric Value
Analyzed 8 (Go: 0, JS: 8)
✅ Design 6 (75%)
⚠️ Implementation 2 (25%)
Edge/error coverage 5 (62.5%)
Duplicate clusters 0
Inflation Yes (5.6:1)
🚨 Violations 0
📋 Test Breakdown
Test File Classification Assertions
rewriteBundleBranchAsSingleCommit uses bundle prerequisite SHA... create_pull_request_bundle_integration.test.cjs design_test 7 (integration: real git ops)
should be exported from git_helpers.cjs git_helpers.test.cjs implementation_test 1
should return single prerequisite SHA from bundle verify output git_helpers.test.cjs design_test 2
should return multiple prerequisite SHAs when bundle has several git_helpers.test.cjs design_test 3 (edge case)
should return empty array when bundle has no prerequisites git_helpers.test.cjs design_test 1 (edge case)
should return empty array and not throw when git bundle verify fails git_helpers.test.cjs design_test 1 (error)
should pick up prerequisites from stderr block git_helpers.test.cjs design_test 1 (parsing variant)
should deduplicate SHAs that appear in both sections git_helpers.test.cjs design_test 1 (correctness)
should pass additional options to getExecOutput git_helpers.test.cjs implementation_test 1
⚠️ Flagged Issues

Test inflation: 217 test lines added vs. 39 production lines (5.6:1 ratio, threshold 2:1)

  • Integration test rewriteBundleBranchAsSingleCommit (~110 lines) necessitates realistic git repository setup
  • High ratio is justified by critical behavioral coverage (base-branch drift fix)

Implementation-focused tests (2 tests, 25%):

  • Export check test ("should be exported") could be merged with adjacent test
  • Options-threading test verifies internal API; minimal user-facing value

Positive signals:

  • 75% design/contract tests (target: >70%) ✅
  • Comprehensive edge-case coverage: multiple prereqs, self-contained bundles, parse failures, deduplication
  • 0 mocking violations (vitest mocks for external I/O are acceptable)
  • High-quality integration test covers the exact behavioral fix (drift preservation)

Verdict

passed. 25% implementation tests (threshold: 30%). Despite high test inflation, the test suite covers critical design contracts and edge cases with strong fidelity.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 19.5 AIC · ⊞ 8.4K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 69/100. 25% implementation tests (threshold: 30%).

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review feedback, and rerun checks once the branch is current.

Failed checks to investigate:

Recent reviewer feedback to address:

  • PR Code Quality Reviewer flagged the new shallow-range guard as unreachable from its production caller and asked for the guard to run before artifact generation.
  • Matt Pocock Skills Reviewer called out duplicated implausible-range logic and missing direct coverage for hasMergeCommitsInRange.

Run: https://github.com/github/gh-aw/actions/runs/30520508562

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 7.12 AIC · ⌖ 6.35 AIC · ⊞ 8.7K ·
Comment /souschef to run again

@github-actions

Copy link
Copy Markdown
Contributor

Triage Summary

  • Category: bug
  • Risk: medium
  • Priority Score: 61/100 (impact 30 + urgency 18 + quality 13)
  • Recommended Action: batch_review

Fixes file-deletion bug when base drifts after agent checkout. Batch B-git-checkout (with #49003, #49059) — overlapping edits to git_helpers.cjs; review together to avoid conflicts.

Generated by 🔧 PR Triage Agent · auto · 54.9 AIC · ⌖ 5.64 AIC · ⊞ 7.9K ·

- Fix integration test to model the production race faithfully: bundle is
  now created BEFORE main advances (not after agent merges drift), so the
  bundle's prereq SHA accurately reflects the agent's actual base commit.

- Strengthen drift-absorption assertion: changed from checking base-drift.txt
  is not deleted to checking it does not appear in the diff at all. The old
  assertion was trivially satisfied when base drift was absorbed as an addition
  rather than a deletion.

- Add SHA reachability guard in rewriteBundleBranchAsSingleCommit: before using
  the bundle prereq SHA as the linearization base, verify the commit object is
  accessible locally (git cat-file -e). Falls back to origin/<baseBranch> when
  the prereq is not available (e.g., in a shallow clone).

- Add direct unit tests for hasMergeCommitsInRange in git_helpers.test.cjs:
  covers null/empty inputs, git failure (unreachable refs), a range with no
  merge commits, and a range containing a merge commit (uses a real temp repo).

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address unresolved review feedback, and rerun checks once the branch is current.

Failed checks to investigate:...

Addressed in commit fix: strengthen bundle linearizer tests and add SHA reachability guard:

  1. Integration test refactored — the test now creates the bundle before main advances, accurately modelling the production race. The agent repo bundles at the original base commit (A), then main advances to B (base-drift.txt), then safe-outputs clones the updated origin and applies the bundle. This removes the misleading agent-merges-drift step that was present before.

  2. Assertion strengthened — changed from not.toMatch(/^D\s+base-drift\.txt/) (only blocks deletions) to not.toMatch(/base-drift\.txt/) (blocks any appearance in the diff). This would catch the bug where the old naive soft-reset absorbed drift as an addition rather than a deletion. The baseDriftInWorkingTree assertion also now correctly expects false (the feature branch doesn't include drift; only origin/main does).

  3. SHA reachability guard added — before using the bundle prereq SHA as the git reset --soft base, rewriteBundleBranchAsSingleCommit now runs git cat-file -e <sha>^{commit} to verify the commit is accessible locally. If the check fails (e.g., shallow clone), it falls back to origin/<baseBranch> cleanly before any commit synthesis begins.

  4. hasMergeCommitsInRange unit tests added — covers null/empty guard conditions, git failure (bad cwd → false), a range with no merge commits, and a range containing a merge commit (using a real temp git repo).

Copilot AI requested a review from gh-aw-bot July 30, 2026 07:19
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot add git integration tests

Copilot AI and others added 2 commits July 30, 2026 10:02
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…izeRangeAsCommit

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

@copilot add git integration tests

Added 6 git integration tests in git_helpers.test.cjs (commit test: add git integration tests for getBundlePrerequisites and linearizeRangeAsCommit):

getBundlePrerequisites (integration with real git):

  • Extracts the prerequisite SHA from a real incremental bundle (baseSha..HEAD)
  • Extracts prerequisites from a bundle created with a named range (multiple commits, single prereq)
  • Returns empty array for a self-contained bundle (--all)

linearizeRangeAsCommit (integration with real git):

  • Collapses multiple commits into a single commit on top of baseSha (verifies single parent, correct SHA returned, both files present)
  • Preserves the working tree contents of the agent's last commit after linearization (verifies final file content, not intermediate)
  • Restores original HEAD when the baseRef does not exist (verifies rollback works end-to-end)

All tests use real tmpRepo directories with spawnSync git, matching the pattern already used by hasMergeCommitsInRange. 78/78 pass.

@pelikhan
pelikhan merged commit f8adb1d into main Jul 30, 2026
8 checks passed
@pelikhan
pelikhan deleted the copilot/fix-linearizer-rewrite branch July 30, 2026 10:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[plan] Linearize rewrite against agent's own base commit, not moving branch tip

4 participants