Files
PaperClipAI/maps/feature-map/recovery.md
T
DottaandPaperclip 5c3dce09b5 fix(runner): preserve verified remote artifacts across controller releases (#17512)
## Thinking Path

> - Paperclip manages AI agents and owns their task authority.
> - Remote native agents can keep working while their controller
restarts.
> - The controller must authenticate the same runner and verify its
provider files before recovery.
> - The current path compares a retained provider pack with the new
controller release. A release change can reject intact files.
> - The new controller binary digest can also reject the original daemon
during PRP authentication.
> - This PR records the admitted release authority in private controller
state and uses it for exact process reattachment.

## Linked Issues or Issue Description

**What happened?**

A remote Claude session started and used tools. After controller
shutdown, recovery failed with
`runner_remote_provider_artifact_incompatible`. The transport combined
stderr into stdout, so the error omitted the verifier reason. The
historical rejected manifest is unavailable. This PR reproduces
controller release drift as a cause; it does not claim that the
historical mismatch is proven. Reaching pack verification implies the
earlier exact-process/controller-identity checks passed. That supports a
preserved-state recovery path but is not a direct inspection of the
deployed volume.

**Expected behavior**

A compatible, unchanged remote runner can reattach after a controller
release changes. Full package hashes, exact process scope, protocol
capabilities, and authenticated runner identity must still pass. Fresh
launches use the current release.

**Steps to reproduce**

1. Verify and admit a remote native session with release A.
2. Preserve its runner process and replace the controller with release
B.
3. Reattach the original process. The old path compares its pack with B
and fails at `manifest mismatch`.
4. With this fix, the controller reads its saved A authority and
verifies all A hashes. Changed files still fail.

**Paperclip version or commit**

Reproduced from master `470cb34c0e42cb9d0b232d0bcf5d45cc7b0a81ca`;
rebased onto `a3675ddcdf`.

**Deployment mode**

Native remote runner on a Daytona-backed Paperclip Computer. Local
acceptance uses isolated Linux containers and actual packaged artifacts.

Related work: #17361 and #17448 handle runner upgrade and qualification
permissions. #17097 and #17439 add durable controller checkpoints and
server adoption. That stack can skip remote artifact preparation with a
saved checkpoint. This PR fixes the current file-backed path and retains
full provider-pack verification; it has no checkpoint schema or
empty-disk controller recovery.

## What Changed

- Save the complete verified pack manifest and authenticated runner
version/digest after successful session admission.
- Bind the receipt to company, agent, workspace, session, runner,
environment lease, remote directory, and provider descriptor. Keep it
outside guest backup directories.
- Reuse the saved authority for exact reattachment. Keep current release
selection for fresh launches and explicit session replacement.
- Fail closed for mismatched or corrupt receipts. Historical sessions
without a receipt retain the current release integrity gate.
- Report bounded, redacted verifier output from both transport streams.
Warn on receipt write failure while returning the already admitted
session to its owner.
- Add scope, persistence, replacement, storage failure, and backend
wiring regressions. Update recovery documentation and the feature map.

## Verification

- Test-first red: the original selection chose release B instead of A
after controller recreation. The new receipt regression passes. A
separate red test showed the missing stdout diagnostic; it passes after
the diagnostic fix.
- Authority and diagnostic suites: 49 tests pass. Backend pack, process
adoption, and harness persistence selection: 47 tests pass.
- Actual Linux provider image: complete A manifest/artifact verification
passed; controller source-revision drift failed on the old path; saved A
authority passed; a modified ACPX sidecar failed its SHA-256 check.
- Actual native Linux runner: authenticated a session, detached the
controller, changed the controller manifest and binary, then
authenticated the original PID and provider session with saved
authority. There were zero duplicate spawns, zero new provider turns, no
stop/suspend, and the workspace file remained. This probe used the real
runner with a deterministic Codex fixture; it is separate from the real
ACPX package integrity proof.
- Full workspace typecheck and build passed. The full native executor
suite first hit host PostgreSQL semaphore exhaustion. The complete
changed files then passed 864/864 tests with separate real PostgreSQL
databases in a disposable Linux container. The temporary local fixture
changed only database provisioning and used normal repository
migrations. Current-head CI also passed the full required test, Runner,
browser, typecheck, and build lanes.
- `node scripts/check-maps.mjs` and `git diff --check` passed. These
structural checks do not prove live recovery.
- Current head `48fca2ae33d99f7f6d2fe12f431cf76d25185c70`: 55 successful
checks, two conditional skips, no pending or failed checks; fresh
Greptile 5/5 with zero findings and zero unresolved review threads.
Commitperclip quality review passed.
- No original task was retried or changed. Existing Daytona access could
not resolve the original sandbox or its image; a fresh staging Claude
recovery is not claimed.

## Risks

This prevents recurrence for sessions admitted with the new receipt. It
does not retroactively recover the historical failed run. Saved
authority exists only after this change has admitted a session. It
cannot repair an ambiguous historical launch or restore a receipt on an
empty replacement controller. A stale protocol or modified pack remains
a hard failure. Receipt write errors warn without losing the live
session handle, but later recovery still needs matching authority. The
broader checkpoint stack changes the same server preparation and
runner-artifact option and will require a deliberate integration.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools, and code
execution. The runtime does not expose a more specific model ID or
context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-24 16:44:50 -07:00

28 KiB
Raw Blame History

navigation
navigation
section sectionOrder order
Projects and execution 40 50

Recovery

People can see why a task stopped, who owns the next action, and whether a safe continuation is available. Recovery retains task context and ownership, honors pause and budget gates, and distinguishes confirmed failure from an uncertain external outcome. A retry acknowledgement alone is not proof that work resumed.

Feature ID: recovery.

Agent access: companion.

Sub-features

  • diagnosis: recovery/diagnosis — a task/run exposes the recorded cause, evidence, owner, attempt progress, and next action without requiring a raw-log interpretation.
  • bounded-continuation: recovery/bounded-continuation — supported interrupted work continues with preserved conversation context and bounded attempts, without blindly replaying tools.
  • source-action: recovery/source-action — source-scoped recovery actions remain attached to the task; independent repair work may have its own issue and return owner.
  • exhaustion: recovery/exhaustion — an exhausted infrastructure attempt offers ordinary Retry. Internal repair stays system-owned; genuine user decisions retain their specific action.
  • workspace: recovery/workspace — workspace divergence/export failures show the applicable repair, reconcile, or isolated reissue action with evidence and authority checks. Retained-file recovery runs in the harness with observed task progress and a ordinary Retry action after exhaustion or during backoff. It preserves newer files, keeps uncertain sources held, and delivers saved human input through ordinary admission. The task does not expose infrastructure-certification forms or a workspace link when the destination is disabled. Local PostgreSQL and real filesystem regressions cover native and legacy recovery and one-time delivery; hosted provider recovery remains separate acceptance.
  • unknown-outcome: recovery/unknown-outcome — uncertain external side effects require inspection and an explicit decision, not automatic replay.
  • user-continuation: recovery/user-continuation — saved user messages after a stop are reconsidered once cleanup proves the old execution is stopped; duplicate wakes are avoided.
  • reconnect: recovery/reconnect — browser stream recovery refreshes persisted state without claiming the agent itself restarted or that a failed run succeeded.

How to get to it (user POV)

source-task

Open the affected task from Tasks, Inbox/Blocked, search, or a direct task link. Observe one working, backoff, failed or waiting-for-user state. Use Retry after an infrastructure failure. No process or file certification is required.

A workspace project denial names the blocked access and links to the agent's Permissions / Trust settings. After an authorized person adds the required project, return to the saved task for an explicit Retry task action. Saving permissions alone does not wake the agent. Historical setup denials receive the same notice on reload; unrelated errors and prior assignees do not.

Thinking appears immediately after Retry, before the request completes. Technical source/run details remain in deliberate diagnostics. The task recovery contract describes receipt identity, fresh workspaces, bounded retries, historical adoption and service incidents.

run-detail

Open the agent's run (/agents/:agentId/runs/:runId) from activity or the task's run link. The run page hosts workspace recovery controls only for a failed run with error code workspace_validation_failed and a linked source task that still has a live workspace_validation recovery action. Use the source task for other stopped-run recovery checks.

operator-retry

Use the task's Retry action. It can create an isolated attempt after old execution is contained even when old-file preservation remains unresolved. Approval, credentials, budget and deliberate pause use their existing specific controls.

For a connected conversation, click Retry while an exact retained source still needs containment. Observe a saved wait with no new executor. After verified containment, the same chat receipt must admit one successor with the original source batch. Reload and repeat reconciliation. Check that a paused endpoint, changed source, superseded request or revoked access cannot start that work. The connected-chat database regression in chat channel integration exercises admission and queue continuation. It does not establish hosted provider recovery or successful delivery.

post-stop-message

After a stop, send a new instruction on the same task or inspect one already saved while cleanup was pending. Observe the waiting message, continuation, and eventual result rather than manually changing the task status to look complete.

A continuation denied before startup by an existing execution hold remains a wait. Once the original hold clears, the server verifies the dispatch receipt and absence of execution evidence, ignores any historical false incident for that unused admission, and reconsiders saved input through normal admission. No infrastructure-certification form is needed. The database regressions cover incident prevention, retained audit history, concurrent restart admission, ordered messages, contradictory execution evidence, and unchanged pause and task gates. These tests do not qualify a live provider or prove task completion.

browser-reconnect

While viewing an active or recovering task, interrupt the browser connection and restore it, or suspend and return to the tab. Reload is a separate check. The current task and run state should reconcile with the server.

For a consumed review path, verify that the one automatic repair run receives the instruction to save a one-shot monitor before it promises another check. Repeat with a fresh session, a resumed session, and a connection-pool retry that retains the typed repair marker but changes the wake reason. Confirm a persisted schedule before claiming that follow-up will occur; the repair instruction does not schedule one by itself. The wake regression exercises the real wake builder, serializer and prompt selector with an empty credential-history database boundary. It does not prove a hosted agent scheduled or completed the follow-up. Exhaustion still blocks for a board decision.

Driving it

Preconditions: follow the baseline. Use a disposable instance and reproducible failure. Record source task/run, adapter/runtime mode, cause, attempt count, current owner, and verified stop evidence. Do not kill an unrelated server or induce provider side effects just to get a failure screen. Use the existing deterministic recovery suite first.

Operation checkpoints

Use these checks alongside the entry-point recipes below and the operation records in the access companion. Record the actual actor, surface, saved result and denial separately. Source review and linked tests do not establish a live pass.

Capability ID Action and persisted result Denial, race and retry checks
recovery/diagnosis Open the source task and exact run; compare cause, source action, original owner, recovery owner, attempt bounds and next action after reload. Test an unrelated/private task and a stale run notice. Read-side retirement of a stale action must not create a new attempt, participant, or permission to replay.
recovery/bounded-continuation Use a deterministic recoverable interruption and inspect the original run/session, attempt receipts, saved result and eventual useful output. Repeat restart/receipt delivery, race Stop or a new owner, and retain unknown external outcomes. Accepted completion/finalization recovery must not start a new provider turn or repeat its side effect.
recovery/source-action Inspect active and retained actions, follow the repair owner, and resolve one eligible source-bound action with its exact ID. Retry the settled resolution and reject a stale/foreign action. Recovery ownership alone cannot mutate the source task; inspect source status, audit and wake independently.
recovery/exhaustion Exhaust or age a bounded fixture; use the currently offered retry once, then inspect its durable acknowledgement and actual continuation. Reassign, pause, stop, close, add an unresolved dependency, or impose a budget hold before retry. The old notice must not reopen terminal work or run the wrong owner.
recovery/workspace Exercise retained-file recovery, export retry, reconstruction, branch repair/reconciliation and exact-owner finalization as distinct authorized operator paths. Use exact source/action/run/receipt bindings and preserve existing files. Reject stale source, live/uncertain writers, unsafe archives and insufficient authority. Verify actual files and accepted result separately from queued acknowledgement.
recovery/unknown-outcome Inspect the provider and saved intent before recording a source-specific chat, email or governed-provider outcome. Use performed, not-performed and unresolved cases. Wrong company/request, stale state and unauthorized resolution must fail. Unknown effects must not replay automatically. An explicit retry must record the operator’s duplicate-risk acceptance and dispatch only the authorized attempt.
recovery/user-continuation Save a distinguishable instruction while Stop cleanup is pending; after verified stop, inspect exactly one eligible continuation and its useful response. Keep the message waiting under task/ancestor pause, budget or retained-file holds. Do not impersonate a human message or infer delivery from a queued wake.
recovery/reconnect Disconnect the browser, let the server change persisted task/run state, reconnect, then reload and compare. Inspect duplicate comments/decisions and private/company boundaries after access changes. Browser transport recovery and agent execution recovery require separate results.

source-task

Automated: recovery actions covers source scoping, bounded attempts, operator stops, quota monitoring, ownership, stale action retirement, and continuation deduplication. Recovery cards cover diagnosis, retry progress, exhaustion, and action availability. The fixture-backed execution-recovery journeys start their own test-drive instances and exercise safe, uncertain, restart, manager-lineage, and legacy-unknown paths:

pnpm exec vitest run server/src/__tests__/issue-recovery-actions.test.ts ui/src/components/IssueRecoveryActionCard.test.tsx
pnpm exec playwright test -c tests/e2e/execution-recovery/playwright.config.ts recovery.spec.ts

Manual: open a stopped task through each affected navigation path. Confirm its explanation, original owner, recovery owner if different, and next action. Follow the supported recovery until the source task produces a useful result or a specific escalation. Reload and verify that resolved notices retire and attempt history remains. A fixture pass does not qualify every live harness/provider.

The native restart integration suite also covers a prepared remote workspace interrupted before the executor's first claim. Real database tests assert one same-run bootstrap claim under concurrent controllers and reject prior claims, launch/provider evidence, and malformed workspace references. These cases prove classification, not a live remote provider restart. For that journey, interrupt a disposable deployment after workspace preparation and before its first executor claim, then confirm one provider turn starts on the original run through the normal admission gates.

Active remote process supervision distinguishes a failed health read from a confirmed exit. The executor tests exercise timeout recovery with a real listening process, unchanged ownership, and rejected identity evidence. The Runner restart tests require positive exit evidence before replacement. This coverage does not prove recovery of a historical retained sandbox or behavior during a live provider outage.

For a release change during a remote provider session, verify that reattachment uses the controller's admitted pack manifest and runner digest. Check useful output from the same process without replaying a turn. Repeat with a modified sidecar, mismatched receipt scope, replacement lease, and incompatible runner protocol. The authority tests and executor wiring regressions cover selection and fail-closed scope checks. They do not prove a historical retained sandbox was recovered.

For completion recovery, restart after a native completion proposal is appended but before its session checkpoint. Verify that the original result is finalized after provider termination, with no new provider turn or repeated external action. The runtime regressions exercise the real Codex driver with a simulated transport, including a still-active provider and invalid, conflicting, duplicate, or stale proposals. This does not qualify a hosted sandbox restart.

The same database suite pauses an original preparation while recovery claims the remote run. It then verifies that the old preparation yields without opening a provider or changing the recovery owner, for both live and expired leases. Heartbeat boundary tests verify that the detachment signal transfers recovery ownership before cleanup. The suite also rejects an older unmarked preparation and verifies that calling preparation again cannot add the fencing marker from supplied profile data.

For a native workspace owner hold, repeat issue reads and recovery checks after the task becomes done or cancelled. Verify one unchanged action ID, attempt count, and timestamps; task completion must not authorize copyback. Clear only the exact receipt after operator stop verification, then confirm stale action cleanup. The route regressions and heartbeat regression cover this sequence without a live operator recovery claim.

An intentionally Done onboarding parent can still need its validated child-completion report. Recovery must not mark that callback successful before execution solely because the parent is Done. The existing stale-lock sweep suite covers delayed native/Legacy startup, invalid parent/child/company/assignee bindings and a dead owner. Process-death and ownership backstops remain active. Repeat the real onboarding journey and correlate callback turn events with its reply and downloadable child file; status alone does not prove delivery. The literal ea6c8eb5e16d61111968f633a66748b397b64a3e live failure is retained, and the revised behavior still needs live qualification.

run-detail

Automated: run workspace recovery tests this host; it is component coverage, not an end-to-end repair.

Manual: prepare a failed run with error code workspace_validation_failed, a linked source task, and a live workspace_validation recovery action on that task. Open that exact run and compare its recovery diagnosis to the source task. Use an eligible action from the run surface, return to the task, and verify the same action state, persisted outcome, and restored execution. Repeat with an actor who lacks authority. Verify that the run-page controls are absent for other error codes or after the source action retires; continue other recovery checks on the source task. Full run-detail navigation remains a manual gap.

operator-retry

Automated: disposition notices tests pending/error/gated states and waiting for the real retry acknowledgement. The recovery-card suite covers workspace divergence, reissue, reconcile, and confirmation. Workspace export covers quiet restoration, legacy/operator assistance and resolved states.

For a retained legacy or native file hold, open the source task after its recovery action has resolved but its files still need recovery. The shared task projection shows actual working/backoff state or ordinary Retry. It does not show a separate infrastructure attention card. Unknown external outcomes remain fenced internally. An authorized board operator can use the resolution API to record verified files and a backup link without changing task status or starting another run. The server independently validates the original stopped-execution receipt. Agent Chat retains the hold rather than suggesting another message. The notice and repair route cover quiet live recovery, read-only access, stale or missing stop proof, source-only repair, and preserved task status. These tests use fixtures; separately verify original files and their attached artifact before recording a real repair.

For a source with a retained-file protection marker, exercise recovery through the assembled environment runtime. Its exact preservation owner may reopen the stopped allocation to read saved files; ordinary resume stays blocked. Reject a changed claim, missing stop intent, wrong lease or wrong environment before a provider call. Verify saved and newer local files, one preservation receipt, and no additional agent turn. The PostgreSQL regression covers this boundary; the opt-in provider probe also uses this runtime path with the protection marker.

For a missing managed allocation, verify a fresh Cloud-signed absence observation under the exact physical recovery owner. The existing Retry may then select an independent workspace through normal admission. Verify that the original run stays failed and missing files are not called recovered. Refuse a foreign stack, source, allocation or owner, expired signatures, changed provider organizations, permission failures and provider outages. Local PostgreSQL and signed-protocol regressions cover these boundaries; deployed recovery requires separate proof.

Repeat source-only repair with a succeeded legacy run that still has a retained file obligation. Its result must remain successful, its current task owner and status must stay unchanged, and later saved input must become eligible for normal admission. A running or queued source and a normal retry of a successful turn remain denied. The reconciliation regressions cover these cases and stop-receipt validation without claiming live provider proof.

For an abandoned native copyback owner, the harness verifies the exact stopped deployment and the reconciler restores saved work automatically. Task users see no process details or shutdown form. The documented operator API handles older receipts after independent platform verification, through a separate administrator gate. The authorization regressions and route regressions cover administrator admission, ordinary-member and agent denial, suspended and foreign membership, missing role data, and stale cloud admin rows. The native workspace finalizer suite runs the real reconciler with a valid saved yield from result_accepted, workspace_finalizing, and retryable_failure. It verifies physical fencing before verified stop, automatic and operator recovery, successful export and commitment, hold resolution, unchanged saved results, retained approval/monitor waits, and no new provider wake. These local regressions do not prove a deployment platform stopped a foreign writer.

Manual: reproduce an exhausted disposable action. Click once, verify pending state prevents repeated clicks, and inspect a successful continuation or an explicit rejection. Reload to confirm the durable outcome. For a workspace repair, record the compared branches/revisions and preserve uncommitted work; confirm the repair's actual result before resuming. Check the denied and stale action paths. A changed card label is insufficient proof of workspace integrity.

For graceful legacy shutdown, interrupt a disposable sandbox run while file export is in flight. Delay settlement past the first 30-second wait. Confirm the lease stays available, no repair hold or successor appears before export, and the finalizer drain waits for the same owner. After export, confirm saved files, successful lease cleanup, and one bounded continuation. Reload before checking the successor. Repeat with controller loss: the expired fence must retain the exact source and require repair, never claim restored files or start a second executor. The database-backed shutdown regression checks finalizer joining, release order, durable retry uniqueness, and reload. The settlement regressions cover other-controller fencing and owner loss. Provider boundaries are controlled in these tests; they do not qualify a live sandbox export.

For an abandoned native controller, follow the stop-verification procedure with a disposable deployment. A new platform-bound owner recovers automatically only after the harness signs proof that the exact deployment fully stopped. Legacy owners require independently verified stop evidence through the operator API; the task has no shutdown form. Confirm the original accepted result commits, the recovery hold resolves, saved files remain intact, and existing approval waits or monitor checks remain. No new agent turn should start. Repeat with a retry-phase owner and an ordinary runtime manager; the first can resume through automatic proof or authorized operator confirmation, while the second cannot use the operator API.

post-stop-message

Automated: legacy continuation, queue routes, and native restart recovery cover their respective recovery paths. Legacy failure continuation adds a browser journey; it is not native runtime proof.

Queued Stop database regressions verify that an operator cancellation before runtime selection releases its own locks, preserves deferred input without launching it, and leaves another active cycle of the same routine unchanged. Provider cancellations and already selected or dispatched runs retain their existing release behavior.

Manual: stop a disposable run, submit a distinguishable follow-up, and observe any cleanup wait. After confirmed stop, verify the follow-up reaches one eligible continuation and produces the requested answer. Repeat with a task/ancestor pause and a budget hold: recovery must not bypass the gate. For unknown external outcomes, inspect the provider before authorizing another operation.

browser-reconnect

Automated: live-update recovery covers client transport recovery with mocks. It does not prove runner restart.

Manual: go offline during a disposable run, let the server reach a new state, then reconnect. Confirm task/run/attention data refresh without duplicate comments or decisions; reload and compare. A healthy websocket proves transport only. Separately inspect the underlying execution if it is still stuck.

Gotchas

  • Legacy and native runtimes have different ownership and restart paths. Record which one was exercised instead of inferring coverage from the adapter name.
  • A process disappearance, elapsed timeout, or successful API retry does not prove an external tool operation never happened. Unknown outcomes stay unknown until corroborated; automatic replay can duplicate a side effect.
  • Active-run watchdogs, task watchdogs, reviewers, and recovery owners have different authority. No recovery card grants board permission by itself.
  • Idle Agent Chat is not stranded work. An intentional operator stop must not silently trigger recovery that fights the user's instruction.
  • Source inspection and mocked component tests do not qualify real server restart continuity. Use the appropriate runtime suite for that claim.
  • See execution semantics for the governing recovery contract and steering for saved follow-ups.

Storybook verification

exhaustion

Capability ID: recovery/exhaustion.

Scope: ui Storybook: Exhausted/overdue attempts, operator retry pending/error, blocked authority/gate and specific escalation. Production: Inspect durable attempt bounds and actual eligible retry/repair; acknowledgment alone does not prove continued execution. Sources: Source: IssueRecoveryActionCard.tsx, DispositionRecoveryNotice.tsx, issue-recovery-actions.ts. Tests: IssueRecoveryActionCard.test.tsx, DispositionRecoveryNotice.test.tsx, issue-recovery-actions.test.ts, recovery.spec.ts

unknown-outcome

Capability ID: recovery/unknown-outcome.

Scope: ui Storybook: Uncertain external outcome, required inspection/evidence and explicit permitted operator decision. Production: Corroborate provider outcome before authorization; enforce no automatic external side-effect replay and reject stale/unauthorized decisions. Sources: Source: IssueRecoveryActionCard.tsx, issue-recovery-actions.ts. Tests: IssueRecoveryActionCard.test.tsx, issue-recovery-actions.test.ts, recovery.spec.ts

user-continuation

Capability ID: recovery/user-continuation.

Scope: ui Storybook: Saved follow-up waiting on stop cleanup, pause/budget gate, one eligible continuation and result/failure. Production: Confirm previous execution stopped before delivery, preserve message, avoid duplicate wakes and honor actual task/ancestor/budget gate in separate legacy/native paths. Sources: Source: TaskChatQueuedMessages.tsx, legacy-continuation.ts, native-restart-recovery.ts. Tests: legacy-continuation.test.ts, native-restart-recovery.test.ts, issue-queued-comments-routes.test.ts, legacy-failure-continuation.spec.ts