Skip to content

test: regression tests for validation/push file-set parity in create_pull_request - #49075

Merged
pelikhan merged 8 commits into
mainfrom
copilot/plan-add-end-to-end-regression-test
Jul 30, 2026
Merged

test: regression tests for validation/push file-set parity in create_pull_request#49075
pelikhan merged 8 commits into
mainfrom
copilot/plan-add-end-to-end-regression-test

Conversation

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Both bugs in #48934 share the same root cause: validation and push operate on different objects, so a passing patch validation doesn't predict what actually lands on the remote.

New test file: create_pull_request_validation_push_parity.test.cjs

Three tests asserting file_set(patch) == file_set(pushed_commit):

Sanity (no exclusions)

Confirms the baseline invariant holds when excludedFiles is empty. Always passes.

Non-rewrite path — excluded-files/bundle bug

generateGitBundle has no excludedFiles support. The bundle includes all file changes; the patch excludes some. Fails on pre-fix code where excluded_file.txt appears in the pushed commit but not in the validated patch.

Merge-commit rewrite path — linearization bug

linearizeRangeAsCommit does git reset --soft origin/base then commits all staged files without filtering. Fails on pre-fix code because excluded files are restaged by the soft-reset and committed wholesale.

// Both regression tests assert this invariant:
const fromPatch = fileListFromPatch(patchContent);         // honours excludedFiles
const fromPush  = fileListFromPushedCommit(repo, "origin/main"); // what actually lands
expect(fromPush).toEqual(fromPatch);  // FAILS pre-fix, PASSES post-fix

Both regression tests are intentionally failing until the companion fixes (adding excludedFiles to generateGitBundle and threading it through linearizeRangeAsCommit) land.


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.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 21.4 AIC · ⊞ 6.3K ·
Comment /souschef to run again

…ate_pull_request

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add end-to-end regression test for validation and push test: regression tests for validation/push file-set parity in create_pull_request Jul 30, 2026
Copilot AI requested a review from pelikhan July 30, 2026 07:12
@github-actions

Copy link
Copy Markdown
Contributor

Triage Summary

  • Category: test
  • Risk: low
  • Priority Score: 7/100 (impact 3 + urgency 2 + quality 2)
  • Recommended Action: close

WIP PR with an empty diff (0 files changed) and an unstarted task checklist — appears stale/superseded. Recommend closing unless actively being worked.

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

@pelikhan
pelikhan marked this pull request as ready for review July 30, 2026 10:05
Copilot AI review requested due to automatic review settings July 30, 2026 10:05
@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.

@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

PR Code Quality Reviewer completed the code quality 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 #49075 does not have the 'implementation' label and has 0 new lines of code in business logic directories (threshold: 100).

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

Adds regression coverage for validation/push file-set parity in create_pull_request.

Changes:

  • Adds baseline parity coverage.
  • Tests excluded files in bundle and rewrite paths.
  • Adds Git repository test fixtures and file-set helpers.
Show a summary per file
File Description
actions/setup/js/create_pull_request_validation_push_parity.test.cjs Adds patch-to-pushed-commit parity regression tests.

Review details

Tip

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

Comments suppressed due to low confidence (1)

actions/setup/js/create_pull_request_validation_push_parity.test.cjs:374

  • This invokes the low-level helper directly, without an exclusion list, rather than the production rewriteBundleBranchAsSingleCommit recovery path. An excludedFiles-aware companion fix therefore receives no exclusions here, and the test also cannot catch the production wrapper forgetting to forward them—the exact pipeline-parity regression the linked acceptance criteria require. Exercise the production recovery entry point with excluded-file configuration (or add explicit wrapper-forwarding coverage) so the test validates the real path.
    // Simulate the merge-commit rewrite path: linearise the bundle commits into a
    // single commit on top of origin/main.  In production this is triggered by
    // pushSignedCommits refusing merge-commit topology, causing create_pull_request
    // to call rewriteBundleBranchAsSingleCommit → linearizeRangeAsCommit.
    await linearizeRangeAsCommit("origin/main", "apply bundled changes", createExecApi(safeOutputsRepo));
  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Medium

expect(fromPatch, "patch should contain only main_file.txt").toEqual(["main_file.txt"]);

