# Callback, recovery and reconnect lifecycle review — 2026-10-09

Session: `codex-review-lifecycle-1009`. Initial base: `bf275d694`.
Publication base: `1091b38fa94d`.

## Changes

- **C++ FIXP callback continuation:** the initiator, single acceptor and multi-acceptor
  now distinguish an unchanged connection from callback-driven Close/Init/replacement.
  An old callback cannot finalize, fault or close its replacement. Local Terminate stops
  finalization continuation, and a nested send/store failure retains its terminal result.
  The multi-acceptor also distinguishes an explicit close from a stale WOULD_BLOCK result.
- **C++ FIXP recovery scheduling:** both enforced and legacy idempotent finalization gaps
  send one Count-sized NotApplied chunk per pump. A bounded input poll services eligible
  head controls, EOF, read errors and silence while notifications remain. Application frames
  and controls that could open a new gap retain FIFO order. An early Terminate does not
  persist a finalized-session marker before FinishedReceiving has actually been sent.
  Public initiator Connect discards the prior connection's pending notifications and clocks.
- **Java FIXP restart:** successful initiator start discards old notification queues and
  liveness deadlines, including after failed required sends. Refused starts preserve the
  current connection. Existing outbound sequence, replay records and completed-finalization
  marker behavior remain covered by controls.
- **Rust FIXT dictionary lifetime:** reconnect releases a table inferred from the preceding
  Logon so the new DefaultApplVerID selects the appropriate table. Explicit configuration
  survives. `set_app_dictionary` can pin the same table negotiation inferred: an assignment
  of the identical pointer to the existing public field is intrinsically unobservable, and
  that limitation is documented. Unavailable dictionaries remain unavailable; they do not
  silently reuse a previous connection's table.

Code commits: `c7e44b223`, `08a142cfc`, `d801184d7`.

The latency change is a bounded-work guarantee during recovery, not a measured percentile
claim. Each pending step reads at most once and emits at most one NotApplied chunk (plus
FinishedReceiving when it completes). Regression tests include the full unsigned range.
The redundant initiator result reset initially proposed on every application callback was
removed before final validation. Ordinary callbacks retain the necessary lifecycle guard.

## Verification

All heavyweight work was serialized through `tools/host-cpu-slot` and a process/memory guard;
build parallelism was capped at 16. Outputs stayed under the session build root. C++ used
C++11, native LTO used `-flto=1`, and Java used JDK 21 with the project's Java 8 target.

| Gate | Result |
|---|---|
| C++ regression against original headers | 134 pass, 302 fail out of 436 |
| C++ corrected regression | 436 pass, 11,498 checks |
| C++ ASan + UBSan regression | 436 pass, no sanitizer findings |
| Standalone runner header compilation | all three headers pass |
| C++ full open edition | 154 binaries pass, zero reported skips; 85 private binaries excluded |
| Affected C++ private tests | 10 binaries pass, zero reported skips |
| Java original restart regression + native guard | 45 cases: 28 failures, 17 passes; zero errors/skips |
| Java focused corrected tests | 351 pass, zero skips |
| Java full development edition | 6,162 unique cases pass, zero remaining skips |
| Java full open edition | 5,694 unique cases pass, zero remaining skips |
| Rust original default / service-pack regression | 7 pass + 3 fail / 7 pass + 4 fail |
| Rust corrected default / service-pack / no-default regression | 12 / 13 / 12 pass, zero ignored |
| Rust full default and no-default suites | 1,516 pass each; one existing ignored builder documentation example each |
| Rust strict all-target Clippy, default and service-pack feature | pass |
| Rust open edition, service-pack reconnect regressions | 13 pass, zero ignored |
| Export scan | 10 changed engine files, zero findings |

Java native loading was required. Each full Maven run intentionally skipped three
HftTelemetryCountersDisabledTest cases in its default JVM and reran all three successfully
in its dedicated counter-disabled JVM. Final XML reports, required-native guard, raw skip
lines and driver exit codes were audited; no remaining skip is counted as a pass.
Rust original-header counterfactuals use the final wire tests unchanged and omit only the
two tests that call the newly added setter. The additional service-pack case is feature-gated.

Initial probes caught a missing direct SocketSession include in the acceptor headers,
a stale multi-peer explicit-close result and incorrect listener-close fixture expectations.
They were corrected before the final complete gates. Failed attempts remain in the evidence.

## Publication and evidence

Rebased on `1091b38fa94d` after the complete suites. Incoming changes affected benchmark
reporting and retained data, not engine sources or engine build tools. All ten tested source hashes
matched after rebase; the verification audit, export scan and focused C++ replay were repeated
on the publication tree. The outgoing range contained only this session's changes.

Evidence: `/home/yann/archive/libhft/reviews/2026-10-09-codex-review-lifecycle-1009/`, mirrored
to HP at the same path. It includes original/final sources, baseline failures, complete suite
logs and XML reports, source hashes, review notes, gate drivers and a Git bundle. The archive
manifest and final-source manifest are verified on both machines before session cleanup.

This pass does not claim a full all-language conformance run or hardware latency benchmark.
The earlier .NET borrowed-span proposal remains at
`s/codex-review-edges-1009-dotnet-proposal`, commit `9d7765a3ff0bfdb9ad33c4ace3c2aa249d63c2b6`;
the broad `engine/dotnet/**` ownership reservation remains in force, and this pass did not edit it.
