# Binary Hub Protocol (wire) — TODO Archive: 2026-05

Archived entries from `SIGNALR_BINARY_PROTOCOL_TODO.md` per the Status-based rotation rule (`docs-archive` Rule A — only `Status: Closed` entries). IDs preserved (never reassigned). Format identical to the active file. Year-month bucket = **2026-05** (Resolution dates 2026-05-XX).

> **Archive files are NOT auto-loaded** by `docs-discovery` or any session-start pre-load. Read on-demand when historical context is needed (e.g., an active entry or code comment references an ID below). Cross-references from active entries to these IDs remain functional — open this file when following a link.

---

## ACCORE-SBP-T-P8X9: ~~SegmentBufferReader isolated unit tests~~
**Status:** Closed (2026-05-03) — obsoleted by `ACCORE-SBP-T-G7T2` · **Priority:** ~~P1~~ · **Type:** ~~Test coverage~~

~~Original `vast-brewing-moonbeam` refactor plan (chunked receive-path) listed these tests in its verification section; they were never written. Needed: `Write` / `TryAdvanceSegment` / `Complete` basic contract; producer-consumer concurrency (grow-buffer handoff race); missed-signal double-check pattern under `ManualResetEventSlim` reset; `Dispose` lifecycle (buffer pool return, old-buffer cleanup).~~

### Resolution (2026-05-03)

The `SegmentBufferReader` and `SegmentBufferReaderInput` types were deleted by `ACCORE-SBP-T-G7T2` (the Step 6 migration to `AsyncPipeReaderInput`). Test coverage for the equivalent contract on the replacement primitive lives in `AyCode.Core.Tests/Serialization/AcBinarySerializerPipeParallelTests.cs` (added by `ACCORE-BIN-T-D6H4`): `Feed` / `TryAdvanceSegment` / `Complete` / `Dispose` contracts plus framing-state-machine, multi-chunk handoff, sliding-window cycle, and producer-consumer concurrency cases. The original test scope is therefore covered on the replacement type — no separate test file needed.

## ACCORE-SBP-T-G7T2: ~~Migrate `AcBinaryHubProtocol.TryParseChunkData` to `AsyncPipeReaderInput`; delete `SegmentBufferReader` + `SegmentBufferReaderInput`~~ (Step 6 of ADR-0003)
**Status:** Closed (2026-05-03) · **Priority:** ~~P1~~ · **Type:** ~~Refactor~~ · **Related ADR:** [`docs/adr/0003-acbinary-streaming-receive-architecture.md`](../../../docs/adr/0003-acbinary-streaming-receive-architecture.md) Step 6

~~Switch `AcBinaryHubProtocol.TryParseChunkData` from `SegmentBufferReader.Write(span)` to `AsyncPipeReaderInput.Feed(span)`. Update `AsyncChunkState` field type from `SegmentBufferReader Buffer` to `AsyncPipeReaderInput Input`. Lazy `Task.Run` deser-task start (after first chunk), `CHUNK_END` lifecycle (`Complete()` + `Dispose()` + `_chunkStates.Remove`), and the WASM synchronous-deser path all preserved. Wire format unchanged.~~

### Resolution (2026-05-03)

Migration completed in three coordinated commits:

1. **`AcBinaryHubProtocol.cs` receive-side migration**:
   - `AsyncChunkState.Buffer : SegmentBufferReader` → `AsyncChunkState.Input : AsyncPipeReaderInput`
   - `ParseAsyncChunkStart` instantiation: `new AsyncPipeReaderInput(BufferWriterChunkSize × 2, multiMessage: false)` — framing parsed externally by the protocol's own `[201]/[202]` branches in `TryParseChunkData`; raw data flows via `Feed`.
   - `state.Buffer.Write(span)` → `state.Input.Feed(span)`, `Complete()` and `Dispose()` calls retained (same names on the new class).
   - Background deser-task uses `AcBinaryDeserializer.Deserialize(AsyncPipeReaderInput, Type, opts)` overload (already present at line ~328).
   - WASM fallback redesigned to use the streaming overload with `state.Input` directly — after `Complete()`, `TryAdvanceSegment` never enters the `MRES.Wait` path so no `PlatformNotSupportedException` risk. Replaces previous direct byte[]-mutation access (`state.Buffer.Buffer`, `WritePos`) which is not exposed on `AsyncPipeReaderInput`.
   - Diagnostic warning log dropped the `WritePos` / `ReadPos` arguments (private on the new class) and substituted `chunkFrameBytesConsumed`. Deeper instrumentation available via `AsyncPipeReaderInput.DiagnosticLog` static hook (`[Conditional("DEBUG")]`).

2. **`AcBinaryDeserializer.cs` API surface cleanup**: removed the public `Deserialize(SegmentBufferReader, Type, AcBinarySerializerOptions)` overload (was a 2-line forwarder to `DeserializeSequence(new SegmentBufferReaderInput(reader), ...)`). The only caller was the now-migrated `AcBinaryHubProtocol`.

