Problem
When a host daemon disconnects, every active thread on that host is interrupted and lands in error. A parent-notifiable child does this silently: its parent receives no child outcome or attention signal, so a parent correctly waiting on native delivery can wait forever.
Observed on BB 0.43.3: two drops in about 35 minutes took six child threads across two parents and three unrelated providers. One parent's three children sat errored for about 25 minutes while the parent remained active and unaware. All six continued cleanly with bb thread retry; the outage is not this defect, the missing parent delivery is.
A measured consequence was a brief duplicate-owner collision: an authorized sibling manager retried errored children before its notice cleared a saturated dispatch queue, while the owning manager had independently observed the errors and created fresh recovery threads against the preserved worktrees. The duplicates were stopped before mutation, but native delivery should have woken the owning parent first.
Existing path that is bypassed
child-interrupted already exists:
childOutcomeSystemMessageKind maps interrupted to it;
buildSingleChildThreadTurnStatusSegments renders it;
applyEventEffects raises a parent follow-up for turn/completed status=interrupted.
The daemon-disconnect path instead reaches interruptActiveThreadsForHost → interruptActiveThreads in src/services/threads/thread-lifecycle.ts. It writes turn/completed status=interrupted, system/error, and system/thread/interrupted directly, applies run.failed, and notifies only the child's own hub channel. It never queues the existing parent child-outcome notification.
The sibling provisioning-failure path settleThreadCommandFailure does queue queueChildThreadTurnNotificationBestEffort, so this appears to be an omission rather than a design choice.
Classification already exists
systemThreadInterruptedEventDataSchema records:
reason: manual-stop | host-daemon-restarted | provider-turn-idle
- optional
cause: host-connection-lost
The runtime already queries this data. The reason simply does not reach the parent.
Not requesting automatic retry
Please do not retry automatically. A turn may have mutated state before transport loss, and reconcileDaemonReportedThreads already deliberately declines to revive a host-daemon-restarted interruption. Parent delivery is sufficient; the parent can decide whether bb thread retry is safe.
Acceptance
- A parent-notifiable child interrupted by host daemon loss delivers one terminal notification through the existing batched child-outcome path.
- The notification carries the machine-readable interruption
reason and optional cause.
manual-stop still does not notify the parent.
- Nothing retries automatically and existing restart-revival suppression remains unchanged.
- A child already notified through command-failure handling is not notified twice for the same turn.
One discriminating test: interrupt child A with host-daemon-restarted and child B with manual-stop, flush the parent notification batch, and assert exactly one child-interrupted message naming A with its reason.
Problem
When a host daemon disconnects, every active thread on that host is interrupted and lands in
error. A parent-notifiable child does this silently: its parent receives no child outcome or attention signal, so a parent correctly waiting on native delivery can wait forever.Observed on BB 0.43.3: two drops in about 35 minutes took six child threads across two parents and three unrelated providers. One parent's three children sat errored for about 25 minutes while the parent remained active and unaware. All six continued cleanly with
bb thread retry; the outage is not this defect, the missing parent delivery is.A measured consequence was a brief duplicate-owner collision: an authorized sibling manager retried errored children before its notice cleared a saturated dispatch queue, while the owning manager had independently observed the errors and created fresh recovery threads against the preserved worktrees. The duplicates were stopped before mutation, but native delivery should have woken the owning parent first.
Existing path that is bypassed
child-interruptedalready exists:childOutcomeSystemMessageKindmapsinterruptedto it;buildSingleChildThreadTurnStatusSegmentsrenders it;applyEventEffectsraises a parent follow-up forturn/completed status=interrupted.The daemon-disconnect path instead reaches
interruptActiveThreadsForHost→interruptActiveThreadsinsrc/services/threads/thread-lifecycle.ts. It writesturn/completed status=interrupted,system/error, andsystem/thread/interrupteddirectly, appliesrun.failed, and notifies only the child's own hub channel. It never queues the existing parent child-outcome notification.The sibling provisioning-failure path
settleThreadCommandFailuredoes queuequeueChildThreadTurnNotificationBestEffort, so this appears to be an omission rather than a design choice.Classification already exists
systemThreadInterruptedEventDataSchemarecords:reason:manual-stop | host-daemon-restarted | provider-turn-idlecause:host-connection-lostThe runtime already queries this data. The reason simply does not reach the parent.
Not requesting automatic retry
Please do not retry automatically. A turn may have mutated state before transport loss, and
reconcileDaemonReportedThreadsalready deliberately declines to revive ahost-daemon-restartedinterruption. Parent delivery is sufficient; the parent can decide whetherbb thread retryis safe.Acceptance
reasonand optionalcause.manual-stopstill does not notify the parent.One discriminating test: interrupt child A with
host-daemon-restartedand child B withmanual-stop, flush the parent notification batch, and assert exactly onechild-interruptedmessage naming A with its reason.