// Generate bundle (currently does NOT honour excludedFiles)
const bundleResult = await generateGitBundle(branchName, "main", { cwd: agentRepo });
Comment on lines +332 to +334
// Agent reconciles: merges updated main into feature branch, creating a merge commit
execGit(["checkout", branchName], { cwd: agentRepo });
execGit(["merge", "--no-ff", "main", "-m", "reconcile: merge main"], { cwd: agentRepo });
@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

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

@github-actions github-actions Bot mentioned this pull request Jul 30, 2026

@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 /tdd — requesting changes on two correctness issues in the test helpers before the companion fix lands.

📋 Key Themes & Highlights

Key Themes

  • fileListFromPatch rename handling (line 117): adding both oldPath and newPath for every entry makes the helper asymmetric with git diff --name-only, causing false mismatch failures for any rename-only change.
  • linearizeRangeAsCommit call site (line 380): the rewrite-path test doesn't pass excludedFiles to linearizeRangeAsCommit, so the test doesn't lock down that callers actually use the new parameter once the companion fix lands.
  • Minor: the beforeAll/cleanup relationship for the prompt directory could be annotated for clarity.

Positive Highlights

  • ✅ Excellent test architecture: bare-remote + agent-repo + safe-outputs-repo pattern cleanly isolates each layer
  • ✅ Accurate and detailed PR description — the root-cause analysis maps directly to the three test cases
  • ✅ Merge-commit topology test is thorough: it verifies the drift scenario and checks mergeCount before asserting
  • ✅ Consistent Arrange / Act / Assert structure throughout all three tests

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

}