3. **File deletion**: `AyCode.Core/Serializers/Binaries/SegmentBufferReader.cs` and `AyCode.Core/Serializers/Binaries/SegmentBufferReaderInput.cs` removed from disk.

**Acceptance criteria met:**
- ✅ All existing SignalR integration tests pass (`AcBinaryHubProtocolConcurrencyTests`: 2/2; AyCode.Core.Tests pipe/signalr/hubprotocol filters: 30/30) — no behavioral regression.
- ✅ Wire format unchanged at the protocol level — `[201]/[202]` framing still owned by SignalR's `TryParseChunkData`; `AsyncPipeReaderInput` with `multiMessage: false` is a passive byte buffer here, not a framing-aware reader.
- ✅ Code-search finds 0 references to `SegmentBufferReader` / `SegmentBufferReaderInput` in `.cs` files (only historical mentions remain in `docs/` — ADR-0003, deprecated TODO entries).
- The pre-cutover send-side migration (write-path uses `AsyncPipeWriterOutput` since the `bool waitForFlush` → `FlushPolicy` enum refactor) plus this receive-side cutover means **the SignalR protocol is now fully on the AsyncPipe primitives** — no parallel chunk-handling code in the protocol layer.

**Note on ADR-0003 promotion to Accepted:** ADR-0003 status update tracked separately — the original wording referenced unimplemented Steps 4/5 (NamedPipe/FileStream helpers) which were dropped. ADR may need a Resolution-style addendum noting the scope reduction; orthogonal cleanup task.

## ACCORE-SBP-T-Q3M7: ~~CHUNK_ABORT marker for mid-stream serialize-failure fault isolation~~
**Status:** Closed (2026-05-23) · **Priority:** ~~P1~~ · **Type:** ~~Feature~~

~~Add a `[203]` CHUNK_ABORT marker to the AsyncSegment chunked-framing wire format. When the streamed-arg serialize throws after CHUNK_START has been sent, `WriteMessageChunked` emits the abort marker instead of letting the exception propagate (which would abort the entire SignalR transport connection in `HubConnectionContext.WriteCore`, killing all other in-flight invocations on the same WebSocket). The receiver's `TryParseChunkData` recognises `[203]` as a first-class case, tears down the chunk-state safely (mirroring the `[202]` Complete → DeserTask observe → Dispose ordering), and surfaces the failure to the awaiting caller as a synthesised `CompletionMessage.WithError(invocationId, ...)` — caller's task faults with a specific, actionable error rather than a transport timeout. Fault isolation: blast radius = one message; connection stays alive.~~

### Resolution (2026-05-23)

Shipped in `AcBinaryHubProtocol.cs`:

- **`MsgAsyncChunkAbort = 203`** constant added next to the `[200]/[201]/[202]` markers.
- **`WriteMessageChunked` (send side)**: `try/catch` around `AcBinarySerializer.Serialize(streamedArg, ...)`. The catch logs Error, then calls `TryEmitChunkAbort(pipeWriter)` (best-effort `GetSpan(1) + Advance + SyncFlush` on `[203]`). On success: returns gracefully; on emit-failure: rethrows so the SignalR outer handler aborts the connection (baseline behaviour preserved).
- **`TryParseChunkData` (receive side)**: new first-class branch on `firstByte == MsgAsyncChunkAbort`. Shared `AbandonChunkState(state, binder, reason)` helper for safe teardown (Complete → observe DeserTask → Dispose → Remove); `GetInvocationId(state.PartialMessage)` extracts the routing key; `CompletionMessage.WithError` synthesised for the caller. Fire-and-forget fallback: `PingMessage.Instance` (preserves the "consumed input ↔ produced message" contract).
- **Protocol-violation path tightened**: the previous "fallback to normal parse" guess-and-recover (for unknown byte in chunk mode) replaced with `LogError` + safe teardown + `System.IO.InvalidDataException`. With CHUNK_ABORT in the protocol, legitimate sender-abort is handled by the `[203]` branch; anything else is genuine framing corruption and must not be masked.

