#!/usr/bin/env node import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; const requiredFiles = [ "src/app/routes.tsx", "src/shared/lib/navigation.tsx", "src/shared/components/ProjectFlowRail.tsx", "src/shared/components/BuildLoopActionRail.tsx", "src/shared/components/ProjectRouteFallback.tsx", "src/shared/lib/projectOperatingStep.ts", "src/shared/lib/projectStartFlow.ts", "src/shared/lib/routeRegistry.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/pages/ExecutionCenter.tsx", "src/features/execution-feedback/pages/ExecutionFeedback.tsx", "src/features/operations/pages/ReviewQueue.tsx", "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 listSourceFiles = (dir) => { if (!existsSync(dir)) return []; return readdirSync(dir).flatMap((entry) => { const path = `${dir}/${entry}`; const stat = statSync(path); if (stat.isDirectory()) return listSourceFiles(path); return /\.(ts|tsx|js|jsx)$/.test(path) ? [path] : []; }); }; const sourceText = Object.fromEntries(listSourceFiles("src").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 buildLoopActionRail = text["src/shared/components/BuildLoopActionRail.tsx"] ?? ""; const projectRouteFallback = text["src/shared/components/ProjectRouteFallback.tsx"] ?? ""; const operatingStep = text["src/shared/lib/projectOperatingStep.ts"] ?? ""; const startFlow = text["src/shared/lib/projectStartFlow.ts"] ?? ""; const routeRegistry = text["src/shared/lib/routeRegistry.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 execution = text["src/features/execution/pages/ExecutionCenter.tsx"] ?? ""; const feedback = text["src/features/execution-feedback/pages/ExecutionFeedback.tsx"] ?? ""; const reviewQueue = text["src/features/operations/pages/ReviewQueue.tsx"] ?? ""; const pkg = text["package.json"] ?? ""; const mustContain = [ [routes, 'projectRoutePattern("workspace")', "Workspace route missing."], [routes, "/projects/:projectId/build-loop", "Build Loop route missing."], [routes, "/projects/:projectId/source-library", "Source Library route missing."], [routes, "/projects/:projectId/review-queue", "Review Queue 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", "Execution Center route missing."], [routes, "/projects/:projectId/execution-feedback", "Execution Feedback 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, "getPrimaryPathNavigationItems", "Primary navigation must expose the workspace that owns the project flow."], [routeRegistry, 'key: "workspace"', "Canonical route registry must expose the workspace that owns the project flow."], [flowRail, 'id: "build-loop"', "Project Flow Rail must expose Build Loop."], [buildLoopActionRail, "Build-loop closure path", "Build Loop action rail must name the handoff-to-current-state path."], [buildLoopActionRail, "execution output is evidence only", "Build Loop action rail must preserve review-before-truth language."], [buildLoopActionRail, "/runtime", "Build Loop action rail must link Current State/Runtime endpoint."], [projectRouteFallback, "Projecten zoeken", "Project route fallback must guide users back to projects."], [projectRouteFallback, "Nieuw project starten", "Project route fallback must guide users back to the unified start flow."], [routes, "routeProjectExists", "Route sync must avoid activating unknown project slugs."], [routes, "ProjectRouteSync", "The app router must synchronize valid project routes."], [navigation, "getProjectIdFromPath", "Project route parsing must be shared with navigation."], [projects, "getProjectOperatingStep", "Projects cards must derive the shared next safe action."], [projects, "operatingState.label", "Projects must make the recommended continuation label explicit."], [projects, "operatingState.description", "Projects must make the recommended continuation explanation explicit."], [buildLoop, "getBuildLoopStatus", "Technical Build Loop must derive its state from the shared helper."], [buildLoop, "getProjectReleaseReadiness", "Technical Build Loop must retain release gates."], [startFlow, "Repository structureren", "Project start flow must expose repository shaping."], [workspace, "Technische details", "Workspace must progressively disclose technical execution."], [navigation, 'level: "advanced"', "Navigation must keep specialist modules outside the primary path."], [workspace, 'data-page-primary="true"', "Workspace must expose one state-driven primary action."], [buildLoop, "Technische route openen", "Build Loop must provide linked module CTA."], [buildLoop, "navigate(step.route)", "Build Loop must link each technical stage to its destination."], [buildLoop, "navigate(gate.route)", "Build Loop must expose destinations for release evidence gates."], [buildLoop, "getFirstTimeBuildLoopSteps", "Build Loop must derive its technical stage navigation."], [buildLoop, "ProjectRouteFallback", "Build Loop must use the shared project fallback."], [repoBuilder, "Huidige fase:", "Repo Builder must expose the current repository phase."], [repoBuilder, "Repository-informatie importeren", "Repo Builder must expose repository intake as the first task in its flow."], [repoBuilder, "ProjectBrain gebruikt dit als geïmporteerde informatie, niet als bewijs van de live-bestanden.", "Repo Builder must show the imported-evidence boundary."], [repoBuilder, "Dit is een ProjectBrain-simulatie, geen live git-diff.", "Repo Builder must label the impact-review simulation boundary."], [handoffs, "Review impact", "Codex Handoffs must expose Review impact flow language."], [handoffs, "Handoff quality gate", "Codex Handoffs must expose the quality gate."], [handoffs, "ProjectBrain-derived impact simulation, not a live git diff", "Codex Handoffs must show impact simulation boundary."], [handoffs, "Download ZIP", "Codex Handoffs must expose real session-pack ZIP export."], [handoffs, "Export session pack files", "Codex Handoffs must keep individual session-pack file export."], [handoffs, "controlled input for a Codex build session", "Codex Handoffs must describe session packs as controlled input."], [handoffs, "BuildLoopActionRail", "Codex Handoffs must show the shared build-loop action rail."], [handoffs, "ProjectRouteFallback", "Codex Handoffs must use the shared project fallback."], [sessionPacks, "A session pack is controlled input for a Codex build session", "Session Packs must show controlled input boundary."], [sessionPacks, "Session pack execution path", "Session Packs must expose the execution path."], [sessionPacks, "Download instructions/scripts ZIP", "Session Packs must expose clear instructions/scripts ZIP download."], [sessionPacks, "Export session pack files", "Session Packs must use honest export label."], [sessionPacks, "Create Codex handoff", "Session Packs empty state must link back to handoffs."], [sessionPacks, "Return to Build Loop", "Session Packs empty state must link back to Build Loop."], [sessionPacks, "ProjectRouteFallback", "Session Packs must use the shared project fallback."], [execution, "Run in Codex", "Execution Center must expose Run in Codex step language."], [execution, "ProjectRouteFallback", "Execution Center must use the shared project fallback."], [feedback, "Pasted Codex reports are user-provided execution evidence", "Execution Feedback must show user-provided evidence boundary."], [feedback, "Create follow-up handoff", "Execution Feedback must expose follow-up handoff action."], [feedback, "BuildLoopActionRail", "Execution Feedback must show the shared build-loop action rail."], [feedback, "ProjectRouteFallback", "Execution Feedback must use the shared project fallback."], [reviewQueue, "BuildLoopActionRail", "Review Queue must show the shared build-loop action rail."], [reviewQueue, "Close evidence into project state", "Review Queue must explain closure into project state."], [reviewQueue, "ProjectRouteFallback", "Review Queue must use the shared project fallback."], [pkg, "audit:visual-flow", "package.json must expose audit:visual-flow."], [pkg, "check-visual-flow-readiness.mjs", "package.json must wire visual flow readiness."], ]; for (const [haystack, needle, message] of mustContain) { if (!haystack.includes(needle)) failures.push(message); } for (const [file, contents] of Object.entries(sourceText)) { if (file.startsWith("src/") && contents.includes("Download Bundle / ZIP")) { failures.push(`${file} still contains misleading Download Bundle / ZIP copy.`); } if (file.startsWith("src/") && /existing (lint )?warnings remain/i.test(contents)) { failures.push(`${file} still contains stale existing-warnings copy.`); } if (file.startsWith("src/features/") && contents.includes("
Project not found
")) { failures.push(`${file} still contains a bare project-not-found fallback.`); } } const scripts = JSON.parse(pkg || "{}").scripts ?? {}; const auditAll = scripts["audit:all"] ?? ""; const experienceAudit = scripts["audit:experience"] ?? ""; if (!auditAll.includes("audit:experience") || !experienceAudit.includes("audit:visual-flow")) { failures.push("audit:all must include the experience gate with visual flow readiness."); } if (failures.length) { console.error("ProjectBrain visual flow readiness: FAIL"); for (const failure of failures) console.error(`- ${failure}`); process.exit(1); } console.log("ProjectBrain visual flow readiness: PASS");