Phase 3a: runtime-neutral session hibernate/wake#1562
Conversation
Extract git-bundle and HOME-tar archive helpers from session_snapshot.go into session_snapshot_archive.go to keep both files under the 800-line limit. Make the AgentCrashReportView copy test await the "Copied" state so it no longer races the async clipboard write in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rompts VmAgentContainer.proxyHttp/fetch read the container state once before wakeFromSnapshot(), then applied the stopped -> 410 guard using that stale pre-wake state. A freshly-woken, restored container was rejected with 410 (surfaced by the Worker as a generic 500), so users could restore state but not continue chatting after wake. Re-read the state after a successful wake. Adds a discriminating regression test that fails on the pre-fix code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wakeFromSnapshot launched the restored container with a workspace-scoped callback token (signCallbackToken) passed as the container CALLBACK_TOKEN, but the vm-agent uses it for node callbacks (error/activity/message reporting) which reject workspace-scoped tokens with 403 "Insufficient token scope". Restored sessions therefore accepted a prompt (200) but silently failed to report the agent's reply, so no answer appeared after wake. Sign a node-scoped token (signNodeCallbackToken) to match the initial launch in launchInstantSession. Diagnosed from staging Worker logs (node_auth.rejected_workspace_scoped_token). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ersist The message reporter and snapshot callbacks use the workspace-scoped runtime.CallbackToken (workspaceCallbackToken), which has NO node-scoped fallback. A freshly-woken container never runs create-workspace, so that token was unset — the reporter hit "no auth token" and chat replies were silently discarded (restored sessions accepted a prompt but never answered). The DO now signs a workspace-scoped callback token and passes it on the restore request; the vm-agent persists it via upsertWorkspaceRuntime before resolving the snapshot callback token. Combined with the node-scoped CALLBACK_TOKEN, this matches the normal launch (node token for node callbacks + workspace token for message/snapshot callbacks). Diagnosed from staging Worker logs: workspace_auth.rejected_node_scoped_token on /session-snapshot/restore and reporter "no auth token". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The normal agent-session-create path (handleCreateAgentSession) sets runtime.ProjectID and creates/binds the per-workspace message reporter via getOrCreateReporter before the agent runs. The restore path only recreated the session host + SelectAgent, so the restored agent's streamed output had no reporter to enqueue to — replies were generated (ACP prompt completed) but never POSTed to /messages, so no answer appeared after wake. Restore now primes the reporter with the workspace's projectID (from the runtime or PROJECT_ID env) and the restored chatSessionID. Diagnosed from staging Worker logs: message POSTs present during setup, zero after wake despite ACP prompt completing with no auth errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fd2fc5c to
023eb65
Compare
✅ Wake→reply now works end-to-end on staging (root-caused via Worker logs)Using The four fixes
Live end-to-end verification (staging,
|
…gging Addresses the CRITICAL/HIGH findings from the PR #1562 specialist review: - Concurrency: serialize wake-from-snapshot behind a promise-chain mutex (ensureAwake) so two concurrent requests to a sleeping container don't both launch+restore; re-reads lifecycleStatus inside the critical section (.claude/rules/45). Adds a discriminating concurrency regression test. - Tests: add Go unit tests locking in the two silent-failure invariants — the restore-body workspace token is persisted (callbackTokenForWorkspace / workspaceCallbackToken) and the message reporter is primed on restore. - Defensive logging: primeRestoredMessageReporter now warns (instead of silently no-oping) when project/chat-session context is missing. Remaining LOW/pre-existing hardening tracked in tasks/backlog/2026-07-12-cf-container-wake-restore-hardening.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Specialist Review EvidenceRan 5 local specialist reviewers on the full diff (
CRITICAL/HIGH findings — all addressed in commit
|
|



Summary
cf-containersessions: private R2$HOME/Git WIP snapshots, additive D1 metadata, visible degradation, wake-before-proxy, and fresh runtime asset reinjection.Validation
pnpm lint(warnings only; no errors)pnpm typecheckgo test -race ./internal/serverand focused snapshot race tests passedpnpm quality:migration-safety,pnpm quality:do-migration-safety,pnpm quality:file-sizes, andpnpm quality:wrangler-bindingsStaging Verification (REQUIRED for all code changes — merge-blocking)
$HOME/WIP snapshot → sleep → fresh wake → restore → native resume/fallbackStaging Verification Evidence
Pending coordinated staging turn 1. This PR remains draft until the complete live flow and cleanup pass.
UI Compliance Checklist (Required for UI changes)
N/A: no UI files changed.
End-to-End Verification (Required for multi-component changes)
Data Flow Trace
apps/api/src/routes/projects/agent-activity-callback.ts, causingVmAgentContainerto hibernate before sleep.packages/vm-agent/internal/server/session_snapshot.gocreates a non-mutating Git bundle and$HOMEtar, then uploads through workspace-scoped callback routes.apps/api/src/routes/workspaces/session-snapshots.tsderives R2 keys, verifies manifest identity and authoritative object sizes, andapps/api/src/services/session-snapshots.tspersists additive D1 metadata.apps/api/src/durable-objects/vm-agent-container.tslaunches a fresh container on wake and calls restore before proxying.$HOME/WIP, callsprovisionWorkspaceRuntimeto overwrite stale runtime assets/credentials, rebuilds the session host, and reports visible restore status.Untested Gaps
The real Cloudflare Container sleep/wake boundary is intentionally not claimed from unit tests; it will be exercised on staging before the draft is marked ready.
Post-Mortem (Required for bug fix PRs)
What broke
The initial draft could mutate a user's Git index/branch while snapshotting, leave restore on a temporary branch, accept unbounded chunked uploads, trust client-reported sizes, and follow pre-existing home symlinks during extraction.
Root cause
The first implementation used the live Git index/branch as snapshot scratch state and treated callback-authenticated metadata as authoritative without rechecking storage facts or extraction filesystem state.
Class of bug
Cross-boundary data-integrity and trust-boundary validation gaps.
Why it wasn't caught
The initial tests covered happy-path artifacts and lexical tar traversal, but not exact Git status preservation, existing filesystem symlinks, chunked uploads, or client/storage size disagreement.
Process fix included in this PR
Regression tests now assert exact branch/index preservation, original-branch restore, symlink rejection, required upload lengths, server-derived R2 sizes, and manifest identity. Pulumi tests assert managed TTL cleanup.
Post-mortem file
tasks/archive/2026-07-11-runtime-neutral-session-hibernate-wake.mdSpecialist Review Evidence
needs-human-reviewlabel added and merge deferred to human — N/A after the complete in-session checklist rerun; the PR remains draft for staging, not review gaps..env.example, and shared env reference are synchronizedExceptions
workerd1.20260329.1 repeatedly SIGSEGVs before test execution in this workspace; CI Worker/VM-agent integration and E2E checks pass, and live staging remains blocking.Agent Preflight (Required)
Classification
External References
Official documentation from Cloudflare was consulted for R2 object lifecycle behavior and the bucket lifecycle API: https://developers.cloudflare.com/r2/buckets/object-lifecycles/ and https://developers.cloudflare.com/api/resources/r2/subresources/buckets/subresources/lifecycle/.
Codebase Impact Analysis
Affected paths:
apps/apicallback routes, D1 schema/migration, snapshot service, container DO and deployment vars;packages/vm-agenthibernate/restore and tests;infraR2 lifecycle resources;scripts/deployPulumi-output to Worker-var synchronization; task and environment reference docs.Documentation & Specs
Updated
apps/api/.env.example,.claude/skills/env-reference/SKILL.md, andtasks/archive/2026-07-11-runtime-neutral-session-hibernate-wake.mdwith current configuration, superseding authorization, findings, and evidence.Constitution & Risk Check
Checked additive migration/data-loss rules, Principle XI configurable values, callback authentication scoping, server authority over storage keys and sizes, private snapshot handling, TTL cleanup, visible degradation, and post-restore fresh credential injection. The remaining real-runtime risk is blocked on coordinated staging E2E.