Files
PaperClipAI/server
DottaandPaperclip 0d57b9b98e refactor(heartbeat): extract run preparation (#15591)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The heartbeat service prepares context and configuration before
agent runs.
> - The workspace extraction left the main file at about 27,000 lines.
> - Preparation helpers form another large boundary with explicit inputs
and one database dependency.
> - This pull request moves those helpers into one module and preserves
their callers.
> - The benefit is a 24,000-line orchestration file and one place to
maintain run preparation.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

It improves the structure and test coverage of heartbeat run
preparation.

**Current behavior**

`server/src/services/heartbeat.ts` mixes context and configuration
preparation with execution, queueing, and recovery in 27,048 lines.

**Proposed behavior**

Move preparation into
`server/src/services/heartbeat/run-preparation.ts`. The main file loses
3,048 lines and ends at 24,000 lines. Keep the existing imports and
behavior. This continues #15568, #15573, and #15578.

**Reason and benefit**

A larger domain extraction makes useful progress toward a few manageable
files. Context, identity, environment, and tool preparation can be
reviewed together without searching the run engine.

**Breaking changes**

None. Existing public helpers and the configuration-incomplete error
class retain their identity.

## What Changed

- Move wake payloads, comments, attachments, skill mentions, adapter
environment configuration, and MCP/tool setup into
`heartbeat/run-preparation.ts`.
- Bind issue context, pinned routine snapshots, organization rows, and
responsible-user resolution through `createHeartbeatRunPreparation(db)`.
- Keep factory construction free of database work. Keep queueing,
dispatch, retries, cancellation, and execution order in `heartbeat.ts`.
- Preserve all 140 existing exports. A syntax-tree comparison confirms
that the 47 extracted function bodies and 25 moved declarations are
unchanged. The private string normalizer is also unchanged.
- Add six tests for legacy export identity, independent database
binding, company scope, pinned routine configuration, wake author
selection, company-owner fallback, and credential preflight.
- Document the preparation boundary in `doc/DEVELOPING.md`.

## Verification

- Passed: 183 focused tests across ten suites, including the six new
tests. Command: `pnpm exec vitest run
server/src/services/heartbeat/run-preparation.test.ts
server/src/__tests__/heartbeat-context-summary.test.ts
server/src/__tests__/heartbeat-agent-session-message.test.ts
server/src/__tests__/heartbeat-runtime-mcp-servers.test.ts
server/src/__tests__/heartbeat-runtime-skills.test.ts
server/src/__tests__/heartbeat-project-env.test.ts
server/src/__tests__/heartbeat-responsible-user-invariant.test.ts
server/src/__tests__/heartbeat-comment-wake-batching.test.ts
server/src/__tests__/run-secret-redaction.test.ts
server/src/__tests__/low-trust-red-team-routes.test.ts`.
- One runtime-skills case reported a Postgres deadlock in its after-test
`TRUNCATE` cleanup. A focused rerun passed both runtime-skills cases.
The other nine suites passed in the combined run.
- Passed: `pnpm -r typecheck`.
- Passed: `pnpm build`.
- Passed: all 54 GitHub checks on
`8d0cb9a251b10be5735fa13035e8a31e50b132e7`. The two optional Storybook
jobs were skipped. The complete CI test matrix is green.
- The full local `pnpm test:run` was started and then stopped after the
complete CI test matrix passed. It had no failures reported before
stopping and did not complete locally.
- Greptile: 5/5 on the same commit, with no inline comments or
actionable findings.

## Risks

The extraction crosses context, identity, credential, and tool-access
module boundaries. Existing company filters, secret redaction, low-trust
rules, and error classes stay intact. The new factory captures only the
service database and does no database work during construction. Existing
integration tests cover responsible-user authority, credential
boundaries, MCP tokens, and quarantine. New tests cover the factory
wiring and legacy identity. A syntax-tree comparison confirms that
orchestration changed only to bind the extracted loaders.

## 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
- [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-08 13:47:29 -05:00
..