#!/usr/bin/env node import { existsSync, readFileSync } from "node:fs"; const requiredFiles = [ "src/app/routes.tsx", "src/shared/lib/navigation.tsx", "src/shared/components/ProjectFlowRail.tsx", "src/shared/lib/projectStartFlow.ts", "src/features/projects/pages/Projects.tsx", "src/features/projects/pages/Workspace.tsx", "src/features/build-loop/pages/BuildLoopDashboard.tsx", "src/features/repo-builder/pages/RepoBuilder.tsx", "src/features/handoffs/pages/CodexHandoffs.tsx", "src/features/session-packs/pages/SessionPacks.tsx", "src/features/execution-feedback/pages/ExecutionFeedback.tsx", "src/shared/lib/safeData.ts", "src/shared/lib/codexSessionPackExport.ts", "src/shared/lib/downloads.ts", "package.json", ]; const failures = []; for (const file of requiredFiles) { if (!existsSync(file)) failures.push(`Missing required file: ${file}`); } const read = (file) => readFileSync(file, "utf8"); const text = Object.fromEntries(requiredFiles.filter(existsSync).map((file) => [file, read(file)])); const routes = text["src/app/routes.tsx"] ?? ""; const navigation = text["src/shared/lib/navigation.tsx"] ?? ""; const flowRail = text["src/shared/components/ProjectFlowRail.tsx"] ?? ""; const startFlow = text["src/shared/lib/projectStartFlow.ts"] ?? ""; const projects = text["src/features/projects/pages/Projects.tsx"] ?? ""; const workspace = text["src/features/projects/pages/Workspace.tsx"] ?? ""; const buildLoop = text["src/features/build-loop/pages/BuildLoopDashboard.tsx"] ?? ""; const repoBuilder = text["src/features/repo-builder/pages/RepoBuilder.tsx"] ?? ""; const handoffs = text["src/features/handoffs/pages/CodexHandoffs.tsx"] ?? ""; const sessionPacks = text["src/features/session-packs/pages/SessionPacks.tsx"] ?? ""; const feedback = text["src/features/execution-feedback/pages/ExecutionFeedback.tsx"] ?? ""; const safeData = text["src/shared/lib/safeData.ts"] ?? ""; const exportHelper = text["src/shared/lib/codexSessionPackExport.ts"] ?? ""; const downloads = text["src/shared/lib/downloads.ts"] ?? ""; const pkg = text["package.json"] ?? ""; const mustContain = [ [routes, "/start", "Unified Start route must be registered."], [routes, "/projects/new", "Projects/new compatibility route must remain registered."], [routes, "/projects/:projectId/workspace", "Workspace route missing."], [routes, "/projects/:projectId/source-library", "Source Library route missing."], [routes, "/projects/:projectId/skills", "Skill Library route missing."], [routes, "/projects/:projectId/repo-builder", "Repo Builder route missing."], [routes, "/projects/:projectId/handoffs", "Codex Handoffs route missing."], [routes, "/projects/:projectId/session-packs", "Session Packs route missing."], [routes, "/projects/:projectId/execution-feedback", "Execution Feedback route missing."], [routes, "/projects/:projectId/build-loop", "Build Loop route missing."], [routes, "/projects/:projectId/execution", "Execution Center route missing."], [routes, "/projects/:projectId/history", "History route missing."], [routes, "/projects/:projectId/audit", "Audit Center route missing."], [routes, "/projects/:projectId/prompts", "Prompt Builder route missing."], [navigation, "Build Loop", "Navigation must expose Build Loop."], [navigation, "Repo Builder", "Navigation must expose Repo Builder."], [navigation, "Codex Handoffs", "Navigation must expose Codex Handoffs."], [navigation, "Session Packs", "Navigation must expose Session Packs."], [navigation, "Execution Feedback", "Navigation must expose Execution Feedback."], [flowRail, 'id: "source-library"', "Flow Rail must include Source Library."], [flowRail, 'id: "skills"', "Flow Rail must include Skills."], [flowRail, 'id: "repo-builder"', "Flow Rail must include Repo Builder."], [flowRail, 'id: "handoffs"', "Flow Rail must include Handoffs."], [flowRail, 'id: "session-packs"', "Flow Rail must include Session Packs."], [flowRail, 'id: "execution-feedback"', "Flow Rail must include Execution Feedback."], [flowRail, 'id: "build-loop"', "Flow Rail must include Build Loop."], [startFlow, "/start?path=no-idea", "Start flow must support no-idea path."], [startFlow, "/start?path=rough-idea", "Start flow must support rough-idea path."], [startFlow, "/start?path=source-material", "Start flow must support source-material path."], [startFlow, "/start?path=existing-repo", "Start flow must support existing-repo path."], [startFlow, "/projects/new?source=unified-start", "Direct project setup must pass through unified project setup route."], [projects, "/start", "Projects page must link into unified Start."], [projects, "getProjectNextAction", "Projects page must use shared next-action helper."], [workspace, "Build Loop", "Workspace must expose Build Loop."], [workspace, "Session Packs", "Workspace must expose Session Packs."], [buildLoop, "Open linked module", "Build Loop page must expose linked module navigation."], [buildLoop, "Open Session Packs", "Build Loop page must link to Session Packs."], [repoBuilder, "Imported repository evidence is not live filesystem truth", "Repo Builder must label imported evidence boundary."], [handoffs, "ProjectBrain-derived impact simulation, not a live git diff", "Codex Handoffs must label simulated review boundary."], [handoffs, "controlled input for a Codex build session", "Codex Handoffs must explain session pack boundary."], [sessionPacks, "Download instructions/scripts ZIP", "Session Packs must expose clear instructions/scripts ZIP export."], [sessionPacks, "Export session pack files", "Session Packs must keep individual-file export fallback."], [sessionPacks, "ZIP export contains only", "Session Packs must explain ZIP contents."], [feedback, "not live git truth or live filesystem truth", "Execution Feedback must label feedback truth boundary."], [safeData, "repositoryIntakes: asArray(project.repositoryIntakes)", "safeData must normalize repository intakes."], [safeData, "codexHandoffs: asArray(project.codexHandoffs)", "safeData must normalize Codex handoffs."], [safeData, "handoffReviews: asArray(project.handoffReviews)", "safeData must normalize handoff reviews."], [safeData, "codexExecutionFeedback: asArray(project.codexExecutionFeedback)", "safeData must normalize execution feedback."], [safeData, "codexSessionPacks: asArray(project.codexSessionPacks)", "safeData must normalize session packs."], [safeData, "repoStateSnapshots: asArray(project.repoStateSnapshots)", "safeData must normalize repo state snapshots."], [safeData, "repoDeliveryPackages: asArray(project.repoDeliveryPackages)", "safeData must normalize delivery packages."], [safeData, "repoWorkPlans: asArray(project.repoWorkPlans)", "safeData must normalize work plans."], [safeData, "codexSkills: asArray(project.codexSkills)", "safeData must normalize project skill selections."], [safeData, "hiddenHistoryEntryIds: asArray(project.hiddenHistoryEntryIds)", "safeData must normalize hidden history ids."], [exportHelper, "zipExport: true", "Session pack manifest must state ZIP export availability."], [exportHelper, "downloadCodexSessionPackZip", "Session pack helper must expose ZIP download."], [exportHelper, "bundle-manifest.json", "Session pack bundle must include manifest."], [downloads, "URL.revokeObjectURL", "Download helper must revoke Blob URLs."], [pkg, "audit:release-consolidation", "package.json must expose release consolidation audit."], [pkg, "check-build-loop-release-consolidation.mjs", "package.json must wire release consolidation audit."], ]; for (const [haystack, needle, message] of mustContain) { if (!haystack.includes(needle)) failures.push(message); } if (sessionPacks.includes("Download Bundle / ZIP")) { failures.push("Session Packs must not imply that a real ZIP is produced."); } const auditAll = JSON.parse(pkg || "{}")?.scripts?.["audit:all"] ?? ""; if (!auditAll.includes("audit:release-consolidation")) { failures.push("audit:all must include the release consolidation audit."); } if (failures.length) { console.error("ProjectBrain release consolidation readiness: FAIL"); for (const failure of failures) console.error(`- ${failure}`); process.exit(1); } console.log("ProjectBrain release consolidation readiness: PASS");