Skip to content

fix(server): wire summary-slots (and friends) to the plugin worker manager, guard the class - #308

Open
stubbi wants to merge 1 commit into
mainfrom
fix/plugin-worker-wiring-remaining
Open

fix(server): wire summary-slots (and friends) to the plugin worker manager, guard the class#308
stubbi wants to merge 1 commit into
mainfrom
fix/plugin-worker-wiring-remaining

Conversation

@stubbi

@stubbi stubbi commented Jul 26, 2026

Copy link
Copy Markdown

Follow-up to #304. Upstream: paperclipai#10262.

It recurred

#304 fixed three routes. Runs kept failing after it rolled:

07-26 10:20:38  e21b422f  Go for it  setup_failed  source: summary-slot.generate
07-26 10:20:39  7d6fe0c8  Go for it  setup_failed  source: issue.continuation_recovery
07-26 10:20:40  fa3e4ecc  Go for it  setup_failed  source: issue_recovery_action

They carry the NEW message from #304's second commit, Sandbox provider "kubernetes" is installed, but sandbox plugin workers are unavailable in this server process, which named the cause immediately instead of blaming a healthy worker. That part worked.

Why the first sweep missed it

I enumerated affected routes by grepping for heartbeat.wakeup(. But a route can also dispatch by handing its heartbeat to a helper:

await queueIssueAssignmentWakeup({ heartbeat, ..., contextSource: "summary-slot.generate" });

summarySlotRoutes does exactly that and is mounted as summarySlotRoutes(db), so it built an unwired heartbeat and every run it dispatched failed setup. The two recovery runs are the in-process reaction to that first failure.

Hand enumeration missed this twice. So this PR stops enumerating.

Changes

  • summarySlotRoutes accepts and forwards pluginWorkerManager. This is the production bug.
  • activityRoutes and instanceSettingsRoutes are wired too. Neither dispatches today (getRun, reconcileIssueGraphLiveness, verified no run creation), but wiring them makes the rule uniform so it cannot rot as those routes grow.
  • New route-plugin-worker-wiring-guard.test.ts enforces both halves statically: no route builds a heartbeat service without the manager, and app.ts hands one to every route that accepts it.

The guard is not theoretical. Run against the tree as it stood, it immediately flagged four offenders, including status-cards.ts, which I had not found by hand. status-cards is upstream-only and not mounted in this fork, so it is fixed in the upstream PR and omitted here.

Verification

  • pnpm typecheck clean.
  • 68 tests pass across the guard, the wiring pins, and the summary-slot, activity and instance-settings suites.
  • The guard fails on the pre-fix tree with exactly the offender list above.

Risk

Low. All options parameters default to {}. Additive dependency threading plus a static test.

Enumerating the affected routes by hand missed two of them, and one recurred in
production after the first fix landed. A route can dispatch a run either by
calling heartbeat.wakeup() directly or by handing its heartbeat to a helper such
as queueIssueAssignmentWakeup, and only the first is greppable, so summary-slots
and status-cards were both missed.

- summary-slots and status-cards dispatch via queueIssueAssignmentWakeup and
  were producing failed runs in production.
- activity and instance-settings do not dispatch today, but are wired too so the
  rule is uniform and cannot rot as those routes grow.
- A guard test enforces both halves: no route builds a heartbeat service without
  the manager, and app.ts hands one to every route that accepts it. This is what
  found status-cards.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant