# Recovery store and learned-peer review — 2026-10-10

All final gates below completed successfully. The evidence verifier checks exact test inventories, pass/fail/skip lists, source hashes, native loading, and the preserved original-code comparisons. Failed intermediate attempts are retained separately.

Session `codex-review-recovery-1010`; base `21c6872217d0a5b84e30db101566305841439007`. Work is isolated in the session worktree. Parallel agents reviewed C++ and Java learned-peer identity/store admission and Rust Logon admission; root reviewed journal repair and cross-reviewed the changes. Root runs build/test jobs under the host's single heavy-job lease, with compilation capped at `-j16`. Final LTO-enabled JNI and C++ builds use `-flto=1`; the focused learned-identity gate uses `-O2`, and sanitizer gates use `-O1` with ASan/UBSan. Reserved .NET paths are outside this pass.

## Changes

- `955b17e18`: C++ journal repair collects pending slot indices once and sorts by sequence and slot before writing owed frames. Previously each next frame rescanned the entire window, producing quadratic owner lookups for descending backlogs. The new index costs four bytes per configured slot, allocated at `Start` and released with the journal; `Sync` and `Close` add no allocation. The caller does not mutate the owner window during repair; the file gate excludes writer acknowledgements, and `Close` joins the writer before repair. Lowest valid duplicate ownership, missing/empty views, write-failure boundaries, and checkpoint ordering retain their existing behavior.
- `7fe57fc91`: C++ learned peer identity becomes fixed after admission succeeds and any learned-store binding completes. `OnConnected` cannot relabel retained history; a fresh configuration and `Init` can learn a new peer. Candidate identity remains visible to existing admission callbacks and diagnostic routing, but a pre-binding veto does not latch it. Initial SenderSubID values exceeding the 35-byte settings representation are refused instead of being silently cleared and selecting a different journal. Same-peer Logons avoid redundant learning/header rendering.
- `aa30585a7`: Java classic FIX pins learned CompID/SubID to the admitted connection and journal, including checks for an oversized wire identity sharing the stored 64-byte prefix. Mandatory heartbeat, FIXT version, and Logon time checks precede application veto, learning, and initial binding. Rejected initial candidates use a temporarily routed reply encoder without publishing SessionID or opening their peer journals. The single-connection binder finishes acquire/refresh/frontier reads locally before publishing the store and then learning identity; a failed initial bind leaves no closed published store or permanently pinned candidate. A fresh accepted TCP connection may learn a different peer and its own journal; transcript stop/start retains an already admitted identity/store. Recovered low-sequence and 789 checks remain after store opening; their established policy is unchanged.
- `a498c03be`: Rust separates mandatory Logon contents/identity checks, configured field admission, application veto, binding, and commit. Learned-store candidates validate before binding; successful binding pins exact peer identity, including a valid low replay that binds without establishing. Ordinary accepted peers are pinned independently of store flavor. Rejected reset/veto paths no longer prematurely commit their inbound frontier or inferred dictionary. Ordinary ordered strict field Reject retains its once-consumed frontier and existing 369 response. Accepted Logons validate/callback once; existing discarded low replay and established high-Logon policies remain. Heartbeat validation borrows wire bytes, target buffers are reused, and peer identity is retained once per session.

The subsequent Rust commits complete diagnostic routing, sequence preservation, and capacity checks. Cold admission diagnostics temporarily route to the unadmitted candidate, or retain the previously pinned peer, and restore public routing on success/error. Their exact encoded size is checked before fixed-buffer writes and sequence consumption. Before learned-store binding, diagnostics retain the highest claimed admin sequence across reconnect; binding persists a checked empty marker above recovered history before a duplicate can be discarded or a reply emitted. This prevents store recovery from rewinding an already used transport sequence. Accepted reset deliberately starts a new epoch; another candidate may inherit harmless admin gaps. Failed marker persistence retires with the existing store-error policy before inbound consumption/output.

The same follow-up checks identity capacity against the built-in administrative envelopes at maximum sequence widths, including future 369, then checks the prospective Logon before callback/store binding. Local and peer-requested reset paths check their response capacity before mutating the epoch. The limit follows the fixed buffer's available capacity, rather than introducing a 35/64-byte identity limit. Normal application send/receive paths gain no temporary diagnostic routing work. The final 54-case matrix and broad suites validate these changes.

