# Framing alias and initiator receive-progress review — 2026-10-13

Scope: C++, Java, .NET and Rust FIXP framing/socket/initiator paths, plus the
C++ and .NET generic SBE framing helpers. Worktree `codex-review-framing-1013`.
The main checkout was used only for fast-forward pulls. Three parallel agents
reviewed separate flavours; the root coordinated edits and serial validation.

## Confirmed defects

1. C++ and .NET generic SBE encoders wrote the two-byte frame prefix before
   consuming an overlapping source. C++ also passed overlapping ranges to
   `memcpy`. Copying with overlap support before writing the prefix preserves
   the original payload. The C++ path skips an already positioned payload.
2. The .NET SOFH encoder had the same ordering defect, unlike its C++ and Java
   counterparts. Copying before the header also permits the already positioned
   payload to avoid a copy. All argument/refusal checks still precede mutation.
3. The .NET frame stream compacted unread bytes before copying an aliased append
   source. Compaction could overwrite consumed bytes or rebase unread source
   bytes. The fix ports the existing C++/Java allocation-free rotation/copy
   algorithm, leaving the ordinary append path unchanged.
4. All four initiators treated `WouldBlock` as an empty read even when the
   bounded socket read had consumed part of a frame. An application larger than
   the 512-byte read chunk, a fragmented heartbeat or a fragmented Terminate
   response could close the session at an expired deadline while more of the
   frame was queued. Deadline judgment now requires zero bytes read, matching
   the acceptor behaviour. Only complete frames refresh the receive timer;
   stalled partial input still expires on the next empty read.

5. Rust's direct initiator `pump()` left its public liveness clock running after
   clean EOF, including a stale Terminate-wait flag. The higher-level `step()`
   already cleaned this up. The direct path now stops liveness too. Its regression
   covers both SOFH formats in established and Terminate-wait states, including
   repeated terminal pumps without output.

## Regression design

The SBE tests compare independent payload snapshots and whole backing arrays
across 867 source/destination/length combinations per flavour, including refusal
without mutation. SOFH tests cover both layouts, managed/native paths, overlapping
and separate ranges, and short output buffers. The .NET stream oracle reaches
states through public calls and checks 802,150 compacting aliases. A separate
partial-header case checks refused/zero appends and successful completion.

Receive-progress fixtures cover both SOFH layouts, large queued application
frames, fragmented heartbeat/Terminate completion, and stalled partial input.
The C++ fixture additionally limits reads to one byte. Existing liveness/socket
and framing fixtures provide adjacent coverage. The new C++ fixture is registered
in the ordinary correctness target list and therefore the open-edition gate.

## Validation

Qualification passed on the rebased candidate. These are selected regression
and adjacent FIXP/framing suites, not a claim that the entire repository suite ran:

| Check | Development | Open |
|---|---:|---:|
| C++ selected framing/socket/liveness fixtures | 5 passed | 5 passed |
| Java selected FIXP/native fixtures | 122 passed, 0 skipped | 116 passed, 0 skipped |
| .NET selected framing/FIXP/native fixtures | 109 passed, 1 skipped | 103 passed, 1 skipped |
| Rust receive-progress/EOF integration | 6 passed, 0 ignored | 6 passed, 0 ignored |
| Rust FIXP library tests | 378 passed, 0 ignored | 328 passed, 0 ignored |

The Java, .NET and Rust rows total 1,168 passes and two environmental skips.
C++ SBE and receive-progress fixtures also passed ASan+UBSan and explicit
C++11 open-header builds, supplementing the C++17 matrix. The receive-progress
fixture makes 14,586 assertions across 12 cases; the existing runner-liveness
fixture covers 49 cases and 1,415 checks. Assertion-based stream/socket fixtures
were compiled with assertions enabled. The .NET alias oracle checked all 802,150
states in each edition. Native load guards and both native SOFH overlap rows
passed in both managed-language editions.

The sole .NET skip in each edition was the existing multicast loopback test:
`joined multicast group but kernel/sandbox did not loop packets back`. All new
regressions ran. TRX outcomes and JUnit fixture lists were inspected, rather than
relying only on process status.

Rust EOF was reproduced independently: its integration test failed without
`liveness.stop()`, then all six integration tests passed with that cleanup.

Baseline C++ SBE: 867 combinations, 904 failed checks. Baseline C++ receive
progress: 12 cases, 80 failed checks. Baseline Rust integration: three expected
failures and two passing stalled-input controls, zero ignored tests.
Baseline .NET: ten expected failures and eight passes, zero skipped tests. Three
of the passes were existing classic FIX overlap tests selected by the broad
baseline name filter; the other passes include the native-load guard, both native
SOFH overlap rows and both partial-header/refusal controls.

The first 45-minute lease expired while Java was still waiting for foreign jobs;
no Java baseline command had launched. A renewed local wait was also cancelled
before launch. Validation then moved to a session worktree on `scan`, using GCC15,
JDK21 and the existing .NET 8 SDK. Its shared-lane affinity is CPUs0–9; CPUs10–19
belong to the isolated lane. The shared/isolated lease policy permits both lanes,
with a 32GiB available-memory floor for this build. BEELINK2 ran no further build.
The Java baseline there produced five expected failures, one passing native-load
guard and zero errors/skips. These resource waits are not test failures.

Independent final reviews found no additional defects in the changed paths. A
GCC15 native-build bounds warning in the unchanged multi-acceptor header was
audited: every caller bounds-checks the peer index before private dispatch, and
round-robin indices are reduced modulo the slot count. No reachable violation
was found.

No latency numbers are claimed. The fixes preserve bounded storage and introduce
no temporary payload allocation; skipping already positioned encoder payloads
removes redundant copy work, but this review is primarily a correctness result.

## Evidence

Logs, source hashes, commands and test reports are retained beneath
`~/archive/libhft/reviews/2026-10-13-codex-review-framing-1013/` before the worktree
and build outputs are removed. Builds use `LIBHFT_BUILD_ROOT`, a shared host lease,
a foreign-heavy-process guard, and at most 16 jobs. On BEELINK2 the guard waits
for every foreign heavy job; on scan it also recognizes the independently leased
isolated lane described above. Unleased foreign heavy jobs block both guards.

The matrix tested commit `3292327f3` plus the one-line Rust EOF cleanup. SHA-256
comparison of all 3,704 tracked inputs under the four language trees, tools and
root toolchain file confirmed that the initial local candidate matched those
tested sources. The final rebase onto `5f0b45ca2` changed nine C++ inputs: four
classic FIX headers, three classic FIX tests and two guides. All 17 files changed
by this review and every Java/.NET/Rust source remained identical to the tested
inputs. All 30 post-rebase C++ development/open/sanitizer and native Java/.NET
build/test stages passed again, with identical test and skip counts. The final
3,704-input manifest matches the publishing tree. Both input manifests and the
exact drift list are retained in the archive.
All 35 matrix stages completed as expected, including the intentionally failing
EOF baseline; the two additional C++11 build/run checks passed.
`tools/session-preflight`, `git diff --check` and the export scan of all 17 changed
engine files passed. Three agents independently reviewed the final changes.
