Skip to content

Worker exits when an expectsReply escalation times out; lane-runner does not relaunch it and registry/lane state stay 'running' #630

Description

@HenryLach

Summary

A worker's escalate_to_supervisor call with expectsReply: true blocks the worker polling for a reply keyed to the escalation id. When the wait times out with no reply, the worker session exits. Nothing relaunches it: the lane-runner does not respawn the worker, and the runtime registry and lane state stay running with frozen timestamps. Supervisor steers then fail (send_agent_message has no live process to deliver to), and the supervisor has no tool that re-engages the lane short of orch_resume — which, in the observed run, was itself blocked (see the companion issue on replacement-supervisor resume).

Observed in penster batch henrylach-20260905T210935 (TP-2047, single wave, single lane), 2026-09-06.

Sequence

  1. The worker hit a plan-gate REVISE at the round-2 cap and, per the operator's standing hold rule, entered a hard hold and escalated to the supervisor for a ruling: escalation id 1788657826267-4ee58, expectsReply: true.
  2. The supervisor session that should have replied was itself wedged on a provider-side 400 (thinking-block mismatch) and was being replaced; no reply arrived inside the worker's wait window.
  3. The worker's poll timed out and the process (pid 59452) exited. Get-Process returned nothing.
  4. .pi/runtime/<batch>/registry.json and lanes/lane-1.json remained status: running, updatedAt 1788657826268 (the moment after the escalation call), for the next 17+ minutes. batch-state.json stayed phase: executing.
  5. The replacement supervisor's send_agent_message refused (no live consumer); a manual inbox file drop was inert (no process to drain it; an escalation-wait expects a replyTo-keyed reply, not a fresh steer). orch_pause was accepted but inert (nothing running to honor it).
  6. Recovery required a manual batch-state.json phase edit plus a fresh supervisor session plus orch_resume (precedence-4 re-execute in the existing worktree). Committed work survived; the hold-time commits (hold(TP-2047):) were intact.

Why this matters

The hard hold is the operator-sanctioned posture for a cap escalation (#627). A worker that holds correctly and then dies because the ruling took longer than its poll timeout converts a governance pause into a dead lane with stale running state — the worst of both: no work, no signal, and a registry that lies about it.

Expected

  • A timed-out expectsReply escalation must not terminate the worker. Options: the worker re-enters its hold loop and re-polls (bounded by the stall limit, which the hold should suspend per Cap-ruling hold: runtime must block .DONE and merge while an operator ruling is pending (.PENDING-RULING) #627); or the lane-runner treats the exit as a hold-safe exit and preserves a held marker rather than nothing.
  • If the worker process does exit for any reason while the task is non-terminal, the lane-runner (or the engine monitor) must reconcile the registry/lane state to exited within one poll interval and surface a supervisor alert (worker-exited-while-held or similar), instead of leaving running frozen.
  • send_agent_message to a dead worker should return a distinct, actionable error naming the dead pid and last-seen timestamp, not a generic refusal.

Relation to other issues

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions