Files
gstack/test/helpers/bootstrap-retention.ts
T
Garry Tan dcaea52800 v1.91.7.0 feat: add functional QA and pre-publication docs checks (#2983)
* 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
2026-09-29 06:07:35 -07:00

363 lines
22 KiB
TypeScript

import * as fs from 'node:fs';
import * as path from 'node:path';
import { createHash, randomUUID } from 'node:crypto';
import { spawnSync } from 'node:child_process';
type Identity = { path: string; dev: number; ino: number };
type Native = { pid: number; start: string; group: number; settled: boolean; executable?: { path: string; sha256: string } };
type Scope = { runId: string; temporary: Identity; durable: Identity; registry: Identity; uninspectable: Native[] };
type Registration = { attempt: string; runId: string; deadline: number; root: Identity; artifact: Identity; owner: Native; native?: Native; initial: object };
type Entry = { path: string; kind: string; bytes?: number; sha256?: string; target?: string; resolved?: string };
export type BootstrapReceipt = { attempt: string; complete: boolean; acknowledged: boolean; quiescent: boolean; errors: string[]; artifact: string };
const scopeVariable = 'GSTACK_BOOTSTRAP_RETENTION';
const locks = ['bun.lock', 'bun.lockb', 'package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock', 'pnpm-lock.yaml'];
const digest = (bytes: Buffer | string) => createHash('sha256').update(bytes).digest('hex');
const inside = (root: string, target: string) => target.startsWith(root + path.sep);
function identity(file: string): Identity {
const stat = fs.lstatSync(file);
if (!stat.isDirectory() || fs.realpathSync(file) !== path.resolve(file)) throw new Error('noncanonical directory');
return { path: path.resolve(file), dev: stat.dev, ino: stat.ino };
}
function verify(expected: Identity) {
if (JSON.stringify(identity(expected.path)) !== JSON.stringify(expected)) throw new Error('directory identity changed');
}
function verifyPrivateTemporary(temporary: Identity) {
verify(temporary);
const stat = fs.lstatSync(temporary.path);
if (stat.uid !== process.getuid!() || (stat.mode & 0o077) !== 0) throw new Error('temporary state is not privately owned');
}
function durableWrite(file: string, bytes: Buffer | string) {
const fd = fs.openSync(file + '.tmp', fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600);
try { fs.writeFileSync(fd, bytes); fs.fsyncSync(fd); } finally { fs.closeSync(fd); }
fs.renameSync(file + '.tmp', file);
const dir = fs.openSync(path.dirname(file), fs.constants.O_RDONLY);
try { fs.fsyncSync(dir); } finally { fs.closeSync(dir); }
}
function artifactDirectory(root: Identity, directory: string) {
verify(root);
let current = root.path;
for (const part of path.relative(root.path, directory).split(path.sep)) {
if (!part || part === '..') throw new Error('unsafe artifact directory');
current = path.join(current, part);
if (!fs.existsSync(current)) fs.mkdirSync(current, { mode: 0o700 });
identity(current);
}
}
function readRegular(file: string, maximum = 8 * 1024 * 1024): Buffer {
const fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
try {
const before = fs.fstatSync(fd);
if (!before.isFile() || before.size > maximum) throw new Error('file type or byte limit');
const bytes = fs.readFileSync(fd);
const after = fs.fstatSync(fd);
if (before.size !== bytes.length || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) throw new Error('file changed during capture');
return bytes;
} finally { fs.closeSync(fd); }
}
function executableIdentity(file: string) {
const resolved = fs.realpathSync(file);
const fd = fs.openSync(resolved, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
try {
const before = fs.fstatSync(fd);
if (!before.isFile() || before.size > 256 * 1024 * 1024) throw new Error('toolchain identity unavailable');
const hash = createHash('sha256');
const buffer = Buffer.alloc(65536);
let count: number;
while ((count = fs.readSync(fd, buffer)) > 0) hash.update(buffer.subarray(0, count));
const after = fs.fstatSync(fd);
if (before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) throw new Error('toolchain changed');
return { path: resolved, sha256: hash.digest('hex'), dev: before.dev, ino: before.ino };
} finally { fs.closeSync(fd); }
}
function nativeIdentity(pid: number): Native {
if (process.platform !== 'linux') throw new Error('native lifetime qualification requires Linux procfs');
const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8').split(') ').slice(1).join(') ').split(' ');
return { pid, start: stat[19], group: Number(stat[2]), settled: false };
}
function alive(native: Native): boolean {
try { return nativeIdentity(native.pid).start === native.start; }
catch (error: any) { if (error.code === 'ENOENT' || error.code === 'ESRCH') return false; throw error; }
}
function exitedDuringCensus(pid: string, start: string, deadline: number) {
const until = Math.min(deadline, Date.now() + 20);
const pause = new Int32Array(new SharedArrayBuffer(4));
while (true) {
let stat: string[];
try { stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8').split(') ').slice(1).join(') ').split(' '); }
catch (error: any) { if (error.code === 'ENOENT' || error.code === 'ESRCH') return true; throw error; }
if (stat[19] !== start) return false;
if (stat[0] === 'Z' || stat[0] === 'X') return true;
if (!(Number(stat[6]) & 4) || Date.now() >= until) return false;
Atomics.wait(pause, 0, 0, 1);
}
}
function quiet(scope: Scope, registration: Registration, deadline: number) {
verifyPrivateTemporary(scope.temporary);
if (!registration.native) throw new Error('native lifetime not registered');
for (const pid of fs.readdirSync('/proc').filter(name => /^\d+$/.test(name))) {
let stat: string[];
try { stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8').split(') ').slice(1).join(') ').split(' '); }
catch (error: any) { if (error.code === 'ENOENT' || error.code === 'ESRCH') continue; throw new Error('process census unavailable'); }
if (stat[0] === 'Z' || stat[0] === 'X') continue;
if (Number(stat[2]) === registration.native.group) throw new Error('native group remains live');
if (Number(pid) === process.pid) continue;
try {
if (fs.statSync(`/proc/${pid}`).uid !== process.getuid!()) continue;
const cwd = fs.readlinkSync(`/proc/${pid}/cwd`);
if (cwd === registration.root.path || inside(registration.root.path, cwd)) throw new Error('fixture process remains live');
for (const fd of fs.readdirSync(`/proc/${pid}/fd`)) {
const target = fs.readlinkSync(`/proc/${pid}/fd/${fd}`);
if (!inside(registration.root.path, target)) continue;
const flags = fs.readFileSync(`/proc/${pid}/fdinfo/${fd}`, 'utf8').match(/^flags:\s+(\d+)/m);
if (!flags || (parseInt(flags[1], 8) & 3) !== 0) throw new Error('fixture writer remains live');
}
} catch (error: any) {
if (error.code === 'ENOENT' || error.code === 'ESRCH') continue;
if (error.code === 'EACCES' || error.code === 'EPERM') {
if (exitedDuringCensus(pid, stat[19], deadline)) continue;
if (scope.uninspectable.some(native => native.pid === Number(pid) && native.start === stat[19] && alive(native))) continue;
throw new Error(`writer census unavailable: ${error.code} ${error.syscall} ${error.path}`);
}
throw error;
}
}
}
function loadScope(env: NodeJS.ProcessEnv): Scope {
if (!env[scopeVariable]) throw new Error('bootstrap retention requires a runner-owned scope');
const scope: Scope = JSON.parse(env[scopeVariable]!);
verifyPrivateTemporary(scope.temporary); verify(scope.durable); verify(scope.registry);
if (!inside(scope.temporary.path, scope.registry.path) || inside(scope.temporary.path, scope.durable.path)) throw new Error('invalid retention scope');
return scope;
}
export function createBootstrapRetentionScope(temporaryRoot: string, durableRoot: string, runId: string) {
const temporary = identity(temporaryRoot);
if (fs.lstatSync(temporary.path).uid !== process.getuid!()) throw new Error('temporary state is not privately owned');
fs.chmodSync(temporary.path, 0o700);
verifyPrivateTemporary(temporary);
if (fs.readdirSync(temporary.path).length) throw new Error('retention scope requires empty temporary state');
const uninspectable: Native[] = [];
for (const pid of fs.readdirSync('/proc').filter(name => /^\d+$/.test(name))) {
let native: Native | undefined;
try {
if (fs.statSync(`/proc/${pid}`).uid !== process.getuid!()) continue;
native = nativeIdentity(Number(pid));
fs.readlinkSync(`/proc/${pid}/cwd`);
fs.readdirSync(`/proc/${pid}/fd`);
} catch (error: any) {
if (error.code === 'ENOENT' || error.code === 'ESRCH') continue;
if (native && (error.code === 'EACCES' || error.code === 'EPERM') && alive(native)) uninspectable.push(native);
else throw error;
}
}
if (path.resolve(durableRoot) === temporary.path || inside(temporary.path, path.resolve(durableRoot))) throw new Error('artifact root must survive temporary cleanup');
fs.mkdirSync(durableRoot, { recursive: true, mode: 0o700 });
const durable = fs.mkdtempSync(path.join(fs.realpathSync(durableRoot), 'bootstrap-'));
fs.chmodSync(durable, 0o700);
const registry = fs.mkdtempSync(path.join(fs.realpathSync(temporaryRoot), '.bootstrap-'));
fs.chmodSync(registry, 0o700);
if (inside(temporary.path, fs.realpathSync(durableRoot))) throw new Error('artifact root resolves into temporary state');
const scope: Scope = { runId, temporary, durable: identity(durable), registry: identity(registry), uninspectable };
return { env: { [scopeVariable]: JSON.stringify(scope) }, cleanup: (deadline: number) => cleanupBootstrapRetentions(scope, deadline) };
}
export function registerBootstrapRetention(root: string, runId: string, options: { deadline: number; env?: NodeJS.ProcessEnv }) {
const scope = loadScope(options.env ?? process.env);
if (!Number.isFinite(options.deadline) || options.deadline <= Date.now()) throw new Error('bootstrap deadline expired');
const owned = identity(root);
if (runId !== scope.runId || path.dirname(owned.path) !== scope.temporary.path || !path.basename(root).startsWith('skill-e2e-bs-')) throw new Error('wrong bootstrap root or run');
const git = (args: string[]) => {
const result = spawnSync('git', args, { cwd: root, encoding: 'utf8', timeout: 5000 });
if (result.status !== 0) throw new Error('initial Git input unavailable');
return result.stdout;
};
const attempt = randomUUID();
const artifact = path.join(scope.durable.path, attempt);
fs.mkdirSync(artifact, { mode: 0o700 });
const gitStatus = git(['status', '--porcelain=v1']);
if (gitStatus.trim()) throw new Error('initial Git inputs are not clean');
const registration: Registration = {
attempt, runId, deadline: options.deadline, root: owned, artifact: identity(artifact), owner: nativeIdentity(process.pid),
initial: { package: readRegular(path.join(root, 'package.json')).toString('base64'),
gitHead: git(['rev-parse', 'HEAD']), gitTree: git(['rev-parse', 'HEAD^{tree}']), gitFiles: git(['ls-files', '--stage']), gitStatus,
bun: { version: Bun.version, ...executableIdentity(process.execPath) },
nodeCompatibility: process.versions.node, externalNode: Bun.which('node') ? executableIdentity(Bun.which('node')!) : null,
platform: process.platform, arch: process.arch },
};
const save = () => durableWrite(path.join(scope.registry.path, attempt + '.json'), JSON.stringify(registration));
durableWrite(path.join(artifact, 'registration.json'), JSON.stringify(registration));
save();
return {
attempt, artifact,
lifecycle: {
onSpawn(pid: number) {
registration.native = nativeIdentity(pid);
if (registration.native.group !== pid) throw new Error('native process is not group owner');
const executable = fs.realpathSync(`/proc/${pid}/exe`);
registration.native.executable = executableIdentity(executable);
save();
durableWrite(path.join(artifact, 'registration.json'), JSON.stringify(registration));
},
async onSettled(input: { deadline: number; exited: boolean }) {
if (!input.exited) throw new Error('native exit was not observed');
while (true) {
try { quiet(scope, registration, input.deadline); break; }
catch (error) {
if (Date.now() >= input.deadline) throw error;
await new Promise(resolve => setTimeout(resolve, Math.min(20, input.deadline - Date.now())));
}
}
registration.native!.settled = true;
save();
},
},
retain() { return retain(scope, registration, false, options.deadline); },
cleanup() {
const receipt = retain(scope, registration, false, options.deadline);
if (receipt.complete && receipt.acknowledged && receipt.quiescent) { verify(registration.root); fs.rmSync(root, { recursive: true }); }
if (!receipt.complete || !receipt.acknowledged) throw new Error(`bootstrap retention failed: ${receipt.errors.join('; ')}`);
return receipt;
},
};
}
function retain(scope: Scope, registration: Registration, fallback: boolean, ownerDeadline: number): BootstrapReceipt {
if (!/^[0-9a-f-]{36}$/.test(registration.attempt)) throw new Error('wrong attempt');
const artifact = path.join(scope.durable.path, registration.attempt);
if (registration.artifact.path !== artifact) throw new Error('wrong artifact root');
const receipt: BootstrapReceipt = { attempt: registration.attempt, complete: false, acknowledged: false, quiescent: false, errors: [], artifact };
const entries: Entry[] = [];
try {
verify(scope.temporary); verify(scope.registry); verify(scope.durable); verify(registration.artifact);
if (!/^[0-9a-f-]{36}$/.test(registration.attempt) || registration.runId !== scope.runId || path.dirname(registration.root.path) !== scope.temporary.path || !path.basename(registration.root.path).startsWith('skill-e2e-bs-')) throw new Error('wrong attempt or root');
verify(registration.root);
if (fallback && alive(registration.owner)) throw new Error('attempt owner remains live');
const deadline = Math.min(registration.deadline, ownerDeadline);
quiet(scope, registration, deadline);
receipt.quiescent = true;
if (!Number.isFinite(deadline) || Date.now() >= deadline) throw new Error('retention deadline expired');
if (!fallback && !registration.native?.settled) throw new Error('native settlement not acknowledged');
let bytes = 0;
const seen = new Set<string>();
const visit = (relative: string, copy: boolean) => {
if (seen.has(relative)) return;
seen.add(relative);
if (seen.size > 50000 || Date.now() > deadline) throw new Error('inventory limit exceeded');
verify(registration.root);
const file = path.join(registration.root.path, relative);
const stat = fs.lstatSync(file);
if (stat.isSymbolicLink()) {
const target = fs.readlinkSync(file);
const resolved = fs.realpathSync(file);
if (!inside(registration.root.path, resolved)) throw new Error('escaping installed link');
const destination = path.relative(registration.root.path, resolved);
if (!destination.startsWith('node_modules' + path.sep)) throw new Error('installed link leaves package tree');
entries.push({ path: relative, kind: 'link', target, resolved: destination });
visit(destination, false);
} else if (stat.isDirectory()) {
if (fs.realpathSync(file) !== file) throw new Error('directory link changed during capture');
entries.push({ path: relative, kind: 'directory' });
for (const name of fs.readdirSync(file).sort()) visit(path.join(relative, name), copy);
} else {
if (!stat.isFile() || fs.realpathSync(file) !== file) throw new Error('unsafe installed file');
bytes += stat.size;
if (bytes > 256 * 1024 * 1024) throw new Error('inventory byte limit exceeded');
const content = readRegular(file, 64 * 1024 * 1024);
entries.push({ path: relative, kind: 'file', bytes: content.length, sha256: digest(content) });
if (copy || path.basename(file) === 'package.json') {
const output = path.join(artifact, 'files', relative);
artifactDirectory(registration.artifact, path.dirname(output));
durableWrite(output, content);
if (!fs.readFileSync(output).equals(content)) throw new Error('copy verification failed');
}
}
};
visit('package.json', true);
const availableLocks = locks.filter(name => fs.existsSync(path.join(registration.root.path, name)));
for (const lock of availableLocks) visit(lock, true);
if (!availableLocks.length) receipt.errors.push('installed lock missing');
if (!fs.existsSync(path.join(registration.root.path, 'node_modules'))) receipt.errors.push('installed graph missing');
else visit('node_modules', false);
if (!entries.some(entry => entry.path.startsWith('node_modules/') && entry.path.endsWith('/package.json'))) receipt.errors.push('installed manifests missing');
for (const entry of entries) {
const file = path.join(registration.root.path, entry.path);
if (entry.kind === 'file' && digest(readRegular(file, 64 * 1024 * 1024)) !== entry.sha256) throw new Error('inventory changed');
if (entry.kind === 'link' && (fs.readlinkSync(file) !== entry.target || path.relative(registration.root.path, fs.realpathSync(file)) !== entry.resolved)) throw new Error('link changed');
if (entry.kind === 'directory') {
const children = entries.filter(child => path.dirname(child.path) === entry.path).map(child => path.basename(child.path)).sort();
if (JSON.stringify(fs.readdirSync(file).sort()) !== JSON.stringify(children)) throw new Error('inventory changed');
}
if (Date.now() > deadline) throw new Error('verification limit exceeded');
}
verify(registration.root);
receipt.quiescent = false;
quiet(scope, registration, deadline);
receipt.quiescent = true;
receipt.complete = receipt.errors.length === 0;
} catch (error) { receipt.errors.push(error instanceof Error ? error.message : 'capture failed'); }
try {
verify(scope.durable); verify(registration.artifact);
const evidence = JSON.stringify({ registration, fallback, receipt, entries, uninspectable: scope.uninspectable });
durableWrite(path.join(artifact, fallback ? 'fallback-evidence.json' : 'callback-evidence.json'), evidence);
durableWrite(path.join(artifact, 'evidence.json'), evidence);
if (digest(fs.readFileSync(path.join(artifact, 'evidence.json'))) !== digest(evidence)) throw new Error('evidence readback failed');
durableWrite(path.join(artifact, 'ack.json'), JSON.stringify({ attempt: registration.attempt, sha256: digest(evidence), complete: receipt.complete }));
const ack = JSON.parse(fs.readFileSync(path.join(artifact, 'ack.json'), 'utf8'));
receipt.acknowledged = ack.attempt === registration.attempt && ack.sha256 === digest(evidence);
} catch { receipt.errors.push('durable acknowledgment failed'); receipt.complete = false; }
return receipt;
}
async function cleanupBootstrapRetentions(scope: Scope, deadline: number) {
verifyPrivateTemporary(scope.temporary);
verify(scope.registry);
const receipts: BootstrapReceipt[] = [];
for (const name of fs.readdirSync(scope.registry.path).sort()) {
if (!name.endsWith('.json')) throw new Error('unfinished bootstrap registration');
const registration: Registration = JSON.parse(readRegular(path.join(scope.registry.path, name)).toString());
if (!/^[0-9a-f-]{36}\.json$/.test(name) || name !== registration.attempt + '.json' || registration.runId !== scope.runId) throw new Error('wrong attempt registration');
const artifact = path.join(scope.durable.path, registration.attempt);
try {
verify(scope.durable); verify(registration.artifact);
if (registration.artifact.path !== artifact) throw new Error('wrong artifact root');
const evidence = fs.readFileSync(path.join(artifact, 'evidence.json'));
const ack = JSON.parse(readRegular(path.join(artifact, 'ack.json')).toString());
if (ack.attempt !== registration.attempt || ack.sha256 !== digest(evidence)) throw new Error('invalid acknowledgment');
const saved = JSON.parse(evidence.toString());
if (JSON.stringify(saved.registration) !== JSON.stringify(registration)) throw new Error('registration changed');
if (!saved.receipt.quiescent) throw new Error('previous retention did not establish quiescence');
receipts.push({ ...saved.receipt, acknowledged: true });
} catch {
try {
verify(scope.temporary); verify(scope.durable); verify(registration.artifact); verify(registration.root);
const durableRegistration = JSON.parse(readRegular(path.join(artifact, 'registration.json')).toString());
if (JSON.stringify(durableRegistration) !== JSON.stringify({ ...registration, native: registration.native && { ...registration.native, settled: false } })) throw new Error('native registration mismatch');
if (alive(registration.owner)) throw new Error('attempt owner remains live');
if (registration.native && alive(registration.native)) {
if (nativeIdentity(registration.native.pid).group !== registration.native.pid) throw new Error('native group identity changed');
process.kill(-registration.native.pid, 'SIGKILL');
}
while (Date.now() < deadline) {
try { quiet(scope, registration, deadline); break; }
catch { await new Promise(resolve => setTimeout(resolve, Math.min(20, deadline - Date.now()))); }
}
} catch {}
receipts.push(retain(scope, registration, true, deadline));
}
}
return { complete: receipts.every(receipt => receipt.complete), removable: receipts.every(receipt => receipt.complete && receipt.acknowledged && receipt.quiescent), receipts };
}