# Stream progress and callback lifecycle review — 2026-10-08

Worktree: `codex-review-stream-1008`, based on `c3f188a64`.
Parallel reviewers covered C++ classic FIX stream recovery, Java FIXP callback
lifecycle, and Rust FIXP finalization. Root covered Java JNI output contracts
and serialized builds/tests under the shared host lease, at most 16 workers.

## Confirmed defects and fixes

- C++ checksum rejection could lose a split BeginString after a complete bad
  frame. The parser had already advanced to the next frame, but recovery began
  its search one byte later when that next BeginString was incomplete. The
  advanced-cursor path now includes its current byte in both the candidate scan
  and retained-prefix bounds. Ordinary parse-error recovery still advances past
  the rejected start, and cached full-marker recovery is unchanged. Tests cover
  every split from zero through the complete BeginString, observed/plain session
  processing, LENIENT and STRICT transcript controls, and owned/borrowed parser
  windows with detachment before later input.
- Java native frame/message pops wrote through read-only direct buffers and
  consumed queued input. The public wrappers now refuse read-only output before
  JNI, return INVALID_LENGTH, clear output metadata and preserve the buffered
  frame for a writable retry. Closed-owner checks retain their precedence.
  Native Sequence, fixed-message and variable-message encoders similarly refuse
  read-only output with zero bytes encoded. Read-only inputs remain supported.
  Tests verify ordinary and sliced direct buffers, guard bytes, position/limit,
  frame retention, exact writable retry bytes and both SOFH formats for pops.
- Java application callbacks could send Terminate and then return into a stale
  recovery continuation that emitted FinishedReceiving after it. They could also
  replace their runner/peer slot and return an error that the old pump applied to
  the replacement, closing its store and transport. Direct runners now snapshot
  a lifecycle generation across the callback and check protocol establishment
  before continuing finalization. Multi-peer dispatch checks the child generation
  before old-result bookkeeping or release. Replacements keep their diagnostics;
  same-lifecycle terminal send failures retain the nested operation's result and
  transport byte counts. Ordinary nested application sends still complete recovery.
- Rust initiator finalization consumed FinishedReceiving but left the runner live
  after a zero/negative Terminate write, with no trigger left to retry. Every
  failed required finalization Terminate now retires the connection; a positive
  short write, which already retires it, is not closed twice. Diagnostics remain
  readable, and full successful sends retain their existing liveness behavior.
- Rust multi-acceptor legacy idempotent finalization passed an exhausted unsigned
  frontier through a setter that normalizes zero to sequence one. Repeated
  FinishedSending could then restart enormous gap notifications. It now keeps
  zero as the exhausted frontier and explicitly clears recovery state, matching
  the other runners. Wire tests cover MAX-1/MAX, both SOFH formats, enforced and
  legacy flow rules, repeated finalization, later application admission and a
  usable sibling peer.

## Baselines and intermediate runs

The first C++ baseline reported 148 failed assertions. Of those, 126 exposed
split-prefix loss and 22 were an incorrect new test expectation: STRICT in TEST
runtime uses transcript diagnostics and drops a checksum-invalid frame without
returning ERROR. The first fixed full open run passed 146/147 binaries and failed
only those 22 expectations. The new test was corrected to the existing contract
and now checks second-read recovery for the STRICT controls as well. Production
was unchanged by that correction. A fresh counterfactual run of the corrected
matrix against the original FrameParser header reports 126 failed assertions
and is retained separately. All 66 cases execute in that run.

Before Java production changes, the focused suite ran 113 tests with 27 assertion
failures and no errors/skips; mandatory JNI loading passed. The lifecycle class
contributed 22 failures among 84 cases (late finalization and stale callback errors
against replacements). The native output tests contributed five failing methods.
Twelve additional lifecycle cases for terminal positive short writes are final
coverage rather than independently run red evidence. The final focused suite
passes 125 tests without failures, errors or skips.

Rust's six focused test methods had three assertion failures before production
changes: negative and zero finalization writes, and the legacy exhausted frontier.
The other three methods passed as controls. Both SOFH formats and liveness/flow
variants execute inside these methods.

## Verification

Java development passes 3707 unique tests; Java open passes 3265. Both have
zero final failures, errors or skips, with mandatory JNI loading and separately
built development/open native libraries. The three counters-disabled cases skip
in the ordinary fork and pass in their configured fork; final aggregation keys
by class and case name.

C++11 open edition passes 147/147 test binaries with zero reported skips,
including its layering and simulator checks; 83 private binaries are excluded.
The complete garbled-frame policy binary also passes AddressSanitizer and
UndefinedBehaviorSanitizer with leak detection and fatal UB diagnostics. Its 66
new split-prefix cases execute in both the open and sanitized runs.
Rust default and no-default-features each pass 1351 tests with zero failures and
one pre-existing ignored builder documentation example; their library subsets
pass 1203 tests. All six focused methods also pass.

## Evidence and scope

No .NET or reserved C++ FIXP runner, flow, liveness or finalization source is
edited. Verification is Linux-only; optional Rust venue/service-pack features
and Windows execution are not claimed. Independent reviewers checked the C++
resync bounds/progress/cache behavior and Java native output/lifecycle guards.
Callback exceptions still propagate; transport-method reentry and concurrent
runner use are not newly promised.

No per-message allocation, lock or atomic is introduced. Lifecycle generation
writes occur only during cold lifecycle changes; callback continuation adds
scalar checks. Native read-only refusals avoid a JNI call. These are code-path
properties, with no measured throughput or latency claim.

Evidence is retained at publication locally and on HP under
`~/archive/libhft/reviews/2026-10-08-codex-review-stream-1008/`: baseline/intermediate/
final logs, JUnit reports, source hashes, drivers, this record, patch and history
bundle. Both SHA-256 manifests are checked before worktree/build cleanup.

## Publication

Rust fix: `24089311b`, also pushed to the private session branch after its gates.
Java native buffers: `41f1b7d01`; Java lifecycle: `2c1635f17`. These were also
backed up to the private session branch after the full development gate.
C++ split-header fix: `13ec66199`. Final fetch/rebase is onto `1513e9c45`
(the initial claim commit), with no incoming commits. The outgoing range contains
only this session's fixes and record. All 13 changed engine files pass the export
scan without findings, match the source hashes used for their final gates, and
pass whitespace checks.
Publication targets private `atrekes/libhft-devel` directly at main. Other
sessions' claim blocks are preserved exactly. Final verification counts are
machine-readable in the archived `verification-summary.json`.
