import { existsSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { access, lstat, mkdir, mkdtemp, readFile, readdir, readlink, rm } from 'node:fs/promises'; import { spawnSync } from 'node:child_process'; import path from 'node:path'; function git(root, args, { allowFailure = false } = {}) { const result = spawnSync('git', args, { cwd: root, encoding: 'utf8' }); if (result.status !== 0 && !allowFailure) { throw new Error(`git ${args.join(' ')} failed: ${(result.stderr || result.stdout).trim()}`); } return result; } export async function listProspectiveCommits(root, activationCommit, head = 'HEAD') { const result = git(root, [ 'rev-list', '--first-parent', '--reverse', `${activationCommit}..${head}`, ]); return result.stdout.trim() ? result.stdout.trim().split('\n') : []; } export async function readManifestAtCommit(root, commit) { const result = git(root, ['show', `${commit}:gates/gates.manifest.json`]); return JSON.parse(result.stdout); } async function snapshotAuthoritativeTree(root) { const snapshot = new Map(); async function walk(current) { for (const child of await readdir(current, { withFileTypes: true })) { if (['.git', '.home', 'node_modules'].includes(child.name)) continue; const absolute = path.join(current, child.name); const relative = path.relative(root, absolute).split(path.sep).join('/'); const stats = await lstat(absolute); if (stats.isDirectory()) { await walk(absolute); } else if (stats.isSymbolicLink()) { snapshot.set(relative, `symlink:${stats.mode}:${await readlink(absolute)}`); } else if (stats.isFile()) { const digest = createHash('sha256').update(await readFile(absolute)).digest('hex'); snapshot.set(relative, `file:${stats.mode}:${digest}`); } } } await walk(root); return snapshot; } async function authoritativeTreeChanges(root, snapshot) { const changes = []; for (const [relative, expected] of snapshot) { const absolute = path.join(root, relative); let actual; try { const stats = await lstat(absolute); if (stats.isSymbolicLink()) { actual = `symlink:${stats.mode}:${await readlink(absolute)}`; } else if (stats.isFile()) { const digest = createHash('sha256').update(await readFile(absolute)).digest('hex'); actual = `file:${stats.mode}:${digest}`; } else { actual = `other:${stats.mode}`; } } catch (error) { if (error.code !== 'ENOENT') throw error; actual = 'missing'; } if (actual !== expected) changes.push(relative); } return changes; } function bubblewrap(root, command, args, { storePath, timeout = 300_000 } = {}) { const sandboxArgs = [ '--unshare-net', '--unshare-pid', '--unshare-ipc', '--unshare-uts', '--die-with-parent', '--new-session', '--clearenv', ]; for (const systemPath of ['/usr', '/bin', '/lib', '/lib64', '/etc']) { if (existsSync(systemPath)) sandboxArgs.push('--ro-bind', systemPath, systemPath); } sandboxArgs.push('--dev', '/dev', '--proc', '/proc', '--tmpfs', '/tmp', '--bind', root, '/work'); if (storePath) sandboxArgs.push('--ro-bind', storePath, '/pnpm-store'); const corepackHome = path.join(process.env.HOME ?? '', '.cache', 'node', 'corepack'); if (existsSync(corepackHome)) sandboxArgs.push('--ro-bind', corepackHome, '/corepack'); sandboxArgs.push( '--chdir', '/work', '--setenv', 'HOME', '/work/.home', '--setenv', 'PATH', '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', '--setenv', 'LANG', 'C.UTF-8', '--setenv', 'CI', 'true', ); if (storePath) sandboxArgs.push('--setenv', 'NPM_CONFIG_STORE_DIR', '/pnpm-store'); if (existsSync(corepackHome)) sandboxArgs.push('--setenv', 'COREPACK_HOME', '/corepack'); sandboxArgs.push(command, ...args); return spawnSync('bwrap', sandboxArgs, { encoding: 'utf8', timeout }); } export async function replayCommit(root, commit) { const replayRoot = await mkdtemp( path.join(path.dirname(root), `.gate-history-${commit.slice(0, 12)}-`), ); const archive = `${replayRoot}.tar`; try { git(root, ['archive', '--format=tar', `--output=${archive}`, commit]); const extract = spawnSync('tar', ['-xf', archive, '-C', replayRoot], { encoding: 'utf8' }); if (extract.status !== 0) { return { status: extract.status, stdout: extract.stdout, stderr: extract.stderr }; } const authoritativeSnapshot = await snapshotAuthoritativeTree(replayRoot); await mkdir(path.join(replayRoot, '.home'), { recursive: true }); let storePath; try { await access(path.join(replayRoot, 'package.json')); const init = spawnSync('git', ['init', '--quiet', replayRoot], { encoding: 'utf8' }); if (init.status !== 0) return init; const store = spawnSync('pnpm', ['store', 'path'], { encoding: 'utf8' }); if (store.status !== 0) return store; storePath = store.stdout.trim(); const install = bubblewrap( replayRoot, 'pnpm', ['install', '--frozen-lockfile', '--offline'], { storePath, timeout: 600_000 }, ); if (install.status !== 0 || install.error || install.signal) { return { ...install, stderr: `historical frozen dependency install failed: ${install.stderr || install.stdout || ''}`, }; } const authoritativeChanges = await authoritativeTreeChanges( replayRoot, authoritativeSnapshot, ); if (authoritativeChanges.length > 0) { return { status: 1, stdout: '', stderr: `authoritative archived file changed during historical install: ${authoritativeChanges.join(', ')}`, }; } } catch (error) { if (error.code !== 'ENOENT') throw error; } return bubblewrap( replayRoot, process.execPath, [ '/work/scripts/gate-verify.mjs', '--root', '/work', '--manifest', 'gates/gates.manifest.json', '--skip-history', ], { storePath }, ); } finally { await rm(archive, { force: true }); await rm(replayRoot, { recursive: true, force: true }); } } export function assessProviderEvidence(commit, pipelines) { const matches = pipelines.filter((candidate) => candidate.commit === commit); if (matches.length === 0) { return { state: 'absent', detail: 'no retained provider record was supplied; retention expiry and never-ran are not inferred', }; } const pipelineStates = new Set(['success', 'failure', 'error', 'pending', 'running', 'queued']); const stepStates = new Set([ 'success', 'failure', 'error', 'pending', 'running', 'queued', 'skipped', ]); const numbers = matches.map((candidate) => candidate.number); const malformed = matches.some( (candidate) => typeof candidate.commit !== 'string' || candidate.commit.length === 0 || !Number.isInteger(candidate.number) || !pipelineStates.has(candidate.status) || !Array.isArray(candidate.steps) || candidate.steps.some( (step) => typeof step?.name !== 'string' || typeof step?.status !== 'string' || !stepStates.has(step.status), ), ); if (malformed || new Set(numbers).size !== numbers.length) { return { state: 'terminal-failure', detail: 'provider records are malformed or have ambiguous pipeline numbers', }; } const pipeline = [...matches].sort((left, right) => right.number - left.number)[0]; const gateSteps = (pipeline.steps ?? []).filter((step) => step.name === 'gate-verify'); if (gateSteps.length !== 1) { return { state: 'terminal-failure', detail: `provider record has ambiguous gate-verify step count ${gateSteps.length}`, }; } const [gateStep] = gateSteps; if (pipeline.status === 'success' && gateStep.status === 'success') { return { state: 'terminal-success', detail: 'pipeline and gate-verify step succeeded' }; } if (['pending', 'running', 'queued'].includes(pipeline.status)) { return { state: 'current-running', detail: `pipeline is ${pipeline.status}` }; } return { state: 'terminal-failure', detail: `pipeline=${pipeline.status ?? 'unknown'}, gate-verify=${gateStep?.status ?? 'absent'}`, }; } async function loadProviderEvidence() { const evidenceFile = process.env.GATE_PROVIDER_EVIDENCE_FILE; if (!evidenceFile) return []; const parsed = JSON.parse(await readFile(evidenceFile, 'utf8')); if (!Array.isArray(parsed)) throw new Error('provider evidence file must contain a JSON array'); return parsed; } function isMainCommit(root, head) { if (process.env.CI_COMMIT_BRANCH === 'main') return true; const result = git(root, ['merge-base', '--is-ancestor', head, 'refs/remotes/origin/main'], { allowFailure: true, }); return result.status === 0; } export async function verifyHistory({ root, manifest }) { const failures = []; const observations = []; const head = git(root, ['rev-parse', 'HEAD']).stdout.trim(); if (!manifest.activationCommit) { failures.push('history activationCommit is missing'); return { failures, observations }; } const activationCheck = git( root, ['merge-base', '--is-ancestor', manifest.activationCommit, head], { allowFailure: true }, ); if (activationCheck.status !== 0) { failures.push( `history activation commit ${manifest.activationCommit} is not an ancestor of ${head}`, ); return { failures, observations }; } const onMain = isMainCommit(root, head); if (!onMain) { observations.push( `PROVIDER ASSERTION DEFERRED ${head}: commit is not yet on main; prospective own-tree replay still runs, while retained merge evidence starts after merge`, ); } const pipelines = onMain ? await loadProviderEvidence() : []; const commits = await listProspectiveCommits(root, manifest.activationCommit, head); for (const commit of commits) { let commitManifest; try { commitManifest = await readManifestAtCommit(root, commit); } catch (error) { failures.push(`${commit}: own-tree registry cannot be read: ${error.message}`); continue; } if (commitManifest.schemaVersion !== manifest.schemaVersion) { failures.push(`${commit}: own-tree registry schema is not supported`); continue; } const evidence = assessProviderEvidence(commit, pipelines); if (commit === head) { observations.push( `CURRENT TREE EVALUATED ${commit}: all registered cases ran from this checkout; provider evidence=${evidence.state} (${evidence.detail})`, ); continue; } const replay = await replayCommit(root, commit); if (replay.status !== 0 || replay.error || replay.signal) { failures.push( `${commit}: own-tree gate replay failed with exit ${String(replay.status)}${replay.signal ? ` signal ${replay.signal}` : ''}${replay.error ? ` error ${replay.error.message}` : ''}: ${(replay.stderr || replay.stdout || '').trim().slice(0, 500)}`, ); continue; } observations.push(`TREE REPLAY ${commit}: own-tree gate verifier exited 0`); if (!onMain) { observations.push( `PROVIDER EVIDENCE ${commit}: DEFERRED until the commit is on main; no success is inferred`, ); continue; } if (evidence.state === 'terminal-failure') { failures.push( `${commit}: retained provider evidence is not terminal-success (${evidence.detail})`, ); } else if (evidence.state === 'terminal-success') { observations.push(`PROVIDER EVIDENCE ${commit}: terminal-success (${evidence.detail})`); } else { observations.push( `PROVIDER EVIDENCE ${commit}: ${evidence.state.toUpperCase()} (${evidence.detail}); no merge-time success is inferred`, ); } } return { failures, observations }; }