# FIX session recovery and observation review — 2026-10-09

Review session: `codex-review-session-1009`, isolated worktree on BEELINK2.
Private repository: `atrekes/libhft-devel`. Initial base: `379ad23ac`.

## Reproduced defects and fixes

C++ classic FIX admitted a send batch whose frame-end or timestamp metadata pointer
was null. A later send could dereference that missing metadata. BeginBatch now
refuses both forms before touching the caller's counters or opening the batch.
Tests cover FAST, SAFE and TEST configurations, healthy retry and invalid nested
admission while a healthy batch is pending.

C++ FIXP finalization requested at most UINT32_MAX records for a recoverable gap,
but did not request the remaining chunks. Finalization now tracks the end of the
current request and the three runners continue after that frontier arrives. The
existing public completion-only OnInboundProgress API retains its behavior;
runners use the additive OnInboundRecoveryProgress API. Tests cover both frame
formats, enforcement on/off, three owner roles, signed-boundary crossings,
multiple full Count-sized chunks and the exhausted UINT64_MAX frontier. They
seed the supported public frontier near boundaries and deliver actual replay
headers and application frames, rather than transferring billions of records.

A required FIXP retransmit request whose encode/write failed after its trigger
was consumed could also leave the owner live, with no retry queued. All three
runners now retire the affected owner for zero, negative, partial and encoding
failures. Single/multi acceptors retain the listener and unaffected peers. The
initiator similarly retires when the required finalization Terminate fails after
FinishedReceiving has finalized its logical session ID. Ordinary caller-issued
retryable sends retain their existing policy.

Java audit close used to close the stream immediately if join was interrupted,
while accepted records still belonged to its writer. Close now waits for drain
and final sync, closes the output, then restores the caller's interruption.
Three tests use the public session, actual audit records and a delegating output
gate to verify no interruption, interruption before close and interruption while
joining. Teardown releases the gate even when testing the old failing code.

Java and Rust rejected PossDup messages missing OrigSendingTime, or with a future
OrigSendingTime, before consuming an ordinary matching expected sequence. That
could leave the checkpoint behind and strand recovery. Expected rejected frames
now consume/checkpoint their number. Missing-time Reject precedes continued
recovery; a future-time terminal reject clears recovery before Reject/Logout.
Low/high messages and Logon/SequenceReset control exceptions retain their prior
rules. Tests cover buffered application delivery, chunk continuation, deferred
admin replies and checkpoint failure. Java exercises transcript, worker, TCP and
multiplexed TCP owners and checks persisted QuickFIX-compatible store contents.
Rust uses a checkpoint-refusing store and clone/restore controls, including
queued outbound batch and Reject-hook failure cases.

Rust skipped inbound admin observation for applied SequenceReset, the first high
GapFill, serviced high ResendRequest and low/high Logout paths. The observation
now runs at each admitted branch, with retained body borrowing and the existing
checkpoint ordering. Invalid/discarded/deferred controls remain unobserved.

Rust NoApplication also evaluated clock reads solely to call empty inbound
observer hooks. An object-safe default Application capability preserves existing
implementors; NoApplication disables these three hooks. Injected-clock tests
verify one fewer clock call for application/Heartbeat and two fewer for Reject,
while receive/liveness timestamps and enabled observers remain intact. This is
measured work reduction, not a measured latency-percentile or throughput claim.

## Verification

Three parallel language agents implemented disjoint claimed paths and performed
independent cross-review. Root serialized all heavy builds/tests through the
shared host CPU lease, checking processes and memory, at no more than 16 build
jobs. Builds used the session build root; Java fixtures use TestTempFiles. C++11
and explicit -flto=1 were used for native compilation; Java used JDK21 targeting
Java8, with native loading required and pass/skip reports inspected.

| Regression gate | Original production | Fixed production |
| --- | --- | --- |
| C++ batch metadata admission | 9 failures | 9 pass |
| C++ FIXP recovery and mandatory-send failures | 116 failures, 29 controls pass | 145 pass |
| C++ ASan + UBSan | — | All 154 new cases pass |
| Java audit/PossDup/recovery, including native guard | 14 assertions fail, 12 socket timeout errors, 234 pass | Affected selection: 279 pass |
| Rust admin observation and clock work | 6 failures, 6 controls pass | 12 pass, debug and optimized release |
| Rust PossDup sequence progress | 5 failures, 2 controls pass | 7 pass, covering 42 combinations |

