Problem
In generate_git_patch.cjs, the full-mode patch generation (used by create_pull_request) uses a stale origin/branchName as the base ref when it exists locally, instead of computing the merge-base with the default branch. This causes the patch to include commits the agent never made.
Root Cause
File: actions/setup/js/generate_git_patch.cjs, lines 178-224 (Strategy 1, Full Mode)
// FULL MODE (for create_pull_request):
debugLog(`Strategy 1 (full): Checking if origin/${branchName} exists`);
try {
execGitSync(["show-ref", "--verify", "--quiet", `refs/remotes/origin/${branchName}`], { cwd });
baseRef = `origin/${branchName}`; // ← BUG: uses stale remote-tracking ref
debugLog(`Strategy 1 (full): Using existing origin/${branchName} as baseRef`);
} catch {
// Falls through to merge-base with default branch — this path is CORRECT
baseRef = execGitSync(["merge-base", "--", `origin/${defaultBranch}`, branchName], { cwd }).trim();
}
When origin/branchName exists locally, line 182 short-circuits to using it as the base ref. The patch is then:
git format-patch origin/branchName..branchName --stdout
The problem: origin/branchName is a stale local ref — it was fetched at the start of the workflow run (during the checkout step with fetch: ["*"]). It points to wherever the remote branch was at that moment, not the state of the branch before the agent made changes.
In the autoloop scenario:
- Workflow checks out the repo with
fetch: ["*"], fetching all branches including origin/autoloop/tsb-perf-evolve (which is 50 commits behind main, 0 ahead — fully merged)
- Git credentials are cleaned (
clean_git_credentials.sh)
- Agent runs, fast-forwards the local branch to
origin/main, commits 1 file (src/core/series.ts), pushes
- Agent calls
create_pull_request MCP tool
- MCP tool checks: does
refs/remotes/origin/autoloop/tsb-perf-evolve exist? Yes — it was fetched in step 1
baseRef = origin/autoloop/tsb-perf-evolve — pointing to the old commit (50 behind main)
- Patch =
git format-patch old-commit..current-branch --stdout — includes all ~50 commits between the old branch position and current branch tip
Result: the patch contains .github/workflows/*, README.md, assets/tsessebe-logo.png, and other files from those 50 commits that the agent never touched. Only src/core/series.ts was the agent's actual change.
The Fix
The base ref should be the state of the branch before the agent made changes — i.e., the merge-base with the default branch. In full mode, always use the merge-base path (line 217) instead of short-circuiting to origin/branchName (line 182).
The fallback path already does the right thing:
baseRef = execGitSync(["merge-base", "--", `origin/${defaultBranch}`, branchName], { cwd }).trim();
This correctly computes the common ancestor between origin/main and the local branch, capturing exactly what the agent changed and nothing else.
The origin/branchName shortcut only makes sense for incremental mode (which already has its own code path at lines 141-172). In full mode it's always wrong when the remote branch is stale.
Impact
This bug blocks all three autoloop programs on githubnext/tsessebe:
Example failing run: https://github.com/githubnext/tsessebe/actions/runs/25053867836
Related
Problem
In
generate_git_patch.cjs, the full-mode patch generation (used bycreate_pull_request) uses a staleorigin/branchNameas the base ref when it exists locally, instead of computing the merge-base with the default branch. This causes the patch to include commits the agent never made.Root Cause
File:
actions/setup/js/generate_git_patch.cjs, lines 178-224 (Strategy 1, Full Mode)When
origin/branchNameexists locally, line 182 short-circuits to using it as the base ref. The patch is then:The problem:
origin/branchNameis a stale local ref — it was fetched at the start of the workflow run (during the checkout step withfetch: ["*"]). It points to wherever the remote branch was at that moment, not the state of the branch before the agent made changes.In the autoloop scenario:
fetch: ["*"], fetching all branches includingorigin/autoloop/tsb-perf-evolve(which is 50 commits behind main, 0 ahead — fully merged)clean_git_credentials.sh)origin/main, commits 1 file (src/core/series.ts), pushescreate_pull_requestMCP toolrefs/remotes/origin/autoloop/tsb-perf-evolveexist? Yes — it was fetched in step 1baseRef = origin/autoloop/tsb-perf-evolve— pointing to the old commit (50 behind main)git format-patch old-commit..current-branch --stdout— includes all ~50 commits between the old branch position and current branch tipResult: the patch contains
.github/workflows/*,README.md,assets/tsessebe-logo.png, and other files from those 50 commits that the agent never touched. Onlysrc/core/series.tswas the agent's actual change.The Fix
The base ref should be the state of the branch before the agent made changes — i.e., the merge-base with the default branch. In full mode, always use the merge-base path (line 217) instead of short-circuiting to
origin/branchName(line 182).The fallback path already does the right thing:
This correctly computes the common ancestor between
origin/mainand the local branch, capturing exactly what the agent changed and nothing else.The
origin/branchNameshortcut only makes sense for incremental mode (which already has its own code path at lines 141-172). In full mode it's always wrong when the remote branch is stale.Impact
This bug blocks all three autoloop programs on githubnext/tsessebe:
tsb-perf-evolve: patch includes 7 phantom files (workflow files, README, logo) → triggersprotect_top_level_dot_folders→ "Failed to apply patch"perf-comparison: patch includes ~1768 phantom files → previously hit E003 100-file limit (fixed by Count unique files increate_pull_requestpatch limit and addmax-patch-filesconfig #28472), now hits protected-filesbuild-tsb-pandas-typescript-migration: same patternExample failing run: https://github.com/githubnext/tsessebe/actions/runs/25053867836
Related
create_pull_requestpatch limit and addmax-patch-filesconfig #28472 — addedmax-patch-filesconfig (helped, but doesn't fix root cause)