Category: bug Severity: major
Location: src/Arcp.Runtime/SessionState.Outbound.cs:95-101
What
The subscriber's _outbound channel is bounded (SessionState.cs:66, FullMode.Wait). TryWrite returns false and silently discards the event when the channel is full, yet the event has already consumed an event_seq via EventLog.Append. The owner path uses awaited SendAsync (back-pressure-aware); the subscriber path does not. A slow subscriber therefore observes a gap in event_seq, which §8.3 says must be gap-free.
Evidence
internal void EmitToSubscriber(Envelope env, CancellationToken cancellationToken)
{
// Re-stamp for the subscriber's session_id and event_seq.
var rekeyed = RedactCredentialSecretsForSubscriber(env) with { SessionId = SessionId.Value };
var stamped = EventLog.Append(rekeyed);
_outbound.Writer.TryWrite(stamped);
}
Proposed fix
Await a bounded WriteAsync (with the subscriber's cancellation) instead of fire-and-forget TryWrite, or detect the dropped write and tear the subscription down deterministically rather than leaving a silent gap.
Acceptance criteria
Category: bug Severity: major
Location:
src/Arcp.Runtime/SessionState.Outbound.cs:95-101What
The subscriber's _outbound channel is bounded (SessionState.cs:66, FullMode.Wait). TryWrite returns false and silently discards the event when the channel is full, yet the event has already consumed an event_seq via EventLog.Append. The owner path uses awaited SendAsync (back-pressure-aware); the subscriber path does not. A slow subscriber therefore observes a gap in event_seq, which §8.3 says must be gap-free.
Evidence
Proposed fix
Await a bounded WriteAsync (with the subscriber's cancellation) instead of fire-and-forget TryWrite, or detect the dropped write and tear the subscription down deterministically rather than leaving a silent gap.
Acceptance criteria