Skip to content

Wire pause resume end to end: triggers now wake paused missions - #295

Open
mcclowes wants to merge 1 commit into
mainfrom
worktree-issue-197-pause-resume-wiring
Open

Wire pause resume end to end: triggers now wake paused missions#295
mcclowes wants to merge 1 commit into
mainfrom
worktree-issue-197-pause-resume-wiring

Conversation

@mcclowes

Copy link
Copy Markdown
Owner

Fixes #197

Problem

pause resumeOn: webhook registered the endpoint, the caller POSTed to it, the server recorded the event — and nothing continued the run. PauseManager.handleWebhook and startMonitoring had zero production callers, so timeout pauses likewise had no running component watching the deadline. The resume mechanism (execute({ resumeFrom }) replaying the durable log past an already-resumed pause) was sound; the trigger that invokes it was missing.

What this adds

PauseOrchestrator (src/pause/orchestrator.ts) — the wiring between the webhook server, the pause store, and a host resume callback:

  • routes an inbound delivery to the waiting pause holding that path's trigger (registration id first, path as the restart-safe fallback)
  • polls for expired timeout pauses, with an immediate sweep at startup so pauses that expired while nothing was running resume without waiting a tick
  • recovers across restarts: re-registers webhook paths whose registrations were lost, and picks up deliveries that landed before it started
  • hands each resumed pause to resume(pause); a throwing callback is reported, never an unhandled rejection, and the pause stays recoverable via a manual resume

executeWithResume(source, config) (src/index.ts) — run a mission to completion: execute, wait for a trigger, re-execute with resumeFrom, loop. Resume runs are serialized through a queue so a webhook landing while the paused run is still unwinding can't start a concurrent run. Requires config.executionLog (replaying past a pause is log-based) and throws a clear error without it.

Supporting changes: PauseManager.handleWebhookEvent (event → pause mapping), WebhookServer.onEvent() post-construction subscription, registration/event read accessors, path on delivered events, and getPort() reporting the actual bound port so port: 0 works in tests.

The test that would have caught this

src/interpreter/executor-pause-resume-e2e.test.ts: pause a mission, deliver a real webhook over HTTP, assert the pipeline tail runs exactly once — one pause.created, one pause.resumed, one mission.completed, one stored record carrying the webhook payload — and a duplicate delivery 404s without re-running anything. Plus a timeout-driven resume with no webhook at all, and orchestrator unit tests covering live routing, early delivery, restart recovery, failing resume callbacks, and the webhook-vs-timeout race resuming exactly once.

Docs updated (docs/durability/pause.md, README, CLAUDE.md): the "you drive the resume" warning is replaced with the executeWithResume / PauseOrchestrator story; plain CLI runs still exit on pause and resume via --resume.

🤖 Generated with Claude Code

…ggers into execute({resumeFrom})

A paused mission never woke up on its own: PauseManager.handleWebhook and
startMonitoring had no production callers, so `pause resumeOn: webhook`
registered the endpoint and then nothing continued the run, and a timeout
pause had no component watching its deadline.

This adds the orchestration layer:

- PauseOrchestrator wires a webhook server and a pause store into automatic
  resumes: routes inbound deliveries to the waiting pause on that path,
  polls for expired deadlines (with a startup sweep so pauses that expired
  while nothing was running resume immediately), re-registers webhook paths
  lost to a restart, picks up deliveries that landed before it started, and
  hands each resumed pause to a host resume callback.
- executeWithResume runs a mission to completion: execute, wait for a
  trigger, re-execute with resumeFrom, repeat. Resume runs are serialized
  through a queue so a webhook landing mid-unwind can't race the paused run.
- PauseManager.handleWebhookEvent maps a delivery (registration id, path
  fallback) to the waiting pause holding that trigger.
- WebhookServer gains onEvent() subscription, registration/event read
  accessors, a path on delivered events, and getPort() now reports the
  actual bound port (so port 0 works).

The e2e tests the issue asked for: pause a mission, deliver a real webhook
over HTTP, assert the pipeline tail runs exactly once (and a duplicate
delivery 404s); plus timeout-driven resume via the poll.

Fixes #197

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
reqon Ready Ready Preview Jul 29, 2026 9:14pm

Request Review

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.

Pause resume is not wired end to end (paused missions never wake up)

1 participant