Files
gstack/test/shared-libs-checker-interface-evidence-declared.test.ts
T
Garry Tanandcapy-ai[bot] 83ae96e086 v1.92.30.0 test(free): split files over the 60 s seed ceiling and the Windows doctor file, then reseed (GSTA-151 A) (#3585)
* test(free): split files over the 60 s seed ceiling and the Windows doctor file

Eight free test files the fresh recordings put over the seed ratchet's 60 s ceiling, and test/gstack-doctor.test.ts (343 s on Windows), are split by describe block or test with shared helpers moved to test/helpers/. Test bodies are unchanged.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(todos): note the GSTA-151 seed-ceiling splits

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(free): reseed both duration seeds at this head

Recorded by free-durations-record.yml (open_pr=false) run 40818261447 at ec6faf1:
1,416 Linux files on ubicloud-standard-8 and the 795 curated Windows files on
windows-latest. test/setup-codex-scope.test.ts fell to 49 s and leaves the
seed allowlist.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* chore: changelog entry for the GSTA-151 seed-ceiling splits (unreleased)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(free): split test/ubicloud-runner.test.ts over the 60 s seed ceiling; keep setup-codex-scope allowlisted

The reseed at the main merge (free-durations-record run 41226501374, 221320e) put
test/ubicloud-runner.test.ts, which main added after this branch's first reseed,
at 127 s and test/setup-codex-scope.test.ts back at 66 s. The fake-API cases move
into four parts by describe topic with their shared fake VM in
test/helpers/ubi-runner-fake-vm.ts (46 tests before and after). The allowlist
keeps main's setup-codex-scope entry.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(free): reseed both duration seeds at the main merge; changelog fragment

Recorded by free-durations-record.yml (open_pr=false) run 41229924108 at 4f5bd2d:
1,470 Linux files on ubicloud-standard-8 and 822 curated Windows files. The
Windows recorder was red on browse/test/cookie-import-native-job.test.ts (known
Edge leak) and test/hook-scripts.test.ts, plus test/free-seed-ratchet.test.ts on
both OSes against the pre-reseed seed; their durations are kept. The CHANGELOG
entry moves to changelog.d/ now that main runs on fragments.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: correct the seed-ceiling TODO and the ubicloud-runner test paths after the split

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-11-01 14:23:49 +00:00

179 lines
11 KiB
TypeScript

// Split from test/shared-libs-checker-interface-evidence.test.ts (GSTA-151): the cases that replay the declared-protocol captures.
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
import * as fs from 'node:fs';
import * as path from 'node:path';
import { createHash } from 'node:crypto';
import { sharedLibsFingerprint } from '../lib/review-evidence';
import { hasTrustedSharedLibsCheck, hasTrustedReviewStartRead } from './helpers/shared-libs-review-start-evidence';
import {
fixtureGit, fixtureWorkingTree, reviewRecords, SHARED_LIBS_ROOT, toolCommandTrace, type SharedLibsFixture,
} from './helpers/shared-libs-eval-fixture';
import {
preparePathEligibilityFixture, checkPathReviewPrerequisites, hasPathReviewPrerequisiteReceipt,
} from './helpers/shared-libs-path-fixture';
import { fixtures, captures, pathKinds, capture, replay, call, result, pathCallback } from './helpers/shared-libs-checker-evidence';
const source = fs.readFileSync(path.join(import.meta.dir, 'skill-e2e-shared-libs.test.ts'), 'utf8');
beforeAll(async () => {
for (const change of ['unchanged', 'secondary', 'branch', 'filtered']) captures.set(`declared-${change}`, await capture(change, undefined, true));
for (const kind of pathKinds) captures.set(`declared-path-${kind}`, await capture(`path-${kind}`, preparePathEligibilityFixture(kind), true));
}, 120_000);
afterAll(() => { for (const fixture of fixtures) fs.rmSync(fixture.root, { recursive: true, force: true }); });
describe('native executable shared-code checker evidence', () => {
test('the actual path callback consumes the current synthetic prerequisite result without claiming native coverage', async () => {
const run = replay('declared-path-assume-unchanged');
run.kind = 'assume-unchanged';
let verified = 0;
const adapter = pathCallback(run, { hasPathReviewPrerequisiteReceipt: (events: any[], command: string, expected: any) => {
verified++;
expect(command).toBe(run.prepared.resumed.checkCommand);
expect(expected).toMatchObject({ synthetic: true, native_coverage: false, settled: true, current: true });
expect(expected.context.binding.repo).toBe(run.f.repo);
expect(expected.context.binding.state).toBe(run.f.state);
expect(expected.context.binding.index).toMatch(/^h /m);
return hasPathReviewPrerequisiteReceipt(events, command, expected);
} });
await adapter.invoke();
expect(verified).toBe(1);
expect(adapter.rows[0].row.passed).toBe(true);
expect(adapter.rows[0].row.transcript[0]).toMatchObject({
prerequisite_source: 'synthetic-fixture-input', prerequisite_native_coverage: false,
});
const refused = pathCallback(run, { hasPathReviewPrerequisiteReceipt: () => false });
await expect(refused.invoke()).rejects.toThrow('consume current synthetic prerequisites');
expect(refused.rows[0].row.passed).toBe(false);
});
test.each(['missing', 'failed', 'unpaired', 'assistant-only', 'caption', 'false', 'foreign state', 'after finish'])(
'the registered path callback rejects %s prerequisite receipts even with a completed record', async invalid => {
const run = replay('declared-path-ignored');
run.kind = 'ignored';
const at = run.events.findIndex((event: any) => event.type === 'assistant'
&& event.message.content[0].input?.command === run.prepared.resumed.checkCommand);
expect(at).toBeGreaterThan(run.checkAt);
if (invalid === 'missing') run.events.splice(at, 2);
if (invalid === 'failed') result(run, at).is_error = true;
if (invalid === 'unpaired') result(run, at).tool_use_id = 'another-call';
if (invalid === 'assistant-only') run.events[at + 1].type = 'assistant';
if (invalid === 'caption') call(run, at).input = { command: 'true', description: run.prepared.resumed.checkCommand };
if (invalid === 'false' || invalid === 'foreign state') {
const receipt = JSON.parse(result(run, at).content);
if (invalid === 'false') receipt.settled = false;
else receipt.context.binding.state = '/another-fixture/state';
result(run, at).content = JSON.stringify(receipt);
}
if (invalid === 'after finish') run.events.push(...run.events.splice(at, 2));
const adapter = pathCallback(run);
await expect(adapter.invoke()).rejects.toThrow('consume current synthetic prerequisites');
expect(adapter.rows[0].row.passed).toBe(false);
});
test.each(['raw source', 'supplied context'])('the registered callback rejects late changes to %s', async changed => {
const run = replay('declared-path-assume-unchanged');
run.kind = 'assume-unchanged';
const file = changed === 'raw source' ? path.join(run.f.repo, 'src/retry-route.ts') : run.prepared.resumed.input;
const before = fs.readFileSync(file, 'utf8');
const adapter = pathCallback(run, { runSharedInteractive: async () => {
fs.writeFileSync(file, before + '\n');
return { questions: run.questions, result: { exitReason: 'success', output: '', events: run.events,
toolCalls: run.events.filter((event: any) => event.type === 'assistant')
.map((event: any) => ({ tool: event.message.content[0].name, input: event.message.content[0].input })) } };
} });
try {
await expect(adapter.invoke()).rejects.toThrow('prerequisite state must remain unchanged');
expect(adapter.rows[0].row.passed).toBe(false);
} finally { fs.writeFileSync(file, before); }
});
test.each(['unchanged', 'secondary', 'branch', 'filtered'])('declared receipt commands satisfy the real %s revalidation callback', change => {
const run = replay(`declared-${change}`);
expect(result(run, 0).content.trim()).toBe(run.token);
expect(JSON.parse(result(run).content)).toEqual(run.receipt);
expect(call(run, run.finishAt).input.command).toContain(`--finish ${run.token} && '${SHARED_LIBS_ROOT}/bin/gstack-review-read'`);
expect(run.questions).toHaveLength(change === 'unchanged' ? 0 : 1);
const scenario = source.slice(source.indexOf("test('shared-libs-review-revalidation'"));
const marker = '}, result => {';
const start = scenario.indexOf(marker) + marker.length;
const body = scenario.slice(start, scenario.indexOf('\n });', start));
const verify = new Function('deps', 'result', new Bun.Transpiler({ loader: 'ts' }).transformSync(`const {
change, questions, expect, toolCommandTrace, reviewRecords, createHash, path, f, current,
fixtureGit, fixtureWorkingTree, hasTrustedReviewStartRead, hasTrustedSharedLibsCheck, SHARED_LIBS_ROOT,
resumed, prerequisites, checkPathReviewPrerequisites, hasPathReviewPrerequisiteReceipt
} = deps; ${body}`));
const native = { events: run.events, toolCalls: run.events.filter((event: any) => event.type === 'assistant')
.map((event: any) => ({ tool: event.message.content[0].name, input: event.message.content[0].input })) };
let checks = 0;
verify({ change, questions: run.questions, expect, toolCommandTrace, reviewRecords, createHash, path,
f: run.f, current: run.current, fixtureGit, fixtureWorkingTree, hasTrustedReviewStartRead, SHARED_LIBS_ROOT,
resumed: run.resumed, prerequisites: run.prerequisites, checkPathReviewPrerequisites, hasPathReviewPrerequisiteReceipt,
hasTrustedSharedLibsCheck: (events: unknown[], expected: any) => {
checks++;
expect(events).toBe(run.events);
expect(expected).toEqual(run.expected);
return hasTrustedSharedLibsCheck(events, expected);
} }, native);
expect(checks).toBe(1);
});
test.each(pathKinds)('declared receipt commands satisfy the real %s path callback after an actual actor decision', async kind => {
const run = replay(`declared-path-${kind}`);
run.kind = kind;
expect(result(run, 0).content.trim()).toBe(run.token);
expect(JSON.parse(result(run).content)).toEqual(run.receipt);
expect(run.receipt.reusable).toBe(false);
expect(run.questions).toHaveLength(1);
let checks = 0;
const adapter = pathCallback(run, { hasTrustedSharedLibsCheck: (events: unknown[], expected: any) => {
checks++;
expect(expected).toEqual(run.expected);
return hasTrustedSharedLibsCheck(events, expected);
} });
await adapter.invoke();
expect(checks).toBe(1);
expect(adapter.rows).toMatchObject([{ scenario: kind, row: { passed: true } }]);
});
test.each(['revised-only identity', 'unsupported finding', 'unfinished review', 'missing explicit decision'])(
'canonical transport cannot waive %s in the real ignored-path callback', async failure => {
const run = replay('declared-path-ignored');
run.kind = 'ignored';
if (failure === 'missing explicit decision') run.questions = [];
const adapter = pathCallback(run, { reviewRecords: (fixture: SharedLibsFixture) => {
const records = reviewRecords(fixture);
const final = records.at(-1);
if (failure === 'revised-only identity') {
const revised = { ...final.findings[0], evidence_paths: [
'src/retry-worker.ts', 'src/scheduler.ts', 'src/retry-route.ts', 'lib/retry-after.ts',
] };
revised.fingerprint = sharedLibsFingerprint(revised);
revised.snapshot_covered_paths = [...revised.evidence_paths];
expect(revised.fingerprint).not.toBe(run.current.fingerprint);
final.findings = [revised];
}
if (failure === 'unsupported finding') final.findings = [];
if (failure === 'unfinished review') final.completed = false;
return records;
} });
await expect(adapter.invoke()).rejects.toThrow();
expect(adapter.rows).toMatchObject([{ scenario: 'ignored', row: { passed: false } }]);
});
test.each(['start batching', 'mixed checker stdout', 'finish metadata prelude', 'loop-only caller reads'])(
'the unchanged detector still rejects %s after protocol declaration', shape => {
const run = replay('declared-unchanged');
if (shape === 'start batching') call(run, 0).input.command = `echo start; ${call(run, 0).input.command}; git diff origin/main`;
if (shape === 'mixed checker stdout') result(run).content = `checker receipt:\n${result(run).content}\nexit=0`;
if (shape === 'finish metadata prelude') call(run, run.finishAt).input.command = `TS=$(date -u); ${call(run, run.finishAt).input.command}`;
if (shape === 'loop-only caller reads') {
for (const event of run.events) for (const block of event.message.content) {
if (block.type === 'tool_use' && block.name === 'Bash' && block.input.command.startsWith('cat ')) {
block.input.command = `for f in ${block.input.command.slice(4)}; do cat "$f"; done`;
}
}
}
expect(hasTrustedSharedLibsCheck(run.events, run.expected)).toBe(false);
});
});