// release_freeze.ts — Phase 9 final orchestrator. Runs every gate the // distillation system has + writes a release manifest + verifies clean // git state. Never creates the git tag itself — prints the command for // J to authorize. // // USAGE // bun run scripts/distillation/release_freeze.ts // // Exit code 0 = release-ready. Non-zero = one or more gates failed. import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync, } from "node:fs"; import { resolve, dirname } from "node:path"; import { spawnSync } from "node:child_process"; const DEFAULT_ROOT = process.env.LH_DISTILL_ROOT ?? "/home/profit/lakehouse"; const VERSION = "v1.0.0"; const TAG = `distillation-${VERSION}`; // Phase → known commit. Sourced from git log; if a commit gets // rewritten the manifest will surface the mismatch. const PHASE_COMMITS: Array<{ phase: string; commit: string; subject: string }> = [ { phase: "0+1+2 scaffold", commit: "27b1d27", subject: "distillation: Phase 0 recon + Phase 1 schemas + Phase 2 transforms scaffold" }, { phase: "2 materializer", commit: "1ea8029", subject: "distillation: Phase 2 — Evidence View materializer + health audit" }, { phase: "3 scorer", commit: "c989253", subject: "distillation: Phase 3 — deterministic Success Scorer" }, { phase: "4 exports", commit: "68b6697", subject: "distillation: Phase 4 — dataset export layer" }, { phase: "5 receipts", commit: "2cf359a", subject: "distillation: Phase 5 — receipts harness (system-level observability)" }, { phase: "6 acceptance", commit: "1b433a9", subject: "distillation: Phase 6 — acceptance gate suite" }, { phase: "auditor rebuild", commit: "20a039c", subject: "auditor: rebuild on mode runner + drop tree-split (use distillation substrate)" }, { phase: "7 replay", commit: "681f39d", subject: "distillation: Phase 7 — replay-driven local model bootstrapping" }, { phase: "8 audit-full", commit: "5bdd159", subject: "distillation: Phase 8 — full system audit" }, ]; interface GateResult { name: string; passed: boolean; detail: string; } const gates: GateResult[] = []; function gate(name: string, passed: boolean, detail: string) { gates.push({ name, passed, detail }); } function shell(cmd: string, args: string[], cwd = DEFAULT_ROOT, timeoutMs = 600_000) { return spawnSync(cmd, args, { cwd, encoding: "utf8", timeout: timeoutMs, env: { ...process.env, LH_DISTILL_ROOT: cwd } }); } function gitOutput(args: string[]): string { const r = spawnSync("git", ["-C", DEFAULT_ROOT, ...args], { encoding: "utf8" }); return r.status === 0 ? r.stdout.trim() : ""; } // ─── Gate 1: clean git state ────────────────────────────────────── function checkCleanGit() { const status = gitOutput(["status", "--porcelain"]); // Tolerate two classes of dirty: // 1. Untracked artifacts (data/, exports/, /tmp/, reports/distillation//) // 2. Auto-regenerated reports under reports/distillation/phase*-*.md + // reports/distillation/release-*.{md,json} — release-freeze itself // rewrites these before it can check the gate const lines = status.split("\n").filter(Boolean); const tracked = lines.filter(l => /^\s*[MADRCU]/.test(l)); const concerning = tracked.filter(l => { // git status --porcelain format: "XY " where XY is 2-char status const m = l.match(/^[\sMADRCU]{2}\s+(.+?)$/); const path = m ? m[1] : l.replace(/^\s*[MADRCU]+\s*/, ""); if (/^reports\/distillation\/phase\d+-.*\.md$/.test(path)) return false; if (/^reports\/distillation\/release-.*\.(md|json)$/.test(path)) return false; return true; }); const passed = concerning.length === 0; gate( "clean git state (no source-tree modifications)", passed, passed ? `tree clean (${tracked.length - concerning.length} auto-regenerated reports tolerated)` : `${concerning.length} concerning modified file(s):\n${concerning.slice(0, 6).map(l => " " + l).join("\n")}`, ); } // ─── Gate 2: full test suite ────────────────────────────────────── function checkTests() { const r = shell("bun", ["test", "tests/distillation/", "auditor/schemas/distillation/"]); const out = (r.stdout ?? "") + (r.stderr ?? ""); const m = out.match(/(\d+)\s*pass\s*\n\s*(\d+)\s*fail/); const pass = m ? Number(m[1]) : 0; const fail = m ? Number(m[2]) : 1; gate( `full test suite (bun test tests/distillation/ auditor/schemas/distillation/)`, r.status === 0 && fail === 0, `${pass} pass, ${fail} fail (exit=${r.status})`, ); } // ─── Gate 3: acceptance gate ────────────────────────────────────── function checkAcceptance() { const r = shell("bun", ["run", "scripts/distillation/acceptance.ts"]); const out = (r.stdout ?? "") + (r.stderr ?? ""); const m = out.match(/PASS\s*—\s*(\d+)\/(\d+)/); const passed = r.status === 0 && m && m[1] === m[2]; gate( "acceptance gate (22-invariant fixture E2E)", !!passed, m ? `${m[1]}/${m[2]} invariants` : `exit=${r.status}, no PASS line found`, ); } // ─── Gate 4: full audit ─────────────────────────────────────────── function checkAuditFull() { const r = shell("bun", ["run", "scripts/distillation/audit_full.ts"]); const out = (r.stdout ?? "") + (r.stderr ?? ""); const m = out.match(/PASS\s*—\s*(\d+)\/(\d+)\s*required/); const passed = r.status === 0 && m && m[1] === m[2]; gate( "audit-full (Phases 0-7 verified + drift)", !!passed, m ? `${m[1]}/${m[2]} required checks` : `exit=${r.status}`, ); } // ─── Gate 5: tag does not yet exist ────────────────────────────── function checkTagAvailable() { const tags = gitOutput(["tag", "-l", TAG]); const exists = tags.trim() === TAG; gate( `tag ${TAG} available (does not yet exist)`, !exists, exists ? `tag already exists; bump VERSION or delete the prior tag` : "tag name is free", ); } // ─── Gather dataset/export counts ──────────────────────────────── interface DatasetCounts { rag_rows: number; sft_rows: number; preference_pairs: number; evidence_files: number; evidence_rows: number; scored_files: number; scored_rows: number; quarantined_total: number; } function countLines(path: string): number { if (!existsSync(path)) return 0; return readFileSync(path, "utf8").split("\n").filter(Boolean).length; } function walkCount(dir: string): { files: number; rows: number } { if (!existsSync(dir)) return { files: 0, rows: 0 }; let files = 0, rows = 0; function walk(p: string) { for (const e of readdirSync(p)) { const full = resolve(p, e); const st = statSync(full); if (st.isDirectory()) walk(full); else if (e.endsWith(".jsonl")) { files++; rows += countLines(full); } } } walk(dir); return { files, rows }; } function gatherCounts(root: string): DatasetCounts { const ev = walkCount(resolve(root, "data/evidence")); const sc = walkCount(resolve(root, "data/scored-runs")); return { rag_rows: countLines(resolve(root, "exports/rag/playbooks.jsonl")), sft_rows: countLines(resolve(root, "exports/sft/instruction_response.jsonl")), preference_pairs: countLines(resolve(root, "exports/preference/chosen_rejected.jsonl")), evidence_files: ev.files, evidence_rows: ev.rows, scored_files: sc.files, scored_rows: sc.rows, quarantined_total: ["sft", "rag", "preference"] .reduce((acc, n) => acc + countLines(resolve(root, `exports/quarantine/${n}.jsonl`)), 0), }; } // ─── Gather latest baseline ────────────────────────────────────── function loadLatestBaseline(root: string): any { const p = resolve(root, "data/_kb/audit_baselines.jsonl"); if (!existsSync(p)) return null; const lines = readFileSync(p, "utf8").split("\n").filter(Boolean); if (lines.length === 0) return null; try { return JSON.parse(lines[lines.length - 1]); } catch { return null; } } // ─── Verify phase commits actually exist ───────────────────────── function verifyPhaseCommits() { const missing: string[] = []; for (const p of PHASE_COMMITS) { const full = gitOutput(["rev-parse", p.commit]); if (!full || full.length < 40) missing.push(`${p.phase} (${p.commit})`); } gate( "every phase commit resolves", missing.length === 0, missing.length === 0 ? `${PHASE_COMMITS.length}/${PHASE_COMMITS.length} commits verified` : `missing: ${missing.join(", ")}`, ); } // ─── Build manifest + report ───────────────────────────────────── interface Manifest { schema: "distillation_release_manifest.v1"; version: string; tag: string; released_at: string; git_head: string; git_branch: string; phase_commits: typeof PHASE_COMMITS; dataset_counts: DatasetCounts; latest_baseline: any; gates: GateResult[]; passed: boolean; } function renderReport(m: Manifest): string { const md: string[] = []; md.push("# Distillation Release Freeze — " + m.version); md.push(""); md.push(`**Tag (proposed):** \`${m.tag}\``); md.push(`**Released at:** ${m.released_at}`); md.push(`**Git head:** \`${m.git_head}\``); md.push(`**Branch:** ${m.git_branch}`); md.push(""); md.push(`## Result: ${m.passed ? "**RELEASE-READY** ✓" : "**NOT READY ✗** — one or more gates failed"}`); md.push(""); md.push("## Gates"); md.push(""); md.push("| # | Gate | Status | Detail |"); md.push("|---|---|---|---|"); for (let i = 0; i < m.gates.length; i++) { const g = m.gates[i]; md.push(`| ${i + 1} | ${g.name} | ${g.passed ? "✓" : "✗ FAIL"} | ${g.detail.split("\n")[0].slice(0, 100)} |`); } md.push(""); md.push("## Phase commits"); md.push(""); md.push("| Phase | Commit | Subject |"); md.push("|---|---|---|"); for (const p of m.phase_commits) { md.push(`| ${p.phase} | \`${p.commit}\` | ${p.subject} |`); } md.push(""); md.push("## Dataset counts at freeze"); md.push(""); md.push("| Artifact | Count |"); md.push("|---|---|"); md.push(`| RAG rows | ${m.dataset_counts.rag_rows} |`); md.push(`| SFT rows (strict accepted-only) | ${m.dataset_counts.sft_rows} |`); md.push(`| Preference pairs | ${m.dataset_counts.preference_pairs} |`); md.push(`| Evidence files | ${m.dataset_counts.evidence_files} |`); md.push(`| Evidence rows | ${m.dataset_counts.evidence_rows} |`); md.push(`| Scored-run files | ${m.dataset_counts.scored_files} |`); md.push(`| Scored rows | ${m.dataset_counts.scored_rows} |`); md.push(`| Quarantined total | ${m.dataset_counts.quarantined_total} |`); md.push(""); if (m.latest_baseline) { md.push("## Latest audit baseline"); md.push(""); md.push("```json"); md.push(JSON.stringify(m.latest_baseline, null, 2)); md.push("```"); md.push(""); } md.push("## Tag command (run after release-ready confirmation)"); md.push(""); md.push("```bash"); md.push(`git tag -a ${m.tag} ${m.git_head.slice(0, 12)} -m "distillation v${m.version.replace(/^v/, "")} — 8-phase substrate frozen"`); md.push(`git push origin ${m.tag}`); md.push("```"); md.push(""); md.push("## Failure detail"); md.push(""); const failed = m.gates.filter(g => !g.passed); if (failed.length === 0) { md.push("(no failures)"); } else { for (const g of failed) { md.push(`### ${g.name}`); md.push(""); md.push("```"); md.push(g.detail); md.push("```"); md.push(""); } } return md.join("\n"); } async function main() { const root = DEFAULT_ROOT; console.log("[release-freeze] running gates..."); checkCleanGit(); checkTests(); verifyPhaseCommits(); checkAcceptance(); checkAuditFull(); checkTagAvailable(); const counts = gatherCounts(root); const baseline = loadLatestBaseline(root); const manifest: Manifest = { schema: "distillation_release_manifest.v1", version: VERSION, tag: TAG, released_at: new Date().toISOString(), git_head: gitOutput(["rev-parse", "HEAD"]), git_branch: gitOutput(["rev-parse", "--abbrev-ref", "HEAD"]), phase_commits: PHASE_COMMITS, dataset_counts: counts, latest_baseline: baseline, gates, passed: gates.every(g => g.passed), }; const reportPath = resolve(root, "reports/distillation/release-freeze.md"); mkdirSync(dirname(reportPath), { recursive: true }); writeFileSync(reportPath, renderReport(manifest)); // Also persist the manifest JSON for machines. const manifestPath = resolve(root, "reports/distillation/release-manifest.json"); writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n"); console.log(""); console.log(`[release-freeze] ${manifest.passed ? "RELEASE-READY" : "NOT READY"} — ${gates.filter(g => g.passed).length}/${gates.length} gates passed`); for (const g of gates) { console.log(` ${g.passed ? "✓" : "✗"} ${g.name}`); if (!g.passed) console.log(` ${g.detail.split("\n").slice(0, 2).join(" | ")}`); } console.log(""); console.log(`[release-freeze] manifest: ${manifestPath}`); console.log(`[release-freeze] report: ${reportPath}`); if (manifest.passed) { console.log(""); console.log("To create the tag (manual step — operator must confirm):"); console.log(` git tag -a ${TAG} -m "distillation v${VERSION.replace(/^v/, "")} — 8-phase substrate frozen"`); console.log(` git push origin ${TAG}`); } process.exit(manifest.passed ? 0 : 1); } if (import.meta.main) main().catch(e => { console.error(e); process.exit(1); });