fix(ship-preflight): scope from the fetched origin/<base>; 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>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-30 02:15:32 +00:00
1 parent 628278ebe4
commit 193e8e9651
2 files changed
+13 -2

No files matched your search

+4 -2
View File
@@ -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);
}
+9
View File
@@ -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/<base>, 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 <base>;
// 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', () => {