# QuickFIX/J sequence range and non-mutating recovery refusal

C++ and Rust QuickFIX/J file stores now refuse frames whose successor cannot
fit QuickFIX/J's signed int counter. Checkpoints may reach 2,147,483,647, but a
frame at that number cannot be saved. Java already refused that save; its
recovery ordering is now corrected, along with C++'s, so an incompatible
checkpoint or terminal complete index entry is rejected before torn-index repair.
Rust's recovered index and checkpoints now enforce the same signed range.

The .NET defect is reproduced but not fixed by this commit: its source remains
reserved by the structure owner. The separate classic Java async-journal call
site was handed to its current owner, who published it in ad1f2b2b5 before this
correction landed. It is not an overlapping source change in this commit.

## Why the boundary matters

The pinned QuickFIX/J 2.3.1 reader parses both UTF decimal checkpoints with
Integer.parseInt and reads binary header sequences as signed int. A store may
have a valid binary index for frame 2,147,483,647 yet write an unreadable next
sender checkpoint of 2,147,483,648. An inbound/outbound checkpoint setter can
produce the same incompatibility without saving a frame.

The real QuickFIX C++ profile is different: its pinned modern SEQNUM is uint64
and its decimal checkpoint layout is wider. This correction does not clamp
C++-profile stores to QuickFIX/J's range. Rust's generic 64-bit journal is also
unchanged. Java's own classic counters remain signed int in both file profiles.

## Baselines and fixes driven

The C++ store producer ran against the actual pinned QuickFIX/J reader, not a
replacement parser. Four baseline stores failed with NumberFormatException:
the terminal frame, a larger frame, and outbound/inbound oversized checkpoints.
The last valid frame, 2,147,483,646, was the successful control. With the C++
fix, all four invalid writes are refused and all five stores remain readable.

| Surface | Regression result before correction | Verified corrected scope |
|---|---|---|
| C++ QuickFIX/J | New unit aborts on terminal save; live reader rejects 4 of 5 stores | Unit binary, all 5 live-reader cases, normal live handoff/crash-recovery smoke |
| Rust QuickFIX/J | 4 of 6 new tests fail; boundary and wider C++ profile controls pass | 955 development / 840 open library tests pass, none failed or ignored |
| Java QuickFIX stores | 2 of 20 tests fail because rejected opens repair the index | Storage/native parity selection passes in both editions, no failures or skips |
| .NET QuickFIX/J | Real reader rejects 3 of 5 direct-store cases | Reproducer retained; owner correction pending |

C++ validates the checkpoint before loading or repairing the index, validates
every complete indexed sequence, and validates setters before disk or memory
state changes. Rust rejects the terminal frame on save/recovery and validates
inbound and recovered checkpoint bounds. Java loads sequence checkpoints before
the index and checks terminal complete entries while scanning, before repair.
Regressions compare the original index/body/checkpoint bytes and verify writer
fences are released after failed recovery. The last valid successor still opens.

The unchanged-runtime baseline for the new unit regressions was 560f86421, with
tests first committed privately as b48c68993. Those baseline and correction
commits are combined before publication, so main never receives a deliberate
red-test-only commit. The live C++ oracle baseline was retained separately.

## Verification scope and exclusions

The first corrected Java selection passed 94 tests in each edition, including
three proposed async-journal session tests. That call-site patch was then parked
for its current owner and the three dependent tests removed from this change.
The publishable selection has 91 tests in each edition, with a normal Maven JNI
build and no failures or skips. The corrected QuickFIX tests themselves number
20. The extra three session tests and their green output remain archived, not
silently counted as published coverage. After integrating ad1f2b2b5, the Java
development selection including its 16 owner recovery/HA tests passed 107 tests;
the edition-neutral open selection passed 91. Both had zero failures or skips.
The two pro-only HA methods were exercised in development, not reported as
open passes. The archived 16-case standalone session probe also passed against
freshly compiled integrated sources in both editions: zero mutations and zero
incompatible acceptances.

Rust counts are full `cargo test --lib` runs, not integration/doc-test claims.
The initial corrected runs passed 951 development / 836 open tests; after
integrating the owner's classic shutdown corrections at 8461be40e, the final
counts are 955 / 840. The four additional tests are upstream work, not authored
by this storage review. Focused Java post-integration verification also passed
all 27 QuickFIX store and direct journal-range tests.
The C++ live smoke includes the pinned Java handoff and torn-index crash
recovery. This is not a full C++ correctness sweep. The recovery-review owner
corrected the borrowed-tail regression test in 193680ce5, distinguishing a
malformed complete body from a genuinely oversized partial frame. Functional checks
used shared or isolated CPU leases; overlapping shared work was allowed for
isolated functional runs. These were not quiet-host latency measurements.

No new send-path lock or allocation is introduced. No latency improvement or
observed production outage is claimed. The failure demonstrated is an unreadable
interop checkpoint or modification of an incompatible input during refusal.

## Reserved follow-ups, with runnable evidence

The .NET probe compiles the actual unchanged QuickFixJFileStore.cs with only
minimal IOutboundMessageStore/ISessionStateStore contracts. It is a direct store
test, not a managed-provider integration test. Saving int.MaxValue and setting
either checkpoint to int.MaxValue + 1 produced the three unreadable stores.
A larger frame was already refused, and the last valid frame succeeded. Source
tracing also shows ReadUtfLong accepts larger longs and recovery repairs a torn
header suffix before validating complete entries. The exact source snapshot,
project, producer, reader and confirmed output are retained; the request names
the source and compatibility test files in docs/WIP_AGENT_CLAIMS.md.

For the classic Java async journal, the proposed six-argument FileStore
constructor wiring and closeWithoutCheckpoint fallback drove the existing
16-case probe to zero mutated inputs and zero incompatible acceptances. Those
cases cover modern/legacy checkpoints, inbound/outbound overflow, torn/intact
tails and a later in-range reset. After the recovery-review session claimed the
same nested method in d0be46e92, this session removed its unpublished overlapping
source/test changes and retained a handoff patch. The owner then published
ad1f2b2b5 with the same wiring and a separate 16-test recovery/HA suite. The
earlier journal record is updated to name that completed call-site change;
post-integration verification is retained separately.

## Retained evidence

Archive: `~/archive/libhft/quickfixj-range-review-20261003/`.
It contains baseline/fixed logs, live producer/reader sources, the parked Java
session patch/probe, the .NET project/source snapshot, source hashes and final
publication verification. The pinned QuickFIX/J core jar SHA-256 is
`d063713127ae8fe86989065d183d1b0db43b5aa0d41c377770020681ea3a4593`.
Its checkout is managed by fetch-conformance-data and was not modified.

The frozen prepared NIC bundle remains separate from these freshly rebuilt JNI
artifacts. This review performed no root staging or NIC latency measurement.
