Skip to content

A child interrupted by a host daemon disconnect never notifies its parent #3956

Description

@pixexid

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 interruptActiveThreadsForHostinterruptActiveThreads 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.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions