Verify warm continuity with run-scoped credential rotation

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-09-17 11:25:18 -05:00
1 parent 3fa1a22914
commit 3da3d0c307
3 files changed
+23 -11

No files matched your search

+3 -1
View File
@@ -126,7 +126,9 @@ three browser-driven turns on one issue. Every turn reads and extends the same
nonce file, verifies host copy-back, records scheduler/run/end-to-end timing,
and asserts `created`, `resumed`, `resumed` lease acquisition on one sandbox.
Runner Codex additionally proves stable native session, provider session,
runner instance, PID, and process-start identity. Each turn is bounded to ten
and runner instance. With managed GitHub authentication, run-scoped credentials
require distinct process identities on each turn; without it, PID and process-start
identity remain stable. Each turn is bounded to ten
minutes, the cell to thirty minutes, and cleanup explicitly deletes the
sandbox rather than waiting for Daytona's idle timeout.
+1
View File
@@ -823,6 +823,7 @@ function warmTurnInstructions(turn: 1 | 2 | 3, nonce: string) {
: `In a legacy runner, after verification POST exactly one request_confirmation to /api/issues/$PAPERCLIP_TASK_ID/interactions with {"kind":"request_confirmation","idempotencyKey":"daytona-warm-review-T${turn}-${nonce}","resolverPolicy":"human_only","title":"Warm continuity turn ${turn}","summary":"Review completed warm continuity turn ${turn}.","continuationPolicy":"wake_assignee","payload":{"version":1,"prompt":"Is this warm continuity task ready to complete after turn ${turn}?","acceptLabel":"Approve completion","rejectLabel":"Continue work","rejectRequiresReason":true,"allowDeclineReason":true,"supersedeOnUserComment":false,"target":{"type":"custom","key":"daytona_warm_turn_${turn}","revisionId":"${nonce}-T${turn}","label":"Warm continuity turn ${turn}"}}}. Capture the returned interaction id. Then make exactly one issue PATCH with {"status":"in_review","comment":"${marker}","reviewInteractionId":"<returned interaction id>"}. Include Authorization and X-Paperclip-Run-Id on both writes. If the issue PATCH fails, retry only that PATCH and never create another interaction. Do not POST a separate comment. After both writes succeed, end the response and heartbeat immediately; do not wait or poll because the reviewer action will start the next turn.`;
return [
`This is warm Daytona continuity turn ${turn} of 3. Work only in the current execution workspace.`,
"The workspace file is an internal continuity-test sentinel, not a requested file deliverable. Do not register or attach it. The only requested user-facing deliverable is the exact response marker below.",
turn === 1
? `Create ${file} with exactly this one line followed by a newline: ${lines[0]}`
: `Before changing anything, read ${file} and verify its content is exactly ${lines.slice(0, -1).join("\\n")} followed by a newline. Then append exactly ${lines.at(-1)} followed by a newline.`,
+19 -10
View File
@@ -2022,16 +2022,6 @@ for (const execution of executions) {
label: "provider session",
values: selectedRuns.map((candidate) => candidate.sessionIdAfter),
},
{
label: "runner pid",
values: selectedRuns.map((candidate) => candidate.processPid),
},
{
label: "runner process fingerprint",
values: selectedRuns.map(
(candidate) => candidate.processStartedAt,
),
},
];
for (const { label, values } of stableIdentityFields) {
if (
@@ -2048,6 +2038,25 @@ for (const execution of executions) {
);
}
}
// Managed GitHub capabilities are run-scoped. The supervisor must
// rotate processes while preserving the durable provider session.
const managedCredentials = selectedRuns.every(
(candidate) => record(candidate.contextSnapshot).githubAuthenticationMode === "managed",
);
const processIdentities: Array<{ label: string; values: unknown[] }> = [
{ label: "runner pid", values: selectedRuns.map((candidate) => candidate.processPid) },
{ label: "runner process fingerprint", values: selectedRuns.map((candidate) => candidate.processStartedAt) },
];
for (const { label, values } of processIdentities) {
const expectedDistinct = managedCredentials ? selectedRuns.length : 1;
if (values.some((value) => value === null || value === undefined || String(value).length === 0)
|| new Set(values).size !== expectedDistinct) {
invariantFailures.push(
`expected ${expectedDistinct} distinct ${label} values for ${managedCredentials ? "rotating managed credentials" : "warm process reuse"}; observed ${JSON.stringify(values)}`,
);
}
}
}
if (
turnTimings?.length !== 3 ||