[#992] Auto-respawn agents on server restart when a batch is active#995
Conversation
#972's clean shutdown kills agent PTYs on pm2/local restart, but nothing respawned them — a project mid-batch silently stalled (no dev progress, no reviews) until an operator manually restarted head/dev/re1/re2. This bit every v2.5.x upgrade. respawnActiveBatchAgents(cfg) runs once in the startup callback (after auto-reseed, before the watchdog) and, for each project with an ACTIVE batch, respawns its agents via the existing spawnAgentPty. "Active batch" comes from the same getOrComputeBatchProgress + isBatchActiveFromProgress source the /api/batch-active route and the auto-reseed gate use — no new batch/pulse logic. Safety: - Idle projects are LEFT ALONE (still spawn on demand); fail-SAFE — an unknowable batch state (throw or null) never spawns, so an idle project is never disturbed. Opt out via config.json restart_respawn.enabled:false. - #972 clean shutdown untouched; spawn internals untouched (orchestration only). - Idempotent: an already-live agent is not double-spawned. Never blocks boot. - Logs which projects/agents were restored. Dependency-injected (getProgress/isActiveFromProgress/spawnAgentPty/ agentSessions/log) like autoReseedOnStartup so tests need neither gh nor a pty. New server/restartRespawn.test.js: active→4 restored, idle→none, unknown/throw→ none, opt-out→none, idempotent-skip, failing-spawn logged, malformed projects. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Epic Alignment: PASS
Advances #967's reliability goal and #992's contract: restore active-batch agents after restart while leaving idle projects alone and keeping #972 shutdown/spawn internals unchanged.
Checked (evidence)
- Structural gate: PR body contains filled
## EPIC Alignment,## Self-Verification, and non-UI## Deviations. - Issue/epic context:
gh issue view 992+gh api repos/realproject7/quadwork/issues/967; #992 requires active-batch respawn only and no batch/pulse or #972 shutdown changes. - Live diff scope:
gh api repos/realproject7/quadwork/pulls/995/files→ exactlyserver/index.js(+88) andserver/restartRespawn.test.js(+161). - Active source reuse:
server/index.js:2254usesroutes.getOrComputeBatchProgress;server/index.js:2255usesroutes.isBatchActiveFromProgress, matchingserver/routes.js:3411and/api/batch-activeatserver/routes.js:3431. - Idle/unknown fail-safe:
server/index.js:2269-2280; tests cover complete, cleared, null, and throwing progress atserver/restartRespawn.test.js:67,:79,:89,:100. - Active restore/idempotence/logged failures:
server/index.js:2286-2307; tests cover four-agent restore atserver/restartRespawn.test.js:51, live-session skip at:122, and failed spawn logging at:135. - Startup placement:
server/index.js:2376runs after auto-reseed and before watchdog; no shutdown path changed. - Riskiest part of this diff: startup orchestration can accidentally wake idle projects; acceptable because unknown/complete/cleared states all skip and the predicate is the existing lifecycle signal.
- Kill-list: scanned all items — clean.
- CI:
gh pr checks 995→test pass 49s.
Findings
- None.
Decision
Approve at 38c78780503237b14674500fb1376d018d75cc35. The implementation is narrow startup orchestration around existing batch-state and spawn primitives, with focused tests for the idle-project and unknown-state regressions that would violate #992.
|
@re2 APPROVE at Scope is exactly the two files claimed: Checked (evidence)
No blocking issues. Standing no-merge rule — operator merges. |
Closes #992
Summary
shutdown()(#972) now kills agent PTYs cleanly onpm2 restart/ local restart, but nothing respawned them on startup — so a project driving an active batch stayedmissing(no dev progress, no reviews) until an operator manuallyagent_control started each of head/dev/re1/re2. This bit the v2.5.0/v2.5.1 upgrades repeatedly.New
respawnActiveBatchAgents(cfg)runs once in theserver.listenstartup callback (after auto-reseed, before the watchdog starts). For each project with an active batch it respawns the project's agents via the existingspawnAgentPty. Idle projects are left alone — agents still spawn on demand (terminal-connect / batch-start) exactly as before."Active batch" is determined from the same source
/api/batch-activeand the auto-reseed gate already use:getOrComputeBatchProgress(projectId)→isBatchActiveFromProgress(progress). No new batch/pulse logic.Fail-SAFE direction (inverse of auto-reseed's fail-CLOSED gate): if the batch state can't be proven active — the check throws, or returns
null(no repo / no progress payload) — we do nothing and never spawn, so an idle project is never disturbed. Opt out viaconfig.jsonrestart_respawn: { enabled: false }(defaults on).Dependency-injected (
getProgress/isActiveFromProgress/spawnAgentPty/agentSessions/log), exactly likeautoReseedOnStartup, so tests need neitherghnor a real pty.EPIC Alignment
EPIC #967 — folded / isolated (per the batch brief); isolated to startup logic. Enables restart-safe overnight runs so upgrades don't strand in-flight batches. Respects the epic's stated contracts: do NOT auto-spawn idle projects; keep #972's clean shutdown; do not change the batch/pulse model.
Self-Verification
Adversarial diff re-read — the only production change is one new
asyncfunction + one guarded call in the startup callback + one export.shutdown(), the spawn internals, and the batch/pulse model are untouched.Kill-list / safety invariants:
!active(false OR null) →skip; test 2/2b assert an idle/cleared project spawns 0 agents.getProgressthrowing or returningnull→ 0 spawns (tests 3, 4).shutdown().getOrComputeBatchProgress/isBatchActiveFromProgressand callsspawnAgentPtyas-is (orchestration only).isPtyAlive(term)) is skipped, not double-spawned (test 6).Build + test evidence:
npm test→ 64 passed, 0 failed, 2 skipped (pre-existing Jest-style skips).server/restartRespawn.test.js(8 cases): active→4 restored (suppressed lifecycle msg); idle-complete→none; cleared-active→none; unknown(null)→none; throw→none; opt-out→none; idempotent-skip of a live agent; failing-spawn logged & not counted restored; malformed projects ignored. Drives the realisBatchActiveFromProgressvia crafted progress payloads.node server/pack-smoke.test.js→ PASS.QUADWORK_SKIP_LISTEN=1 node -e "require('./server/index.js')"→ loads OK.Acceptance criteria (1:1):
[respawn] <project>: active batch — restored agents: …)Deviations
running_agentsmarker — the issue offered either; the brief directed the batch-progress/active-batch source, which needs no change toshutdown()and reuses the exact predicate the rest of the system trusts.project.agentskeys (falling back to the canonical head/re1/re2/dev) so legacy worktree layouts are covered;spawnAgentPtyreturns{ok:false}for an unknown agent, so a stray key can't crash boot.