# FIX field ownership and market-data review, 2026-10-18

Session `codex-review-fields-1018`, thread `01a0fcb1-4c4d-7380-bb51-0d6ed4a2b4d6`,
on BEELINK2. Continued the user's deep correctness/latency review with three
parallel source reviewers, an isolated worktree and external build root. Heavy
builds/tests used one shared host lease and sequential stages, capped at 16 workers.

## Findings and changes

1. C++ `fix::Message` copied its parser cursor into the source object's field
   array. Parsing a copied fresh/partial message could write into the source while
   advancing the destination's state. Copy construction/assignment now rebase that
   pointer by its exact array offset, preserving the completed checksum cursor,
   parser/index state and optional structural scratch. Rvalues use the same copy
   operations. Wire bytes remain borrowed; the caller still owns their lifetime.
   The ordinary parse path has no added work.
2. C++ snapshot extraction selected price and size independently across entries.
   An omitted optional size on the first bid/offer could acquire a size from a
   different price level. The first entry on each side is now selected as a unit.
   Missing size stays absent; a missing first price cannot be filled by a later
   entry. Existing two-sided extraction requirements remain unchanged.
3. Java's market-data, execution-report and reject event adapters kept the previous
   raw frame's index/address. Inherited exact-value/header/group accessors could
   expose that prior message after an event rebind or a rejected null/wrong event.
   Event binding now clears the raw state and validates the event kind. `isValid`
   follows the current event, including its close fence. No exact integer value is
   invented from a normalized floating-point event.
4. Java `AbstractMarketDataView.entry()` ignored the declared group count and
   exposed entries when the count was absent/zero or exceeded. It now bounds
   access by both declared count and physically present entries. Bid/ask lookup
   visits entries once instead of repeatedly rescanning for each index, while
   keeping the first matching entry and first field, incremental field ordering,
   and existing count parsing semantics. This is linear in field/value bytes;
   no elapsed-time improvement is claimed.
5. .NET's snapshot-to-top-of-book overload stopped after a fixed ten-level scratch
   array. An eleventh offer, better price or conflicting resolved symbol was
   ignored. It now walks every declared entry directly through the existing
   accumulator, preserving best-price selection, first-at-equal-price behavior,
   size presence and symbol consistency without intermediate level storage.

The C++ compact-event test helper also returned a message borrowing a destroyed
local frame. Its callers now retain the frame storage until projection completes.

## Qualification

The regression baseline was committed at
`9453be9c931f057c864a0eee5683df1523294225`. The C++ run reproduced both
independent failures (a cursor pointing into the source object and a snapshot size
borrowed from a later entry). Java recorded 13 tests: 7 failures, 6 passes,
0 errors and 0 skips. .NET recorded 11 tests: 10 failures, 1 pass and 0 skips.

The fixed implementation is
`926cfce2efb6cab83df2f7cf6656759e1336d899`.

| Check | Development edition | Open edition |
| --- | --- | --- |
| C++ selected binaries | 8 passed | 8 passed |
| Java selected tests | 99 passed, 0 skipped | 99 passed, 0 skipped |
| .NET selected tests | 73 passed, 0 skipped | 73 passed, 0 skipped |

Every fixed stage recorded a clean source tree at that commit. Both managed
editions built their native libraries at the canonical edition-specific paths;
both required-native guards executed and passed. Java reports contain zero
failures/errors/skips. .NET reports contain only Passed outcomes, including all
11 new snapshot cases. The projection allocation assertion passed with zero bytes
across 1,000 calls after 1,000 warmup calls; its result checksum also verified the
late entries, so a truncated but allocation-free projection would fail.

Each C++ edition passed the 864-case copy matrix and 26,664 codec checks with zero
failures. The cursor matrix also passed with structural parsing and local body
state enabled. Both cursor and compact-event tests passed AddressSanitizer and
UndefinedBehaviorSanitizer, including stack-use-after-return detection, without
diagnostics. The export scan and whitespace checks passed. Independent agents
reviewed the patches and checked the actual XML/TRX rows, native guards, class
loading, success markers and source IDs.

The final rebase onto `1362c68aa` required no source changes: qualification already
used that main base. The complete `engine`, `tools`, `bench`, `sbe` and `publish`
trees are unchanged from the qualified commit. The publication record and pending
Java handoff artifacts add no executable production changes.

The C++ regression matrix contains 864 fresh/partial/complete copy scenarios,
including both parser APIs, every byte boundary of plain/binary-data frames,
assignment/rvalue/self-copy and independent source reset/destruction. Twelve
snapshot scenarios cover both sides and field orders. The same cursor tests also
run with structural parsing and local body state enabled. Address/undefined
behavior sanitizers cover the copy/cursor and compact-event tests.

Java's 13 new tests cover five concrete adapters, raw/event/null/wrong-kind/closed
reuse, connection addresses, exact values, groups, declared/actual count bounds,
100-entry depth, and first-entry/field semantics. The broader selected suite also
requires the native library and exercises existing codec/view/group behavior.

.NET's 11 regressions include either side beyond entry ten, improved positive,
zero and negative prices, equal-price handling, omitted sizes, missing prices,
late symbol mismatch and a zero-allocation projection assertion after setup.

## Review boundaries and evidence

Rust message cursor/index/encoder and typed-view review found no confirmed new
issue. C++ legacy TIME table mapping was investigated and is compensated by the
validator's raw-type timestamp handling; no change was needed.

The Java session counterpart of the cross-entry size issue affects
`parseCurrentFrameCursor` and `parseCommonApplicationFrameFast` in
`PureJavaSession.java`, whose lifecycle changes are owned by another session.
No live session source has been edited, pending the ownership decision. A narrow
candidate patch and 24 transcript regression cases are retained under
`internal/docs/reviews/field-market-data-20261018/` for that handoff.

The original session reproduced 8 failures and 16 passes. An isolated compilation
of the candidate passed all 24 cases. The same baseline dependencies were used,
with the candidate class directory first on the classpath; class-loading output
confirms that `PureJavaSession` came from that directory. Both actual parser
choices and both generic/compact event formats were exercised. The patch closes
a selected snapshot side when its entry ends, preserving missing optional sizes;
its `35=W` guard leaves existing incremental partial-field behavior unchanged.
This is candidate qualification, not a claim that the live session is fixed.

Raw evidence is kept outside Git under
`~/archive/libhft/reviews/2026-10-18-codex-review-fields-1018`, with checksums,
reviewed patches, source identities, commands, compiler/runtime logs, XML/TRX
reports, source-equivalence proof and a commit bundle. This was targeted review
and qualification, not a complete repository test run.
