#!/usr/bin/env node const assert = require("assert"); const fs = require("fs"); const path = require("path"); const repo = path.resolve(__dirname, ".."); const readme = fs.readFileSync(path.join(repo, "README.md"), "utf8"); const userFacingDocs = [ "README.md", "MVP.md", "acceptance_criteria.md", "acceptance_criteria_phase2.md", "cli_acceptance_criteria.md", ].map((file) => [file, fs.readFileSync(path.join(repo, file), "utf8")]); const publicAcceptance = fs.readFileSync( path.join(repo, "scripts/acceptance-public.sh"), "utf8" ); const publicSplit = fs.readFileSync( path.join(repo, "scripts/verify-public-split.sh"), "utf8" ); const privateAcceptance = fs.readFileSync( path.join(repo, "scripts/acceptance-private.sh"), "utf8" ); const requiredReadmePatterns = [ ["quickstart heading", /## Quickstart/], ["workspace build", /cargo build --workspace/], ["CLI install", /cargo install --path crates\/disasmer-cli --bin disasmer/], ["node install", /cargo install --path crates\/disasmer-node --bin disasmer-node/], [ "coordinator install", /cargo install --path crates\/disasmer-coordinator --bin disasmer-coordinator/, ], ["DAP install", /cargo install --path crates\/disasmer-dap --bin disasmer-debug-dap/], ["VS Code local extension", /code --extensionDevelopmentPath/], ["local coordinator", /disasmer-coordinator --listen/], ["node attach", /disasmer node attach --coordinator/], ["automatic local run", /disasmer run --local --project examples\/launch-build-demo build/], ["demo run", /disasmer run --local --coordinator/], ["entrypoint selection", /disasmer run \[entry\]/], ["implicit hosted mode", /uses the hosted coordinator/], ["local override", /force local coordinator mode/], ["project override", /--project[\s\S]*overrides the project\s+directory/], ["VS Code debug", /Disasmer: Launch\s+Virtual Process/], ["artifact download smoke", /node scripts\/artifact-download-smoke\.js/], ["artifact export smoke", /node scripts\/artifact-export-smoke\.js/], ["acceptance report smoke", /node scripts\/acceptance-report-smoke\.js/], ["CLI-first acceptance gate", /scripts\/acceptance-cli-first\.sh/], ["CLI-first acceptance before e2e", /CLI-first non-e2e gate before any final public-release e2e attempt/], ["public private boundary smoke", /node scripts\/public-private-boundary-smoke\.js/], ["release blocker smoke", /node scripts\/release-blocker-smoke\.js/], ["explicit export", /attached receiver node or user-provided\s+storage integration/], ["cleanup", /Cleanup for the local quickstart/], ["flush docs", /`flush\(\)` publishes metadata/], ["sync docs", /`sync\(\)` is explicit/], ["storage integration is user code", /User-provided storage\/export integrations are ordinary project code or external\s+commands/], ["no managed artifact store feature", /does not provide\s+or manage an explicit artifact-store feature/], ["best-effort retention", /best-effort retained on nodes/], [ "secure downloads", /Download links are scoped to the tenant, project, process, artifact, actor, and policy context, expire after a bounded TTL, can be revoked/, ], ["node trust", /Users attach their own nodes for real work/], ["self-hosted trusted teams", /self-hosted local clouds, trusted teams, or VPN deployments/], ["self-hosted coordinator smoke", /node scripts\/self-hosted-coordinator-smoke\.js/], ["wasmtime node smoke", /node scripts\/wasmtime-node-smoke\.js/], ["wasmtime command host import", /node `disasmer\.cmd_run` host import/], ["Windows sandbox limitation", /Production-grade managed Windows sandboxing is behind an explicit backend stub/], ["hosted community limit", /community tier does not provide arbitrary hosted native commands or hosted containers/], ["browser login", /disasmer login --browser/], ["public-key agents", /disasmer agent enroll --public-key/], ["noninteractive agent CLI", /DISASMER_AGENT_PUBLIC_KEY= disasmer run build/], ["agent key lifecycle", /register, list, rotate, and revoke an agent key/], ["capability auto-detect", /auto-detects OS, architecture/], ["capability override", /--cap /], ["non-Git source provider", /Non-Git source providers can implement the public source-provider interface/], ["first-run diagnostics", /## First-Run Diagnostics/], ["missing nodes diagnostic", /Missing nodes/], ["missing environment diagnostic", /Missing environments/], ["quota diagnostic", /Quota limits/], ["unavailable artifact diagnostic", /Unavailable artifacts/], ["auth diagnostic", /Auth failures/], ["debug freeze diagnostic", /Failed debug freezes/], ["source-provider diagnostic", /Source-provider capability gaps/], ["browser and VS Code report metadata", /browser\/VS Code harness metadata/], ["Podman incomplete report", /Linux Podman backend behavior is marked `incomplete`/], ["manual Windows validation workflow", /manual `Windows validation`\s+workflow/], ["intermittent Forgejo Windows runner", /intermittent Windows runner/], ["Windows validation env gate", /DISASMER_WINDOWS_VALIDATION = "forgejo-windows-runner"/], ["Windows validation attach", /runs `disasmer node attach`/], ["public dry-run operator endpoint", /https:\/\/disasmer\.michelpaulissen\.com:9443/], ["public dry-run DNS record", /record is live[\s\S]*no resolver override is required|DNS record is deployed[\s\S]*no resolver override is required/], ["public dry-run real deployment", /real externally reachable service/], ["public dry-run selected users", /shared with selected users/], ["public dry-run Forgejo repo", /git\.michelpaulissen\.com/], ["public dry-run Forgejo Release assets", /Forgejo Release publishes compiled\s+assets/], ["public dry-run filters root markdown", /root `\*\.md` files/], ["public dry-run filters Forgejo workflows by default", /\.forgejo\/\*\*` by\s+default/], ["public dry-run Forgejo workflow opt in", /--include-forgejo-workflows/], ["public dry-run selected-user quickstart", /DISASMER_PUBLIC_DRYRUN_GETTING_STARTED-\*\.md/], ["public dry-run selected-user invite", /DISASMER_PUBLIC_DRYRUN_INVITE-\*\.md/], ["public dry-run resolver instructions env", /DISASMER_PUBLIC_DRYRUN_RESOLVER_INSTRUCTIONS=/], ["public dry-run fallback hosts entry env", /DISASMER_PUBLIC_DRYRUN_HOSTS_ENTRY=" disasmer\.michelpaulissen\.com"/], ["public dry-run deployment IP env", /DISASMER_PUBLIC_RELEASE_DRYRUN_IP=/], ["public dry-run prep script", /node scripts\/prepare-public-release-dryrun\.js/], ["public dry-run publish opt-in", /DISASMER_PUBLISH_PUBLIC_TREE=1/], ["public dry-run public repo remote", /DISASMER_PUBLIC_REPO_REMOTE=ssh:\/\/git\.michelpaulissen\.com/], ["public dry-run Forgejo workflow", /Public release dry run assets/], ["public dry-run manifest", /public-release-manifest\.json/], ["public dry-run release publisher", /node scripts\/publish-public-release-dryrun\.js/], ["public dry-run non-e2e preflight", /node scripts\/public-release-dryrun-preflight\.js/], ["public dry-run Forgejo token", /DISASMER_FORGEJO_TOKEN=/], ["public dry-run publisher infers repo", /publisher infers the Forgejo owner and repository name/], ["public dry-run Forgejo repo owner override", /DISASMER_PUBLIC_REPO_OWNER=/], ["public dry-run Forgejo repo name override", /DISASMER_PUBLIC_REPO_NAME=/], ["public dry-run service smoke", /public-release-dryrun-service-smoke\.js/], ["public dry-run service address", /DISASMER_PUBLIC_RELEASE_DRYRUN_SERVICE_ADDR=disasmer\.michelpaulissen\.com:9443/], ["public dry-run private hosted coordinator", /default operator at `disasmer\.michelpaulissen\.com:9443` is the private hosted\s+coordinator from `private\/hosted-policy`/], ["public dry-run not standalone public coordinator", /does not mean deploying the standalone\s+open-source\/public coordinator as the hosted operator/], ["public dry-run validates both coordinators", /dry-run acceptance validates both coordinator implementations/], ["public dry-run validates public coordinator separately", /standalone public\/open-source\s+coordinator is validated separately/], ["public browser login site", /disasmer\.michelpaulissen\.com\/auth\/browser\/start/], ["public browser login local callback", /local callback/], ["public dry-run barebones HTML no CSS", /barebones HTML with\s+no CSS/], ["browser login plan diagnostic", /disasmer login --browser --plan/], ["public dry-run deployment prep", /prepare-public-release-dryrun-deployment\.js/], ["public dry-run systemd unit", /systemd unit that\s+binds `0\.0\.0\.0:9443`/], ["public dry-run e2e runner", /public-release-dryrun-e2e\.js/], ["public dry-run e2e gate env", /DISASMER_PUBLIC_RELEASE_DRYRUN_E2E=1/], ["public dry-run e2e service address", /DISASMER_PUBLIC_RELEASE_DRYRUN_SERVICE_ADDR=disasmer\.michelpaulissen\.com:9443/], ["public dry-run final verifier", /public-release-dryrun-final-evidence\.js/], ["public dry-run final gate env", /DISASMER_PUBLIC_RELEASE_DRYRUN_FINAL=1/], ["public dry-run e2e evidence", /public-release-dryrun-e2e\.json/], ["public dry-run e2e checks", /downloaded release assets[\s\S]*VS Code debugger behavior[\s\S]*artifact metadata/], ["public operator compatibility smoke", /public-operator-compat-smoke\.js/], ["public operator compatibility purpose", /public CLI and public\s+node runtime can attach, launch work through coordinator task assignment, and\s+publish debug\/log\/artifact metadata/], ]; for (const [name, pattern] of requiredReadmePatterns) { assert.match(readme, pattern, `README missing ${name}`); } for (const [file, contents] of userFacingDocs) { assert.doesNotMatch( contents, /community-tier/i, `${file} should use "community tier" in user-facing prose` ); } for (const script of [publicAcceptance, publicSplit]) { assert( script.includes("node scripts/docs-smoke.js"), "public acceptance gates must run docs-smoke.js" ); assert( script.includes("node scripts/cli-install-smoke.js"), "public acceptance gates must run cli-install-smoke.js" ); assert( script.includes("node scripts/cli-output-mode-smoke.js"), "public acceptance gates must run cli-output-mode-smoke.js" ); assert( script.includes("node scripts/cli-login-smoke.js"), "public acceptance gates must run cli-login-smoke.js" ); assert( script.includes("node scripts/cli-error-exit-smoke.js"), "public acceptance gates must run cli-error-exit-smoke.js" ); assert( script.includes("node scripts/acceptance-report-smoke.js"), "public acceptance gates must run acceptance-report-smoke.js" ); assert( script.includes("node scripts/acceptance-doc-contract-smoke.js"), "public acceptance gates must run acceptance-doc-contract-smoke.js" ); assert( script.includes("node scripts/acceptance-environment-contract-smoke.js"), "public acceptance gates must run acceptance-environment-contract-smoke.js" ); assert( script.includes("node scripts/acceptance-evidence-contract-smoke.js"), "public acceptance gates must run acceptance-evidence-contract-smoke.js" ); assert( script.includes("node scripts/public-private-boundary-smoke.js"), "public acceptance gates must run public-private-boundary-smoke.js" ); assert( script.includes("node scripts/release-blocker-smoke.js"), "public acceptance gates must run release-blocker-smoke.js" ); assert( script.includes("node scripts/resource-metering-contract-smoke.js"), "public acceptance gates must run resource-metering-contract-smoke.js" ); assert( script.includes("node scripts/hostile-input-contract-smoke.js"), "public acceptance gates must run hostile-input-contract-smoke.js" ); assert( script.includes("node scripts/tenant-isolation-contract-smoke.js"), "public acceptance gates must run tenant-isolation-contract-smoke.js" ); assert( script.includes("node scripts/public-story-contract-smoke.js"), "public acceptance gates must run public-story-contract-smoke.js" ); assert( script.includes("node scripts/public-release-dryrun-contract-smoke.js"), "public acceptance gates must run public-release-dryrun-contract-smoke.js" ); assert( script.includes("node scripts/prepare-public-release-dryrun.js"), "public acceptance gates must run prepare-public-release-dryrun.js" ); assert( script.includes("node scripts/self-hosted-coordinator-smoke.js"), "public acceptance gates must run self-hosted-coordinator-smoke.js" ); assert( script.includes("node scripts/public-local-demo-matrix-smoke.js"), "public acceptance gates must run public-local-demo-matrix-smoke.js" ); assert( script.includes("scripts/release-source-scan.sh"), "public acceptance gates must run release-source-scan.sh" ); assert( script.includes("node scripts/flagship-demo-smoke.js"), "public acceptance gates must run flagship-demo-smoke.js" ); assert( script.includes("node scripts/cancellation-smoke.js"), "public acceptance gates must run cancellation-smoke.js" ); assert( script.includes("node scripts/sdk-spawn-runtime-smoke.js"), "public acceptance gates must run sdk-spawn-runtime-smoke.js" ); assert( script.includes("node scripts/node-lifecycle-contract-smoke.js"), "public acceptance gates must run node-lifecycle-contract-smoke.js" ); assert( script.includes("node scripts/artifact-export-smoke.js"), "public acceptance gates must run artifact-export-smoke.js" ); assert( script.includes("node scripts/windows-validation-contract-smoke.js"), "public acceptance gates must run windows-validation-contract-smoke.js" ); } assert( publicAcceptance.includes("node scripts/podman-backend-smoke.js"), "public acceptance must run the Linux Podman backend smoke when Podman is available" ); assert( publicAcceptance.includes("node scripts/wasmtime-node-smoke.js"), "public acceptance must run the Wasmtime node smoke" ); assert( privateAcceptance.includes("node scripts/resource-metering-contract-smoke.js"), "private acceptance must run resource-metering-contract-smoke.js" ); assert( privateAcceptance.includes("node scripts/hostile-input-contract-smoke.js"), "private acceptance must run hostile-input-contract-smoke.js" ); assert( privateAcceptance.includes("node scripts/tenant-isolation-contract-smoke.js"), "private acceptance must run tenant-isolation-contract-smoke.js" ); assert( privateAcceptance.includes("node scripts/acceptance-doc-contract-smoke.js"), "private acceptance must run acceptance-doc-contract-smoke.js" ); assert( privateAcceptance.includes("node scripts/acceptance-environment-contract-smoke.js"), "private acceptance must run acceptance-environment-contract-smoke.js" ); assert( privateAcceptance.includes("node scripts/acceptance-evidence-contract-smoke.js"), "private acceptance must run acceptance-evidence-contract-smoke.js" ); assert( privateAcceptance.includes("node private/hosted-policy/scripts/hosted-deployment-smoke.js"), "private acceptance must run hosted-deployment-smoke.js" ); assert( privateAcceptance.includes("node private/hosted-policy/scripts/public-operator-compat-smoke.js"), "private acceptance must run public-operator-compat-smoke.js" ); assert( privateAcceptance.includes("node scripts/self-hosted-coordinator-smoke.js"), "private acceptance must run self-hosted-coordinator-smoke.js before final dry-run evidence" ); assert( privateAcceptance.includes("node private/hosted-policy/scripts/prepare-public-release-dryrun-deployment.js"), "private acceptance must prepare public release dry-run deployment bundle" ); assert( privateAcceptance.includes("node private/hosted-policy/scripts/public-release-dryrun-service-smoke.js"), "private acceptance must be able to run public-release-dryrun-service-smoke.js" ); for (const script of [publicAcceptance, privateAcceptance]) { assert( script.includes("node scripts/public-release-dryrun-final-evidence.js"), "acceptance must be able to run public-release-dryrun-final-evidence.js" ); assert( script.includes("DISASMER_PUBLIC_RELEASE_DRYRUN_FINAL"), "acceptance must gate public-release-dryrun-final-evidence.js" ); } assert( publicAcceptance.includes("node scripts/public-release-dryrun-e2e.js"), "public acceptance must be able to run public-release-dryrun-e2e.js" ); assert( publicAcceptance.includes("DISASMER_PUBLIC_RELEASE_DRYRUN_E2E"), "public acceptance must gate public-release-dryrun-e2e.js" ); console.log("Docs smoke passed");