The first broad Rust run also exposed two incomplete existing SubID fixtures: they configure a local SenderSubID without sending the corresponding inbound TargetSubID(57). Cross-review confirmed that C++ and Java already require that match, including a learned first Logon. Fixture amendments complete that setup while preserving their wire/replay assertions; final broad verification passes.

## Completed verification

| Gate | Final preserved result | Unchanged final tests against saved original production |
| --- | --- | --- |
| C++ journal repair | 33 pass, zero fail; optimized and ASan/UBSan | 25 pass, 8 fail |
| C++ learned identity | 906 pass, zero fail; optimized and ASan/UBSan | 447 pass, 459 fail |
| Java learned identity, phase 2 | 47 pass, zero failures/errors/skips; includes required native guard | 10 pass, 37 assertion failures, zero errors/skips |
| Rust Logon admission | 54 pass in default, service-pack, no-default and open service-pack modes; zero failures/ignored cases | 9 pass, 45 fail in default and service-pack modes |

C++ original identity failures are exactly 405 changed-peer cases and 54 oversized first-SubID cases. The 906-case matrix covers NullStore, MemoryStore, real FileStore, validation/mode combinations, ordinary/reset/reconnect Logons, same-peer controls, fresh `Init`, invalid-first-candidate retries, SubID boundaries, and pre-binding vetoes. Real journals are reopened to check replay bytes and sequence frontiers. The fresh-`Init` case exercises the reusable acceptor-slot lifecycle through the protocol API; it does not itself open sockets. A required Logout on an unopened/closed FileStore retains the existing terminal store-fault policy; the FileStore veto/retry control uses silent owner refusal.

C++ broad verification is complete: all 155 open-edition binaries pass, with zero reported skips and 85 private binaries excluded by edition design. Per-binary logs retain exit status/output; the open core gates and simulator refusal/serve checks pass. The two affected private binaries, `test_fix_session_reliability_pro` and `test_fix_store_selection_pro`, also pass with zero reported skips. Sanitized focused logs contain no ASan/UBSan/leak diagnostics.

Java's 46 regression cases use public transcript, dedicated TCP acceptor, and multiplexed TCP acceptor APIs with actual async-file journals and `TestTempFiles`. They check identity/routing, callback counts, absent unadmitted journals, exact original bytes and recovered frontier, same-peer reset, valid retry, and independent new-connection storage. The locked-store case holds one peer's journal open, verifies failed reset admission leaves its bytes and template identity intact, then admits a different peer after explicit lifecycle retry. Both phase-2 XML inventories were independently counted: 47 unique cases, and `NativeLibraryRequiredTest` passes with `libhft.require.native=true`. No injected post-open refresh exception test is claimed; the existing static provider does not offer a narrow per-test hook.

Java full verification is complete: development passes 6,278 unique cases and open edition passes 5,810 unique cases. Both report `BUILD SUCCESS`; root independently counted final XML and successfully ran the verifier's complete Java checks. Both require native loading and have zero failures, errors, or remaining skips. The three telemetry-disabled cases skipped in the default fork pass in their dedicated counters-disabled fork.

## Rust verification and retained intermediate failures

The final inventory contains 54 focused cases: 24 initial admission cases, 12 routing/compatibility cases, and 18 frontier/capacity cases. All pass in default, service-pack, no-default, and open service-pack builds. The unchanged final tests against original production produce nine passes/45 failures in both default and service-pack builds, with identical case outcomes. Against the pre-routing implementation they produce 30 passes/24 failures, keeping all earlier 24 cases green. Against the intermediate routing implementation they produce 41 passes/13 failures, keeping all earlier 36 cases green.

Both complete Rust suites, default and no-default, pass 1,590 cases with zero failures. The only ignored case is the existing documentation example `src/builders.rs - builders (line 9)`. Both full suites include the exact 54-case final inventory and six earlier high-Logon regressions. Those six also pass in open service-pack mode. Clippy passes with warnings denied, all targets, in default and service-pack configurations.

The intermediate 36-case routing smoke passed before deeper probes found two more defects. A real-FileStore sequence probe produced four failures/four controls, reproducing outbound sequence reuse after pre-binding diagnostics. A valid oversized-identity probe produced two slice-bound panics/one normal-identity control. Their source snapshots, logs, and JSON remain under `evidence/rust-phase2`. The final regressions cover both failures, including recovery from disk, reset/refusal, reconnect, marker-write failure, exact buffer boundaries, future 369 growth, and basic-validity Reject capacity.

