fix(runner): keep recovery state read bounds consistent

Use the control-plane bound during initial recovery, revalidation, and cleanup snapshots. A retained state above 16 MiB reproduces the prior rejection; 32 focused recovery and authority checks now pass.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-09-24 16:06:08 -07:00
1 parent c9618fcf16
commit 6262173bf5
2 files changed
+21 -9

No files matched your search

@@ -9100,6 +9100,7 @@ describe("runnerd provider runtime wiring", () => {
it.each([
"quarantined",
"large control plane",
"empty retry shell",
"unsuspended current",
"live runner",
@@ -9298,6 +9299,12 @@ describe("runnerd provider runtime wiring", () => {
},
],
committedEvents: [
...(scenario === "large control plane"
? Array.from({ length: 2048 }, () => ({
eventType: "item.delta",
envelope: { ...identity, payload: { delta: "x".repeat(8192) } },
}))
: []),
{ eventType: "run.terminal", envelope: identity },
],
}),
@@ -9337,6 +9344,7 @@ describe("runnerd provider runtime wiring", () => {
state.createTransport.mockClear();
const shouldRecover = [
"quarantined",
"large control plane",
"empty retry shell",
"unsuspended current",
"active goal",
@@ -1835,7 +1835,9 @@ function cleanupStateSnapshot(root: string, providerFile = "codex-provider-state
const bytes = files.map((file) =>
readBoundedNativeFile(
resolve(root, file),
NATIVE_RUNNER_STATE_MAX_BYTES,
file === "control-plane/control-plane-state.json"
? NATIVE_CONTROL_PLANE_STATE_MAX_BYTES
: NATIVE_RUNNER_STATE_MAX_BYTES,
"native_cleanup_maintenance_unproven",
),
);
@@ -4766,7 +4768,9 @@ async function recoverQuiescentRunnerdState(input: {
throw new Error("unsafe_recovery_state");
return readBoundedNativeFile(
resolve(root, directory, name),
NATIVE_RUNNER_STATE_MAX_BYTES,
directory === "control-plane" && name === "control-plane-state.json"
? NATIVE_CONTROL_PLANE_STATE_MAX_BYTES
: NATIVE_RUNNER_STATE_MAX_BYTES,
"recovery_state_too_large",
).toString("utf8");
};
@@ -4935,15 +4939,15 @@ async function recoverQuiescentRunnerdState(input: {
const candidate = verified[0]!;
// Candidate enumeration can await other database reads. Revalidate the
// selected evidence and dead owner immediately before the atomic moves.
for (const [relativePath, expected] of [
["runner/runner-state.json", candidate.runnerBytes],
["runner/codex-provider-state.json", candidate.providerBytes],
["control-plane/control-plane-state.json", candidate.controlBytes],
]) {
for (const [relativePath, expected, maxBytes] of [
["runner/runner-state.json", candidate.runnerBytes, NATIVE_RUNNER_STATE_MAX_BYTES],
["runner/codex-provider-state.json", candidate.providerBytes, NATIVE_RUNNER_STATE_MAX_BYTES],
["control-plane/control-plane-state.json", candidate.controlBytes, NATIVE_CONTROL_PLANE_STATE_MAX_BYTES],
] as const) {
if (
readBoundedNativeFile(
resolve(candidate.root, relativePath!),
NATIVE_RUNNER_STATE_MAX_BYTES,
resolve(candidate.root, relativePath),
maxBytes,
"recovery_state_too_large",
).toString("utf8") !== expected
) {