## Thinking Path > - Paperclip governs tool execution and keeps durable approval records. > - An interrupted execution may have produced an external effect without a saved final request state. > - The maintenance tick currently scans executing requests even when none exist. > - Every executing claim already has a ten-minute stale deadline. > - This PR schedules that deadline from committed claim changes and clears it after settlement. > - Recovery preserves saved provider outcomes and quarantines unknown outcomes without another provider call. ## Linked Issues or Issue Description Refs #17454. This is the sixth recurring-server sleep slice, stacked on `codex/sleep-05-approved-tool-recovery`. **Problem** The aggregate maintenance tick repeatedly searches for stale executing tool requests. Its settlement can overwrite a saved successful invocation with an unknown-outcome failure after a crash between invocation and request writes. **Proposed Solution** Use the existing review notification boundary and shared scheduler. Read the earliest executing claim deadline through a partial index. At that deadline, lock invocation then request, recheck claim identity, and atomically settle from the saved outcome or quarantine the unknown outcome. **Alternatives Considered** A longer polling interval still queries empty work. A new queue would duplicate existing records. Replaying an interrupted call can duplicate an external effect. Treating every stale request as unknown discards already saved outcomes. ## What Changed - Coalesce sequential review notifications for up to 30 seconds while preserving already loaded recovery deadlines. Do not schedule scans when no notification or execution deadline remains. - Extract interrupted-execution deadline lookup and settlement into the owning tool-review service. - Register its worker at startup. Use committed claim/result changes to update the existing ten-minute deadline; retry failed settlement after 30 seconds. - Preserve terminal invocation results, reasons, and completion times when repairing request state. - Settle uncertain request/invocation state atomically with the existing unknown-outcome reason, without dispatching a provider call. - Notify all gateway claim and settlement paths through the existing review transaction helper. Keep framework wiring and the shared scheduler free of provider-specific behavior. - Add an idempotent concurrent partial index for executing deadlines. Preserve the combined manual sweep and scheduler-disabled startup pass. - Remove interrupted scanning from the aggregate tick. Interaction projection repair remains the next separate duty. ## Verification Current head: `648d8a9a64ded15deeeb331545b92a12d69e277d`. The final commit changes no files and requests CI after the master retarget. A preceding run appeared while that trigger was being published; it was observational delay, not an established CI outage. No test rerun or bypass was used to turn a failure green. Source remains identical to the 198-test passing integration, apart from the already recorded ledger update. Fresh checks and both automated reviews must qualify this current head before merge. Current master-targeted head: `d55e44b376555e4926bb2c2a0e4dcfb431324162`. Parent #17454 landed as `77295faebc8`; a normal merge reconciles that squash. Every conflicted master file was checked against the already-integrated parent, and all matched byte-for-byte. Each interruption-only hunk was retained; the complete integrated source tree matched the 198-test passing `848f1b87b3` exactly before the sole final documentation update. All master migration journal entries are unchanged, with only this PR's executing-deadline index appended. Server types pass. Fresh CI and current-head Greptile/Ponytail are required before the next authorized merge. Latest published integration: `848f1b87b3a6646ae4e56e8ab142f64889d7a69c`, normally merging approved-recovery parent `23868c1751`. All 198 gateway/interruption/startup/deadline cases and server production typechecking pass. Both parent approval fixes and all interruption tests are retained. The two merge conflicts preserved the interruption import alongside the parent's claim-bound hash helper and preserved the parameterized historical test helpers. This remains stacked on #17454; fresh CI and both automated review gates are required after integration. - Review documentation correction at `c985eb9d1b48a481909de6a1c2d89d7af11bfc03`: the focused real-PostgreSQL interruption-worker selection passes all 16 cases; 124 unrelated cases were filtered out. The docs now correctly name 16 cases and six saved terminal statuses. No production behavior changed in the correction. Fresh remote CI/review is pending after integrating the verified parent. Approved delegated Test-tab calls now evaluate caller and target policies in the existing claim transaction. Real HTTP regressions first showed a missing caller charge and shared-limit self-rejection. All 78 connection-management cases and 92 gateway cases now pass on the owning branch, along with server production typechecking. All 78 connection-management cases also pass at combined tip `d25f830db89c1623bff3547bfc68015c1c66e0cf`. The three new cases cover shared charges, separate charges, rollback on denial, duplicate approval, and rejection of a later authorized call. Intermediate counter/identity fixture failures and a reused-database Slack collision are recorded in [the owning audit](https://github.com/paperclipai/paperclip/blob/f6a5949fb726f71c247950714adecb275655284f/doc/idle-approved-tool-recovery.md#delegated-test-tab-admission-after-policy-changes). The earlier combined checks below remain evidence for their stated revisions. Current-head CI and review remain required. Current head: `f6a5949fb726f71c247950714adecb275655284f`. This remains a single-duty PR stacked on #17454. - Integrated pinned master `8548173fc9efdc0fa07644fb92dcc0b6851a95e5` with normal merges. Every branch passed generated-schema checks and its target-branch migration-order gate. Existing SQL is unchanged; the 25 stack migrations follow master’s new migration. - Duty-specific producer coverage, lifecycle tests, and earlier failure records: [2026-10-24-recurring-server-sleep-stack.md](https://github.com/paperclipai/paperclip/blob/eaac8d343e5ef4d2c13cdf7cb7607cb5a498e9a2/doc/plans/2026-10-24-recurring-server-sleep-stack.md). - Combined tip `51e6cbe67979b55b340ea9f0f434c5b5c682ccd6`: **355 startup/cancellation/recovery/dispatch/preservation tests**, **75 connection-management tests**, and production server typechecking pass. These combined checks do not mean every intermediate PR independently ran the full repository suite. - Real master-to-stack database upgrade passes: **379 unchanged prior migration receipts plus 25 new migrations**, with the gateway responsibility fixture preserved. - Owning-branch checks: 84 runner installation/settlement cases plus runner typechecking and generated TypeScript/Rust profile agreement; 253 GitHub review workflow cases. Stop-attribution and held-worker readiness regressions failed before their fixes. - Failure accounting: the initial combined run passed 349 cases and failed six idle assertions in a reused preservation database containing old cancelled runs. Repeating the combined command with a fresh preservation database passed. Earlier invalid project selection, merge import/type errors, and the interrupted full local suite are not counted as passing checks. - **Latest-head CI and review remain required.** Prior CI findings include GitHub idle observation, route teardown, attention cleanup, touch-picker E2E, workspace health, and voice-cleanup timing; local passes do not erase those failures. No full local repository-suite, deployed sleep, fleet cost, or live Copilot qualification claim is made. ## Risks - Signals cover the existing single-active-writer database owner. Independent writers and replicas need an explicit wake contract. - The ten-minute policy is unchanged. A provider call that outlives its claim remains an uncertain outcome; this worker never retries the external call. - Interaction projection follows authoritative settlement outside its transaction. Its separate existing repair duty still handles an interrupted projection. - Non-executing review writes can cause one empty reconciliation. Without an executing claim or failed attempt, no recurring deadline remains. - Cloud eligibility and external-alarm coverage remain conservative. Worker quiet does not prove actual host or database suspension. ## Model Used OpenAI Codex, GPT-6 family, with reasoning and code execution. The exact serving model identifier and context window were not exposed. ## 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 --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
6.8 KiB
Interrupted tool-action recovery
An executing tool-action request has an existing ten-minute stale deadline from
its updatedAt claim timestamp. Recovery now registers the earliest actual
claim deadline with the shared scheduler. It no longer scans executing requests
on every aggregate maintenance tick.
Ownership and producers
The gateway owns execution claims, provider calls, and result settlement. The new interruption service owns only the deadline query and stale-row settlement. The worker uses the existing deadline registration and exposes no provider protocol or signed argument representation to the scheduler.
The review write helper signals interruption alongside approved recovery and pending expiry. All three agent claim paths and the Test-tab claim use that helper. Test-tab outcomes already commit both rows inside it. Agent pre-dispatch and execution failure settlement uses the same notification transaction, and successful or failed direct execution request updates now notify before writing. Administrative review transitions continue to use the same existing boundary.
Unrelated pending review changes can cause a bounded empty reconciliation. They do not create a periodic deadline. Process-local signals cover the existing single-active-writer database owner; independent writers require a wake contract.
Settlement and quiet states
The query uses a partial executing-request index and reads one earliest row. There is no deadline after all executing claims settle. Retained terminal requests and configured-but-idle tools produce no recurring worker queries.
At the deadline, settlement locks invocation then request, matching the existing
execution-failure lock order. It rechecks the claim status and timestamp after
locking. A changed or completed claim cannot be settled from an earlier scan.
Unknown outcomes atomically mark both rows failed with
tool_execution_outcome_unknown. Recovery never calls the external provider.
A saved terminal invocation is authoritative. Recovery repairs its request to executed or failed without replacing the saved result, failure reason, or completion time with an unknown-outcome failure. This covers a crash between legacy invocation completion and request settlement. Interaction projection runs after the transaction through the existing gateway owner. A failed projection still relies on the separate projection-repair duty.
A database failure rolls back both outcome rows and retains a 30-second retry. Startup reconstructs saved deadlines. Drain pauses dispatch without forgetting the first claim or failed retry; resume processes overdue claims. Completion notifications cancel unnecessary future scans. The scheduler-disabled path keeps one startup pass. The public combined review sweep retains its existing duties.
Verification and limits
Sixteen new real-PostgreSQL cases cover real foreground execution activation and completion, configured idle and terminal history, restart/deadline timing, six saved terminal invocation states, first-write drain/resume, outer commit and rollback, transactional failure/retry, concurrent claim change, and multi-page settlement. The service/startup selection passes 136 tests on the combined parent-review head. Workspace typecheck/build and migration safety checks pass on that head. Migration application and reapplication passed. The combined run also exercised 469 tool-access tests: 468 passed and an unchanged connection-archive case timed out under concurrent build load. That case passes in isolation. The separate full tool-access suite passes all 469 tests and gateway acceptance passes all 92 (697 across the final selections). Fresh full CI/review remains required.
The first typecheck caught optional approval IDs losing narrowing in transaction callbacks; capturing them before the callback fixes it. Two new fault/transaction cases initially waited for idle before advancing their next fake-clock retry. They now advance time while waiting for the authoritative outcome, and the full selection passes. These are fixture corrections, not permission to clear an unresolved work hold.
The ten-minute policy is unchanged. Active provider execution, unknown external outcomes, and other worker obligations still prevent unsafe sleep. No Cloud eligibility or external-alarm coverage changes here. Worker quiet is not evidence of whole-instance or database suspension.
Sequential-write cost correction (October 27)
Review found that sequential tenant writes could each request a global interruption pass, even when no execution was stale. A real-SQL regression created ten tool review requests through the gateway and allowed each preceding pass to finish. It measured 11 stale-claim scans including startup. Concurrent notification coalescing alone did not cover this sequence.
The worker now uses the existing deadline helper's 30-second notification coalescing window, matching the former default maintenance cadence. The same regression measures one startup scan and one follow-up scan for those ten requests, then no worker SQL through an additional idle hour. A loaded stale claim deadline only 500 milliseconds away still preempts the window and quarantines the unknown outcome without invoking the provider. Foreground execution and result settlement remain immediate. Newly discovered work can wait up to this window; the ten-minute stale-claim policy is unchanged.
This is the bounded-frequency alternative raised in review. Discovery is still instance-wide, and real stale backlogs still drain through the existing pages. It does not claim company-isolated scans or constant cost for a real backlog. No notification and no saved execution deadline means no periodic scan. No new table, index, or scheduler is added. Startup, drain, retry and shutdown retain the existing owners.
The first full verification passed 175 cases and failed three fake-clock waits: settlement writes leave a notification pending for its coalesced follow-up, so waiting for the idle hold without advancing that armed deadline cannot finish. An initial shared clock adjustment then advanced an unrelated five-second expiry test too far (177 passed, one failed). The adjustment is now confined to the interruption tests. It lets real SQL finish and advances only to an actually armed short follow-up. The imminent-deadline test checks quarantine before that 30-second window and separately waits for eventual quiet. Product assertions were retained.
The final complete gateway-service and startup-wiring suites pass all 178 cases
without skips, and server typechecking passes, on owner source based on
e255d8eff3363cf0bb63541bdc7ef055d4321f83. The attempted third test selector
deadline-work.test.ts did not exist at this owner; no separate helper-suite
pass is claimed. The shared helper is exercised by the full gateway suites.
Fresh combined-source checks and remote review remain required.