Merge remote-tracking branch 'origin/master' into fix/runner-paid-matrix-integrity

* origin/master:
  chore(deps-dev): bump rollup from 4.62.4 to 4.63.1 (#12570)
  ci(runner): route paid matrix to AWS fleet (#12765)
  fix(paperclip-runner): emit turn.accepted before any terminal turn event (#12752)
  test(heartbeat): drain in-flight runs before native-isolation TRUNCATE (#12751)

# Conflicts:
#	.github/workflows/runner-full-stack-e2e.yml
#	server/src/__tests__/heartbeat-direct-adapter-native-isolation.test.ts
#	tests/runner-e2e/README.md
#	tests/runner-e2e/SECURITY.md
#	tests/runner-e2e/workflow-security.test.ts
This commit is contained in:
Dotta 2026-09-03 09:54:20 -05:00
commit ec428113dc
9 changed files with 279 additions and 142 deletions

View File

@ -463,4 +463,106 @@ describe("Codex app-server Codex driver", () => {
});
});
it("orders turn.accepted before a terminal event even when the provider notifies the terminal turn ahead of the turn/start response", async () => {
const transport = new FakeCodexTransport();
let resolveTurnStart: (value: Record<string, unknown>) => void = () => {};
transport.turnStartResponse = new Promise((resolve) => {
resolveTurnStart = resolve;
});
const session = await makeDriver([transport]).openSession({
runId: "run-terminal-race",
normalizedSessionId: "normalized-terminal-race",
workingDirectory: WORKSPACE,
});
const startTurnPromise = session.startTurn({
message: { role: "user", text: "Race the terminal event." },
});
// Give the provider's turn/started and turn/completed notifications every
// chance to run ahead of the still-pending turn/start response, the way
// one read chunk can carry all three JSON-RPC lines back to back.
transport.push("turn/started", {
threadId: "thread-1",
turn: { id: "turn-1", status: "inProgress" },
});
transport.push("turn/completed", {
threadId: "thread-1",
turn: {
id: "turn-1",
status: "failed",
items: [],
error: { message: "provider rejected the turn" },
},
});
// A macrotask boundary drains every microtask the notification pump can
// run on its own, so an unguarded terminal handler has already run by
// the time the turn/start response resolves below.
await new Promise((resolve) => setImmediate(resolve));
resolveTurnStart({
turn: { id: "turn-1", status: "inProgress", items: [] },
});
const turn = await startTurnPromise;
expect(turn.turnId).toBe("turn-1");
const events = await collectUntilTerminal(session.events());
const eventTypes = events.map((event) => event.eventType);
expect(eventTypes).toEqual(
expect.arrayContaining(["turn.started", "turn.accepted", "turn.failed"]),
);
expect(eventTypes.indexOf("turn.started")).toBeLessThan(
eventTypes.indexOf("turn.accepted"),
);
expect(eventTypes.indexOf("turn.accepted")).toBeLessThan(
eventTypes.indexOf("turn.failed"),
);
expect(
events.some((event) => event.eventType === "session.failed"),
).toBe(false);
});
it("does not release a terminal event for a turn when turn/start itself rejects", async () => {
const transport = new FakeCodexTransport();
let rejectTurnStart: (error: Error) => void = () => {};
transport.turnStartResponse = new Promise((_resolve, reject) => {
rejectTurnStart = reject;
});
const session = await makeDriver([transport]).openSession({
runId: "run-terminal-reject-race",
normalizedSessionId: "normalized-terminal-reject-race",
workingDirectory: WORKSPACE,
});
const startTurnPromise = session.startTurn({
message: { role: "user", text: "Race the terminal event against a rejection." },
});
// The provider notifies turn/started and turn/completed ahead of its own
// turn/start response, then that response rejects. No turn was ever
// accepted, so neither notification may release a terminal event.
transport.push("turn/started", {
threadId: "thread-1",
turn: { id: "turn-1", status: "inProgress" },
});
transport.push("turn/completed", {
threadId: "thread-1",
turn: {
id: "turn-1",
status: "failed",
items: [],
error: { message: "provider rejected the turn" },
},
});
await new Promise((resolve) => setImmediate(resolve));
rejectTurnStart(new CodexRpcError("turn/start rejected by provider", -32000));
await expect(startTurnPromise).rejects.toThrow(
"turn/start rejected by provider",
);
const events = await collectUntilTerminal(session.events());
const eventTypes = events.map((event) => event.eventType);
expect(eventTypes).not.toContain("turn.accepted");
expect(eventTypes).not.toContain("turn.completed");
expect(eventTypes).not.toContain("turn.failed");
expect(eventTypes).toContain("session.failed");
});
});