Java's 260-case baseline includes 259 audit/recovery/PossDup cases and the native
loading guard. Its errors are expected missing-response socket timeouts on the
original recovery behavior, not missing libraries or setup failures. The final
279-case selection adds audit-count and checkpoint-failure regression controls.

| Integration gate | Result |
| --- | --- |
| C++ open edition, layering and simulator | 153/153 binaries pass, no reported skips; 85 private binaries excluded |
| Affected private C++ runners and stores | 10/10 binaries pass, no reported skips |
| Java complete development edition | 6,118 unique cases pass, no remaining skips |
| Java complete open edition | 5,650 unique cases pass, no remaining skips |
| Rust full default suite | 1,504 pass; one existing ignored builders documentation example |
| Rust full no-default-features suite | 1,504 pass; the same one ignored documentation example |
| Rust all-target strict Clippy | Pass, warnings denied |
| Rust open-edition session regressions | 19/19 pass, none ignored |
| Export-name scan | 14 changed engine files, zero findings |

The full Java default fork intentionally skips its three counters-disabled cases;
the dedicated counters-disabled fork reruns all three successfully. Unique XML
case counts and remaining skip counts include those successful reruns. Native
tests are required and none are allowed to disappear through a missing library.
The focused Java selection is repeated by the configured forks because -Dtest
overrides their includes; its case count is deduplicated, not multiplied by three.

Rust counterfactual runs combine original session production with the final test
module and final app.rs. The latter is needed for the new capability API test;
the original session never calls that method. Baseline test methods stop at their
first failing assertion; loop combinations are not counted as independently
reproduced baseline failures. Optimized observer evidence preserves its exact
pre-PossDup-extension source snapshot; the observer implementation and clock
regression remain unchanged in the later extension. Final full/default, feature-
disabled and open regression checks bind to the final integrated source.

Both Java Maven invocations reported BUILD SUCCESS. The set -e wrapper copied
both XML report trees and printed both PASS markers, then the outer host-slot
exec returned 143. Its terminating signal was not attributed. A separate report
audit verified all 11,768 unique edition/case results and the successful native
and counters-disabled guards. The wrapper anomaly is retained in
java-full-results.json; no zero exit for that outer invocation is claimed.

The incoming parser experiment 5460c0b4c was reverted by 7e0327c74 before final
integration. The net incoming diff at d9b0ba698 contains only internal benchmark
sources/evidence. All 14 tested source hashes matched after rebasing; C++/Java
source and build dependencies were unchanged. Rust integration ran after rebase.

## Scope and evidence

No full .NET, venue, Windows or all-language conformance run is claimed. Read-only
follow-up candidates, without execution proof or a claimed fix in this batch,
remain in the archived agent notes: C++ callback retirement during finalization,
NotApplied drain scheduling, and Rust's inferred FIXT application dictionary
across reconnection. The reserved .NET borrowed-buffer proposal remains separately on
`s/codex-review-edges-1009-dotnet-proposal` pending its owner's integration.

Engine commits after rebase:

- `42dda8063`: C++ send-batch metadata admission.
- `aa5cc3697`: Java audit shutdown and rejected duplicate progress.
- `163aef772`: C++ FIXP recovery continuation and required-send retirement.
- `f166e4e7e`: Rust admin observation, rejected duplicate progress and clock work.

Evidence archive:
`/home/yann/archive/libhft/reviews/2026-10-09-codex-review-session-1009/`,
with a replica at the same path on HP. It contains original/final regression
logs, all Java XML reports, sanitizer and integration output, agent reviews,
source snapshots/hashes, exact gate scripts, verification-summary.json and a
Git bundle/patch from publication base `d9b0ba698`. The optimized observer source
snapshot is preserved separately from the final PossDup extension. The optional
small public-API clock probe in scratch was prepared but never run: the optimized
unit-test build completed successfully first.

Both archive manifests and final-source hashes are verified before this session's
worktree/build outputs are removed. Other sessions and the reserved .NET proposal
branch are left in place.
