# FIX builder boundaries and reject references, 2026-10-19

Session `codex-review-builders-1019`, thread `01a0fcb1-4c4d-7380-bb51-0d6ed4a2b4d6`,
on BEELINK2. Three parallel source reviewers worked in the session worktree.
Builds/tests used one shared host lease and sequential stages with at most 16 workers.

## Findings and changes

1. .NET builder methods wrote a tag or partial value before discovering an undersized
   destination. Catching the error and appending a smaller field could produce a valid
   frame with that field swallowed into the previous value. Complete appends and the
   checksum trailer now check capacity before changing the buffer or cursor. Near the
   capacity boundary, exact lengths preserve exact-fit writes; adequate buffers use
   inexpensive maximum-size checks for numeric values. Signed extremes and fixed/trimmed
   decimals retain their existing formatting.
2. .NET's packed tag prefix store could overwrite an input span borrowing the output
   buffer before copying its value. Aliased fields now move the value to its final
   location before writing the exact prefix. An aliased BeginString is preserved in
   the reserved header prefix, and a multi-byte MsgType is moved before its prefix is
   written. These paths require no temporary allocation.
3. Three .NET Message tag scanners used a conservative overflow bound based on digit 9,
   rejecting valid tags 2147483640 through 2147483647. All three now check the actual
   digit, consistent with the structural/data scanners. Overflowing tags still cannot
   wrap to another field identifier, and scanning resumes at the next field.
4. Java business-reject builders accepted only 32-bit RefSeqNum, while business/session
   reject views clamped larger references to Integer.MAX_VALUE. The builder has a long
   overload and both getters preserve signed 64-bit values. The existing int setter
   delegates to the long setter.
5. Rust's business-reject builder accepted only u32 references despite u64 session and
   codec sequence numbers. It now accepts u64 and preserves the full wire digits.
6. Java's wrong-session simulator restricted copied references to nine digits and
   accepted malformed numeric prefixes. Its strict byte parser now preserves all
   nonnegative long references and uses zero for absent, malformed or overflowing
   values. The shared simulator scanner also prevents oversized tags from wrapping
   into tags 34/35 and stops malformed fields at their delimiter without swallowing
   the next valid field. Legal maximum Int32 and zero-padded tags remain readable.

## API compatibility

Java's two getter return descriptors change from int to long: callers must recompile,
and int assignments must be widened. The existing Java int setter descriptor remains.
Rust callers passing explicitly typed u32 values must widen them to u64; ordinary
unsuffixed literals remain accepted. These changes implement the existing ruling that
sequence-carrying APIs use 64-bit values.

## Qualification

The original-code baseline is `73c88ac032e10b73ce19962d788e59e3d0699718`.
Java recorded five tests: three failures, two passes, zero errors/skips. .NET recorded
113 tests: 64 failures, 49 passes, zero skips. The failures comprise 16 reader boundary
cases and 48 builder cases; independent report inspection found 39 builder failures
showing concrete corruption and nine first failing on capacity exception contracts.
All 33 exact-fit controls and the raw-append recovery control passed on the baseline.
Rust failed compilation at the old u32 setter when called with u64 references.

The simulator baseline is `c846c93a5dd2f0d734c9527057b6ab2c30bc940f`, after the
other fixes but before the simulator change: 48 tests, 36 failures, 12 passes,
zero errors/skips. Failures comprise eight width/leading-zero references, four
malformed numeric prefixes, sixteen tag-wrap cases and eight delimiter-recovery
cases. Routing, missing-reference reuse and valid maximum/padded-tag controls pass.

The fixed implementation tested is `bcc6eb4dfa5db4e3eaaadffc4ae223276bda05db`.
Every fixed native/test stage recorded that clean source ID.

| Selected tests | Default | Open / no-default-features | Failures / skips |
| --- | ---: | ---: | --- |
| Java, eight suites | 95 passed | 95 passed (open) | 0 / 0 in each |
| .NET, eleven suites | 353 passed | 353 passed (open) | 0 / 0 in each |
| Rust reject wire integration | 3 passed | 3 passed (no-default-features) | 0 / 0 in each |
| Rust builder units | 7 passed | 7 passed (no-default-features) | 0 / 0 in each |
| Rust codec units | 16 passed | 16 passed (no-default-features) | 0 / 0 in each |

Default/open managed test identities match exactly, as do Rust's two feature
configurations. All 53 new Java and 113 new .NET case identities match their
baselines; every previously failing case passes. Rust's unit filters exclude
1,381 unrelated cases for builders and 1,372 for codecs; those cases were not run.
Native JNI and .NET libraries were built in canonical default/open directories;
required-native loading/ABI guards passed in all four managed runs. The .NET alias
check measured zero allocation over 1,000 calls after 1,000 warmups and checked
correct output. All five Java simulator allocation tests ran in both editions,
including the wrong-session zero-allocation case and allocating positive control.
The owned-file export scan and whitespace check passed.

Before publication, the seven commits were rebased onto
`8861ee06d5e06ab50d8553ce70ffa1aca43842dd`, producing code tip
`774315e40b81a1768ef95894569843c010f5e965`. All twelve owned production/test
files are byte-identical to the qualified revision. Incoming changes add unrelated
inbound-parity tests and a separate Java compatibility module, plus private Rust
provider work. In the qualified Rust crate, the only change widens four file-store
helpers from pub(crate) to pub and adds two comments; exact comparison after
normalizing those declarations confirms identical function bodies and calls.
Selected build inputs, native code and selected tests are otherwise unchanged.
These changes do not affect the exercised paths, so runtime qualification remains
pinned to the tested revision; the rebased revision is not claimed as a fresh run.
The full changed-path list and source-equivalence receipt are retained in the archive.
A concurrent publication required one further rebase onto
`09d73c21941820a263ee38c84ec83494c6190c1c`, yielding code tip
`ce61bb273fd8715a6433368fbc8696a33ac8cf57`. Its additional changes are documentation and a separate
private benchmark timing controller. The twelve owned files remain identical;
no selected build or runtime input changed in this second rebase.
A final claim-only rebase likewise leaves those files unchanged; its exact source
identity is recorded in the archived publication receipt.

The .NET regressions exercise exact-fit and one-byte-short primitive writes, complete
frame checksums, failure recovery, aliased fields/BeginString/MsgType, header/trailer
capacity, leading-zero tags, integer overflow rejection, and a warmed allocation check.
Java tests cover both reject views, the old and new setter APIs, reset and raw/event
reuse. Rust tests verify complete transmitted frames/checksums across the u32 boundary,
i64::MAX and u64::MAX, plus oversized-body refusal and recovery without sequence loss.
The maximum reference is a RefSeqNum serialization control, not an outbound session
sequence; the existing live-sequence admission ceiling is unchanged.

## Review boundaries and evidence

C++ builders/encoder/formatters were reviewed across supported Init/add/Finalise usage;
no confirmed new issue was found. Unsupported lifecycle misuse and a timestamp-helper
call outside its documented digit precondition were not treated as defects. This batch
changes no C++ production code and makes no measured latency-improvement claim. The
allocation checks qualify only the exercised alias and simulator paths; no timed
latency benchmark was performed.

The previously documented Java session snapshot price/size association candidate remains
with the session owner; this batch does not edit that claimed lifecycle code. The
pending handoff is under `internal/docs/reviews/field-market-data-20261018/`.

Raw logs, commands, source IDs, reviewed patches, XML/TRX reports and a commit bundle
are archived under `~/archive/libhft/reviews/2026-10-19-codex-review-builders-1019`.
This is targeted review and qualification, not a whole-repository test run.