View File

@ -164,6 +164,10 @@ export class CodexHarnessSession extends CodexSessionState implements HarnessSes
effectiveCollaborationMode,
});
this.turnStartPending = true;
let releaseTurnStartSettled: () => void = () => {};
this.turnStartSettled = new Promise((resolve) => {
releaseTurnStartSettled = resolve;
});
let response: Record<string, unknown>;
const requestedMode = this.opened.context.collaborationMode;
try {
@ -184,6 +188,13 @@ export class CodexHarnessSession extends CodexSessionState implements HarnessSes
: { outputSchema: CODEX_RESULT_OUTPUT_SCHEMA }),
});
} catch (error) {
// A turn/started notification can arrive and mark a turn active while
// turn/start is still pending. The turn/start request just rejected,
// so no turn was accepted. Roll that optimistic state back so a
// terminal notification for it cannot pass the active-turn check below
// and release a terminal event for a turn that was never accepted.
this.activeTurnId = null;
this.turnStarted = false;
if (dispositionOnlyRecovery) {
if (error instanceof CodexRpcError) {
// A JSON-RPC error is a definite provider rejection: no turn was
@ -200,6 +211,11 @@ export class CodexHarnessSession extends CodexSessionState implements HarnessSes
throw error;
} finally {
this.turnStartPending = false;
// Release a terminal notification that arrived and parked itself
// while this turn/start was in flight. This runs before turn.accepted
// below, in the same synchronous continuation, so a released waiter
// never observes the terminal turn ahead of turn.accepted.
releaseTurnStartSettled();
}
const turn = record(response.turn);
const turnId = text(turn.id);

View File

@ -37,7 +37,7 @@ import {
export async function pumpNotifications(state: CodexSessionState): Promise<void> {
try {
for await (const notification of state.transport.notifications()) {
mapNotification(state, notification);
await mapNotification(state, notification);
}
} catch (error) {
state.emit("harness.diagnostic", {
@ -52,11 +52,11 @@ export async function pumpNotifications(state: CodexSessionState): Promise<void>
}
}
function mapNotification(state: CodexSessionState, notification: CodexRpcNotification): void {
async function mapNotification(state: CodexSessionState, notification: CodexRpcNotification): Promise<void> {
const sourceSequenceBefore = state.sourceSequence;
let rejected = false;
try {
mapNotificationBody(state, notification);
await mapNotificationBody(state, notification);
} catch (error) {
rejected = true;
throw error;
@ -98,7 +98,7 @@ function mapNotification(state: CodexSessionState, notification: CodexRpcNotific
}
}
function mapNotificationBody(state: CodexSessionState, notification: CodexRpcNotification): void {
async function mapNotificationBody(state: CodexSessionState, notification: CodexRpcNotification): Promise<void> {
if (!isSupportedCodexNotificationMethod(notification.method)) return;
if (!isBoundCodexNotification(notification, {
runId: state.runId,
@ -365,6 +365,11 @@ function mapNotificationBody(state: CodexSessionState, notification: CodexRpcNot
return;
}
if (notification.method === "turn/completed") {
// A terminal notification can arrive on the provider's notification
// channel before turn/start's own response settles on the request
// channel. Wait for the pending turn/start to settle first, so
// turn.accepted always precedes the terminal event for the same turn.
await state.turnStartSettled;
if (state.terminalTurns.has(turnId)) {
mapTerminalTurn(state, turn, turnId);
return;

View File

@ -82,6 +82,14 @@ export class CodexSessionState {
resultCallId: string | null = null;
resultTurnId: string | null = null;
turnStartPending = false;
/**
* Resolves once a pending turn/start settles, on the accepted path or on a
* provider rejection. A terminal notification for that turn must wait on
* this promise, so turn.accepted always precedes any terminal event for
* the same turn even when the provider notifies the terminal turn before
* the turn/start response arrives.
*/
turnStartSettled: Promise<void> = Promise.resolve();
protocolFailed = false;
protocolFailureCode: string | null = null;
protocolFailureMessage: string | null = null;

View File

@ -34,7 +34,7 @@
"@types/node": "^24.0.0",
"@types/react": "^19.2.18",
"esbuild": "^0.28.2",
"rollup": "^4.62.4",
"rollup": "^4.63.1",
"tslib": "^2.8.1",
"typescript": "^7.0.2",
"vitest": "^4.1.10"

View File

@ -35,7 +35,7 @@
"@types/node": "^24.0.0",
"@types/react": "^19.2.18",
"esbuild": "^0.28.2",
"rollup": "^4.62.4",
"rollup": "^4.63.1",
"tslib": "^2.8.1",
"typescript": "^7.0.2",
"vitest": "^4.1.10"

View File

@ -44,7 +44,7 @@
"@types/react-dom": "^19.2.4",
"esbuild": "^0.28.2",
"react-dom": "^19.2.8",
"rollup": "^4.62.4",
"rollup": "^4.63.1",
"tslib": "^2.8.1",
"typescript": "^7.0.2",
"vitest": "^4.1.10"

View File

@ -560,10 +560,10 @@ importers:
devDependencies:
'@rollup/plugin-node-resolve':
specifier: ^16.0.1
version: 16.0.3(rollup@4.62.4)
version: 16.0.3(rollup@4.63.1)
'@rollup/plugin-typescript':
specifier: ^12.1.2
version: 12.3.0(rollup@4.62.4)(tslib@2.8.1)(typescript@7.0.2)
version: 12.3.0(rollup@4.63.1)(tslib@2.8.1)(typescript@7.0.2)
'@types/node':
specifier: ^24.0.0
version: 24.13.3
@ -575,7 +575,7 @@ importers:
version: 0.28.2
rollup:
specifier: '>=4.59.0'
version: 4.62.4
version: 4.63.1
tslib:
specifier: ^2.8.1
version: 2.8.1
@ -711,10 +711,10 @@ importers:
version: link:../sdk
'@rollup/plugin-node-resolve':
specifier: ^16.0.1
version: 16.0.3(rollup@4.62.4)
version: 16.0.3(rollup@4.63.1)
'@rollup/plugin-typescript':
specifier: ^12.1.2
version: 12.3.0(rollup@4.62.4)(tslib@2.8.1)(typescript@7.0.2)
version: 12.3.0(rollup@4.63.1)(tslib@2.8.1)(typescript@7.0.2)
'@types/node':
specifier: ^24.0.0
version: 24.13.3
@ -732,7 +732,7 @@ importers:
version: 19.2.8(react@19.2.8)
rollup:
specifier: '>=4.59.0'
version: 4.62.4
version: 4.63.1
tslib:
specifier: ^2.8.1
version: 2.8.1
@ -1146,10 +1146,10 @@ importers:
version: 10.5.10(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))
'@storybook/addon-docs':
specifier: 10.5.10
version: 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
version: 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.2)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
'@storybook/react-vite':
specifier: 10.5.10
version: 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
version: 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
'@tailwindcss/vite':
specifier: ^4.3.3
version: 4.3.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
@ -4285,128 +4285,128 @@ packages:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.62.4':
resolution: {integrity: sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==}
'@rollup/rollup-android-arm-eabi@4.63.1':
resolution: {integrity: sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.62.4':
resolution: {integrity: sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==}
'@rollup/rollup-android-arm64@4.63.1':
resolution: {integrity: sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.62.4':
resolution: {integrity: sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==}
'@rollup/rollup-darwin-arm64@4.63.1':
resolution: {integrity: sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.62.4':
resolution: {integrity: sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==}
'@rollup/rollup-darwin-x64@4.63.1':
resolution: {integrity: sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.62.4':
resolution: {integrity: sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==}
'@rollup/rollup-freebsd-arm64@4.63.1':
resolution: {integrity: sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.62.4':
resolution: {integrity: sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==}
'@rollup/rollup-freebsd-x64@4.63.1':
resolution: {integrity: sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.62.4':
resolution: {integrity: sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==}
'@rollup/rollup-linux-arm-gnueabihf@4.63.1':
resolution: {integrity: sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.62.4':
resolution: {integrity: sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==}
'@rollup/rollup-linux-arm-musleabihf@4.63.1':
resolution: {integrity: sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.62.4':
resolution: {integrity: sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==}
'@rollup/rollup-linux-arm64-gnu@4.63.1':
resolution: {integrity: sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.62.4':
resolution: {integrity: sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==}
'@rollup/rollup-linux-arm64-musl@4.63.1':
resolution: {integrity: sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-loong64-gnu@4.62.4':
resolution: {integrity: sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==}
'@rollup/rollup-linux-loong64-gnu@4.63.1':
resolution: {integrity: sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-loong64-musl@4.62.4':
resolution: {integrity: sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==}
'@rollup/rollup-linux-loong64-musl@4.63.1':
resolution: {integrity: sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.62.4':
resolution: {integrity: sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==}
'@rollup/rollup-linux-ppc64-gnu@4.63.1':
resolution: {integrity: sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-ppc64-musl@4.62.4':
resolution: {integrity: sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==}
'@rollup/rollup-linux-ppc64-musl@4.63.1':
resolution: {integrity: sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.62.4':
resolution: {integrity: sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==}
'@rollup/rollup-linux-riscv64-gnu@4.63.1':
resolution: {integrity: sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.62.4':
resolution: {integrity: sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==}
'@rollup/rollup-linux-riscv64-musl@4.63.1':
resolution: {integrity: sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.62.4':
resolution: {integrity: sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==}
'@rollup/rollup-linux-s390x-gnu@4.63.1':
resolution: {integrity: sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.62.4':
resolution: {integrity: sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==}
'@rollup/rollup-linux-x64-gnu@4.63.1':
resolution: {integrity: sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.62.4':
resolution: {integrity: sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==}
'@rollup/rollup-linux-x64-musl@4.63.1':
resolution: {integrity: sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==}
cpu: [x64]
os: [linux]
'@rollup/rollup-openbsd-x64@4.62.4':
resolution: {integrity: sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==}
'@rollup/rollup-openbsd-x64@4.63.1':
resolution: {integrity: sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.62.4':
resolution: {integrity: sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==}
'@rollup/rollup-openharmony-arm64@4.63.1':
resolution: {integrity: sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.62.4':
resolution: {integrity: sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==}
'@rollup/rollup-win32-arm64-msvc@4.63.1':
resolution: {integrity: sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.62.4':
resolution: {integrity: sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==}
'@rollup/rollup-win32-ia32-msvc@4.63.1':
resolution: {integrity: sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.62.4':
resolution: {integrity: sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==}
'@rollup/rollup-win32-x64-gnu@4.63.1':
resolution: {integrity: sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.62.4':
resolution: {integrity: sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==}
'@rollup/rollup-win32-x64-msvc@4.63.1':
resolution: {integrity: sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==}
cpu: [x64]
os: [win32]
@ -7692,8 +7692,8 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
rollup@4.62.4:
resolution: {integrity: sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==}
rollup@4.63.1:
resolution: {integrity: sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@ -11576,114 +11576,114 @@ snapshots:
'@rolldown/pluginutils@1.0.1': {}
'@rollup/plugin-node-resolve@16.0.3(rollup@4.62.4)':
'@rollup/plugin-node-resolve@16.0.3(rollup@4.63.1)':
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.62.4)
'@rollup/pluginutils': 5.3.0(rollup@4.63.1)
'@types/resolve': 1.20.2
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.11
optionalDependencies:
rollup: 4.62.4
rollup: 4.63.1
'@rollup/plugin-typescript@12.3.0(rollup@4.62.4)(tslib@2.8.1)(typescript@7.0.2)':
'@rollup/plugin-typescript@12.3.0(rollup@4.63.1)(tslib@2.8.1)(typescript@7.0.2)':
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.62.4)
'@rollup/pluginutils': 5.3.0(rollup@4.63.1)
resolve: 1.22.11
typescript: 7.0.2
optionalDependencies:
rollup: 4.62.4
rollup: 4.63.1
tslib: 2.8.1
'@rollup/pluginutils@5.3.0(rollup@4.62.4)':
'@rollup/pluginutils@5.3.0(rollup@4.63.1)':
dependencies:
'@types/estree': 1.0.9
estree-walker: 2.0.2
picomatch: 4.0.7
optionalDependencies:
rollup: 4.62.4
rollup: 4.63.1
'@rollup/pluginutils@5.4.0(rollup@4.62.4)':
'@rollup/pluginutils@5.4.0(rollup@4.63.1)':
dependencies:
'@types/estree': 1.0.9
estree-walker: 2.0.2
picomatch: 4.0.7
optionalDependencies:
rollup: 4.62.4
rollup: 4.63.1
'@rollup/rollup-android-arm-eabi@4.62.4':
'@rollup/rollup-android-arm-eabi@4.63.1':
optional: true
'@rollup/rollup-android-arm64@4.62.4':
'@rollup/rollup-android-arm64@4.63.1':
optional: true
'@rollup/rollup-darwin-arm64@4.62.4':
'@rollup/rollup-darwin-arm64@4.63.1':
optional: true
'@rollup/rollup-darwin-x64@4.62.4':
'@rollup/rollup-darwin-x64@4.63.1':
optional: true
'@rollup/rollup-freebsd-arm64@4.62.4':
'@rollup/rollup-freebsd-arm64@4.63.1':
optional: true
'@rollup/rollup-freebsd-x64@4.62.4':
'@rollup/rollup-freebsd-x64@4.63.1':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.62.4':
'@rollup/rollup-linux-arm-gnueabihf@4.63.1':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.62.4':
'@rollup/rollup-linux-arm-musleabihf@4.63.1':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.62.4':
'@rollup/rollup-linux-arm64-gnu@4.63.1':
optional: true
'@rollup/rollup-linux-arm64-musl@4.62.4':
'@rollup/rollup-linux-arm64-musl@4.63.1':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.62.4':
'@rollup/rollup-linux-loong64-gnu@4.63.1':
optional: true
'@rollup/rollup-linux-loong64-musl@4.62.4':
'@rollup/rollup-linux-loong64-musl@4.63.1':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.62.4':
'@rollup/rollup-linux-ppc64-gnu@4.63.1':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.62.4':
'@rollup/rollup-linux-ppc64-musl@4.63.1':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.62.4':
'@rollup/rollup-linux-riscv64-gnu@4.63.1':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.62.4':
'@rollup/rollup-linux-riscv64-musl@4.63.1':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.62.4':
'@rollup/rollup-linux-s390x-gnu@4.63.1':
optional: true
'@rollup/rollup-linux-x64-gnu@4.62.4':
'@rollup/rollup-linux-x64-gnu@4.63.1':
optional: true
'@rollup/rollup-linux-x64-musl@4.62.4':
'@rollup/rollup-linux-x64-musl@4.63.1':
optional: true
'@rollup/rollup-openbsd-x64@4.62.4':
'@rollup/rollup-openbsd-x64@4.63.1':
optional: true
'@rollup/rollup-openharmony-arm64@4.62.4':
'@rollup/rollup-openharmony-arm64@4.63.1':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.62.4':
'@rollup/rollup-win32-arm64-msvc@4.63.1':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.62.4':
'@rollup/rollup-win32-ia32-msvc@4.63.1':
optional: true
'@rollup/rollup-win32-x64-gnu@4.62.4':
'@rollup/rollup-win32-x64-gnu@4.63.1':
optional: true
'@rollup/rollup-win32-x64-msvc@4.62.4':
'@rollup/rollup-win32-x64-msvc@4.63.1':
optional: true
'@sentry/browser-utils@10.71.0':
@ -11814,10 +11814,10 @@ snapshots:
axe-core: 4.13.0
storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)
'@storybook/addon-docs@10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))':
'@storybook/addon-docs@10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.2)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))':
dependencies:
'@mdx-js/react': 3.1.1(@types/react@19.2.18)(react@19.2.8)
'@storybook/csf-plugin': 10.5.10(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
'@storybook/csf-plugin': 10.5.10(esbuild@0.28.2)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
'@storybook/icons': 2.1.0(react@19.2.8)
'@storybook/react-dom-shim': 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))
react: 19.2.8
@ -11833,9 +11833,9 @@ snapshots:
- vite
- webpack
'@storybook/builder-vite@10.5.10(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))':
'@storybook/builder-vite@10.5.10(esbuild@0.28.2)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))':
dependencies:
'@storybook/csf-plugin': 10.5.10(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
'@storybook/csf-plugin': 10.5.10(esbuild@0.28.2)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)
ts-dedent: 2.3.0
vite: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)
@ -11844,13 +11844,13 @@ snapshots:
- rollup
- webpack
'@storybook/csf-plugin@10.5.10(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))':
'@storybook/csf-plugin@10.5.10(esbuild@0.28.2)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))':
dependencies:
storybook: 10.5.10(@types/react@19.2.18)(react@19.2.8)
unplugin: 2.3.11
optionalDependencies:
esbuild: 0.28.2
rollup: 4.62.4
rollup: 4.63.1
vite: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12)
'@storybook/global@5.0.0': {}
@ -11868,11 +11868,11 @@ snapshots:
'@types/react': 19.2.18
'@types/react-dom': 19.2.4(@types/react@19.2.18)
'@storybook/react-vite@10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))':
'@storybook/react-vite@10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))':
dependencies:
'@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
'@rollup/pluginutils': 5.4.0(rollup@4.62.4)
'@storybook/builder-vite': 10.5.10(esbuild@0.28.2)(rollup@4.62.4)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
'@rollup/pluginutils': 5.4.0(rollup@4.63.1)
'@storybook/builder-vite': 10.5.10(esbuild@0.28.2)(rollup@4.63.1)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.12))
'@storybook/react': 10.5.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.10(@types/react@19.2.18)(react@19.2.8))(typescript@7.0.2)
empathic: 2.0.1
magic-string: 0.30.21
@ -15357,36 +15357,36 @@ snapshots:
'@rolldown/binding-win32-arm64-msvc': 1.2.5
'@rolldown/binding-win32-x64-msvc': 1.2.5
rollup@4.62.4:
rollup@4.63.1:
dependencies:
'@types/estree': 1.0.9
optionalDependencies:
'@napi-rs/lzma-linux-x64-gnu': 1.5.1
'@rollup/rollup-android-arm-eabi': 4.62.4
'@rollup/rollup-android-arm64': 4.62.4
'@rollup/rollup-darwin-arm64': 4.62.4
'@rollup/rollup-darwin-x64': 4.62.4
'@rollup/rollup-freebsd-arm64': 4.62.4
'@rollup/rollup-freebsd-x64': 4.62.4
'@rollup/rollup-linux-arm-gnueabihf': 4.62.4
'@rollup/rollup-linux-arm-musleabihf': 4.62.4
'@rollup/rollup-linux-arm64-gnu': 4.62.4
'@rollup/rollup-linux-arm64-musl': 4.62.4
'@rollup/rollup-linux-loong64-gnu': 4.62.4
'@rollup/rollup-linux-loong64-musl': 4.62.4
'@rollup/rollup-linux-ppc64-gnu': 4.62.4
'@rollup/rollup-linux-ppc64-musl': 4.62.4
'@rollup/rollup-linux-riscv64-gnu': 4.62.4
'@rollup/rollup-linux-riscv64-musl': 4.62.4
'@rollup/rollup-linux-s390x-gnu': 4.62.4
'@rollup/rollup-linux-x64-gnu': 4.62.4
'@rollup/rollup-linux-x64-musl': 4.62.4
'@rollup/rollup-openbsd-x64': 4.62.4
'@rollup/rollup-openharmony-arm64': 4.62.4
'@rollup/rollup-win32-arm64-msvc': 4.62.4
'@rollup/rollup-win32-ia32-msvc': 4.62.4
'@rollup/rollup-win32-x64-gnu': 4.62.4
'@rollup/rollup-win32-x64-msvc': 4.62.4
'@rollup/rollup-android-arm-eabi': 4.63.1
'@rollup/rollup-android-arm64': 4.63.1
'@rollup/rollup-darwin-arm64': 4.63.1
'@rollup/rollup-darwin-x64': 4.63.1
'@rollup/rollup-freebsd-arm64': 4.63.1
'@rollup/rollup-freebsd-x64': 4.63.1
'@rollup/rollup-linux-arm-gnueabihf': 4.63.1
'@rollup/rollup-linux-arm-musleabihf': 4.63.1
'@rollup/rollup-linux-arm64-gnu': 4.63.1
'@rollup/rollup-linux-arm64-musl': 4.63.1
'@rollup/rollup-linux-loong64-gnu': 4.63.1
'@rollup/rollup-linux-loong64-musl': 4.63.1
'@rollup/rollup-linux-ppc64-gnu': 4.63.1
'@rollup/rollup-linux-ppc64-musl': 4.63.1
'@rollup/rollup-linux-riscv64-gnu': 4.63.1
'@rollup/rollup-linux-riscv64-musl': 4.63.1
'@rollup/rollup-linux-s390x-gnu': 4.63.1
'@rollup/rollup-linux-x64-gnu': 4.63.1
'@rollup/rollup-linux-x64-musl': 4.63.1
'@rollup/rollup-openbsd-x64': 4.63.1
'@rollup/rollup-openharmony-arm64': 4.63.1
'@rollup/rollup-win32-arm64-msvc': 4.63.1
'@rollup/rollup-win32-ia32-msvc': 4.63.1
'@rollup/rollup-win32-x64-gnu': 4.63.1
'@rollup/rollup-win32-x64-msvc': 4.63.1
fsevents: 2.3.3
rou3@0.9.2: {}

View File

@ -14,6 +14,7 @@ import {
companies,
completionContracts,
createDb,
heartbeatRuns,
nativeRunFinalizations,
nativeRunResults,
statusDecisions,
@ -24,6 +25,7 @@ import {
getEmbeddedPostgresTestSupport,
startEmbeddedPostgresTestDatabase,
} from "./helpers/embedded-postgres.js";
import { drainHeartbeatRunsToQuiescence } from "./helpers/drain-heartbeat-runs.js";
import {
registerServerAdapter,
unregisterServerAdapter,
@ -90,10 +92,14 @@ describeEmbeddedPostgres("direct adapter native-runner isolation", () => {
}, 20_000);
afterEach(async () => {
// Terminal run status is committed before trailing lifecycle events and
// scratch cleanup settle. Drain those writes before taking the exclusive
// TRUNCATE locks, or the cleanup transaction can deadlock with them.
await heartbeat.drainActiveRunExecutions();
await drainHeartbeatRunsToQuiescence(db, heartbeat);
const runStatuses = await db
.select({ status: heartbeatRuns.status })
.from(heartbeatRuns);
const pendingRuns = runStatuses.filter(
(run) => run.status === "queued" || run.status === "running",
);
expect(pendingRuns).toEqual([]);
vi.clearAllMocks();
await db.execute(
sql.raw(`
@ -119,10 +125,10 @@ describeEmbeddedPostgres("direct adapter native-runner isolation", () => {
});
afterAll(async () => {
await drainHeartbeatRunsToQuiescence(db, heartbeat);
for (const [adapterType] of DIRECT_ADAPTERS) {
unregisterServerAdapter(adapterType);
}
await heartbeat.drainActiveRunExecutions();
await tempDb?.cleanup();
});