Files
hermes-agent/tools
Brooklyn Nicholson 79e222d869 fix(delegation): collect a child's real result in a grace window after the stale-heartbeat verdict
When the heartbeat's stale verdict fires (frozen activity fingerprint for the
threshold cycles), await_child used to raise FuturesTimeoutError the moment the
wait ended if the worker future had not resolved — even though a child that
already wrote its final answer often lands its recorded result moments later
while its worker thread unwinds. That synthesized timeout abandoned the real
result: in an async batch the never-collected child left the unit unable to
satisfy 'all children terminal', so the batch stayed running for hours with no
completion delivered (#113222; the reporter's 7h hang, and the follow-up traces
where a task's final line sat uncollected for 23+ minutes).

Collect instead of abandoning: after the stale verdict, signal the cooperative
stop, then wait up to _STALE_RESULT_GRACE_SECONDS (2s) for the worker to
settle. The real result wins when it lands (status completed, real summary);
only a worker that never settles falls through to the existing structured
timeout with the deferred-close drain. A configured inactivity cap
(child_timeout_seconds) keeps its teeth — it is NOT a stale verdict, so the
#116001 abandonment contract is unchanged (pinned by test_delegate_liveness_timeout).
Steering stays open across the window: the success path's _merge_late_steer (or
the failure path) remains the linearization boundary, so a concurrent steer is
never lost.
2026-10-05 16:40:38 -05:00
..