From b22b0ef22a34ce983d12d015d2895f70263f457a Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 24 Oct 2026 18:03:11 +0800 Subject: [PATCH] refactor: retire test-only fallback exports Remove duplicate MCP defaults, the fixed CDP deadline message, and the canned Windows authority reason after verifying their only consumers were tests. Pin independent expected budget facts, exercise active default and selected CDP deadlines, and verify exact authored native diagnostic propagation. Preserve malformed-input, cancellation, omission, and authority assertions. Verified 222 focused cases, 9 README cases, build, static checks, docs, discovery, and duplication at 3.58%. --- src/browser/CdpPageCaptureScripts.test.ts | 160 ++++++++++-------- src/browser/CdpScriptSourceBudget.ts | 3 - src/config/mcpStartupBudgets.test.ts | 56 +++++- src/config/mcpStartupBudgets.ts | 8 - src/ghidra/GhidraDoctor.test.ts | 11 +- src/ghidra/GhidraProvider.behavior.test.ts | 8 +- src/main/messages.test.ts | 4 +- src/process/WindowsAuthority.ts | 4 - tests/boundary/mcp/mcpStartupPolicy.test.ts | 29 +--- .../ghidra/windowsCapabilities.test.ts | 13 +- 10 files changed, 160 insertions(+), 136 deletions(-) diff --git a/src/browser/CdpPageCaptureScripts.test.ts b/src/browser/CdpPageCaptureScripts.test.ts index 4494cf70f2..19b7bfccc0 100644 --- a/src/browser/CdpPageCaptureScripts.test.ts +++ b/src/browser/CdpPageCaptureScripts.test.ts @@ -4,10 +4,7 @@ import { AnalysisCancelledError } from "../domain/analysisErrorCore.js"; import { inspectWebPageInputSchema } from "../domain/browserObservation.js"; import { CdpCaptureEvents } from "./CdpCaptureEvents.js"; import type { CdpConnection } from "./CdpConnection.js"; -import { - CDP_SCRIPT_SOURCE_BYTES_OMISSION_REASON, - CDP_SCRIPT_SOURCE_DEADLINE_OMISSION_REASON, -} from "./CdpScriptSourceBudget.js"; +import { CDP_SCRIPT_SOURCE_BYTES_OMISSION_REASON } from "./CdpScriptSourceBudget.js"; import { captureScripts } from "./CdpPageCaptureScripts.js"; import type { CaptureContext } from "./CdpPageCapture.js"; import type { CapturedScript } from "./CdpCaptureEventTypes.js"; @@ -126,73 +123,98 @@ it("preserves the caller cancellation reason when a source read is pending", asy ).rejects.toMatchObject({ _tag: "AnalysisCancelledError", cause: reason }); }); -it("stops source reads at the shared collection deadline and retains metadata", async () => { - vi.useFakeTimers(); - try { - const input = inspectWebPageInputSchema.parse({ - cdp_endpoint: "http://127.0.0.1:9222", - target_id: "page-1", - allowed_origins: ["https://example.test"], - observation_ms: 0, - include_script_sources: true, - }); - const events = new CdpCaptureEvents(input, new Set(input.allowed_origins)); - events.scripts.set("first", script("first")); - events.scripts.set("second", script("second")); - const send = vi.fn( - ( - _method: string, - _params: unknown, - _sessionId: string | undefined, - signal?: AbortSignal, - ) => - new Promise((_resolve, reject) => { - signal?.addEventListener( - "abort", - () => reject(new AnalysisCancelledError("inspect_web_page")), - { once: true }, - ); - }), - ); - const context = { - connection: { send } as unknown as CdpConnection, - sessionId: undefined, - operation: "inspect_web_page", - discovery: undefined, - target: undefined, - input, - } as unknown as CaptureContext; - const pending = captureScripts({ - context, - events, - rawResources: [], - resources: [], - frameIds: new Set(), - }); +it.each([ + { selection: "default", commandTimeoutMs: undefined, deadlineMs: 20_000 }, + { selection: "caller-selected", commandTimeoutMs: 1_234, deadlineMs: 1_234 }, +])( + "stops source reads at the $selection collection deadline and retains metadata", + async ({ commandTimeoutMs, deadlineMs }) => { + vi.useFakeTimers(); + try { + const input = inspectWebPageInputSchema.parse({ + cdp_endpoint: "http://127.0.0.1:9222", + target_id: "page-1", + allowed_origins: ["https://example.test"], + observation_ms: 0, + include_script_sources: true, + ...(commandTimeoutMs === undefined + ? {} + : { command_timeout_ms: commandTimeoutMs }), + }); + const events = new CdpCaptureEvents( + input, + new Set(input.allowed_origins), + ); + events.scripts.set("first", script("first")); + events.scripts.set("second", script("second")); + const send = vi.fn( + ( + _method: string, + _params: unknown, + _sessionId: string | undefined, + signal?: AbortSignal, + ) => + new Promise((_resolve, reject) => { + signal?.addEventListener( + "abort", + () => reject(new AnalysisCancelledError("inspect_web_page")), + { once: true }, + ); + }), + ); + const context = { + connection: { send } as unknown as CdpConnection, + sessionId: undefined, + operation: "inspect_web_page", + discovery: undefined, + target: undefined, + input, + } as unknown as CaptureContext; + const pending = captureScripts({ + context, + events, + rawResources: [], + resources: [], + frameIds: new Set(), + }); - await vi.advanceTimersByTimeAsync(20_000); - const result = await pending; + await vi.advanceTimersByTimeAsync(deadlineMs); + const result = await pending; - expect(send).toHaveBeenCalledTimes(1); - expect(result.inventory.items.map((item) => item.source)).toEqual([ - { - included: false, - reason: CDP_SCRIPT_SOURCE_DEADLINE_OMISSION_REASON, - }, - { - included: false, - reason: CDP_SCRIPT_SOURCE_DEADLINE_OMISSION_REASON, - }, - ]); - expect(events.completeness.snapshot().excluded).toContainEqual({ - section: "script_sources", - reason: "resource_budget_exhausted", - count: 2, - }); - } finally { - vi.useRealTimers(); - } -}); + expect(send).toHaveBeenCalledTimes(1); + expect(result.inventory.items).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + cdp_hash: "first", + url: "https://example.test/first.js", + }), + expect.objectContaining({ + cdp_hash: "second", + url: "https://example.test/second.js", + }), + ]), + ); + const expectedReason = `script source was omitted because the ${deadlineMs} ms command_timeout_ms source collection deadline elapsed (deadlineExpired). This is not the caller observation window. The 33554432-byte source budget stays fixed. This release has no runtime override. command_timeout_ms does not raise it.`; + expect(result.inventory.items.map((item) => item.source)).toEqual([ + { + included: false, + reason: expectedReason, + }, + { + included: false, + reason: expectedReason, + }, + ]); + expect(events.completeness.snapshot().excluded).toContainEqual({ + section: "script_sources", + reason: "resource_budget_exhausted", + count: 2, + }); + } finally { + vi.useRealTimers(); + } + }, +); it("leaves source-off capture unchanged and does not issue source reads", async () => { const { result, events, send } = await capture({ first: "unused" }, false); diff --git a/src/browser/CdpScriptSourceBudget.ts b/src/browser/CdpScriptSourceBudget.ts index f6b1a89f40..09bd007344 100644 --- a/src/browser/CdpScriptSourceBudget.ts +++ b/src/browser/CdpScriptSourceBudget.ts @@ -15,9 +15,6 @@ export const cdpScriptSourceDeadlineOmissionReason = ( ): string => `script source was omitted because the ${String(timeoutMs)} ms command_timeout_ms source collection deadline elapsed (deadlineExpired). This is not the caller observation window. ${noRuntimeOverrideSentence(`The ${String(WEB_RUNTIME_LIMITS.sourceBytes)}-byte source budget stays fixed.`, "command_timeout_ms")}`; -export const CDP_SCRIPT_SOURCE_DEADLINE_OMISSION_REASON = - cdpScriptSourceDeadlineOmissionReason(selectedCdpCommandTimeoutMs(undefined)); - /** Shared admission for source text retained from passive CDP script reads. */ export class CdpScriptSourceBudget { readonly #deadline: ProviderStartupDeadline; diff --git a/src/config/mcpStartupBudgets.test.ts b/src/config/mcpStartupBudgets.test.ts index e1a77ed9b9..cf5fbddb09 100644 --- a/src/config/mcpStartupBudgets.test.ts +++ b/src/config/mcpStartupBudgets.test.ts @@ -4,7 +4,6 @@ import { ConfigurationError } from "../domain/configurationErrors.js"; import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; import { parseEnvironment } from "./environment.js"; import { - DEFAULT_MCP_STARTUP_BUDGETS, MCP_PHASE_BUDGET_FLOOR_MS, MCP_PHASE_BUDGET_MAXIMUM_MS, parseMcpStartupBudgets, @@ -12,12 +11,51 @@ import { } from "./mcpStartupBudgets.js"; import { parseConfig } from "./parseConfig.js"; -it("keeps today's phase budgets when the settings are unset", () => { +const expectedDefaultBudgets = { + initialize: { + setting: "REA_MCP_INITIALIZE_BUDGET_MS", + phase: "MCP initialize phase budget", + milliseconds: 15_000, + source: "default", + defaultMilliseconds: 15_000, + floorMilliseconds: 1, + maximumMilliseconds: 2_147_483_647, + }, + toolsList: { + setting: "REA_MCP_TOOLS_LIST_BUDGET_MS", + phase: "MCP tools/list phase budget", + milliseconds: 5_000, + source: "default", + defaultMilliseconds: 5_000, + floorMilliseconds: 1, + maximumMilliseconds: 2_147_483_647, + }, + firstCatalog: { + setting: "REA_MCP_FIRST_CATALOG_BUDGET_MS", + phase: "MCP first-catalog phase budget", + milliseconds: 20_000, + source: "default", + defaultMilliseconds: 20_000, + floorMilliseconds: 1, + maximumMilliseconds: 2_147_483_647, + }, + doctor: { + setting: "REA_MCP_DOCTOR_DEADLINE_MS", + phase: "MCP doctor startup deadline", + milliseconds: 30_000, + source: "default", + defaultMilliseconds: 30_000, + floorMilliseconds: 1, + maximumMilliseconds: 2_147_483_647, + }, +}; + +it("resolves the default phase budgets and their configuration metadata", () => { expect(parseMcpStartupBudgets({})).toEqual({ ok: true, - value: DEFAULT_MCP_STARTUP_BUDGETS, + value: expectedDefaultBudgets, }); - expect(resolveMcpStartupBudgets({})).toEqual(DEFAULT_MCP_STARTUP_BUDGETS); + expect(resolveMcpStartupBudgets({})).toEqual(expectedDefaultBudgets); const parsed = parseConfig({}); if (!parsed.ok) throw new Error("unset phase budgets were rejected"); expect(parsed.value.analysisProvider).toBe("auto"); @@ -35,23 +73,23 @@ it("uses valid phase budget overrides", () => { ok: true, value: { initialize: { - ...DEFAULT_MCP_STARTUP_BUDGETS.initialize, + ...expectedDefaultBudgets.initialize, milliseconds: 16_000, source: "setting", }, toolsList: { - ...DEFAULT_MCP_STARTUP_BUDGETS.toolsList, + ...expectedDefaultBudgets.toolsList, milliseconds: 6_000, source: "setting", }, firstCatalog: { - ...DEFAULT_MCP_STARTUP_BUDGETS.firstCatalog, + ...expectedDefaultBudgets.firstCatalog, milliseconds: 21_000, source: "setting", }, doctor: { - ...DEFAULT_MCP_STARTUP_BUDGETS.doctor, - milliseconds: MCP_PHASE_BUDGET_MAXIMUM_MS, + ...expectedDefaultBudgets.doctor, + milliseconds: 2_147_483_647, source: "setting", }, }, diff --git a/src/config/mcpStartupBudgets.ts b/src/config/mcpStartupBudgets.ts index 85a07b96be..5753661dbc 100644 --- a/src/config/mcpStartupBudgets.ts +++ b/src/config/mcpStartupBudgets.ts @@ -112,14 +112,6 @@ const defaultBudget = (phase: McpPhaseBudgetDefinition): McpPhaseBudget => ({ maximumMilliseconds: MCP_PHASE_BUDGET_MAXIMUM_MS, }); -/** Budgets used when every phase setting is unset. */ -export const DEFAULT_MCP_STARTUP_BUDGETS: McpStartupBudgets = { - initialize: defaultBudget(PHASES[0]), - toolsList: defaultBudget(PHASES[1]), - firstCatalog: defaultBudget(PHASES[2]), - doctor: defaultBudget(PHASES[3]), -}; - const parsePhaseBudget = ( phase: McpPhaseBudgetDefinition, value: string | undefined, diff --git a/src/ghidra/GhidraDoctor.test.ts b/src/ghidra/GhidraDoctor.test.ts index ae7dcb458c..02ccaafbf3 100644 --- a/src/ghidra/GhidraDoctor.test.ts +++ b/src/ghidra/GhidraDoctor.test.ts @@ -8,7 +8,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { runDoctor, type DoctorHost } from "../application/Doctor.js"; import { createDoctorHostFixture } from "../application/Doctor.fixture.js"; import { providerRegistrationEnvironment } from "../application/SetupRegistrationEnvironment.js"; -import { WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON } from "../process/WindowsAuthority.js"; import * as nativeLoader from "../windows/WindowsNativeLoader.js"; import { inspectGhidraInstallation, @@ -199,7 +198,7 @@ describe("Windows Ghidra doctor authority", () => { vi.spyOn(nativeLoader, "systemWindowsNativeAuthority").mockReturnValue({ available: false, failureStage: "artifact", - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason: "Authored native artifact unavailable diagnostic", }); }); afterEach(() => vi.restoreAllMocks()); @@ -243,7 +242,7 @@ describe("Windows Ghidra doctor authority", () => { ).toMatchObject({ ok: false, classification: "missing_dependency", - detail: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + detail: "Authored native artifact unavailable diagnostic", remediation: expect.stringContaining("matching Windows x64 REA package"), }); }); @@ -267,7 +266,7 @@ describe("Windows Ghidra doctor authority", () => { expect.objectContaining({ name: "ghidra-native_authority", classification: "missing_dependency", - detail: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + detail: "Authored native artifact unavailable diagnostic", }), ); }); @@ -278,7 +277,7 @@ describe("Windows native doctor failure diagnostics", () => { vi.spyOn(nativeLoader, "systemWindowsNativeAuthority").mockReturnValue({ available: false, failureStage: "artifact", - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason: "Authored native artifact unavailable diagnostic", }); }); afterEach(() => vi.restoreAllMocks()); @@ -371,7 +370,7 @@ describe("Windows native doctor failure diagnostics", () => { expect.objectContaining({ name: "native_authority", code: "runtime_missing", - detail: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + detail: "Authored native artifact unavailable diagnostic", }), ); }); diff --git a/src/ghidra/GhidraProvider.behavior.test.ts b/src/ghidra/GhidraProvider.behavior.test.ts index 5d51e4069f..9241b086b5 100644 --- a/src/ghidra/GhidraProvider.behavior.test.ts +++ b/src/ghidra/GhidraProvider.behavior.test.ts @@ -19,7 +19,6 @@ import { fileURLToPath } from "node:url"; import { promisify } from "node:util"; import { afterEach, describe, expect, it, onTestFinished, vi } from "vitest"; import * as nativeLoader from "../windows/WindowsNativeLoader.js"; -import { WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON } from "../process/WindowsAuthority.js"; import { fixtureDosLoadImage, compareDosClientFixture, @@ -703,12 +702,13 @@ describe("Ghidra platform support", () => { describe("Ghidra Windows platform support", () => { it("keeps Windows P0 unavailable until native isolation authority exists", async () => { + const reason = "Authored native artifact unavailable diagnostic"; const native = vi .spyOn(nativeLoader, "systemWindowsNativeAuthority") .mockReturnValue({ available: false, failureStage: "artifact", - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason, }); onTestFinished(() => native.mockRestore()); const ghidra = provider(installationHost("win32")); @@ -728,9 +728,7 @@ describe("Ghidra Windows platform support", () => { expect(await ghidra.inspectAvailability()).toMatchObject({ status: "unavailable", code: "runtime_missing", - reason: expect.stringContaining( - "Windows native controls are unavailable", - ), + reason, diagnostics: { windows_native_failure: { stage: "artifact", diff --git a/src/main/messages.test.ts b/src/main/messages.test.ts index 376a918ad9..9e4be50c0e 100644 --- a/src/main/messages.test.ts +++ b/src/main/messages.test.ts @@ -10,8 +10,8 @@ import { DEFAULT_CAPTURE_DECODER_HEAP, } from "../config/captureDecoderHeap.js"; import { - DEFAULT_MCP_STARTUP_BUDGETS, mcpPhaseBudgetRecovery, + resolveMcpStartupBudgets, } from "../config/mcpStartupBudgets.js"; import { AnalysisTimeoutError } from "../domain/analysisErrorCore.js"; import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; @@ -77,7 +77,7 @@ it("keeps ceiling dial recoveries and scrubs non-adjustable timeout teaching", a expect(heap).toContain("source "); expect(heap).toContain("then restart REA"); expect(heap).not.toContain("tell the user"); - const phase = mcpPhaseBudgetRecovery(DEFAULT_MCP_STARTUP_BUDGETS.initialize); + const phase = mcpPhaseBudgetRecovery(resolveMcpStartupBudgets({}).initialize); expect(phase).toContain("Set "); expect(phase).toContain("then restart REA"); expect(phase).not.toContain("tell the user"); diff --git a/src/process/WindowsAuthority.ts b/src/process/WindowsAuthority.ts index bef4f0c544..7fa4ed7366 100644 --- a/src/process/WindowsAuthority.ts +++ b/src/process/WindowsAuthority.ts @@ -34,10 +34,6 @@ export interface WindowsNativeCapabilitySet { readonly reparse_safe_path_admission: WindowsNativeCapability; } -/** Stable explanation used whenever the native Windows authority is absent. */ -export const WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON = - "Windows native controls are unavailable in this runtime; use the Windows x64 REA package with its matching bundled native artifact."; - /** One readiness observation, shared by diagnostics and security controls. */ export type WindowsNativeReadiness = | { readonly available: true } diff --git a/tests/boundary/mcp/mcpStartupPolicy.test.ts b/tests/boundary/mcp/mcpStartupPolicy.test.ts index e2522c5961..a23555512b 100644 --- a/tests/boundary/mcp/mcpStartupPolicy.test.ts +++ b/tests/boundary/mcp/mcpStartupPolicy.test.ts @@ -13,7 +13,6 @@ import { profileMcpModuleLoading, } from "../../../scripts/lib/mcp-startup-probe.mjs"; import { - DEFAULT_MCP_STARTUP_BUDGETS, MCP_PHASE_BUDGET_FLOOR_MS, MCP_PHASE_BUDGET_MAXIMUM_MS, mcpPhaseBudgetRecovery, @@ -60,7 +59,6 @@ const expectThrownBudget = async ( describe("MCP startup policy", () => { it("names phase budget settings without turning Codex's 30s into a REA knob", () => { const budgets = resolveMcpStartupBudgets({}); - expect(budgets).toEqual(DEFAULT_MCP_STARTUP_BUDGETS); expect(MCP_STARTUP_POLICY.codexStartupTimeoutSeconds).toBe(30); expect(budgets.initialize.milliseconds).toBe(15_000); expect(budgets.toolsList.milliseconds).toBe(5_000); @@ -123,15 +121,6 @@ describe("MCP startup policy", () => { const probeChild = resolve("tests/fixtures/mcpStartupProbeChild.mjs"); -const selectedBudget = ( - budget: McpPhaseBudget, - milliseconds: number, -): McpPhaseBudget => ({ - ...budget, - milliseconds, - source: "setting", -}); - const probePolicy = ( overrides: Partial<{ initializeBudgetMs: number; @@ -141,24 +130,18 @@ const probePolicy = ( }> = {}, ) => { const generous = 30_000; - return { - initialize: selectedBudget( - DEFAULT_MCP_STARTUP_BUDGETS.initialize, + return resolveMcpStartupBudgets({ + REA_MCP_INITIALIZE_BUDGET_MS: String( overrides.initializeBudgetMs ?? generous, ), - toolsList: selectedBudget( - DEFAULT_MCP_STARTUP_BUDGETS.toolsList, + REA_MCP_TOOLS_LIST_BUDGET_MS: String( overrides.toolsListBudgetMs ?? generous, ), - firstCatalog: selectedBudget( - DEFAULT_MCP_STARTUP_BUDGETS.firstCatalog, + REA_MCP_FIRST_CATALOG_BUDGET_MS: String( overrides.firstCatalogBudgetMs ?? generous, ), - doctor: selectedBudget( - DEFAULT_MCP_STARTUP_BUDGETS.doctor, - overrides.doctorDeadlineMs ?? generous, - ), - }; + REA_MCP_DOCTOR_DEADLINE_MS: String(overrides.doctorDeadlineMs ?? generous), + }); }; describe("packaged MCP startup probe failures", () => { diff --git a/tests/conformance/providers/ghidra/windowsCapabilities.test.ts b/tests/conformance/providers/ghidra/windowsCapabilities.test.ts index abc96587d8..77d2bca8bd 100644 --- a/tests/conformance/providers/ghidra/windowsCapabilities.test.ts +++ b/tests/conformance/providers/ghidra/windowsCapabilities.test.ts @@ -5,7 +5,6 @@ import { systemNoFollowOpenCapability, type WindowsCapabilityDependencies, } from "../../../../src/application/WindowsCapabilities.js"; -import { WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON } from "../../../../src/process/WindowsAuthority.js"; const dependencies = ( overrides: Partial = {}, @@ -15,7 +14,7 @@ const dependencies = ( nativeCapabilities: () => ({ job_object_process_ownership: { available: false, - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason: "Authored Job Object authority unavailable diagnostic", proof: "not-proven", code: "runtime_missing", failureStage: "artifact", @@ -23,7 +22,7 @@ const dependencies = ( }, private_runtime_dacl: { available: false, - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason: "Authored private DACL authority unavailable diagnostic", proof: "not-proven", code: "runtime_missing", failureStage: "artifact", @@ -31,7 +30,7 @@ const dependencies = ( }, reparse_safe_path_admission: { available: false, - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason: "Authored reparse admission authority unavailable diagnostic", proof: "not-proven", code: "runtime_missing", failureStage: "artifact", @@ -98,7 +97,7 @@ describe("Windows host capability report", () => { security: { job_object_process_ownership: { available: false, - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason: "Authored Job Object authority unavailable diagnostic", proof: "not-proven", code: "runtime_missing", failureStage: "artifact", @@ -106,7 +105,7 @@ describe("Windows host capability report", () => { }, private_runtime_dacl: { available: false, - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason: "Authored private DACL authority unavailable diagnostic", proof: "not-proven", code: "runtime_missing", failureStage: "artifact", @@ -114,7 +113,7 @@ describe("Windows host capability report", () => { }, reparse_safe_path_admission: { available: false, - reason: WINDOWS_NATIVE_AUTHORITY_UNAVAILABLE_REASON, + reason: "Authored reparse admission authority unavailable diagnostic", proof: "not-proven", code: "runtime_missing", failureStage: "artifact",