# QuickFIX recovery: complete records are not torn appends

C++'s two QuickFIX file profiles now refuse a complete frame that exceeds the
configured message slot instead of deleting its index entry during recovery.
The C++ profile also checks checkpoints before index repair and refuses complete
sequences with no 32-bit successor. Rust and Java refuse complete C++-format
records whose sequence cannot fit their session counters. Java's two profiles
also refuse complete frames above their parser capacity before any tail repair.

The correction is entirely on recovery paths. It adds no send-path lock or
allocation, and no latency improvement or production incident is claimed. This
follows the separate signed QuickFIX/J boundary correction in 895cd376b.

## Failures reproduced

Baseline runtime: c8aec7f98, with new tests only. Both C++ unit binaries aborted
because recovery incorrectly succeeded for a 257-byte stored frame with 256-byte
slots. The diagnostic recorded the actual physical index change:

| Profile | Header bytes before incompatible open | Header bytes after accepted open |
|---|---:|---:|
| C++ text index | 8 | 0 |
| QuickFIX/J binary index | 16 | 0 |

The body still held the committed payload and the checkpoint still advanced
past it, but the resend index no longer held its entry. A smaller consumer
configuration is not evidence that the writer left a torn append.

Rust's new test also failed with an actual empty header where the complete
`4294967296,0,1 ` entry had been. Its C++ parser tried u32 first; the valid wider
value was treated as an unparsable final append and removed. Java failed both
new regression methods (20 passed, two failed): it accepted a complete C++
sequence above Integer.MAX_VALUE and a complete frame above parser capacity.
The initial standalone Java test compile lacked its existing TestTempFiles
utility on the classpath; after correcting that fixture, the unchanged runtime
produced those two failures. That compile error is not an engine defect.

## Compatibility boundaries and correction

The pinned modern QuickFIX C++ reader uses unsigned 64-bit sequence fields.
Its FileStore.cpp scans the text index with SCNu64. The live upstream oracle
now explicitly replays complete entries at 4,294,967,295, 4,294,967,296 and
18,446,744,073,709,551,615 before libhft refuses them without changing their
header. These are valid upstream records, not malformed numeric garbage.
The pinned reader source is 386ce46e917ae494ab6e90b1be90fd421cdbe3f9.

libhft's C++ and Rust classic counters remain unsigned 32-bit; Java's remain
signed 32-bit. The fix does not narrow the C++ profile to Java's range. A next
sequence of UINT32_MAX remains valid for C++/Rust, as does Integer.MAX_VALUE for
Java, but a frame at the owner's terminal value has no representable successor.
Checkpoint values beyond the owner's range must also be refused before repair.

C++ moves checkpoint loading ahead of index scanning and separates complete
sequence/slot incompatibility from final incomplete or dangling entries. Rust
parses a complete C++ index sequence as u64 before checking its own range.
Java checks complete sequence/capacity incompatibility before its generic
invalid-final-record recovery branch. Its existing malformed/overflow decimal
refusals are retained; it does not adopt a wider counter.

Regressions cover intact and torn suffixes, oversized sender/target checkpoints,
terminal/wider index sequences, byte-for-byte preservation on refusal and the
last supported successor. Reopening with sufficiently large C++ slots still
works. Existing tests continue to repair a genuinely incomplete suffix and a
final dangling entry whose body did not reach disk. Failed opens release the
store's writer fences so a compatible owner can retry.

## Verification

- Both C++ physical-layout unit binaries pass.
- The real pinned QuickFIX C++ and QuickFIX/J live store handoff/crash-recovery
  smokes pass, without skips. The C++ smoke additionally proves all three wider
  upstream sequence records are replayable before libhft refuses them.
- Rust development library: 958 passed, zero failed or ignored.
- Rust open library: 843 passed, zero failed or ignored.
- Java development storage/native selection: 109 passed, zero failed or skipped.
- Java edition-neutral open selection: 93 passed, zero failed or skipped.
- Export scrub: seven changed engine files, zero findings.

Java includes the owner's 16 recovery/HA tests in development; its two pro-only
HA methods are not counted as open passes. Both editions rebuild JNI through
the normal Maven lifecycle. Rust counts are cargo test --lib, not integration
or doc-test coverage. They include upstream body-boundary tests; this review
adds one Rust test method, two Java methods and the C++ compatibility groups.
This is not a full C++ correctness sweep or a NIC benchmark. Checks ran under
functional CPU leases, not a claimed quiet-host latency experiment.

## Evidence and remaining scope

Sources, baseline test patch, byte-loss diagnostics, full logs and publication
verification are retained in
`~/archive/libhft/quickfix-complete-record-review-20261003/`.
The .NET QuickFIX/J checkpoint defect remains reserved by the structure owner;
its separately reproduced reader failure is recorded in the preceding review.
No .NET or NIC-installed binary is changed by this correction.
