Soul's fix batch, findings 3, 4, 5 and 6:
- A peer-initiated CONNECTION_SHUTDOWN only set `disposed = true`, so
`dispose()`'s early-return skipped `release()` and `offConnectionEvent`: the
runtime reference leaked forever and a pending `receiveFrame` hung forever.
`disposed` and "already cleaned up" are now the same flag, run through one
`cleanup()` reached from `dispose()`, a peer shutdown, and the new `fault()`
(wired from fix 1's `onFault`). Every path releases the runtime reference
exactly once and rejects every pending and future receive/send with the real
reason instead of a generic "disposed" message.
- `CultMeshQuicRealtimeSessionManager.connect()` cached a transport forever,
even a dead one, and raced candidates with `Promise.allSettled`, so one dead
route delayed every connect by the full handshake timeout. It now: evicts a
session once its transport reports itself disposed (`onDisposed`, mirroring
`CultMeshSessions.InvalidateRealtimeSession`); shares one in-flight dial
across concurrent callers for the same target (mirrors C#'s `Lazy`); refuses
a transport that lands after `dispose()` instead of caching it; and races
each tier for the first verified success (mirrors `Task.WhenAny`), aborting
and disposing the losers via an `AbortSignal` threaded into
`connector.connect`, which now also honours a signal already aborted at
entry.
- `streamKinds` never pruned a finished stream (`STREAM_SHUTDOWN` fell into
`default`); it now removes the entry. The receive side queued every
latest-only frame in an unbounded array; it now mirrors the C# reference's
`CultMeshRealtimeInbox`: at most one pending frame per `(channel, body)`,
coalesced on arrival, while reliable-ordered frames still queue directly
(unbounded, matching the C# reference's own unbounded channel).
Subtraction: `trimmedOrEmpty` duplicated `cultnet-ts`'s exported `trimCSharp`;
callers now use the shared one.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>