import fs from "fs"; import { fileURLToPath } from "url"; import path from "path"; // Default fixture — used when FIXTURE_PATH is not set (existing behaviour). const DEFAULT_FIXTURE_PATH = path.resolve( path.dirname(fileURLToPath(import.meta.url)), "../tests/fixtures/pre-anchored-update-savings-realism.json", ); /** * Resolves the fixture path for updateOnly mode. * FIXTURE_PATH (env) overrides the default if set and non-empty. */ function resolveFixturePath() { const envPath = process.env.FIXTURE_PATH; if (envPath && String(envPath).trim() !== "") { return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", envPath); } return DEFAULT_FIXTURE_PATH; } const BASE_URL = process.env.CONFIDENCE_ENGINE_BASE_URL || "http://127.0.0.1:3000"; // ── Bounded execution configuration ────────────────────── // Every update call is explicit and bounded — no implicit retries. const config = { scenario: "Should I relocate my engineering team from London to Manchester?", maxUpdates: 2, // strict upper bound on update calls answers: [ // positional; answer[i] used for Update i+1 "We're looking at this mainly for cost reduction — roughly £2M annual savings on office overhead.", "We don't want to increase staff turnover or lose key engineers as part of the move.", ], }; // ── Mode selector ──────────────────────────────────────── const fixtureMode = process.env.FIXTURE_MODE; /** * Resolve the continuation state file path. * CONTINUATION_FILE (env) overrides the default location. */ function resolveContinuationPath() { const envPath = process.env.CONTINUATION_FILE; if (envPath && String(envPath).trim() !== "") { return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", envPath); } return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", ".evidence-temp", "continuation-start-only.json"); } /** * Persist the exact Start response fields needed by the Update request path. */ async function writeContinuationState(continuationPath, startResponse) { const state = { version: 1, situationGraph: startResponse.situationGraph, selectedQuestion: startResponse.selectedQuestion, }; // Ensure parent directory exists const dir = path.dirname(continuationPath); await fs.promises.mkdir(dir, { recursive: true }); await fs.promises.writeFile(continuationPath, JSON.stringify(state, null, 2), "utf-8"); return state; } /** * Load the persisted Start continuation state. */ function readContinuationState(continuationPath) { const raw = fs.readFileSync(continuationPath, "utf-8"); const state = JSON.parse(raw); if (!state.situationGraph) { throw new Error("Invalid continuation file: missing situationGraph"); } if (!state.selectedQuestion) { throw new Error("Invalid continuation file: missing selectedQuestion"); } return state; } // ── Call accounting (reflects actual API calls, not successes) ── const calls = { startCalls: 0, updateCalls: 0 }; async function postJson(path, body) { const response = await fetch(`${BASE_URL}${path}`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body), }); const json = await response.json(); return { status: response.status, json }; } function nodeCount(g) { return g?.nodes?.length ?? 0; } function edgeCount(g) { return g?.edges?.length ?? 0; } async function main() { // ── Mode dispatch ────────────────────────────────────── if (fixtureMode === "updateOnly") { await runUpdateOnlyMode(); reportCallAccounting(); return; } if (fixtureMode === "startOnly") { await runStartOnlyMode(); reportCallAccounting(); return; } if (fixtureMode === "continueOneUpdate") { await runContinueOneUpdateMode(); reportCallAccounting(); return; } if (fixtureMode !== undefined) { console.error(`ERROR: Unsupported FIXTURE_MODE="${fixtureMode}". Use "updateOnly", "startOnly", "continueOneUpdate" or unset.`); process.exitCode = 1; return; } // ── Normal mode: Start→Update chain (unchanged from original) ── const startResult = await postJson("/api/cases/start", { scenario: config.scenario }); calls.startCalls++; if (!startResult.json?.success) { console.log("=== START ==="); console.log(`HTTP status: ${startResult.status}`); console.log(`errors: ${JSON.stringify(startResult.json?.errors ?? startResult.json?.message ?? null)}`); reportCallAccounting(); process.exitCode = 1; return; } const situationGraph = startResult.json.situationGraph; let selectedQuestion = startResult.json.selectedQuestion?.question ?? null; console.log("=== START ==="); console.log(`HTTP status: ${startResult.status}`); console.log(`stage: ${startResult.json.stage ?? "unknown"}`); console.log(`selected question: ${JSON.stringify(selectedQuestion)}`); console.log(`node count: ${nodeCount(situationGraph)}`); console.log(`edge count: ${edgeCount(situationGraph)}`); if (!selectedQuestion) { console.log("ERROR: No selected question returned from start. Stopping."); process.exitCode = 1; return; } // ── Update loop (bounded, explicit, no retry) ────────── for (let i = 0; i < Math.min(config.maxUpdates, config.answers.length); i++) { const updateNum = i + 1; const prevQ = selectedQuestion; let updateResult = await postJson("/api/cases/update", { situationGraph, previousQuestion: prevQ, answer: config.answers[i], }); calls.updateCalls++; if (!updateResult.json?.success) { console.log(`\n=== UPDATE ${updateNum} ===`); console.log(`HTTP status: ${updateResult.status}`); console.log(`stage: ${updateResult.json?.stage ?? "unknown"}`); console.log(`selected question: null`); console.log(`node count: ${nodeCount(situationGraph)}`); console.log(`edge count: ${edgeCount(situationGraph)}`); console.log(`error/validation summary: ${JSON.stringify(updateResult.json?.errors ?? updateResult.json?.proposalErrors ?? updateResult.json?.graphValidationErrors ?? updateResult.json?.validationErrors ?? updateResult.json?.message ?? null)}`); // Preserve rejectedProposalSnapshot diagnostics if present if (updateResult.json?.diagnostics?.rejectedProposalSnapshot) { console.log(`\ndiagnostics.rejectedProposalSnapshot: ${JSON.stringify(updateResult.json.diagnostics.rejectedProposalSnapshot, null, 2)}`); } // ── Capture structuralActionRequired from rejected snapshot if present ── const rejectedSnapshot = updateResult.json?.diagnostics?.rejectedProposalSnapshot ?? null; if (rejectedSnapshot && "structuralActionRequired" in rejectedSnapshot) { console.log(`structuralActionRequired (from rejected proposal snapshot): ${JSON.stringify(rejectedSnapshot.structuralActionRequired)}`); } else { console.log(`structuralActionRequired: UNAVAILABLE`); } reportCallAccounting(); console.log("\n*** UPDATE REJECTED — STOPPING (no retry). ***"); process.exitCode = 1; return; // rejection stops the chain immediately } const updatedGraph = updateResult.json.updatedSituationGraph; selectedQuestion = updateResult.json.selectedQuestion?.question ?? null; console.log(`\n=== UPDATE ${updateNum} ===`); console.log(`HTTP status: ${updateResult.status}`); console.log(`stage: ${updateResult.json.stage ?? "unknown"}`); console.log(`proposal/apply success: ${updateResult.json.proposal?.success ?? updateResult.json.applySuccess ?? null}`); // ── Capture accepted structural mutation fields ──────── const proposal = updateResult.json.updatedProposal ?? updateResult.json.proposal ?? null; // ── Capture answerMeaning and structuralActionRequired from proposal (production path) ─ const am = proposal?.answerMeaning ?? null; if (am) { console.log(`answerMeaning.userSupportedMeaning: ${JSON.stringify(am.userSupportedMeaning ?? null)}`); console.log(`answerMeaning.possibleInference: ${JSON.stringify(am.possibleInference ?? null)}`); console.log(`answerMeaning.supportCategory: ${JSON.stringify(am.supportCategory ?? null)}`); console.log(`answerMeaning.resolutionGuidance: ${JSON.stringify(am.resolutionGuidance ?? null)}`); } if (proposal) { console.log(`updatedNodes: ${JSON.stringify(proposal.updatedNodes ?? [])}`); console.log(`resolvedUnknownNodeIds: ${JSON.stringify(proposal.resolvedUnknownNodeIds ?? [])}`); console.log(`addedNodes: ${JSON.stringify(proposal.addedNodes ?? [])}`); console.log(`addedEdges: ${JSON.stringify(proposal.addedEdges ?? [])}`); } // ── Capture structuralActionRequired from accepted update ─ const sar = proposal?.structuralActionRequired; if (sar === undefined || sar === null) { console.log(`structuralActionRequired: null`); } else { console.log(`structuralActionRequired: ${JSON.stringify(sar)}`); } // ── Capture selectedQuestion node reference ──────────── const sq = updateResult.json.selectedQuestion ?? null; if (sq && typeof sq === "object") { console.log(`selectedQuestion: ${JSON.stringify(sq.question ?? null)}`); if (sq.nodeId) { console.log(`selectedQuestion.nodeId: ${JSON.stringify(sq.nodeId)}`); } } // ── Compact structural snapshot of resulting graph ───── const nodes = updatedGraph?.nodes ?? []; const edges = updatedGraph?.edges ?? []; console.log(`\nresulting graph (${nodes.length} nodes, ${edges.length} edges):`); for (const n of nodes) { console.log(` node: id=${n.id ?? n.nodeId}, kind=${n.kind}, label=${n.label ?? n.description ?? ""}, status=${n.status}`); } for (const e of edges) { console.log(` edge: from=${e.fromNodeId ?? e.from}, to=${e.toNodeId ?? e.to}, relationship=${e.relationship}`); } situationGraph = updatedGraph; } } // ── Pre-anchored update-only mode (57J.78) ─────────────── /** * Loads the committed pre-anchored fixture, skips Start, * sends exactly one Update through the production HTTP route, * and preserves all hardened capture/no-retry behaviour. */ async function runUpdateOnlyMode() { const answer = process.env.ANSWER_2; if (!answer || String(answer).trim() === "") { console.log("BLOCKED - missing ANSWER_2"); return; // zero live calls made } const fixturePath = resolveFixturePath(); // Load committed fixture — single source of truth. let fixtureData; try { const raw = fs.readFileSync(fixturePath, "utf-8"); fixtureData = JSON.parse(raw); } catch (err) { console.error(`ERROR: Cannot load pre-anchored fixture from ${fixturePath}`); process.exitCode = 1; return; } const initialGraph = fixtureData.graph; // Verify fixture integrity before proceeding. const nodes = initialGraph.nodes; const edges = initialGraph.edges; // Generic anchor check: any unresolved unknown node (supports both savings-realism and decision-options fixtures). const unresolvedNodes = nodes.filter( (n) => n.kind === "unknown" && n.status === "unknown", ); if (unresolvedNodes.length < 1) { console.log(`ERROR: pre-anchored fixture must contain at least one unresolved unknown node (found ${unresolvedNodes.length}).`); process.exitCode = 1; return; } // Use the first unresolved unknown as the anchor for previousQuestion derivation. const anchorNode = unresolvedNodes[0]; // Pre-call compact fixture snapshot. console.log(`\n=== UPDATE-ONLY MODE ===`); console.log(`fixture node count: ${nodes.length}`); console.log(`fixture edge count: ${edges.length}`); console.log(`anchor node:`); console.log(` id: ${anchorNode.id}`); console.log(` label: ${anchorNode.label}`); console.log(` kind: ${anchorNode.kind}`); console.log(` status: ${anchorNode.status}`); // Deep-copy so mutation doesn't corrupt the original fixture. const situationGraph = JSON.parse(JSON.stringify(initialGraph)); // Derive previousQuestion from the fixture's unresolved_question or anchor node label. let selectedQuestion = fixtureData.unresolved_question ?? anchorNode.label; // ── Exactly one Update through production route ──────── const updateNum = 1; let updateResult = await postJson("/api/cases/update", { situationGraph, previousQuestion: selectedQuestion, answer: String(answer), }); calls.updateCalls++; if (!updateResult.json?.success) { console.log(`\n=== UPDATE ${updateNum} ===`); console.log(`HTTP status: ${updateResult.status}`); console.log(`stage: ${updateResult.json?.stage ?? "unknown"}`); console.log(`selected question: null`); console.log(`node count: ${nodeCount(situationGraph)}`); console.log(`edge count: ${edgeCount(situationGraph)}`); console.log( `error/validation summary: ${JSON.stringify(updateResult.json?.errors ?? updateResult.json?.proposalErrors ?? updateResult.json?.graphValidationErrors ?? updateResult.json?.validationErrors ?? updateResult.json?.message ?? null)}`, ); // Preserve rejectedProposalSnapshot diagnostics if present. if (updateResult.json?.diagnostics?.rejectedProposalSnapshot) { console.log( `\ndiagnostics.rejectedProposalSnapshot: ${JSON.stringify(updateResult.json.diagnostics.rejectedProposalSnapshot, null, 2)}`, ); } // Capture structuralActionRequired from rejected snapshot. const rejectedSnapshot = updateResult.json?.diagnostics?.rejectedProposalSnapshot ?? null; if (rejectedSnapshot && "structuralActionRequired" in rejectedSnapshot) { console.log( `structuralActionRequired (from rejected proposal snapshot): ${JSON.stringify(rejectedSnapshot.structuralActionRequired)}`, ); } else { console.log(`structuralActionRequired: UNAVAILABLE`); } console.log("\n*** UPDATE REJECTED — STOPPING (no retry). ***"); process.exitCode = 1; return; } const updatedGraph = updateResult.json.updatedSituationGraph; selectedQuestion = updateResult.json.selectedQuestion?.question ?? null; // ── Capture accepted answer meaning ───────────────────── const am = updateResult.json.answerMeaning ?? null; if (am) { console.log(`answerMeaning.userSupportedMeaning: ${JSON.stringify(am.userSupportedMeaning ?? null)}`); console.log(`answerMeaning.possibleInference: ${JSON.stringify(am.possibleInference ?? null)}`); console.log(`answerMeaning.supportCategory: ${JSON.stringify(am.supportCategory ?? null)}`); console.log(`answerMeaning.resolutionGuidance: ${JSON.stringify(am.resolutionGuidance ?? null)}`); } // ── Capture accepted structural mutation fields ──────── const proposal = updateResult.json.updatedProposal ?? updateResult.json.proposal ?? null; if (proposal) { console.log(`updatedNodes: ${JSON.stringify(proposal.updatedNodes ?? [])}`); console.log(`resolvedUnknownNodeIds: ${JSON.stringify(proposal.resolvedUnknownNodeIds ?? [])}`); console.log(`addedNodes: ${JSON.stringify(proposal.addedNodes ?? [])}`); console.log(`addedEdges: ${JSON.stringify(proposal.addedEdges ?? [])}`); } // ── Capture structuralActionRequired from accepted update ─ const sar = updateResult.json.structuralActionRequired; if (sar === undefined || sar === null) { console.log(`structuralActionRequired: null`); } else { console.log(`structuralActionRequired: ${JSON.stringify(sar)}`); } // ── Capture selectedQuestion node reference ──────────── const sq = updateResult.json.selectedQuestion ?? null; if (sq && typeof sq === "object") { console.log(`selectedQuestion: ${JSON.stringify(sq.question ?? null)}`); if (sq.nodeId) { console.log(`selectedQuestion.nodeId: ${JSON.stringify(sq.nodeId)}`); } } // ── Capture explicit closure metadata from accepted update ─ const finalActiveUnknownNodeId = updatedGraph?.activeUnknownNodeId === undefined ? null : updatedGraph.activeUnknownNodeId; console.log( `finalActiveUnknownNodeId: ${JSON.stringify(finalActiveUnknownNodeId)}`, ); console.log(`finalSelectedQuestion: ${JSON.stringify(sq)}`); // ── Capture persistent graph after update ─────────────── const pNodes = updatedGraph?.nodes ?? []; const pEdges = updatedGraph?.edges ?? []; console.log(`\nresulting persistent graph (${pNodes.length} nodes, ${pEdges.length} edges):`); for (const n of pNodes) { console.log(` node: id=${n.id ?? n.nodeId}, kind=${n.kind}, label=${n.label ?? n.description ?? ""}, status=${n.status}`); } for (const e of pEdges) { console.log(` edge: from=${e.fromNodeId ?? e.from}, to=${e.toNodeId ?? e.to}, relationship=${e.relationship}`); } // ── UPDATE-ONLY EXIT — no retry, no additional calls ─── } // ── Gated apparatus: start-only mode ───────────────────── /** * Phase 1 of the gated investigation apparatus. * Makes exactly one Start call, persists the continuation state to disk, * and exits with zero Update requests issued. */ async function runStartOnlyMode() { const continuationPath = resolveContinuationPath(); if (!config.scenario || String(config.scenario).trim() === "") { console.log("BLOCKED - missing scenario"); return; // zero live calls made } // ── Exactly one Start ──────────────────────────────── const startResult = await postJson("/api/cases/start", { scenario: config.scenario }); calls.startCalls++; if (!startResult.json?.success) { console.log("=== START ==="); console.log(`HTTP status: ${startResult.status}`); console.log(`errors: ${JSON.stringify(startResult.json?.errors ?? startResult.json?.message ?? null)}`); return; } const situationGraph = startResult.json.situationGraph; let selectedQuestion = startResult.json.selectedQuestion?.question ?? null; console.log("=== START (startOnly mode) ==="); console.log(`HTTP status: ${startResult.status}`); console.log(`stage: ${startResult.json.stage ?? "unknown"}`); console.log(`selected question: ${JSON.stringify(selectedQuestion)}`); console.log(`node count: ${nodeCount(situationGraph)}`); console.log(`edge count: ${edgeCount(situationGraph)}`); // Null selectedQuestion is a valid Start outcome (active target, no graph-backed question available). // Only genuine failure (no success flag or missing graph) should block. if (!situationGraph || !Array.isArray(situationGraph.nodes)) { console.log("ERROR: Invalid Start response — missing situationGraph."); process.exitCode = 1; return; } // ── Persist exact continuation state ───────────────── const state = await writeContinuationState(continuationPath, { situationGraph, selectedQuestion: startResult.json.selectedQuestion, }); console.log(`\n=== CONTINUATION STATE WRITTEN ===`); console.log(`path: ${continuationPath}`); console.log(`graph nodes: ${state.situationGraph.nodes.length}`); console.log(`graph edges: ${state.situationGraph.edges.length}`); console.log(`selectedQuestion: ${JSON.stringify(state.selectedQuestion)}`); if (state.situationGraph.activeUnknownNodeId !== undefined && state.situationGraph.activeUnknownNodeId !== null) { console.log(`activeUnknownNodeId: ${state.situationGraph.activeUnknownNodeId}`); } // No Updates issued — this is the gate boundary. } // ── Gated apparatus: continue one update ───────────────── /** * Phase 2 of the gated investigation apparatus. * Loads a persisted Start continuation file, requires an explicit answer, * and makes exactly one Update against that preserved state. */ async function runContinueOneUpdateMode() { const continuationPath = resolveContinuationPath(); const answer = process.env.CONTINUATION_ANSWER; // ── Block on missing explicit answer before any file/HTTP work ── if (!answer || String(answer).trim() === "") { console.log("BLOCKED - missing CONTINUATION_ANSWER"); return; // zero live calls made } // ── Load persisted continuation state ──────────────── let continuationState; try { continuationState = readContinuationState(continuationPath); } catch (err) { console.error(`ERROR: Cannot load continuation state from ${continuationPath}: ${err.message}`); process.exitCode = 1; return; // zero live calls made } const situationGraph = JSON.parse(JSON.stringify(continuationState.situationGraph)); const selectedQuestion = continuationState.selectedQuestion; console.log(`\n=== CONTINUE-ONE-UPDATE MODE ===`); console.log(`continuation file: ${continuationPath}`); console.log(`graph nodes (from captured Start): ${situationGraph.nodes.length}`); console.log(`graph edges (from captured Start): ${situationGraph.edges.length}`); // Verify persisted state integrity if (!selectedQuestion) { console.log("ERROR: Continuation file has no selectedQuestion — cannot determine the investigation gate."); process.exitCode = 1; return; } // ── Exactly one Update using preserved state + explicit answer ── const updateNum = 1; let updateResult = await postJson("/api/cases/update", { situationGraph, previousQuestion: selectedQuestion?.question ?? null, answer: String(answer), }); calls.updateCalls++; if (!updateResult.json?.success) { console.log(`\n=== UPDATE ${updateNum} ===`); console.log(`HTTP status: ${updateResult.status}`); console.log(`stage: ${updateResult.json?.stage ?? "unknown"}`); console.log(`selected question: null`); console.log(`node count: ${nodeCount(situationGraph)}`); console.log(`edge count: ${edgeCount(situationGraph)}`); console.log( `error/validation summary: ${JSON.stringify(updateResult.json?.errors ?? updateResult.json?.proposalErrors ?? updateResult.json?.graphValidationErrors ?? updateResult.json?.validationErrors ?? updateResult.json?.message ?? null)}`, ); if (updateResult.json?.diagnostics?.rejectedProposalSnapshot) { console.log( `\ndiagnostics.rejectedProposalSnapshot: ${JSON.stringify(updateResult.json.diagnostics.rejectedProposalSnapshot, null, 2)}`, ); } const rejectedSnapshot = updateResult.json?.diagnostics?.rejectedProposalSnapshot ?? null; if (rejectedSnapshot && "structuralActionRequired" in rejectedSnapshot) { console.log( `structuralActionRequired (from rejected proposal snapshot): ${JSON.stringify(rejectedSnapshot.structuralActionRequired)}`, ); } else { console.log(`structuralActionRequired: UNAVAILABLE`); } console.log("\n*** UPDATE REJECTED — STOPPING (no retry). ***"); process.exitCode = 1; return; } const updatedGraph = updateResult.json.updatedSituationGraph; selectedQuestion = updateResult.json.selectedQuestion?.question ?? null; // ── Capture accepted answer meaning ─────────────────── const am = updateResult.json.answerMeaning ?? null; if (am) { console.log(`answerMeaning.userSupportedMeaning: ${JSON.stringify(am.userSupportedMeaning ?? null)}`); console.log(`answerMeaning.possibleInference: ${JSON.stringify(am.possibleInference ?? null)}`); console.log(`answerMeaning.supportCategory: ${JSON.stringify(am.supportCategory ?? null)}`); console.log(`answerMeaning.resolutionGuidance: ${JSON.stringify(am.resolutionGuidance ?? null)}`); } // ── Capture accepted structural mutation fields ────── const proposal = updateResult.json.updatedProposal ?? updateResult.json.proposal ?? null; if (proposal) { console.log(`updatedNodes: ${JSON.stringify(proposal.updatedNodes ?? [])}`); console.log(`resolvedUnknownNodeIds: ${JSON.stringify(proposal.resolvedUnknownNodeIds ?? [])}`); console.log(`addedNodes: ${JSON.stringify(proposal.addedNodes ?? [])}`); console.log(`addedEdges: ${JSON.stringify(proposal.addedEdges ?? [])}`); } // ── Capture structuralActionRequired from accepted update ─ const sar = updateResult.json.structuralActionRequired; if (sar === undefined || sar === null) { console.log(`structuralActionRequired: null`); } else { console.log(`structuralActionRequired: ${JSON.stringify(sar)}`); } // ── Capture selectedQuestion node reference ─────────── const sq = updateResult.json.selectedQuestion ?? null; if (sq && typeof sq === "object") { console.log(`selectedQuestion: ${JSON.stringify(sq.question ?? null)}`); if (sq.nodeId) { console.log(`selectedQuestion.nodeId: ${JSON.stringify(sq.nodeId)}`); } } const finalActiveUnknownNodeId = updatedGraph?.activeUnknownNodeId === undefined ? null : updatedGraph.activeUnknownNodeId; console.log( `finalActiveUnknownNodeId: ${JSON.stringify(finalActiveUnknownNodeId)}`, ); // ── Capture persistent graph after update ───────────── const pNodes = updatedGraph?.nodes ?? []; const pEdges = updatedGraph?.edges ?? []; console.log(`\nresulting persistent graph (${pNodes.length} nodes, ${pEdges.length} edges):`); for (const n of pNodes) { console.log(` node: id=${n.id ?? n.nodeId}, kind=${n.kind}, label=${n.label ?? n.description ?? ""}, status=${n.status}`); } for (const e of pEdges) { console.log(` edge: from=${e.fromNodeId ?? e.from}, to=${e.toNodeId ?? e.to}, relationship=${e.relationship}`); } // ── CONTINUE-ONE-UPDATE EXIT — no retry, no additional calls ── } function reportCallAccounting() { const totalCalls = calls.startCalls + calls.updateCalls; console.log("\n=== CALL ACCOUNTING ==="); console.log(`startCalls: ${calls.startCalls}`); console.log(`updateCalls: ${calls.updateCalls}`); console.log(`totalCalls: ${totalCalls}`); } main().catch((error) => { console.error(error instanceof Error ? error.message : String(error)); reportCallAccounting(); process.exitCode = 1; });