Commit Graph
3 Commits
Author SHA1 Message Date
Devin FoleyandPaperclip 7e10dbe652 fix(server): retain stopped ephemeral workspaces after failed copy-back (#15790)
## Thinking Path

> - Paperclip manages work performed by agents.
> - A remote adapter must copy workspace changes back after execution.
> - Cancellation can stop and release an ephemeral sandbox before
copy-back fails.
> - The recovery recorder previously considered only active or already
held leases.
> - This change carries the original host lease into the recorder and
checks both stop receipts.
> - The exact stopped source receives a board repair obligation without
replaying work.

## Linked Issues or Issue Description

**What happened?**

A legacy remote run can report `workspaceRestoreFailure` after its
dedicated stop has released the source lease. The recorder misses that
source and does not create its file-repair action. Related prior work:
#15637 adds bounded transfer diagnostics; #15718 protects native
accepted finalization. This change addresses the separate legacy
recording order.

**Expected behavior**

Preserve the exact eligible source and record the existing board repair
obligation. Preserve cancellation, later task progress, and newer
execution ownership.

**Steps to reproduce**

1. Acquire an ephemeral remote lease for a legacy run.
2. Complete the dedicated stop-and-retain lifecycle and persist both
stop receipts.
3. Report a workspace copy-back failure from the original adapter
invocation.
4. Observe that the old recorder omits the released source. The
PostgreSQL regression reproduces this order.

**Paperclip version or commit**

de042e79ef.

**Deployment mode**

Server with PostgreSQL and a sandbox provider. The regression uses
disposable local PostgreSQL and synthetic provider receipts.

## What Changed

- Clone the realized host lease before adapter dispatch and carry it
into restore recording.
- Admit a released source only when its original and current policies
are ephemeral, its full ownership and workspace identity match, and its
dedicated retention receipt agrees with the independent
stopped-execution receipt.
- Reject reusable, expired, destroyed, rebound, competing, or
insufficiently proved sources. Preserve release state and receipts when
recording the existing no-replay repair action.
- Cover the actual stop-before-copy-back lifecycle, cleanup retries,
built-in dispatch, rollback, repeated recording, newer owners, and
ownership/receipt failures. Document the narrow scope.

## Verification

- `pnpm exec vitest run
server/src/__tests__/legacy-workspace-restore-recovery.test.ts
server/src/services/legacy-execution-recovery.test.ts`: 80 passed.
- Existing Grok confirmed-stop/copy-back boundary test: passed.
- The new PostgreSQL regression fails on the baseline because the
recovery marker is absent.
- Guard-removal controls fail when either the ephemeral-policy gate or
original-source identity checks are removed.
- `pnpm --filter @paperclipai/server typecheck`: passed.
- Additional competing-owner/lifecycle checks: 6 passed, including older
stopped and destroyed allocation owners.
- `pnpm -r typecheck` and `pnpm build`: passed.
- Independent review approved the final source. The exact ephemeral
regression fails on the baseline; policy/identity guard-removal controls
fail their intended assertions.
- Compatibility with #15789 was checked in a disposable combined
checkout: both commit orders yield the same tree. The earlier broad run
passed 136 cases; the final guard/SDK delta passed 9 cases.
- Greptile: 5/5 on the current head, with no review threads.
- Exact-head CI: 53 successful checks and 2 expected skips; no failures
or pending checks. This includes all test shards and Canary Dry Run.
- The duplicate local `pnpm test:run` aggregate was stopped after full
CI passed. It is not reported as a completed local suite.
- No live provider calls, historical data repair, run replay, or
deployment was performed.

## Risks

No schema or API change. A qualifying source remains held for manual
file repair and eventual removal, which can retain provider storage
longer. This fix does not recover historical files or establish that the
provider still holds the bytes. Reusable sources remain excluded from
this new path because provider resume can start before the database
handoff. Existing active-source and previously recorded recovery
behavior is unchanged.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools, and
independent agent review. The exact model revision and context-window
size are not exposed to this session.

## 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-10 01:27:03 -07:00
Devin FoleyandPaperclip 7a58591721 fix(recovery): preserve accepted workspace finalization during reaping (#15718)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A native run saves its accepted result before it copies workspace
changes back.
> - A restart can leave that copyback under an unverified physical
owner.
> - The generic orphan reaper can mistake the exited provider for lost
work and release the source lease.
> - Recovery must preserve the accepted result and let the existing
finalizer settle its remaining work.
> - This pull request checks that authority at the conditional
process-loss write and preserves the matching recovery hold.

## Linked Issues or Issue Description

Related: #15475 added bounded process-loss evidence. The earlier #9687
addresses dead-run finalize barriers with synthetic receipts. This
change preserves an existing accepted native result and requires real
workspace settlement.

**What happened?**

After a server restart, the native finalizer required operator
verification of unfinished copyback. The generic orphan reaper then
marked the run `process_lost` and released its source lease. A new
execution path also made the recovery reconciler clear the copyback hold
before physical ownership was verified.

**Expected behavior**

An accepted result bound to the same company, issue, and run keeps its
unfinished workspace suffix under native finalization. The generic
reaper must not discard its source. The existing finalizer must still
complete the work, apply its bounded failure policy, or require operator
verification.

**Steps to reproduce**

1. Accept a native result and leave its coordinator in
`workspace_finalizing`.
2. Stop the controller before copyback completes, leaving its physical
owner receipt.
3. Start recovery, then run the generic orphan and lease cleanup sweeps.
4. Before this change, the run can fail with `process_lost` despite its
accepted result and explicit copyback hold.

**Paperclip version or commit**

Reproduced against `011e5669`; the same recovery code remains at base
`1602512820`.

**Deployment mode**

A hosted server with PostgreSQL and native runner execution. The tests
use only isolated local PostgreSQL and synthetic fixtures.

## What Changed

- Check the exact accepted result and `workspace_finalizing` coordinator
before generic process-loss cleanup.
- Repeat the check in the conditional status write. Compare the
heartbeat tuple version to reject a result publisher that commits while
the update waits on its row lock.
- Preserve nonterminal copyback holds when another execution path
appears. Resolve only the matching run's hold after verified
finalization commits.
- Add fourteen real PostgreSQL service regressions and document the
recovery boundary.

## Verification

- `pnpm -r typecheck` passed.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-process-recovery.test.ts -t 'native
finalization'`: 14 passed. The test filter excluded 383 existing tests.
- Tests cover startup, repeated reaper and cleanup sweeps, malformed and
foreign owners, an expired old controller, exact database bindings,
legacy and unsupported states, verified settlement, and unchanged
terminal-task dispositions.
- The concurrent publisher test uses the real accepted-result
transaction and proves the reaper update is blocked by its row lock
before the publisher commits.
- The local `pnpm test:run` aggregate was stopped after setup timeouts
in the chat and tool-access files and a damaged-epoch Runner test
failure. It is not a full local pass.
- The chat file passed all 1,145 tests when run alone. The tool-access
file then passed 419 of 420; one case returned `socket hang up`. That
single case passed on its isolated rerun. The cause of the socket
failure is not established here.
- The damaged-epoch Runner case still fails locally at its existing
completion assertion. The same assertion failed on untouched baseline
`556daf9a` before this patch; its test and Runner sources are unchanged.
Exact-head CI passes this coverage. No workaround for that local fixture
is included.
- `pnpm build` passed.
- Independent review approved this exact commit. All 14 new PostgreSQL
regressions and 181 existing recovery/finalizer cases passed. One
existing suite needed an isolated rerun after a setup timeout.
- Independent negative controls reproduced the original bug. Removing
only the tuple-version guard reproduced the blocked-row race. Removing
only the hold exclusion cleared the safety hold too early. Restoring
this patch passed all three checks.
- GitHub CI passed all 53 applicable checks. The two Storybook jobs were
skipped as expected. Greptile scored this commit 5/5 with no unresolved
review threads.
- Local runtime: Node 25.6.1. GitHub CI supplies the repository's Node
24 checks.
- `git diff --check`, Gitleaks, and a local diff scan for secrets and
private data passed.

## Risks

- PostgreSQL tuple versions are used only as an optimistic fence for
`process_lost`. Both accepted-result publishers lock and update that
heartbeat in their result transaction.
- The guard requires the bound accepted result and the durable
coordinator state. A heartbeat phase or arbitrary owner JSON alone
cannot suppress ordinary process loss.
- Existing finalizer retries, ownership verification, explicit
cancellation, and terminal-task dispositions remain unchanged. A
physical owner can still require operator verification.
- This change does not replay provider work or restore a source already
destroyed by an older controller. No migration or runtime operation is
included.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools, code
execution, and independent agent review. The exact service model ID and
context-window size are not exposed in this worker session.

## 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-09 12:28:16 -07:00
DottaandPaperclip 14795136f5 fix(runtime): finalize and recover sandbox workspace exports safely (#14402)
Serialize native workspace finalization, validate streamed archives within bounded limits, and quietly recover unsafe exports from saved results. Preserve exact allocations for exhausted transient failures and provide export-only retry without rerunning the provider.

Consolidates #14314, #14315, #14329, and #14334 while preserving the already-merged finalization label changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-28 10:57:32 -05:00