From f9e8f7b1e2a6b37b7941ccf559bc1169fa6b38d3 Mon Sep 17 00:00:00 2001 From: garrytan Date: Mon, 26 Oct 2026 21:06:25 +0000 Subject: [PATCH] test: repair the CI reds the retarget onto main surfaced (test-only) Seven test files, no product change. The pull_request run at 2d73fadd failed 5 Linux shards and 4 Windows shards: - test/ship-frozen-scope-actor.test.ts imported only test code, so the test-of-test ratchet refused it; the first case now recomputes the loop summary with summarizeReviewLoop/reviewLoopLine from lib/review-scope and asserts the exact REVIEW_LOOP line. Its three cases replay the bash workflow through bash -c and the shebang bins, so they are POSIX-only. - test/codex-host.test.ts: the new gate case is Claude-specific (33 -> 34). - test/paid-retry-supervision.test.ts: the gate census fixture now carries a trial record for the one planned behavior-kind gate case, the way test/ci-paid-coordination.test.ts already does. - test/review-fix-range.test.ts asserted the pre-#3487 DIFF_BASE sentence; it now asserts the ${RANGE_BASE:-merge-base} clause that carries the same contract (every reviewer reads the range from pass 2 on). - test/gstack-coauthor-trailers.test.ts: the fixture sets its own git identity; a CI runner has none to auto-detect ("Committer identity unknown"). - test/review-scope-followups.test.ts and test/gstack-ship-receipt-docs.test.ts: the gh and old-git stand-ins are sh scripts on a colon-joined PATH, which CreateProcess cannot run, so those three cases skip on win32. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- test/codex-host.test.ts | 4 ++-- test/gstack-coauthor-trailers.test.ts | 5 ++++- test/gstack-ship-receipt-docs.test.ts | 3 ++- test/paid-retry-supervision.test.ts | 3 ++- test/review-fix-range.test.ts | 2 +- test/review-scope-followups.test.ts | 5 +++-- test/ship-frozen-scope-actor.test.ts | 12 +++++++++--- 7 files changed, 23 insertions(+), 11 deletions(-) diff --git a/test/codex-host.test.ts b/test/codex-host.test.ts index d6dda8b79..e41e55ef3 100644 --- a/test/codex-host.test.ts +++ b/test/codex-host.test.ts @@ -128,13 +128,13 @@ describe('per-case host metadata', () => { for (const id of Object.keys(E2E_HOSTS)) expect(Object.hasOwn(E2E_TOUCHFILES, id), id).toBe(true); }); - test('classifies 52 adapter-only, 33 Claude-specific, 26 rework and 1 host-neutral case', () => { + test('classifies 52 adapter-only, 34 Claude-specific, 26 rework and 1 host-neutral case', () => { const counts: Record = {}; for (const support of Object.values(E2E_HOSTS)) counts[support] = (counts[support] ?? 0) + 1; expect(counts.both).toBe(52); expect(counts.rework).toBe(26); expect(counts['host-neutral']).toBe(1); - expect(['pty', 'sdk', 'subagents', 'skill-tool', 'hermetic', 'self-host'].reduce((sum, reason) => sum + (counts[reason] ?? 0), 0)).toBe(33); + expect(['pty', 'sdk', 'subagents', 'skill-tool', 'hermetic', 'self-host'].reduce((sum, reason) => sum + (counts[reason] ?? 0), 0)).toBe(34); }); }); diff --git a/test/gstack-coauthor-trailers.test.ts b/test/gstack-coauthor-trailers.test.ts index 29c2de9c1..3c08d1cdd 100644 --- a/test/gstack-coauthor-trailers.test.ts +++ b/test/gstack-coauthor-trailers.test.ts @@ -15,7 +15,10 @@ import { EXIT } from '../lib/headless-artifacts'; const BIN = path.resolve(import.meta.dir, '../bin/gstack-coauthor-trailers'); const temps: string[] = []; afterAll(() => { for (const dir of temps) fs.rmSync(dir, { recursive: true, force: true }); }); -const ENV = { ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null', GIT_CONFIG_NOSYSTEM: '1' }; +const ENV = { + ...process.env, GIT_CONFIG_GLOBAL: '/dev/null', GIT_CONFIG_SYSTEM: '/dev/null', GIT_CONFIG_NOSYSTEM: '1', + GIT_COMMITTER_NAME: 'Porter', GIT_COMMITTER_EMAIL: 'porter@example.invalid', GIT_AUTHOR_NAME: 'Porter', GIT_AUTHOR_EMAIL: 'porter@example.invalid', +}; function git(cwd: string, args: string[], input?: string) { const r = spawnSync('git', args, { cwd, env: ENV, encoding: 'utf8', timeout: 30_000, input }); diff --git a/test/gstack-ship-receipt-docs.test.ts b/test/gstack-ship-receipt-docs.test.ts index 387d4ffef..120a6ce58 100644 --- a/test/gstack-ship-receipt-docs.test.ts +++ b/test/gstack-ship-receipt-docs.test.ts @@ -170,7 +170,8 @@ describe('carry follow-ups: git floor, named refusal, branch-touched walk, digit expect((plan as any).message.split(', ').length).toBe(CARRY_NAMED_PATHS); }); - test('carry refuses git below 2.39 by name (RECEIPT_GIT_TOO_OLD) and names a product path the branch touched', () => { + // The old-git stand-in is a sh script on a colon-joined PATH, which CreateProcess cannot run on Windows. + test.skipIf(process.platform === 'win32')('carry refuses git below 2.39 by name (RECEIPT_GIT_TOO_OLD) and names a product path the branch touched', () => { const repo = fixture(); makePr(repo, 'pr-carry', { expects: '1.2.4.0' }); const body = path.join(repo.root, 'body.md'); diff --git a/test/paid-retry-supervision.test.ts b/test/paid-retry-supervision.test.ts index 95c5fd3b3..b8a43dbb2 100644 --- a/test/paid-retry-supervision.test.ts +++ b/test/paid-retry-supervision.test.ts @@ -4,7 +4,7 @@ import { join } from 'node:path'; import { buildPaidShardArgs, buildRunManifest, parseRunManifest, planPaidShards, DEFAULT_JOBS, parseCliOptions, paidShardWallUpperBoundMs, resolvePaidShardBudget, retriesForFiles, verifySliceResults, collectPaidTestFiles, selectPaidTestFiles, - shardFile, sliceExecutionOrder, sliceSupervisedWallMs, CASE_SHARDED_FILES, sliceCiTimeoutMinutes, resolvePaidShardTimeoutMs, + shardFile, shardCaseId, shardTrial, sliceExecutionOrder, sliceSupervisedWallMs, CASE_SHARDED_FILES, sliceCiTimeoutMinutes, resolvePaidShardTimeoutMs, } from '../scripts/test-paid-shards'; import { ALL_TIERS, AUQ_CONSISTENCY_RETRY_BUDGET, FILE_RETRY_BUDGETS, @@ -331,6 +331,7 @@ test('gate census requires all seven distinct slice results and its own reconcil files: [row.file], status: 'passed' as const, exitCode: 0, elapsedMs: 1, skippedTests: 0, executedTests: STRICT_RETRY_CASE_BUDGETS.find(budget => budget.file === row.file)?.cases ?? 1, ...(row.budget ? { budget: row.budget } : {}), + ...(row.trial ? { trial: { case: shardCaseId(row.file)!, trial: shardTrial(row.file)!, ...row.trial, outcome: 'passed' as const, cost_usd: 0, duration_ms: 1 } } : {}), })), })); expect(verifySliceResults(manifest, results)).toEqual({ ok: true, problems: [] }); diff --git a/test/review-fix-range.test.ts b/test/review-fix-range.test.ts index a27851ecb..b3b909fd7 100644 --- a/test/review-fix-range.test.ts +++ b/test/review-fix-range.test.ts @@ -211,7 +211,7 @@ describe('ship Step 9 instructions', () => { const army = fs.readFileSync(path.join(ROOT, 'ship/sections/review-army.md'), 'utf8').replace(/\s+/g, ' '); test('pass 2+ reads the range since the previous pass under a full token; the fix-range token is /review\'s alone', () => { expect(army).toContain('Every entry saves `PASS_HEAD=$(git rev-parse HEAD)`; from pass 2 on, `RANGE_BASE` is the previous pass\'s PASS_HEAD.'); - expect(army).toContain('use `DIFF_BASE=$RANGE_BASE` instead, so every reviewer reads what changed since the previous pass.'); + expect(army).toContain('`DIFF_BASE=${RANGE_BASE:-$(git merge-base origin/ HEAD)}`, so with `RANGE_BASE` exported from pass 2 on every reviewer reads what changed since the previous pass'); expect(army).toContain('Every pass captures a full token (no `--fix-range`), so the record binds the whole tree even when reviewers read only the range.'); expect(army).not.toContain('--start review --fix-range'); expect(army).toContain('gstack-review-scope classify --base --pass $PASS --range-base $RANGE_BASE'); diff --git a/test/review-scope-followups.test.ts b/test/review-scope-followups.test.ts index 0ec61451f..93eedd873 100644 --- a/test/review-scope-followups.test.ts +++ b/test/review-scope-followups.test.ts @@ -54,7 +54,8 @@ describe('deferred-issue search page and the repository argument gh accepts', () return { dir, log }; }; - test('an exactly-full search page is reported truncated and recorded on the issue it files; a short page is not', () => { + // The gh stand-in is a sh script on a colon-joined PATH, which CreateProcess cannot run on Windows. + test.skipIf(process.platform === 'win32')('an exactly-full search page is reported truncated and recorded on the issue it files; a short page is not', () => { const repo = fixture(); const classified = path.join(repo.root, 'classified.json'); fs.writeFileSync(classified, JSON.stringify([finding('lib/legacy.ts:7:data-loss', 'Unbounded delete')])); @@ -75,7 +76,7 @@ describe('deferred-issue search page and the repository argument gh accepts', () expect(JSON.parse(r2.out)[0]).toEqual({ fingerprint: 'lib/legacy.ts:7:data-loss', path: 'lib/legacy.ts:7', severity: 'CRITICAL', summary: 'Unbounded delete', issue: 900, url: 'https://example.test/acme/app/issues/900' }); }); - test('with --repo the visibility read passes the repository positionally, so a MEDIUM finding on a public repository waits for a human and stays unfiled', () => { + test.skipIf(process.platform === 'win32')('with --repo the visibility read passes the repository positionally, so a MEDIUM finding on a public repository waits for a human and stays unfiled', () => { const repo = fixture(); const classified = path.join(repo.root, 'classified.json'); fs.writeFileSync(classified, JSON.stringify([ diff --git a/test/ship-frozen-scope-actor.test.ts b/test/ship-frozen-scope-actor.test.ts index 532498dca..a970913d7 100644 --- a/test/ship-frozen-scope-actor.test.ts +++ b/test/ship-frozen-scope-actor.test.ts @@ -11,6 +11,7 @@ import { spawnSync } from 'node:child_process'; import { createShipFrozenScopeFixture, FROZEN_SCOPE_DEFER_IDENTITY, FROZEN_SCOPE_FIX_IDENTITY, shipFrozenScopeFailures, shipFrozenScopeWorkflow, } from './helpers/ship-frozen-scope-actor'; +import { reviewLoopLine, summarizeReviewLoop } from '../lib/review-scope'; type Fixture = ReturnType; let workflow = ''; @@ -78,7 +79,9 @@ async function act(f: Fixture, options: { fix?: boolean; passes?: 1 | 2 } = {}) sh(f, f.commands.continue!); } -test('a scripted actor that fixes the diff, defers the pre-existing defect and converges on pass 2 passes the verdict', async () => { +// The actor replays the bash workflow through bash -c and the shebang bins, so it is POSIX-only like the paid case it mirrors. +const posixTest = test.skipIf(process.platform === 'win32'); +posixTest('a scripted actor that fixes the diff, defers the pre-existing defect and converges on pass 2 passes the verdict', async () => { const f = fixture(); await act(f); const verdict = await shipFrozenScopeFailures(f, { exitReason: 'success' }); @@ -89,9 +92,12 @@ test('a scripted actor that fixes the diff, defers the pre-existing defect and c expect(rows[0]!.findings.map((x: any) => [x.fingerprint, x.action])).toEqual([[FROZEN_SCOPE_FIX_IDENTITY, 'auto-fixed'], [FROZEN_SCOPE_DEFER_IDENTITY, 'deferred']]); expect(rows[1]!.range_base).toBe(verdict.evidence.head); expect(verdict.evidence.payments).toBe(verdict.evidence.originalPayments); + const summary = summarizeReviewLoop(rows, verdict.evidence.start.started_at); + expect(summary).toEqual(verdict.evidence.loop); + expect(reviewLoopLine(summary)).toBe('REVIEW_LOOP: passes=2 blocking_found=2 deferred=1 noted=0 fixed=1 scope=frozen'); }, 120_000); -test('the fixture interface refuses the undeclared interactions before any decision', async () => { +posixTest('the fixture interface refuses the undeclared interactions before any decision', async () => { const f = fixture(); expect(await hook(f, 'Edit', { file_path: f.payments, old_string: 'card', new_string: '?' })).toBe('Edit outside invoice.ts'); expect(await hook(f, 'Write', { file_path: f.payments, content: '' })).toBe('Write outside invoice.ts and the review record'); @@ -104,7 +110,7 @@ test('the fixture interface refuses the undeclared interactions before any decis expect(f.snapshot().executions.filter(e => !e.allowed)).toHaveLength(6); }, 120_000); -test('leaving the diff defect in place or exiting after one pass are named failures', async () => { +posixTest('leaving the diff defect in place or exiting after one pass are named failures', async () => { const f = fixture(); await act(f, { fix: false, passes: 1 }); const verdict = await shipFrozenScopeFailures(f, { exitReason: 'success' });