# FIX wire boundary review — 2026-10-05

Base engine revision: `ed0c1a783`. Session: `codex-review-wire-boundaries-1005`.
Parallel agents reviewed Java, JNI/C++ and Rust. Only the root ran builds and tests,
under the shared host lease after process/memory preflight. Build outputs were
external through `LIBHFT_BUILD_ROOT`; native and Cargo parallelism stayed at `-j8`,
and JNI used C++11, `-O3`, `-flto=1`. Java compilation targets Java 8 APIs.

## Corrections

Java basic-validity rejection used a raw SOH walk and trailer substring search.
FIX-looking bytes inside RawData/EncodedText/SecureData could replace the true
sequence, message type or identity, or cut the frame short. The cold rejection
walk now honors the existing Length/DATA pairs, finds trailers outside DATA, and
keeps a coalesced next envelope's header out of this rejection context while
rejecting actual duplicate identity fields. The injection tests prove header
isolation; they do not assert delivery of the following coalesced frame. Every RefMsgType byte is checked before copying it into Reject; a
nonprintable trailing byte no longer escapes as an encoder exception. Normal
valid-frame parsing does not acquire another pass.

Java native typed sends now reject quantities outside the rounded positive
uint32 range at both public and JNI boundaries. Infinity at the direct JNI entry
and finite oversized quantities no longer silently clamp to 4294967295. Exact
maximum and the representable value immediately below the rounding cutoff remain
accepted. The common guard covers new-order/cancel, string/numeric/instrument
identifiers, and queued/direct entry points.

Native transcript typed events now publish from the C++ protocol's admitted
application and Session Reject callbacks. Pre-logon frames, low duplicates and
high messages pending recovery no longer leak typed events before admission.
Recovered application messages publish once in order. Compact reports and event
clock gating are preserved. The transcript raw tap retains its existing behavior;
live TCP application dispatch is unchanged. The existing admin recovery watermark
is preserved: a high Session Reject is not buffered as an application message.

Java MessageBuilder now uses the same 1..65535 tag domain as MessageCursor,
MessageEncoder and the native codecs. Both Length/DATA append overloads validate
both tags before recording either field, avoiding a stranded length field when
the data tag is invalid.

Rust's existing field survey now distinguishes malformed structural pairs from
its tag-syntax and truncation verdicts. Sessions reject missing, mismatched,
orphan, malformed-length and overflowing-tag fields before sequencing or
application delivery, independently of optional dictionary validation. Legal
SOH-bearing and empty DATA still work; parseable invalid tags 0/-1 retain their
field-reject behavior. Header tag accumulation cannot wrap into MsgSeqNum.
The header walk also keeps all 24 DATA pairs opaque while seeking a missing
or later header field: embedded tag 34 bytes cannot supply or override the real
sequence. Normal receive reuses a complete field index only for its exact current
body slice, avoiding duplicate per-digit checks. Incomplete, cleared and mismatched
indexes use the checked parser. Pending-frame insertion clears the index and
restarts the scan; intervening paths that continue parsing leave receive bytes
unchanged. No second ordinary field walk is introduced.

Rust FIXP application classification requires the payload to contain its SBE
header's declared fixed block. Socket, single-acceptor and multi-acceptor receive
paths check before delivery or sequence consumption; outbound classification
checks before persistence or send. The public header-only decoder remains useful
for incomplete headers. Unknown-schema group and variable-data semantics remain
the application's responsibility.

## Reproduction evidence

| Unmodified production code | Final regression cases | Failing | Passing controls |
|---|---:|---:|---:|
| Java rejection parser | 35 | 28 | 7 |
| Java native quantity boundary | 7 | 2 | 5 |
| JNI transcript admission | 6 | 6 | 0 |
| Java builder tag bounds | 7 | 6 | 1 |
| Rust framing boundaries | 7 | 7 | 0 |
| Rust missing/later sequence inside DATA (follow-up) | 2 | 2 | 0 |

