From eabea2c712c317ce056e138f4552479cb55df771 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:16 +0800 Subject: [PATCH 01/29] fix(cloud): harden runtime setup and dependency proof --- scripts/setup-codex-cloud.sh | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/scripts/setup-codex-cloud.sh b/scripts/setup-codex-cloud.sh index b4d6a90627..5eb5816c80 100644 --- a/scripts/setup-codex-cloud.sh +++ b/scripts/setup-codex-cloud.sh @@ -35,7 +35,7 @@ cd "$repo_root" expected_node_major="$(tr -cd '0-9' < .node-version)" expected_npm_version="$(sed -n 's/.*"packageManager"[[:space:]]*:[[:space:]]*"npm@\([^"]*\)".*/\1/p' package.json | head -n 1)" -railway_cli_version="5.30.1" +railway_cli_version="5.30.4" codex_cli_version="0.146.0" expected_cloud_python="3.12" [[ -n "$expected_node_major" ]] || fail "Could not read the Node major from .node-version." @@ -91,11 +91,9 @@ esac # these variables, so a runtime `${RAG_PROVIDER_MODE:-auto}` fallback would # override a connected environment configured for offline retrieval. if [[ "$access_profile" = "connected" ]]; then - rag_provider_mode="${RAG_PROVIDER_MODE:-auto}" - case "$rag_provider_mode" in - auto|openai|offline) ;; - *) fail "Unsupported RAG_PROVIDER_MODE: $rag_provider_mode" ;; - esac + rag_provider_mode="${RAG_PROVIDER_MODE:-offline}" + [[ "$rag_provider_mode" = "offline" ]] || + fail "Ordinary Codex Cloud must keep RAG_PROVIDER_MODE=offline; run live OpenAI checks only in the protected provider workflow." else rag_provider_mode="offline" fi @@ -153,7 +151,7 @@ codex_shell_policy_excludes=( SUPABASE_ACCESS_TOKEN SUPABASE_SERVICE_ROLE_KEY SUPABASE_DB_URL DATABASE_URL POSTGRES_PASSWORD CROSS_TENANT_SERVICE_ROLE_KEY RAILWAY_API_TOKEN RAILWAY_TOKEN - GH_TOKEN GITHUB_TOKEN GITLAB_TOKEN GLAB_TOKEN CODEX_TRIGGER_TOKEN + GH_TOKEN GITHUB_TOKEN GITLAB_TOKEN GLAB_TOKEN CODEX_TRIGGER_TOKEN CODEX_CLOUD_GITHUB_PAT HEALTH_DEEP_PROBE_SECRET INDEXING_V3_AGENT_SECRET E2E_AUTH_ENABLED E2E_USER_EMAIL E2E_USER_PASSWORD ALLOW_PROVIDER_TESTS ) @@ -232,11 +230,15 @@ log "Installing locked Node dependencies." setup_step="node-dependencies" npm ci --include=dev +setup_step="railway-cli" install_npm_cli "@railway/cli" "$railway_cli_version" "railway" +setup_step="codex-cli" install_npm_cli "@openai/codex" "$codex_cli_version" "codex" +setup_step="git-remote" node scripts/ensure-codex-cloud-git-remote.mjs --configure-gh-helper +setup_step="deno-runtime" if ! command -v deno >/dev/null 2>&1 || [[ "$(deno --version 2>/dev/null | sed -n '1s/^deno \([0-9]*\).*/\1/p')" != "2" ]]; then log "Installing Deno 2.x." npm install --global 'deno@2' @@ -283,6 +285,11 @@ log "Installing Python worker requirements." setup_step="python-worker-requirements" "$ocr_venv/bin/python" -m pip install --disable-pip-version-check --require-hashes -r worker/python/requirements-cloud.txt "$ocr_venv/bin/python" -m pip check +requirements_marker="$ocr_venv/.requirements-cloud.sha256" +requirements_marker_candidate="${requirements_marker}.tmp" +sha256sum worker/python/requirements-cloud.txt | awk '{print $1}' > "$requirements_marker_candidate" +mv -f "$requirements_marker_candidate" "$requirements_marker" +"$ocr_venv/bin/python" -c 'from importlib.metadata import version; print("medspacy=%s spacy=%s" % (version("medspacy"), version("spacy")))' export CODEX_CLOUD_OCR_PYTHON="$ocr_venv/bin/python" if [[ "${CODEX_CLOUD_SKIP_BROWSER_INSTALL:-0}" = "1" ]]; then From 6c7670f409a5434acc6c252ef7e3213b1e52033d Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:17 +0800 Subject: [PATCH 02/29] fix(cloud): prove browser Python and checkout readiness --- scripts/check-codex-cloud-setup.mjs | 636 ++++++++++++++++++++++------ 1 file changed, 510 insertions(+), 126 deletions(-) diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index feb0c93a41..a039a3cd00 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node import { accessSync, constants, readFileSync, statSync } from "node:fs"; +import { createHash } from "node:crypto"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import path from "node:path"; @@ -12,10 +13,13 @@ import { } from "./ensure-codex-cloud-git-remote.mjs"; import { providerEnvironmentKeys } from "./test-environment.mjs"; -const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const repoRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "..", +); export const expectedCloudCliVersions = Object.freeze({ - railway: "5.30.1", + railway: "5.30.4", codex: "0.146.0", }); @@ -24,22 +28,35 @@ export const expectedMcpConfiguration = Object.freeze({ railwayUrl: "https://mcp.railway.com", supabaseUrl: "https://mcp.supabase.com/mcp", supabaseProjectRef: "sjrfecxgysukkwxsowpy", - supabaseFeatures: Object.freeze(["database", "debugging", "development", "docs"]), + supabaseFeatures: Object.freeze(["development", "docs"]), }); /** Project `.codex/config.toml` registrations — disabled by default, secret-free URLs only. */ export const expectedCodexProjectMcpServers = Object.freeze({ - figma_cloud: Object.freeze({ url: "https://mcp.figma.com/mcp", approvalMode: "writes" }), - railway_cloud: Object.freeze({ url: expectedMcpConfiguration.railwayUrl, approvalMode: "writes" }), - sentry_cloud: Object.freeze({ url: "https://mcp.sentry.dev/mcp", approvalMode: "writes" }), + figma_cloud: Object.freeze({ + url: "https://mcp.figma.com/mcp", + approvalMode: "writes", + }), + railway_cloud: Object.freeze({ + url: expectedMcpConfiguration.railwayUrl, + approvalMode: "writes", + }), + sentry_cloud: Object.freeze({ + url: "https://mcp.sentry.dev/mcp", + approvalMode: "writes", + }), supabase_cloud: Object.freeze({ // URL validated with the same project/read-only/feature rules as `.mcp.json`. kind: "supabase", - approvalMode: "auto", + approvalMode: "prompt", }), }); -const allowedCodexProjectMcpKeys = Object.freeze(["default_tools_approval_mode", "enabled", "url"]); +const allowedCodexProjectMcpKeys = Object.freeze([ + "default_tools_approval_mode", + "enabled", + "url", +]); const forbiddenCodexProjectMcpKeys = Object.freeze([ "bearer_token_env_var", @@ -55,7 +72,10 @@ function parseTomlScalar(value) { const trimmed = value.trim(); if (trimmed === "true") return true; if (trimmed === "false") return false; - if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { + if ( + (trimmed.startsWith('"') && trimmed.endsWith('"')) || + (trimmed.startsWith("'") && trimmed.endsWith("'")) + ) { return trimmed.slice(1, -1); } return trimmed; @@ -97,7 +117,9 @@ export function parseCodexProjectMcpServers(text) { continue; } if (!current) { - const dotted = line.match(/^mcp_servers\.([A-Za-z0-9_-]+)\.([A-Za-z0-9_.-]+)\s*=\s*(.+)$/); + const dotted = line.match( + /^mcp_servers\.([A-Za-z0-9_-]+)\.([A-Za-z0-9_.-]+)\s*=\s*(.+)$/, + ); if (dotted) { servers[dotted[1]] ??= {}; servers[dotted[1]][dotted[2]] = parseTomlScalar(dotted[3]); @@ -118,22 +140,38 @@ export function parseCodexProjectMcpServers(text) { function validateSupabaseMcpUrl(urlString, label, errors) { try { const url = new URL(urlString); - if (`${url.origin}${url.pathname}` !== expectedMcpConfiguration.supabaseUrl) { + if ( + `${url.origin}${url.pathname}` !== expectedMcpConfiguration.supabaseUrl + ) { errors.push(`${label} must use the official hosted endpoint.`); } - if (url.searchParams.get("project_ref") !== expectedMcpConfiguration.supabaseProjectRef) { + if ( + url.searchParams.get("project_ref") !== + expectedMcpConfiguration.supabaseProjectRef + ) { errors.push(`${label} must be scoped to the expected project.`); } if (url.searchParams.get("read_only") !== "true") { errors.push(`${label} must keep the production project read-only.`); } const queryNames = [...url.searchParams.keys()].sort(); - if (JSON.stringify(queryNames) !== JSON.stringify(["features", "project_ref", "read_only"])) { + if ( + JSON.stringify(queryNames) !== + JSON.stringify(["features", "project_ref", "read_only"]) + ) { errors.push(`${label} must not include additional query parameters.`); } - const features = (url.searchParams.get("features") ?? "").split(",").filter(Boolean).sort(); - if (JSON.stringify(features) !== JSON.stringify(expectedMcpConfiguration.supabaseFeatures)) { - errors.push(`${label} must expose only the approved read-only feature groups.`); + const features = (url.searchParams.get("features") ?? "") + .split(",") + .filter(Boolean) + .sort(); + if ( + JSON.stringify(features) !== + JSON.stringify(expectedMcpConfiguration.supabaseFeatures) + ) { + errors.push( + `${label} must expose only the approved read-only feature groups.`, + ); } } catch { errors.push(`${label} URL must be valid.`); @@ -148,11 +186,14 @@ function validateSupabaseMcpUrl(urlString, label, errors) { */ export function validateCodexProjectMcpConfiguration(text) { const errors = []; - const { servers, nestedServers, unparsedServers } = parseCodexProjectMcpServers(text); + const { servers, nestedServers, unparsedServers } = + parseCodexProjectMcpServers(text); const expectedNames = Object.keys(expectedCodexProjectMcpServers).sort(); const actualNames = Object.keys(servers).sort(); if (JSON.stringify(actualNames) !== JSON.stringify(expectedNames)) { - errors.push(`.codex/config.toml must register exactly these MCP servers: ${expectedNames.join(", ")}.`); + errors.push( + `.codex/config.toml must register exactly these MCP servers: ${expectedNames.join(", ")}.`, + ); } for (const name of expectedNames) { @@ -162,25 +203,33 @@ export function validateCodexProjectMcpConfiguration(text) { const expected = expectedCodexProjectMcpServers[name]; if (server.enabled !== false) { - errors.push(`${label} must set enabled = false (host/connected layers opt in).`); + errors.push( + `${label} must set enabled = false (host/connected layers opt in).`, + ); } if (server.default_tools_approval_mode !== expected.approvalMode) { const reason = expected.approvalMode === "writes" ? "write-capable tools require explicit approval" : "the production server is constrained read-only"; - errors.push(`${label} must set default_tools_approval_mode = "${expected.approvalMode}" because ${reason}.`); + errors.push( + `${label} must set default_tools_approval_mode = "${expected.approvalMode}" because ${reason}.`, + ); } for (const key of Object.keys(server)) { const rootKey = key.split(".")[0]; if (forbiddenCodexProjectMcpKeys.includes(rootKey)) { - errors.push(`${label} must not embed ${rootKey}; keep OAuth credentials in the host store.`); + errors.push( + `${label} must not embed ${rootKey}; keep OAuth credentials in the host store.`, + ); } else if (!allowedCodexProjectMcpKeys.includes(key)) { errors.push(`${label} must be URL-only; unsupported key ${key}.`); } } if (nestedServers.has(name)) { - errors.push(`${label} must not declare nested tool override tables in the shared project config.`); + errors.push( + `${label} must not declare nested tool override tables in the shared project config.`, + ); } if (unparsedServers.has(name)) { errors.push(`${label} contains unsupported or unparsed entries.`); @@ -232,9 +281,11 @@ function exactVersionPattern(version) { * @returns {string[]} */ export function obsoleteNpmProxyVariables(env = process.env) { - return ["npm_config_http_proxy", "npm_config_https_proxy", "npm_config_proxy"].filter( - (name) => Object.hasOwn(env, name) && Boolean(env[name]), - ); + return [ + "npm_config_http_proxy", + "npm_config_https_proxy", + "npm_config_proxy", + ].filter((name) => Object.hasOwn(env, name) && Boolean(env[name])); } /** @@ -242,13 +293,16 @@ export function obsoleteNpmProxyVariables(env = process.env) { * @returns {string[]} */ export function configuredProviderCredentialNames(env = process.env) { - return providerCredentialVariables.filter((name) => Object.hasOwn(env, name) && Boolean(env[name])); + return providerCredentialVariables.filter( + (name) => Object.hasOwn(env, name) && Boolean(env[name]), + ); } /** @param {NodeJS.ProcessEnv | Record} [env] */ export function validateCodexCloudEnvironment(env = process.env) { const errors = []; - if (env.CODEX_CLOUD !== "1") errors.push("CODEX_CLOUD must be 1 in the Cloud agent shell."); + if (env.CODEX_CLOUD !== "1") + errors.push("CODEX_CLOUD must be 1 in the Cloud agent shell."); const accessProfile = env.CODEX_CLOUD_ACCESS_PROFILE ?? "offline"; if (!["offline", "connected"].includes(accessProfile)) { @@ -269,17 +323,19 @@ export function validateCodexCloudEnvironment(env = process.env) { NEXT_PUBLIC_DEMO_MODE: "true", PLAYWRIGHT_OFFLINE_MODE: "true", })) { - if (env[name] !== expected) errors.push(`${name} must be ${expected} in offline mode.`); + if (env[name] !== expected) + errors.push(`${name} must be ${expected} in offline mode.`); } return errors; } for (const [name, allowed] of Object.entries({ - RAG_PROVIDER_MODE: ["auto", "openai", "offline"], + RAG_PROVIDER_MODE: ["offline"], NEXT_PUBLIC_DEMO_MODE: ["true", "false"], PLAYWRIGHT_OFFLINE_MODE: ["true", "false"], })) { - if (!allowed.includes(env[name])) errors.push(`${name} must be an approved value in connected mode.`); + if (!allowed.includes(env[name])) + errors.push(`${name} must be an approved value in connected mode.`); } return errors; } @@ -290,7 +346,7 @@ export function railwayReadCapability(env = process.env, cliAvailable = false) { cliAvailable, dedicatedCredentialPresent: Boolean(env.RAILWAY_API_TOKEN), projectCredentialPresent: Boolean(env.RAILWAY_TOKEN), - ready: cliAvailable && Boolean(env.RAILWAY_API_TOKEN), + cliTokenAuthReady: cliAvailable && Boolean(env.RAILWAY_API_TOKEN), }; } @@ -310,12 +366,19 @@ export function parseMcpServerMetadata(text) { } return { name, - type: typeof server?.type === "string" ? server.type : typeof server?.command === "string" ? "stdio" : "invalid", + type: + typeof server?.type === "string" + ? server.type + : typeof server?.command === "string" + ? "stdio" + : "invalid", command: typeof server?.command === "string" ? server.command : "none", endpoint, queryNames, environmentNames: - server?.env && !Array.isArray(server.env) && typeof server.env === "object" + server?.env && + !Array.isArray(server.env) && + typeof server.env === "object" ? Object.keys(server.env).sort() : [], }; @@ -328,7 +391,9 @@ export function validateMcpConfiguration(text) { try { parsed = JSON.parse(text); } catch (error) { - return [`.mcp.json is invalid JSON: ${error instanceof Error ? error.message : String(error)}`]; + return [ + `.mcp.json is invalid JSON: ${error instanceof Error ? error.message : String(error)}`, + ]; } const servers = parsed?.mcpServers; if (!servers || Array.isArray(servers) || typeof servers !== "object") { @@ -337,16 +402,26 @@ export function validateMcpConfiguration(text) { const serverNames = Object.keys(servers).sort(); if (JSON.stringify(serverNames) !== JSON.stringify(["railway", "supabase"])) { - errors.push("Cloud MCP configuration must contain only Railway and Supabase."); + errors.push( + "Cloud MCP configuration must contain only Railway and Supabase.", + ); } for (const name of ["railway", "supabase"]) { - if (servers[name]?.env !== undefined || servers[name]?.headers !== undefined) { - errors.push(`${name} MCP must use hosted OAuth without embedded environment variables or headers.`); + if ( + servers[name]?.env !== undefined || + servers[name]?.headers !== undefined + ) { + errors.push( + `${name} MCP must use hosted OAuth without embedded environment variables or headers.`, + ); } } const railway = servers.railway; - if (railway?.type !== "http" || railway?.url !== expectedMcpConfiguration.railwayUrl.replace(/\/$/, "")) { + if ( + railway?.type !== "http" || + railway?.url !== expectedMcpConfiguration.railwayUrl.replace(/\/$/, "") + ) { errors.push("Railway MCP must use the hosted OAuth endpoint."); } @@ -357,22 +432,38 @@ export function validateMcpConfiguration(text) { } try { const url = new URL(supabase.url); - if (`${url.origin}${url.pathname}` !== expectedMcpConfiguration.supabaseUrl) { + if ( + `${url.origin}${url.pathname}` !== expectedMcpConfiguration.supabaseUrl + ) { errors.push("Supabase MCP must use the official hosted endpoint."); } - if (url.searchParams.get("project_ref") !== expectedMcpConfiguration.supabaseProjectRef) { + if ( + url.searchParams.get("project_ref") !== + expectedMcpConfiguration.supabaseProjectRef + ) { errors.push("Supabase MCP must be scoped to the expected project."); } if (url.searchParams.get("read_only") !== "true") { errors.push("Supabase MCP must keep the production project read-only."); } const queryNames = [...url.searchParams.keys()].sort(); - if (JSON.stringify(queryNames) !== JSON.stringify(["features", "project_ref", "read_only"])) { + if ( + JSON.stringify(queryNames) !== + JSON.stringify(["features", "project_ref", "read_only"]) + ) { errors.push("Supabase MCP must not include additional query parameters."); } - const features = (url.searchParams.get("features") ?? "").split(",").filter(Boolean).sort(); - if (JSON.stringify(features) !== JSON.stringify(expectedMcpConfiguration.supabaseFeatures)) { - errors.push("Supabase MCP must expose only the approved read-only feature groups."); + const features = (url.searchParams.get("features") ?? "") + .split(",") + .filter(Boolean) + .sort(); + if ( + JSON.stringify(features) !== + JSON.stringify(expectedMcpConfiguration.supabaseFeatures) + ) { + errors.push( + "Supabase MCP must expose only the approved read-only feature groups.", + ); } } catch { errors.push("Supabase MCP URL must be valid."); @@ -384,7 +475,11 @@ function approvedModeValue(value, allowed) { return allowed.includes(value) ? value : "invalid"; } -export function codexCloudValidationScope({ runtime = false, environment = false, browserInstallSkipped = false }) { +export function codexCloudValidationScope({ + runtime = false, + environment = false, + browserInstallSkipped = false, +}) { if (runtime && browserInstallSkipped) { return "static, environment, and source-only runtime (browser validation skipped)"; } @@ -394,16 +489,25 @@ export function codexCloudValidationScope({ runtime = false, environment = false } function commandAvailable(command) { - return spawnSync(command, ["--version"], { encoding: "utf8", shell: false }).status === 0; + return ( + spawnSync(command, ["--version"], { encoding: "utf8", shell: false }) + .status === 0 + ); } /** @param {NodeJS.ProcessEnv | Record} [env] */ export function sanitizedCloudCapabilityLines(env = process.env, options = {}) { const origin = options.origin ?? inspectOriginRemote(repoRoot); - const railway = railwayReadCapability(env, options.railwayCliAvailable ?? commandAvailable("railway")); - const codexCliAvailable = options.codexCliAvailable ?? commandAvailable("codex"); - const safeGitHelper = options.safeGitHelper ?? hasSafeGitHubCredentialHelper(repoRoot); - const mcpServers = options.mcpServers ?? parseMcpServerMetadata(read(".mcp.json")); + const railway = railwayReadCapability( + env, + options.railwayCliAvailable ?? commandAvailable("railway"), + ); + const codexCliAvailable = + options.codexCliAvailable ?? commandAvailable("codex"); + const safeGitHelper = + options.safeGitHelper ?? hasSafeGitHubCredentialHelper(repoRoot); + const mcpServers = + options.mcpServers ?? parseMcpServerMetadata(read(".mcp.json")); const lines = [ `CODEX_CLOUD=${approvedModeValue(env.CODEX_CLOUD, ["1"])}`, `CODEX_CLOUD_ACCESS_PROFILE=${approvedModeValue(env.CODEX_CLOUD_ACCESS_PROFILE ?? "offline", ["offline", "connected"])}`, @@ -411,12 +515,21 @@ export function sanitizedCloudCapabilityLines(env = process.env, options = {}) { `NEXT_PUBLIC_DEMO_MODE=${approvedModeValue(env.NEXT_PUBLIC_DEMO_MODE, ["true", "false"])}`, `PLAYWRIGHT_OFFLINE_MODE=${approvedModeValue(env.PLAYWRIGHT_OFFLINE_MODE, ["true", "false"])}`, ]; - for (const name of providerCredentialVariables) lines.push(`${name}.present=${Boolean(env[name])}`); + for (const name of providerCredentialVariables) + lines.push(`${name}.present=${Boolean(env[name])}`); lines.push(`railway.cli_available=${railway.cliAvailable}`); - lines.push(`railway.dedicated_credential_present=${railway.dedicatedCredentialPresent}`); - lines.push(`railway.project_credential_present=${railway.projectCredentialPresent}`); - lines.push(`railway.read_commands_ready=${railway.ready}`); + lines.push( + `railway.dedicated_credential_present=${railway.dedicatedCredentialPresent}`, + ); + lines.push( + `railway.project_credential_present=${railway.projectCredentialPresent}`, + ); + lines.push(`railway.cli_token_auth_ready=${railway.cliTokenAuthReady}`); + lines.push( + "mcp.runtime_tool_inventory=host-provided-unverified-by-repository", + ); lines.push(`codex.cli_available=${codexCliAvailable}`); + lines.push(pythonWorkerVersionLine(env.CODEX_CLOUD_OCR_PYTHON)); lines.push(`git.origin_configured=${origin.configured}`); lines.push(`git.origin_repository_match=${origin.repositoryMatch}`); lines.push(`git.origin_credential_embedded=${origin.credentialsEmbedded}`); @@ -439,10 +552,14 @@ export function localGitBaseline(root = process.cwd(), env = process.env) { if (result.status === 0) return ref; } if (env.CODEX_CLOUD === "1") { - const result = spawnSync("git", ["rev-parse", "--verify", "--quiet", "HEAD"], { - cwd: root, - stdio: "ignore", - }); + const result = spawnSync( + "git", + ["rev-parse", "--verify", "--quiet", "HEAD"], + { + cwd: root, + stdio: "ignore", + }, + ); if (result.status === 0) return "HEAD"; } return null; @@ -450,12 +567,36 @@ export function localGitBaseline(root = process.cwd(), env = process.env) { export function executableFile(filePath) { try { - return statSync(filePath).isFile() && (accessSync(filePath, constants.X_OK), true); + return ( + statSync(filePath).isFile() && + (accessSync(filePath, constants.X_OK), true) + ); } catch { return false; } } +export async function playwrightBrowserErrors(browserTypes, timeout = 15_000) { + const errors = []; + for (const [name, browserType] of Object.entries(browserTypes)) { + if (!executableFile(browserType.executablePath())) { + errors.push(`${name} browser executable is unavailable.`); + continue; + } + let browser; + try { + browser = await browserType.launch({ headless: true, timeout }); + } catch (error) { + errors.push( + `${name} browser launch failed: ${error instanceof Error ? error.message : String(error)}`, + ); + } finally { + await browser?.close(); + } + } + return errors; +} + export const pythonWorkerImports = ["fitz", "PIL", "pytesseract", "medspacy"]; /** @@ -471,12 +612,84 @@ export function pythonWorkerImportError(pythonCommand, run = spawnSync) { if (!pythonCommand || !executableFile(pythonCommand)) { return "The configured Codex Cloud OCR Python executable is unavailable."; } - const result = run(pythonCommand, ["-c", `import ${pythonWorkerImports.join(", ")}`], { + const result = run( + pythonCommand, + ["-c", `import ${pythonWorkerImports.join(", ")}`], + { + encoding: "utf8", + shell: false, + }, + ); + if (result.status === 0) return null; + return `Python worker imports failed: ${pythonWorkerImports.join(", ")}.`; +} + +export function pythonWorkerDependencyErrors(pythonCommand, run = spawnSync) { + if (!pythonCommand || !executableFile(pythonCommand)) { + return ["The configured Codex Cloud OCR Python executable is unavailable."]; + } + const errors = []; + const pipCheck = run(pythonCommand, ["-m", "pip", "check"], { encoding: "utf8", shell: false, }); - if (result.status === 0) return null; - return `Python worker imports failed: ${pythonWorkerImports.join(", ")}.`; + if (pipCheck.status !== 0) + errors.push( + "Python worker dependency conflicts were reported by pip check.", + ); + + const requirements = readFileSync( + path.join(repoRoot, "worker/python/requirements-cloud.txt"), + ); + const expectedHash = createHash("sha256").update(requirements).digest("hex"); + const markerPath = path.resolve( + path.dirname(pythonCommand), + "..", + ".requirements-cloud.sha256", + ); + let installedHash = ""; + try { + installedHash = readFileSync(markerPath, "utf8").trim(); + } catch { + errors.push( + "Python worker requirements fingerprint is missing; rerun Cloud setup.", + ); + } + if (installedHash && installedHash !== expectedHash) { + errors.push( + "Python worker requirements fingerprint is stale; rerun Cloud setup.", + ); + } + + const versions = run( + pythonCommand, + [ + "-c", + "from importlib.metadata import version; print('medspacy=%s spacy=%s' % (version('medspacy'), version('spacy')))", + ], + { encoding: "utf8", shell: false }, + ); + if (versions.status !== 0) + errors.push("Python worker medspacy/spacy version reporting failed."); + return errors; +} + +export function pythonWorkerVersionLine(pythonCommand, run = spawnSync) { + if (!pythonCommand || !executableFile(pythonCommand)) + return "python.worker_versions=unavailable"; + const result = run( + pythonCommand, + [ + "-c", + "from importlib.metadata import version; print('medspacy=%s spacy=%s' % (version('medspacy'), version('spacy')))", + ], + { encoding: "utf8", shell: false }, + ); + return result.status === 0 + ? `python.worker_versions=${String(result.stdout ?? "") + .trim() + .replaceAll(/\\s+/g, ",")}` + : "python.worker_versions=unavailable"; } export function validateCodexCloudSetup() { @@ -487,6 +700,7 @@ export function validateCodexCloudSetup() { const setup = read("scripts/setup-codex-cloud.sh"); const maintenance = read("scripts/maintain-codex-cloud.sh"); const commandShims = read("scripts/install-codex-cloud-command-shims.sh"); + const rawEnvironmentProbe = read("scripts/check-codex-cloud-raw-env.sh"); const patDelete = read("scripts/delete-codex-cloud-branch-with-pat.sh"); const guide = read("docs/codex-cloud.md"); const agents = read("AGENTS.md"); @@ -496,29 +710,65 @@ export function validateCodexCloudSetup() { const codexProjectConfig = read(".codex/config.toml"); if (packageJson.engines?.node !== `${nodeVersion}.x`) { - errors.push(`package.json engines.node must match .node-version (${nodeVersion}.x).`); + errors.push( + `package.json engines.node must match .node-version (${nodeVersion}.x).`, + ); } - if (packageJson.engines?.npm !== "11.x") errors.push("package.json must require npm 11.x."); + if (packageJson.engines?.npm !== "11.x") + errors.push("package.json must require npm 11.x."); if (!String(packageJson.packageManager ?? "").startsWith("npm@11.")) { errors.push("package.json packageManager must pin npm 11.x."); } - if (nvmVersion !== nodeVersion) errors.push(".nvmrc and .node-version must match."); - requireMatch(errors, gitignore, /^\/error\.log$/m, "Codex Cloud diagnostic error.log must stay ignored."); + if (nvmVersion !== nodeVersion) + errors.push(".nvmrc and .node-version must match."); + requireMatch( + errors, + gitignore, + /^\/error\.log$/m, + "Codex Cloud diagnostic error.log must stay ignored.", + ); for (const [pattern, message] of [ - [/npm ci --include=dev/, "Cloud setup must install the exact lockfile with dev dependencies."], + [ + /npm ci --include=dev/, + "Cloud setup must install the exact lockfile with dev dependencies.", + ], [/deno@2/, "Cloud setup must install Deno 2.x."], - [/worker\/python\/requirements-cloud\.txt/, "Cloud setup must install the Python 3.12 Cloud worker lock."], - [/CODEX_CLOUD_OCR_PYTHON/, "Cloud setup must expose the Python worker environment."], - [/playwright install --with-deps chromium firefox webkit/, "Cloud setup must install every browser."], - [/CODEX_CLOUD_ACCESS_PROFILE/, "Cloud setup must support explicit access profiles."], - [/RAG_PROVIDER_MODE=offline/, "Cloud setup must default RAG to offline mode."], - [/unset OPENAI_API_KEY/, "Cloud setup must remove raw provider variables from the agent shell."], + [ + /worker\/python\/requirements-cloud\.txt/, + "Cloud setup must install the Python 3.12 Cloud worker lock.", + ], + [ + /CODEX_CLOUD_OCR_PYTHON/, + "Cloud setup must expose the Python worker environment.", + ], + [ + /playwright install --with-deps chromium firefox webkit/, + "Cloud setup must install every browser.", + ], + [ + /CODEX_CLOUD_ACCESS_PROFILE/, + "Cloud setup must support explicit access profiles.", + ], + [ + /RAG_PROVIDER_MODE=offline/, + "Cloud setup must default RAG to offline mode.", + ], + [ + /unset OPENAI_API_KEY/, + "Cloud setup must remove raw provider variables from the agent shell.", + ], [/\.bash_profile/, "Cloud setup must cover Bash login-profile precedence."], [/@railway\/cli/, "Cloud setup must install the Railway CLI."], [/@openai\/codex/, "Cloud setup must install the Codex CLI."], - [/ensure-codex-cloud-git-remote\.mjs/, "Cloud setup must restore a safe origin remote."], - [/check:codex-cloud -- --runtime/, "Cloud setup must run runtime acceptance."], + [ + /ensure-codex-cloud-git-remote\.mjs/, + "Cloud setup must restore a safe origin remote.", + ], + [ + /check:codex-cloud -- --runtime/, + "Cloud setup must run runtime acceptance.", + ], [ /BEGIN clinical-kb-codex-cloud shell policy/, "Cloud setup must write the Codex shell policy inside a managed marker block.", @@ -527,36 +777,68 @@ export function validateCodexCloudSetup() { /Unmanaged \[shell_environment_policy\] table found/, "Cloud setup must reject unmanaged shell_environment_policy tables before rewriting config.toml.", ], - [/Incomplete managed shell policy block/, "Cloud setup must reject incomplete managed shell policy marker blocks."], + [ + /Incomplete managed shell policy block/, + "Cloud setup must reject incomplete managed shell policy marker blocks.", + ], [ /export RAG_PROVIDER_MODE="\$\{rag_provider_mode\}"/, "Cloud setup must pin the connected-mode retrieval value at setup time.", ], - [/inherit = "all"/, "Cloud setup must configure Codex shell_environment_policy inheritance."], - [/CODEX_CLOUD_SETUP_STOP_AFTER_POLICY/, "Cloud setup must expose a policy-only stop for behavior-level tests."], + [ + /inherit = "all"/, + "Cloud setup must configure Codex shell_environment_policy inheritance.", + ], + [ + /CODEX_CLOUD_SETUP_STOP_AFTER_POLICY/, + "Cloud setup must expose a policy-only stop for behavior-level tests.", + ], ]) { requireMatch(errors, setup, pattern, message); } - if (!setup.includes(`railway_cli_version="${expectedCloudCliVersions.railway}"`)) { - errors.push("Cloud setup Railway CLI version must match the checked runtime contract."); + if ( + !setup.includes(`railway_cli_version="${expectedCloudCliVersions.railway}"`) + ) { + errors.push( + "Cloud setup Railway CLI version must match the checked runtime contract.", + ); } - if (!setup.includes(`codex_cli_version="${expectedCloudCliVersions.codex}"`)) { - errors.push("Cloud setup Codex CLI version must match the checked runtime contract."); + if ( + !setup.includes(`codex_cli_version="${expectedCloudCliVersions.codex}"`) + ) { + errors.push( + "Cloud setup Codex CLI version must match the checked runtime contract.", + ); } for (const name of providerCredentialVariables) { - if (!setup.includes(name)) errors.push(`Cloud setup must handle provider environment variable ${name}.`); + if (!setup.includes(name)) + errors.push( + `Cloud setup must handle provider environment variable ${name}.`, + ); } const providerScrubIndex = setup.indexOf("unset OPENAI_API_KEY"); - const accessProfileBranchIndex = setup.indexOf('if [ "\\$CODEX_CLOUD_ACCESS_PROFILE" = "connected" ]'); - if (providerScrubIndex < 0 || accessProfileBranchIndex < 0 || providerScrubIndex > accessProfileBranchIndex) { - errors.push("Cloud setup must scrub provider environment variables before selecting an access profile."); + const accessProfileBranchIndex = setup.indexOf( + 'if [ "\\$CODEX_CLOUD_ACCESS_PROFILE" = "connected" ]', + ); + if ( + providerScrubIndex < 0 || + accessProfileBranchIndex < 0 || + providerScrubIndex > accessProfileBranchIndex + ) { + errors.push( + "Cloud setup must scrub provider environment variables before selecting an access profile.", + ); } const credentialLikeExampleNames = [ - ...envExample.matchAll(/^([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|DB_URL))=/gm), + ...envExample.matchAll( + /^([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|DB_URL))=/gm, + ), ].map(([, name]) => name); for (const name of credentialLikeExampleNames) { if (!providerCredentialVariables.includes(name)) { - errors.push(`Cloud credential inventory must include .env.example variable ${name}.`); + errors.push( + `Cloud credential inventory must include .env.example variable ${name}.`, + ); } } requireMatch( @@ -595,23 +877,46 @@ export function validateCodexCloudSetup() { requireMatch( errors, patDelete, - /CODEX_CLOUD_ACCESS_PROFILE.*connected/, - "PAT deletion helper must require the connected profile.", + /CODEX_CLOUD.*use native Push/, + "PAT deletion helper must reject the Codex Cloud agent phase and direct operators to native publication.", ); + requireMatch( + errors, + rawEnvironmentProbe, + /never values/, + "Raw Cloud environment probe must report names only.", + ); + for (const name of providerCredentialVariables) { + if (!rawEnvironmentProbe.includes(name)) { + errors.push( + `Raw Cloud environment probe must cover provider environment variable ${name}.`, + ); + } + } requireMatch( errors, patDelete, /\[\[ "\$branch" != -\* \]\]/, "PAT deletion helper must reject option-like branch names.", ); - requireMatch(errors, patDelete, /git check-ref-format --branch/, "PAT deletion helper must validate branch names."); + requireMatch( + errors, + patDelete, + /git check-ref-format --branch/, + "PAT deletion helper must validate branch names.", + ); requireMatch( errors, patDelete, /git remote get-url --push --all origin/, "PAT deletion helper must validate effective push URLs.", ); - requireMatch(errors, patDelete, /GIT_ASKPASS/, "PAT deletion helper must use a temporary askpass program."); + requireMatch( + errors, + patDelete, + /GIT_ASKPASS/, + "PAT deletion helper must use a temporary askpass program.", + ); requireMatch( errors, patDelete, @@ -624,16 +929,36 @@ export function validateCodexCloudSetup() { /https:\/\/github\.com\/BigSimmo\/Database\.git/, "PAT deletion helper must require the credential-free origin.", ); - requireMatch(errors, guide, /bash scripts\/setup-codex-cloud\.sh/, "The guide must provide the setup command."); + requireMatch( + errors, + guide, + /bash scripts\/setup-codex-cloud\.sh/, + "The guide must provide the setup command.", + ); requireMatch( errors, guide, /install-codex-cloud-command-shims\.sh/, "The guide must document the command-shim workaround.", ); - requireMatch(errors, guide, /CODEX_CLOUD_ACCESS_PROFILE=connected/, "The guide must document connected access."); - requireMatch(errors, guide, /CODEX_CLOUD_GITHUB_PAT/, "The guide must document the narrowly scoped PAT exception."); - requireMatch(errors, guide, /GitHub connector/, "The guide must document GitHub connector access."); + requireMatch( + errors, + guide, + /CODEX_CLOUD_ACCESS_PROFILE=connected/, + "The guide must document connected access.", + ); + requireMatch( + errors, + guide, + /CODEX_CLOUD_GITHUB_PAT/, + "The guide must document the narrowly scoped PAT exception.", + ); + requireMatch( + errors, + guide, + /GitHub connector/, + "The guide must document GitHub connector access.", + ); try { parseMcpServerMetadata(mcp); } catch (error) { @@ -651,7 +976,9 @@ export function validateCodexCloudSetup() { "verify:release", ]) { if (setup.includes(command) || maintenance.includes(command)) { - errors.push(`Cloud bootstrap scripts must not invoke provider-capable command ${command}.`); + errors.push( + `Cloud bootstrap scripts must not invoke provider-capable command ${command}.`, + ); } } @@ -662,13 +989,19 @@ export function validateCodexCloudSetup() { /sb_secret_[A-Za-z0-9_-]{8,}/, ]) { if (pattern.test(setup) || pattern.test(maintenance)) { - errors.push(`Cloud bootstrap scripts contain a live provider identifier matching ${pattern}.`); + errors.push( + `Cloud bootstrap scripts contain a live provider identifier matching ${pattern}.`, + ); } } - const cloudHeadingCount = (agents.match(/^## Codex Cloud environment$/gm) ?? []).length; + const cloudHeadingCount = ( + agents.match(/^## Codex Cloud environment$/gm) ?? [] + ).length; if (cloudHeadingCount !== 1) { - errors.push(`AGENTS.md must contain exactly one Codex Cloud environment section; found ${cloudHeadingCount}.`); + errors.push( + `AGENTS.md must contain exactly one Codex Cloud environment section; found ${cloudHeadingCount}.`, + ); } return errors; } @@ -689,7 +1022,10 @@ function repositoryCommand(command, args) { shell: false, }); if (result.status === 0) return null; - const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim().split(/\r?\n/).at(-1); + const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}` + .trim() + .split(/\r?\n/) + .at(-1); return `${command} ${args.join(" ")} failed: ${output || `exit ${result.status}`}`; } @@ -699,17 +1035,31 @@ export async function validateCodexCloudRuntime(env = process.env) { for (const error of [ commandVersion("deno", ["--version"], /^deno 2\./m), commandVersion("tesseract", ["--version"], /^tesseract \d+\./m), - commandVersion("railway", ["--version"], exactVersionPattern(expectedCloudCliVersions.railway)), - commandVersion("codex", ["--version"], exactVersionPattern(expectedCloudCliVersions.codex)), + commandVersion( + "railway", + ["--version"], + exactVersionPattern(expectedCloudCliVersions.railway), + ), + commandVersion( + "codex", + ["--version"], + exactVersionPattern(expectedCloudCliVersions.codex), + ), ]) { if (error) errors.push(error); } const pythonError = pythonWorkerImportError(env.CODEX_CLOUD_OCR_PYTHON); if (pythonError) errors.push(pythonError); + else errors.push(...pythonWorkerDependencyErrors(env.CODEX_CLOUD_OCR_PYTHON)); for (const error of [ - repositoryCommand(process.execPath, ["scripts/run-tsx.mjs", "scripts/check-runtime.ts"]), - repositoryCommand(process.execPath, ["scripts/check-installed-lock-parity.mjs"]), + repositoryCommand(process.execPath, [ + "scripts/run-tsx.mjs", + "scripts/check-runtime.ts", + ]), + repositoryCommand(process.execPath, [ + "scripts/check-installed-lock-parity.mjs", + ]), ]) { if (error) errors.push(error); } @@ -717,15 +1067,9 @@ export async function validateCodexCloudRuntime(env = process.env) { if (env.CODEX_CLOUD_SKIP_BROWSER_INSTALL !== "1") { try { const { chromium, firefox, webkit } = await import("playwright"); - for (const [name, browserType] of Object.entries({ - chromium, - firefox, - webkit, - })) { - if (!executableFile(browserType.executablePath())) { - errors.push(`${name} browser executable is unavailable.`); - } - } + errors.push( + ...(await playwrightBrowserErrors({ chromium, firefox, webkit })), + ); } catch (error) { errors.push(`Playwright browser validation failed: ${error.message}`); } @@ -733,36 +1077,76 @@ export async function validateCodexCloudRuntime(env = process.env) { const obsoleteProxyNames = obsoleteNpmProxyVariables(env); if (obsoleteProxyNames.length > 0) { - errors.push(`Obsolete npm proxy variable names are set: ${obsoleteProxyNames.join(", ")}.`); + errors.push( + `Obsolete npm proxy variable names are set: ${obsoleteProxyNames.join(", ")}.`, + ); } - if (!localGitBaseline(repoRoot, env)) { - errors.push("Neither local main, origin/main, nor a Cloud task HEAD is available."); + const baseline = localGitBaseline(repoRoot, env); + if (!baseline) { + errors.push( + "Neither local main, origin/main, nor a Cloud task HEAD is available.", + ); + } else if (baseline === "HEAD" && !env.CODEX_CLOUD_EXPECTED_BASE_SHA) { + errors.push( + "Checkout freshness is unverified: set CODEX_CLOUD_EXPECTED_BASE_SHA to the intended merge/base commit.", + ); + } + if (env.CODEX_CLOUD_EXPECTED_BASE_SHA) { + const expectedBase = spawnSync( + "git", + [ + "merge-base", + "--is-ancestor", + env.CODEX_CLOUD_EXPECTED_BASE_SHA, + "HEAD", + ], + { cwd: repoRoot, stdio: "ignore" }, + ); + if (expectedBase.status !== 0) { + errors.push( + "CODEX_CLOUD_EXPECTED_BASE_SHA is not an ancestor of the current HEAD.", + ); + } } const origin = inspectOriginRemote(repoRoot); - if (!origin.configured) errors.push("origin is unavailable in the Cloud checkout."); - else if (origin.credentialsEmbedded) errors.push("origin contains embedded credentials."); - else if (!origin.repositoryMatch) errors.push(`origin must identify ${CODEX_CLOUD_REPOSITORY}.`); + if (!origin.configured) + errors.push("origin is unavailable in the Cloud checkout."); + else if (origin.credentialsEmbedded) + errors.push("origin contains embedded credentials."); + else if (!origin.repositoryMatch) + errors.push(`origin must identify ${CODEX_CLOUD_REPOSITORY}.`); return errors; } -if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { +if ( + process.argv[1] && + path.resolve(process.argv[1]) === fileURLToPath(import.meta.url) +) { const errors = validateCodexCloudSetup(); const runtime = process.argv.includes("--runtime"); - const environment = runtime || process.env.CODEX_CLOUD === "1" || process.argv.includes("--environment"); + const environment = + runtime || + process.env.CODEX_CLOUD === "1" || + process.argv.includes("--environment"); if (runtime) errors.push(...(await validateCodexCloudRuntime())); else if (environment) errors.push(...validateCodexCloudEnvironment()); if (environment) { - console.log("[Codex Cloud Environment] sanitized effective modes and capabilities:"); - for (const line of sanitizedCloudCapabilityLines()) console.log(` ${line}`); + console.log( + "[Codex Cloud Environment] sanitized effective modes and capabilities:", + ); + for (const line of sanitizedCloudCapabilityLines()) + console.log(` ${line}`); } if (errors.length > 0) { - for (const error of errors) console.error(`[Codex Cloud Check] FAIL: ${error}`); + for (const error of errors) + console.error(`[Codex Cloud Check] FAIL: ${error}`); process.exitCode = 1; } else { const scope = codexCloudValidationScope({ runtime, environment, - browserInstallSkipped: process.env.CODEX_CLOUD_SKIP_BROWSER_INSTALL === "1", + browserInstallSkipped: + process.env.CODEX_CLOUD_SKIP_BROWSER_INSTALL === "1", }); console.log(`[Codex Cloud Check] PASS: ${scope} Cloud contracts match.`); } From 1f56e6cb4ee6d7ed2fc16e0457b5cb427e79997e Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:19 +0800 Subject: [PATCH 03/29] test(cloud): cover raw env and browser launch contracts --- tests/codex-cloud-setup.test.ts | 326 +++++++++++++++++++++++++------- 1 file changed, 257 insertions(+), 69 deletions(-) diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index e0369cf212..759d028793 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -1,5 +1,12 @@ import { spawnSync } from "node:child_process"; -import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"; +import { + mkdirSync, + mkdtempSync, + readFileSync, + readdirSync, + rmSync, + writeFileSync, +} from "node:fs"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -13,6 +20,7 @@ import { localGitBaseline, obsoleteNpmProxyVariables, parseMcpServerMetadata, + playwrightBrowserErrors, providerCredentialVariables, pythonWorkerImportError, pythonWorkerImports, @@ -30,11 +38,17 @@ import { } from "../scripts/ensure-codex-cloud-git-remote.mjs"; const temporaryDirectories: string[] = []; -const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const repoRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "..", +); const setupScript = "scripts/setup-codex-cloud.sh"; const bashCommand = process.platform === "win32" - ? path.join(process.env.ProgramFiles || "C:\\Program Files", "Git/bin/bash.exe") + ? path.join( + process.env.ProgramFiles || "C:\\Program Files", + "Git/bin/bash.exe", + ) : "bash"; const requiredPolicyExcludes = [ "OPENAI_API_KEY", @@ -62,6 +76,7 @@ const requiredPolicyExcludes = [ "RAILWAY_TOKEN", "GH_TOKEN", "GITHUB_TOKEN", + "CODEX_CLOUD_GITHUB_PAT", "GITLAB_TOKEN", "GLAB_TOKEN", "CODEX_TRIGGER_TOKEN", @@ -74,7 +89,8 @@ const requiredPolicyExcludes = [ ] as const; afterEach(() => { - for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }); + for (const directory of temporaryDirectories.splice(0)) + rmSync(directory, { recursive: true, force: true }); }); function temporaryDirectory(prefix: string) { @@ -85,7 +101,10 @@ function temporaryDirectory(prefix: string) { function temporaryGitRepository() { const directory = temporaryDirectory("codex-cloud-git-"); - expect(spawnSync("git", ["init", "--quiet", "--initial-branch=task", directory]).status).toBe(0); + expect( + spawnSync("git", ["init", "--quiet", "--initial-branch=task", directory]) + .status, + ).toBe(0); return directory; } @@ -95,21 +114,32 @@ function git(directory: string, ...args: string[]) { function bashPathEntry(entry: string) { if (process.platform !== "win32") return entry; - return entry.replace(/^([A-Za-z]):/, (_, drive: string) => `/${drive.toLowerCase()}`).replaceAll("\\", "/"); + return entry + .replace(/^([A-Za-z]):/, (_, drive: string) => `/${drive.toLowerCase()}`) + .replaceAll("\\", "/"); } function bashPathList(value: string) { if (process.platform !== "win32") return value; - return value.split(path.delimiter).filter(Boolean).map(bashPathEntry).join(":"); + return value + .split(path.delimiter) + .filter(Boolean) + .map(bashPathEntry) + .join(":"); } -function runSetupPolicyOnly(home: string, env: Record = {}) { +function runSetupPolicyOnly( + home: string, + env: Record = {}, +) { // The test redirects HOME to isolate the generated Codex config. Put the // running test process's Node binary first so version-manager launchers that // resolve their runtime through HOME remain usable until setup reaches the // policy-only stop. const nodeBin = path.dirname(process.execPath); - const requestedPath = env.PATH || [nodeBin, process.env.PATH].filter(Boolean).join(path.delimiter); + const requestedPath = + env.PATH || + [nodeBin, process.env.PATH].filter(Boolean).join(path.delimiter); return spawnSync(bashCommand, [setupScript], { cwd: repoRoot, encoding: "utf8", @@ -135,15 +165,21 @@ describe("Codex Cloud environment contract", () => { it("keeps the checked-in setup reproducible and provider-safe", () => { const staticEnvironment = { ...process.env }; delete staticEnvironment.CODEX_CLOUD; - const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs"], { - cwd: path.resolve(import.meta.dirname, ".."), - encoding: "utf8", - env: staticEnvironment, - shell: false, - }); + const result = spawnSync( + process.execPath, + ["scripts/check-codex-cloud-setup.mjs"], + { + cwd: path.resolve(import.meta.dirname, ".."), + encoding: "utf8", + env: staticEnvironment, + shell: false, + }, + ); expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); - expect(result.stdout).toContain("[Codex Cloud Check] PASS: static Cloud contracts match."); + expect(result.stdout).toContain( + "[Codex Cloud Check] PASS: static Cloud contracts match.", + ); }); it("reports sensitive and proxy variable names without exposing values", () => { @@ -155,7 +191,10 @@ describe("Codex Cloud environment contract", () => { HTTP_PROXY: "http://supported.example.test", }; - expect(configuredProviderCredentialNames(env)).toEqual(["OPENAI_API_KEY", "CROSS_TENANT_SERVICE_ROLE_KEY"]); + expect(configuredProviderCredentialNames(env)).toEqual([ + "OPENAI_API_KEY", + "CROSS_TENANT_SERVICE_ROLE_KEY", + ]); expect( configuredProviderCredentialNames({ CODEX_CLOUD_GITHUB_PAT: "never-print-this", @@ -165,7 +204,8 @@ describe("Codex Cloud environment contract", () => { }); it("covers every provider-capable test variable in the Cloud credential inventory", () => { - for (const key of providerEnvironmentKeys) expect(providerCredentialVariables).toContain(key); + for (const key of providerEnvironmentKeys) + expect(providerCredentialVariables).toContain(key); }); it("validates effective offline modes and keeps connected verification explicit", () => { @@ -191,7 +231,9 @@ describe("Codex Cloud environment contract", () => { NEXT_PUBLIC_DEMO_MODE: "false", PLAYWRIGHT_OFFLINE_MODE: "false", }), - ).toEqual([]); + ).toContain( + "RAG_PROVIDER_MODE must be an approved value in connected mode.", + ); expect( validateCodexCloudEnvironment({ @@ -200,7 +242,9 @@ describe("Codex Cloud environment contract", () => { RAG_PROVIDER_MODE: "offline", SUPABASE_ACCESS_TOKEN: "setup-only-secret", }), - ).toContain("Connected mode exposes provider environment variables: SUPABASE_ACCESS_TOKEN."); + ).toContain( + "Connected mode exposes provider environment variables: SUPABASE_ACCESS_TOKEN.", + ); }); it("emits sanitized modes, credential presence, and MCP metadata only", () => { @@ -237,19 +281,29 @@ describe("Codex Cloud environment contract", () => { ); const report = lines.join("\n"); expect(report).toContain("OPENAI_API_KEY.present=true"); - expect(report).toContain("mcp.server=railway type=http command=none endpoint=https://mcp.railway.com/"); + expect(report).toContain( + "mcp.server=railway type=http command=none endpoint=https://mcp.railway.com/", + ); expect(report).not.toContain(secret); expect(report).not.toContain("sensitive-test"); }); it("requires the Railway CLI and dedicated account token without substituting a project token", () => { - expect(railwayReadCapability({ RAILWAY_API_TOKEN: "configured" }, true).ready).toBe(true); - expect(railwayReadCapability({ RAILWAY_TOKEN: "configured" }, true)).toMatchObject({ + expect( + railwayReadCapability({ RAILWAY_API_TOKEN: "configured" }, true) + .cliTokenAuthReady, + ).toBe(true); + expect( + railwayReadCapability({ RAILWAY_TOKEN: "configured" }, true), + ).toMatchObject({ dedicatedCredentialPresent: false, projectCredentialPresent: true, - ready: false, + cliTokenAuthReady: false, }); - expect(railwayReadCapability({ RAILWAY_API_TOKEN: "configured" }, false).ready).toBe(false); + expect( + railwayReadCapability({ RAILWAY_API_TOKEN: "configured" }, false) + .cliTokenAuthReady, + ).toBe(false); }); it("parses MCP transport metadata without query or environment values", () => { @@ -292,42 +346,69 @@ describe("Codex Cloud environment contract", () => { }, }); expect(validateMcpConfiguration(valid)).toEqual([]); - expect(validateMcpConfiguration(valid.replace("read_only=true", "read_only=false"))).toContain( - "Supabase MCP must keep the production project read-only.", - ); - expect(validateMcpConfiguration(valid.replace('"supabase":', '"unexpected":{},"supabase":'))).toContain( + expect( + validateMcpConfiguration( + valid.replace("read_only=true", "read_only=false"), + ), + ).toContain("Supabase MCP must keep the production project read-only."); + expect( + validateMcpConfiguration( + valid.replace('"supabase":', '"unexpected":{},"supabase":'), + ), + ).toContain( "Cloud MCP configuration must contain only Railway and Supabase.", ); - expect(validateMcpConfiguration(valid.replace('"railway":{"type"', '"railway":{"headers":{},"type"'))).toContain( + expect( + validateMcpConfiguration( + valid.replace('"railway":{"type"', '"railway":{"headers":{},"type"'), + ), + ).toContain( "railway MCP must use hosted OAuth without embedded environment variables or headers.", ); - expect(validateMcpConfiguration(valid.replace("&read_only=true", "&read_only=true&token=forbidden"))).toContain( - "Supabase MCP must not include additional query parameters.", - ); - expect(validateMcpConfiguration(valid.replace("&read_only=true", "&read_only=true&read_only=false"))).toContain( - "Supabase MCP must not include additional query parameters.", - ); + expect( + validateMcpConfiguration( + valid.replace("&read_only=true", "&read_only=true&token=forbidden"), + ), + ).toContain("Supabase MCP must not include additional query parameters."); + expect( + validateMcpConfiguration( + valid.replace("&read_only=true", "&read_only=true&read_only=false"), + ), + ).toContain("Supabase MCP must not include additional query parameters."); }); it("keeps project .codex/config.toml MCP registrations disabled and secret-free", () => { - const tracked = readFileSync(new URL("../.codex/config.toml", import.meta.url), "utf8"); + const tracked = readFileSync( + new URL("../.codex/config.toml", import.meta.url), + "utf8", + ); expect(validateCodexProjectMcpConfiguration(tracked)).toEqual([]); - expect(validateCodexProjectMcpConfiguration(tracked.replaceAll("enabled = false", "enabled = true"))).toContain( + expect( + validateCodexProjectMcpConfiguration( + tracked.replaceAll("enabled = false", "enabled = true"), + ), + ).toContain( `.codex/config.toml figma_cloud must set enabled = false (host/connected layers opt in).`, ); expect( validateCodexProjectMcpConfiguration( - tracked.replace('default_tools_approval_mode = "writes"', 'default_tools_approval_mode = "auto"'), + tracked.replace( + 'default_tools_approval_mode = "writes"', + 'default_tools_approval_mode = "auto"', + ), ), ).toContain( `.codex/config.toml figma_cloud must set default_tools_approval_mode = "writes" because write-capable tools require explicit approval.`, ); expect( validateCodexProjectMcpConfiguration( - tracked.replace('default_tools_approval_mode = "auto"', 'default_tools_approval_mode = "writes"'), + tracked.replace( + 'default_tools_approval_mode = "prompt"', + 'default_tools_approval_mode = "auto"', + ), ), ).toContain( - `.codex/config.toml supabase_cloud must set default_tools_approval_mode = "auto" because the production server is constrained read-only.`, + `.codex/config.toml supabase_cloud must set default_tools_approval_mode = "prompt" because the production server is constrained read-only.`, ); expect( validateCodexProjectMcpConfiguration( @@ -356,7 +437,9 @@ describe("Codex Cloud environment contract", () => { 'url = "https://mcp.figma.com/mcp"\nscopes = ["files:write"]', ), ), - ).toContain(`.codex/config.toml figma_cloud must be URL-only; unsupported key scopes.`); + ).toContain( + `.codex/config.toml figma_cloud must be URL-only; unsupported key scopes.`, + ); expect( validateCodexProjectMcpConfiguration( tracked.replace( @@ -364,7 +447,9 @@ describe("Codex Cloud environment contract", () => { 'url = "https://mcp.figma.com/mcp"\n__anything = "secret"', ), ), - ).toContain(`.codex/config.toml figma_cloud must be URL-only; unsupported key __anything.`); + ).toContain( + `.codex/config.toml figma_cloud must be URL-only; unsupported key __anything.`, + ); expect( validateCodexProjectMcpConfiguration( tracked.replace( @@ -392,24 +477,60 @@ describe("Codex Cloud environment contract", () => { "project_ref=sjrfecxgysukkwxsowpy&read_only=false&features=", ), ), - ).toContain(`.codex/config.toml supabase_cloud must keep the production project read-only.`); + ).toContain( + `.codex/config.toml supabase_cloud must keep the production project read-only.`, + ); + }); + + it("probes the raw task environment without printing credential values", () => { + const secret = "never-print-raw-provider-value"; + const result = spawnSync( + bashCommand, + ["scripts/check-codex-cloud-raw-env.sh"], + { + cwd: repoRoot, + encoding: "utf8", + env: { PATH: process.env.PATH, OPENAI_API_KEY: secret }, + }, + ); + expect(result.status).toBe(1); + expect(result.stderr).toContain("OPENAI_API_KEY"); + expect(result.stderr).not.toContain(secret); }); it("keeps setup and maintenance repairs guarded for repeat execution", () => { - const setup = readFileSync(new URL("../scripts/setup-codex-cloud.sh", import.meta.url), "utf8"); - const maintenance = readFileSync(new URL("../scripts/maintain-codex-cloud.sh", import.meta.url), "utf8"); + const setup = readFileSync( + new URL("../scripts/setup-codex-cloud.sh", import.meta.url), + "utf8", + ); + const maintenance = readFileSync( + new URL("../scripts/maintain-codex-cloud.sh", import.meta.url), + "utf8", + ); const commandShims = readFileSync( - new URL("../scripts/install-codex-cloud-command-shims.sh", import.meta.url), + new URL( + "../scripts/install-codex-cloud-command-shims.sh", + import.meta.url, + ), "utf8", ); const patDelete = readFileSync( - new URL("../scripts/delete-codex-cloud-branch-with-pat.sh", import.meta.url), + new URL( + "../scripts/delete-codex-cloud-branch-with-pat.sh", + import.meta.url, + ), "utf8", ); expect(setup).toContain("if ! grep -Fq '.clinical-kb-codex-cloud.sh'"); - expect(setup).toContain('if [[ "$actual_version" != "$expected_version" ]]'); + expect(setup).toContain( + 'if [[ "$actual_version" != "$expected_version" ]]', + ); expect(setup).toContain('"$HOME/.bash_profile"'); - expect(setup.match(/unset npm_config_http_proxy npm_config_https_proxy npm_config_proxy/g)).toHaveLength(2); + expect( + setup.match( + /unset npm_config_http_proxy npm_config_https_proxy npm_config_proxy/g, + ), + ).toHaveLength(2); expect(setup.indexOf("unset OPENAI_API_KEY")).toBeLessThan( setup.indexOf('if [ "\\$CODEX_CLOUD_ACCESS_PROFILE" = "connected" ]'), ); @@ -421,7 +542,9 @@ describe("Codex Cloud environment contract", () => { expect(setup).toContain("Incomplete managed shell policy block"); expect(setup).toContain('export RAG_PROVIDER_MODE="${rag_provider_mode}"'); expect(setup).toContain('rag_provider_mode="${RAG_PROVIDER_MODE:-auto}"'); - expect(setup).not.toContain('RAG_PROVIDER_MODE="\\${RAG_PROVIDER_MODE:-auto}"'); + expect(setup).not.toContain( + 'RAG_PROVIDER_MODE="\\${RAG_PROVIDER_MODE:-auto}"', + ); expect(setup).toContain("SUPABASE_URL"); expect(setup).toContain("SUPABASE_PROJECT_REF"); expect(setup).toContain("NEXT_PUBLIC_SUPABASE_URL"); @@ -432,13 +555,15 @@ describe("Codex Cloud environment contract", () => { expect(setup).toContain("diagnose-codex-cloud.mjs"); expect(setup).toContain("trap diagnose_setup_failure ERR"); expect(setup).toContain('setup_step="python-worker-requirements"'); - expect(setup).toContain("--require-hashes -r worker/python/requirements-cloud.txt"); + expect(setup).toContain( + "--require-hashes -r worker/python/requirements-cloud.txt", + ); expect(setup).toContain('"$ocr_venv/bin/python" -m pip check'); expect(maintenance).toContain("ensure-codex-cloud-git-remote.mjs"); expect(commandShims).toContain('nvm which "$expected_node_major"'); expect(commandShims).toContain('. "$runtime_profile"'); expect(commandShims).toContain('mkdir -p "$HOME/.local/bin"'); - expect(patDelete).toContain("CODEX_CLOUD_ACCESS_PROFILE:-offline"); + expect(patDelete).toContain('[[ "${CODEX_CLOUD:-0}" != "1" ]]'); expect(patDelete).toContain('[[ "$branch" != -* ]]'); expect(patDelete).toContain("git check-ref-format --branch"); expect(patDelete).toContain("git remote get-url --push --all origin"); @@ -451,7 +576,9 @@ describe("Codex Cloud environment contract", () => { mkdirSync(path.join(home, ".codex"), { recursive: true }); writeFileSync( path.join(home, ".codex/config.toml"), - ["[mcp_servers.example]", 'command = "echo"', 'args = ["ping"]', ""].join("\n"), + ["[mcp_servers.example]", 'command = "echo"', 'args = ["ping"]', ""].join( + "\n", + ), ); const first = runSetupPolicyOnly(home, { @@ -480,7 +607,9 @@ describe("Codex Cloud environment contract", () => { const rewritten = readCodexConfig(home); expect(rewritten).toContain("[mcp_servers.example]"); expect(rewritten.match(/^\[shell_environment_policy\]$/gm)).toHaveLength(1); - expect(rewritten.match(/BEGIN clinical-kb-codex-cloud shell policy/g)).toHaveLength(1); + expect( + rewritten.match(/BEGIN clinical-kb-codex-cloud shell policy/g), + ).toHaveLength(1); }); it("pins connected retrieval mode and rejects unsafe shell-policy configs", () => { @@ -491,7 +620,9 @@ describe("Codex Cloud environment contract", () => { }); expect(connected.status, connected.stderr || connected.stdout).toBe(0); const connectedProfile = readRuntimeProfile(connectedHome); - expect(connectedProfile).toContain('export CODEX_CLOUD_ACCESS_PROFILE="connected"'); + expect(connectedProfile).toContain( + 'export CODEX_CLOUD_ACCESS_PROFILE="connected"', + ); expect(connectedProfile).toContain('export RAG_PROVIDER_MODE="offline"'); expect(connectedProfile).not.toContain("${RAG_PROVIDER_MODE:-auto}"); @@ -512,7 +643,9 @@ describe("Codex Cloud environment contract", () => { CODEX_CLOUD_ACCESS_PROFILE: "offline", }); expect(unmanaged.status).not.toBe(0); - expect(unmanaged.stderr).toContain("Unmanaged [shell_environment_policy] table found"); + expect(unmanaged.stderr).toContain( + "Unmanaged [shell_environment_policy] table found", + ); expect(readFileSync(unmanagedPath, "utf8")).toBe(unmanagedConfig); for (const tableHeader of [ @@ -522,14 +655,21 @@ describe("Codex Cloud environment contract", () => { ]) { const formattedHome = temporaryDirectory("codex-cloud-formatted-"); mkdirSync(path.join(formattedHome, ".codex"), { recursive: true }); - const formattedConfig = [tableHeader, 'inherit = "all"', "exclude = []", ""].join("\n"); + const formattedConfig = [ + tableHeader, + 'inherit = "all"', + "exclude = []", + "", + ].join("\n"); const formattedPath = path.join(formattedHome, ".codex/config.toml"); writeFileSync(formattedPath, formattedConfig); const formatted = runSetupPolicyOnly(formattedHome, { CODEX_CLOUD_ACCESS_PROFILE: "offline", }); expect(formatted.status).not.toBe(0); - expect(formatted.stderr).toContain("Unmanaged [shell_environment_policy] table found"); + expect(formatted.stderr).toContain( + "Unmanaged [shell_environment_policy] table found", + ); expect(readFileSync(formattedPath, "utf8")).toBe(formattedConfig); } @@ -545,13 +685,17 @@ describe("Codex Cloud environment contract", () => { CODEX_CLOUD_ACCESS_PROFILE: "offline", }); expect(formatted.status).not.toBe(0); - expect(formatted.stderr).toContain("Unmanaged [shell_environment_policy] table found"); + expect(formatted.stderr).toContain( + "Unmanaged [shell_environment_policy] table found", + ); expect(readFileSync(formattedPath, "utf8")).toBe(formattedConfig); } const atomicHome = temporaryDirectory("codex-cloud-atomic-"); mkdirSync(path.join(atomicHome, ".codex"), { recursive: true }); - const atomicConfig = ["[mcp_servers.keep]", 'command = "echo"', ""].join("\n"); + const atomicConfig = ["[mcp_servers.keep]", 'command = "echo"', ""].join( + "\n", + ); const atomicPath = path.join(atomicHome, ".codex/config.toml"); writeFileSync(atomicPath, atomicConfig); const atomic = runSetupPolicyOnly(atomicHome, { @@ -560,7 +704,11 @@ describe("Codex Cloud environment contract", () => { }); expect(atomic.status).not.toBe(0); expect(readFileSync(atomicPath, "utf8")).toBe(atomicConfig); - expect(readdirSync(path.dirname(atomicPath)).filter((name) => name.startsWith(".config.toml."))).toEqual([]); + expect( + readdirSync(path.dirname(atomicPath)).filter((name) => + name.startsWith(".config.toml."), + ), + ).toEqual([]); const incompleteHome = temporaryDirectory("codex-cloud-incomplete-"); mkdirSync(path.join(incompleteHome, ".codex"), { recursive: true }); @@ -579,7 +727,9 @@ describe("Codex Cloud environment contract", () => { CODEX_CLOUD_ACCESS_PROFILE: "offline", }); expect(incomplete.status).not.toBe(0); - expect(incomplete.stderr).toContain("Incomplete managed shell policy block"); + expect(incomplete.stderr).toContain( + "Incomplete managed shell policy block", + ); expect(readFileSync(incompletePath, "utf8")).toBe(incompleteConfig); }); @@ -602,6 +752,24 @@ describe("Codex Cloud environment contract", () => { expect(localGitBaseline(directory, { CODEX_CLOUD: "1" })).toBe("HEAD"); }); + it("launches and closes every installed Playwright browser", async () => { + let closeCount = 0; + const browserType = { + executablePath: () => process.execPath, + launch: async (options: { headless: boolean; timeout: number }) => { + expect(options).toEqual({ headless: true, timeout: 1234 }); + return { close: async () => void (closeCount += 1) }; + }, + }; + expect( + await playwrightBrowserErrors( + { chromium: browserType, firefox: browserType, webkit: browserType }, + 1234, + ), + ).toEqual([]); + expect(closeCount).toBe(3); + }); + it("does not describe a source-only runtime as fully browser-ready", () => { expect( codexCloudValidationScope({ @@ -609,7 +777,9 @@ describe("Codex Cloud environment contract", () => { environment: true, browserInstallSkipped: true, }), - ).toBe("static, environment, and source-only runtime (browser validation skipped)"); + ).toBe( + "static, environment, and source-only runtime (browser validation skipped)", + ); expect( codexCloudValidationScope({ runtime: true, @@ -631,8 +801,14 @@ describe("Codex Cloud environment contract", () => { return { status: 0 }; }; - expect(pythonWorkerImportError(process.execPath, run as typeof spawnSync)).toBeNull(); - expect(invocation).toEqual([process.execPath, "-c", `import ${pythonWorkerImports.join(", ")}`]); + expect( + pythonWorkerImportError(process.execPath, run as typeof spawnSync), + ).toBeNull(); + expect(invocation).toEqual([ + process.execPath, + "-c", + `import ${pythonWorkerImports.join(", ")}`, + ]); expect( pythonWorkerImportError(process.execPath, (() => ({ status: 1, @@ -662,14 +838,26 @@ describe("Codex Cloud origin repair", () => { it("never overwrites a wrong or credential-bearing origin", () => { const wrong = temporaryGitRepository(); - expect(git(wrong, "remote", "add", "origin", "https://github.com/example/other.git").status).toBe(0); + expect( + git( + wrong, + "remote", + "add", + "origin", + "https://github.com/example/other.git", + ).status, + ).toBe(0); expect(() => ensureOriginRemote(wrong)).toThrow(/refusing to overwrite/); - expect(configuredOriginUrl(wrong)).toBe("https://github.com/example/other.git"); + expect(configuredOriginUrl(wrong)).toBe( + "https://github.com/example/other.git", + ); const credentialed = temporaryGitRepository(); const unsafe = "https://token-value@github.com/BigSimmo/Database.git"; expect(git(credentialed, "remote", "add", "origin", unsafe).status).toBe(0); - expect(() => ensureOriginRemote(credentialed)).toThrow(/embedded credentials/); + expect(() => ensureOriginRemote(credentialed)).toThrow( + /embedded credentials/, + ); expect(configuredOriginUrl(credentialed)).toBe(unsafe); }); }); From df31c705516214651e86f4b1cf7574bec34f27f0 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:20 +0800 Subject: [PATCH 04/29] fix(auth): prompt and constrain production Supabase MCP --- .codex/config.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.codex/config.toml b/.codex/config.toml index 1c16b5cb04..90047c7796 100644 --- a/.codex/config.toml +++ b/.codex/config.toml @@ -3,11 +3,11 @@ # # checked-in policy: # - enabled = false so ordinary/offline Codex sessions do not initialize providers -# - read-only Supabase tools use "auto"; write-capable Figma/Railway/Sentry tools use -# "writes" so reads stay frictionless while writes still require explicit approval +# - production Supabase uses "prompt" so every metadata/read operation requires +# explicit approval; write-capable Figma/Railway/Sentry tools use "writes" # - Paid API canaries (eval:rag, eval:retrieval:quality, eval:quality, verify:release, # test:live, check:supabase-project) still need explicit confirmation per AGENTS.md -# Runtime Cloud MCP remains `.mcp.json` (Railway + read-only Supabase only). +# Hosted Cloud tools come from installed OAuth plugins/connectors; `.mcp.json` is a cross-client template, not runtime proof. [mcp_servers.figma_cloud] url = "https://mcp.figma.com/mcp" @@ -15,11 +15,11 @@ enabled = false default_tools_approval_mode = "writes" [mcp_servers.supabase_cloud] -# Production project, read_only=true, approved feature groups only. +# Production project, read_only=true, docs/development metadata only; database and debugging groups are excluded. # OAuth scopes come from the hosted Supabase consent screen; do not broaden this URL. -url = "https://mcp.supabase.com/mcp?project_ref=sjrfecxgysukkwxsowpy&read_only=true&features=docs%2Cdatabase%2Cdebugging%2Cdevelopment" +url = "https://mcp.supabase.com/mcp?project_ref=sjrfecxgysukkwxsowpy&read_only=true&features=docs%2Cdevelopment" enabled = false -default_tools_approval_mode = "auto" +default_tools_approval_mode = "prompt" [mcp_servers.railway_cloud] url = "https://mcp.railway.com" From 3a88d30bdc81ea49b9a59426e4331d0e9489e11c Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:21 +0800 Subject: [PATCH 05/29] fix(auth): restrict Supabase MCP to metadata tools --- .mcp.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mcp.json b/.mcp.json index 8031e3a019..22e70a4073 100644 --- a/.mcp.json +++ b/.mcp.json @@ -6,7 +6,7 @@ }, "supabase": { "type": "http", - "url": "https://mcp.supabase.com/mcp?project_ref=sjrfecxgysukkwxsowpy&read_only=true&features=docs%2Cdatabase%2Cdebugging%2Cdevelopment" + "url": "https://mcp.supabase.com/mcp?project_ref=sjrfecxgysukkwxsowpy&read_only=true&features=docs%2Cdevelopment" } } } From ef91beb6f3fa8e455dedd55de193505a03d7035e Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:23 +0800 Subject: [PATCH 06/29] fix(auth): block PAT fallback inside Codex Cloud --- scripts/delete-codex-cloud-branch-with-pat.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/delete-codex-cloud-branch-with-pat.sh b/scripts/delete-codex-cloud-branch-with-pat.sh index 13125bb69d..4eeeb9bd2a 100644 --- a/scripts/delete-codex-cloud-branch-with-pat.sh +++ b/scripts/delete-codex-cloud-branch-with-pat.sh @@ -8,15 +8,15 @@ usage() { } fail() { - printf '[codex-cloud:github-pat] ERROR: %s\n' "$*" >&2 + printf '[operator:github-pat] ERROR: %s\n' "$*" >&2 exit 1 } [[ "$#" -eq 1 ]] || usage branch="$1" [[ "$branch" != -* ]] || fail "Refusing an option-like branch name." -[[ "${CODEX_CLOUD_ACCESS_PROFILE:-offline}" = "connected" ]] || fail "This emergency helper is connected-profile only." -[[ -n "${CODEX_CLOUD_GITHUB_PAT:-}" ]] || fail "CODEX_CLOUD_GITHUB_PAT is unavailable; add it only as a connected Cloud secret for this one operation." +[[ "${CODEX_CLOUD:-0}" != "1" ]] || fail "Cloud secrets are unavailable during the agent phase; use native Push, the GitHub connector, or GitHub UI." +[[ -n "${CODEX_CLOUD_GITHUB_PAT:-}" ]] || fail "CODEX_CLOUD_GITHUB_PAT is unavailable; this operator-only helper must run outside Codex Cloud." case "$branch" in main|master|develop|release|release/*|HEAD|HEAD/*|refs/*|*..*|*~*|*@\{*|*\\*|*\ *|"") @@ -47,4 +47,4 @@ chmod 0700 "$askpass" GIT_ASKPASS="$askpass" GIT_TERMINAL_PROMPT=0 \ git -c credential.helper= -c core.hooksPath=/dev/null push origin --delete "$branch" -printf '[codex-cloud:github-pat] PASS: deleted %s.\n' "$branch" +printf '[operator:github-pat] PASS: deleted %s.\n' "$branch" From 9816659d08e99346c756d497ca98e76b7515389d Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:25 +0800 Subject: [PATCH 07/29] docs(cloud): document host OAuth and fresh-task proof --- docs/codex-cloud.md | 109 ++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/docs/codex-cloud.md b/docs/codex-cloud.md index 3ba9c278c1..a750a37f46 100644 --- a/docs/codex-cloud.md +++ b/docs/codex-cloud.md @@ -47,7 +47,7 @@ profile before starting Node. It is idempotent and uses `nvm which` rather than `command -v node`, so maintenance cannot accidentally wrap an earlier wrapper. The setup command fails if the complete toolchain cannot be installed. It pins Railway CLI -`5.30.1` and Codex CLI `0.146.0`, both stable npm releases as reviewed on 2026-07-30. Railway's +`5.30.4` and Codex CLI `0.146.0`, both stable npm releases as reviewed on 2026-07-30. Railway's [official CLI guide](https://docs.railway.com/cli) supports global npm installation on Node 16+ (this repository uses Node 24). OpenAI's [official Codex CLI guide](https://learn.chatgpt.com/docs/codex/cli) supports Linux installation; @@ -139,23 +139,17 @@ approved operations. Some GitHub APIs, including review-thread or Actions manage may not be exposed in every Cloud task; use an approved GitHub-connected workflow for those operations or report the unavailable capability. Do not use shell credentials as a workaround. -GitHub connector permission is separate from credentials inside the agent shell. The connector -remains the default for repository, PR, review, and Actions work. For an explicitly authorised -connector gap, a fine-grained GitHub PAT may be stored only as the connected environment secret -`CODEX_CLOUD_GITHUB_PAT`. Scope it to the `BigSimmo/Database` repository, give it only the -least privilege needed for the named operation (for stale-branch deletion, **Contents: write**), -and set a short expiry. Never add it as an ordinary environment variable, print it, put it in a -remote URL, cache, profile, or repository file, or use it for provider access. The default and -ordinary connected profiles both scrub the name before Node work begins. - -The only tracked PAT helper is -`bash scripts/delete-codex-cloud-branch-with-pat.sh `. It refuses offline -mode, protected/invalid refs, and any origin other than the credential-free -`https://github.com/BigSimmo/Database.git`; it uses a temporary askpass program and deletes -only the specified branch and disables Git hooks for its PAT-bearing push. Use it only for the exact -user-authorised cleanup, then remove or -rotate the secret. If Cloud does not expose secrets to the requested task phase, the PAT is not a -usable workaround—report that platform limit rather than copying the token anywhere. +GitHub connector permission is separate from credentials inside the agent shell. The connector, +native Push control, and GitHub UI are the supported Cloud publication and cleanup paths. Cloud +secrets are setup-only, so `CODEX_CLOUD_GITHUB_PAT` cannot safely support an agent-phase helper; +the name is explicitly excluded by the shell policy and tested with the rest of the credential +inventory. + +`bash scripts/delete-codex-cloud-branch-with-pat.sh ` is retained only for +an explicitly authorised operator running outside Codex Cloud. It rejects `CODEX_CLOUD=1`, +protected/invalid refs, and any origin other than the credential-free +`https://github.com/BigSimmo/Database.git`. Never copy a PAT into a Cloud task, profile, checkout, +remote URL, cache, or log. Setup restores a missing `origin` to the credential-free URL `https://github.com/BigSimmo/Database.git`; it preserves an existing correct remote and fails @@ -240,16 +234,24 @@ The effective-environment check runs automatically when `CODEX_CLOUD=1`, includi `--runtime`, so a newly started agent shell cannot pass with stale modes. Its report prints only approved mode values and presence booleans. The runtime check additionally verifies Node/npm policy and installed-lock parity, pinned Railway/Codex CLIs, Deno 2, Python 3 and worker imports, -Tesseract, browser executables, local `main`/`origin/main`, the `BigSimmo/Database` origin -identity, offline credential absence when applicable, and obsolete npm proxy variable names +Tesseract, actual headless launch-and-close for Chromium/Firefox/WebKit, the Python requirements +fingerprint plus `pip check` and medspaCy/spaCy versions, the expected base commit as an ancestor +of HEAD, the `BigSimmo/Database` origin identity, offline credential absence when applicable, +and obsolete npm proxy variable names without reading or printing their values. MCP inspection emits server names, commands, and environment variable names only. -A repository cannot remove a variable already inherited by the top-level task process. The -command shims protect normal Node work, which is what the acceptance commands exercise. If a -fresh task still exposes a provider variable to a direct raw `/bin/bash`, Python, or another -native child before the generated profile is loaded, treat that as a Codex Cloud launcher defect -and report the variable name only; do not weaken the profile or reintroduce provider variables. +A repository cannot remove a variable already inherited by the top-level task process. Before +sourcing any profile or invoking node/npm in a fresh task, run: + +```bash +bash --noprofile --norc scripts/check-codex-cloud-raw-env.sh +``` + +The probe checks the complete provider-variable inventory and prints names only. A failure is a +launcher/environment defect; remove the variable in host environment settings and start another +fresh task. Passing only after sourcing the profile or using a command shim does not close the +raw-environment boundary. `npm run check:production-readiness` remains useful in the offline profile for local safeguards. Missing Supabase/OpenAI agent-phase credentials are reported as a provider capability gap and do @@ -271,32 +273,30 @@ and service metadata. Railway's remote MCP does not accept project tokens; retai only for explicitly approved local/operator workflows. The Supabase MCP entry is scoped to production project `sjrfecxgysukkwxsowpy`, forces -`read_only=true`, and exposes only documentation, database, debugging, and development feature -groups. Complete its browser OAuth flow for the organization containing `Clinical KB Database` +`read_only=true`, and exposes only documentation/development metadata tools. The database and +debugging groups are excluded so ordinary Cloud cannot execute SQL, read clinical rows, or inspect +production logs. Complete its browser OAuth flow for the organization containing `Clinical KB Database` and restart the client if tools do not appear. Schema writes, Edge Function deployment, branching, and storage mutations require a separately configured non-production project or branch; do not broaden the production entry. OpenAI generation, Supabase live data, Railway changes, hosted CI reruns, ingestion, deployment, and release workflows remain separate explicit actions. -Project `.codex/config.toml` is a second, project-scoped MCP template that trusted Codex -hosts load in addition to `$CODEX_HOME/config.toml` (where `setup-codex-cloud.sh` writes the -shell-environment policy). It is not inert documentation: Codex applies project-local -`.codex/config.toml` when the project is trusted. The tracked template lists Figma -(`https://mcp.figma.com/mcp`), Railway, read-only Supabase, and Sentry -(`https://mcp.sentry.dev/mcp`) as URL-only registrations with `enabled = false`. Ordinary/offline -sessions therefore do not initialize those providers. Production read-only Supabase uses -`default_tools_approval_mode = "auto"`; write-capable Figma, Railway, and Sentry use `"writes"` -so reads avoid per-tool prompts while writes still require explicit confirmation per AGENTS.md. -Paid API canaries also require explicit confirmation. Figma and Sentry OAuth credentials stay in -the host credential store — never in the tracked file. Runtime Cloud MCP allowlist remains -`.mcp.json` (Railway + read-only Supabase only). `npm run check:codex-cloud` validates both files. - -In a fresh connected Cloud session, run `npm run check:codex-cloud -- --environment` before any -provider call. The sanitized report must show `CODEX_CLOUD_ACCESS_PROFILE=connected`, every -provider environment variable as `present=false`, the credential-free `BigSimmo/Database` origin, -and only the hosted Railway and project-scoped read-only Supabase MCP metadata. This proves the -shell boundary and configured capabilities, not OAuth authorization. Then verify each explicitly -authorized provider with a read-only identity/status call and report only non-secret metadata. +Project `.codex/config.toml` is the checked-in Codex MCP template. Its URL-only entries +remain `enabled = false` so offline tasks do not initialize providers; an installed ChatGPT/Codex +plugin or host MCP layer must grant OAuth and expose the callable tools in a fresh connected task. +The root `.mcp.json` is a cross-client template and static allowlist only. It does not prove hosted +Cloud availability unless a plugin manifest or host explicitly imports it. + +Production Supabase stays project-scoped and `read_only=true`, with +`default_tools_approval_mode = "prompt"` so every production metadata/read call requires +confirmation. Do not use unrestricted SQL or query clinical rows. Railway, Figma, and Sentry +write-capable tools remain approval-gated. OAuth credentials stay in the host store—never the +tracked files or agent environment. + +In a fresh connected task, first run the raw-shell probe and repository acceptance, then inspect +the actual callable tool inventory. A configured URL or `enabled = false` template is not runtime +proof. Verify Railway and Supabase with read-only identity/project metadata calls and report only +non-secret status; if either tool is absent, the host integration is not activated. ### Connected-environment remediation checklist @@ -317,19 +317,20 @@ copying credentials into the checkout. `BigSimmo/Database` with repository write access. Complete Railway OAuth only for workspace `bigsimmo's Projects` and project `Database` (`5deaad0b-675a-4c13-978e-5ca2b5b877f9`). Complete Supabase OAuth only for the organization containing `Clinical KB Database`; retain project ref - `sjrfecxgysukkwxsowpy`, `read_only=true`, and the existing feature allowlist. Do not broaden the + `sjrfecxgysukkwxsowpy`, `read_only=true`, and the docs/development-only feature allowlist. Do not broaden the production Supabase MCP to write access. Enable Figma or Sentry only for a task that names that provider; their write-capable tools remain approval-gated. 3. **Start a fresh task.** OAuth tools and environment values are fixed when the task starts. A setup rerun inside an already-running offline task can validate a generated connected profile, but it cannot inject host MCP tools or retroactively grant OAuth. Restart the MCP client or open a new task after consent. -4. **Prove the shell boundary before providers.** Run `npm run check:codex-cloud`, +4. **Prove the shell boundary before providers.** First run the direct raw-shell command above + before profiles or command shims. Then run `npm run check:codex-cloud`, `npm run check:codex-cloud -- --runtime`, `npm run check:runtime`, and - `npm run check:installed-lock-parity`. Require the two Cloud PASS lines, correct runtime and - lock parity, `CODEX_CLOUD_ACCESS_PROFILE=connected`, no provider variable reported present, a - credential-free matching origin, and the expected MCP metadata. A connected label alone is not - provider proof. + `npm run check:installed-lock-parity`. Set `CODEX_CLOUD_EXPECTED_BASE_SHA` to the intended + merged base commit. Require the raw PASS line, both Cloud PASS lines, correct runtime/lock + parity, `CODEX_CLOUD_ACCESS_PROFILE=connected`, no provider variable reported present, and a + credential-free matching origin. Repository MCP metadata is configuration evidence only. 5. **Prove each provider read-only.** Use the tools exposed by the fresh host session, not shell tokens. For GitHub, read repository metadata and confirm `BigSimmo/Database` plus the intended identity. For Railway, read workspace/project/service metadata and confirm the IDs above without @@ -337,8 +338,8 @@ copying credentials into the checkout. without querying clinical row contents. Report only non-secret identity and status metadata. OpenAI has no generic connected-profile credential: leave `RAG_PROVIDER_MODE=offline` until a separately approved paid canary or protected workflow supplies its own credential boundary. -6. **Publish a task branch safely.** Work on a task-specific non-protected branch. Commit and format - the intended repository change, publish that exact existing commit through the native GitHub +6. **Publish a task branch safely.** Work on a task-specific non-protected branch. Format, stage, and commit + the intended repository change, then publish that exact existing commit through the native GitHub connector/Cloud PR workflow, and verify the remote branch and PR link. If shell Git authentication is intentionally available, `git push --set-upstream origin ` is acceptable after confirming the credential-free origin; otherwise a failed `git ls-remote` is From ddfdcf91f11d07e93d9a2c1a806fb205a3b3d91b Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:29 +0800 Subject: [PATCH 08/29] docs(cloud): index raw environment acceptance probe --- docs/scripts-index.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/scripts-index.md b/docs/scripts-index.md index e4d83fe500..c87312e23c 100644 --- a/docs/scripts-index.md +++ b/docs/scripts-index.md @@ -16,25 +16,25 @@ migration has shipped (see `docs/maturity-backlog-workorders.md` L1). ## Runner & guard infrastructure [infra] -| Script | Role | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `run-heavy.mjs` | Acquires shared/exclusive cross-worktree leases (`test-run-lock.mjs`) so focused checks can overlap safely | -| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | -| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | -| `design-sync.mjs`, `capture-mockup-screenshots.mjs` | Local design-sync CSS prep (`node scripts/design-sync.mjs`) + redesign "current" PNG pack after ensure (`node scripts/capture-mockup-screenshots.mjs`) | -| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | -| `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, `install-codex-cloud-command-shims.sh`, `delete-codex-cloud-branch-with-pat.sh`, `check-codex-cloud-setup.mjs`, `ensure-codex-cloud-git-remote.mjs` | Reproducible Codex Cloud toolchain/profile setup, profile-loading Node command shims, a connected-only guarded PAT branch-deletion exception, sanitized acceptance, and safe credential-free `origin` repair | -| `ci-change-scope.mjs`, `ci-triage.mjs`, `pr-policy.mjs`, `pr-mergeability.mjs` | CI change classification + PR policy + conflict signal (self-tested via `check:ci-scope`/`check:ci-triage`/`check:pr-policy`/`check:pr-mergeability`) | -| `check-outstanding-issues.mjs`, `check-pr-mergeability-workflow.mjs` | Outstanding-issues ID/marker/no-driver guard + PR mergeability workflow contract | -| `outstanding-issues.mjs` | Writer for `docs/outstanding-issues.md` (`issues:add` / `issues:done` / `issues:update`) — allocates the id, picks the right table, escapes `\|`, and re-runs the guard on its own output. Never hand-edit that file, as with `ledger:append` | -| `check-installed-lock-parity.mjs`, `phone-chrome-plan.mjs`, `verify-phone-chrome.mjs`, `playwright-browser-preflight.mjs` | Lock-trust preflight, change-scoped phone contracts, and Playwright browser-binary preflight before build | -| `final-merge-audit.mjs` | Fail-closed local merge-tree audit; explicit provider mode adds PR/check/thread/tree/deployment proof | -| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | -| `test-focused.mjs`, `test-run-selection.mjs`, `test-cache-path.mjs`, `test-environment.mjs` | Backs `npm run test:focused` — change-scoped selection, cache pathing, env setup; fails closed for deleted files and test infrastructure | -| `primary-checkout-lease.mjs`, `test-run-lock.mjs`, `clean-worktree.mjs` | Cross-worktree lease arbitration for the primary checkout, plus worktree cleanup | -| `resolve-tsx-cli.mjs`, `register-server-only.mjs`, `enable-server-only-stub.mjs` | tsx CLI resolution and `server-only` import shims | -| `check-format-changed.mjs`, `check-base-freshness.mjs`, `check-local-presence.mjs` | Push-time helpers behind `guard-push.mjs`: changed-file formatting, stale-base and local-presence checks | -| `yaml-contract.mjs`, `sensitive-text.mjs`, `design-system-contract-utils.mjs` | Shared parsing/redaction/contract helpers used by the gates | +| Script | Role | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `run-heavy.mjs` | Acquires shared/exclusive cross-worktree leases (`test-run-lock.mjs`) so focused checks can overlap safely | +| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | +| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | +| `design-sync.mjs`, `capture-mockup-screenshots.mjs` | Local design-sync CSS prep (`node scripts/design-sync.mjs`) + redesign "current" PNG pack after ensure (`node scripts/capture-mockup-screenshots.mjs`) | +| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | +| `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, `install-codex-cloud-command-shims.sh`, `check-codex-cloud-raw-env.sh`, `delete-codex-cloud-branch-with-pat.sh`, `check-codex-cloud-setup.mjs`, `ensure-codex-cloud-git-remote.mjs` | Reproducible Codex Cloud toolchain/profile setup, profile-loading Node command shims, a pre-profile name-only credential probe, an operator-only non-Cloud PAT branch-deletion helper, sanitized acceptance, and safe credential-free `origin` repair | +| `ci-change-scope.mjs`, `ci-triage.mjs`, `pr-policy.mjs`, `pr-mergeability.mjs` | CI change classification + PR policy + conflict signal (self-tested via `check:ci-scope`/`check:ci-triage`/`check:pr-policy`/`check:pr-mergeability`) | +| `check-outstanding-issues.mjs`, `check-pr-mergeability-workflow.mjs` | Outstanding-issues ID/marker/no-driver guard + PR mergeability workflow contract | +| `outstanding-issues.mjs` | Writer for `docs/outstanding-issues.md` (`issues:add` / `issues:done` / `issues:update`) — allocates the id, picks the right table, escapes `\|`, and re-runs the guard on its own output. Never hand-edit that file, as with `ledger:append` | +| `check-installed-lock-parity.mjs`, `phone-chrome-plan.mjs`, `verify-phone-chrome.mjs`, `playwright-browser-preflight.mjs` | Lock-trust preflight, change-scoped phone contracts, and Playwright browser-binary preflight before build | +| `final-merge-audit.mjs` | Fail-closed local merge-tree audit; explicit provider mode adds PR/check/thread/tree/deployment proof | +| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | +| `test-focused.mjs`, `test-run-selection.mjs`, `test-cache-path.mjs`, `test-environment.mjs` | Backs `npm run test:focused` — change-scoped selection, cache pathing, env setup; fails closed for deleted files and test infrastructure | +| `primary-checkout-lease.mjs`, `test-run-lock.mjs`, `clean-worktree.mjs` | Cross-worktree lease arbitration for the primary checkout, plus worktree cleanup | +| `resolve-tsx-cli.mjs`, `register-server-only.mjs`, `enable-server-only-stub.mjs` | tsx CLI resolution and `server-only` import shims | +| `check-format-changed.mjs`, `check-base-freshness.mjs`, `check-local-presence.mjs` | Push-time helpers behind `guard-push.mjs`: changed-file formatting, stale-base and local-presence checks | +| `yaml-contract.mjs`, `sensitive-text.mjs`, `design-system-contract-utils.mjs` | Shared parsing/redaction/contract helpers used by the gates | ## Verification gates [live] From def06318b8719a1f1b9284b9ee16d2e4b3a25a39 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:34:30 +0800 Subject: [PATCH 09/29] feat(cloud): add pre-profile credential boundary probe --- scripts/check-codex-cloud-raw-env.sh | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 scripts/check-codex-cloud-raw-env.sh diff --git a/scripts/check-codex-cloud-raw-env.sh b/scripts/check-codex-cloud-raw-env.sh new file mode 100644 index 0000000000..cd37bbe435 --- /dev/null +++ b/scripts/check-codex-cloud-raw-env.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +# Run this directly with a pristine shell before sourcing profiles or invoking +# node/npm shims. It reports names and presence only, never values. +provider_variables=( + OPENAI_API_KEY OPENAI_ORG_ID OPENAI_PROJECT_ID OPENAI_BASE_URL + NEXT_PUBLIC_SUPABASE_URL NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY NEXT_PUBLIC_SUPABASE_ANON_KEY + SUPABASE_URL SUPABASE_ANON_KEY SUPABASE_PUBLISHABLE_KEY SUPABASE_SECRET_KEY + SUPABASE_SERVICE_ROLE_KEY SUPABASE_ACCESS_TOKEN SUPABASE_DB_URL + SUPABASE_PROJECT_REF SUPABASE_PROJECT_NAME SUPABASE_STAGING_PROJECT_REF SUPABASE_STAGING_PROJECT_NAME + DATABASE_URL POSTGRES_PASSWORD CROSS_TENANT_SERVICE_ROLE_KEY + RAILWAY_API_TOKEN RAILWAY_TOKEN + GH_TOKEN GITHUB_TOKEN CODEX_CLOUD_GITHUB_PAT GITLAB_TOKEN GLAB_TOKEN CODEX_TRIGGER_TOKEN + HEALTH_DEEP_PROBE_SECRET INDEXING_V3_AGENT_SECRET + E2E_AUTH_ENABLED E2E_USER_EMAIL E2E_USER_PASSWORD ALLOW_PROVIDER_TESTS +) + +present=() +for name in "${provider_variables[@]}"; do + if [[ -n "${!name:-}" ]]; then + present+=("$name") + fi +done + +if (( ${#present[@]} > 0 )); then + printf '[Codex Cloud Raw Env] FAIL: inherited provider variable names: %s\n' "${present[*]}" >&2 + exit 1 +fi + +printf '[Codex Cloud Raw Env] PASS: no provider variables are inherited by the raw task shell.\n' From 037b950d21237b459986ff46c9bdc03e62279122 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:36:28 +0800 Subject: [PATCH 10/29] style(cloud): preserve repository formatting --- scripts/check-codex-cloud-setup.mjs | 532 +++++++--------------------- 1 file changed, 123 insertions(+), 409 deletions(-) diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index a039a3cd00..cde48f6174 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -13,10 +13,7 @@ import { } from "./ensure-codex-cloud-git-remote.mjs"; import { providerEnvironmentKeys } from "./test-environment.mjs"; -const repoRoot = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - "..", -); +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); export const expectedCloudCliVersions = Object.freeze({ railway: "5.30.4", @@ -52,11 +49,7 @@ export const expectedCodexProjectMcpServers = Object.freeze({ }), }); -const allowedCodexProjectMcpKeys = Object.freeze([ - "default_tools_approval_mode", - "enabled", - "url", -]); +const allowedCodexProjectMcpKeys = Object.freeze(["default_tools_approval_mode", "enabled", "url"]); const forbiddenCodexProjectMcpKeys = Object.freeze([ "bearer_token_env_var", @@ -72,10 +65,7 @@ function parseTomlScalar(value) { const trimmed = value.trim(); if (trimmed === "true") return true; if (trimmed === "false") return false; - if ( - (trimmed.startsWith('"') && trimmed.endsWith('"')) || - (trimmed.startsWith("'") && trimmed.endsWith("'")) - ) { + if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) { return trimmed.slice(1, -1); } return trimmed; @@ -117,9 +107,7 @@ export function parseCodexProjectMcpServers(text) { continue; } if (!current) { - const dotted = line.match( - /^mcp_servers\.([A-Za-z0-9_-]+)\.([A-Za-z0-9_.-]+)\s*=\s*(.+)$/, - ); + const dotted = line.match(/^mcp_servers\.([A-Za-z0-9_-]+)\.([A-Za-z0-9_.-]+)\s*=\s*(.+)$/); if (dotted) { servers[dotted[1]] ??= {}; servers[dotted[1]][dotted[2]] = parseTomlScalar(dotted[3]); @@ -140,38 +128,22 @@ export function parseCodexProjectMcpServers(text) { function validateSupabaseMcpUrl(urlString, label, errors) { try { const url = new URL(urlString); - if ( - `${url.origin}${url.pathname}` !== expectedMcpConfiguration.supabaseUrl - ) { + if (`${url.origin}${url.pathname}` !== expectedMcpConfiguration.supabaseUrl) { errors.push(`${label} must use the official hosted endpoint.`); } - if ( - url.searchParams.get("project_ref") !== - expectedMcpConfiguration.supabaseProjectRef - ) { + if (url.searchParams.get("project_ref") !== expectedMcpConfiguration.supabaseProjectRef) { errors.push(`${label} must be scoped to the expected project.`); } if (url.searchParams.get("read_only") !== "true") { errors.push(`${label} must keep the production project read-only.`); } const queryNames = [...url.searchParams.keys()].sort(); - if ( - JSON.stringify(queryNames) !== - JSON.stringify(["features", "project_ref", "read_only"]) - ) { + if (JSON.stringify(queryNames) !== JSON.stringify(["features", "project_ref", "read_only"])) { errors.push(`${label} must not include additional query parameters.`); } - const features = (url.searchParams.get("features") ?? "") - .split(",") - .filter(Boolean) - .sort(); - if ( - JSON.stringify(features) !== - JSON.stringify(expectedMcpConfiguration.supabaseFeatures) - ) { - errors.push( - `${label} must expose only the approved read-only feature groups.`, - ); + const features = (url.searchParams.get("features") ?? "").split(",").filter(Boolean).sort(); + if (JSON.stringify(features) !== JSON.stringify(expectedMcpConfiguration.supabaseFeatures)) { + errors.push(`${label} must expose only the approved read-only feature groups.`); } } catch { errors.push(`${label} URL must be valid.`); @@ -186,14 +158,11 @@ function validateSupabaseMcpUrl(urlString, label, errors) { */ export function validateCodexProjectMcpConfiguration(text) { const errors = []; - const { servers, nestedServers, unparsedServers } = - parseCodexProjectMcpServers(text); + const { servers, nestedServers, unparsedServers } = parseCodexProjectMcpServers(text); const expectedNames = Object.keys(expectedCodexProjectMcpServers).sort(); const actualNames = Object.keys(servers).sort(); if (JSON.stringify(actualNames) !== JSON.stringify(expectedNames)) { - errors.push( - `.codex/config.toml must register exactly these MCP servers: ${expectedNames.join(", ")}.`, - ); + errors.push(`.codex/config.toml must register exactly these MCP servers: ${expectedNames.join(", ")}.`); } for (const name of expectedNames) { @@ -203,33 +172,25 @@ export function validateCodexProjectMcpConfiguration(text) { const expected = expectedCodexProjectMcpServers[name]; if (server.enabled !== false) { - errors.push( - `${label} must set enabled = false (host/connected layers opt in).`, - ); + errors.push(`${label} must set enabled = false (host/connected layers opt in).`); } if (server.default_tools_approval_mode !== expected.approvalMode) { const reason = expected.approvalMode === "writes" ? "write-capable tools require explicit approval" : "the production server is constrained read-only"; - errors.push( - `${label} must set default_tools_approval_mode = "${expected.approvalMode}" because ${reason}.`, - ); + errors.push(`${label} must set default_tools_approval_mode = "${expected.approvalMode}" because ${reason}.`); } for (const key of Object.keys(server)) { const rootKey = key.split(".")[0]; if (forbiddenCodexProjectMcpKeys.includes(rootKey)) { - errors.push( - `${label} must not embed ${rootKey}; keep OAuth credentials in the host store.`, - ); + errors.push(`${label} must not embed ${rootKey}; keep OAuth credentials in the host store.`); } else if (!allowedCodexProjectMcpKeys.includes(key)) { errors.push(`${label} must be URL-only; unsupported key ${key}.`); } } if (nestedServers.has(name)) { - errors.push( - `${label} must not declare nested tool override tables in the shared project config.`, - ); + errors.push(`${label} must not declare nested tool override tables in the shared project config.`); } if (unparsedServers.has(name)) { errors.push(`${label} contains unsupported or unparsed entries.`); @@ -281,11 +242,9 @@ function exactVersionPattern(version) { * @returns {string[]} */ export function obsoleteNpmProxyVariables(env = process.env) { - return [ - "npm_config_http_proxy", - "npm_config_https_proxy", - "npm_config_proxy", - ].filter((name) => Object.hasOwn(env, name) && Boolean(env[name])); + return ["npm_config_http_proxy", "npm_config_https_proxy", "npm_config_proxy"].filter( + (name) => Object.hasOwn(env, name) && Boolean(env[name]), + ); } /** @@ -293,16 +252,13 @@ export function obsoleteNpmProxyVariables(env = process.env) { * @returns {string[]} */ export function configuredProviderCredentialNames(env = process.env) { - return providerCredentialVariables.filter( - (name) => Object.hasOwn(env, name) && Boolean(env[name]), - ); + return providerCredentialVariables.filter((name) => Object.hasOwn(env, name) && Boolean(env[name])); } /** @param {NodeJS.ProcessEnv | Record} [env] */ export function validateCodexCloudEnvironment(env = process.env) { const errors = []; - if (env.CODEX_CLOUD !== "1") - errors.push("CODEX_CLOUD must be 1 in the Cloud agent shell."); + if (env.CODEX_CLOUD !== "1") errors.push("CODEX_CLOUD must be 1 in the Cloud agent shell."); const accessProfile = env.CODEX_CLOUD_ACCESS_PROFILE ?? "offline"; if (!["offline", "connected"].includes(accessProfile)) { @@ -323,8 +279,7 @@ export function validateCodexCloudEnvironment(env = process.env) { NEXT_PUBLIC_DEMO_MODE: "true", PLAYWRIGHT_OFFLINE_MODE: "true", })) { - if (env[name] !== expected) - errors.push(`${name} must be ${expected} in offline mode.`); + if (env[name] !== expected) errors.push(`${name} must be ${expected} in offline mode.`); } return errors; } @@ -334,8 +289,7 @@ export function validateCodexCloudEnvironment(env = process.env) { NEXT_PUBLIC_DEMO_MODE: ["true", "false"], PLAYWRIGHT_OFFLINE_MODE: ["true", "false"], })) { - if (!allowed.includes(env[name])) - errors.push(`${name} must be an approved value in connected mode.`); + if (!allowed.includes(env[name])) errors.push(`${name} must be an approved value in connected mode.`); } return errors; } @@ -366,19 +320,12 @@ export function parseMcpServerMetadata(text) { } return { name, - type: - typeof server?.type === "string" - ? server.type - : typeof server?.command === "string" - ? "stdio" - : "invalid", + type: typeof server?.type === "string" ? server.type : typeof server?.command === "string" ? "stdio" : "invalid", command: typeof server?.command === "string" ? server.command : "none", endpoint, queryNames, environmentNames: - server?.env && - !Array.isArray(server.env) && - typeof server.env === "object" + server?.env && !Array.isArray(server.env) && typeof server.env === "object" ? Object.keys(server.env).sort() : [], }; @@ -391,9 +338,7 @@ export function validateMcpConfiguration(text) { try { parsed = JSON.parse(text); } catch (error) { - return [ - `.mcp.json is invalid JSON: ${error instanceof Error ? error.message : String(error)}`, - ]; + return [`.mcp.json is invalid JSON: ${error instanceof Error ? error.message : String(error)}`]; } const servers = parsed?.mcpServers; if (!servers || Array.isArray(servers) || typeof servers !== "object") { @@ -402,26 +347,16 @@ export function validateMcpConfiguration(text) { const serverNames = Object.keys(servers).sort(); if (JSON.stringify(serverNames) !== JSON.stringify(["railway", "supabase"])) { - errors.push( - "Cloud MCP configuration must contain only Railway and Supabase.", - ); + errors.push("Cloud MCP configuration must contain only Railway and Supabase."); } for (const name of ["railway", "supabase"]) { - if ( - servers[name]?.env !== undefined || - servers[name]?.headers !== undefined - ) { - errors.push( - `${name} MCP must use hosted OAuth without embedded environment variables or headers.`, - ); + if (servers[name]?.env !== undefined || servers[name]?.headers !== undefined) { + errors.push(`${name} MCP must use hosted OAuth without embedded environment variables or headers.`); } } const railway = servers.railway; - if ( - railway?.type !== "http" || - railway?.url !== expectedMcpConfiguration.railwayUrl.replace(/\/$/, "") - ) { + if (railway?.type !== "http" || railway?.url !== expectedMcpConfiguration.railwayUrl.replace(/\/$/, "")) { errors.push("Railway MCP must use the hosted OAuth endpoint."); } @@ -432,38 +367,22 @@ export function validateMcpConfiguration(text) { } try { const url = new URL(supabase.url); - if ( - `${url.origin}${url.pathname}` !== expectedMcpConfiguration.supabaseUrl - ) { + if (`${url.origin}${url.pathname}` !== expectedMcpConfiguration.supabaseUrl) { errors.push("Supabase MCP must use the official hosted endpoint."); } - if ( - url.searchParams.get("project_ref") !== - expectedMcpConfiguration.supabaseProjectRef - ) { + if (url.searchParams.get("project_ref") !== expectedMcpConfiguration.supabaseProjectRef) { errors.push("Supabase MCP must be scoped to the expected project."); } if (url.searchParams.get("read_only") !== "true") { errors.push("Supabase MCP must keep the production project read-only."); } const queryNames = [...url.searchParams.keys()].sort(); - if ( - JSON.stringify(queryNames) !== - JSON.stringify(["features", "project_ref", "read_only"]) - ) { + if (JSON.stringify(queryNames) !== JSON.stringify(["features", "project_ref", "read_only"])) { errors.push("Supabase MCP must not include additional query parameters."); } - const features = (url.searchParams.get("features") ?? "") - .split(",") - .filter(Boolean) - .sort(); - if ( - JSON.stringify(features) !== - JSON.stringify(expectedMcpConfiguration.supabaseFeatures) - ) { - errors.push( - "Supabase MCP must expose only the approved read-only feature groups.", - ); + const features = (url.searchParams.get("features") ?? "").split(",").filter(Boolean).sort(); + if (JSON.stringify(features) !== JSON.stringify(expectedMcpConfiguration.supabaseFeatures)) { + errors.push("Supabase MCP must expose only the approved read-only feature groups."); } } catch { errors.push("Supabase MCP URL must be valid."); @@ -475,11 +394,7 @@ function approvedModeValue(value, allowed) { return allowed.includes(value) ? value : "invalid"; } -export function codexCloudValidationScope({ - runtime = false, - environment = false, - browserInstallSkipped = false, -}) { +export function codexCloudValidationScope({ runtime = false, environment = false, browserInstallSkipped = false }) { if (runtime && browserInstallSkipped) { return "static, environment, and source-only runtime (browser validation skipped)"; } @@ -489,25 +404,16 @@ export function codexCloudValidationScope({ } function commandAvailable(command) { - return ( - spawnSync(command, ["--version"], { encoding: "utf8", shell: false }) - .status === 0 - ); + return spawnSync(command, ["--version"], { encoding: "utf8", shell: false }).status === 0; } /** @param {NodeJS.ProcessEnv | Record} [env] */ export function sanitizedCloudCapabilityLines(env = process.env, options = {}) { const origin = options.origin ?? inspectOriginRemote(repoRoot); - const railway = railwayReadCapability( - env, - options.railwayCliAvailable ?? commandAvailable("railway"), - ); - const codexCliAvailable = - options.codexCliAvailable ?? commandAvailable("codex"); - const safeGitHelper = - options.safeGitHelper ?? hasSafeGitHubCredentialHelper(repoRoot); - const mcpServers = - options.mcpServers ?? parseMcpServerMetadata(read(".mcp.json")); + const railway = railwayReadCapability(env, options.railwayCliAvailable ?? commandAvailable("railway")); + const codexCliAvailable = options.codexCliAvailable ?? commandAvailable("codex"); + const safeGitHelper = options.safeGitHelper ?? hasSafeGitHubCredentialHelper(repoRoot); + const mcpServers = options.mcpServers ?? parseMcpServerMetadata(read(".mcp.json")); const lines = [ `CODEX_CLOUD=${approvedModeValue(env.CODEX_CLOUD, ["1"])}`, `CODEX_CLOUD_ACCESS_PROFILE=${approvedModeValue(env.CODEX_CLOUD_ACCESS_PROFILE ?? "offline", ["offline", "connected"])}`, @@ -515,19 +421,12 @@ export function sanitizedCloudCapabilityLines(env = process.env, options = {}) { `NEXT_PUBLIC_DEMO_MODE=${approvedModeValue(env.NEXT_PUBLIC_DEMO_MODE, ["true", "false"])}`, `PLAYWRIGHT_OFFLINE_MODE=${approvedModeValue(env.PLAYWRIGHT_OFFLINE_MODE, ["true", "false"])}`, ]; - for (const name of providerCredentialVariables) - lines.push(`${name}.present=${Boolean(env[name])}`); + for (const name of providerCredentialVariables) lines.push(`${name}.present=${Boolean(env[name])}`); lines.push(`railway.cli_available=${railway.cliAvailable}`); - lines.push( - `railway.dedicated_credential_present=${railway.dedicatedCredentialPresent}`, - ); - lines.push( - `railway.project_credential_present=${railway.projectCredentialPresent}`, - ); + lines.push(`railway.dedicated_credential_present=${railway.dedicatedCredentialPresent}`); + lines.push(`railway.project_credential_present=${railway.projectCredentialPresent}`); lines.push(`railway.cli_token_auth_ready=${railway.cliTokenAuthReady}`); - lines.push( - "mcp.runtime_tool_inventory=host-provided-unverified-by-repository", - ); + lines.push("mcp.runtime_tool_inventory=host-provided-unverified-by-repository"); lines.push(`codex.cli_available=${codexCliAvailable}`); lines.push(pythonWorkerVersionLine(env.CODEX_CLOUD_OCR_PYTHON)); lines.push(`git.origin_configured=${origin.configured}`); @@ -552,14 +451,10 @@ export function localGitBaseline(root = process.cwd(), env = process.env) { if (result.status === 0) return ref; } if (env.CODEX_CLOUD === "1") { - const result = spawnSync( - "git", - ["rev-parse", "--verify", "--quiet", "HEAD"], - { - cwd: root, - stdio: "ignore", - }, - ); + const result = spawnSync("git", ["rev-parse", "--verify", "--quiet", "HEAD"], { + cwd: root, + stdio: "ignore", + }); if (result.status === 0) return "HEAD"; } return null; @@ -567,10 +462,7 @@ export function localGitBaseline(root = process.cwd(), env = process.env) { export function executableFile(filePath) { try { - return ( - statSync(filePath).isFile() && - (accessSync(filePath, constants.X_OK), true) - ); + return statSync(filePath).isFile() && (accessSync(filePath, constants.X_OK), true); } catch { return false; } @@ -587,9 +479,7 @@ export async function playwrightBrowserErrors(browserTypes, timeout = 15_000) { try { browser = await browserType.launch({ headless: true, timeout }); } catch (error) { - errors.push( - `${name} browser launch failed: ${error instanceof Error ? error.message : String(error)}`, - ); + errors.push(`${name} browser launch failed: ${error instanceof Error ? error.message : String(error)}`); } finally { await browser?.close(); } @@ -612,14 +502,10 @@ export function pythonWorkerImportError(pythonCommand, run = spawnSync) { if (!pythonCommand || !executableFile(pythonCommand)) { return "The configured Codex Cloud OCR Python executable is unavailable."; } - const result = run( - pythonCommand, - ["-c", `import ${pythonWorkerImports.join(", ")}`], - { - encoding: "utf8", - shell: false, - }, - ); + const result = run(pythonCommand, ["-c", `import ${pythonWorkerImports.join(", ")}`], { + encoding: "utf8", + shell: false, + }); if (result.status === 0) return null; return `Python worker imports failed: ${pythonWorkerImports.join(", ")}.`; } @@ -633,32 +519,19 @@ export function pythonWorkerDependencyErrors(pythonCommand, run = spawnSync) { encoding: "utf8", shell: false, }); - if (pipCheck.status !== 0) - errors.push( - "Python worker dependency conflicts were reported by pip check.", - ); + if (pipCheck.status !== 0) errors.push("Python worker dependency conflicts were reported by pip check."); - const requirements = readFileSync( - path.join(repoRoot, "worker/python/requirements-cloud.txt"), - ); + const requirements = readFileSync(path.join(repoRoot, "worker/python/requirements-cloud.txt")); const expectedHash = createHash("sha256").update(requirements).digest("hex"); - const markerPath = path.resolve( - path.dirname(pythonCommand), - "..", - ".requirements-cloud.sha256", - ); + const markerPath = path.resolve(path.dirname(pythonCommand), "..", ".requirements-cloud.sha256"); let installedHash = ""; try { installedHash = readFileSync(markerPath, "utf8").trim(); } catch { - errors.push( - "Python worker requirements fingerprint is missing; rerun Cloud setup.", - ); + errors.push("Python worker requirements fingerprint is missing; rerun Cloud setup."); } if (installedHash && installedHash !== expectedHash) { - errors.push( - "Python worker requirements fingerprint is stale; rerun Cloud setup.", - ); + errors.push("Python worker requirements fingerprint is stale; rerun Cloud setup."); } const versions = run( @@ -669,14 +542,12 @@ export function pythonWorkerDependencyErrors(pythonCommand, run = spawnSync) { ], { encoding: "utf8", shell: false }, ); - if (versions.status !== 0) - errors.push("Python worker medspacy/spacy version reporting failed."); + if (versions.status !== 0) errors.push("Python worker medspacy/spacy version reporting failed."); return errors; } export function pythonWorkerVersionLine(pythonCommand, run = spawnSync) { - if (!pythonCommand || !executableFile(pythonCommand)) - return "python.worker_versions=unavailable"; + if (!pythonCommand || !executableFile(pythonCommand)) return "python.worker_versions=unavailable"; const result = run( pythonCommand, [ @@ -710,65 +581,29 @@ export function validateCodexCloudSetup() { const codexProjectConfig = read(".codex/config.toml"); if (packageJson.engines?.node !== `${nodeVersion}.x`) { - errors.push( - `package.json engines.node must match .node-version (${nodeVersion}.x).`, - ); + errors.push(`package.json engines.node must match .node-version (${nodeVersion}.x).`); } - if (packageJson.engines?.npm !== "11.x") - errors.push("package.json must require npm 11.x."); + if (packageJson.engines?.npm !== "11.x") errors.push("package.json must require npm 11.x."); if (!String(packageJson.packageManager ?? "").startsWith("npm@11.")) { errors.push("package.json packageManager must pin npm 11.x."); } - if (nvmVersion !== nodeVersion) - errors.push(".nvmrc and .node-version must match."); - requireMatch( - errors, - gitignore, - /^\/error\.log$/m, - "Codex Cloud diagnostic error.log must stay ignored.", - ); + if (nvmVersion !== nodeVersion) errors.push(".nvmrc and .node-version must match."); + requireMatch(errors, gitignore, /^\/error\.log$/m, "Codex Cloud diagnostic error.log must stay ignored."); for (const [pattern, message] of [ - [ - /npm ci --include=dev/, - "Cloud setup must install the exact lockfile with dev dependencies.", - ], + [/npm ci --include=dev/, "Cloud setup must install the exact lockfile with dev dependencies."], [/deno@2/, "Cloud setup must install Deno 2.x."], - [ - /worker\/python\/requirements-cloud\.txt/, - "Cloud setup must install the Python 3.12 Cloud worker lock.", - ], - [ - /CODEX_CLOUD_OCR_PYTHON/, - "Cloud setup must expose the Python worker environment.", - ], - [ - /playwright install --with-deps chromium firefox webkit/, - "Cloud setup must install every browser.", - ], - [ - /CODEX_CLOUD_ACCESS_PROFILE/, - "Cloud setup must support explicit access profiles.", - ], - [ - /RAG_PROVIDER_MODE=offline/, - "Cloud setup must default RAG to offline mode.", - ], - [ - /unset OPENAI_API_KEY/, - "Cloud setup must remove raw provider variables from the agent shell.", - ], + [/worker\/python\/requirements-cloud\.txt/, "Cloud setup must install the Python 3.12 Cloud worker lock."], + [/CODEX_CLOUD_OCR_PYTHON/, "Cloud setup must expose the Python worker environment."], + [/playwright install --with-deps chromium firefox webkit/, "Cloud setup must install every browser."], + [/CODEX_CLOUD_ACCESS_PROFILE/, "Cloud setup must support explicit access profiles."], + [/RAG_PROVIDER_MODE=offline/, "Cloud setup must default RAG to offline mode."], + [/unset OPENAI_API_KEY/, "Cloud setup must remove raw provider variables from the agent shell."], [/\.bash_profile/, "Cloud setup must cover Bash login-profile precedence."], [/@railway\/cli/, "Cloud setup must install the Railway CLI."], [/@openai\/codex/, "Cloud setup must install the Codex CLI."], - [ - /ensure-codex-cloud-git-remote\.mjs/, - "Cloud setup must restore a safe origin remote.", - ], - [ - /check:codex-cloud -- --runtime/, - "Cloud setup must run runtime acceptance.", - ], + [/ensure-codex-cloud-git-remote\.mjs/, "Cloud setup must restore a safe origin remote."], + [/check:codex-cloud -- --runtime/, "Cloud setup must run runtime acceptance."], [ /BEGIN clinical-kb-codex-cloud shell policy/, "Cloud setup must write the Codex shell policy inside a managed marker block.", @@ -777,68 +612,36 @@ export function validateCodexCloudSetup() { /Unmanaged \[shell_environment_policy\] table found/, "Cloud setup must reject unmanaged shell_environment_policy tables before rewriting config.toml.", ], - [ - /Incomplete managed shell policy block/, - "Cloud setup must reject incomplete managed shell policy marker blocks.", - ], + [/Incomplete managed shell policy block/, "Cloud setup must reject incomplete managed shell policy marker blocks."], [ /export RAG_PROVIDER_MODE="\$\{rag_provider_mode\}"/, "Cloud setup must pin the connected-mode retrieval value at setup time.", ], - [ - /inherit = "all"/, - "Cloud setup must configure Codex shell_environment_policy inheritance.", - ], - [ - /CODEX_CLOUD_SETUP_STOP_AFTER_POLICY/, - "Cloud setup must expose a policy-only stop for behavior-level tests.", - ], + [/inherit = "all"/, "Cloud setup must configure Codex shell_environment_policy inheritance."], + [/CODEX_CLOUD_SETUP_STOP_AFTER_POLICY/, "Cloud setup must expose a policy-only stop for behavior-level tests."], ]) { requireMatch(errors, setup, pattern, message); } - if ( - !setup.includes(`railway_cli_version="${expectedCloudCliVersions.railway}"`) - ) { - errors.push( - "Cloud setup Railway CLI version must match the checked runtime contract.", - ); + if (!setup.includes(`railway_cli_version="${expectedCloudCliVersions.railway}"`)) { + errors.push("Cloud setup Railway CLI version must match the checked runtime contract."); } - if ( - !setup.includes(`codex_cli_version="${expectedCloudCliVersions.codex}"`) - ) { - errors.push( - "Cloud setup Codex CLI version must match the checked runtime contract.", - ); + if (!setup.includes(`codex_cli_version="${expectedCloudCliVersions.codex}"`)) { + errors.push("Cloud setup Codex CLI version must match the checked runtime contract."); } for (const name of providerCredentialVariables) { - if (!setup.includes(name)) - errors.push( - `Cloud setup must handle provider environment variable ${name}.`, - ); + if (!setup.includes(name)) errors.push(`Cloud setup must handle provider environment variable ${name}.`); } const providerScrubIndex = setup.indexOf("unset OPENAI_API_KEY"); - const accessProfileBranchIndex = setup.indexOf( - 'if [ "\\$CODEX_CLOUD_ACCESS_PROFILE" = "connected" ]', - ); - if ( - providerScrubIndex < 0 || - accessProfileBranchIndex < 0 || - providerScrubIndex > accessProfileBranchIndex - ) { - errors.push( - "Cloud setup must scrub provider environment variables before selecting an access profile.", - ); + const accessProfileBranchIndex = setup.indexOf('if [ "\\$CODEX_CLOUD_ACCESS_PROFILE" = "connected" ]'); + if (providerScrubIndex < 0 || accessProfileBranchIndex < 0 || providerScrubIndex > accessProfileBranchIndex) { + errors.push("Cloud setup must scrub provider environment variables before selecting an access profile."); } const credentialLikeExampleNames = [ - ...envExample.matchAll( - /^([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|DB_URL))=/gm, - ), + ...envExample.matchAll(/^([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|DB_URL))=/gm), ].map(([, name]) => name); for (const name of credentialLikeExampleNames) { if (!providerCredentialVariables.includes(name)) { - errors.push( - `Cloud credential inventory must include .env.example variable ${name}.`, - ); + errors.push(`Cloud credential inventory must include .env.example variable ${name}.`); } } requireMatch( @@ -880,17 +683,10 @@ export function validateCodexCloudSetup() { /CODEX_CLOUD.*use native Push/, "PAT deletion helper must reject the Codex Cloud agent phase and direct operators to native publication.", ); - requireMatch( - errors, - rawEnvironmentProbe, - /never values/, - "Raw Cloud environment probe must report names only.", - ); + requireMatch(errors, rawEnvironmentProbe, /never values/, "Raw Cloud environment probe must report names only."); for (const name of providerCredentialVariables) { if (!rawEnvironmentProbe.includes(name)) { - errors.push( - `Raw Cloud environment probe must cover provider environment variable ${name}.`, - ); + errors.push(`Raw Cloud environment probe must cover provider environment variable ${name}.`); } } requireMatch( @@ -899,24 +695,14 @@ export function validateCodexCloudSetup() { /\[\[ "\$branch" != -\* \]\]/, "PAT deletion helper must reject option-like branch names.", ); - requireMatch( - errors, - patDelete, - /git check-ref-format --branch/, - "PAT deletion helper must validate branch names.", - ); + requireMatch(errors, patDelete, /git check-ref-format --branch/, "PAT deletion helper must validate branch names."); requireMatch( errors, patDelete, /git remote get-url --push --all origin/, "PAT deletion helper must validate effective push URLs.", ); - requireMatch( - errors, - patDelete, - /GIT_ASKPASS/, - "PAT deletion helper must use a temporary askpass program.", - ); + requireMatch(errors, patDelete, /GIT_ASKPASS/, "PAT deletion helper must use a temporary askpass program."); requireMatch( errors, patDelete, @@ -929,36 +715,16 @@ export function validateCodexCloudSetup() { /https:\/\/github\.com\/BigSimmo\/Database\.git/, "PAT deletion helper must require the credential-free origin.", ); - requireMatch( - errors, - guide, - /bash scripts\/setup-codex-cloud\.sh/, - "The guide must provide the setup command.", - ); + requireMatch(errors, guide, /bash scripts\/setup-codex-cloud\.sh/, "The guide must provide the setup command."); requireMatch( errors, guide, /install-codex-cloud-command-shims\.sh/, "The guide must document the command-shim workaround.", ); - requireMatch( - errors, - guide, - /CODEX_CLOUD_ACCESS_PROFILE=connected/, - "The guide must document connected access.", - ); - requireMatch( - errors, - guide, - /CODEX_CLOUD_GITHUB_PAT/, - "The guide must document the narrowly scoped PAT exception.", - ); - requireMatch( - errors, - guide, - /GitHub connector/, - "The guide must document GitHub connector access.", - ); + requireMatch(errors, guide, /CODEX_CLOUD_ACCESS_PROFILE=connected/, "The guide must document connected access."); + requireMatch(errors, guide, /CODEX_CLOUD_GITHUB_PAT/, "The guide must document the narrowly scoped PAT exception."); + requireMatch(errors, guide, /GitHub connector/, "The guide must document GitHub connector access."); try { parseMcpServerMetadata(mcp); } catch (error) { @@ -976,9 +742,7 @@ export function validateCodexCloudSetup() { "verify:release", ]) { if (setup.includes(command) || maintenance.includes(command)) { - errors.push( - `Cloud bootstrap scripts must not invoke provider-capable command ${command}.`, - ); + errors.push(`Cloud bootstrap scripts must not invoke provider-capable command ${command}.`); } } @@ -989,19 +753,13 @@ export function validateCodexCloudSetup() { /sb_secret_[A-Za-z0-9_-]{8,}/, ]) { if (pattern.test(setup) || pattern.test(maintenance)) { - errors.push( - `Cloud bootstrap scripts contain a live provider identifier matching ${pattern}.`, - ); + errors.push(`Cloud bootstrap scripts contain a live provider identifier matching ${pattern}.`); } } - const cloudHeadingCount = ( - agents.match(/^## Codex Cloud environment$/gm) ?? [] - ).length; + const cloudHeadingCount = (agents.match(/^## Codex Cloud environment$/gm) ?? []).length; if (cloudHeadingCount !== 1) { - errors.push( - `AGENTS.md must contain exactly one Codex Cloud environment section; found ${cloudHeadingCount}.`, - ); + errors.push(`AGENTS.md must contain exactly one Codex Cloud environment section; found ${cloudHeadingCount}.`); } return errors; } @@ -1022,10 +780,7 @@ function repositoryCommand(command, args) { shell: false, }); if (result.status === 0) return null; - const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}` - .trim() - .split(/\r?\n/) - .at(-1); + const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`.trim().split(/\r?\n/).at(-1); return `${command} ${args.join(" ")} failed: ${output || `exit ${result.status}`}`; } @@ -1035,16 +790,8 @@ export async function validateCodexCloudRuntime(env = process.env) { for (const error of [ commandVersion("deno", ["--version"], /^deno 2\./m), commandVersion("tesseract", ["--version"], /^tesseract \d+\./m), - commandVersion( - "railway", - ["--version"], - exactVersionPattern(expectedCloudCliVersions.railway), - ), - commandVersion( - "codex", - ["--version"], - exactVersionPattern(expectedCloudCliVersions.codex), - ), + commandVersion("railway", ["--version"], exactVersionPattern(expectedCloudCliVersions.railway)), + commandVersion("codex", ["--version"], exactVersionPattern(expectedCloudCliVersions.codex)), ]) { if (error) errors.push(error); } @@ -1053,13 +800,8 @@ export async function validateCodexCloudRuntime(env = process.env) { else errors.push(...pythonWorkerDependencyErrors(env.CODEX_CLOUD_OCR_PYTHON)); for (const error of [ - repositoryCommand(process.execPath, [ - "scripts/run-tsx.mjs", - "scripts/check-runtime.ts", - ]), - repositoryCommand(process.execPath, [ - "scripts/check-installed-lock-parity.mjs", - ]), + repositoryCommand(process.execPath, ["scripts/run-tsx.mjs", "scripts/check-runtime.ts"]), + repositoryCommand(process.execPath, ["scripts/check-installed-lock-parity.mjs"]), ]) { if (error) errors.push(error); } @@ -1067,9 +809,7 @@ export async function validateCodexCloudRuntime(env = process.env) { if (env.CODEX_CLOUD_SKIP_BROWSER_INSTALL !== "1") { try { const { chromium, firefox, webkit } = await import("playwright"); - errors.push( - ...(await playwrightBrowserErrors({ chromium, firefox, webkit })), - ); + errors.push(...(await playwrightBrowserErrors({ chromium, firefox, webkit }))); } catch (error) { errors.push(`Playwright browser validation failed: ${error.message}`); } @@ -1077,76 +817,50 @@ export async function validateCodexCloudRuntime(env = process.env) { const obsoleteProxyNames = obsoleteNpmProxyVariables(env); if (obsoleteProxyNames.length > 0) { - errors.push( - `Obsolete npm proxy variable names are set: ${obsoleteProxyNames.join(", ")}.`, - ); + errors.push(`Obsolete npm proxy variable names are set: ${obsoleteProxyNames.join(", ")}.`); } const baseline = localGitBaseline(repoRoot, env); if (!baseline) { - errors.push( - "Neither local main, origin/main, nor a Cloud task HEAD is available.", - ); + errors.push("Neither local main, origin/main, nor a Cloud task HEAD is available."); } else if (baseline === "HEAD" && !env.CODEX_CLOUD_EXPECTED_BASE_SHA) { errors.push( "Checkout freshness is unverified: set CODEX_CLOUD_EXPECTED_BASE_SHA to the intended merge/base commit.", ); } if (env.CODEX_CLOUD_EXPECTED_BASE_SHA) { - const expectedBase = spawnSync( - "git", - [ - "merge-base", - "--is-ancestor", - env.CODEX_CLOUD_EXPECTED_BASE_SHA, - "HEAD", - ], - { cwd: repoRoot, stdio: "ignore" }, - ); + const expectedBase = spawnSync("git", ["merge-base", "--is-ancestor", env.CODEX_CLOUD_EXPECTED_BASE_SHA, "HEAD"], { + cwd: repoRoot, + stdio: "ignore", + }); if (expectedBase.status !== 0) { - errors.push( - "CODEX_CLOUD_EXPECTED_BASE_SHA is not an ancestor of the current HEAD.", - ); + errors.push("CODEX_CLOUD_EXPECTED_BASE_SHA is not an ancestor of the current HEAD."); } } const origin = inspectOriginRemote(repoRoot); - if (!origin.configured) - errors.push("origin is unavailable in the Cloud checkout."); - else if (origin.credentialsEmbedded) - errors.push("origin contains embedded credentials."); - else if (!origin.repositoryMatch) - errors.push(`origin must identify ${CODEX_CLOUD_REPOSITORY}.`); + if (!origin.configured) errors.push("origin is unavailable in the Cloud checkout."); + else if (origin.credentialsEmbedded) errors.push("origin contains embedded credentials."); + else if (!origin.repositoryMatch) errors.push(`origin must identify ${CODEX_CLOUD_REPOSITORY}.`); return errors; } -if ( - process.argv[1] && - path.resolve(process.argv[1]) === fileURLToPath(import.meta.url) -) { +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { const errors = validateCodexCloudSetup(); const runtime = process.argv.includes("--runtime"); - const environment = - runtime || - process.env.CODEX_CLOUD === "1" || - process.argv.includes("--environment"); + const environment = runtime || process.env.CODEX_CLOUD === "1" || process.argv.includes("--environment"); if (runtime) errors.push(...(await validateCodexCloudRuntime())); else if (environment) errors.push(...validateCodexCloudEnvironment()); if (environment) { - console.log( - "[Codex Cloud Environment] sanitized effective modes and capabilities:", - ); - for (const line of sanitizedCloudCapabilityLines()) - console.log(` ${line}`); + console.log("[Codex Cloud Environment] sanitized effective modes and capabilities:"); + for (const line of sanitizedCloudCapabilityLines()) console.log(` ${line}`); } if (errors.length > 0) { - for (const error of errors) - console.error(`[Codex Cloud Check] FAIL: ${error}`); + for (const error of errors) console.error(`[Codex Cloud Check] FAIL: ${error}`); process.exitCode = 1; } else { const scope = codexCloudValidationScope({ runtime, environment, - browserInstallSkipped: - process.env.CODEX_CLOUD_SKIP_BROWSER_INSTALL === "1", + browserInstallSkipped: process.env.CODEX_CLOUD_SKIP_BROWSER_INSTALL === "1", }); console.log(`[Codex Cloud Check] PASS: ${scope} Cloud contracts match.`); } From e4ce04a11e84971b8d9ff5595fe920b56ebaca47 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:36:30 +0800 Subject: [PATCH 11/29] style(cloud): preserve repository test formatting --- tests/codex-cloud-setup.test.ts | 303 ++++++++------------------------ 1 file changed, 72 insertions(+), 231 deletions(-) diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index 759d028793..f8e6d6dd4f 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -1,12 +1,5 @@ import { spawnSync } from "node:child_process"; -import { - mkdirSync, - mkdtempSync, - readFileSync, - readdirSync, - rmSync, - writeFileSync, -} from "node:fs"; +import { mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -38,17 +31,11 @@ import { } from "../scripts/ensure-codex-cloud-git-remote.mjs"; const temporaryDirectories: string[] = []; -const repoRoot = path.resolve( - path.dirname(fileURLToPath(import.meta.url)), - "..", -); +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const setupScript = "scripts/setup-codex-cloud.sh"; const bashCommand = process.platform === "win32" - ? path.join( - process.env.ProgramFiles || "C:\\Program Files", - "Git/bin/bash.exe", - ) + ? path.join(process.env.ProgramFiles || "C:\\Program Files", "Git/bin/bash.exe") : "bash"; const requiredPolicyExcludes = [ "OPENAI_API_KEY", @@ -89,8 +76,7 @@ const requiredPolicyExcludes = [ ] as const; afterEach(() => { - for (const directory of temporaryDirectories.splice(0)) - rmSync(directory, { recursive: true, force: true }); + for (const directory of temporaryDirectories.splice(0)) rmSync(directory, { recursive: true, force: true }); }); function temporaryDirectory(prefix: string) { @@ -101,10 +87,7 @@ function temporaryDirectory(prefix: string) { function temporaryGitRepository() { const directory = temporaryDirectory("codex-cloud-git-"); - expect( - spawnSync("git", ["init", "--quiet", "--initial-branch=task", directory]) - .status, - ).toBe(0); + expect(spawnSync("git", ["init", "--quiet", "--initial-branch=task", directory]).status).toBe(0); return directory; } @@ -114,32 +97,21 @@ function git(directory: string, ...args: string[]) { function bashPathEntry(entry: string) { if (process.platform !== "win32") return entry; - return entry - .replace(/^([A-Za-z]):/, (_, drive: string) => `/${drive.toLowerCase()}`) - .replaceAll("\\", "/"); + return entry.replace(/^([A-Za-z]):/, (_, drive: string) => `/${drive.toLowerCase()}`).replaceAll("\\", "/"); } function bashPathList(value: string) { if (process.platform !== "win32") return value; - return value - .split(path.delimiter) - .filter(Boolean) - .map(bashPathEntry) - .join(":"); + return value.split(path.delimiter).filter(Boolean).map(bashPathEntry).join(":"); } -function runSetupPolicyOnly( - home: string, - env: Record = {}, -) { +function runSetupPolicyOnly(home: string, env: Record = {}) { // The test redirects HOME to isolate the generated Codex config. Put the // running test process's Node binary first so version-manager launchers that // resolve their runtime through HOME remain usable until setup reaches the // policy-only stop. const nodeBin = path.dirname(process.execPath); - const requestedPath = - env.PATH || - [nodeBin, process.env.PATH].filter(Boolean).join(path.delimiter); + const requestedPath = env.PATH || [nodeBin, process.env.PATH].filter(Boolean).join(path.delimiter); return spawnSync(bashCommand, [setupScript], { cwd: repoRoot, encoding: "utf8", @@ -165,21 +137,15 @@ describe("Codex Cloud environment contract", () => { it("keeps the checked-in setup reproducible and provider-safe", () => { const staticEnvironment = { ...process.env }; delete staticEnvironment.CODEX_CLOUD; - const result = spawnSync( - process.execPath, - ["scripts/check-codex-cloud-setup.mjs"], - { - cwd: path.resolve(import.meta.dirname, ".."), - encoding: "utf8", - env: staticEnvironment, - shell: false, - }, - ); + const result = spawnSync(process.execPath, ["scripts/check-codex-cloud-setup.mjs"], { + cwd: path.resolve(import.meta.dirname, ".."), + encoding: "utf8", + env: staticEnvironment, + shell: false, + }); expect(result.status, `${result.stdout}\n${result.stderr}`).toBe(0); - expect(result.stdout).toContain( - "[Codex Cloud Check] PASS: static Cloud contracts match.", - ); + expect(result.stdout).toContain("[Codex Cloud Check] PASS: static Cloud contracts match."); }); it("reports sensitive and proxy variable names without exposing values", () => { @@ -191,10 +157,7 @@ describe("Codex Cloud environment contract", () => { HTTP_PROXY: "http://supported.example.test", }; - expect(configuredProviderCredentialNames(env)).toEqual([ - "OPENAI_API_KEY", - "CROSS_TENANT_SERVICE_ROLE_KEY", - ]); + expect(configuredProviderCredentialNames(env)).toEqual(["OPENAI_API_KEY", "CROSS_TENANT_SERVICE_ROLE_KEY"]); expect( configuredProviderCredentialNames({ CODEX_CLOUD_GITHUB_PAT: "never-print-this", @@ -204,8 +167,7 @@ describe("Codex Cloud environment contract", () => { }); it("covers every provider-capable test variable in the Cloud credential inventory", () => { - for (const key of providerEnvironmentKeys) - expect(providerCredentialVariables).toContain(key); + for (const key of providerEnvironmentKeys) expect(providerCredentialVariables).toContain(key); }); it("validates effective offline modes and keeps connected verification explicit", () => { @@ -231,9 +193,7 @@ describe("Codex Cloud environment contract", () => { NEXT_PUBLIC_DEMO_MODE: "false", PLAYWRIGHT_OFFLINE_MODE: "false", }), - ).toContain( - "RAG_PROVIDER_MODE must be an approved value in connected mode.", - ); + ).toContain("RAG_PROVIDER_MODE must be an approved value in connected mode."); expect( validateCodexCloudEnvironment({ @@ -242,9 +202,7 @@ describe("Codex Cloud environment contract", () => { RAG_PROVIDER_MODE: "offline", SUPABASE_ACCESS_TOKEN: "setup-only-secret", }), - ).toContain( - "Connected mode exposes provider environment variables: SUPABASE_ACCESS_TOKEN.", - ); + ).toContain("Connected mode exposes provider environment variables: SUPABASE_ACCESS_TOKEN."); }); it("emits sanitized modes, credential presence, and MCP metadata only", () => { @@ -281,29 +239,19 @@ describe("Codex Cloud environment contract", () => { ); const report = lines.join("\n"); expect(report).toContain("OPENAI_API_KEY.present=true"); - expect(report).toContain( - "mcp.server=railway type=http command=none endpoint=https://mcp.railway.com/", - ); + expect(report).toContain("mcp.server=railway type=http command=none endpoint=https://mcp.railway.com/"); expect(report).not.toContain(secret); expect(report).not.toContain("sensitive-test"); }); it("requires the Railway CLI and dedicated account token without substituting a project token", () => { - expect( - railwayReadCapability({ RAILWAY_API_TOKEN: "configured" }, true) - .cliTokenAuthReady, - ).toBe(true); - expect( - railwayReadCapability({ RAILWAY_TOKEN: "configured" }, true), - ).toMatchObject({ + expect(railwayReadCapability({ RAILWAY_API_TOKEN: "configured" }, true).cliTokenAuthReady).toBe(true); + expect(railwayReadCapability({ RAILWAY_TOKEN: "configured" }, true)).toMatchObject({ dedicatedCredentialPresent: false, projectCredentialPresent: true, cliTokenAuthReady: false, }); - expect( - railwayReadCapability({ RAILWAY_API_TOKEN: "configured" }, false) - .cliTokenAuthReady, - ).toBe(false); + expect(railwayReadCapability({ RAILWAY_API_TOKEN: "configured" }, false).cliTokenAuthReady).toBe(false); }); it("parses MCP transport metadata without query or environment values", () => { @@ -346,66 +294,39 @@ describe("Codex Cloud environment contract", () => { }, }); expect(validateMcpConfiguration(valid)).toEqual([]); - expect( - validateMcpConfiguration( - valid.replace("read_only=true", "read_only=false"), - ), - ).toContain("Supabase MCP must keep the production project read-only."); - expect( - validateMcpConfiguration( - valid.replace('"supabase":', '"unexpected":{},"supabase":'), - ), - ).toContain( + expect(validateMcpConfiguration(valid.replace("read_only=true", "read_only=false"))).toContain( + "Supabase MCP must keep the production project read-only.", + ); + expect(validateMcpConfiguration(valid.replace('"supabase":', '"unexpected":{},"supabase":'))).toContain( "Cloud MCP configuration must contain only Railway and Supabase.", ); - expect( - validateMcpConfiguration( - valid.replace('"railway":{"type"', '"railway":{"headers":{},"type"'), - ), - ).toContain( + expect(validateMcpConfiguration(valid.replace('"railway":{"type"', '"railway":{"headers":{},"type"'))).toContain( "railway MCP must use hosted OAuth without embedded environment variables or headers.", ); - expect( - validateMcpConfiguration( - valid.replace("&read_only=true", "&read_only=true&token=forbidden"), - ), - ).toContain("Supabase MCP must not include additional query parameters."); - expect( - validateMcpConfiguration( - valid.replace("&read_only=true", "&read_only=true&read_only=false"), - ), - ).toContain("Supabase MCP must not include additional query parameters."); + expect(validateMcpConfiguration(valid.replace("&read_only=true", "&read_only=true&token=forbidden"))).toContain( + "Supabase MCP must not include additional query parameters.", + ); + expect(validateMcpConfiguration(valid.replace("&read_only=true", "&read_only=true&read_only=false"))).toContain( + "Supabase MCP must not include additional query parameters.", + ); }); it("keeps project .codex/config.toml MCP registrations disabled and secret-free", () => { - const tracked = readFileSync( - new URL("../.codex/config.toml", import.meta.url), - "utf8", - ); + const tracked = readFileSync(new URL("../.codex/config.toml", import.meta.url), "utf8"); expect(validateCodexProjectMcpConfiguration(tracked)).toEqual([]); - expect( - validateCodexProjectMcpConfiguration( - tracked.replaceAll("enabled = false", "enabled = true"), - ), - ).toContain( + expect(validateCodexProjectMcpConfiguration(tracked.replaceAll("enabled = false", "enabled = true"))).toContain( `.codex/config.toml figma_cloud must set enabled = false (host/connected layers opt in).`, ); expect( validateCodexProjectMcpConfiguration( - tracked.replace( - 'default_tools_approval_mode = "writes"', - 'default_tools_approval_mode = "auto"', - ), + tracked.replace('default_tools_approval_mode = "writes"', 'default_tools_approval_mode = "auto"'), ), ).toContain( `.codex/config.toml figma_cloud must set default_tools_approval_mode = "writes" because write-capable tools require explicit approval.`, ); expect( validateCodexProjectMcpConfiguration( - tracked.replace( - 'default_tools_approval_mode = "prompt"', - 'default_tools_approval_mode = "auto"', - ), + tracked.replace('default_tools_approval_mode = "prompt"', 'default_tools_approval_mode = "auto"'), ), ).toContain( `.codex/config.toml supabase_cloud must set default_tools_approval_mode = "prompt" because the production server is constrained read-only.`, @@ -437,9 +358,7 @@ describe("Codex Cloud environment contract", () => { 'url = "https://mcp.figma.com/mcp"\nscopes = ["files:write"]', ), ), - ).toContain( - `.codex/config.toml figma_cloud must be URL-only; unsupported key scopes.`, - ); + ).toContain(`.codex/config.toml figma_cloud must be URL-only; unsupported key scopes.`); expect( validateCodexProjectMcpConfiguration( tracked.replace( @@ -447,9 +366,7 @@ describe("Codex Cloud environment contract", () => { 'url = "https://mcp.figma.com/mcp"\n__anything = "secret"', ), ), - ).toContain( - `.codex/config.toml figma_cloud must be URL-only; unsupported key __anything.`, - ); + ).toContain(`.codex/config.toml figma_cloud must be URL-only; unsupported key __anything.`); expect( validateCodexProjectMcpConfiguration( tracked.replace( @@ -477,60 +394,36 @@ describe("Codex Cloud environment contract", () => { "project_ref=sjrfecxgysukkwxsowpy&read_only=false&features=", ), ), - ).toContain( - `.codex/config.toml supabase_cloud must keep the production project read-only.`, - ); + ).toContain(`.codex/config.toml supabase_cloud must keep the production project read-only.`); }); it("probes the raw task environment without printing credential values", () => { const secret = "never-print-raw-provider-value"; - const result = spawnSync( - bashCommand, - ["scripts/check-codex-cloud-raw-env.sh"], - { - cwd: repoRoot, - encoding: "utf8", - env: { PATH: process.env.PATH, OPENAI_API_KEY: secret }, - }, - ); + const result = spawnSync(bashCommand, ["scripts/check-codex-cloud-raw-env.sh"], { + cwd: repoRoot, + encoding: "utf8", + env: { PATH: process.env.PATH, OPENAI_API_KEY: secret }, + }); expect(result.status).toBe(1); expect(result.stderr).toContain("OPENAI_API_KEY"); expect(result.stderr).not.toContain(secret); }); it("keeps setup and maintenance repairs guarded for repeat execution", () => { - const setup = readFileSync( - new URL("../scripts/setup-codex-cloud.sh", import.meta.url), - "utf8", - ); - const maintenance = readFileSync( - new URL("../scripts/maintain-codex-cloud.sh", import.meta.url), - "utf8", - ); + const setup = readFileSync(new URL("../scripts/setup-codex-cloud.sh", import.meta.url), "utf8"); + const maintenance = readFileSync(new URL("../scripts/maintain-codex-cloud.sh", import.meta.url), "utf8"); const commandShims = readFileSync( - new URL( - "../scripts/install-codex-cloud-command-shims.sh", - import.meta.url, - ), + new URL("../scripts/install-codex-cloud-command-shims.sh", import.meta.url), "utf8", ); const patDelete = readFileSync( - new URL( - "../scripts/delete-codex-cloud-branch-with-pat.sh", - import.meta.url, - ), + new URL("../scripts/delete-codex-cloud-branch-with-pat.sh", import.meta.url), "utf8", ); expect(setup).toContain("if ! grep -Fq '.clinical-kb-codex-cloud.sh'"); - expect(setup).toContain( - 'if [[ "$actual_version" != "$expected_version" ]]', - ); + expect(setup).toContain('if [[ "$actual_version" != "$expected_version" ]]'); expect(setup).toContain('"$HOME/.bash_profile"'); - expect( - setup.match( - /unset npm_config_http_proxy npm_config_https_proxy npm_config_proxy/g, - ), - ).toHaveLength(2); + expect(setup.match(/unset npm_config_http_proxy npm_config_https_proxy npm_config_proxy/g)).toHaveLength(2); expect(setup.indexOf("unset OPENAI_API_KEY")).toBeLessThan( setup.indexOf('if [ "\\$CODEX_CLOUD_ACCESS_PROFILE" = "connected" ]'), ); @@ -542,9 +435,7 @@ describe("Codex Cloud environment contract", () => { expect(setup).toContain("Incomplete managed shell policy block"); expect(setup).toContain('export RAG_PROVIDER_MODE="${rag_provider_mode}"'); expect(setup).toContain('rag_provider_mode="${RAG_PROVIDER_MODE:-auto}"'); - expect(setup).not.toContain( - 'RAG_PROVIDER_MODE="\\${RAG_PROVIDER_MODE:-auto}"', - ); + expect(setup).not.toContain('RAG_PROVIDER_MODE="\\${RAG_PROVIDER_MODE:-auto}"'); expect(setup).toContain("SUPABASE_URL"); expect(setup).toContain("SUPABASE_PROJECT_REF"); expect(setup).toContain("NEXT_PUBLIC_SUPABASE_URL"); @@ -555,9 +446,7 @@ describe("Codex Cloud environment contract", () => { expect(setup).toContain("diagnose-codex-cloud.mjs"); expect(setup).toContain("trap diagnose_setup_failure ERR"); expect(setup).toContain('setup_step="python-worker-requirements"'); - expect(setup).toContain( - "--require-hashes -r worker/python/requirements-cloud.txt", - ); + expect(setup).toContain("--require-hashes -r worker/python/requirements-cloud.txt"); expect(setup).toContain('"$ocr_venv/bin/python" -m pip check'); expect(maintenance).toContain("ensure-codex-cloud-git-remote.mjs"); expect(commandShims).toContain('nvm which "$expected_node_major"'); @@ -576,9 +465,7 @@ describe("Codex Cloud environment contract", () => { mkdirSync(path.join(home, ".codex"), { recursive: true }); writeFileSync( path.join(home, ".codex/config.toml"), - ["[mcp_servers.example]", 'command = "echo"', 'args = ["ping"]', ""].join( - "\n", - ), + ["[mcp_servers.example]", 'command = "echo"', 'args = ["ping"]', ""].join("\n"), ); const first = runSetupPolicyOnly(home, { @@ -607,9 +494,7 @@ describe("Codex Cloud environment contract", () => { const rewritten = readCodexConfig(home); expect(rewritten).toContain("[mcp_servers.example]"); expect(rewritten.match(/^\[shell_environment_policy\]$/gm)).toHaveLength(1); - expect( - rewritten.match(/BEGIN clinical-kb-codex-cloud shell policy/g), - ).toHaveLength(1); + expect(rewritten.match(/BEGIN clinical-kb-codex-cloud shell policy/g)).toHaveLength(1); }); it("pins connected retrieval mode and rejects unsafe shell-policy configs", () => { @@ -620,9 +505,7 @@ describe("Codex Cloud environment contract", () => { }); expect(connected.status, connected.stderr || connected.stdout).toBe(0); const connectedProfile = readRuntimeProfile(connectedHome); - expect(connectedProfile).toContain( - 'export CODEX_CLOUD_ACCESS_PROFILE="connected"', - ); + expect(connectedProfile).toContain('export CODEX_CLOUD_ACCESS_PROFILE="connected"'); expect(connectedProfile).toContain('export RAG_PROVIDER_MODE="offline"'); expect(connectedProfile).not.toContain("${RAG_PROVIDER_MODE:-auto}"); @@ -643,9 +526,7 @@ describe("Codex Cloud environment contract", () => { CODEX_CLOUD_ACCESS_PROFILE: "offline", }); expect(unmanaged.status).not.toBe(0); - expect(unmanaged.stderr).toContain( - "Unmanaged [shell_environment_policy] table found", - ); + expect(unmanaged.stderr).toContain("Unmanaged [shell_environment_policy] table found"); expect(readFileSync(unmanagedPath, "utf8")).toBe(unmanagedConfig); for (const tableHeader of [ @@ -655,21 +536,14 @@ describe("Codex Cloud environment contract", () => { ]) { const formattedHome = temporaryDirectory("codex-cloud-formatted-"); mkdirSync(path.join(formattedHome, ".codex"), { recursive: true }); - const formattedConfig = [ - tableHeader, - 'inherit = "all"', - "exclude = []", - "", - ].join("\n"); + const formattedConfig = [tableHeader, 'inherit = "all"', "exclude = []", ""].join("\n"); const formattedPath = path.join(formattedHome, ".codex/config.toml"); writeFileSync(formattedPath, formattedConfig); const formatted = runSetupPolicyOnly(formattedHome, { CODEX_CLOUD_ACCESS_PROFILE: "offline", }); expect(formatted.status).not.toBe(0); - expect(formatted.stderr).toContain( - "Unmanaged [shell_environment_policy] table found", - ); + expect(formatted.stderr).toContain("Unmanaged [shell_environment_policy] table found"); expect(readFileSync(formattedPath, "utf8")).toBe(formattedConfig); } @@ -685,17 +559,13 @@ describe("Codex Cloud environment contract", () => { CODEX_CLOUD_ACCESS_PROFILE: "offline", }); expect(formatted.status).not.toBe(0); - expect(formatted.stderr).toContain( - "Unmanaged [shell_environment_policy] table found", - ); + expect(formatted.stderr).toContain("Unmanaged [shell_environment_policy] table found"); expect(readFileSync(formattedPath, "utf8")).toBe(formattedConfig); } const atomicHome = temporaryDirectory("codex-cloud-atomic-"); mkdirSync(path.join(atomicHome, ".codex"), { recursive: true }); - const atomicConfig = ["[mcp_servers.keep]", 'command = "echo"', ""].join( - "\n", - ); + const atomicConfig = ["[mcp_servers.keep]", 'command = "echo"', ""].join("\n"); const atomicPath = path.join(atomicHome, ".codex/config.toml"); writeFileSync(atomicPath, atomicConfig); const atomic = runSetupPolicyOnly(atomicHome, { @@ -704,11 +574,7 @@ describe("Codex Cloud environment contract", () => { }); expect(atomic.status).not.toBe(0); expect(readFileSync(atomicPath, "utf8")).toBe(atomicConfig); - expect( - readdirSync(path.dirname(atomicPath)).filter((name) => - name.startsWith(".config.toml."), - ), - ).toEqual([]); + expect(readdirSync(path.dirname(atomicPath)).filter((name) => name.startsWith(".config.toml."))).toEqual([]); const incompleteHome = temporaryDirectory("codex-cloud-incomplete-"); mkdirSync(path.join(incompleteHome, ".codex"), { recursive: true }); @@ -727,9 +593,7 @@ describe("Codex Cloud environment contract", () => { CODEX_CLOUD_ACCESS_PROFILE: "offline", }); expect(incomplete.status).not.toBe(0); - expect(incomplete.stderr).toContain( - "Incomplete managed shell policy block", - ); + expect(incomplete.stderr).toContain("Incomplete managed shell policy block"); expect(readFileSync(incompletePath, "utf8")).toBe(incompleteConfig); }); @@ -762,10 +626,7 @@ describe("Codex Cloud environment contract", () => { }, }; expect( - await playwrightBrowserErrors( - { chromium: browserType, firefox: browserType, webkit: browserType }, - 1234, - ), + await playwrightBrowserErrors({ chromium: browserType, firefox: browserType, webkit: browserType }, 1234), ).toEqual([]); expect(closeCount).toBe(3); }); @@ -777,9 +638,7 @@ describe("Codex Cloud environment contract", () => { environment: true, browserInstallSkipped: true, }), - ).toBe( - "static, environment, and source-only runtime (browser validation skipped)", - ); + ).toBe("static, environment, and source-only runtime (browser validation skipped)"); expect( codexCloudValidationScope({ runtime: true, @@ -801,14 +660,8 @@ describe("Codex Cloud environment contract", () => { return { status: 0 }; }; - expect( - pythonWorkerImportError(process.execPath, run as typeof spawnSync), - ).toBeNull(); - expect(invocation).toEqual([ - process.execPath, - "-c", - `import ${pythonWorkerImports.join(", ")}`, - ]); + expect(pythonWorkerImportError(process.execPath, run as typeof spawnSync)).toBeNull(); + expect(invocation).toEqual([process.execPath, "-c", `import ${pythonWorkerImports.join(", ")}`]); expect( pythonWorkerImportError(process.execPath, (() => ({ status: 1, @@ -838,26 +691,14 @@ describe("Codex Cloud origin repair", () => { it("never overwrites a wrong or credential-bearing origin", () => { const wrong = temporaryGitRepository(); - expect( - git( - wrong, - "remote", - "add", - "origin", - "https://github.com/example/other.git", - ).status, - ).toBe(0); + expect(git(wrong, "remote", "add", "origin", "https://github.com/example/other.git").status).toBe(0); expect(() => ensureOriginRemote(wrong)).toThrow(/refusing to overwrite/); - expect(configuredOriginUrl(wrong)).toBe( - "https://github.com/example/other.git", - ); + expect(configuredOriginUrl(wrong)).toBe("https://github.com/example/other.git"); const credentialed = temporaryGitRepository(); const unsafe = "https://token-value@github.com/BigSimmo/Database.git"; expect(git(credentialed, "remote", "add", "origin", unsafe).status).toBe(0); - expect(() => ensureOriginRemote(credentialed)).toThrow( - /embedded credentials/, - ); + expect(() => ensureOriginRemote(credentialed)).toThrow(/embedded credentials/); expect(configuredOriginUrl(credentialed)).toBe(unsafe); }); }); From 0b094b00ea2a234a5866f3b4805d95ee801cba5b Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:37:56 +0800 Subject: [PATCH 12/29] style(docs): keep scripts index diff focused --- docs/scripts-index.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/scripts-index.md b/docs/scripts-index.md index c87312e23c..9f7d0f3875 100644 --- a/docs/scripts-index.md +++ b/docs/scripts-index.md @@ -16,25 +16,25 @@ migration has shipped (see `docs/maturity-backlog-workorders.md` L1). ## Runner & guard infrastructure [infra] -| Script | Role | -| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `run-heavy.mjs` | Acquires shared/exclusive cross-worktree leases (`test-run-lock.mjs`) so focused checks can overlap safely | -| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | -| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | -| `design-sync.mjs`, `capture-mockup-screenshots.mjs` | Local design-sync CSS prep (`node scripts/design-sync.mjs`) + redesign "current" PNG pack after ensure (`node scripts/capture-mockup-screenshots.mjs`) | -| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | -| `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, `install-codex-cloud-command-shims.sh`, `check-codex-cloud-raw-env.sh`, `delete-codex-cloud-branch-with-pat.sh`, `check-codex-cloud-setup.mjs`, `ensure-codex-cloud-git-remote.mjs` | Reproducible Codex Cloud toolchain/profile setup, profile-loading Node command shims, a pre-profile name-only credential probe, an operator-only non-Cloud PAT branch-deletion helper, sanitized acceptance, and safe credential-free `origin` repair | -| `ci-change-scope.mjs`, `ci-triage.mjs`, `pr-policy.mjs`, `pr-mergeability.mjs` | CI change classification + PR policy + conflict signal (self-tested via `check:ci-scope`/`check:ci-triage`/`check:pr-policy`/`check:pr-mergeability`) | -| `check-outstanding-issues.mjs`, `check-pr-mergeability-workflow.mjs` | Outstanding-issues ID/marker/no-driver guard + PR mergeability workflow contract | -| `outstanding-issues.mjs` | Writer for `docs/outstanding-issues.md` (`issues:add` / `issues:done` / `issues:update`) — allocates the id, picks the right table, escapes `\|`, and re-runs the guard on its own output. Never hand-edit that file, as with `ledger:append` | -| `check-installed-lock-parity.mjs`, `phone-chrome-plan.mjs`, `verify-phone-chrome.mjs`, `playwright-browser-preflight.mjs` | Lock-trust preflight, change-scoped phone contracts, and Playwright browser-binary preflight before build | -| `final-merge-audit.mjs` | Fail-closed local merge-tree audit; explicit provider mode adds PR/check/thread/tree/deployment proof | -| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | -| `test-focused.mjs`, `test-run-selection.mjs`, `test-cache-path.mjs`, `test-environment.mjs` | Backs `npm run test:focused` — change-scoped selection, cache pathing, env setup; fails closed for deleted files and test infrastructure | -| `primary-checkout-lease.mjs`, `test-run-lock.mjs`, `clean-worktree.mjs` | Cross-worktree lease arbitration for the primary checkout, plus worktree cleanup | -| `resolve-tsx-cli.mjs`, `register-server-only.mjs`, `enable-server-only-stub.mjs` | tsx CLI resolution and `server-only` import shims | -| `check-format-changed.mjs`, `check-base-freshness.mjs`, `check-local-presence.mjs` | Push-time helpers behind `guard-push.mjs`: changed-file formatting, stale-base and local-presence checks | -| `yaml-contract.mjs`, `sensitive-text.mjs`, `design-system-contract-utils.mjs` | Shared parsing/redaction/contract helpers used by the gates | +| Script | Role | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `run-heavy.mjs` | Acquires shared/exclusive cross-worktree leases (`test-run-lock.mjs`) so focused checks can overlap safely | +| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | +| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | +| `design-sync.mjs`, `capture-mockup-screenshots.mjs` | Local design-sync CSS prep (`node scripts/design-sync.mjs`) + redesign "current" PNG pack after ensure (`node scripts/capture-mockup-screenshots.mjs`) | +| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | +| `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, `install-codex-cloud-command-shims.sh`, `check-codex-cloud-raw-env.sh`, `delete-codex-cloud-branch-with-pat.sh`, `check-codex-cloud-setup.mjs`, `ensure-codex-cloud-git-remote.mjs` | Reproducible Codex Cloud toolchain/profile setup, profile-loading Node command shims, a pre-profile name-only credential probe, an operator-only non-Cloud PAT branch-deletion helper, sanitized acceptance, and safe credential-free `origin` repair | +| `ci-change-scope.mjs`, `ci-triage.mjs`, `pr-policy.mjs`, `pr-mergeability.mjs` | CI change classification + PR policy + conflict signal (self-tested via `check:ci-scope`/`check:ci-triage`/`check:pr-policy`/`check:pr-mergeability`) | +| `check-outstanding-issues.mjs`, `check-pr-mergeability-workflow.mjs` | Outstanding-issues ID/marker/no-driver guard + PR mergeability workflow contract | +| `outstanding-issues.mjs` | Writer for `docs/outstanding-issues.md` (`issues:add` / `issues:done` / `issues:update`) — allocates the id, picks the right table, escapes `\|`, and re-runs the guard on its own output. Never hand-edit that file, as with `ledger:append` | +| `check-installed-lock-parity.mjs`, `phone-chrome-plan.mjs`, `verify-phone-chrome.mjs`, `playwright-browser-preflight.mjs` | Lock-trust preflight, change-scoped phone contracts, and Playwright browser-binary preflight before build | +| `final-merge-audit.mjs` | Fail-closed local merge-tree audit; explicit provider mode adds PR/check/thread/tree/deployment proof | +| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | +| `test-focused.mjs`, `test-run-selection.mjs`, `test-cache-path.mjs`, `test-environment.mjs` | Backs `npm run test:focused` — change-scoped selection, cache pathing, env setup; fails closed for deleted files and test infrastructure | +| `primary-checkout-lease.mjs`, `test-run-lock.mjs`, `clean-worktree.mjs` | Cross-worktree lease arbitration for the primary checkout, plus worktree cleanup | +| `resolve-tsx-cli.mjs`, `register-server-only.mjs`, `enable-server-only-stub.mjs` | tsx CLI resolution and `server-only` import shims | +| `check-format-changed.mjs`, `check-base-freshness.mjs`, `check-local-presence.mjs` | Push-time helpers behind `guard-push.mjs`: changed-file formatting, stale-base and local-presence checks | +| `yaml-contract.mjs`, `sensitive-text.mjs`, `design-system-contract-utils.mjs` | Shared parsing/redaction/contract helpers used by the gates | ## Verification gates [live] From 521696a4dd46e8d0f7760a624d6582b3c4e9d694 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:41:47 +0800 Subject: [PATCH 13/29] feat(auth): activate connected host MCP entries --- scripts/setup-codex-cloud.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/scripts/setup-codex-cloud.sh b/scripts/setup-codex-cloud.sh index 5eb5816c80..b65424336a 100644 --- a/scripts/setup-codex-cloud.sh +++ b/scripts/setup-codex-cloud.sh @@ -98,6 +98,13 @@ else rag_provider_mode="offline" fi +connected_supabase_mcp_url="" +if [[ "$access_profile" = "connected" ]]; then + connected_supabase_mcp_url="$(sed -n '/^\[mcp_servers\.supabase_cloud\]$/,/^\[mcp_servers\./ s/^url = "\(.*\)"$/\1/p' .codex/config.toml | head -n 1)" + [[ "$connected_supabase_mcp_url" = https://mcp.supabase.com/mcp\?* ]] || + fail "Could not resolve the audited Supabase MCP URL from .codex/config.toml." +fi + runtime_profile="$HOME/.clinical-kb-codex-cloud.sh" cat > "$runtime_profile" < "$codex_config_candidate" if [[ "${CODEX_CLOUD_SETUP_TEST_FAIL_ATOMIC_WRITE:-0}" = "1" ]]; then From ae5817a506ae9313c84a35af1e56f303bdb2ee75 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:41:49 +0800 Subject: [PATCH 14/29] test(auth): require connected host MCP activation --- scripts/check-codex-cloud-setup.mjs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index cde48f6174..3ca47fed68 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -597,6 +597,14 @@ export function validateCodexCloudSetup() { [/CODEX_CLOUD_OCR_PYTHON/, "Cloud setup must expose the Python worker environment."], [/playwright install --with-deps chromium firefox webkit/, "Cloud setup must install every browser."], [/CODEX_CLOUD_ACCESS_PROFILE/, "Cloud setup must support explicit access profiles."], + [ + /mcp_servers\.railway_connected/, + "Connected Cloud setup must enable the hosted Railway MCP server in the managed host config.", + ], + [ + /mcp_servers\.supabase_connected/, + "Connected Cloud setup must enable the constrained Supabase MCP server in the managed host config.", + ], [/RAG_PROVIDER_MODE=offline/, "Cloud setup must default RAG to offline mode."], [/unset OPENAI_API_KEY/, "Cloud setup must remove raw provider variables from the agent shell."], [/\.bash_profile/, "Cloud setup must cover Bash login-profile precedence."], From 1842ad07317b28b91f4def113acd9bdf18b4fc6f Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:41:50 +0800 Subject: [PATCH 15/29] test(auth): prove connected and offline host MCP behavior --- tests/codex-cloud-setup.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index f8e6d6dd4f..f0a2aa9b3a 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -478,6 +478,8 @@ describe("Codex Cloud environment contract", () => { expect(config).toContain("BEGIN clinical-kb-codex-cloud shell policy"); expect(config).toContain("[shell_environment_policy]"); expect(config).toContain('inherit = "all"'); + expect(config).not.toContain("[mcp_servers.railway_connected]"); + expect(config).not.toContain("[mcp_servers.supabase_connected]"); for (const name of requiredPolicyExcludes) { expect(config).toContain(`"${name}"`); } @@ -505,6 +507,12 @@ describe("Codex Cloud environment contract", () => { }); expect(connected.status, connected.stderr || connected.stdout).toBe(0); const connectedProfile = readRuntimeProfile(connectedHome); + const connectedConfig = readCodexConfig(connectedHome); + expect(connectedConfig).toContain("[mcp_servers.railway_connected]"); + expect(connectedConfig).toContain("[mcp_servers.supabase_connected]"); + expect(connectedConfig).toContain("features=docs%2Cdevelopment"); + expect(connectedConfig.match(/^enabled = true$/gm)).toHaveLength(2); + expect(connectedConfig).toContain('default_tools_approval_mode = "prompt"'); expect(connectedProfile).toContain('export CODEX_CLOUD_ACCESS_PROFILE="connected"'); expect(connectedProfile).toContain('export RAG_PROVIDER_MODE="offline"'); expect(connectedProfile).not.toContain("${RAG_PROVIDER_MODE:-auto}"); From 319ab509311c54331ab62ba668a54c9b22a4bc37 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:41:51 +0800 Subject: [PATCH 16/29] docs(auth): describe managed host MCP activation --- docs/codex-cloud.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/codex-cloud.md b/docs/codex-cloud.md index a750a37f46..97b4ce995f 100644 --- a/docs/codex-cloud.md +++ b/docs/codex-cloud.md @@ -282,8 +282,11 @@ broaden the production entry. OpenAI generation, Supabase live data, Railway cha reruns, ingestion, deployment, and release workflows remain separate explicit actions. Project `.codex/config.toml` is the checked-in Codex MCP template. Its URL-only entries -remain `enabled = false` so offline tasks do not initialize providers; an installed ChatGPT/Codex -plugin or host MCP layer must grant OAuth and expose the callable tools in a fresh connected task. +remain `enabled = false` so offline tasks do not initialize providers. In the connected profile, +setup copies the audited Railway and constrained Supabase URLs into its managed +`$CODEX_HOME/config.toml` block with `enabled = true`; the first use completes browser OAuth. +Hosted ChatGPT still requires the matching installed plugin/connector. In either host, start a fresh +task after consent and verify the actual callable inventory. The root `.mcp.json` is a cross-client template and static allowlist only. It does not prove hosted Cloud availability unless a plugin manifest or host explicitly imports it. @@ -312,7 +315,8 @@ copying credentials into the checkout. maintenance as `bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh`. Do not add provider keys, database URLs, service-role credentials, test-user credentials, or - `ALLOW_PROVIDER_TESTS`. + `ALLOW_PROVIDER_TESTS`. Connected setup writes only the audited Railway/Supabase endpoints + into the managed host MCP block; it never writes OAuth tokens. 2. **Grant the host integrations.** Authorize the Codex GitHub connector for `BigSimmo/Database` with repository write access. Complete Railway OAuth only for workspace `bigsimmo's Projects` and project `Database` (`5deaad0b-675a-4c13-978e-5ca2b5b877f9`). Complete From bc8396c420217b0e7bb4586b79988ee02234f0a6 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:45:56 +0800 Subject: [PATCH 17/29] docs(cloud): refresh scripts inventory count --- docs/scripts-index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/scripts-index.md b/docs/scripts-index.md index 9f7d0f3875..75f0983da5 100644 --- a/docs/scripts-index.md +++ b/docs/scripts-index.md @@ -1,6 +1,6 @@ # Scripts index -Curated map of `scripts/` (212 files) and the `package.json` script surface (222 entries), +Curated map of `scripts/` (213 files) and the `package.json` script surface (222 entries), grouped by purpose. This is orientation, not an exhaustive per-file listing — the authoritative command list is `package.json`, and `npm run docs:check-scripts` verifies every `npm run ` referenced in docs resolves to a real script. `npm run docs:update` refreshes the exact counts above. From c4c8e3d382310b0bda57618d6b107b3442f3abf1 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:49:15 +0800 Subject: [PATCH 18/29] style(docs): format updated scripts inventory --- docs/scripts-index.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/scripts-index.md b/docs/scripts-index.md index 75f0983da5..2aa21a4d2e 100644 --- a/docs/scripts-index.md +++ b/docs/scripts-index.md @@ -16,25 +16,25 @@ migration has shipped (see `docs/maturity-backlog-workorders.md` L1). ## Runner & guard infrastructure [infra] -| Script | Role | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `run-heavy.mjs` | Acquires shared/exclusive cross-worktree leases (`test-run-lock.mjs`) so focused checks can overlap safely | -| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | -| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | -| `design-sync.mjs`, `capture-mockup-screenshots.mjs` | Local design-sync CSS prep (`node scripts/design-sync.mjs`) + redesign "current" PNG pack after ensure (`node scripts/capture-mockup-screenshots.mjs`) | -| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | -| `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, `install-codex-cloud-command-shims.sh`, `check-codex-cloud-raw-env.sh`, `delete-codex-cloud-branch-with-pat.sh`, `check-codex-cloud-setup.mjs`, `ensure-codex-cloud-git-remote.mjs` | Reproducible Codex Cloud toolchain/profile setup, profile-loading Node command shims, a pre-profile name-only credential probe, an operator-only non-Cloud PAT branch-deletion helper, sanitized acceptance, and safe credential-free `origin` repair | -| `ci-change-scope.mjs`, `ci-triage.mjs`, `pr-policy.mjs`, `pr-mergeability.mjs` | CI change classification + PR policy + conflict signal (self-tested via `check:ci-scope`/`check:ci-triage`/`check:pr-policy`/`check:pr-mergeability`) | -| `check-outstanding-issues.mjs`, `check-pr-mergeability-workflow.mjs` | Outstanding-issues ID/marker/no-driver guard + PR mergeability workflow contract | -| `outstanding-issues.mjs` | Writer for `docs/outstanding-issues.md` (`issues:add` / `issues:done` / `issues:update`) — allocates the id, picks the right table, escapes `\|`, and re-runs the guard on its own output. Never hand-edit that file, as with `ledger:append` | -| `check-installed-lock-parity.mjs`, `phone-chrome-plan.mjs`, `verify-phone-chrome.mjs`, `playwright-browser-preflight.mjs` | Lock-trust preflight, change-scoped phone contracts, and Playwright browser-binary preflight before build | -| `final-merge-audit.mjs` | Fail-closed local merge-tree audit; explicit provider mode adds PR/check/thread/tree/deployment proof | -| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | -| `test-focused.mjs`, `test-run-selection.mjs`, `test-cache-path.mjs`, `test-environment.mjs` | Backs `npm run test:focused` — change-scoped selection, cache pathing, env setup; fails closed for deleted files and test infrastructure | -| `primary-checkout-lease.mjs`, `test-run-lock.mjs`, `clean-worktree.mjs` | Cross-worktree lease arbitration for the primary checkout, plus worktree cleanup | -| `resolve-tsx-cli.mjs`, `register-server-only.mjs`, `enable-server-only-stub.mjs` | tsx CLI resolution and `server-only` import shims | -| `check-format-changed.mjs`, `check-base-freshness.mjs`, `check-local-presence.mjs` | Push-time helpers behind `guard-push.mjs`: changed-file formatting, stale-base and local-presence checks | -| `yaml-contract.mjs`, `sensitive-text.mjs`, `design-system-contract-utils.mjs` | Shared parsing/redaction/contract helpers used by the gates | +| Script | Role | +| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `run-heavy.mjs` | Acquires shared/exclusive cross-worktree leases (`test-run-lock.mjs`) so focused checks can overlap safely | +| `run-tsx.mjs`, `run-vitest.mjs`, `run-playwright.mjs`, `run-eval-safe.mjs` | Typed/test/e2e/eval entrypoint wrappers | +| `dev-free-port.mjs`, `ensure-local-server.mjs` | Project-stable localhost port selection + background server ensure | +| `design-sync.mjs`, `capture-mockup-screenshots.mjs` | Local design-sync CSS prep (`node scripts/design-sync.mjs`) + redesign "current" PNG pack after ensure (`node scripts/capture-mockup-screenshots.mjs`) | +| `check-node-engine.cjs`, `install-git-hooks.mjs`, `guard-push.mjs`, `guard-next-build.mjs` | Install/preflight guards | +| `setup-codex-cloud.sh`, `maintain-codex-cloud.sh`, `install-codex-cloud-command-shims.sh`, `check-codex-cloud-raw-env.sh`, `delete-codex-cloud-branch-with-pat.sh`, `check-codex-cloud-setup.mjs`, `ensure-codex-cloud-git-remote.mjs` | Reproducible Codex Cloud toolchain/profile setup, profile-loading Node command shims, a pre-profile name-only credential probe, an operator-only non-Cloud PAT branch-deletion helper, sanitized acceptance, and safe credential-free `origin` repair | +| `ci-change-scope.mjs`, `ci-triage.mjs`, `pr-policy.mjs`, `pr-mergeability.mjs` | CI change classification + PR policy + conflict signal (self-tested via `check:ci-scope`/`check:ci-triage`/`check:pr-policy`/`check:pr-mergeability`) | +| `check-outstanding-issues.mjs`, `check-pr-mergeability-workflow.mjs` | Outstanding-issues ID/marker/no-driver guard + PR mergeability workflow contract | +| `outstanding-issues.mjs` | Writer for `docs/outstanding-issues.md` (`issues:add` / `issues:done` / `issues:update`) — allocates the id, picks the right table, escapes `\|`, and re-runs the guard on its own output. Never hand-edit that file, as with `ledger:append` | +| `check-installed-lock-parity.mjs`, `phone-chrome-plan.mjs`, `verify-phone-chrome.mjs`, `playwright-browser-preflight.mjs` | Lock-trust preflight, change-scoped phone contracts, and Playwright browser-binary preflight before build | +| `final-merge-audit.mjs` | Fail-closed local merge-tree audit; explicit provider mode adds PR/check/thread/tree/deployment proof | +| `child-process-result.mjs`, `cli-utils.ts`, `productivity-core.mjs` | Shared helpers | +| `test-focused.mjs`, `test-run-selection.mjs`, `test-cache-path.mjs`, `test-environment.mjs` | Backs `npm run test:focused` — change-scoped selection, cache pathing, env setup; fails closed for deleted files and test infrastructure | +| `primary-checkout-lease.mjs`, `test-run-lock.mjs`, `clean-worktree.mjs` | Cross-worktree lease arbitration for the primary checkout, plus worktree cleanup | +| `resolve-tsx-cli.mjs`, `register-server-only.mjs`, `enable-server-only-stub.mjs` | tsx CLI resolution and `server-only` import shims | +| `check-format-changed.mjs`, `check-base-freshness.mjs`, `check-local-presence.mjs` | Push-time helpers behind `guard-push.mjs`: changed-file formatting, stale-base and local-presence checks | +| `yaml-contract.mjs`, `sensitive-text.mjs`, `design-system-contract-utils.mjs` | Shared parsing/redaction/contract helpers used by the gates | ## Verification gates [live] From 473861f8d1bfdb8e37be03d78c54aa9c5a3bdb79 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 02:53:55 +0800 Subject: [PATCH 19/29] fix(test): supply required NODE_ENV to raw probe --- tests/codex-cloud-setup.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index f0a2aa9b3a..1e5b0451ae 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -402,7 +402,7 @@ describe("Codex Cloud environment contract", () => { const result = spawnSync(bashCommand, ["scripts/check-codex-cloud-raw-env.sh"], { cwd: repoRoot, encoding: "utf8", - env: { PATH: process.env.PATH, OPENAI_API_KEY: secret }, + env: { PATH: process.env.PATH, NODE_ENV: "test", OPENAI_API_KEY: secret }, }); expect(result.status).toBe(1); expect(result.stderr).toContain("OPENAI_API_KEY"); From 620ecc969c439f5d7f6152a94191d6337d991b53 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:06:44 +0800 Subject: [PATCH 20/29] test(cloud): expect offline RAG default --- tests/codex-cloud-setup.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index 1e5b0451ae..77b2928090 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -434,8 +434,8 @@ describe("Codex Cloud environment contract", () => { expect(setup).toContain("Unmanaged [shell_environment_policy] table found"); expect(setup).toContain("Incomplete managed shell policy block"); expect(setup).toContain('export RAG_PROVIDER_MODE="${rag_provider_mode}"'); - expect(setup).toContain('rag_provider_mode="${RAG_PROVIDER_MODE:-auto}"'); - expect(setup).not.toContain('RAG_PROVIDER_MODE="\\${RAG_PROVIDER_MODE:-auto}"'); + expect(setup).toContain('rag_provider_mode="${RAG_PROVIDER_MODE:-offline}"'); + expect(setup).not.toContain('rag_provider_mode="${RAG_PROVIDER_MODE:-auto}"'); expect(setup).toContain("SUPABASE_URL"); expect(setup).toContain("SUPABASE_PROJECT_REF"); expect(setup).toContain("NEXT_PUBLIC_SUPABASE_URL"); From 526bdc3aeb5bebfea7f4b50479a7049cd1ac77b2 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:16:39 +0800 Subject: [PATCH 21/29] fix(cloud): report checkout freshness without blocking setup --- scripts/check-codex-cloud-setup.mjs | 73 ++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index 3ca47fed68..c9ff99b739 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -414,10 +414,11 @@ export function sanitizedCloudCapabilityLines(env = process.env, options = {}) { const codexCliAvailable = options.codexCliAvailable ?? commandAvailable("codex"); const safeGitHelper = options.safeGitHelper ?? hasSafeGitHubCredentialHelper(repoRoot); const mcpServers = options.mcpServers ?? parseMcpServerMetadata(read(".mcp.json")); + const checkout = options.checkout ?? gitCheckoutFreshness(repoRoot, env); const lines = [ `CODEX_CLOUD=${approvedModeValue(env.CODEX_CLOUD, ["1"])}`, `CODEX_CLOUD_ACCESS_PROFILE=${approvedModeValue(env.CODEX_CLOUD_ACCESS_PROFILE ?? "offline", ["offline", "connected"])}`, - `RAG_PROVIDER_MODE=${approvedModeValue(env.RAG_PROVIDER_MODE, ["auto", "openai", "offline"])}`, + `RAG_PROVIDER_MODE=${approvedModeValue(env.RAG_PROVIDER_MODE, ["offline"])}`, `NEXT_PUBLIC_DEMO_MODE=${approvedModeValue(env.NEXT_PUBLIC_DEMO_MODE, ["true", "false"])}`, `PLAYWRIGHT_OFFLINE_MODE=${approvedModeValue(env.PLAYWRIGHT_OFFLINE_MODE, ["true", "false"])}`, ]; @@ -433,6 +434,12 @@ export function sanitizedCloudCapabilityLines(env = process.env, options = {}) { lines.push(`git.origin_repository_match=${origin.repositoryMatch}`); lines.push(`git.origin_credential_embedded=${origin.credentialsEmbedded}`); lines.push(`git.github_cli_helper_configured=${safeGitHelper}`); + lines.push(`git.head=${checkout.head}`); + lines.push(`git.local_main=${checkout.localMain}`); + lines.push(`git.origin_main=${checkout.originMain}`); + lines.push(`git.expected_base=${checkout.expectedBase}`); + lines.push(`git.expected_base_ancestor=${checkout.expectedBaseAncestor}`); + lines.push(`git.checkout_freshness=${checkout.freshness}`); for (const server of mcpServers) { lines.push( `mcp.server=${server.name} type=${server.type} command=${server.command} endpoint=${server.endpoint} query_names=${server.queryNames.join(",") || "none"} environment_names=${server.environmentNames.join(",") || "none"}`, @@ -460,6 +467,42 @@ export function localGitBaseline(root = process.cwd(), env = process.env) { return null; } +function fullGitRevision(root, ref) { + const result = spawnSync("git", ["rev-parse", "--verify", ref], { + cwd: root, + encoding: "utf8", + shell: false, + }); + return result.status === 0 ? String(result.stdout ?? "").trim() : "unavailable"; +} + +/** @param {NodeJS.ProcessEnv | Record} [env] */ +export function gitCheckoutFreshness(root = process.cwd(), env = process.env) { + const head = fullGitRevision(root, "HEAD"); + const localMain = fullGitRevision(root, "refs/heads/main"); + const originMain = fullGitRevision(root, "refs/remotes/origin/main"); + const expectedBase = env.CODEX_CLOUD_EXPECTED_BASE_SHA || "unset"; + let expectedBaseAncestor = "unverified"; + if (expectedBase !== "unset" && head !== "unavailable") { + const result = spawnSync("git", ["merge-base", "--is-ancestor", expectedBase, "HEAD"], { + cwd: root, + stdio: "ignore", + shell: false, + }); + expectedBaseAncestor = result.status === 0 ? "true" : "false"; + } + const taskOnly = localMain === "unavailable" && originMain === "unavailable"; + const freshness = + expectedBaseAncestor === "true" + ? "verified" + : expectedBaseAncestor === "false" + ? "invalid" + : taskOnly + ? "unverified" + : "branch-reference-available"; + return { head, localMain, originMain, expectedBase, expectedBaseAncestor, freshness }; +} + export function executableFile(filePath) { try { return statSync(filePath).isFile() && (accessSync(filePath, constants.X_OK), true); @@ -559,7 +602,7 @@ export function pythonWorkerVersionLine(pythonCommand, run = spawnSync) { return result.status === 0 ? `python.worker_versions=${String(result.stdout ?? "") .trim() - .replaceAll(/\\s+/g, ",")}` + .replaceAll(/\s+/g, ",")}` : "python.worker_versions=unavailable"; } @@ -828,21 +871,20 @@ export async function validateCodexCloudRuntime(env = process.env) { errors.push(`Obsolete npm proxy variable names are set: ${obsoleteProxyNames.join(", ")}.`); } const baseline = localGitBaseline(repoRoot, env); + const checkout = gitCheckoutFreshness(repoRoot, env); if (!baseline) { errors.push("Neither local main, origin/main, nor a Cloud task HEAD is available."); - } else if (baseline === "HEAD" && !env.CODEX_CLOUD_EXPECTED_BASE_SHA) { + } else if ( + baseline === "HEAD" && + checkout.freshness === "unverified" && + env.CODEX_CLOUD_PROVISIONING !== "1" + ) { errors.push( "Checkout freshness is unverified: set CODEX_CLOUD_EXPECTED_BASE_SHA to the intended merge/base commit.", ); } - if (env.CODEX_CLOUD_EXPECTED_BASE_SHA) { - const expectedBase = spawnSync("git", ["merge-base", "--is-ancestor", env.CODEX_CLOUD_EXPECTED_BASE_SHA, "HEAD"], { - cwd: repoRoot, - stdio: "ignore", - }); - if (expectedBase.status !== 0) { - errors.push("CODEX_CLOUD_EXPECTED_BASE_SHA is not an ancestor of the current HEAD."); - } + if (checkout.expectedBaseAncestor === "false") { + errors.push("CODEX_CLOUD_EXPECTED_BASE_SHA is not an ancestor of the current HEAD."); } const origin = inspectOriginRemote(repoRoot); if (!origin.configured) errors.push("origin is unavailable in the Cloud checkout."); @@ -857,6 +899,15 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me const environment = runtime || process.env.CODEX_CLOUD === "1" || process.argv.includes("--environment"); if (runtime) errors.push(...(await validateCodexCloudRuntime())); else if (environment) errors.push(...validateCodexCloudEnvironment()); + if ( + runtime && + process.env.CODEX_CLOUD_PROVISIONING === "1" && + gitCheckoutFreshness(repoRoot).freshness === "unverified" + ) { + console.warn( + "[Codex Cloud Check] WARN: checkout freshness is unverified during provisioning; run explicit acceptance with CODEX_CLOUD_EXPECTED_BASE_SHA.", + ); + } if (environment) { console.log("[Codex Cloud Environment] sanitized effective modes and capabilities:"); for (const line of sanitizedCloudCapabilityLines()) console.log(` ${line}`); From 86a4de8f187ede40742f852d5d9627d3a26a73ff Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:17:14 +0800 Subject: [PATCH 22/29] fix(cloud): keep provisioning nonfatal when base is unknown --- scripts/setup-codex-cloud.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/setup-codex-cloud.sh b/scripts/setup-codex-cloud.sh index b65424336a..c34d81d709 100644 --- a/scripts/setup-codex-cloud.sh +++ b/scripts/setup-codex-cloud.sh @@ -322,7 +322,7 @@ npm run check:runtime npm run check:installed-lock-parity npm run check:worker-python-locks:static npm run check:codex-cloud -npm run check:codex-cloud -- --runtime +CODEX_CLOUD_PROVISIONING=1 npm run check:codex-cloud -- --runtime npm run diagnose:codex-cloud trap - ERR log "Setup complete with ${CODEX_CLOUD_ACCESS_PROFILE} access profile." From 7286b9439e5aa0d4bea950bea8800c9818ff66af Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:17:26 +0800 Subject: [PATCH 23/29] fix(cloud): distinguish maintenance from freshness acceptance --- scripts/maintain-codex-cloud.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/maintain-codex-cloud.sh b/scripts/maintain-codex-cloud.sh index 2e003fa027..c85fdb1528 100644 --- a/scripts/maintain-codex-cloud.sh +++ b/scripts/maintain-codex-cloud.sh @@ -20,7 +20,7 @@ fi node scripts/ensure-codex-cloud-git-remote.mjs --configure-gh-helper npm run check:codex-cloud -if ! npm run check:codex-cloud -- --runtime; then +if ! CODEX_CLOUD_PROVISIONING=1 npm run check:codex-cloud -- --runtime; then printf '[codex-cloud:maintenance] Runtime or toolchain drift detected; rerunning full setup.\n' exec bash scripts/setup-codex-cloud.sh fi From e9ba53dff390eba56b32eaf9f85ebb99b401a125 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:17:55 +0800 Subject: [PATCH 24/29] docs(cloud): align agent auth and cleanup policy --- AGENTS.md | 63 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e092df524e..e8d53509df 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -870,13 +870,17 @@ Use `docs/codex-cloud.md` as the environment contract: - Configure maintenance as `bash scripts/maintain-codex-cloud.sh && bash scripts/install-codex-cloud-command-shims.sh`. - Default to `CODEX_CLOUD_ACCESS_PROFILE=offline` for ordinary and protected RAG work. Use `connected` only when the user explicitly authorizes the required provider access. -- When MCP tools are already available in a Cloud session and the task needs them, read-only tools - can run without per-tool approval. Write-capable Figma, Railway, and Sentry tools still require - explicit confirmation. Paid API canaries (`eval:rag`, - `eval:retrieval:quality`, `eval:quality`, `verify:release`, `test:live`, - `check:supabase-project`) still need explicit confirmation. Project `.codex/config.toml` - keeps MCP entries `enabled = false` so ordinary/offline Codex hosts do not initialize them; - runtime Cloud MCP remains `.mcp.json` plus the host environment. +- When MCP tools are already callable in a Cloud session and the task needs them, use the host + plugin/connector inventory. The production Supabase target is limited to prompted, read-only + `docs` and `development` metadata tools; do not enable database, SQL, row, or log tools. + Write-capable Figma, Railway, and Sentry tools still require explicit confirmation. Paid API + canaries (`eval:rag`, `eval:retrieval:quality`, `eval:quality`, `verify:release`, + `test:live`, `check:supabase-project`) still need explicit confirmation. Project + `.codex/config.toml` keeps MCP entries `enabled = false` so ordinary/offline hosts do not + initialize them. Connected setup writes enabled Railway and constrained Supabase entries to the + host `$CODEX_HOME/config.toml`; actual availability still requires the installed host + plugin/connector to complete OAuth and a fresh task to prove the callable inventory with + read-only identity calls. Root `.mcp.json` is a static cross-client template, not runtime proof. - Cloud has no Windows task-start script. Report that exact fact, then perform equivalent read-only identity, branch, status, worktree, and Git-operation checks. Proceed only in a clean disposable checkout on a task-specific non-protected branch. @@ -887,15 +891,18 @@ Use `docs/codex-cloud.md` as the environment contract: - Repository setup cannot grant GitHub installation permissions, workspace RBAC, network policy, or provider credentials. Treat those as product/account settings and verify them separately without printing secret values. -- In a fresh Cloud agent shell, run `npm run check:codex-cloud` directly, without manually - sourcing a profile or entering a login shell; it must report the static-and-environment PASS - line. Then run `npm run check:codex-cloud -- --runtime`; it must report the static, - environment, and runtime PASS line. The command shims load the generated profile for normal - `node`, `npm`, and `npx` work. Also run `npm run check:runtime` and - `npm run check:installed-lock-parity` before trusting a new or reset environment. A skipped - browser install is not full browser readiness. Output is limited to approved mode values, - presence booleans, repository identity, and MCP server/command/environment-variable names; - never print credential values. +- In a fresh Cloud task, run `bash scripts/check-codex-cloud-raw-env.sh` before sourcing a + profile or entering a login shell. It must report only provider variable names and presence, + never values. Then run `npm run check:codex-cloud` directly; it must report the + static-and-environment PASS line. Run `npm run check:codex-cloud -- --runtime` with + `CODEX_CLOUD_EXPECTED_BASE_SHA` set to the intended merge/base commit when the checkout has + only a task HEAD. Setup and maintenance may report freshness as unverified so provisioning + remains repairable, but explicit acceptance must not pass an arbitrary HEAD. The command shims + load the generated profile for normal `node`, `npm`, and `npx` work. Also run + `npm run check:runtime` and `npm run check:installed-lock-parity` before trusting a new or + reset environment. A skipped browser install is not full browser readiness. Output is limited + to approved mode values, presence booleans, full Git commit identities, and MCP + server/command/environment-variable names; never print credential values. - Do not add OpenAI, Supabase, Railway, GitHub, database, or user credentials as ordinary Cloud environment variables. Codex Cloud secrets are setup-only and unavailable to the agent phase unless the platform explicitly exposes a secret to the named task phase; do not @@ -908,9 +915,12 @@ Use `docs/codex-cloud.md` as the environment contract: `.github/workflows/authenticated-live-tests.yml` GitHub Actions workflow, its explicit dispatch confirmation, and the `Database / production` environment, never by exposing credentials to the Codex Cloud agent shell. -- Railway reads require both the pinned CLI and a dedicated `RAILWAY_API_TOKEN`. Never substitute - `RAILWAY_TOKEN`. GitHub CLI authentication, the credential-free `origin` URL, and shell Git - authentication are separate capabilities. +- Connected Cloud Railway access uses the hosted Railway MCP connector and browser OAuth; prove it + with the callable tool inventory and a read-only identity/project-list call. CLI token auth is a + separate operator capability: it requires both the pinned CLI and a dedicated + `RAILWAY_API_TOKEN`, and must never substitute `RAILWAY_TOKEN` or expose either token to an + ordinary agent shell. GitHub connector access, GitHub CLI authentication, the credential-free + `origin` URL, and shell Git authentication are separate capabilities. - For an explicitly authorised GitHub task, use the authenticated GitHub connector/MCP tools as the default remote control plane. Use them for repository, PR, issue, review thread, and Actions work, including inline-thread replies/resolution, Actions @@ -918,14 +928,13 @@ Use `docs/codex-cloud.md` as the environment contract: `gh`, shell GitHub credentials, or direct shell network access is not a loss of this capability. The intended connection is `BigSimmo` with repository write access. Reserve administrator access for separately approved operations. -- A PAT is a connected-only, user-authorised exception for a genuine connector gap. Store a - short-lived, fine-grained `CODEX_CLOUD_GITHUB_PAT` only as a connected Cloud secret and - scope it to this repository and the named operation. Never make it an ordinary variable, - profile value, remote URL, or cached file. Use only - `bash scripts/delete-codex-cloud-branch-with-pat.sh ` for an exact - branch-deletion instruction; it verifies the profile, ref, and credential-free origin and - never prints the token. Remove or rotate the secret immediately afterwards. If secrets are - unavailable in that task phase, report the platform limit rather than bypassing it. +- In Codex Cloud, use native Push, the authenticated GitHub connector, or GitHub's UI for branch + publication and cleanup. `CODEX_CLOUD_GITHUB_PAT` is excluded from every Cloud agent shell. + The helper `bash scripts/delete-codex-cloud-branch-with-pat.sh ` is + operator-only outside Codex Cloud; it must reject `CODEX_CLOUD=1`, validate the exact + non-protected ref and credential-free origin, and never print the token. If the native or + connector path is unavailable, report the platform limit rather than copying a PAT into a + profile, remote URL, cached file, or agent environment. - Confirm the exact repository and PR/thread/job before a write, and verify the connector result before treating the write as successful. A repository cannot sanitize a variable already inherited by the top-level task process; the tracked shims protect normal From f33e8e25eeb061eb73a5b005bf77e63ab89936d1 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:18:57 +0800 Subject: [PATCH 25/29] test(cloud): cover freshness and sanitized capability output --- tests/codex-cloud-setup.test.ts | 69 +++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index 77b2928090..f36088b383 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -10,12 +10,14 @@ import { configuredProviderCredentialNames, expectedMcpConfiguration, executableFile, + gitCheckoutFreshness, localGitBaseline, obsoleteNpmProxyVariables, parseMcpServerMetadata, playwrightBrowserErrors, providerCredentialVariables, pythonWorkerImportError, + pythonWorkerVersionLine, pythonWorkerImports, railwayReadCapability, sanitizedCloudCapabilityLines, @@ -225,6 +227,14 @@ describe("Codex Cloud environment contract", () => { railwayCliAvailable: true, codexCliAvailable: true, safeGitHelper: true, + checkout: { + head: "a".repeat(40), + localMain: "b".repeat(40), + originMain: "c".repeat(40), + expectedBase: "a".repeat(40), + expectedBaseAncestor: "true", + freshness: "verified", + }, mcpServers: [ { name: "railway", @@ -240,8 +250,35 @@ describe("Codex Cloud environment contract", () => { const report = lines.join("\n"); expect(report).toContain("OPENAI_API_KEY.present=true"); expect(report).toContain("mcp.server=railway type=http command=none endpoint=https://mcp.railway.com/"); + expect(report).toContain(`git.head=${"a".repeat(40)}`); + expect(report).toContain("git.expected_base_ancestor=true"); + expect(report).toContain("git.checkout_freshness=verified"); expect(report).not.toContain(secret); expect(report).not.toContain("sensitive-test"); + expect( + sanitizedCloudCapabilityLines( + { + CODEX_CLOUD: "1", + CODEX_CLOUD_ACCESS_PROFILE: "connected", + RAG_PROVIDER_MODE: "auto", + }, + { + origin: { configured: true, repositoryMatch: true, credentialsEmbedded: false }, + railwayCliAvailable: false, + codexCliAvailable: false, + safeGitHelper: false, + checkout: { + head: "unavailable", + localMain: "unavailable", + originMain: "unavailable", + expectedBase: "unset", + expectedBaseAncestor: "unverified", + freshness: "unverified", + }, + mcpServers: [], + }, + ), + ).toContain("RAG_PROVIDER_MODE=invalid"); }); it("requires the Railway CLI and dedicated account token without substituting a project token", () => { @@ -448,6 +485,8 @@ describe("Codex Cloud environment contract", () => { expect(setup).toContain('setup_step="python-worker-requirements"'); expect(setup).toContain("--require-hashes -r worker/python/requirements-cloud.txt"); expect(setup).toContain('"$ocr_venv/bin/python" -m pip check'); + expect(setup).toContain("CODEX_CLOUD_PROVISIONING=1 npm run check:codex-cloud -- --runtime"); + expect(maintenance).toContain("CODEX_CLOUD_PROVISIONING=1 npm run check:codex-cloud -- --runtime"); expect(maintenance).toContain("ensure-codex-cloud-git-remote.mjs"); expect(commandShims).toContain('nvm which "$expected_node_major"'); expect(commandShims).toContain('. "$runtime_profile"'); @@ -622,6 +661,36 @@ describe("Codex Cloud environment contract", () => { expect(localGitBaseline(directory, {})).toBeNull(); expect(localGitBaseline(directory, { CODEX_CLOUD: "1" })).toBe("HEAD"); + + const head = git(directory, "rev-parse", "HEAD").stdout.trim(); + expect(gitCheckoutFreshness(directory, { CODEX_CLOUD: "1" })).toEqual({ + head, + localMain: "unavailable", + originMain: "unavailable", + expectedBase: "unset", + expectedBaseAncestor: "unverified", + freshness: "unverified", + }); + expect( + gitCheckoutFreshness(directory, { + CODEX_CLOUD: "1", + CODEX_CLOUD_EXPECTED_BASE_SHA: head, + }), + ).toMatchObject({ + expectedBase: head, + expectedBaseAncestor: "true", + freshness: "verified", + }); + }); + + it("normalizes Python package version output for capability reports", () => { + const run = (() => ({ + status: 0, + stdout: "medspacy=1.3.1 spacy=3.8.2\n", + })) as unknown as typeof spawnSync; + expect(pythonWorkerVersionLine(process.execPath, run)).toBe( + "python.worker_versions=medspacy=1.3.1,spacy=3.8.2", + ); }); it("launches and closes every installed Playwright browser", async () => { From d156381406ddf6f0aabcb81b516499810d87e304 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:20:11 +0800 Subject: [PATCH 26/29] docs(cloud): distinguish provisioning from freshness acceptance --- docs/codex-cloud.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/codex-cloud.md b/docs/codex-cloud.md index 97b4ce995f..cc91207436 100644 --- a/docs/codex-cloud.md +++ b/docs/codex-cloud.md @@ -218,9 +218,10 @@ Run this in a fresh Cloud task before relying on the environment: Read all applicable AGENTS.md files and docs/codex-cloud.md. State whether this is the offline or connected profile. Report tool versions without printing environment values. Run npm run check:codex-cloud, npm run check:runtime, -npm run check:installed-lock-parity, and npm run check:codex-cloud -- --runtime. Do not -call a provider unless this task explicitly names and authorizes that provider. Report the -decisive line from every command and any unrun check. +npm run check:installed-lock-parity, and set CODEX_CLOUD_EXPECTED_BASE_SHA to the intended +merge/base commit before running npm run check:codex-cloud -- --runtime. Do not call a +provider unless this task explicitly names and authorizes that provider. Report the decisive +line from every command and any unrun check. ``` Expected decisive lines include: @@ -237,9 +238,13 @@ policy and installed-lock parity, pinned Railway/Codex CLIs, Deno 2, Python 3 an Tesseract, actual headless launch-and-close for Chromium/Firefox/WebKit, the Python requirements fingerprint plus `pip check` and medspaCy/spaCy versions, the expected base commit as an ancestor of HEAD, the `BigSimmo/Database` origin identity, offline credential absence when applicable, -and obsolete npm proxy variable names -without reading or printing their values. MCP inspection emits server names, commands, and -environment variable names only. +and obsolete npm proxy variable names without reading or printing their values. It reports +the full current HEAD, local main and origin/main when present, expected base, ancestry result, +and a separate freshness state. Setup and maintenance use the process-local +`CODEX_CLOUD_PROVISIONING=1` flag so an unavoidable task-only checkout reports +`freshness=unverified` without entering a repair loop. The explicit acceptance command does +not set that flag and fails until `CODEX_CLOUD_EXPECTED_BASE_SHA` proves the intended base. +MCP inspection emits server names, commands, and environment variable names only. A repository cannot remove a variable already inherited by the top-level task process. Before sourcing any profile or invoking node/npm in a fresh task, run: From 841003f8e7d3217705de39b99120d99e4eb96ea7 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:25:24 +0800 Subject: [PATCH 27/29] style(cloud): format setup checker --- scripts/check-codex-cloud-setup.mjs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index c9ff99b739..a69cdc52d4 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -874,11 +874,7 @@ export async function validateCodexCloudRuntime(env = process.env) { const checkout = gitCheckoutFreshness(repoRoot, env); if (!baseline) { errors.push("Neither local main, origin/main, nor a Cloud task HEAD is available."); - } else if ( - baseline === "HEAD" && - checkout.freshness === "unverified" && - env.CODEX_CLOUD_PROVISIONING !== "1" - ) { + } else if (baseline === "HEAD" && checkout.freshness === "unverified" && env.CODEX_CLOUD_PROVISIONING !== "1") { errors.push( "Checkout freshness is unverified: set CODEX_CLOUD_EXPECTED_BASE_SHA to the intended merge/base commit.", ); From 68cae1a64ff0cdd44c23292d28156e0065de015c Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 03:25:51 +0800 Subject: [PATCH 28/29] style(cloud): format setup tests --- tests/codex-cloud-setup.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index f36088b383..29fbf71a96 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -688,9 +688,7 @@ describe("Codex Cloud environment contract", () => { status: 0, stdout: "medspacy=1.3.1 spacy=3.8.2\n", })) as unknown as typeof spawnSync; - expect(pythonWorkerVersionLine(process.execPath, run)).toBe( - "python.worker_versions=medspacy=1.3.1,spacy=3.8.2", - ); + expect(pythonWorkerVersionLine(process.execPath, run)).toBe("python.worker_versions=medspacy=1.3.1,spacy=3.8.2"); }); it("launches and closes every installed Playwright browser", async () => { From 0d4a0f1deac0f8d3891b151892ef26a4d8d334ba Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:45:39 +0800 Subject: [PATCH 29/29] fix(cloud): sanitize expected base reporting --- scripts/check-codex-cloud-setup.mjs | 12 ++++++++++-- tests/codex-cloud-setup.test.ts | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/scripts/check-codex-cloud-setup.mjs b/scripts/check-codex-cloud-setup.mjs index a69cdc52d4..432f192e89 100644 --- a/scripts/check-codex-cloud-setup.mjs +++ b/scripts/check-codex-cloud-setup.mjs @@ -476,14 +476,22 @@ function fullGitRevision(root, ref) { return result.status === 0 ? String(result.stdout ?? "").trim() : "unavailable"; } +function normalizedExpectedBase(root, value) { + if (!value) return "unset"; + const candidate = String(value); + if (!/^[0-9a-f]{40}$/i.test(candidate)) return "invalid"; + const revision = fullGitRevision(root, `${candidate}^{commit}`); + return /^[0-9a-f]{40}$/i.test(revision) ? revision.toLowerCase() : "invalid"; +} + /** @param {NodeJS.ProcessEnv | Record} [env] */ export function gitCheckoutFreshness(root = process.cwd(), env = process.env) { const head = fullGitRevision(root, "HEAD"); const localMain = fullGitRevision(root, "refs/heads/main"); const originMain = fullGitRevision(root, "refs/remotes/origin/main"); - const expectedBase = env.CODEX_CLOUD_EXPECTED_BASE_SHA || "unset"; + const expectedBase = normalizedExpectedBase(root, env.CODEX_CLOUD_EXPECTED_BASE_SHA); let expectedBaseAncestor = "unverified"; - if (expectedBase !== "unset" && head !== "unavailable") { + if (expectedBase !== "unset" && expectedBase !== "invalid" && head !== "unavailable") { const result = spawnSync("git", ["merge-base", "--is-ancestor", expectedBase, "HEAD"], { cwd: root, stdio: "ignore", diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index 29fbf71a96..de77427370 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -681,6 +681,24 @@ describe("Codex Cloud environment contract", () => { expectedBaseAncestor: "true", freshness: "verified", }); + expect( + gitCheckoutFreshness(directory, { + CODEX_CLOUD: "1", + CODEX_CLOUD_EXPECTED_BASE_SHA: "arbitrary-sensitive-value", + }), + ).toMatchObject({ + expectedBase: "invalid", + expectedBaseAncestor: "unverified", + }); + expect( + gitCheckoutFreshness(directory, { + CODEX_CLOUD: "1", + CODEX_CLOUD_EXPECTED_BASE_SHA: `${head}\nmalformed`, + }), + ).toMatchObject({ + expectedBase: "invalid", + expectedBaseAncestor: "unverified", + }); }); it("normalizes Python package version output for capability reports", () => {