diff --git a/skill/scripts/context-signals.mjs b/skill/scripts/context-signals.mjs index dc3951d3b..5c7d3f3bc 100644 --- a/skill/scripts/context-signals.mjs +++ b/skill/scripts/context-signals.mjs @@ -169,7 +169,10 @@ function gitSignals(cwd) { // main; an existing develop therefore outranks the remote default. This // is #302's own repro shape, and repos without develop are unaffected. addCandidate('develop', revsFor('develop')); - for (const head of remoteHeads) addCandidate(head.name, revsFor(head.name)); + // A remote's advertised default prefers its own remote-tracking rev over + // a possibly stale local checkout of the same name, for the same reason + // the upstream candidate leads with its rev. + for (const head of remoteHeads) addCandidate(head.name, [...new Set([head.rev, ...revsFor(head.name)])]); for (const name of ['main', 'master']) addCandidate(name, revsFor(name)); for (const c of candidates) { const rev = c.revs.find((r) => run(['rev-parse', '--verify', '--quiet', r]) !== null); diff --git a/tests/context-signals.test.mjs b/tests/context-signals.test.mjs index a5f3b8d06..3e024b247 100644 --- a/tests/context-signals.test.mjs +++ b/tests/context-signals.test.mjs @@ -551,6 +551,32 @@ describe('gatherSignals', () => { assert.deepEqual(s.git.changedFiles, ['src/Hero.tsx']); }); + it('a remote-advertised default outranks a stale local checkout (#302)', async () => { + const { execFileSync } = await import('node:child_process'); + const git = (...args) => execFileSync('git', args, { cwd: scratch, stdio: 'ignore' }); + git('init', '-q', '-b', 'main'); + git('config', 'user.email', 't@example.com'); + git('config', 'user.name', 'Test'); + write('src/base.css', 'a{}\n'); + git('add', '.'); + git('commit', '-qm', 'A'); + write('src/extra.css', 'b{}\n'); + git('add', '.'); + git('commit', '-qm', 'A2'); + git('update-ref', 'refs/remotes/origin/main', 'HEAD'); + git('symbolic-ref', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/main'); + git('checkout', '-q', '-b', 'feature/s'); + write('src/Hero.tsx', 'export const Hero = () => null;\n'); + git('add', '.'); + git('commit', '-qm', 'feature work'); + // The local main checkout is stale (still at A); the remote default is + // at A2. Diffing against the stale local would drag src/extra.css in. + git('branch', '-f', 'main', 'HEAD~2'); + const s = await gatherSignals(scratch); + assert.equal(s.git.base, 'main'); + assert.deepEqual(s.git.changedFiles, ['src/Hero.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' });