The first broad Rust attempt failed at its library stage with 1,397 passes/two failures: `sub_identities_are_emitted_in_header_position_and_omitted_when_unset` and `the_369_rides_where_cpp_writes_it_and_a_replay_routes_in_its_header`. Later stages in that driver were not reached. The missing inbound TargetSubID setup was corrected as described above, preserving the original output assertions; the final complete suites pass. Phase-1 focused results (24 passes in three modes; original five passes/19 failures) are retained as intermediate evidence, not counted as final-source validation.

The next full suites passed, but Clippy found two collapsible nested `if` statements. Both were combined using short-circuit `&&`, with test bytes unchanged. The source, passing suites and failed Clippy log are retained under `evidence/rust-pre-clippy`. Final focused/full/Clippy/open gates were rerun after that style correction; original/intermediate comparisons remain valid because their production and test bytes did not change.

All eight reviewed engine files match their tested SHA-256 manifests. The refreshed export scan reports eight files and zero findings. `summarize-verification.py` independently checks the complete preserved evidence before writing `verification-summary.json`.

Earlier attempts are preserved, not counted as final gates. Rust's first focused smoke had 23 passes/one failure: the new malformed-field reset fixture placed 141 after the malformed tag, so parsing did not reach the reset marker. Moving 141 before the malformed bytes corrected the fixture; production was unchanged for that correction. Java phase 1, before the locked-store amendment, recorded original 36 failures/10 passes including native, and a broad PureJava selection of 1,926 passes with zero errors/skips. Those XML/logs are retained under `evidence/java-phase1`; the current phase-2 comparison supersedes their focused counts. Baseline drivers restore final production exactly in `finally` and retain unchanged final regression sources.

## Bounded journal experiment

The journal measurement uses fresh files, a queue capacity of two, one-byte frames, a parked worker, and descending slot sequences to force synchronous owed repair. Each source version has nine raw samples: three repetitions at each size, compiled from the same benchmark with `-O3 -flto=1 -march=native -std=c++11`. Timing surrounds `Sync`, including queue drain, repair, checkpoint writes and fsync. File creation, queueing, `Close`, and cleanup are outside the timed interval. This single-host stressed-backlog experiment does not estimate normal per-message latency, production throughput, or latency percentiles.

| Configured slots | Original median Sync (µs) | Final median Sync (µs) | Original owner probes/run | Final owner probes/run |
| ---: | ---: | ---: | ---: | ---: |
| 1,024 | 2,707 | 2,157 | 522,753 | 1,022 |
| 4,096 | 11,141 | 1,869 | 8,382,465 | 4,094 |
| 16,384 | 145,634 | 2,058 | 134,193,153 | 16,382 |

Every sample reports successful `Sync`. Independent correctness tests check repaired file contents, ordering, checkpoint, duplicate ownership, missing/empty owner views, and reopened recovery before enforcing the owner-probe budget. The lower probe count directly demonstrates removal of the quadratic selection work; the timing table makes only the bounded experimental claim above.

No Java/Rust production latency timing is claimed. Java's ordinary application path is unchanged; temporary routing may allocate for a refused unadmitted candidate. C++ avoids redundant same-peer learning/header work and Rust avoids redundant Logon allocations, without a percentile or end-to-end benchmark claim. Read-only inspection of additional C++ FIXP send/replay/token lifecycle and frame-append paths produced no further confirmed defect in this pass; it is not an all-path correctness guarantee.

## Publication and retained evidence

Record: `internal/docs/bench/RECORD_2026-10-10_recovery-store-review.md`.

Archive destination: `/home/yann/archive/libhft/reviews/2026-10-10-codex-review-recovery-1010`; mirror destination: the same path on `hp`. The archive will retain original/final sources, drivers, raw logs/XML, failed and intermediate attempts, source hashes, benchmark samples, cross-review notes, the final verification summary, and a prerequisite-based history bundle. Local and remote manifests are verified before worktree/build cleanup.

Before publication the session rebases on current `origin/main`, verifies that incoming changes leave the engine unchanged, and rechecks all eight source hashes and the evidence verifier. Only this session's commits are pushed. No Windows or all-language conformance result is claimed; reserved .NET paths remain untouched.

Additional Rust commits:

- `0fccccd1c`: fix(rust): route refused Logons without publishing peer identity.
- `fdd5dad45`: fix(rust): preserve pre-bind sequences and bound Logon capacity.

Publication base: `834dece07541d682220f82e05eed5f6f274ea38e`.
