Skip to content

Shared/co-owned broker orphaned when an owning session exits without SessionEnd #450

Description

@hongsu

Context

PR #381 keys broker cleanup on the owning session(s): broker.json records sessionIds, a reused broker adds the reusing session as a co-owner, and teardownBrokersForSession shuts a broker down only once no owner remains. This correctly stops a single session from tearing down a broker its siblings are still using.

Gap

If a co-owning session disappears without running its SessionEnd hook (SIGKILL, OOM, crash, host reboot), its sessionId stays in broker.json forever. When the other owners later exit gracefully, teardownBrokersForSession sees a remaining owner and preserves the broker — but no future hook will ever fire for the dead owner. The shared/worktree broker is orphaned indefinitely.

This is the same class as the original single-owner leak in #108, but surfaced by the new co-ownership model. Codex flagged it on the PR: #381 (comment)

Why not fix it in the SessionEnd hook

PR #381 briefly recorded a per-owner session pid (process.ppid captured at SessionStart) and pruned owners whose pid was gone. That was reverted because:

  • The packaged hooks.json runs SessionStart in shell form (no args), so node's process.ppid is the ephemeral sh wrapper, not the Claude session. The recorded pid is dead almost immediately, which made every shared-broker co-owner look dead and tore down brokers still in use (regression flagged in fix: tear down brokers by sessionId on SessionEnd to avoid orphaned worktree brokers (#380) #381 (comment)).
  • Even with a correct anchor, process.kill(pid, 0) is unsound across PID reuse, and there is no cross-platform way to map a sessionId to a live process (macOS has no /proc; exec-form hooks aren't available in the hook schema).

A hook-side liveness signal is therefore not a reliable fix.

Proposed resolution

Solve it broker-side with the idle timeout already proposed in #108: app-server-broker.mjs self-terminates after N minutes with no active client connection. This is platform-independent, needs no PID/liveness signal, and covers both the abnormal-exit orphan and the dead-co-owner orphan in one mechanism.

This issue tracks the co-owner facet specifically; it can be closed as covered once #108's idle timeout lands (they likely share one implementation).

Related

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