Files
PaperClipAI/server
DottaandPaperclip 4fe45bf8fe refactor(heartbeat): extract run retrieval and session state (#15601)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Heartbeat runs retain task sessions, usage totals, and bounded run
data.
> - After the preparation extraction, the main service still has over
24,000 lines.
> - Run reads and session operations share one database and have no
dispatch dependencies.
> - This pull request moves that group into one module and preserves its
callers.
> - The benefit is a smaller run engine and one place to maintain saved
run and session state.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

It improves the structure and test coverage of heartbeat run retrieval
and session state.

**Current behavior**

`server/src/services/heartbeat.ts` has 24,033 lines on the merged base.
It mixes run projections, session queries, resume rules, compaction, and
usage helpers with execution and recovery.

**Proposed behavior**

Move that group into `server/src/services/heartbeat/run-state.ts`. The
main file loses 1,770 lines and ends at 22,263 lines. Keep current
behavior, public exports, and service methods. This continues #15568,
#15573, #15578, and #15591.

**Reason and benefit**

One domain extraction makes useful progress toward a few manageable
files. Run reads and saved session state can be reviewed together
without searching the run engine. A search found no duplicate open
extraction. Related session changes include #15142, #14661, and #14670;
this PR does not apply their proposed behavior changes.

**Breaking changes**

None. Existing public helpers retain their identity. Service method
names and results stay the same.

## What Changed

- Move bounded run projections, encoding checks, and run/event reads
into `heartbeat/run-state.ts`.
- Move task session persistence, explicit resumes, reset rules,
compaction, and usage/billing helpers into the same module.
- Bind database operations through `createHeartbeatRunState(db)`.
Construction does no database work. The encoding cache stays local to
each service instance.
- Keep execution order, status transitions, session-goal recovery, cost
accounting writes, dispatch, and cancellation in `heartbeat.ts`.
- Preserve all 140 exports. A syntax-tree comparison confirms that 43
moved function bodies, 22 declarations, and nine service method bodies
are unchanged. The copied private string helper is unchanged.
Surrounding orchestration is unchanged apart from the factory binding.
- Add eight tests for legacy export identity, database binding, encoding
cache isolation, scoped resumes, cumulative usage, compaction, and stale
or cancelled conversation session writes.
- Document the module boundary in `doc/DEVELOPING.md`.

## Verification

- Before and after extraction, eight existing suites pass: 235 tests.
They cover workspace/session helpers, runtime state, cost accounting,
ledger attribution, task reset, timer reset, run lists, and run privacy.
- The new module suite passes: eight tests, including five real
PostgreSQL cases. Total focused coverage: 243 tests across nine suites.
- Commands: `pnpm exec vitest run
server/src/services/heartbeat/run-state.test.ts` and `pnpm exec vitest
run server/src/__tests__/heartbeat-runtime-state.test.ts
server/src/__tests__/heartbeat-cost-accounting.test.ts
server/src/__tests__/heartbeat-ledger-billing-code.test.ts
server/src/__tests__/heartbeat-task-session-reset.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts
server/src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts
server/src/__tests__/heartbeat-list.test.ts
server/src/__tests__/heartbeat-run-privacy-routes.test.ts`.
- `pnpm --filter @paperclipai/server typecheck` passes.
- `pnpm -r typecheck` and `pnpm build` pass.
- The full local `pnpm test:run` did not complete and was stopped with
SIGINT after it reported an OAuth scope health-check failure and a
workspace-runtime timeout. All six OAuth variants and the workspace case
pass in isolation. A single rerun of the 413-case tool-access suite
passed the OAuth case but hit a different Notion callback timeout; that
Notion case also passes in isolation. The full local suite is not
claimed as passing.
- Greptile is 5/5 with no findings or inline comments on head
`2ca0908cfc112dd6ae26e2565cb3611b5bec52ba`. Its exact-head check
completed successfully.
- CI remains blocked on hosted-runner assignment after more than ten
minutes. [`ci / Select trusted
runner`](https://github.com/paperclipai/paperclip/actions/runs/37828454507/job/113487287178)
is queued for `ubuntu-latest` with no runner assigned. All seven
completed review/security checks pass; two optional Storybook checks are
skipped. The full CI matrix has not started. It must complete before
merge.

## Risks

- Wiring errors could bind reads to the wrong database or share the
encoding cache. Direct factory tests cover database and cache isolation.
Real PostgreSQL suites cover query and transaction behavior.
- Session writes must retain their issue row locks and generation
fences. The original function bodies are unchanged. New database tests
reject stale and cancelled conversation writes and clears.
- Existing SQL_ASCII safeguards and bounded projections remain in place.
No schema, API contract, migration, lockfile, or workflow changes are
included.

## Model Used

OpenAI GPT-6 via Codex. The exact serving model ID and context window
were not exposed in this session. Used reasoning, repository inspection,
shell tools, and code execution.

## 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
- [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-08 14:53:48 -05:00
..