fix(GWC-28): stamp gateway envelope sequence at write, not construction

CreateEnvelope stamped Sequence with an interlocked increment when the
envelope was built, so two concurrent InvokeAsync callers could take 1
and 2 and then enqueue in the order 2, 1 — non-monotonic on the wire,
breaking gateway.md's "monotonic per sender" contract. Benign today
(neither side validates inbound sequence, old GWC-10 still open) but it
would fault healthy sessions the moment worker-side validation lands.

WriteLoopAsync now stamps immediately before _writer.WriteAsync. It is
the outbound channel's single consumer (SingleReader = true), so wire
order and stamp order are the same thing by construction and
_nextSequence drops to a plain ulong with no interlocking. This mirrors
the worker's WRK-04 fix, which the gateway half never received.

Also adds TST-28: a [Theory] pinning that GatewayHello.MaxFrameBytes
carries the configured worker-frame maximum (default + 2 MiB override).
The adoption half is asserted only in the Windows-only worker suite, so
a regression to sending 0 — "older gateway, use default" to the worker —
would silently downgrade the negotiated IPC-02 limit with every CI test
still green. Mutation-checked (hard-coded 0 fails both cases).

Tests: WorkerClientTests.ConcurrentInvokesEmitStrictlyIncreasingSequences
OnTheWire (32 parallel invokes; failed 3/3 pre-fix) and
.StartAsync_SendsGatewayHelloWithConfiguredMaxFrameBytes.
This commit is contained in:
Joseph Doherty
2026-08-07 06:15:20 -04:00
parent 6092172694
commit f27eb28063
3 changed files with 90 additions and 5 deletions
+7 -3
View File
@@ -348,9 +348,13 @@ messages, tagged from 10 upward:
Rules:
- `sequence` is a monotonic per-sender counter used as a diagnostic aid; it is
not validated for gaps or ordering on receive (the named pipe already
guarantees FIFO delivery).
- `sequence` is a monotonic per-sender counter used as a diagnostic aid. Both
sides stamp it at the point of writing, inside their single write path — the
gateway on its outbound-channel write loop, the worker on its own writer — so
the numbers stay monotonic in wire order no matter how concurrent callers
interleave while building envelopes. It is not validated for gaps or ordering
on receive (the named pipe already guarantees FIFO delivery); if inbound
enforcement is ever added, this is the property it will rely on.
- `correlation_id` links a command to its reply; it is authoritative on the
envelope, and the inner `MxCommandReply.correlation_id` echoes it for
MXAccess parity.