**Acceptance criteria met:**
- ✅ Sender-side serialize failure no longer aborts the WebSocket — blast radius is the affected `requestId` only.
- ✅ Awaiting caller receives a specific error message rather than a transport timeout.
- ✅ Background deser task safely observed (no unobserved task exception, no Dispose-during-Wait race).
- ✅ Teardown logic deduplicated into the `AbandonChunkState` helper; `GetInvocationId` and `TryEmitChunkAbort` helpers extracted; nested try/catch eliminated.
- 🟡 **Deferred edge case** — mid-header serialize failure leaves a partial `[201][UINT16=N]` on the wire; `[203]` is parsed as chunk-data and the receiver lands on the protocol-violation `InvalidDataException` path. Robust graceful-abort fix tracked in [`../../../AyCode.Core/docs/BINARY/BINARY_ASYNCPIPE_TODO.md#accore-bin-t-z6n2`](../../../AyCode.Core/docs/BINARY/BINARY_ASYNCPIPE_TODO.md#accore-bin-t-z6n2).

## ACCORE-SBP-T-W7K4: ~~Prefix-tier VarUInt protocol-side parity + `T = object` regression cluster~~
**Status:** Closed (2026-05-27) · **Priority:** ~~P1~~ · **Type:** ~~Bug fix / Wire format~~ · **Related:** [`../../../AyCode.Core/docs/BINARY/BINARY_TODO.md#accore-bin-t-v3p9`](../../../AyCode.Core/docs/BINARY/BINARY_TODO.md#accore-bin-t-v3p9)

~~Two coordinated fix waves after the V3P9 wire-format change exposed pre-existing latent bugs in
the SignalR-protocol layer: a standalone `ReadVarUInt` that still decoded under LEB128 grammar
(protocol-layer parity miss), and a cluster of `T = object` generic-inference regressions in
multiple developer-facing `Serialize<T>` call sites that bound `T` to `object` instead of the
runtime concrete type, producing object-typed empty wire payloads.~~

### Resolution (2026-05-27)

**1. Protocol-layer `ReadVarUInt` parity.** `AcBinaryHubProtocol.ReadVarUInt(ref SequenceReader<byte>)`
had its own LEB128 implementation that was missed during V3P9. After the serializer-side moved to
prefix-tier, the protocol's standalone `ReadVarUInt` continued decoding under LEB128 grammar,
corrupting argLength / header parsing on the chunked-stream path. Rewrote to match prefix-tier
(1-byte `0xxxxxxx` / 2-byte `10xxxxxx` / 3-byte `110xxxxx` / 4-byte `1110xxxx` / 5-byte `1111xxxx`).
Test count: 96 → 50 failing.

**2. `T = object` generic-inference regression cluster.** Multiple call sites silently invoked
`Serialize<T>(T value)` where the developer-facing call had `object?` static type, causing T to
bind to `object` instead of the runtime concrete type — wire payloads came out object-typed (empty,
no properties). Fixed via runtime-type capture (`value.GetType()`) and a new non-generic
`SerializeToBinary(object?, Type)` overload for heterogeneous `object?` call sites:

- `AyCodeBinaryHubProtocol.WriteArgument` (streamed-arg AsyncSegment path, ~line 565)
- `ISignalParams.SetParameterValues` (per-param explicit-null branch)
- `SerializeObjectExtensions.CloneTo<TDestination>` / `CopyTo` (project-wide deep-clone)
- `SignalRSerializationHelper.CreateResponseData` (server response path)
- `AcSignalRClientBase` context-params, `AcWebSignalRHubBase` `isRawBytes` pre-serialize,
  `AcSignalRDataSource` fallback re-serialize

Test count: 50 → 10 failing (within baseline 13-15 flaky-test variance band).

**3. `SignalRSerializationHelper` byte[]-overload migration.** `SerializeToBinary` overloads
switched from `ArrayBufferWriter<byte>` + `WrittenSpan.ToArray()` (2 allocations: writer buffer
+ ToArray copy) to direct `.ToBinary(...)` byte[]-output (1 pool-backed allocation, no copy).
Same wire output, fewer allocations per call. Same migration applied to `SerializeObjectExtensions`
internal use sites.

**4. `DebugLogArgument [Conditional("DEBUG")]` diagnostics.** Added two `[Conditional("DEBUG")]`
overloads on the write-side (`Type, int, object?`) and read-side (`Type, int, long`) of the
argument framing path. Release-eliminated, debug-enabled — zero production cost, fast diagnostic
when wire-format regressions reappear (the SignalR test regression cluster would have been faster
to localize with these in place).

### Acceptance criteria met

- ✅ Full solution build (`AyCode.Core.sln`) — 0 errors.
- ✅ SignalR test suite: 10 failing (vs baseline 13-15) — within flaky-test variance band.
- ✅ Wire format unchanged at the protocol level — V3P9 covers the encoding change; this fix is
  protocol-side parity catch-up + non-encoding-related generic-inference bugs.
- ✅ Allocation profile improvement on `SignalRSerializationHelper.SerializeToBinary` paths
  (server response + heterogeneous `object?` call sites).

### Note on regression-discovery flow

The `T = object` regression cluster was latent (pre-V3P9), but only surfaced after V3P9 broke the
SignalR test baseline — the failing tests' close investigation exposed the entire generic-inference
gap. Future encoding-format breaks should pair with a focused `(value.GetType())` audit across the
project's `Serialize<T>` / `ToBinary<T>` call sites to catch this earlier.
