mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-02 16:58:52 +01:00
<!-- entire-trail-link-start --> https://entire.com/gh/paperclipai/-/trails/58/changes/paperclip/58 <!-- entire-trail-link-end --> ## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Agent timers must start due work and preserve queue admission rules. > - The server woke their scan on every heartbeat interval, even with no eligible timer. > - Idle scans keep the database and instance active. > - This pull request derives the next deadline from saved policy and work records. > - Supported producer transactions notify the shared coordinator when eligibility changes. > - An idle timer worker stops scanning and resumes when real work or a deadline requires it. ## Linked Issues or Issue Description Refs #17863. This is slice 63 of the recurring-work migration and follows the merged issue-monitor deadline PR. Related: #16632 changes timer issue scope; #16881 changes live-run eligibility. This PR preserves the current admission policy. Those policy changes are separate work. A future time-sensitive predicate must expose its next transition deadline. **What existing behavior does this improve?** Agent timer heartbeat scheduling in the server. **Current behavior** A recurring heartbeat wakes the timer scan even when companies are paused, timers are disabled, or no actionable task exists. **Proposed behavior** Register the earliest eligible interval or positive daily-cap reset deadline. Re-read on supported producer commits and startup. Leave no recurring database scan when no timer obligation remains. ## What Changed - Keep policy and deadline calculation in the heartbeat queue. Scheduling retains company, reporting-chain, worktree, and CAS gates. - Use the existing deadline coordinator. Remove only the timer wake from the server heartbeat loop. - Notify before existing transaction writes that change eligibility or deadlines. Cover configuration, lifecycle, company budgets, task ownership, recovery, conversations, accounting corrections, and completion. - Use the lifecycle status-change hook for saved hire rejection. Cover checkout's second adoption transaction after an initial failed CAS. - Preserve pending work through idle and maintenance drains. Check cancellation between due agents. Reconstruct on restart. - Document the supported writer model and producer inventory. Add no schema, table, or provider branch. ## Verification - **1,710 passing cases across 49 complete files**, no skips, on accepted source `32db86c1974dcbf794f01a8c5e42024e2ad071fc`. Coverage includes real timer policy/CAS/producers, monitor and shared deadline consumers, native reconstruction, Slack lifecycle, skills/settings/configuration, queue isolation and agent lifecycle. - Database build, production server typechecking/build, module boundaries, target migration order and Runner profile identity pass. Twenty-nine fresh PostgreSQL 17 schemas plus per-file fixtures applied all 412 journal entries through index 413; all selected source hashes and temporary fixture imports were restored exactly. - Four new deadline composition cases verify future/overdue timing, minimum scan spacing and disarming. A controlled mutation dropping only the inherited continuation option fails; the composed source passes with identical assertions and timers. - Normal parent reconciliation preserves task-drain expiry and both deadline contracts. The final monitor-parent publication changes only its audit, preserving every qualified runtime and selected test source. - Historical failures and review corrections remain in [the timer audit](doc/idle-agent-timers.md). No full local repository-suite, live provider, measured suspend/wake, external-alarm or fleet-savings claim. Final-head CI/security, exact-head Greptile 5/5 and actionable Ponytail findings remain separate merge gates. ## Risks - Process-local notifications cover supported service/API writers in one server process. Direct SQL and independent replicas need an explicit ingress contract. Startup reconstruction is not live cross-process notification. - Notifications can batch for up to the configured heartbeat interval. Known deadlines retain their scheduler path; failed reconciliation retains bounded retry. - Enabled timers without an actionable-work requirement remain real recurring obligations. Zero caps require policy changes; positive reached caps use UTC midnight. - The timer fixture substitutes adapter enqueue. Existing admission tests also pass, but this is not deployed provider or sleep qualification. - Cloud does not gain legacy agent timer alarms in this PR. Existing sleep-safety blockers remain. No database/host suspension or fleet savings claim is made. - Parent-stack CI and review must pass before merge. This PR does not authorize merging its dependencies. ## Model Used OpenAI Codex (GPT-6-based coding agent), using repository inspection, normal Git merges, composition proofs, a controlled red/green regression and complete PostgreSQL integration suites. No subagents were used in this reconciliation. ## 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> Co-authored-by: Codex <noreply@openai.com>
139 lines
8.7 KiB
Markdown
139 lines
8.7 KiB
Markdown
# Agent timer deadlines
|
|
|
|
The agent timer worker reconstructs its next deadline from existing agent,
|
|
company, issue, run, and cost records. It no longer receives a wake from the
|
|
server's recurring heartbeat interval. The queue service owns timer policy and
|
|
admission; scheduling selects eligible agents and registers the earliest result
|
|
with the shared delivery coordinator. No table or independent scheduler is added.
|
|
|
|
## Deadline and quiet conditions
|
|
|
|
For an enabled timer with a positive interval, the ordinary deadline is
|
|
`(lastHeartbeatAt ?? createdAt) + intervalSec`. Reading it does not advance it.
|
|
A due timer retains the existing atomic timestamp claim and queue admission
|
|
checks. Successful finalization updates the last heartbeat and moves the next
|
|
deadline. An attempted admission still follows the existing claim semantics;
|
|
this change does not promise replay of every elapsed interval.
|
|
|
|
Only active companies and invokable agents with valid reporting chains qualify.
|
|
Worktree execution suppression and the worktree issue creation cutoff still
|
|
apply. A disabled timer or zero interval has no deadline. When
|
|
`skipTimerWhenNoActionableWork` is enabled, the queue's existing actionable-work
|
|
predicate must match an assigned, visible, non-conversation task in Todo or
|
|
In Progress without a user assignee. Completed history does not qualify.
|
|
|
|
A reached positive daily run or cost cap defers work until the next UTC day,
|
|
or the ordinary interval deadline if later. Either zero cap leaves no timed
|
|
obligation: only a policy change can make that timer runnable. An accounting
|
|
correction or released run claim can lower today's usage and bring a saved
|
|
midnight deadline forward. These producers notify the worker.
|
|
|
|
Enabled timers that do not require actionable work remain real periodic
|
|
obligations. This migration does not silently disable those timers. With no
|
|
eligible timer, unresolved transaction, or pending reconciliation, the worker
|
|
has no recurring SQL or network scan.
|
|
|
|
## Producer and transaction ownership
|
|
|
|
Every notification is registered before the relevant write in its existing
|
|
transaction. Nested transactions retain outer commit/rollback ownership through
|
|
`packages/db/src/work-signals.ts`. Durable records are the source of truth;
|
|
notifications request another read and are not an in-memory queue length.
|
|
|
|
| Changed facts | Owning producers |
|
|
| --- | --- |
|
|
| Timer configuration or reporting chain | Agent configuration service, including `updateAgentConfigurationInTransaction`; agent deletion's report reassignment |
|
|
| Agent eligibility | Lifecycle status transition hook, including rejection through a saved hire approval |
|
|
| Company eligibility | Company update/archive; company budget pause/resume |
|
|
| Last heartbeat and daily run claim | Queue timer claim/skip paths, suppression claim release, heartbeat finalization, active-run watchdog finalization |
|
|
| Actionable task creation/status/assignment/visibility | Issue create/update/checkout/release/admin release/delete, including checkout's second adoption transaction |
|
|
| Restored or reassigned tasks | Subtree cancellation restoration, member archival reassignment, native workspace reconstruction |
|
|
| Direct task disarm paths | Queue's invalid workspace block, unrecoverable execution disposition, expired skill harness hiding |
|
|
| Conversation/worktree eligibility | Agent conversation prepare/settle; external conversation resume/settle |
|
|
| Daily cost corrections | Accounting correction transaction |
|
|
| Worktree activation | Experimental settings owner; scheduling requests a fresh suppression read |
|
|
|
|
Agent creation/approval remains governed by lifecycle preparation and its eventual
|
|
status transition. Company deletion requires completed agent termination, which
|
|
already removes timer eligibility. Ordinary run insertion, execution-lock changes,
|
|
monitor metadata, nonnegative cost accumulation, and terminal run retention do
|
|
not create earlier timer eligibility by themselves. In particular, a conversation
|
|
cannot satisfy the actionable-task predicate, but its status can change the
|
|
separate worktree cutoff gate, so those status writers are still covered.
|
|
|
|
Supported live writers are the serving process's API and service boundaries.
|
|
Independent SQL writers and additional server replicas do not share process-local
|
|
notifications. Startup reconstructs their saved state, but there is no live
|
|
cross-process delivery promise or unconditional recovery polling fallback.
|
|
Such writers require an explicit ingress or durable notification contract.
|
|
|
|
## Lifecycle and failure behavior
|
|
|
|
Notifications coalesce for up to the configured heartbeat interval, normally
|
|
30 seconds; already-known deadlines can run sooner. Failed reconciliation keeps
|
|
bounded retries through the shared coordinator. Intent uncertainty is resolved
|
|
by the shared transaction protocol, never by assuming an empty query means a
|
|
write cannot commit later.
|
|
|
|
Idle and maintenance drains pause timer work. The worker retains its request
|
|
without passive database polling, then retries in memory until admission resumes.
|
|
The timer scan checks cancellation/admission between agents. A skipped candidate
|
|
keeps its unclaimed deadline; shutdown stops local scheduling, and restart
|
|
reconstructs deadlines from the database. This uses the shared `registerDeadlineWork`
|
|
generation protocol rather than a timer-specific recovery loop.
|
|
|
|
## Verification and limits
|
|
|
|
`agent-timer-deadlines.test.ts` exercises real PostgreSQL producers, the queue's
|
|
deadline policy and CAS, and the delivery coordinator. It measures no SQL after
|
|
settlement for empty, disabled, completed-history, capped, and disarmed states;
|
|
first-work reactivation; outer commit and rollback; restart; failed reads;
|
|
concurrent claims; both drain modes; and interruption between due agents.
|
|
Native reconstruction and external conversation suites separately verify outer
|
|
transaction visibility and timer notification at their realistic boundaries.
|
|
The timer scheduling fixture substitutes adapter enqueue, so these tests do not
|
|
prove provider execution or replace existing queue/admission regressions.
|
|
|
|
Worker quiet is not host sleep qualification. The Cloud alarm protocol still
|
|
covers only its explicitly supported durable schedules. Legacy agent timers
|
|
remain blocked by the existing sleep-safety proof; this change neither exports
|
|
them as Cloud alarms nor relaxes that proof. Deployed database suspension,
|
|
host suspension, and wake correctness require separate qualification.
|
|
|
|
## October 29 master reconciliation and current acceptance
|
|
|
|
The existing agent-timer slice was reconciled with the qualified issue-monitor
|
|
parent using normal merges. Four conflicts needed explicit composition. Startup
|
|
keeps both task-drain expiry registration and the timer worker's fresh drain read;
|
|
scheduled work keeps the new timer deadline owner; a comment-only conflict has
|
|
no runtime effect. The shared deadline helper retains the parent's explicit
|
|
continuation interval alongside the new per-worker minimum scan spacing. All
|
|
other paths match the automatic merge, and packages remain byte-identical to the
|
|
qualified profile-80 parent tree.
|
|
|
|
Source `32db86c1974dcbf794f01a8c5e42024e2ad071fc` passed **1,710 cases in 49
|
|
complete files**, without skips, in 653.08 seconds. This includes the 35 previously
|
|
qualified monitor/deadline-consumer files plus real timer policy, CAS and producer
|
|
coverage, stale-queue invalidation, native workspace reconstruction, Slack
|
|
conversation lifecycle, skill/settings/configuration producers, claim isolation,
|
|
five agent-lifecycle module suites, and the new deadline composition regression.
|
|
Database build, production server typechecking/build, module boundaries, migration
|
|
order and Runner profile identity pass. Twenty-nine fresh PostgreSQL 17 schemas
|
|
plus per-file fixtures applied all 412 journal entries through index 413. Every
|
|
selected source hash and temporary fixture import was restored exactly.
|
|
|
|
Four new deadline cases check future and overdue work with and without an explicit
|
|
minimum spacing, no dispatch before the selected time, and complete disarming
|
|
after settlement. A controlled mutation discarded only the inherited continuation
|
|
option while retaining timer spacing: the regression failed. Restoring the
|
|
composed source made all four cases pass with the same assertions and timers.
|
|
This guards against fixing the timer floor by restoring a 30-second delay for
|
|
other workers that deliberately use shorter bounded continuations.
|
|
|
|
The final monitor-parent publication changed only its audit. All production trees
|
|
and 49 selected test hashes remain identical to this accepted source. Final
|
|
publication must also adopt the actual merged prerequisite and receive fresh
|
|
exact-head CI, security and reviews. No full local repository-suite, live provider,
|
|
external alarm delivery, measured instance/database suspension or fleet-cost claim
|
|
is made. Existing Cloud alarm coverage and timer sleep-safety gates remain intact.
|