/**
* Return the sorted list of files changed between a base ref and HEAD via

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.

[/tdd] fileListFromPatch adds both oldPath and newPath for each diff entry, which would cause a false mismatch for renames: the patch side shows two entries (old + new) but git diff --name-only (used by fileListFromPushedCommit) shows only the new path.

💡 Suggested fix

For renames the canonical representative is the new path; the old path is only needed for pure deletes:

for (const entry of entries) {
  const file = entry.newPath || entry.oldPath;
  if (file) files.add(file);
}

This keeps both helpers symmetric and prevents spurious assertion failures on rename-only changes.

@copilot please address this.

// On pre-fix code this fails because linearizeRangeAsCommit stages ALL files that
// differ from origin/main (including excluded_file.txt) and commits them all.
const fromPush = fileListFromPushedCommit(safeOutputsRepo, "origin/main");
expect(fromPush, "rewritten commit should match patch file set (excluded_file.txt must not be committed)").toEqual(fromPatch);

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.

[/tdd] linearizeRangeAsCommit is called here without an excludedFiles argument, but the whole point of this test is to verify that the rewrite path respects excluded files. The test as written exercises the current (buggy) API surface and will fail with a 'too many arguments' or silent-ignore error once the companion fix adds the parameter—meaning the test doesn't actually lock down the correct post-fix calling convention.

💡 Suggested fix

Pass the excludedFiles list to linearizeRangeAsCommit once the companion fix lands and update the call site here to reflect the expected post-fix signature:

await linearizeRangeAsCommit(
  "origin/main",
  "apply bundled changes",
  createExecApi(safeOutputsRepo),
  { excludedFiles: ["excluded_file.txt"] },  // companion fix adds this param
);

Without this the test doesn't verify that callers actually use the new parameter—it only checks the outcome after the fix, which could accidentally pass even if excludedFiles is ignored.

@copilot please address this.

}
for (const p of createdArtifacts.splice(0)) {
try {
fs.rmSync(p, { force: true });

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.

[/tdd] The tempDirs and createdArtifacts arrays are declared inside the describe block but populated in each test and cleaned up in afterEach. However, beforeAll also implicitly depends on RUNNER_TEMP/os.tmpdir() directories that are never tracked — if ensureDisclosureHeaderPrompt creates a path that already exists from a previous test run, errors are swallowed silently.

Consider tracking or verifying the prompt directory in afterEach cleanup, or at minimum confirming in a comment that re-creating it is idempotent (it is, via mkdirSync({ recursive: true }), but the intent isn't obvious).

@copilot please address this.

@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.

REQUEST_CHANGES — Two of three new regression tests will fail CI immediately on merge; they need to be marked as expected-to-fail until the companion fix lands.

💡 Themes and highlights
  • The test file itself is well-documented and the intent (TDD-style regression tests proving a validation/push parity bug before the fix lands) is sound.
  • Blocking: non-rewrite path and merge-commit rewrite path tests are plain it() blocks with no it.fails/.skip/.todo gating, despite the file's own header stating they fail on pre-fix code. Merging as-is breaks CI for every future PR until an unrelated companion fix (in generate_git_bundle.cjs/git_helpers.cjs) lands — use it.fails(...) or stack this PR behind the fix.
  • Secondary: the createExecApi error wrapper strips the failing git command from thrown errors, hurting CI triage.
  • Secondary: temp artifact registration for cleanup happens only after a success assertion, risking disk leaks especially on the two currently-failing tests that will exercise this path on every run.
  • Non-blocking: large near-identical setup boilerplate duplicated across all three tests — worth extracting into a shared helper for maintainability, but not blocking.

🔎 Code quality review by PR Code Quality Reviewer · auto · 48 AIC · ⌖ 4.95 AIC · ⊞ 7.8K
Comment /review to run again

const { applyBundleToBranch } = require("./create_pull_request.cjs");

const branchName = "parity-excl-no-rewrite";

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.

This test is documented as intentionally failing on current code but runs as a normal (non-skipped) assertion — merging this will break CI for every subsequent PR.

💡 Details

The header comment explicitly states: "These tests are written against the CORRECT behaviour. They FAIL on pre-fix code and PASS after companion fixes land." But it("non-rewrite path: excluded files are absent from the pushed commit", ...) (and the merge-commit rewrite test at line ~305) are ordinary it() blocks with no it.fails, .skip, .todo, or conditional gate.

Since the companion fixes to generateGitBundle and linearizeRangeAsCommit are not part of this PR, merging as-is means npm test/CI will fail on main immediately, blocking every other PR's CI status until the unrelated fix lands — the opposite of the safe TDD pattern this PR is going for.

Fix: use vitest's it.fails(...) for these two tests (vitest inverts the pass/fail so a currently-failing assertion is reported as a pass, and once the companion fix lands the test will start failing — signaling you to flip it back to it(...)), or gate the whole PR behind having the companion fix land in the same or a stacked PR.

it.fails("non-rewrite path: excluded files are absent from the pushed commit", async () => {
  // ...
});

const { applyBundleToBranch } = require("./create_pull_request.cjs");
const { linearizeRangeAsCommit } = require("./git_helpers.cjs");

const branchName = "parity-excl-rewrite";

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.

Same issue as the non-rewrite test above: this merge-commit rewrite regression test is also unmarked and will fail CI on merge.

💡 Details

See companion comment on the non-rewrite test (line 231) — same fix applies: use it.fails(...) instead of it(...) until the linearizeRangeAsCommit companion fix lands, otherwise this test will fail on every CI run for this repo starting the moment this PR merges.

if (command !== "git") throw new Error(`unexpected command: ${command}`);
const result = execGit(args, { cwd, allowFailure: true });
if (result.status !== 0) throw new Error(result.stderr || result.stdout);
return result.status;

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 exec/getExecOutput mock re-throws with a stripped error message, discarding which git subcommand actually failed.

💡 Details

createExecApi always calls execGit(args, { cwd, allowFailure: true }) and on non-zero exit throws new Error(result.stderr || result.stdout). This loses the args (the actual git command) from the error, unlike execGit's own default throw path (git ${args.join(" ")} failed:\n${result.stderr}) used elsewhere in this same file.

When applyBundleToBranch or linearizeRangeAsCommit fail inside a test (which is exactly the failure mode these regression tests are probing), the resulting CI error will show only stderr/stdout text with no indication of which git invocation produced it, making failures much harder to triage in CI logs.

Suggested fix:

if (result.status !== 0) throw new Error(`git ${args.join(" ")} failed:\n${result.stderr || result.stdout}`);

}
for (const p of createdArtifacts.splice(0)) {
try {
fs.rmSync(p, { force: true });

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.

Temp-dir cleanup only runs in afterEach and depends on directories being pushed to tempDirs/createdArtifacts before an assertion throws — any repo created but not yet pushed to the array on an early failure leaks on disk.

💡 Details

bareRemote/agentRepo/safeOutputsRepo are pushed to tempDirs right after mkdtempSync, which is fine, but patchResult.patchPath/bundleResult.bundlePath are only pushed to createdArtifacts after the corresponding expect(...).toBe(true) on .success passes. If generateGitPatch/generateGitBundle throw or return a falsy path unexpectedly before that push, the artifact is never tracked for cleanup and will accumulate in the OS temp dir across repeated CI runs — this is compounded by the fact that two of the three tests in this file are expected to produce failing assertions (see other comment), so this leak path will be exercised on every CI run once merged.

Consider wrapping artifact registration before result validation, e.g. push the expected path first (computed deterministically) rather than only after a successful assertion.

@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.

Review: regression tests for validation/push file-set parity

The test file is well-structured and clearly documents the two failure modes from #48999. The approach — asserting file_set(patch) == file_set(pushed_commit) end-to-end — is the right level of abstraction for catching this class of parity bug.

Observations

  1. fileListFromPatch includes oldPath for renames/deletions (line 111). git diff --name-only in fileListFromPushedCommit also outputs both sides of a rename, so the two helpers are consistent. Correct behavior — worth a follow-up comment if rename test cases are added later.

  2. tempDirs and createdArtifacts at describe scope, cleaned in afterEach: intentional so cleanup still runs after assertion failures. Pattern is correct.

  3. Rewrite-path test calls linearizeRangeAsCommit directly rather than through rewriteBundleBranchAsSingleCommit — this is the right boundary: the exported function is exactly what needs the fix, and this mirrors the production call graph.

  4. Tests are annotated as intentionally failing until companion fixes land. Clear, accurate documentation.

No blocking issues. ✅

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 64 AIC · ⌖ 6.1 AIC · ⊞ 5.3K

@pelikhan

Copy link
Copy Markdown
Collaborator

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

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

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 please run the pr-finisher skill, address the unresolved review feedback below, and rerun checks once updates are pushed.

Unresolved feedback to address (newest first):

Failed checks:

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 21.4 AIC · ⊞ 6.3K ·
Comment /souschef to run again

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

[!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....

Addressed in a087002.

Updates:

  • fixed the JS typecheck failure by documenting excludedFiles on linearizeRangeAsCommit
  • tightened the parity test helpers (fileListFromPatch, git error reporting, artifact cleanup tracking)
  • passed exclusions through the exercised bundle/rewrite paths
  • rebuilt the rewrite-path fixture so merge topology is created independently from later base drift
  • marked the remaining rewrite/base-drift regression as it.fails(...) while the companion rewrite fix is still outstanding

Local reruns completed (npm --prefix actions/setup/js run typecheck, targeted vitest for create_pull_request_validation_push_parity.test.cjs, and make agent-report-progress).

I could not re-run GitHub Actions from this agent; CI on a087002 is still stale and needs a maintainer-triggered re-run.

@github-actions

Copy link
Copy Markdown
Contributor

PR Triage

  • Category: test
  • Risk: low
  • Score: 65/100 (impact 25, urgency 20, quality 20)
  • Recommended action: fast_track (corrected from prior close triage)

Rationale: Adds targeted regression tests proving the file-set parity bug (#48934) between patch validation and actual push in create_pull_request. All CI checks are passing (JS tests x4 shards, typecheck, lint-js, Artifact API Integration). Test-only change, low risk, directly closes a known gap. Re-labeled from a stale prior pr-action:close — recommend merging promptly rather than closing.

Generated by 🔧 PR Triage Agent · auto · 69.5 AIC · ⌖ 8.05 AIC · ⊞ 7.9K ·

@pelikhan
pelikhan merged commit 50cabbf into main Jul 30, 2026
8 checks passed
@pelikhan
pelikhan deleted the copilot/plan-add-end-to-end-regression-test branch July 30, 2026 12:56
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.84.1

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] Add end-to-end regression test: validation and push must operate on the same object

4 participants