mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-12 06:10:44 +01:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - A remote task workspace runs the project's provisioning commands in a sandbox. Each command has a `remote_workspace_provision` journal row, because an arbitrary command cannot be replayed after an ambiguous reply > - A row left `running` with `uncertain: true` blocks every later reservation of that workspace, and nothing ever settles it > - A crash or lost reply during provisioning therefore leaves the task's workspace unusable, with no next action > - This pull request settles such a row only when the earlier run's sandbox is provably stopped. Otherwise the 409 names the row, its run, and the operator's action > - The benefit is that a crashed provisioning attempt no longer locks the workspace forever, and the effect is never assumed to have succeeded ## Linked Issues or Issue Description **Bug description** The Phase 0 ownership inventory (finding 4) lists `remote_workspace_provision` rows left `running`/`uncertain` (`remote-workspaces.ts`) as a record with no automatic release. `reserveRemoteWorkspace` rejects every other run with `remote_workspace_provision_unverified`. A command that simply exited non-zero also stayed `uncertain`. **Steps to reproduce** 1. Start remote provisioning and lose the runner's reply (or have the command exit non-zero). 2. End the run and stop its sandbox. 3. Start another run on the task. Reservation fails with `remote_workspace_provision_unverified`, and the next attempt fails the same way. **Expected behavior** After the earlier sandbox is provably stopped, the row settles and the workspace can be reserved again. While that cannot be proven, the error says what to do. Refs #19846 ## What Changed - `remote-workspaces.ts` `reserveRemoteWorkspace`: for each uncertain provision row from another run, `provisionOwnerProvenStopped` requires two things. The row's run is terminal, and every environment lease of that run carries a provider stop or destroy receipt (`hasRemoteTerminationReceipt` or `hasRecoveredCleanupStopReceipt`). A proven row is settled as `failed` (`uncertain: false`, `settledBy: "owner_sandbox_stopped"`) and logged as `workspace.remote_provision_settled`. It is never marked succeeded. - If a later reservation check (for example a writer hold) rejects the attempt, the proven rows are re-proved and settled in their own transaction, so they do not block again. - An unprovable row still blocks. The 409 now carries `operationId` and `runId` and names the action: stop the earlier sandbox, or set the task's Workspace to "New isolated workspace". That picker option already exists in `IssueProperties`. - `provisionRemoteWorkspace`: a reported non-zero exit (not a timeout) is recorded as `failed` with its `exitCode` instead of staying uncertain. ## Verification - `npx vitest run src/services/remote-workspaces.integration.test.ts src/services/remote-workspace-save-concurrency.test.ts`: 126 passed. - New case "settles an uncertain provisioning command once its run's sandbox is provably stopped". A failed run alone still gets the 409 with `operationId`/`runId`. After a stop receipt, the row settles, and the activity is logged once. - New case "records a provisioning command that reported a failing exit as finished, not uncertain". - Red before the fix: with the old `remote-workspaces.ts`, both new cases fail. Green after. - Server `tsc --noEmit` shows no errors in `remote-workspaces.ts`. - Not run: a live browser check of the "New isolated workspace" path for this error. I confirmed in code that it reserves a different workspace, so it bypasses this check. ## Risks - A settled row means "effect unknown". The next writer provisions again in its own epoch, as every new writer already does. A partially applied, non-idempotent command could see its partial effect. That is the same exposure as any failed provisioning, and it happens only after the earlier sandbox has stopped, so the two commands never run at the same time. - Previous-release story: no persisted shape changes. Uncertain rows saved by the previous release are evaluated by the same proof on the next reservation. - No new waits before `turn.start`. The proof runs only when an uncertain row exists, inside the existing reservation transaction. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used with tool use and code execution in Claude Code. ## 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 - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>