diff --git a/skill/scripts/context-signals.mjs b/skill/scripts/context-signals.mjs index 5c7d3f3bc..743bb220a 100644 --- a/skill/scripts/context-signals.mjs +++ b/skill/scripts/context-signals.mjs @@ -168,10 +168,14 @@ function gitSignals(cwd) { // even when the platform default (origin/HEAD) was never flipped off // 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')); // 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. + // the upstream candidate leads with its rev. That applies to the develop + // candidate too when the remote default IS develop: it sits before the + // remote-default entries in the order, so it must lead with their rev + // itself or a stale local develop would win. + const advertisedRevs = (name) => remoteHeads.filter((head) => head.name === name).map((head) => head.rev); + addCandidate('develop', [...new Set([...advertisedRevs('develop'), ...revsFor('develop')])]); 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) { diff --git a/tests/context-signals.test.mjs b/tests/context-signals.test.mjs index 3e024b247..505258c76 100644 --- a/tests/context-signals.test.mjs +++ b/tests/context-signals.test.mjs @@ -577,6 +577,30 @@ describe('gatherSignals', () => { assert.deepEqual(s.git.changedFiles, ['src/Hero.tsx']); }); + it('a develop-pointing remote default outranks a stale local develop (#302)', async () => { + const { execFileSync } = await import('node:child_process'); + const git = (...args) => execFileSync('git', args, { cwd: scratch, stdio: 'ignore' }); + git('init', '-q', '-b', 'develop'); + 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/develop', 'HEAD'); + git('symbolic-ref', 'refs/remotes/origin/HEAD', 'refs/remotes/origin/develop'); + git('checkout', '-q', '-b', 'feature/t'); + write('src/Hero.tsx', 'export const Hero = () => null;\n'); + git('add', '.'); + git('commit', '-qm', 'feature work'); + git('branch', '-f', 'develop', 'HEAD~2'); // local develop is stale at A + const s = await gatherSignals(scratch); + assert.equal(s.git.base, 'develop'); + 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' });