# Recovery and fault-callback review — 2026-10-07

Worktree: `codex-review-recovery-1007`, based on `84aae31b6`.
Parallel reviewers covered C++ classic FIX fault hooks, Java FIXP recovery,
and Rust store reset/TLS readiness. Root covered Rust FIXP continuation and
serialized builds/tests under the shared host lease, at most 16 workers.

## Confirmed defects and fixes

- C++ notified `OnOutboundStoreFault` before closing application admission.
  The callback could send another message or recursively enter failing Logout
  persistence. Admission now closes before notification; callback-originated
  sends cannot claim sequences, flush, reset or reach the transport. The first
  fault owns reason, sequence, telemetry, notification and teardown, including
  when its cleanup Logout also fails. Optional-hook guards compile away for
  applications without the hook. A post-send store failure still reports SENT
  to describe wire visibility, but Logon/Logout completion cannot revive the
  terminal session. Normal and high-sequence acceptor Logon continuations also
  stop before recovery or OnSessionLogon after a Logon/ResendRequest checkpoint
  failure; admission reports ERROR while the direct send retains SENT visibility.
- Java FIXP required recovery requests with zero or negative writes left a
  consumed trigger marked in flight with no queued retry. Initiator and single
  acceptor now retire every failed required request; multi-peer transport close
  occurs exactly once and siblings remain usable. Initiator diagnostics retain
  the response write result, including positive short writes.
- Java and Rust finalization requested only the first Count-sized part of a
  larger gap. Repeated FinishedSending messages were suppressed, so recovery
  stalled at that boundary. They now track the outstanding request's inclusive
  end separately from the peer's overall last sequence, ask for the next chunk
  only when the current one has arrived, and acknowledge the completed flow.
  Existing answers still name the full remaining gap. Java retains its positive
  int count limit and completion-only one-argument API; runners use its new
  allocation-free Answer overload. Rust similarly preserves its completion-only
  API and supplies on_inbound_recovery_progress to all three runners, retaining
  its u32 count limit. Tests seed
  public frontiers near a huge gap boundary, then deliver actual replay frames,
  covering failed continuation writes and the exhausted unsigned frontier zero.
- Rust QuickFixFileStore published reset counters and cleared replay memory
  before fallible reset I/O; later files were changed even after the first
  operation failed. Reset now short-circuits and publishes memory only after all
  requested I/O, including offline reset syncs, succeeds. Failure fences ordinary
  writes/checkpoints/unchecked clear while preserving writer locks and the last
  published replay evidence. An explicit successful offline reset after repair
  recovers the same object. Multi-file disk reset is not atomic, and later
  failures may leave partially changed disk files; rollback is not promised.
- Rust TLS reads discarded WANT_WRITE, causing classic FIX idle parking to wait
  for readability. The retry direction is now held in the shared connection
  state and selects POLLOUT; subsequent WANT_READ/success clears it. The test
  uses scripted SSL calls and a real TCP socket, including cloned handles,
  decrypted pending data and ordinary readable/idle controls. It does not force
  OpenSSL renegotiation or measure general throughput.

## Baselines

Before production changes, the C++ original 36-case matrix reported 270 failed
assertions. Java focused tests ran 48 cases with 10 failures, no errors/skips;
mandatory JNI loading passed. Rust reset regressions had nine failures and two
healthy controls; TLS had one failure (300 ms writable wait) and four controls;
all five chunk-continuation tests failed. These were assertion failures, not
compiler failures. Later C++ matrix strengthening is final coverage rather than
an independently executed baseline. The later acceptor admission matrix was
separately run: 30 cases, 108 failed assertions before its three cold-path guards.
The complete C++ addition totals 126 cases (72 callback, 24 initial admin,
30 acceptor admission), covering FAST/SAFE/TEST and healthy controls.

The first Rust green attempt exposed a fixture assertion that inspected a
multi-peer sequence slot after retirement had reset it. The assertion now runs
only for a still-active successful continuation; failed writes still require
retirement, one transport close and exact visible byte counts. The first full
Rust suite then caught a shared-vector compatibility requirement on the old
progress API. The additive method above preserves that contract; neither the
shared vector nor its expectation was changed. Both failed intermediate logs
are retained.

## Verification

C++11 open edition passes 147/147 test binaries with zero reported skips,
including its layering and simulator checks. It excludes 83 private binaries;
the private reliability companion was separately built and passed. The open
reliability binary also passes AddressSanitizer and UndefinedBehaviorSanitizer,
with leak detection enabled and fatal UB diagnostics. All 126 new C++ cases
execute in the open and sanitized reliability runs. The private companion includes
the open file for its fixtures but runs its own mmap cases, not the open main.
This coverage wording was corrected in the 2026-10-08 flow/owner review; the
retained original logs and archives are unchanged.

Java development passes 3606 unique tests; Java open passes 3164. Both have
zero final failures, errors or skips. JNI loading is mandatory, with separately
built development/open 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.

Rust default and no-default-features each pass 1345 tests with no failures
and one pre-existing ignored builder documentation example. Each library
subset passes 1197 tests. Focused reset/TLS/chunk subsets also pass all 21 tests.

The initially started Java full run was
stopped deliberately when review found a related C++ acceptor admission path;
its partial reports are retained separately and are not counted as final proof.

## 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 Java
request retirement/diagnostics, Rust chunk arithmetic/runner clipping, and
Rust reset publication/locks/explicit repair and shared TLS retry state.
No new per-message allocation, lock or shared atomic is introduced. The latency
change concerns TLS retry readiness; no broader benchmark claim is made.

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

## Publication

Fix commits: C++ `e856fea4b`, Java `7913fccda`, Rust reset `c0f63291c`,
Rust TLS readiness `aaeff5163`, Rust recovery `909ccac24`. The C++ and Java fixes
were also pushed to the private session branch as their checks completed.
Final fetch/rebase is onto `55d895a68` (the initial claim commit); there are no
incoming engine changes. The outgoing range contains only this session's fixes
and record. All 14 changed engine files pass the export scan without findings,
source hashes match the final gates, and whitespace checks pass.

Publication targets private `atrekes/libhft-devel` directly at main. Other
sessions' claim blocks are preserved exactly. The final verification counts
are machine-readable in the archived `verification-summary.json`.
