#!/usr/bin/env node /** * Phase 3 closeout gate: Phase 2 evidence + DeepSeek parity + canary unit tests. * Does not authorize 103/105; local loopback only. */ import { spawnSync } from 'node:child_process'; import { checkPassed, classifyCheckResult } from './goose-v149-check-result.mjs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { prepareGooseV149CheckEnv } from './goose-v149-canary.mjs'; import { enforceRealLlmGate } from './goose-v149-real-llm-gate.mjs'; import { gooseV149PortalAgentEnv, waitForAgentRunWorkerIdle, } from './goose-v149-worker-idle.mjs'; const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); enforceRealLlmGate('run-goosed-v149-phase3.mjs'); const checkEnv = prepareGooseV149CheckEnv({ ...process.env }, root); const checks = [ { name: 'phase2', script: 'run-goosed-v149-phase2.mjs', required: true }, { name: 'deepseek-parity', command: [process.execPath, '--test', 'release-gate/deepseek-production-parity.test.mjs'], required: true, }, { name: 'goose-canary-unit', command: [process.execPath, '--test', 'scripts/goose-v149-canary.test.mjs'], required: true, }, { name: 'memory-manifest-unit', command: [process.execPath, '--test', 'scripts/compare-goose-v149-memory-manifest.test.mjs'], required: true, }, { name: 'memory-drift-again', script: 'compare-goose-v149-memory-manifest.mjs', required: true, }, { name: 'portal-resume', script: 'check-goosed-v149-portal-resume.mjs', required: true, extraEnv: { ...gooseV149PortalAgentEnv, GOOSE_V149_PORTAL_RESUME_TIMEOUT_MS: process.env.GOOSE_V149_PORTAL_RESUME_TIMEOUT_MS ?? '600000', }, waitForWorkerIdle: true, }, { name: 'page-e2e', script: 'check-goosed-v149-page-e2e.mjs', required: true, extraEnv: gooseV149PortalAgentEnv, waitForWorkerIdle: true, }, { name: 'memory-chat', script: 'check-goosed-v149-memory-chat.mjs', required: true }, { name: 'missing-evidence', script: 'run-goosed-v149-missing-evidence.mjs', required: true, extraEnv: { GOOSE_V149_MEMORY_VERBAL_FORCE_DEEP: process.env.GOOSE_V149_MEMORY_VERBAL_FORCE_DEEP ?? '0', GOOSE_V149_PORTAL_RESUME_TIMEOUT_MS: process.env.GOOSE_V149_PORTAL_RESUME_TIMEOUT_MS ?? '300000', }, }, ]; function runScript(script, extraEnv = {}) { const result = spawnSync(process.execPath, [path.join(root, 'scripts', script)], { cwd: root, env: { ...checkEnv, ...extraEnv }, encoding: 'utf8', }); return { ok: checkPassed(result), outcome: classifyCheckResult(result), status: result.status, stdout: result.stdout?.trim() ?? '', stderr: result.stderr?.trim() ?? '', }; } function runCommand(command, extraEnv = {}) { const [bin, ...args] = command; const result = spawnSync(bin, args, { cwd: root, env: { ...checkEnv, ...extraEnv }, encoding: 'utf8', }); return { ok: checkPassed(result), outcome: classifyCheckResult(result), status: result.status, stdout: result.stdout?.trim() ?? '', stderr: result.stderr?.trim() ?? '', }; } async function main() { const results = []; for (const check of checks) { if (check.waitForWorkerIdle) { await waitForAgentRunWorkerIdle(root, checkEnv, { logPrefix: '[goose-v149-phase3]', }); } const extraEnv = check.extraEnv ?? {}; const item = { ...check, ...(check.script ? runScript(check.script, extraEnv) : runCommand(check.command, extraEnv)), }; results.push(item); console.log(`[goose-v149-phase3] ${item.outcome} ${check.name}`); if (item.stdout) console.log(item.stdout); if (!item.ok && item.stderr) console.error(item.stderr); } const failedRequired = results.filter((item) => item.required && !item.ok); if (failedRequired.length) { console.error(`GOOSE_V149_PHASE3_FAIL: ${failedRequired.map((item) => item.name).join(', ')}`); process.exit(1); } console.log('GOOSE_V149_PHASE3_OK'); console.log(' next: docs/goose-v149-phase3-closeout.md (merge prep, no 103 until approved)'); } main().catch((error) => { console.error(`GOOSE_V149_PHASE3_FAIL: ${error.message}`); process.exit(1); });