Guard non-standard default branches like conventional ones

Cursor Bugbot: sitting on a non-standard default such as trunk (the
origin/HEAD target) still ran candidate selection, where develop or main
could win and produce an integration-vs-integration diff. The guard now
treats the remote default branch as an integration branch alongside the
conventional names. Failing-first test: on trunk with a develop branch
present, the scope stays the working tree.

Prepared with AI assistance (Claude Code), directed by @pbakaus.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-07-25 20:17:55 -07:00
co-authored by Claude Code
parent e82653965c
commit afb5d9a479
2 changed files with 25 additions and 3 deletions
+5 -3
View File
@@ -112,13 +112,15 @@ function gitSignals(cwd) {
// signal may override that: an origin/HEAD or upstream naming a DIFFERENT
// integration branch (sitting on develop while the remote default is
// main) would produce exactly the integration-vs-integration divergence
// this detection exists to prevent.
const onIntegrationBranch = conventional.includes(branch);
// this detection exists to prevent. "Integration branch" means a
// conventional name OR the remote's default branch, so a non-standard
// default like trunk is guarded the same way.
const remoteHead = splitRemoteRef(run(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD']));
const onIntegrationBranch = conventional.includes(branch) || branch === remoteHead?.name;
let base = null;
let baseRev = null;
if (!onIntegrationBranch) {
const upstream = asUpstream(run(['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']));
const remoteHead = splitRemoteRef(run(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD']));
const candidates = [];
const seen = new Set();
const addCandidate = (name, revs) => {
+20
View File
@@ -376,6 +376,26 @@ describe('gatherSignals', () => {
assert.deepEqual(s.git.changedFiles, ['src/Hero.tsx']);
});
it('sitting on a non-standard default branch keeps the working-tree scope (#302)', async () => {
const { execFileSync } = await import('node:child_process');
const git = (...args) => execFileSync('git', args, { cwd: scratch, stdio: 'ignore' });
git('init', '-q', '-b', 'trunk');
git('config', 'user.email', 't@example.com');
git('config', 'user.name', 'Test');
write('src/App.tsx', 'export default 1;\n');
git('add', '.');
git('commit', '-qm', 'init');
git('branch', '-q', 'develop'); // a conventional name also exists
git('update-ref', 'refs/remotes/origin/trunk', 'HEAD');
git('symbolic-ref', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/trunk');
write('src/App.tsx', 'export default 2;\n'); // dirty on trunk
const s = await gatherSignals(scratch);
// trunk IS the integration branch (origin/HEAD says so); develop must
// not win the candidate scan and produce a trunk-vs-develop diff.
assert.equal(s.git.base, null);
assert.deepEqual(s.git.changedFiles, ['src/App.tsx']);
});
it('never diffs one integration branch against another (#302)', async () => {
const { execFileSync } = await import('node:child_process');
const git = (...args) => execFileSync('git', args, { cwd: scratch, stdio: 'ignore' });