Java's final parser baseline was compiled separately from exact `ed0c1a783`
source and run against the final 35-case suite. The first transcript fixture
expected only an ExecutionReport for ExecType=0, which legitimately also emits
an ACK; it was changed to a partial trade and all six cases were rerun against
the original native library. The corrected baseline shows the duplicate leak.
Two candidate-only fixture expectations were also corrected: a checksum-invalid
duplicate envelope does not itself fault the established session, and high
administrative recovery retires its watermark rather than replaying that admin
message. Original logs are retained separately from final evidence.

The two follow-up Rust regressions failed on the initial fixes plus the bounded
header arithmetic optimization, before the DATA/header correction. The exact
session source and patch from `e91318ae5` are retained with that baseline log.

## Verification

Java development: 3279 unique cases pass, zero final failures/errors/skips.
The first full ordinary fork found one old batch fixture sending sequence 20
immediately after 8; it was corrected to 9. Its eight tests and the two remaining
configured forks then passed. Production Java/JNI code did not change between
those runs. The three counters-disabled tests initially skip in the ordinary
fork and all pass in their dedicated fork.
Java open edition: all 2837 cases pass, zero final failures/errors/skips, with
the ordinary, event-driven and counters-disabled forks completing normally.
Both editions require JNI loading; the open library was built against open C++
headers with HFTJAVA_PRO=0.
Final Rust default and no-default-feature configurations each pass 1266 tests
across the library, binaries, integration and documentation suites, with zero
failures and one explicitly ignored documentation example (`builders.rs`).
Each includes 1118 library tests. The 14 focused boundary tests pass again after
fetch/rebase; the rebase changed no source, and final Java/Rust source hashes
match the verified files.

## Parser performance check

A frozen-source driver compares baseline `ed0c1a783` with the final candidate on
one pinned BEELINK2 logical CPU. It imports production wire/kernel modules and
exact header functions; combined mode uses the actual index-backed selection
after the same field survey and 14 indexed lookups. Three ABBA rounds, three
100000-message batches per case/process and 200000 warmup messages per case use
matching optimized compiler flags. Every full-byte result fingerprint and timed
result checksum matches across variants. These are warmed batch costs, not
network latency or per-message percentiles.

| Combined survey, lookups and header | Baseline median ns | Final median ns | Median paired change |
|---|---:|---:|---:|
| Ordinary body | 135.20 | 138.66 | +2.37% |
| Binary body | 148.23 | 150.77 | +1.58% |
| Binary header | 160.95 | 164.70 | +2.59% |

The field survey/lookup costs are effectively flat in this run. Checked
standalone header parsing still costs about 8–11 ns more, but normal receive
reuses its complete survey. The first fully checked candidate increased combined
cost by roughly 3–8%; the bounded-prefix-only refinement still cost about 5%.
Reusing the completed survey leaves roughly 2–4 ns of combined validation overhead.
This is a correctness improvement with measured overhead, not a speedup against
the old implementation. All three candidate snapshots and raw timings remain
archived.

## Independent checks

Independent review checked DATA semantics, header overflow, all FIXP receive/send
owners, the header-only API contract and the index lifetime between survey and
header use. Java review covered binary bounds, next-header isolation, admitted
callbacks and atomic tag-pair rejection.
Export scan: 19 changed engine files, zero findings. Whitespace check passes.

## Retained evidence

Engine fixes are published through `cadb88d3d`; preceding commits are
`2dfb462a3` (Java rejection context), `09f6416e2` (builder tags), `deda8f719`
(native quantity/admission), `d449c525b` (Rust framing), and `e91318ae5`
(batch fixture). The evidence record and claim release follow separately.

Evidence is retained at
`~/archive/libhft/reviews/2026-10-05-codex-review-wire-boundaries-1005/`
on BEELINK2 and HP, with every file checked against `MANIFEST.sha256`.
It includes baseline and final logs/XML reports, fixed source snapshots and
manifests, all three parser benchmark variants and results, the published patch,
and the final record. The session worktree/build root and scratch are removed
after the archive is mirrored and verified.
