163 lines
11 KiB
JavaScript
163 lines
11 KiB
JavaScript
#!/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("<div>Project not found</div>")) {
|
|
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");
|