diff --git a/scripts/verify-package-setup.mjs b/scripts/verify-package-setup.mjs index f11af1e388..5723280266 100644 --- a/scripts/verify-package-setup.mjs +++ b/scripts/verify-package-setup.mjs @@ -12,7 +12,6 @@ import { import { dirname, isAbsolute, join } from "node:path"; import { parse as parseToml } from "smol-toml"; -import { clientRegistrationCommandText } from "../dist/cli/clientRegistrationCommandText.js"; import { TOOL_CONTRACTS } from "../dist/contracts/toolContracts.js"; import { PRODUCT_IDENTITY } from "../dist/identity.js"; import { pathExists } from "./lib/verify-package-core.mjs"; @@ -53,28 +52,20 @@ const isDirectRegistration = async (command, cli) => (await realpath(command[1])) === (await realpath(cli)); const verifyMcpAdd = async ({ commands, cli, environment, npxLog, home }) => { - await commands.run(cli, ["mcp", "add"], environment); + await commands.run(cli, ["mcp", "add", "--agent", "amp"], environment); const settings = JSON.parse( await readFile(join(home, ".config", "amp", "settings.json"), "utf8"), ); const registration = settings?.["amp.mcpServers"]?.rea; if (!Array.isArray(registration?.args)) - throw new Error("Incur mcp add did not write the native Amp registration"); + throw new Error("REA mcp add did not write the native Amp registration"); const command = [registration.command, ...registration.args]; if (!(await isDirectRegistration(command, cli))) throw new Error( - "Incur mcp add did not register the selected absolute runtime and REA script", + "REA mcp add did not register the selected absolute runtime and REA script", ); - // Amp consumes the same command through Incur's parser. Check the delegated - // helper receives that command as one argument, without flattening its argv. - deepStrictEqual(JSON.parse(await readFile(npxLog, "utf8")), [ - "add-mcp", - clientRegistrationCommandText(command), - "--name", - "rea", - "-y", - "-g", - ]); + if (await pathExists(npxLog)) + throw new Error("Native MCP registration invoked an external installer"); }; const verifySetupPlan = async ({ diff --git a/src/domain/adb/adbDeviceAnalysis.ts b/src/domain/adb/adbDeviceAnalysis.ts index 2fa32a0a88..308560fd39 100644 --- a/src/domain/adb/adbDeviceAnalysis.ts +++ b/src/domain/adb/adbDeviceAnalysis.ts @@ -51,7 +51,6 @@ export const ADB_DEVICE_STATES = [ "detached", ] as const; export const adbDeviceStateSchema = z.enum(ADB_DEVICE_STATES); -export type AdbDeviceState = (typeof ADB_DEVICE_STATES)[number]; const keyStringSchema = z.string().min(1).max(256); const keyPattern = /^[A-Za-z0-9._\x2d]+$/u; diff --git a/src/domain/analysisView/analysisView.test.ts b/src/domain/analysisView/analysisView.test.ts index 8152b392a0..8182a9330d 100644 --- a/src/domain/analysisView/analysisView.test.ts +++ b/src/domain/analysisView/analysisView.test.ts @@ -445,6 +445,7 @@ it("pages every retained dossier collection and the native API record", () => { page("swift_string_literals", { ...original, swift_string_literals: [literal], + swift_string_literal_omissions: [], }), ).toMatchObject({ item: [literal], coverage: { total: 1 } }); expect(page("swift_string_literals")).toMatchObject({ diff --git a/src/domain/releaseExposure.ts b/src/domain/releaseExposure.ts index 9d79fb80b0..160e26022e 100644 --- a/src/domain/releaseExposure.ts +++ b/src/domain/releaseExposure.ts @@ -314,9 +314,6 @@ export const auditReleaseExposureInputSchema = z.strictObject({ export type ReleaseExposureReport = z.infer; export type ReleaseExposureCheck = z.infer; -export type AuditReleaseExposureInput = z.infer< - typeof auditReleaseExposureInputSchema ->; export interface ReleaseExposureTargetFact { readonly selected_path: string; diff --git a/src/process/ProcessOwnership.procIdentity.test.ts b/src/process/ProcessOwnership.procIdentity.test.ts index 5e2fb08186..b5f3b855a2 100644 --- a/src/process/ProcessOwnership.procIdentity.test.ts +++ b/src/process/ProcessOwnership.procIdentity.test.ts @@ -544,7 +544,7 @@ it("preserves cancellation before and after the signal-zero probe", async () => const reason = new Error("cancelled"); controller.abort(reason); filesystem.readFile.mockRejectedValueOnce( - Object.assign(new Error("aborted"), { name: "AbortError" }), + Object.assign(new Error("aborted"), { name: "AbortError", cause: reason }), ); await expect( host.processIdentities?.([entry], controller.signal), diff --git a/tests/acceptance/applications/applicationWorkflowCli.test.ts b/tests/acceptance/applications/applicationWorkflowCli.test.ts index 266b8748bd..799db70b8b 100644 --- a/tests/acceptance/applications/applicationWorkflowCli.test.ts +++ b/tests/acceptance/applications/applicationWorkflowCli.test.ts @@ -1652,15 +1652,15 @@ describe("application workflow CLI export Evidence", () => { operation: "compare_javascript_export_shapes", predicate_type: "rea.javascript-export-shape-comparison", normalized_result: { - summary: { added: 1, removed: 0, changed: 0, unknown: 0 }, - changes: [ - { + summary: { added: 1, removed: 0, changed: 0, unknown: 3 }, + changes: expect.arrayContaining([ + expect.objectContaining({ status: "added", path: "/depth", presence: { left: "absent", right: "present" }, right: { availability: "literal", value: 1 }, - }, - ], + }), + ]), }, }); }, 20_000); diff --git a/tests/acceptance/applications/exportedFunctionBindingsCli.test.ts b/tests/acceptance/applications/exportedFunctionBindingsCli.test.ts index 020f147e99..65fdfeeffb 100644 --- a/tests/acceptance/applications/exportedFunctionBindingsCli.test.ts +++ b/tests/acceptance/applications/exportedFunctionBindingsCli.test.ts @@ -541,8 +541,15 @@ describe("CommonJS export identity and scope regressions", () => { expect(comparison).toMatchObject({ left: { status: "selected" }, right: { status: "selected" }, - summary: { added: 0, removed: 0, changed: 0 }, - changes: [], + summary: { added: 0, removed: 0, changed: 0, unknown: 1 }, + changes: [ + { + status: "unknown", + path: "/count", + left: { availability: "unknown" }, + right: { availability: "unknown" }, + }, + ], }); }); }); diff --git a/tests/acceptance/tools/historicalCaptureResources.test.ts b/tests/acceptance/tools/historicalCaptureResources.test.ts index 540e9dad35..d15cc08f9b 100644 --- a/tests/acceptance/tools/historicalCaptureResources.test.ts +++ b/tests/acceptance/tools/historicalCaptureResources.test.ts @@ -1,5 +1,6 @@ import { captureDecoderHeapRecovery, + captureDecoderHeapContinuingRecovery, CAPTURE_DECODER_OLD_GENERATION_SETTING, DEFAULT_CAPTURE_DECODER_HEAP, } from "../../../src/config/captureDecoderHeap.js"; @@ -112,7 +113,9 @@ it.skipIf(process.versions.bun !== undefined && jobLimit === null)( expect(JSON.stringify(response)).not.toContain("caller-marked"); expect(JSON.stringify(response)).not.toContain("rea doctor"); const remediation = String(exhausted.error.remediation?.action ?? ""); - expect(remediation).toContain(captureDecoderHeapRecovery(testHeap)); + expect(remediation).toContain( + captureDecoderHeapContinuingRecovery(testHeap), + ); const failed: unknown = await execute( process.execPath, [ diff --git a/tests/boundary/browser/cdpBrowserProvider.document_script.test.ts b/tests/boundary/browser/cdpBrowserProvider.document_script.test.ts index a46ccb080f..348998d215 100644 --- a/tests/boundary/browser/cdpBrowserProvider.document_script.test.ts +++ b/tests/boundary/browser/cdpBrowserProvider.document_script.test.ts @@ -318,11 +318,16 @@ it.each( method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: `${origin}/app` }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: `${origin}/app`, + }, childFrames: [ { frame: { id: "child", + loaderId: "loader-child", parentId: "frame-main", url: `${origin}/child`, }, @@ -416,7 +421,11 @@ it.each( method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: `${origin}/app` }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: `${origin}/app`, + }, childFrames: [ { frame: { @@ -1213,7 +1222,11 @@ it.each([undefined, 7, ""])( method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: `${origin}/app` }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: `${origin}/app`, + }, childFrames: [ { frame: { diff --git a/tests/boundary/browser/cdpElectronProvider.test.ts b/tests/boundary/browser/cdpElectronProvider.test.ts index de312341ff..3831df1f5c 100644 --- a/tests/boundary/browser/cdpElectronProvider.test.ts +++ b/tests/boundary/browser/cdpElectronProvider.test.ts @@ -44,13 +44,18 @@ it.each(["late", "initial", "destroyed", "final-malformed", "out-of-scope"])( method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: mainUrl }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: mainUrl, + }, childFrames: reads > 1 || kind === "initial" ? [ { frame: { id: "child", + loaderId: "loader-child", parentId: "frame-main", url: kind === "out-of-scope" @@ -66,6 +71,7 @@ it.each(["late", "initial", "destroyed", "final-malformed", "out-of-scope"])( { frame: { id: "child", + loaderId: "loader-child", parentId: "frame-main", url: childUrl, }, @@ -168,17 +174,27 @@ it.each(["reordered", "parent-changed", "path-changed"])( const first = { frame: { id: "first", + loaderId: "loader-first", parentId: reads > 2 && kind === "parent-changed" ? "second" : "frame-main", url: reads > 2 && kind === "path-changed" ? app : child, }, }; const second = { - frame: { id: "second", parentId: "frame-main", url: app }, + frame: { + id: "second", + loaderId: "loader-second", + parentId: "frame-main", + url: app, + }, }; return { frameTree: { - frame: { id: "frame-main", url: main }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: main, + }, childFrames: reads > 2 ? [second, first] : [first, second], }, }; @@ -223,11 +239,16 @@ it.each([ method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: main }, + frame: { id: "frame-main", loaderId: "main", url: main }, childFrames: reads === 1 || kind === "same-document" ? ["first", "second"].map((id) => ({ - frame: { id, parentId: "frame-main", url: child }, + frame: { + id, + loaderId: `loader-${id}`, + parentId: "frame-main", + url: child, + }, })) : [], }, @@ -292,6 +313,7 @@ it.each([ : { frame: { id: "first", + loaderId: "loader-first", parentId: "frame-main", url: "https://foreign.test/", }, @@ -365,11 +387,16 @@ it("excludes srcdoc scripts before source reads while retaining declared labels method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: mainUrl }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: mainUrl, + }, childFrames: [ { frame: { id: "srcdoc", + loaderId: "loader-srcdoc", parentId: "frame-main", url: "about:srcdoc", }, @@ -730,7 +757,11 @@ describe("CdpElectronProvider frame capture", () => { command.method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: main }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: main, + }, childFrames: [ { frame: { @@ -951,7 +982,10 @@ describe("CdpElectronProvider malformed inventory replies", () => { "resources", "Page.getResourceTree", (fileUrl: string) => ({ - frameTree: { frame: { id: "main", url: fileUrl }, resources: null }, + frameTree: { + frame: { id: "main", loaderId: "loader-main", url: fileUrl }, + resources: null, + }, }), ], ["workers", "Target.getTargets", () => ({ targetInfos: null })], @@ -1119,7 +1153,11 @@ it.each( method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: main }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: main, + }, childFrames: phase === "refreshed" && reads === 1 ? [] @@ -1127,6 +1165,7 @@ it.each( { frame: { id: "child", + loaderId: "loader-child", url: child, ...(parent === "missing" ? {} @@ -1136,6 +1175,7 @@ it.each( { frame: { id: "grandchild", + loaderId: "loader-grandchild", parentId: "child", url: child, }, @@ -1240,7 +1280,11 @@ it.each( method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: main }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: main, + }, childFrames: phase === "refreshed" && reads === 1 ? [] @@ -1248,6 +1292,7 @@ it.each( { frame: { id: "parent", + loaderId: "loader-parent", parentId: "frame-main", url: parentUrl, }, @@ -1255,6 +1300,7 @@ it.each( { frame: { id: "local", + loaderId: "loader-local", parentId: "parent", url: child, }, @@ -1356,10 +1402,19 @@ it.each(["about:blank", "https://foreign.invalid/root"])( method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: rootUrl }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: rootUrl, + }, childFrames: [ { - frame: { id: "local", parentId: "frame-main", url: local }, + frame: { + id: "local", + loaderId: "loader-local", + parentId: "frame-main", + url: local, + }, }, ], }, @@ -1414,7 +1469,7 @@ const replayFrameTree = ( reads: number, ) => ({ frameTree: { - frame: { id: "frame-main", url: main }, + frame: { id: "frame-main", loaderId: "loader-frame-main", url: main }, childFrames: reads === 1 || (kind === "during-tree" && reads === 2) ? [ diff --git a/tests/boundary/browser/cdpWebMcpTransientIdentity.test.ts b/tests/boundary/browser/cdpWebMcpTransientIdentity.test.ts index 0faae7ed85..e5f18c1ff8 100644 --- a/tests/boundary/browser/cdpWebMcpTransientIdentity.test.ts +++ b/tests/boundary/browser/cdpWebMcpTransientIdentity.test.ts @@ -53,7 +53,11 @@ it.each( method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: origin + "/app" }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: origin + "/app", + }, childFrames: [{ frame: frame(origin) }], }, } @@ -149,11 +153,16 @@ it.each([ method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: origin + "/app" }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: origin + "/app", + }, childFrames: [ { frame: { id: "child", + loaderId: "loader-child", parentId: "frame-main", url: rawUrl === "allowed" @@ -279,11 +288,16 @@ it.each([ method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: origin + "/app" }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: origin + "/app", + }, childFrames: [ { frame: { id: "child", + loaderId: "loader-child", parentId: "frame-main", url: origin + "/child", }, @@ -349,7 +363,11 @@ it("retains the committed loader through same-document updates before a replacem method === "Page.getFrameTree" ? { frameTree: { - frame: { id: "frame-main", url: origin + "/app" }, + frame: { + id: "frame-main", + loaderId: "loader-frame-main", + url: origin + "/app", + }, childFrames: [ { frame: { diff --git a/tests/boundary/browser/webSourceMapFetcher.test.ts b/tests/boundary/browser/webSourceMapFetcher.test.ts index e25106d1da..fe163b952d 100644 --- a/tests/boundary/browser/webSourceMapFetcher.test.ts +++ b/tests/boundary/browser/webSourceMapFetcher.test.ts @@ -1048,11 +1048,16 @@ describe("source-map operation deadline observations", () => { ]; const result = await fetchWebSourceMapsOk(requests, input(), undefined, { timeoutMs: 100, - fetch: () => { + fetch: (_url, options) => { calls += 1; return calls === 1 ? Promise.resolve(validMapResponse()) - : new Promise(() => undefined); + : new Promise((_resolve, reject) => { + const signal = options?.signal; + signal?.addEventListener("abort", () => reject(signal.reason), { + once: true, + }); + }); }, }); diff --git a/tests/boundary/browser/webSourceMapHttpStatus.test.ts b/tests/boundary/browser/webSourceMapHttpStatus.test.ts index f9819b309a..ded438f4c5 100644 --- a/tests/boundary/browser/webSourceMapHttpStatus.test.ts +++ b/tests/boundary/browser/webSourceMapHttpStatus.test.ts @@ -248,18 +248,22 @@ it.each([ ); if (cancellationFails) { const result = await pending; - expect(result).toMatchObject({ - ok: false, - error: { - _tag: "AnalysisResourceConstraintError", - message: expect.stringContaining("Expanded source-map evidence"), - cleanupIncomplete: true, - cleanupResources: ["source-map-response"], - cause: expect.objectContaining({ - errors: expect.arrayContaining([reason]), - }), - }, - }); + expect(result.ok).toBe(false); + if (result.ok) throw new Error("Expected output admission failure"); + expect(result.error._tag).toBe("AnalysisResourceConstraintError"); + expect(result.error.message).toContain("Expanded source-map evidence"); + expect(result.error.cleanupIncomplete).toBe(true); + expect(result.error.cleanupResources.length).toBe(1); + expect(result.error.cleanupResources[0] === fetchedUrl).toBe(true); + await expect + .poll(() => + result.error.secondaryFailures.some((failure) => + failure.secondaryFailures.some( + (secondary) => secondary.cause === reason, + ), + ), + ) + .toBe(true); } else { await expect(pending).resolves.toMatchObject({ ok: true, diff --git a/tests/boundary/cli/cutterCommands.test.ts b/tests/boundary/cli/cutterCommands.test.ts index c1db5b48ac..ff83dfd847 100644 --- a/tests/boundary/cli/cutterCommands.test.ts +++ b/tests/boundary/cli/cutterCommands.test.ts @@ -184,8 +184,10 @@ describe("Cutter CLI commands", () => { { REA_CUTTER_BRIDGE_DIR: directory }, ); - expect(result.exitCode).toBe(0); - expect(result.stdout).toContain("invalid_request"); + expect(result.exitCode).toBe(1); + expect(JSON.parse(result.stdout)).toMatchObject({ + code: "invalid_request", + }); }); }); diff --git a/tests/boundary/cli/doctorHopperDiscovery.test.ts b/tests/boundary/cli/doctorHopperDiscovery.test.ts index c613dfd2d8..7e9c692bed 100644 --- a/tests/boundary/cli/doctorHopperDiscovery.test.ts +++ b/tests/boundary/cli/doctorHopperDiscovery.test.ts @@ -36,7 +36,8 @@ cliTest.skipIf(!["darwin", "linux"].includes(process.platform))( arguments: ["providers", "--json"], environment, }); - expect(providers.exitCode).toBe(expectedExit); + // Listing providers does not start native analysis or consume its timeout. + expect(providers.exitCode).toBe(0); const doctor = await cli.run({ arguments: ["doctor", "--provider", "hopper", "--json"], environment, diff --git a/tests/boundary/filesystem/clientRegistrationOwnership.test.ts b/tests/boundary/filesystem/clientRegistrationOwnership.test.ts index efc85ad9aa..70ae419583 100644 --- a/tests/boundary/filesystem/clientRegistrationOwnership.test.ts +++ b/tests/boundary/filesystem/clientRegistrationOwnership.test.ts @@ -111,7 +111,7 @@ it.each([ }, ); -it("retains an OpenCode remote registration with a REA-like command", async () => { +it("removes the owned OpenCode launcher despite invalid transport", async () => { const home = await createTestTempDirectory("rea-registration-remote-owner-"); const configPath = join(home, ".config/opencode/opencode.json"); const environment = { XDG_CONFIG_HOME: join(home, ".config") }; @@ -145,10 +145,17 @@ it("retains an OpenCode remote registration with a REA-like command", async () = if (client === undefined) throw new Error("Expected the configured client"); expect(await host.removeClient(client)).toMatchObject({ name: "opencode", - status: "retained", + status: "removed", }); - expect(await readFile(configPath, "utf8")).toBe(source); - expect(await readdir(dirname(configPath))).toEqual(["opencode.json"]); + expect(JSON.parse(await readFile(configPath, "utf8"))).toEqual({ + caller_root: "keep", + mcp: { servers: { peer: { type: "local", command: ["peer"] } } }, + }); + expect(await readFile(`${configPath}.rea.backup`, "utf8")).toBe(source); + expect(await readdir(dirname(configPath))).toEqual([ + "opencode.json", + "opencode.json.rea.backup", + ]); } finally { expect(await host.close?.()).toBeUndefined(); } diff --git a/tests/boundary/filesystem/droidClientConfiguration.test.ts b/tests/boundary/filesystem/droidClientConfiguration.test.ts index 9477300e3e..443ab19022 100644 --- a/tests/boundary/filesystem/droidClientConfiguration.test.ts +++ b/tests/boundary/filesystem/droidClientConfiguration.test.ts @@ -32,7 +32,7 @@ const droidPath = ( describe("Factory Droid configuration paths", () => { it("resolves the user MCP config file and marker directory", () => { - expect(droidPath("/home/a")).toEqual({ + expect(droidPath("/home/a")).toMatchObject({ name: "droid", displayName: "Factory Droid", configPath: join("/home/a", ".factory", "mcp.json"), diff --git a/tests/boundary/filesystem/dshClientConfiguration.test.ts b/tests/boundary/filesystem/dshClientConfiguration.test.ts index d2103f9826..38257e6cd7 100644 --- a/tests/boundary/filesystem/dshClientConfiguration.test.ts +++ b/tests/boundary/filesystem/dshClientConfiguration.test.ts @@ -92,7 +92,7 @@ describe("DeepSeek Harness configuration paths", () => { ); it("resolves the home Cordis patch under the Harness home", () => { - expect(dshClient({})).toEqual({ + expect(dshClient({})).toMatchObject({ name: "dsh", displayName: "DeepSeek Harness", configPath: resolve("/home/a", ".dsh", "cordis.patch.yml"), diff --git a/tests/boundary/filesystem/freebuffClientConfiguration.test.ts b/tests/boundary/filesystem/freebuffClientConfiguration.test.ts index e0c6c737af..00f88ef97a 100644 --- a/tests/boundary/filesystem/freebuffClientConfiguration.test.ts +++ b/tests/boundary/filesystem/freebuffClientConfiguration.test.ts @@ -52,7 +52,7 @@ const freebuffPath = ( describe("FreeBuff configuration paths", () => { it("resolves the shared user agents file and the profile marker directory", () => { - expect(freebuffPath("/home/a")).toEqual({ + expect(freebuffPath("/home/a")).toMatchObject({ name: "freebuff", displayName: "FreeBuff", configPath: join("/home/a", ".agents", "mcp.json"), diff --git a/tests/boundary/filesystem/hermesClientConfiguration.test.ts b/tests/boundary/filesystem/hermesClientConfiguration.test.ts index b4814b3f26..19aa865c92 100644 --- a/tests/boundary/filesystem/hermesClientConfiguration.test.ts +++ b/tests/boundary/filesystem/hermesClientConfiguration.test.ts @@ -83,7 +83,7 @@ describe("Hermes configuration paths", () => { ); it("resolves config.yaml under the platform Hermes home", () => { - expect(hermesPath({})).toEqual({ + expect(hermesPath({})).toMatchObject({ name: "hermes", displayName: "Hermes", configPath: join("/home/a", ".hermes", "config.yaml"), diff --git a/tests/boundary/filesystem/kimiClientConfiguration.test.ts b/tests/boundary/filesystem/kimiClientConfiguration.test.ts index 344ca4a209..2602171063 100644 --- a/tests/boundary/filesystem/kimiClientConfiguration.test.ts +++ b/tests/boundary/filesystem/kimiClientConfiguration.test.ts @@ -59,7 +59,7 @@ describe("Kimi Code configuration paths and skills", () => { "linux", override === undefined ? {} : { KIMI_CODE_HOME: override }, ); - expect(client).toEqual({ + expect(client).toMatchObject({ name: "kimi", displayName: "Kimi Code", configPath: join(directory, "mcp.json"), diff --git a/tests/boundary/filesystem/lmStudioClientConfiguration.test.ts b/tests/boundary/filesystem/lmStudioClientConfiguration.test.ts index 1be933b4b6..97518ec0c3 100644 --- a/tests/boundary/filesystem/lmStudioClientConfiguration.test.ts +++ b/tests/boundary/filesystem/lmStudioClientConfiguration.test.ts @@ -61,7 +61,7 @@ describe("LM Studio home resolution", () => { "resolves mcp.json in the %s home", async (layout) => { const { directory, client } = await installedHome(layout); - expect(client).toEqual({ + expect(client).toMatchObject({ name: "lm_studio", displayName: "LM Studio", configPath: join(directory, "mcp.json"), diff --git a/tests/boundary/filesystem/ompClientConfiguration.test.ts b/tests/boundary/filesystem/ompClientConfiguration.test.ts index c1b6ff624e..8975ef3b2b 100644 --- a/tests/boundary/filesystem/ompClientConfiguration.test.ts +++ b/tests/boundary/filesystem/ompClientConfiguration.test.ts @@ -22,7 +22,7 @@ const ompPath = (env: Parameters[2]) => describe("OMP configuration paths", () => { it("uses the default agent directory and OMP's environment overrides", () => { - expect(ompPath({})).toEqual({ + expect(ompPath({})).toMatchObject({ name: "omp", displayName: "OMP", configPath: "/home/a/.omp/agent/mcp.json", diff --git a/tests/boundary/filesystem/qoderClientConfiguration.test.ts b/tests/boundary/filesystem/qoderClientConfiguration.test.ts index 1661c37456..dbe33bc7d3 100644 --- a/tests/boundary/filesystem/qoderClientConfiguration.test.ts +++ b/tests/boundary/filesystem/qoderClientConfiguration.test.ts @@ -19,7 +19,7 @@ const qoderPath = ( describe("Qoder configuration paths", () => { it("resolves the user settings file and marker directory", () => { - expect(qoderPath("/home/a")).toEqual({ + expect(qoderPath("/home/a")).toMatchObject({ name: "qoder", displayName: "Qoder", configPath: join("/home/a", ".qoder", "settings.json"), diff --git a/tests/boundary/filesystem/qwenClientConfiguration.test.ts b/tests/boundary/filesystem/qwenClientConfiguration.test.ts index 6380bb4ee6..4504ddf94e 100644 --- a/tests/boundary/filesystem/qwenClientConfiguration.test.ts +++ b/tests/boundary/filesystem/qwenClientConfiguration.test.ts @@ -31,7 +31,7 @@ const qwenPath = ( describe("Qwen Code configuration paths", () => { it("resolves the user settings file and marker directory", () => { - expect(qwenPath("/home/a", "linux")).toEqual({ + expect(qwenPath("/home/a", "linux")).toMatchObject({ name: "qwen_code", displayName: "Qwen Code", configPath: join("/home/a", ".qwen", "settings.json"), diff --git a/tests/boundary/filesystem/zcodeClientConfiguration.test.ts b/tests/boundary/filesystem/zcodeClientConfiguration.test.ts index dbaf81143b..77a7e5da21 100644 --- a/tests/boundary/filesystem/zcodeClientConfiguration.test.ts +++ b/tests/boundary/filesystem/zcodeClientConfiguration.test.ts @@ -40,7 +40,7 @@ const zcodePath = ( describe("ZCode configuration paths", () => { it("resolves the user config file and marker directory", () => { - expect(zcodePath("/home/a")).toEqual({ + expect(zcodePath("/home/a")).toMatchObject({ name: "zcode", displayName: "ZCode", configPath: join("/home/a", ".zcode", "cli", "config.json"), diff --git a/tests/boundary/firmware/firmwareIntegration.test.ts b/tests/boundary/firmware/firmwareIntegration.test.ts index 0270baa030..d6287a6871 100644 --- a/tests/boundary/firmware/firmwareIntegration.test.ts +++ b/tests/boundary/firmware/firmwareIntegration.test.ts @@ -17,9 +17,9 @@ import { createToolResultDelivery } from "../../../src/server/toolResult.js"; import { SafeOutputTree } from "../../../src/artifacts/SafeOutputTree.js"; import { SafeOutputTreeCreationFailure } from "../../../src/artifacts/SafeOutputTreeCreationFailure.js"; -// Assert recovery behavior through the public reference table, while keeping -// row identity expectations independent of the instruction's digest. -const gapsWithContinuations = ( +// Extraction-stage assertions use producer-reported gaps. Unclassified byte +// coverage has its own oracle in the selected-interval workflow below. +const stageGapsWithContinuations = ( result: ReturnType, ) => { const instructions = new Map( @@ -28,12 +28,14 @@ const gapsWithContinuations = ( instruction, ]), ); - return result.extraction_gaps.map(({ continuation_id, ...gap }) => { - const instruction = instructions.get(continuation_id); - if (instruction === undefined) - throw new Error("Missing recovery instruction"); - return { ...gap, continuation: instruction }; - }); + return result.extraction_gaps + .filter(({ kind }) => kind !== "unclassified-range") + .map(({ continuation_id, ...gap }) => { + const instruction = instructions.get(continuation_id); + if (instruction === undefined) + throw new Error("Missing recovery instruction"); + return { ...gap, continuation: instruction }; + }); }; const directoryCloseFailure = vi.hoisted(() => ({ @@ -159,6 +161,22 @@ it("extracts a selected interval with verified file identity and parent-byte lin executable_path: fixture.commands.unblob, }); expect(normalized.selection).toMatchObject({ offset: 2, length: 4 }); + const unclassified = normalized.extraction_gaps.filter( + ({ kind }) => kind === "unclassified-range", + ); + expect(unclassified).toEqual([ + expect.objectContaining({ + input_path: "$output/rootfs/config", + range: { offset: 0, length: 14 }, + root_file_range: null, + }), + ]); + expect( + normalized.extraction_continuations.find( + ({ id }) => id === unclassified[0]?.continuation_id, + )?.instruction, + ).toContain("published child"); + expect(normalized.files[0]).toMatchObject({ relative_path: "rootfs/config", size: 14, @@ -324,19 +342,17 @@ it("shares recovery prose across distinct unknown chunk identities", async () => const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - expect(normalized.extraction_gaps).toHaveLength(1000); - expect( - new Set(normalized.extraction_gaps.map(({ provider_id }) => provider_id)) - .size, - ).toBe(1000); - expect(normalized.extraction_continuations).toHaveLength(1); - const [instruction] = normalized.extraction_continuations; + const gaps = normalized.extraction_gaps.filter( + ({ kind }) => kind === "unknown-chunk-handler", + ); + expect(gaps).toHaveLength(1000); + expect(new Set(gaps.map(({ provider_id }) => provider_id)).size).toBe(1000); + const ids = new Set(gaps.map(({ continuation_id }) => continuation_id)); + expect(ids.size).toBe(1); + const instruction = normalized.extraction_continuations.find(({ id }) => + ids.has(id), + ); expect(instruction?.instruction).toContain("inspect_firmware_regions"); - expect( - normalized.extraction_gaps.every( - (gap) => gap.continuation_id === instruction?.id, - ), - ).toBe(true); }); it("retains a task dependency failure when the producer names no executable", async () => { @@ -349,7 +365,7 @@ it("retains a task dependency failure when the producer names no executable", as const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - expect(gapsWithContinuations(normalized)).toEqual([ + expect(stageGapsWithContinuations(normalized)).toEqual([ { kind: "missing-extractor-dependency", input_path: "$input", @@ -374,7 +390,7 @@ it("preserves an unnamed dependency failure on its owning chunk", async () => { const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - expect(gapsWithContinuations(normalized)).toEqual([ + expect(stageGapsWithContinuations(normalized)).toEqual([ { kind: "missing-extractor-dependency", input_path: "$input", @@ -399,7 +415,7 @@ it("routes a nested unknown chunk through its published child", async () => { const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - expect(gapsWithContinuations(normalized)).toEqual( + expect(stageGapsWithContinuations(normalized)).toEqual( expect.arrayContaining([ expect.objectContaining({ kind: "unknown-chunk-handler", @@ -413,10 +429,10 @@ it("routes a nested unknown chunk through its published child", async () => { }), ]), ); - expect(gapsWithContinuations(normalized)[0]?.continuation).toContain( + expect(stageGapsWithContinuations(normalized)[0]?.continuation).toContain( "only if Binwalk is configured with REA_BINWALK_COMMAND", ); - expect(gapsWithContinuations(normalized)[0]?.continuation).toContain( + expect(stageGapsWithContinuations(normalized)[0]?.continuation).toContain( "unavailable without configured Binwalk", ); }); @@ -432,7 +448,7 @@ it("retains every eligible child in a maximum-depth continuation", async () => { const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - const gap = gapsWithContinuations(normalized).find( + const gap = stageGapsWithContinuations(normalized).find( ({ kind, input_path }) => kind === "depth-limited" && input_path === "$output/rootfs/config", ); @@ -454,7 +470,7 @@ it("withholds the nested rerun advice when the published child exceeds the input const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - expect(gapsWithContinuations(normalized)).toEqual([ + expect(stageGapsWithContinuations(normalized)).toEqual([ { kind: "unknown-chunk-handler", input_path: "$output/rootfs/config", @@ -489,7 +505,7 @@ it.each([ ); expect(normalized.depth_limited_paths.length).toBeGreaterThan(0); expect( - gapsWithContinuations(normalized).filter( + stageGapsWithContinuations(normalized).filter( (gap) => gap.kind === "depth-limited", ), ).toEqual( @@ -513,7 +529,7 @@ it("reports a failed multi-file stage as an extractor-failure gap", async () => const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - expect(gapsWithContinuations(normalized)).toEqual( + expect(stageGapsWithContinuations(normalized)).toEqual( expect.arrayContaining([ expect.objectContaining({ kind: "extractor-failure", @@ -539,7 +555,7 @@ it.each(["task-error", "task-warning"])( const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - const failures = gapsWithContinuations(normalized).filter( + const failures = stageGapsWithContinuations(normalized).filter( (gap) => gap.kind === "extractor-failure", ); expect(normalized.diagnostics).toContainEqual( @@ -580,7 +596,7 @@ it("retains independent failures beside dependencies at each extraction owner", const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - const gaps = gapsWithContinuations(normalized); + const gaps = stageGapsWithContinuations(normalized); expect(gaps).toHaveLength(4); for (const provider_id of ["gzip-id", "mixed-id"]) { const owned = gaps.filter((gap) => gap.provider_id === provider_id); @@ -609,7 +625,7 @@ it("carries the owning multi-file stage on a task-level dependency gap", async ( const normalized = firmwareResultSchemas.extract_firmware.parse( result.value.normalized_result, ); - expect(gapsWithContinuations(normalized)).toEqual([ + expect(stageGapsWithContinuations(normalized)).toEqual([ { kind: "missing-extractor-dependency", input_path: "$input", @@ -666,7 +682,7 @@ it.each(["depth", "dependency", "link"])( if (mode === "depth") expect(normalized.depth_limited_paths).toEqual(["$output/rootfs/config"]); if (mode === "depth") - expect(gapsWithContinuations(normalized)).toEqual([ + expect(stageGapsWithContinuations(normalized)).toEqual([ { kind: "depth-limited", input_path: "$output/rootfs/config", @@ -695,7 +711,7 @@ it.each(["depth", "dependency", "link"])( }, ]); if (mode === "dependency") - expect(gapsWithContinuations(normalized)).toEqual([ + expect(stageGapsWithContinuations(normalized)).toEqual([ { kind: "missing-extractor-dependency", input_path: "$input", @@ -723,7 +739,7 @@ it.each(["depth", "dependency", "link"])( link_target_hex: Buffer.from("/etc/passwd", "utf8").toString("hex"), }, ]); - expect(gapsWithContinuations(normalized)).toEqual([ + expect(stageGapsWithContinuations(normalized)).toEqual([ { kind: "unpublished-entry", input_path: "$output/escape", diff --git a/tests/boundary/javascript-recovery/wakaruFileOwnership.test.ts b/tests/boundary/javascript-recovery/wakaruFileOwnership.test.ts index 98c687bee5..d331f83461 100644 --- a/tests/boundary/javascript-recovery/wakaruFileOwnership.test.ts +++ b/tests/boundary/javascript-recovery/wakaruFileOwnership.test.ts @@ -35,9 +35,6 @@ vi.mock("node:fs/promises", async (importOriginal) => { if (typeof args[0] === "string" && fileClose.selected(args[0])) { const close = handle.close.bind(handle); fileClose.handles.push(handle); - onTestFinished(async () => { - if (handle.fd >= 0) await close(); - }); handle.close = async () => { fileClose.cancel?.abort(); if (fileClose.denied) @@ -175,7 +172,11 @@ linuxX64.each([ denied ? ProviderCleanupError : AnalysisCancelledError, ); if (denied) { - const failure = result.error.cause; + const primary = result.error.cleanupPrimary; + expect(primary?.secondaryFailures).toContainEqual( + expect.objectContaining({ cause: controller.signal.reason }), + ); + const failure = primary?.cause; expect(failure).toBeInstanceOf(ArtifactFileCleanupFailure); if (!(failure instanceof ArtifactFileCleanupFailure)) throw new Error("Expected exact file cleanup owner"); diff --git a/tests/boundary/mcp/batchSourcePublicationCleanup.test.ts b/tests/boundary/mcp/batchSourcePublicationCleanup.test.ts index ad680feddf..ef5208f35d 100644 --- a/tests/boundary/mcp/batchSourcePublicationCleanup.test.ts +++ b/tests/boundary/mcp/batchSourcePublicationCleanup.test.ts @@ -17,29 +17,29 @@ import { awaitAcknowledgement } from "../../fixtures/lifecycleAcknowledgement.js const expectRecoveredBatch = (response: CallToolResult, owned: string) => { expect(parseMcpToolError(response).error).toMatchObject({ - code: "resource_constraint", + code: "cleanup_incomplete", details: { - completed_observation: { + resources: [owned], + partial_observation: { evidence: { - normalized_result: { - items: [ - { status: "ok" }, - { - status: "error", - error: { - code: "cleanup_incomplete", - details: { resources: [owned] }, - }, - }, - ], - }, + normalized_result: { items: [{ status: "ok" }], total: 1 }, }, }, + secondary_failures: expect.arrayContaining([ + { + code: "resource_constraint", + category: "resource_constraint", + message: expect.any(String), + retryable: true, + remediation: expect.any(Object), + details: expect.objectContaining({ operation: "record_evidence" }), + }, + ]), }, }); }; -it("keeps a mixed batch cleanup owner after source retention fails and retries it", async () => { +it("keeps a batch cleanup owner and its completed prefix after source retention fails", async () => { const root = await createTestTempDirectory("rea-batch-owned-cleanup-"); const target = join(root, "fixture.com"), owned = join(root, "owned"); diff --git a/tests/boundary/mcp/buildCaptureRetryMcp.test.ts b/tests/boundary/mcp/buildCaptureRetryMcp.test.ts index e4e28f924d..a33daaacc3 100644 --- a/tests/boundary/mcp/buildCaptureRetryMcp.test.ts +++ b/tests/boundary/mcp/buildCaptureRetryMcp.test.ts @@ -240,7 +240,7 @@ it("cancels a capture's start when the computer closes while it waits for the op expect(parseMcpToolError(await started).error.code).toBe("cancelled"); }, 60_000); -it("returns the bundle an export saved when the computer closes while it commits", async () => { +it("reports cancellation and the published bundle when the computer closes during commit", async () => { const { client } = await fakes(); const id = await startBuildComputer(client, BUILD_REQUEST); await captureSeedAndEnd(client, id); @@ -255,10 +255,13 @@ it("returns the bundle an export saved when the computer closes while it commits tasks: [{ task: BUILD_TASK }], directory, }); - expect(exported.isError, JSON.stringify(exported)).not.toBe(true); - expect(exported.structuredContent).toMatchObject({ - computer_id: id, - directory, + expect(parseMcpToolError(exported).error).toMatchObject({ + code: "cancelled", + details: { + diagnostics: { + published_bundle: { directory, files: expect.any(Number) }, + }, + }, }); expect((await readdir(directory)).length).toBeGreaterThan(0); await closed; diff --git a/tests/boundary/mcp/buildWitnessMcp.test.ts b/tests/boundary/mcp/buildWitnessMcp.test.ts index 543f9f81e1..2dd4554f1b 100644 --- a/tests/boundary/mcp/buildWitnessMcp.test.ts +++ b/tests/boundary/mcp/buildWitnessMcp.test.ts @@ -729,7 +729,7 @@ it("saves a witness record again after a failed save, without checking again", a ).toContain("ended"); }); -it("returns the record a witness saved when the computer closes while the save commits", async () => { +it("reports cancellation and the saved witness when the computer closes during commit", async () => { const witness = await witnessed(); const { client, id, check, directory } = witness; await throughNearMiss(witness); @@ -741,11 +741,17 @@ it("returns the record a witness saved when the computer closes while the save c arguments: { computer_id: id }, }); }); - const saved = parsed(await check({ stage: "after_reset" })); - expect(saved.witness?.record).toMatchObject({ - path: join(directory, "witness.json"), - verdict: "held", + const failure = parseMcpToolError( + await check({ stage: "after_reset" }), + ).error; + expect(failure).toMatchObject({ + code: "cancelled", + details: { partial_observation: expect.any(Object) }, }); + const saved = JSON.parse( + await readFile(join(directory, "witness.json"), "utf8"), + ); + expect(saved.normalized_result).toMatchObject({ verdict: "held" }); await closed; }); diff --git a/tests/boundary/process/applicationWorkflowVerifier.test.ts b/tests/boundary/process/applicationWorkflowVerifier.test.ts index 2e10cd563d..b94551bbe2 100644 --- a/tests/boundary/process/applicationWorkflowVerifier.test.ts +++ b/tests/boundary/process/applicationWorkflowVerifier.test.ts @@ -96,14 +96,14 @@ describe("local application workflow verifier", () => { ); const result = JSON.parse(stdout); expect(result.export_shape_comparison).toMatchObject({ - summary: { added: 1, removed: 0, changed: 0, unknown: 0 }, - changes: [ - { + summary: { added: 1, removed: 0, changed: 0, unknown: 3 }, + changes: expect.arrayContaining([ + expect.objectContaining({ status: "added", path: "/depth", right: { availability: "literal", value: 1 }, - }, - ], + }), + ]), }); }, 20_000); }); diff --git a/tests/boundary/process/regexMatcher.test.ts b/tests/boundary/process/regexMatcher.test.ts index abedde7a57..dc628aeadf 100644 --- a/tests/boundary/process/regexMatcher.test.ts +++ b/tests/boundary/process/regexMatcher.test.ts @@ -6,7 +6,7 @@ import { fileURLToPath, pathToFileURL } from "node:url"; import { expect, it } from "vitest"; -import { regexMatchingDeadlineRecovery } from "../../../src/config/regexMatchingDeadline.js"; +import { regexMatchingDeadlineContinuingRecovery } from "../../../src/config/regexMatchingDeadline.js"; import { DEFAULT_REGEX_MATCHING_DEADLINE_MS, MINIMUM_REGEX_WORKER_HEAP_REQUEST_MIB, @@ -476,7 +476,10 @@ it("stops a slow match at the configured deadline and names the setting", async pending_restart: false, }); expect(projected.remediation.action).toContain( - regexMatchingDeadlineRecovery({ deadlineMs: 100, source: "setting" }), + regexMatchingDeadlineContinuingRecovery({ + deadlineMs: 100, + source: "setting", + }), ); expect(matched.error).toMatchObject({ _tag: "AnalysisResourceConstraintError", diff --git a/tests/boundary/providers/kuna/kunaInstallation.test.ts b/tests/boundary/providers/kuna/kunaInstallation.test.ts index a35dc2924b..61b58e7f95 100644 --- a/tests/boundary/providers/kuna/kunaInstallation.test.ts +++ b/tests/boundary/providers/kuna/kunaInstallation.test.ts @@ -13,7 +13,11 @@ import { expect, it as test } from "vitest"; import { runWithAnalysisOperationSnapshot } from "../../../../src/domain/analysisOperationSnapshot.js"; import { kunaInstallationCancellation } from "../../../../src/kuna/KunaInstallation.js"; -import { readKunaConfiguration } from "../../../../src/kuna/KunaConfiguration.js"; +import { + readKunaConfiguration, + kunaFunctionDeadline, + kunaProjectDeadline, +} from "../../../../src/kuna/KunaConfiguration.js"; import { inspectKunaInstallationOwned as inspectKunaInstallation } from "./kunaResourceScope.js"; import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js"; import { fakeKunaCalls, writeFakeKuna } from "./fakeKuna.js"; @@ -256,24 +260,25 @@ it("parses cache, timeout and job settings at the configuration boundary", () => "kuna.function_timeout_ms": 250, }, }, - () => - readKunaConfiguration( + () => { + const parsed = readKunaConfiguration( { ...environment, REA_KUNA_PROJECT_TIMEOUT_MS: "0", REA_KUNA_TIMEOUT_MS: "-1", }, "linux", - ), + ); + if (!parsed.ok) throw new Error(parsed.error.reason); + return { + project: kunaProjectDeadline(parsed.value), + function: kunaFunctionDeadline(parsed.value), + }; + }, ); expect(overridden).toMatchObject({ - ok: true, - value: { - projectTimeoutMs: 4000, - projectTimeoutSource: "runtime", - functionTimeoutMs: 250, - functionTimeoutSource: "runtime", - }, + project: { ms: 4000, source: "runtime" }, + function: { ms: 250, source: "runtime" }, }); expect( runWithAnalysisOperationSnapshot( diff --git a/tests/evaluation/model-evals/AgentEvaluationFixtures.ts b/tests/evaluation/model-evals/AgentEvaluationFixtures.ts index 60a843dd90..590c105ff9 100644 --- a/tests/evaluation/model-evals/AgentEvaluationFixtures.ts +++ b/tests/evaluation/model-evals/AgentEvaluationFixtures.ts @@ -400,12 +400,12 @@ const parserClaims = ( { id: "comparison_coverage", expectedValue: { - status: "complete-within-inputs", + status: "partial", paired_variants: 3, added: 1, removed: 0, changed: 0, - unknown: 0, + unknown: 3, }, source: { ...source, select: comparisonCoverage }, }, diff --git a/tests/process-global/browserScenarioCleanup.test.ts b/tests/process-global/browserScenarioCleanup.test.ts index 8513c1ce98..4d6fc0f188 100644 --- a/tests/process-global/browserScenarioCleanup.test.ts +++ b/tests/process-global/browserScenarioCleanup.test.ts @@ -334,10 +334,6 @@ it("retries a late Playwright open after cancellation before accepting another c await closeStarted; finishFirstClose(); - const failedRetry = await provider.captureScenario(scenario); - expect(failedRetry.ok).toBe(false); - if (!failedRetry.ok) expect(failedRetry.error.cleanupIncomplete).toBe(true); - const retried = await provider.captureScenario(scenario); expect(retried.ok).toBe(true); if (retried.ok) expect(retried.value.steps).toHaveLength(2);