From 193e8e9651ed8100e95c56d32c33208568959af0 Mon Sep 17 00:00:00 2001 From: garrytan Date: Fri, 30 Oct 2026 02:15:32 +0000 Subject: [PATCH] fix(ship-preflight): scope from the fetched origin/; a scope error prints unknown, never false A Capy checkout has no local main, so gstack-diff-scope main returned SCOPE_ERROR=no_base and the preflight printed frontend=false. Failing-first test in test/ship-preflight.test.ts. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- lib/ship-preflight.ts | 6 ++++-- test/ship-preflight.test.ts | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/ship-preflight.ts b/lib/ship-preflight.ts index 69ceff3e3..777eb545f 100644 --- a/lib/ship-preflight.ts +++ b/lib/ship-preflight.ts @@ -48,8 +48,10 @@ function integrate(cwd: string, base: string, r: Report): boolean { } else r.add('PR: none open'); const apple = git(cwd, ['ls-files', '--', '*.xcodeproj/*', '*.xcworkspace/*', 'Package.swift']).stdout.split('\n').filter(Boolean).map(p => p.replace(/(\.xcodeproj|\.xcworkspace)\/.*$/, '$1')); if (apple.length) r.add(`APPLE: ${short([...new Set(apple)], 3)}`).next('Step 0.9: for an App Store/TestFlight ask, confirm the app target and follow apple-release.md; otherwise ignore'); - const scope = bin(cwd, 'gstack-diff-scope', [base], 60_000).stdout; - r.add(`SCOPE: frontend=${/SCOPE_FRONTEND=true/.test(scope)}${/SCOPE_FRONTEND=true/.test(scope) ? ' (Step 9 runs the design lite check; mention /design-review when no design review exists)' : ''}`); + const scope = bin(cwd, 'gstack-diff-scope', [`origin/${base}`], 60_000).stdout; + const scopeError = /^SCOPE_ERROR=(?!unmatched)(\S+)/m.exec(scope)?.[1]; + if (scopeError) r.add(`SCOPE: unknown (gstack-diff-scope origin/${base}: SCOPE_ERROR=${scopeError})`); + else r.add(`SCOPE: frontend=${/SCOPE_FRONTEND=true/.test(scope)}${/SCOPE_FRONTEND=true/.test(scope) ? ' (Step 9 runs the design lite check; mention /design-review when no design review exists)' : ''}`); return merge(cwd, base, r); } diff --git a/test/ship-preflight.test.ts b/test/ship-preflight.test.ts index 3d761700f..9e2e227d5 100644 --- a/test/ship-preflight.test.ts +++ b/test/ship-preflight.test.ts @@ -85,6 +85,15 @@ describe('gstack-ship-preflight', () => { expect(lineOf(r.stdout, 'POLICY')).toContain('queue_mode=stamp-at-merge'); }); + // Value: protects=Step 1's scope check reads the fetched origin/, so a clone without a local base branch still sees frontend files; fails_when=the preflight reports frontend=false because gstack-diff-scope could not resolve a local ; + // why_new=GSTA-167 dogfood on #3633: a Capy checkout has no local main and SCOPE printed false; seam=gstack-diff-scope; no_claim=design review itself + test('no local base branch: SCOPE still reads the fetched base', () => { + const f = fx({}, { 'web/site.css': 'a { color: red; }\n' }); + f.git('branch', '-D', 'main'); + const r = f.run(PREFLIGHT, ['--base', 'main']); + expect(lineOf(r.stdout, 'SCOPE')).toMatch(/^SCOPE: frontend=true /); + }); + // Value: protects=a failing lane and a zero-run lane both stop for Step 5's triage; fails_when=exit 0 with zero tests counts as a pass, or a failure continues to Step 6; // why_new=moved from Step 5's lane block; seam=gstack-evidence tests_ran; no_claim=the triage outcome test('a failing lane and a ZERO-RUN lane stop for Step 5 triage', () => {