mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
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:
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
|
||||
) {
|
||||
|
||||
Reference in new issue
Block a user