v0.2.2 — per-connection HTTP session isolation
Fixes the identity-collision gap that v0.2.1 (commit 8534c19) documented but
deliberately left unfixed: concurrent HTTP-mode connections used to share one
process-global session (sid, git context, bead_id, objective, captured
channel-push session) — whichever connection registered most recently became
who the server thought every caller was.
What changed
server.py: the module-global_Statesingleton is now a per-connection
_SessionStateregistry, keyed by FastMCP'sContext.session_id— verified
present and stable for every transport (including stdio) in the installed
fastmcp 3.4.4, not just HTTP as the prior investigation assumed.channel.py: the captured channel-push session is per-connection too, so
proactive<channel>push now delivers to every connected client, not just
the first one to register.- Idle HTTP-mode connections (no tool call for 15 minutes) are reaped instead
of heartbeating forever, reusing the existing stale/reap conventions. - stdio mode is unaffected — a stdio process still gets exactly one tracked
connection for its whole life, and the full pre-existing stdio test suite
passes unchanged. - New tests cover concurrent-connection isolation (two live in-process MCP
clients never leak sid/bead_id/objective/status into each other) and the
idle-reap cleanup path.
Residual limitation
Idle-connection cleanup is time-based (no client tool call for 15 minutes),
not a true transport-liveness check — a connection that stays open but is
genuinely idle that long is indistinguishable from an abandoned one and gets
reaped too. Documented in README.md and docs/DESIGN.md. This only applies
under MAILBOX_TRANSPORT=http; stdio is never affected.
Full diff: v0.2.1...v0.2.2