Skip to content

Session lock is published under the shared Claude worker-pool daemon pid, so a sibling session in the same pool passes ownership #2883

Description

@karotkriss

Summary

When a firstmate session acquires the session lock through Claude Code's reparented per-user worker pool, bin/fm-lock.sh publishes the shared pool daemon's pid rather than the acquiring session's own pid. Because ownership is decided by ancestry membership, any other Claude session served by that same per-user pool then passes the ownership test for that home. That breaks the exclusivity the lock exists to provide.

This is pre-existing behaviour on unmodified main. Filing it separately because it was surfaced during review of #2839, which is deliberately scoped to a different mechanism and is inert on this path.

Reproduction path

  1. state/.lock names a prior session that is now dead.
  2. A new session starts with CLAUDE_PID=700, but its tool call is served from the pool, so its ancestry reaches only 320 -> 310 -> 300 -> 1 and never reaches 700.
  3. me=$(fm_harness_ancestry_pid) therefore resolves to the shared pool daemon pid 300.
  4. The stale lock is replaced and publication records 300.
  5. A different Claude session served by that same per-user pool also has 300 in its ancestry, so fm_session_lock_owned_by_self returns true for it against the same home.

Evidence that this is pre-existing

On unmodified main:

  • bin/fm-lock.sh line 36 already derives the published identity purely from ancestry:
    me=$(fm_harness_ancestry_pid) || { echo "error: cannot locate harness process in ancestry" >&2; exit 1; }
    Under a pool reparented to init, that is the daemon pid, so main publishes 300 in this scenario.
  • bin/fm-session-lock-lib.sh fm_session_lock_owned_by_self already decides ownership by ancestry membership of the recorded pid, so a sibling session under the same pool already satisfies it.

Both halves of the defect are therefore present without any change applied.

Impact

Two live sessions sharing one per-user worker pool can each believe they own the same home's session lock. Lock-gated operations - spawn, steer, merge, wake drain, supervision repair - are then no longer serialized between them.

Suggested direction

The identity that gets written needs resolving at the shared acquisition boundary, after the existing lock has been proven free or stale, rather than being taken from an ancestry walk that cannot see past the pool. Any fix should preserve the existing rule that a published session id alone must never let a caller claim a lock a different live session currently holds.

Note that #2437 already proposes a durable session-identity binding recorded at acquisition, which addresses this class of problem directly; this issue may be best resolved as part of whichever ownership mechanism the maintainers select.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ready-for-prTriage: real bug or VISION-aligned feature, open for a PR

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions