test(ab): hw4 evidence - mixed MSP read (#582), listing order (#572) on the press; AbLegacy bit-write test; gate-results + docs
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -247,6 +247,7 @@ public sealed class CipConnection : ICipChannel, IAsyncDisposable
|
||||
_ => await open.Client.SendRRDataAsync(cip, _o.Timeout, linked.Token).ConfigureAwait(false),
|
||||
};
|
||||
_diagnostics.RecordExchange(Stopwatch.GetTimestamp() - started);
|
||||
_diagnostics.NotifyExchange(cip, reply);
|
||||
if (_o.Routing == CipRouting.Connected && ConnectionManagerServices.IsConnectionGone(reply.Span) is { } extended)
|
||||
{
|
||||
await RetireClosedByTargetAsync(open).ConfigureAwait(false);
|
||||
|
||||
@@ -91,6 +91,21 @@ internal sealed class ConnectionDiagnostics
|
||||
Ms(Interlocked.Read(ref _exchangeTotalTicks)) / count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evidence tap for the gate harness (#582 / #572): sees the CIP request and the raw CIP reply of every
|
||||
/// completed exchange. Null in production; set only by the read-only differential harness. A throwing
|
||||
/// observer is swallowed so it can never affect a request.
|
||||
/// </summary>
|
||||
internal Action<ReadOnlyMemory<byte>, ReadOnlyMemory<byte>>? ExchangeObserver { get; set; }
|
||||
|
||||
internal void NotifyExchange(ReadOnlyMemory<byte> request, ReadOnlyMemory<byte> reply)
|
||||
{
|
||||
var observer = ExchangeObserver;
|
||||
if (observer is null) return;
|
||||
try { observer(request, reply); }
|
||||
catch { /* evidence only */ }
|
||||
}
|
||||
|
||||
internal void RecordExchange(long ticks)
|
||||
{
|
||||
Interlocked.Increment(ref _exchangeCount);
|
||||
|
||||
Reference in New Issue
Block a user