From 3e04cf1461fac829b43b37121e578a3fe3a305c7 Mon Sep 17 00:00:00 2001 From: Dotta Date: Wed, 30 Sep 2026 14:46:45 -0500 Subject: [PATCH] test(runner-e2e): bind active Stop actor and terminal stream Observe the isolated local-board session through the public API, retain it before Stop, and require its exact startup cancellation actor. Explicitly bind both closure and terminal envelopes to the observed turn, normalized session and source, including missing/null-turn negatives. Co-Authored-By: Paperclip --- .../runner-e2e/native-active-stop-evidence.ts | 26 ++++++++++--- tests/runner-e2e/native-active-stop-flow.ts | 13 ++++--- tests/runner-e2e/native-active-stop.test.ts | 37 +++++++++++++++---- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/tests/runner-e2e/native-active-stop-evidence.ts b/tests/runner-e2e/native-active-stop-evidence.ts index ad746983064..89e6ba5c3d4 100644 --- a/tests/runner-e2e/native-active-stop-evidence.ts +++ b/tests/runner-e2e/native-active-stop-evidence.ts @@ -6,11 +6,12 @@ import { readCursorToolEvidence } from "./cursor-native-evidence.js"; import { bootstrapReadExecutionId, withoutProvenBootstrapReads, type BootstrapReadProof } from "./native-bootstrap-read-proof.js"; type Row = Record; +export interface ActiveStopCaller { type: "board"; userId: "local-board"; source: "local_implicit" } export type ActiveStopProvider = "cursor" | "copilot"; export interface ActiveStopScope { provider: ActiveStopProvider; companyId: string; issueId: string; runId: string; target: string; commandSha256?: string } export interface ActiveStopPending { schema: "paperclip.e2e.native-active-stop-pending.v1"; - scope: ActiveStopScope; requestId: string; toolCallId: string; nativeSessionId: string; turnId: string; + caller: ActiveStopCaller; scope: ActiveStopScope; requestId: string; toolCallId: string; nativeSessionId: string; turnId: string; normalizedSessionId: string; sourceInstanceId: string; requestSourceSeq: number; permissionSourceSeq: number; requestRowSha256: string; permissionRowSha256: string; cancellationRequestId: string; observedMonotonicNs: string; } @@ -19,6 +20,18 @@ const id = (v: unknown): v is string => typeof v === "string" && v.length > 0 && const uuid = (v: unknown): v is string => typeof v === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/u.test(v); const hash = (v: unknown) => `sha256:${createHash("sha256").update(canonicalJson(v)).digest("hex")}`; function fail(condition: unknown, reason: string): asserts condition { if (!condition) throw new Error(`Native active Stop: ${reason}`); } +/** The isolated Product server uses local-trusted board authentication. Bind + * the same public session observed by the API client, never infer a null actor. */ +export function readActiveStopCaller(health: unknown, authentication: unknown): ActiveStopCaller { + const session = rec(rec(authentication).session); + fail(rec(health).deploymentMode === "local_trusted" && session.userId === "local-board" + && session.id === "paperclip:local_implicit:local-board", "expected isolated board caller missing"); + return { type: "board", userId: "local-board", source: "local_implicit" }; +} +function isActiveStopCaller(caller: unknown): caller is ActiveStopCaller { + const value = rec(caller); + return value.type === "board" && value.userId === "local-board" && value.source === "local_implicit"; +} function canonicalRows(events: readonly unknown[], scope: ActiveStopScope) { fail(events.length <= 20_000 && events.length > 0, "bounded durable evidence required"); const rows = events.map(rec).filter(row => rec(row.payload).prpEvent !== undefined); @@ -92,15 +105,15 @@ function origin(events: readonly unknown[], scope: ActiveStopScope, bootstrap?: .every(x => rec(x.event.payload).status !== "completed"), "target operation completed"); return { rows, notice, permission, created: created[0]!, request, stream }; } -export function observeActiveStopPending(input: { events: readonly unknown[]; run: Row; issue: Row; scope: ActiveStopScope; cancellationRequestId: string; bootstrap?: BootstrapReadProof }): ActiveStopPending { +export function observeActiveStopPending(input: { events: readonly unknown[]; run: Row; issue: Row; scope: ActiveStopScope; caller: ActiveStopCaller; cancellationRequestId: string; bootstrap?: BootstrapReadProof }): ActiveStopPending { const { run, issue, scope } = input, proof = origin(input.events, scope, input.bootstrap); - fail(uuid(input.cancellationRequestId) && run.id === scope.runId && run.companyId === scope.companyId && run.nativeIssueId === scope.issueId + fail(isActiveStopCaller(input.caller) && uuid(input.cancellationRequestId) && run.id === scope.runId && run.companyId === scope.companyId && run.nativeIssueId === scope.issueId && run.status === "running" && run.runtimeMode === "native" && issue.id === scope.issueId && issue.companyId === scope.companyId && issue.status === "in_progress" && run.resultJson?.startupCancellation == null && run.resultJson?.nativeCancellation == null, "run is not fresh active work"); fail(!(scope.provider === "cursor" ? readCursorToolEvidence(input.events, scope.runId) : readCopilotToolEvidence(input.events, scope.runId)).some(n => n.stage === "permission_delivered"), "permission already answered"); fail(!proof.rows.some(x => terminals.has(x.event.eventType) || closures.has(x.event.eventType)), "request or provider already settled"); fail(!proof.rows.some(x => x.event.eventType === "tool.execution.completed" && rec(x.event.payload).executionId === bootstrapReadExecutionId(proof.notice.toolCallId)), "operation already ended"); - return { schema: "paperclip.e2e.native-active-stop-pending.v1", scope, requestId: proof.notice.requestId!, toolCallId: proof.notice.toolCallId, + return { schema: "paperclip.e2e.native-active-stop-pending.v1", scope, caller: input.caller, requestId: proof.notice.requestId!, toolCallId: proof.notice.toolCallId, nativeSessionId: proof.notice.sessionId, turnId: proof.notice.turnId, normalizedSessionId: proof.permission.event.normalizedSessionId, sourceInstanceId: proof.permission.event.sourceInstanceId, requestSourceSeq: proof.created.event.sourceSeq, permissionSourceSeq: proof.permission.event.sourceSeq, requestRowSha256: hash(proof.created.row), permissionRowSha256: hash(proof.permission.row), cancellationRequestId: input.cancellationRequestId, @@ -108,7 +121,7 @@ export function observeActiveStopPending(input: { events: readonly unknown[]; ru } export function readActiveStopSettlement(input: { events: readonly unknown[]; run: Row; issue: Row; pending: ActiveStopPending; dispatchMonotonicNs: string; bootstrap?: BootstrapReadProof }) { const before = input.pending, { run, issue } = input; - fail(before?.schema === "paperclip.e2e.native-active-stop-pending.v1" && uuid(before.cancellationRequestId) + fail(before?.schema === "paperclip.e2e.native-active-stop-pending.v1" && isActiveStopCaller(before.caller) && uuid(before.cancellationRequestId) && /^[1-9][0-9]{0,29}$/u.test(before.observedMonotonicNs) && /^[1-9][0-9]{0,29}$/u.test(input.dispatchMonotonicNs) && BigInt(before.observedMonotonicNs) < BigInt(input.dispatchMonotonicNs), "pre-dispatch pending observation missing"); const proof = origin(input.events, before.scope, input.bootstrap); @@ -120,7 +133,7 @@ export function readActiveStopSettlement(input: { events: readonly unknown[]; ru const closed = proof.rows.filter(x => closures.has(x.event.eventType)), terminal = proof.rows.filter(x => terminals.has(x.event.eventType)); fail(closed.length === 1 && terminal.length === 1, "one closed request and one terminal required"); const c = closed[0]!, t = terminal[0]!, cp = rec(c.event.payload), tp = rec(t.event.payload); - fail(c.event.eventType === "runtime_request.cancelled" && cp.requestId === before.requestId && cp.turnId === before.turnId + fail(proof.stream(c.event) && proof.stream(t.event) && c.event.eventType === "runtime_request.cancelled" && cp.requestId === before.requestId && cp.turnId === before.turnId && cp.requestKind === "permission_approval" && cp.requestType === "permission" && cp.reason === "explicit_cancellation" && cp.provider === "acpx" && cp.itemId === proof.request.itemId && cp.replayAllowed === false && t.event.eventType === "turn.cancelled" && tp.status === "cancelled" && tp.provider === "acpx" && tp.providerTurnId === before.turnId && tp.error === null && c.event.sourceSeq > Math.max(before.requestSourceSeq, before.permissionSourceSeq) && t.event.sourceSeq > c.event.sourceSeq, @@ -131,6 +144,7 @@ export function readActiveStopSettlement(input: { events: readonly unknown[]; ru const stop = rec(rec(run.resultJson).nativeCancellation), startup = rec(rec(run.resultJson).startupCancellation); fail(run.id === before.scope.runId && run.companyId === before.scope.companyId && run.nativeIssueId === before.scope.issueId && run.runtimeMode === "native" && run.status === "cancelled" && issue.id === before.scope.issueId && issue.companyId === before.scope.companyId && issue.status === "in_progress" + && rec(startup.requestedBy).type === before.caller.type && rec(startup.requestedBy).userId === before.caller.userId && startup.cancellationRequestId === before.cancellationRequestId && stop.intentId === `native-cancellation:${before.cancellationRequestId}` && stop.schema === "paperclip.native-cancellation.v1" && stop.companyId === before.scope.companyId && stop.runId === run.id && stop.issueId === issue.id && stop.scope === "run" && stop.dispatched === true && stop.dispatchState === "acknowledged" && stop.reasonCode === "cancellation_run_only" diff --git a/tests/runner-e2e/native-active-stop-flow.ts b/tests/runner-e2e/native-active-stop-flow.ts index 07c337aa905..0d6c8d7fdde 100644 --- a/tests/runner-e2e/native-active-stop-flow.ts +++ b/tests/runner-e2e/native-active-stop-flow.ts @@ -7,7 +7,7 @@ import { createTaskThroughUi } from "./user-actions.js"; import { createDeniedTargetFixture, exists, observeRunProcesses } from "./copilot-local-fixtures.js"; import { assertCopilotRemoteRetirement, copilotRemoteDeniedSample, prepareCopilotRemoteAction, type CopilotRemoteBootstrap, type CopilotRemoteFixture, type CopilotRemoteSnapshot } from "./copilot-protection-evidence.js"; import { cursorDeniedCommand } from "./cursor-native-evidence.js"; -import { assertActiveStopRetirement, observeActiveStopPending, readActiveStopSettlement, type ActiveStopPending, type ActiveStopScope } from "./native-active-stop-evidence.js"; +import { assertActiveStopRetirement, readActiveStopCaller, observeActiveStopPending, readActiveStopSettlement, type ActiveStopCaller, type ActiveStopPending, type ActiveStopScope } from "./native-active-stop-evidence.js"; import type { BootstrapReadProof } from "./native-bootstrap-read-proof.js"; import type { LiveFixtureValues } from "./live-fixtures.js"; import type { MatrixExecution } from "./types.js"; @@ -17,16 +17,16 @@ export interface ActiveStopState { run: Row; issue: Row; events: readonly unknow /** Retain a real pending API observation before the only mutating request. * Never answer first, wait for natural completion, or infer order from clocks. */ export async function stopAtPendingPermission(input: { - scope: ActiveStopScope; bootstrap?: BootstrapReadProof; deadlineAt: number; + scope: ActiveStopScope; caller: ActiveStopCaller; bootstrap?: BootstrapReadProof; deadlineAt: number; load(): Promise; retain(receipt: ActiveStopPending): Promise; stop(runId: string, cancellationRequestId: string): Promise; }) { const cancellationRequestId = randomUUID(); const state = await input.load(); - const pending = observeActiveStopPending({ ...state, scope: input.scope, cancellationRequestId, bootstrap: input.bootstrap }); + const pending = observeActiveStopPending({ ...state, scope: input.scope, caller: input.caller, cancellationRequestId, bootstrap: input.bootstrap }); await input.retain(pending); - const fresh = observeActiveStopPending({ ...await input.load(), scope: input.scope, cancellationRequestId, bootstrap: input.bootstrap }); + const fresh = observeActiveStopPending({ ...await input.load(), scope: input.scope, caller: input.caller, cancellationRequestId, bootstrap: input.bootstrap }); if (fresh.requestRowSha256 !== pending.requestRowSha256 || fresh.permissionRowSha256 !== pending.permissionRowSha256 || Date.now() >= input.deadlineAt) throw new Error("Native active Stop pending boundary changed or expired"); const dispatchMonotonicNs = process.hrtime.bigint().toString(); const stopped = await input.stop(input.scope.runId, cancellationRequestId); @@ -120,6 +120,7 @@ export async function runNativeActiveStopFlow(input: { ? `${execution.task.buildPrompt(nonce)}\nUse this exact native shell command once:\n${command!.command}` : `${execution.task.buildPrompt(nonce)}\nUse the native edit tool once to create exactly ${target} with content MUST_NOT_EXIST. Do not use a shell command.`; try { + const caller = readActiveStopCaller(await api.get("/api/health"), await api.get("/api/auth/get-session")); const agent = await api.get(`/api/agents/${fixtures.agent.id}`); const configured = await api.patch(`/api/agents/${fixtures.agent.id}`, { adapterConfig: { ...agent.adapterConfig, acpxPermissionMode: "approve-reads", ...(provider === "cursor" ? { acpxSessionMode: "agent" } : {}), timeoutSec: 120, lifecycleMode: "per_turn" } }); check("explicit-per-turn-policy", configured.adapterConfig?.acpxPermissionMode === "approve-reads" && configured.adapterConfig?.lifecycleMode === "per_turn" && (provider !== "cursor" || configured.adapterConfig?.acpxSessionMode === "agent"), "Agent mode and per-turn restrictive permission policy selected before startup"); @@ -140,12 +141,12 @@ export async function runNativeActiveStopFlow(input: { const label = "unanswered native permission"; await pollUntil({ label, deadlineAt: input.deadlineAt, intervalMs: 200, load: async () => { const state = await load(); if (["failed", "timed_out", "cancelled", "succeeded"].includes(state.run.status)) throw new Error(`Stopped waiting for ${label}: provider ended without pending callback`); return state; - }, accept: state => { observeActiveStopPending({ ...state, scope: scope(), cancellationRequestId: randomUUID(), bootstrap: bootstrap() }); return true; } }); + }, accept: state => { observeActiveStopPending({ ...state, scope: scope(), caller, cancellationRequestId: randomUUID(), bootstrap: bootstrap() }); return true; } }); await page.goto(`/${fixtures.company.issuePrefix}/issues/${issue.identifier ?? issue.id}`); const card = page.getByTestId("task-chat-runtime-request").filter({ visible: true }); await expect(card).toHaveCount(1); await expect(card.getByRole("button", { name: "Deny", exact: true })).toBeEnabled(); await input.capture("pending-permission", "Native permission left unanswered before Stop", "pending-permission.png"); await sample("pending"); - completed = await stopAtPendingPermission({ scope: scope(), bootstrap: bootstrap(), deadlineAt: input.deadlineAt, load, + completed = await stopAtPendingPermission({ scope: scope(), caller, bootstrap: bootstrap(), deadlineAt: input.deadlineAt, load, retain: proof => input.evidence("native-active-stop-pending.json", proof), stop: (runId, cancellationRequestId) => api.post(`/api/heartbeat-runs/${runId}/cancel`, { cancellationRequestId }) }); check("pending-callback-cancelled", true, "Unanswered native permission closed through a cancelled provider turn and the exact caller-owned Stop acknowledgement"); await input.evidence("native-active-stop-settlement.json", completed); diff --git a/tests/runner-e2e/native-active-stop.test.ts b/tests/runner-e2e/native-active-stop.test.ts index a320ab95e55..86bdf28bec8 100644 --- a/tests/runner-e2e/native-active-stop.test.ts +++ b/tests/runner-e2e/native-active-stop.test.ts @@ -1,10 +1,11 @@ import { createHash } from "node:crypto"; import { describe, expect, it, vi } from "vitest"; -import { assertActiveStopRetirement, observeActiveStopPending, readActiveStopSettlement, type ActiveStopProvider } from "./native-active-stop-evidence.js"; +import { assertActiveStopRetirement, readActiveStopCaller, observeActiveStopPending, readActiveStopSettlement, type ActiveStopProvider } from "./native-active-stop-evidence.js"; import { stopAtPendingPermission } from "./native-active-stop-flow.js"; import { runnerMatrix, runnerSuites, suiteDefinitionHash } from "./catalog.js"; import { selectRunnerExecutions, parseRunnerSelectors } from "./selectors.js"; +const caller = readActiveStopCaller({ deploymentMode: "local_trusted" }, { session: { userId: "local-board", id: "paperclip:local_implicit:local-board" } }); const cancellationRequestId = "11111111-2222-4333-8444-555555555555"; type Row = Record; function fixture(provider: ActiveStopProvider = "copilot") { @@ -28,11 +29,11 @@ function fixture(provider: ActiveStopProvider = "copilot") { const run: Row = { id: "run", companyId: "company", nativeIssueId: "issue", runtimeMode: "native", status: "running", resultJson: {} }; const issue = { id: "issue", companyId: "company", status: "in_progress" }; const state = () => ({ events, run, issue }); - const pending = () => observeActiveStopPending({ ...state(), scope, cancellationRequestId }); + const pending = () => observeActiveStopPending({ ...state(), scope, caller, cancellationRequestId }); const settle = (requestId = cancellationRequestId) => { events.push(row(5, "runtime_request.cancelled", { provider: "acpx", requestId: "request", turnId: "turn", requestKind: "permission_approval", requestType: "permission", itemId: "tool", reason: "explicit_cancellation", replayAllowed: false }), row(6, "turn.cancelled", { provider: "acpx", providerTurnId: "turn", status: "cancelled", error: null })); - run.status = "cancelled"; run.resultJson = { startupCancellation: { cancellationRequestId: requestId }, nativeCancellation: { + run.status = "cancelled"; run.resultJson = { startupCancellation: { cancellationRequestId: requestId, requestedBy: { type: "board", userId: "local-board" } }, nativeCancellation: { schema: "paperclip.native-cancellation.v1", intentId: `native-cancellation:${requestId}`, companyId: "company", runId: "run", issueId: "issue", scope: "run", dispatched: true, dispatchState: "acknowledged", reasonCode: "cancellation_run_only", effects: ["release_run_resources"], intentAuditId: "audit-intent", acknowledgementAuditId: "audit-ack", } }; @@ -61,9 +62,9 @@ describe("definitely active native permission Stop", () => { f.notice(3, "tool", { toolCallId: "bootstrap", operation: "read", status: "completed", readTargetSha256 }), f.row(4, "tool.execution.completed", { schema: "paperclip.tool.execution.v1", executionId: "bootstrap", transport: "builtin", status: "completed", operation: "read", target: actionFile })); const bootstrap = { actionFile, events: f.events }; - expect(() => observeActiveStopPending({ ...f.state(), scope: f.scope, cancellationRequestId, bootstrap })).not.toThrow(); + expect(() => observeActiveStopPending({ ...f.state(), scope: f.scope, caller, cancellationRequestId, bootstrap })).not.toThrow(); payload(f.events[2]!).details.find((d: Row) => d.name === "readTargetSha256").value = `sha256:${"0".repeat(64)}`; - expect(() => observeActiveStopPending({ ...f.state(), scope: f.scope, cancellationRequestId, bootstrap })).toThrow(/bootstrap/); + expect(() => observeActiveStopPending({ ...f.state(), scope: f.scope, caller, cancellationRequestId, bootstrap })).toThrow(/bootstrap/); }); it("does not compare remote wall clock or transaction timestamps to the operator clock", () => { const f = fixture(); for (const row of f.events) { frame(row).emittedAt = "2099-01-01T00:00:00Z"; row.createdAt = "1900-01-01T00:00:00Z"; } @@ -97,6 +98,11 @@ describe("definitely active native permission Stop", () => { ["replay permitted", ({ f }: ReturnType) => { payload(f.events[4]!).replayAllowed = true; }], ["tampered pending row", ({ f }: ReturnType) => { payload(f.events[3]!).request.itemId = "changed"; }], ["missing pending receipt", (s: ReturnType) => { s.pending.requestRowSha256 = ""; }], + ["missing caller", ({ f }: ReturnType) => { delete f.run.resultJson.startupCancellation.requestedBy; }], + ["null caller", ({ f }: ReturnType) => { f.run.resultJson.startupCancellation.requestedBy.userId = null; }], + ["foreign caller", ({ f }: ReturnType) => { f.run.resultJson.startupCancellation.requestedBy.userId = "another-board-user"; }], + ["agent caller", ({ f }: ReturnType) => { f.run.resultJson.startupCancellation.requestedBy.type = "agent"; }], + ["unbound caller receipt", (s: ReturnType) => { (s.pending as Row).caller = null; }], ["foreign intent", ({ f }: ReturnType) => { f.run.resultJson.nativeCancellation.intentId = "native-cancellation:other"; }], ["foreign ack scope", ({ f }: ReturnType) => { f.run.resultJson.nativeCancellation.companyId = "other"; }], ["undispatched cancellation", ({ f }: ReturnType) => { f.run.resultJson.nativeCancellation.dispatched = false; }], @@ -107,6 +113,21 @@ describe("definitely active native permission Stop", () => { ["native session switched", ({ f }: ReturnType) => { payload(f.events[0]!).provenance.sessionId = "other"; }], ["target operation completed", ({ f }: ReturnType) => { f.events.push(f.row(7, "tool.execution.completed", { schema: "paperclip.tool.execution.v1", transport: "builtin", executionId: "tool", status: "completed" })); }], ] as const)("rejects %s", (_label, mutate) => { const s = settled(); mutate(s); expect(s.read).toThrow(); }); + it.each(["closure", "terminal"] as const)("rejects missing/null envelope turn and foreign null-turn stream on %s", kind => { + for (const turn of [null, undefined]) for (const mismatch of ["none", "session", "source"]) { + const s = settled(), event = frame(s.f.events[kind === "closure" ? 4 : 5]!); + event.turnId = turn; + if (mismatch === "session") event.normalizedSessionId = "foreign-session"; + if (mismatch === "source") { event.sourceInstanceId = "foreign-source"; event.sourceEventId = `foreign-source:run:${event.sourceSeq}`; } + expect(s.read).toThrow(/pending callback/); + } + }); + it.each([ + [{ deploymentMode: "authenticated" }, { session: { userId: "local-board", id: "paperclip:local_implicit:local-board" } }], + [{ deploymentMode: "local_trusted" }, { session: { userId: null, id: "paperclip:local_implicit:local-board" } }], + [{ deploymentMode: "local_trusted" }, { session: { userId: "another", id: "paperclip:local_implicit:another" } }], + [{ deploymentMode: "local_trusted" }, { session: { userId: "local-board", id: "paperclip:session:local-board" } }], + ])("rejects a non-fixture authentication context", (health, session) => expect(() => readActiveStopCaller(health, session)).toThrow()); it("rejects a Stop dispatch not causally after the pending observation", () => { const { f, pending } = settled(); expect(() => readActiveStopSettlement({ ...f.state(), pending, dispatchMonotonicNs: pending.observedMonotonicNs })).toThrow(/pre-dispatch/); @@ -117,7 +138,7 @@ describe("active Stop flow wiring", () => { it("awaits retention and rechecks pending before issuing the single caller UUID request", async () => { const f = fixture(); const order: string[] = []; let retainedId = ""; const stop = vi.fn(async (runId: string, id: string) => { expect(runId).toBe("run"); expect(id).toBe(retainedId); order.push("stop"); return f.settle(id); }); - const result = await stopAtPendingPermission({ scope: f.scope, deadlineAt: Date.now() + 1000, + const result = await stopAtPendingPermission({ scope: f.scope, caller, deadlineAt: Date.now() + 1000, load: async () => { order.push("load"); return f.state(); }, retain: async receipt => { order.push("retain-start"); await Promise.resolve(); retainedId = receipt.cancellationRequestId; order.push("retain-done"); }, stop }); expect(order).toEqual(["load", "retain-start", "retain-done", "load", "stop", "load"]); @@ -125,13 +146,13 @@ describe("active Stop flow wiring", () => { }); it.each(["retention failed", "competing Stop", "answered after capture"])("never sends Stop when %s", async mode => { const f = fixture(), stop = vi.fn(); - await expect(stopAtPendingPermission({ scope: f.scope, deadlineAt: Date.now() + 1000, load: async () => f.state(), stop, + await expect(stopAtPendingPermission({ scope: f.scope, caller, deadlineAt: Date.now() + 1000, load: async () => f.state(), stop, retain: async () => { if (mode === "retention failed") throw Error("disk failure"); if (mode === "competing Stop") f.run.resultJson.startupCancellation = { cancellationRequestId: "foreign" }; else f.events.push(f.row(5, "runtime_request.resolved", {})); }, })).rejects.toThrow(); expect(stop).not.toHaveBeenCalled(); }); it("fails promptly on a definitive provider failure before calling a throwing evidence reader", async () => { const f = fixture(); const started = Date.now(); - await expect(stopAtPendingPermission({ scope: f.scope, deadlineAt: started + 10000, load: async () => f.state(), retain: async () => {}, stop: async (_run, id) => { + await expect(stopAtPendingPermission({ scope: f.scope, caller, deadlineAt: started + 10000, load: async () => f.state(), retain: async () => {}, stop: async (_run, id) => { f.settle(id); f.run.status = "failed"; f.events.length = 0; return f.run; } })).rejects.toThrow(/Stopped waiting.*unexpected run terminal/); expect(Date.now() - started).toBeLessThan(500);