mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-02 17:40:02 +02:00
* feat: add surface-aware exploratory QA and ship documentation gates * test: preserve delegated QA setup authority after main integration * fix(qa): clarify exploration order and preserve report artifacts * test(qa): follow the shared setup reference directly * refactor(ship): make verification and recovery routes explicit * test(ship): align evidence and review guards with explicit routes * fix(workflows): clarify ship recovery and functional QA evidence * fix(workflows): clarify approval recovery and full QA coverage * refactor(workflows): order review transactions and clarify ship state * fix(ship): clarify final verification and fail closed at publication * fix(evals): attribute native atomic documentation writes * fix(ship): clarify recovery and documentation lifecycle guidance * fix(test): preserve observed native placeholder styling in CI * fix(codex): report watchdog timeouts without a process-exit race * Checkpoint functional QA implementation and workflow validation repairs * Fix documentation and shared-review fixture contracts * docs: clarify judge reuse and evaluation supervision * test: align review evidence and selected case contracts * test: verify append-only documentation checkpoints and recovery * fix: qualify QA workflows and CI validation repairs * fix: launch shared-libs fixture scripts on Windows * fix: qualify QA deadlines, fixture isolation, and shard cleanup * fix: preserve qualified QA and cancellation repairs * fix: enforce functional fixture authority and share strict event decoding * fix: retain free-test evidence and explain recovery * fix: reject malformed native evidence after decoder consolidation * test: use reliable capture for telemetry privacy filters * test: refresh measured quick coverage and document validation costs * Fix native fixture receipts and preserve VM validation evidence * Align negative judge controls with upstream clarity policy * Fix report-only QA preparation and public evidence handling * Clarify QA-only preparation and current-report preservation * Stream Ship quality judgments with an explicit 64k response contract * Validate compact judge reasoning locally with supported wire schema * Align functional QA fixture instructions with evidence acceptance * Bind native browser diagnostics to execution evidence and align review verdicts * Preserve native diagnostic line boundaries * Serialize functional QA evidence from native captures * Keep large QA evidence fixture payload out of Windows argv
91 lines
5.9 KiB
TypeScript
91 lines
5.9 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import * as fs from 'node:fs';
|
|
import * as os from 'node:os';
|
|
import * as path from 'node:path';
|
|
import { spawnSync } from 'node:child_process';
|
|
|
|
describe.skipIf(process.platform !== 'linux')('bootstrap paid-shard cleanup integration without models', () => {
|
|
test.each(['success', 'retry', 'callback-kill', 'ack-failure'])('%s preserves the real attempt through runner cleanup', async scenario => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'bs-shard-'));
|
|
try {
|
|
const script = path.join(root, 'bootstrap.test.ts');
|
|
fs.writeFileSync(script, `
|
|
import { test } from 'bun:test';
|
|
import * as fs from 'node:fs';
|
|
import * as os from 'node:os';
|
|
import * as path from 'node:path';
|
|
import { spawn } from 'node:child_process';
|
|
import { registerBootstrapRetention } from ${JSON.stringify(path.join(import.meta.dir, 'helpers/bootstrap-retention.ts'))};
|
|
import { gitArgvIn } from ${JSON.stringify(path.join(import.meta.dir, 'helpers/scratch-repo.ts'))};
|
|
let attempt = 0;
|
|
test('qa-bootstrap', async () => {
|
|
attempt++;
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-e2e-bs-'));
|
|
fs.writeFileSync(path.join(root,'package.json'),'{"name":"synthetic-bootstrap","version":"1.0.0"}');
|
|
for (const args of [['init','-q'],['add','.'],['commit','-qm','initial']]) {
|
|
const result = gitArgvIn(root,args,5000);
|
|
if (result.status !== 0) throw new Error('fixture Git seed failed');
|
|
}
|
|
const retention = registerBootstrapRetention(root,process.env.EVALS_RUN_ID!,{deadline:Date.now()+5000});
|
|
fs.appendFileSync(${JSON.stringify(path.join(root, 'attempts.jsonl'))},JSON.stringify({attempt,root,artifact:retention.artifact})+'\\n');
|
|
fs.writeFileSync(path.join(root,'bun.lock'),'exact synthetic installed lock\\n');
|
|
fs.mkdirSync(path.join(root,'node_modules','synthetic'),{recursive:true});
|
|
fs.writeFileSync(path.join(root,'node_modules','synthetic','package.json'),'{"name":"synthetic","version":"1.2.3"}');
|
|
const child = spawn(process.execPath,['-e','setInterval(()=>{},1000)'],{cwd:root,detached:true,stdio:'ignore'});
|
|
retention.lifecycle.onSpawn(child.pid!);
|
|
if (${JSON.stringify(scenario)} === 'callback-kill') process.kill(process.pid,'SIGKILL');
|
|
const exited = new Promise<void>(resolve=>child.once('exit',()=>resolve()));
|
|
process.kill(-child.pid!,'SIGKILL'); await exited;
|
|
await retention.lifecycle.onSettled({deadline:Date.now()+1000,exited:true});
|
|
if (${JSON.stringify(scenario)} === 'ack-failure') fs.mkdirSync(path.join(retention.artifact,'ack.json.tmp'));
|
|
try {
|
|
if (${JSON.stringify(scenario)} === 'retry' && attempt === 1) throw new Error('original synthetic assertion failure');
|
|
} finally { retention.cleanup(); }
|
|
},10000);
|
|
`);
|
|
const controllerScript = path.join(root, 'controller.ts');
|
|
const outcomePath = path.join(root, 'outcome.json');
|
|
fs.writeFileSync(controllerScript, `
|
|
import * as fs from 'node:fs';
|
|
import { runPaidShard } from ${JSON.stringify(path.join(import.meta.dir, '../scripts/test-paid-shards.ts'))};
|
|
const outcome = await runPaidShard(['test/skill-e2e-qa-workflow.test.ts'], 1, 1, {
|
|
rootDir: ${JSON.stringify(path.join(import.meta.dir, '..'))}, timeoutMs: 12000, jobs: 2, logDir: ${JSON.stringify(root)},
|
|
evalDirBase: ${JSON.stringify(path.join(root, 'artifacts'))}, log: () => {},
|
|
env: { PATH: process.env.PATH, GSTACK_CLAUDE_CLI_VERSION: 'synthetic-no-provider', EVALS_RUN_ID: 'integration-run' },
|
|
commandFor: () => ({ command: process.execPath, args: ['test', ${JSON.stringify(script)}, '--retry', ${JSON.stringify(scenario === 'retry' ? '1' : '0')}, '--timeout', '10000'] }),
|
|
});
|
|
fs.writeFileSync(${JSON.stringify(outcomePath)}, JSON.stringify(outcome), { mode: 0o600 });
|
|
`);
|
|
const controller = spawnSync(process.execPath, [controllerScript], {
|
|
cwd: root, encoding: 'utf8', timeout: 20000,
|
|
});
|
|
fs.writeFileSync(path.join(root, 'controller.stdout.log'), controller.stdout ?? '', { mode: 0o600 });
|
|
fs.writeFileSync(path.join(root, 'controller.stderr.log'), controller.stderr ?? '', { mode: 0o600 });
|
|
expect(controller.error).toBeUndefined();
|
|
expect(controller.status).toBe(0);
|
|
const outcome = JSON.parse(fs.readFileSync(outcomePath, 'utf8'));
|
|
if (scenario === 'ack-failure') {
|
|
expect(controller.stdout).toContain('(fail) qa-bootstrap');
|
|
expect(controller.stdout).toContain('durable acknowledgment failed');
|
|
}
|
|
const attempts = fs.readFileSync(path.join(root, 'attempts.jsonl'), 'utf8').trim().split('\n').map(line => JSON.parse(line));
|
|
expect(attempts.length).toBe(scenario === 'retry' ? 2 : 1);
|
|
expect(new Set(attempts.map(attempt => attempt.artifact)).size).toBe(attempts.length);
|
|
expect(outcome.status).toBe(scenario === 'success' || scenario === 'retry' ? 'passed' : 'failed');
|
|
for (const attempt of attempts) {
|
|
const state = path.dirname(path.dirname(attempt.root));
|
|
if (scenario === 'ack-failure') {
|
|
expect(fs.existsSync(state)).toBe(true);
|
|
expect(fs.existsSync(attempt.root)).toBe(true);
|
|
expect(fs.existsSync(path.join(attempt.artifact, 'evidence.json'))).toBe(true);
|
|
fs.rmSync(state, { recursive: true });
|
|
} else {
|
|
expect(fs.existsSync(state)).toBe(false);
|
|
expect(fs.readFileSync(path.join(attempt.artifact, 'files/bun.lock'), 'utf8')).toBe('exact synthetic installed lock\n');
|
|
expect(JSON.parse(fs.readFileSync(path.join(attempt.artifact, 'ack.json'), 'utf8')).complete).toBe(true);
|
|
}
|
|
}
|
|
} finally { fs.rmSync(root, { recursive: true, force: true }); }
|
|
}, 30000);
|
|
});
|