paperclip/server/src
Nicky Leach dbf5ea432d
fix: protect starting runs during overlapping deployments (#13285)
## Thinking Path

> - Paperclip controls agent work across service deployments.
> - A run can provision a remote sandbox before a process or invocation
event exists.
> - Each container previously treated its own missing process handle as
proof that the run was orphaned.
> - Overlapping deployments could therefore fail a run owned by another
container.
> - This pull request records and renews a controller lease before
provisioning.
> - A recovery worker must revoke an expired owner before it finalizes
the run.

## Linked Issues or Issue Description

Merged PR #13272 records startup adapter identity and restores explicit
user continuation. This PR adds controller ownership on top of current
master. Refs #7997 and #10442 for related replica and ownership
problems. Related #13138 addresses silence and detached local processes;
this change does not infer death from silence.

**What happened?**
During an overlapping hosted service deployment, a new container reaped
a legacy conversation run that another container was provisioning. The
run had no PID or adapter invocation yet.

**Expected behavior**
A live controller keeps its run. After controller loss, one recovery
worker takes cleanup authority and the old controller cannot dispatch
further work.

**Steps to reproduce**
Claim a legacy run in controller A. Start controller B against the same
database before A finishes provisioning. Run the startup reaper in B.

**Paperclip version or commit**
Observed on `663c44cb2b9c28336d38d0b4a6971f4f1964bce6` in a hosted
Railway deployment with a Daytona environment.

## What Changed

- Add nullable controller boot ID, lease deadline, and execution stage
columns. Claim ownership in the queued-to-running update.
- Renew ownership independently of run output. Abort and reject dispatch
if renewal fails.
- Serialize reaper revocation against renewal. Let unfinished recovery
claims expire after a restart.
- Restrict graceful shutdown to legacy runs owned by the current
controller.
- Hand ownership back to the existing native coordinator when runtime
selection becomes native.
- Add twelve database regressions and document the lease contract.
Update the task-drain regression to require controller expiry before
reaping.

## Verification

- `pnpm exec vitest run
server/src/services/legacy-controller-lease.test.ts
server/src/__tests__/heartbeat-task-drain-admission-release.test.ts`: 14
passed after rebasing onto master (`f12b647ae`).
- Queue-interruption regressions in
`heartbeat-process-recovery.test.ts`: 2 passed after preserving the new
cleanup promotion from #13275.
- `pnpm --filter @paperclipai/server exec tsc --noEmit`: passed after
rebuilding runner TypeScript outputs for the updated master. Broad local
tests are omitted at the maintainer’s request; CI owns broad coverage.

- Latest-head CI passed on `f255e8e4d5ab2b24b12638a02434e6aa8a2285c5`:
[run
34658248569](https://github.com/paperclipai/paperclip/actions/runs/34658248569).
All test shards, browser suites, typecheck, build, canary, and security
checks passed. Greptile is 5/5 with no unresolved review threads.

## Risks

- Additive, idempotent migration; historical rows retain the previous
recovery behavior.
- Database unavailability aborts new dispatch rather than permitting an
unfenced controller to continue.
- Lease expiry is permission to clean up, not evidence that remote
inference stopped. Follow-up PRs add persistent cleanup and automatic
continuation.
- Mixed-version deployment still includes old binaries whose reapers do
not understand controller leases.

## Model Used

OpenAI GPT-6 through Codex, using reasoning, repository inspection, code
execution, and test tools. The precise backend revision and
context-window size are not exposed in 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-09-11 16:55:44 -07:00
..
__tests__ fix: protect starting runs during overlapping deployments (#13285) 2026-09-11 16:55:44 -07:00
adapters fix: repair runner configuration, macOS execution, and artifact galleries (#13062) 2026-09-08 19:10:09 -05:00
auth feat(runner): add Codex-native application integration (#12591) 2026-08-31 14:38:38 -05:00
built-ins/agents Remove cheap model profiles (#12683) 2026-09-01 14:57:38 -05:00
http feat: add experimental native chat connectors (#13038) 2026-09-10 10:06:45 -05:00
lib perf(server): reduce issue detail request overhead (#10414) 2026-08-11 14:39:23 -04:00
middleware feat(server): accept a Cloud control assertion on the task-drain endpoint (#13125) 2026-09-10 11:56:28 -07:00
modules fix: reliably interrupt and resume legacy message queues (#13275) 2026-09-11 18:26:59 -05:00
onboarding-assets feat(onboarding): first task opens as a chat with a chief of staff (#13068) 2026-09-08 20:19:14 -07:00
realtime feat: add experimental native chat connectors (#13038) 2026-09-10 10:06:45 -05:00
routes fix: reliably interrupt and resume legacy message queues (#13275) 2026-09-11 18:26:59 -05:00
secrets feat(secrets): write through external values and deep-link details (#10196) 2026-07-27 19:08:07 -05:00
services fix: protect starting runs during overlapping deployments (#13285) 2026-09-11 16:55:44 -07:00
storage feat(run-logs): durable run-log store via object-storage mirror (#8984) 2026-07-14 15:45:39 -07:00
types feat(server): accept a Cloud control assertion on the task-drain endpoint (#13125) 2026-09-10 11:56:28 -07:00
vendor/paperclip-runner fix: continue conversations after confirmed remote runner stop (#13254) 2026-09-11 15:18:37 -05:00
agent-auth-jwt.ts fix(server): align agent run JWT default TTL with documented 48h default (#10176) 2026-08-12 16:44:20 -07:00
app.ts feat(connections): add AgentMail inboxes and email tasks (#13256) 2026-09-11 16:56:38 -05:00
attachment-types.ts feat: add opt-in chat provider and data foundation (#13100) 2026-09-09 13:49:12 -05:00
board-claim.ts [codex] Add agent permissions and controls plan (#6386) 2026-05-22 08:12:52 -05:00
build-commit.ts fix(server): preserve source SHA without Git metadata (#9638) 2026-07-15 20:03:52 -05:00
build-version.ts fix(server): stamp the real build version into images instead of the package.json placeholder (#10257) 2026-07-25 10:06:28 -07:00
chat-webhook-public-url.ts feat: add experimental native chat connectors (#13038) 2026-09-10 10:06:45 -05:00
cloud-ui-snippet.ts feat: accept a base64-encoded Cloud UI snippet (#13245) 2026-09-11 14:13:11 -07:00
config-file.ts fix(config): preserve extensions and guard invalid repairs (#11005) 2026-08-07 00:41:19 -05:00
config.ts feat: add experimental native chat connectors (#13038) 2026-09-10 10:06:45 -05:00
db-errors.ts fix(issues): make DELETE /api/issues/:id succeed for issues with dependents (#11331) 2026-08-13 12:02:15 -07:00
dev-native-runner-status.ts feat(runner): integrate Codex native execution (#12616) 2026-08-31 22:51:17 -05:00
dev-runner-worktree.ts feat(workspaces): sign the workspace login handoff and gate readiness (#11671) 2026-08-19 02:37:02 -05:00
dev-server-status.ts fix(runner): recover native sessions across restarts (#12845) 2026-09-04 15:03:53 -05:00
dev-watch-ignore.ts fix(server): ignore sibling worktrees in dev watch (#11074) 2026-08-07 18:30:16 -07:00
embedded-postgres-supervisor.ts fix(workspaces): recover degraded runtime databases (#11651) 2026-08-18 17:40:54 -05:00
env-file-policy.ts feat(cli): add isolated test-drive command (#12894) 2026-09-05 09:33:38 -05:00
errors.ts feat(artifacts): bridge Markdown work products into the document review surface (#11822) 2026-08-20 17:28:01 -07:00
first-admin-claim.ts [codex] Add private browser first-admin claim flow (#6755) 2026-05-27 21:15:01 -10:00
home-paths.ts feat(server): thread plural referenced-project workspaces through run prep (#10448) 2026-07-29 14:27:05 -07:00
index.ts fix: recover legacy Daytona startup failures from task and inbox (#13272) 2026-09-11 18:08:14 -05:00
instrumentation.test.ts fix(server): export manual OpenTelemetry spans (#10565) 2026-07-31 12:47:57 -07:00
instrumentation.ts fix(observability): pin the Sentry browser SDK and gate the optional Sentry server peer on the exact version (#12270) 2026-08-27 07:20:03 -07:00
log-redaction.ts Sync/master post pap1497 followups 2026 04 15 (#3779) 2026-04-15 21:13:56 -05:00
paths.ts
peer-version-check.ts fix(observability): pin the Sentry browser SDK and gate the optional Sentry server peer on the exact version (#12270) 2026-08-27 07:20:03 -07:00
redaction.ts feat: add experimental native chat connectors (#13038) 2026-09-10 10:06:45 -05:00
runtime-api.ts Prefer loopback runtime API URL for local agents (#5102) 2026-06-20 14:03:21 -07:00
runtime-tools-token.test.ts feat(connections): add self-serve intent runtime (#12345) 2026-08-29 12:08:34 -05:00
runtime-tools-token.ts feat: use the responsible person's GitHub for shared agent operations (#13005) 2026-09-07 14:32:20 -05:00
sentry-dsn.ts feat(server): split the Sentry DSN into front-end and backend variables (#12678) 2026-09-01 11:02:04 -07:00
sentry.ts feat(server): split the Sentry DSN into front-end and backend variables (#12678) 2026-09-01 11:02:04 -07:00
server-info.ts fix(server): preserve source SHA without Git metadata (#9638) 2026-07-15 20:03:52 -05:00
shutdown.test.ts fix(db): reap idle pool connections, name the pool, and end it on shutdown (#12956) 2026-09-08 08:56:39 -07:00
shutdown.ts fix(db): reap idle pool connections, name the pool, and end it on shutdown (#12956) 2026-09-08 08:56:39 -07:00
startup-banner.ts fix(security): route paperclipai CLI guidance through safe npx form (CWE-78) (#11400) 2026-08-14 22:11:16 -07:00
startup-recovery-state.ts fix(runner): recover native sessions across restarts (#12845) 2026-09-04 15:03:53 -05:00
startup-refusals.ts fix(server): stop paging Sentry for supervised boot races in managed cloud (#12772) 2026-09-03 10:02:42 -07:00
static-index-html.ts feat: allow operator UI snippets on Cloud instances (#13168) 2026-09-10 15:30:36 -07:00
static-ui-cache.ts fix(ui): keep the installed service worker fresh on parked tabs (#12198) 2026-08-25 16:12:02 -07:00
telemetry.ts fix: add periodic flush and graceful shutdown for server-side telemetry 2026-04-02 10:47:29 -05:00
ui-branding.ts Ensure worktree execution starts only after activation (#9374) 2026-07-10 16:11:26 -05:00
url-utils.test.ts fix(runtime): only rewrite base-URL port for loopback hosts (#10258) 2026-08-13 09:47:13 -07:00
url-utils.ts fix(runtime): only rewrite base-URL port for loopback hosts (#10258) 2026-08-13 09:47:13 -07:00
version.ts fix(server): stamp the real build version into images instead of the package.json placeholder (#10257) 2026-07-25 10:06:28 -07:00
vite-html-renderer.ts fix(runner): stabilize local paid E2E recovery (#12836) 2026-09-04 11:16:20 -05:00
worktree-config.ts feat(apps): consolidate connector management (#12684) 2026-09-01 14:55:35 -05:00
worktree-seed-manifest.ts feat(workspaces): sign the workspace login handoff and gate readiness (#11671) 2026-08-19 02:37:02 -05:00