From eaef0d56552c087aaa4fa68a8655282ffbd12667 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 30 Oct 2026 14:34:42 -0500 Subject: [PATCH] Admit supported PRP v2 idle goal recovery evidence Co-Authored-By: Paperclip --- ...ative-controller-source-probe-receipt.json | 70 +++++++++++++++++++ .../remote-warm-session-recovery.test.ts | 8 ++- .../remote-warm-session-recovery.ts | 3 +- 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 doc/plans/2026-10-30-native-controller-source-probe-receipt.json diff --git a/doc/plans/2026-10-30-native-controller-source-probe-receipt.json b/doc/plans/2026-10-30-native-controller-source-probe-receipt.json new file mode 100644 index 00000000000..73d05dc06a6 --- /dev/null +++ b/doc/plans/2026-10-30-native-controller-source-probe-receipt.json @@ -0,0 +1,70 @@ +{ + "schema": "paperclip.native-controller.release-qualification-receipt/v1", + "finishedAt": "2026-10-30T19:33:20.587023+00:00", + "sourceIntegrity": { + "A": { + "revision": "59ff89a18fe1312b5f0bb908439bbdb1237b0252", + "nativeRuntimeSourceBytesUnchanged": true, + "mismatches": [] + }, + "B": { + "revision": "98f05fcd5b452704131a35ff09486f5c04c6959a", + "nativeRuntimeSourceBytesUnchanged": true, + "mismatches": [] + } + }, + "sourceAndArtifact": { + "A": "59ff89a18fe1312b5f0bb908439bbdb1237b0252", + "B": "98f05fcd5b452704131a35ff09486f5c04c6959a", + "admittedRunnerSha256": "b1f04caa1ce04e720dec7eb62ede170eb3621948c0849b8f305ea5c5116bced8", + "runnerArtifact": { + "role": "admitted Runner artifact A, unchanged through every controller", + "sourceRevision": "59ff89a18fe1312b5f0bb908439bbdb1237b0252", + "binary": "/private/tmp/native-controller-qualification-20261030-1833/installed-A/consumer/node_modules/@paperclipai/server/dist/vendor/paperclip-runner/bin/linux-arm64/paperclip-runnerd", + "sha256": "b1f04caa1ce04e720dec7eb62ede170eb3621948c0849b8f305ea5c5116bced8", + "bytes": 35345144, + "installationReceipt": "/private/tmp/native-controller-qualification-20261030-1833/installed-A/receipt.json" + } + }, + "passed": [ + "Distinct source-controller active SIGKILL followed by natural 60-second lease takeover; exact same run/provider attempt/Runner retained", + "Current real-PostgreSQL stream oracle at 8,512,8192 chunks: zero incremental checkpoint SQL/transactions/changed rows; idle SQL zero; positive control passed", + "B1 recovered run completed and committed its result with ready native snapshot and valid v1 recovery envelope" + ], + "failed": [ + "Fresh B2 retained continuation rejected after B1 completed because durable control journal has a null-goal snapshot after run.terminal", + "Final 98f05fc live probe still rejects actual paperclip.prp.event.v2 because proposed guard accepts only v1" + ], + "notQualified": [ + "Complete A/B/A native installed-application startup/rollback gate", + "Uninterrupted warm provider process through changed completion-contract boundary", + "First compatible published empty-disk rollback reader" + ], + "limits": [ + "Source controller processes and scripted Codex-compatible provider in Docker; no live-provider or live-Daytona claim", + "Qualified immutable A release Runner used; source controller native modules hash-verified, not full installed server execution", + "Only native controller projections deleted; non-native configuration, encryption key, workspace/sandbox dependencies preserved", + "Local stream WAL/elapsed diagnostics excluded from quantitative performance due concurrent isolated fixture" + ], + "evidence": { + "finalProbe": "/tmp/native-controller-release-qualification-qualified-A-20261030/evidence.json", + "finalProbeLog": "/tmp/native-controller-release-qualification-qualified-A-20261030/run-final-fix.log", + "controlLog": "/tmp/native-controller-release-qualification-qualified-A-20261030/controller.log", + "streamOracle": "/tmp/native-controller-release-qualification-20261030/stream-cost-current.json", + "streamLog": "/tmp/native-controller-release-qualification-20261030/stream-cost-current.log", + "earlierThreeTurnBusinessContinuation": "/tmp/native-controller-release-qualification-20261030/evidence-run-06.json" + }, + "finalProbeFailure": { + "message": "Error: runner_state_identity_mismatch: prior_owner_terminal_state_indeterminate\n at deny (/private/tmp/native-controller-release-qualification-qualified-A-20261030/B-98f05f/server/src/services/native-runtime/remote-warm-session-recovery.ts:56:37)\n at inspectRemoteWarmSessionRecovery (/private/tmp/native-controller-release-qualification-qualified-A-20261030/B-98f05f/server/src/services/native-runtime/remote-warm-session-recovery.ts:85:67)\n at process.processTicksAndRejections (node:internal/process/task_queues:104:5)\n at async executePaperclipNativeSessionWithinScope (/private/tmp/native-controller-release-qualification-qualified-A-20261030/B-98f05f/server/src/services/native-runtime/native-session-executor.ts:8237:28)\n at async executePaperclipNativeSession (/private/tmp/native-controller-release-qualification-qualified-A-20261030/B-98f05f/server/src/services/native-runtime/native-session-executor.ts:8057:12)\n at async process. (/private/tmp/native-controller-release-qualification-qualified-A-20261030/controller-worker.mjs:36:16)", + "cause": null + }, + "candidateCorrection": { + "change": "Accept supported PRP event v1 and v2 idle goal suffixes; reject unknown versions", + "fullSourceReplay": "not rerun after the final v2 correction within the one-hour qualification window", + "focusedTests": { + "result": "pass", + "cases": 14, + "test": "server/src/services/native-runtime/remote-warm-session-recovery.test.ts" + } + } +} diff --git a/server/src/services/native-runtime/remote-warm-session-recovery.test.ts b/server/src/services/native-runtime/remote-warm-session-recovery.test.ts index d1f8098f7aa..91317d2af4b 100644 --- a/server/src/services/native-runtime/remote-warm-session-recovery.test.ts +++ b/server/src/services/native-runtime/remote-warm-session-recovery.test.ts @@ -10,10 +10,11 @@ const snapshot = { eventType: "session.goal.snapshot", envelope: { ...identity, const state = (events: unknown[]) => ({ identity, commands: [{ status: "completed" }], committedEvents: events }); describe("settled remote warm control evidence", () => { - it("accepts the terminal and an authenticated idle goal-read suffix without changing its order", () => { - const control = state([terminal, snapshot]); + it.each(["paperclip.prp.event.v1", "paperclip.prp.event.v2"])("accepts the terminal and an authenticated %s idle goal-read suffix without changing its order", schema => { + const suffix = { ...snapshot, envelope: { ...snapshot.envelope, payload: { ...notification, schema } } }; + const control = state([terminal, suffix]); expect(isSettledRemoteWarmControlState(control, identity)).toBe(true); - expect(control.committedEvents).toEqual([terminal, snapshot]); + expect(control.committedEvents).toEqual([terminal, suffix]); expect(isSettledRemoteWarmControlState(state([terminal]), identity)).toBe(true); }); @@ -21,6 +22,7 @@ describe("settled remote warm control evidence", () => { ["active goal", { ...snapshot, envelope: { ...snapshot.envelope, payload: { ...notification, payload: { ...goal, goal: { status: "active" } } } } }], ["provider work", { ...snapshot, envelope: { ...snapshot.envelope, payload: { ...notification, payload: { ...goal, workingNow: true } } } }], ["unknown event", { ...snapshot, eventType: "unknown" }], + ["unknown protocol version", { ...snapshot, envelope: { ...snapshot.envelope, payload: { ...notification, schema: "paperclip.prp.event.v3" } } }], ["new turn", { ...snapshot, eventType: "turn.started" }], ["foreign run", { ...snapshot, envelope: { ...snapshot.envelope, runId: "other" } }], ["foreign session", { ...snapshot, envelope: { ...snapshot.envelope, normalizedSessionId: "other" } }], diff --git a/server/src/services/native-runtime/remote-warm-session-recovery.ts b/server/src/services/native-runtime/remote-warm-session-recovery.ts index b751e5224ff..893dfefaef6 100644 --- a/server/src/services/native-runtime/remote-warm-session-recovery.ts +++ b/server/src/services/native-runtime/remote-warm-session-recovery.ts @@ -36,7 +36,8 @@ export function isSettledRemoteWarmControlState(control: Record const payload = record(notification.payload); return record(event).eventType === "session.goal.snapshot" && matchesSession(event) && Object.entries(identity).every(([key, value]) => envelope[key] === value) && - notification.schema === "paperclip.prp.event.v1" && notification.eventType === "session.goal.snapshot" && + (notification.schema === "paperclip.prp.event.v1" || notification.schema === "paperclip.prp.event.v2") && + notification.eventType === "session.goal.snapshot" && notification.sourceInstanceId === identity.runnerInstanceId && notification.sourceKind === "runner" && notification.runId === identity.runId && notification.normalizedSessionId === identity.normalizedSessionId && payload.schema === "paperclip.session_goal.snapshot.v1" && payload.goal === null && payload.workingNow === false;