Files
PaperClipAI/server
DottaandPaperclip 65965f0d3a refactor(heartbeat): extract scheduling and monitor recovery (#15717)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The heartbeat service schedules and runs agent work.
> - Previous refactors moved queue admission, recovery, and run control
into separate modules.
> - Timer ticks, issue monitors, and session-goal recovery still occupy
more than 1,100 lines in the main service.
> - This pull request moves that scheduling code into one module with
explicit dependencies.
> - This makes scheduling easier to review and test before we split the
execution pipeline.

## Linked Issues or Issue Description

Refs #15685, the merged run-control extraction. A search found no
duplicate open scheduling extraction.

**What existing behavior does this improve?**

Maintenance and testing of heartbeat scheduling.

**Current behavior**

The main heartbeat service contains 10,473 lines. Scheduling shares a
file with the execution pipeline.

**Proposed behavior**

Move scheduling into `server/src/services/heartbeat/scheduling.ts`. Keep
the same public methods, admission rules, database claims, and execution
behavior. The main file falls to 9,330 lines.

**Reason and benefit**

Give timer ticks, issue-monitor recovery, and session-goal recovery one
clear module boundary.

**Breaking changes**

None intended. Existing heartbeat exports and method signatures remain
available.

## What Changed

- Add `createHeartbeatScheduling` with 11 explicit dependencies.
Construction does not query the database or start timers.
- Move timer ticks, issue-monitor dispatch and recovery, and
session-goal recovery into the new module.
- Keep queue admission, heartbeat policy parsing, and atomic timer
claims in `queue.ts`.
- Share the unchanged goal-control parser and issue-link formatter with
their existing callers.
- Add 20 focused tests. Document the module boundary in
`doc/DEVELOPING.md`.

## Verification

- 122 tests pass across 11 files: 20 new scheduler tests, 52 existing
scheduling integration tests, and 50 adjacent queue, goal,
native-selection, and task-drain tests.
- New database tests cover cross-instance monitor claims, replacement
schedules, deferred and failed admission, native ownership, archived
companies, timer claim loss, worktree cutoffs, agent eligibility, goal
recovery keys, pending-action deduplication, and invalid-action
persistence.
- AST comparison confirms 20 unchanged function bodies, unchanged
remaining service statements, unchanged `executeRun`, and all 140 public
exports preserved.
- Workspace typecheck (`pnpm -r typecheck`) and build (`pnpm build`)
pass. Greptile is 5/5 on commit
`89a1a3b3aa428949eba349af5c1618391f97a19c`. All CI checks pass on that
commit. The full test suite runs in CI; local verification was targeted.
- The first CI attempt hit a 15-second timeout in the first test of
unchanged `plugin-scoped-api-routes.test.ts`. All nine tests pass
locally in 6.6 seconds. That shard passed on its one retry without code
changes.
- Run the new tests with `pnpm exec vitest run
server/src/services/heartbeat/scheduling.test.ts --maxWorkers=1`.

## Risks

- Incorrect wiring could bypass suppression, duplicate a monitor wake,
or lose a replacement schedule. Database tests cover these boundaries
and existing integration tests still call the public heartbeat service.
- This PR moves the current behavior without changing scheduling policy,
schema, provider execution, or cancellation ownership.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, code editing, and command
execution. The session does not expose the exact model variant or
context window size.

## 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 15:09:11 -05:00
..