Found while building PR #692 (session rotation), and explicitly not fixed there — it predates that change and is independent of it.
The hazard
The bridge can have two requests in flight at once (PERMITS is 2), and its single-flight collapsing keys on the full transcript, so it only merges byte-identical requests. Two different requests that resolve to the same session title — pet's periodic tick and a manual poke, say — are two distinct keys, so both proceed.
Both then run claude --resume <title> against the same on-disk session, concurrently.
Nothing in the bridge or in hive-claude serialises that. Two --resume runs appending to one session is not a case either side was designed for, and the plausible outcomes range from interleaved turns to one run's history being lost.
Why #692 did not fix it
#692 added a process-wide mutex, but deliberately scoped it to the rotation path — held across the rotation decision and the replacement session's first turn, and never taken on the healthy path. That is the right scope for what #692 was fixing: it prevents two requests from each creating a rival successor session.
Serialising all resumes on one title is a different and much heavier change — it would put a lock on the hot path of every request, which is exactly what the permit count exists to avoid. Worth doing deliberately, not as a side effect.
What to establish first
This one genuinely needs a fact before a fix:
What does claude --resume actually do when two processes resume the same session concurrently? It may already serialise internally, lock the session file, or fail one of them cleanly — in which case this is a documentation note and a typed-error path, not a defect. @kaesaecracker would know, or it is cheap to test directly with two concurrent claude --resume <title> runs against a scratch session.
Do not build a lock before answering that. A hot-path mutex added to defend against something the CLI already handles would be a real cost for no gain.
If it does need fixing
Options, roughly in increasing cost:
- Key the single-flight on the resolved session title rather than the full transcript. Cheap, and it collapses exactly the requests that would collide. Changes dedup semantics — two genuinely different prompts for one session would then share a response, which is wrong. So probably not this, but it is worth stating why not.
- A per-title async lock (a map of
Mutex keyed by title), so concurrent requests to different sessions still run in parallel and only same-title requests queue. Preserves the point of PERMITS while removing the hazard.
- Reduce
PERMITS to 1. Simplest, and wrong — it serialises unrelated plugins against each other.
Option 2 is the shape I would expect, if the answer to the question above is that it needs fixing at all.
Severity
Low, pending the answer above. Not observed in practice, and the window needs two requests to the same title to overlap. Filed because the bridge is about to run unattended against real plugins and this is the kind of thing that shows up as an inexplicably truncated conversation weeks later.
Refs #692, #667, #584.
Found while building PR #692 (session rotation), and explicitly not fixed there — it predates that change and is independent of it.
The hazard
The bridge can have two requests in flight at once (
PERMITSis 2), and its single-flight collapsing keys on the full transcript, so it only merges byte-identical requests. Two different requests that resolve to the same session title — pet's periodic tick and a manual poke, say — are two distinct keys, so both proceed.Both then run
claude --resume <title>against the same on-disk session, concurrently.Nothing in the bridge or in
hive-claudeserialises that. Two--resumeruns appending to one session is not a case either side was designed for, and the plausible outcomes range from interleaved turns to one run's history being lost.Why #692 did not fix it
#692 added a process-wide mutex, but deliberately scoped it to the rotation path — held across the rotation decision and the replacement session's first turn, and never taken on the healthy path. That is the right scope for what #692 was fixing: it prevents two requests from each creating a rival successor session.
Serialising all resumes on one title is a different and much heavier change — it would put a lock on the hot path of every request, which is exactly what the permit count exists to avoid. Worth doing deliberately, not as a side effect.
What to establish first
This one genuinely needs a fact before a fix:
What does
claude --resumeactually do when two processes resume the same session concurrently? It may already serialise internally, lock the session file, or fail one of them cleanly — in which case this is a documentation note and a typed-error path, not a defect. @kaesaecracker would know, or it is cheap to test directly with two concurrentclaude --resume <title>runs against a scratch session.Do not build a lock before answering that. A hot-path mutex added to defend against something the CLI already handles would be a real cost for no gain.
If it does need fixing
Options, roughly in increasing cost:
Mutexkeyed by title), so concurrent requests to different sessions still run in parallel and only same-title requests queue. Preserves the point ofPERMITSwhile removing the hazard.PERMITSto 1. Simplest, and wrong — it serialises unrelated plugins against each other.Option 2 is the shape I would expect, if the answer to the question above is that it needs fixing at all.
Severity
Low, pending the answer above. Not observed in practice, and the window needs two requests to the same title to overlap. Filed because the bridge is about to run unattended against real plugins and this is the kind of thing that shows up as an inexplicably truncated conversation weeks later.
Refs #692, #667, #584.