Skip to content

Background service: daemonize the worker + durable pause/resume switch - #13

Merged
edgehero merged 6 commits into
mainfrom
plan-exec/2026-07-17-worker-daemon-pause-resume
Jul 18, 2026
Merged

Background service: daemonize the worker + durable pause/resume switch#13
edgehero merged 6 commits into
mainfrom
plan-exec/2026-07-17-worker-daemon-pause-resume

Conversation

@edgehero

Copy link
Copy Markdown
Owner

Daemonizes the worker across Linux/macOS/Windows and adds a durable pause/resume switch built on BullMQ's own queue.pause() (a durable Valkey key), exposed as pi-dispatch pause|resume|status. "Off" stops the worker draining without rejecting enqueues, and the paused state survives a worker restart — verified live. Also hardens the daemon path: a Windows SIGBREAK handler, a boot-time reaper for orphaned pi-job-* containers, a config-error exit-code fix so systemd can't hot-loop, and cross-platform service artifacts (systemd unit, launchd plist, nssm installer) with secrets sourced from .env at runtime (never committed).

What's in it

  • CLI (worker/src/cli.mjs): pause/resume/status on queue.pause()/resume()/isPaused() (reads only VALKEY_URL, failFast); entryExitCode() + .catch() maps a config error to exit 2 (non-retryable).
  • Worker robustness (index.mjs, start.mjs): win32 SIGBREAK → existing graceful shutdown; injectable boot reaper (docker ps/rm -f of strays, never throws, awaited before draining); reason on the completion log so a restart-dropped job is visible.
  • Deploy (deploy/): launchd plist + POSIX/Windows .env wrappers + nssm installer; worker.service gains StartLimit* + RestartPreventExitStatus=2 and is relabeled TEMPLATE; new deploy-lint.yml CI (xmllint/sh -n/systemd-analyze).
  • Docs/specs: README "Run as a service" runbook (drain-before-restart); design.md "untested examples" note graduated; DES-CONCURRENCY-3 records the single-worker-per-daemon reaper invariant; REQ-LOCAL-JOB-VISIBILITY note amended for the daemon's persistent captured log.

Verification

Check Baseline Final
typecheck/lint/test tests: 0 fail (257 pass) 261 pass / 0 fail / 9 skip
E2E — Queue Verification N/A PASS — 3 pause-durability tests ran live vs Valkey (enqueue-while-paused / resume-drains / pause-survives-restart / policy-not-retried)
E2E — Container Smoke N/A N/A (job image unchanged)
E2E — Webhook N/A N/A (receiver/src untouched)

Closes #6

🤖 Generated with Claude Code

edgehero and others added 6 commits July 17, 2026 22:27
…e 1: CLI control surface" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
…e 2: Worker robustness" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
…e 3: Deploy artifacts" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
…e 4: Docs + spec lockstep" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
…worker-daemon-pause-resume

# Conflicts:
#	README.md
#	worker/src/start.mjs
#	worker/test/start-wiring.test.mjs
…er PI_DISPATCH_REQUIRE_WORKER_TESTS

A VALKEY_TEST_URL-only skip let the durability tests silently no-op in CI if the
env var were ever dropped -- "skipped = unverified". Match cron.integration: throw
when required-but-unconfigured so the pause-survives-restart guarantee cannot pass green while unrun.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
@edgehero
edgehero merged commit d6c5fce into main Jul 18, 2026
9 checks passed
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.

Background service: daemonize the worker + pause/resume switch

1 participant