# Managed FIX recovery across sparse sequence ranges

The .NET managed recovery path walked every numeric sequence in a missing
range before emitting a coalesced gap fill. A large persistent sequence
frontier with few retained messages could therefore occupy the session owner
for an extended period, delaying timers and other peers. Built-in stores now
skip holes through their existing retained sequence indexes. The same
32-case reproduction passes with the correction; the unchanged runtime
passes 16 small-range controls and hits the watchdog in all 16 large-range
cases.

## Reproduction

The baseline runtime is `9db822088b500f603f0af5182f972f80e2797402`, with
the test and driver additions captured in its retained patch and manifests.
The ordinary SDK 8.0.131 ProjectReference build opens a real TCP acceptor
against each of FileStore, the durable mmap store, QuickFIX C++ FileStore
and QuickFIX/J FileStore. No native session, reflection or I/O fault is used
in the wire reproduction.

Each store is seeded through its own append and checkpoint APIs, closed,
then reopened by the managed session. The two histories are an empty
journal and a sparse journal containing application messages at sequences
2 and `high`, a Heartbeat at 3, and a stored application frame with a bad FIX
checksum at 5. The checkpoint is `high+2` outbound and 1 inbound.

Recovery uses either ResendRequest or NextExpectedMsgSeqNum(789) on Logon.
The small-range controls use `high=128`; the large cases use
`high=1,000,000,000,000`, except QuickFIX/J, whose signed 32-bit sequence
format limits that case to `high=1,000,000,000`. Each child has an external
four-second watchdog. A timeout counts only after the child has entered the
public-session recovery drive, not during its build or startup.

All 16 baseline controls complete and all 16 large cases hit the watchdog.
At `7503dc9f4ac7fb3594e90a942c095b94922c97f3`, all 32 children complete
with zero timeouts. Both builds report zero warnings and errors, and both
retain unchanged source and driver manifests throughout their runs.

The wire assertions verify the initial Logon sequence, replay order,
PossDupFlag, OrigSendingTime, application Text and gap-fill frontiers.
An empty journal produces one gap fill. A populated journal produces five
frames: gap 1 to 2, application 2, gap 3 to `high`, application `high`,
and the trailing gap. The Heartbeat and corrupt record remain holes.
Tag-789 recovery includes the freshly sent Logon in its final gap; the
explicit request ends before that Logon. A subsequent TestRequest receives
a fresh Heartbeat at `high+3`, proving recovery did not advance the fresh
outbound counter. The session remains logged on.

## Correction

An internal optional `IReplaySequenceIndex` capability selects the smallest
retained nonempty sequence within inclusive bounds. MemoryStore, FileStore
and the durable store use their existing sequence/length rings; the two
QuickFIX layouts use their existing offset dictionaries. No disk reads,
new sorted index or append-path bookkeeping is required for that lookup.

The session caches the capability only when its store changes. A nearby
record still gets the existing direct lookup; short index prefixes remain
scalar, avoiding a full retained-index scan for every hole in an alternating
dense history. Long empty stretches scan retained metadata rather than
each numeric sequence. A storeless range becomes one gap fill immediately.

An indexed record is only a candidate. The existing frame, checksum,
headroom and application-type checks still determine whether it is
replayable. Invalid hints retain the scalar fallback; custom store providers
without the internal capability keep their prior behavior. Journal formats,
session checkpoints, public store APIs, native ABI and ordinary send/receive
paths are unchanged. No lock or allocation is added to candidate lookup.

The cost still depends on retained metadata, not a fixed elapsed-time budget:
a history with many non-replayable records can require repeated index scans.
The correction does not claim constant-time recovery for arbitrary history
sizes or a throughput improvement on the ordinary message path.

## Regression coverage

The official suite adds 16 wire cases at a finite `high=100,000`, so a future
regression fails normal assertions without making CI walk trillions of
sequences. Twelve run in the open edition and four durable-store cases run
only in the pro test root. The independently timed driver retains the huge
range watchdog cases.

Fourteen index checks cover non-monotonic writes, duplicate replacement,
ring eviction, reset, empty slots, inclusive endpoints through `long.MaxValue`,
invalid hints and custom-store fallback. A finite retained-record oracle
checks candidate selection instead of iterating the numeric range. Warm
lookup loops check zero current-thread allocation. FileStore candidate reads
remain usable after a flusher failure and refuse a disposed store, matching
its replay-read contract.

## Development and open qualification

After rebasing onto the native transport, strict configuration validation,
package metadata and managed socket-idle polling changes, revision
`ce6855b6cb3c40480ee0fac124ab38f686248443` passes with SDK 8.0.131
and VSTest 17.8.0. Both native editions are freshly built with GCC toolset 14
and export 209 hftnet entry points. The open compile check builds 144 open
sources without 67 private sources and verifies six refused feature boundaries.

| Run | Passed | Failed | Skipped |
|---|---:|---:|---:|
| Development suite | 2409 | 0 | 3 |
| Open suite | 2178 | 0 | 2 |
| Development native pool with two workers | 3 | 0 | 0 |
| Open native pool with two workers | 3 | 0 | 0 |
| Focused wire and index regressions | 30 | 0 | 0 |

The separate native-pool runs close the default suites' pool-only broadcast
skip and also check listener release, restart, status and connection IDs.
The remaining distinct skips are the explicit live bypass-receiver check
and SBE multicast loopback. There is no native-library availability skip.

All nine required guards ran and passed with zero skips, including the native
ABI/export table, public-package checks and all open .NET tool projects.
This is a selected 9-of-361 conformance slice, not a full conformance-suite
verdict. The eleven changed engine files have zero public-scan findings.
Source and driver manifests remain unchanged through the complete run.

This qualification uses an isolated-core lease with shared work excluded.
Its authority is `verification-sdk8-idle-serial/` in the archive.
The earlier complete run at
`2a27cc694aa05c12bcca7550a4516924122ce005` passed 2390 development and
2159 open tests before socket-idle polling landed; it is retained in
`verification-sdk8-config-integrated/`.
The earlier complete run at
`4024166c138aef0b219f089430e52adb9a67453d` passed 2362 development and
2131 open tests before strict configuration validation landed; it is retained
in `verification-sdk8-integrated/`. The complete run at
`83cbb3c195028ad932c906a2b66078f84c8edae6` passed 2360
development and 2129 open tests with the same skip counts, native-pool checks
and nine guards. It precedes the native transport change and is retained in
`verification-sdk8-quiet/`, not substituted for the final integrated run.
The first `verification-sdk8/` attempt was stopped during the initial native build
after an uncoordinated Rust test overlapped startup; its exit is 143 and it
provides no test-suite verdict. Its logs remain separate, not overwritten by
the complete run. These are functional qualifications, not NIC measurements.

### Idle CPU-control fixture correction

The first integration with the new socket-idle polling tests, at
`76ea77b9318a59494fa405c0b954080b7a47af6b`, finished its development suite
with 2407 passes, two failures and three skips. Both failures were the
spinning CPU controls: they received only 5 and 7 ticks over a 600 ms window
while other parallel test collections ran busy session owners. This attempt
stopped before the open suite and guards; its unchanged input manifests and
logs are retained in `verification-sdk8-idle-integrated/`.

The fixture now uses an xUnit collection with parallelization disabled.
Its thresholds and production idle code are unchanged. In the complete
retry, the spinning/parked totals are 60/0 ticks for plain TCP and 57/0 for
TLS. A quiet host reservation alone did not isolate the test process's
parallel collections. This is a fixture correction, not a runtime lock or
a NIC latency result.

## Publication supplement

Upstream subsequently moved the 56 managed-only imported-admin cases into
112 managed/native provider cases and added 56 corresponding C++ cases.
At `6ba8c518e01874b8ce1255c0bb6737ab39f685f4`, the bounded supplement in
`publication-imported-parity/` passes:

| Check | Passed | Failed | Skipped |
|---|---:|---:|---:|
| Development imported replay plus sparse wire/index selection | 142 | 0 | 0 |
| Open imported replay plus sparse wire/index selection | 137 | 0 | 0 |
| Conformance-stage manifest unit tests | 161 | 0 | 0 |
| Earlier standalone classification probe | 56 | 0 | 0 |

The freshly built open C++ FileStore session binary also passes, including
its two added 28-case imported-admin loops. Both added engine fixtures have
zero public-scan findings. The earlier standalone probe builds with zero
warnings and errors.

Comparison against the complete qualification's manifest permits only the
changed C++ fixture, replacement .NET fixture and two conformance registry
files. All C++/.NET runtime source bytes are unchanged. The nine selected
guard constructors are also AST-identical; the registry change extends two
other provider-parity stages. `integration-equivalence.json` records this
comparison, and `source-inputs.json` is rechecked after the supplement.
The earlier full-suite and nine-guard results retain their original revision;
they are not presented as new full runs after the fixture move.

A final publication rebase incorporates the Java configuration update: the
configuration-option generator and one C++ threading-guide key change, not
C++/.NET runtime code. Four affected metadata guards are rerun successfully:
documentation build facts, binding coverage, cross-binding inventory and
the configuration-option matrix self-test. All four ran with zero failures
or skips; `publication-config-followup.json` retains that selected slice.
Every earlier qualified input outside those two expected metadata files
still matches the publication supplement's manifest.

## Evidence

`~/archive/libhft/dotnet-sparse-replay-20261011/baseline/` retains the
16-pass/16-timeout baseline, and `fixed/` retains the 32-pass correction.
Each contains per-child output, build and toolchain logs, source pin/patch
and source/driver manifests. Products and scratch directories are under
`~/build/codex-libhft-hp/libhft/`, never `/tmp`.

The baseline used a shared-core lease; the fixed reproduction used isolated
cores with shared work allowed. Their elapsed totals include runtime startup,
store operations and TCP session setup. These are functional watchdog
checks, not comparable NIC or steady-state latency measurements.

The retained drivers are in `evidence/dotnet-sparse-replay-20261011/`.
`run-probe.sh` expects zero timeouts by default; set
`LIBHFT_SPARSE_EXPECT_TIMEOUTS=16` only to reproduce the old runtime.
The SDK 8 selector is the `integration/tools/dotnet-sdk8/dotnet` shim (then beside
this evidence as `dotnet-sdk8-20261011/dotnet`),
configured with the installed `/usr/lib64/dotnet/sdk/8.0.131` and
`/usr/lib64/dotnet/dotnet`. It does not change any shared SDK setting.

The earlier stored-admin standalone probe now keeps its original fixture
inside `evidence/dotnet-stored-admin-20261011/`, byte-identical to the
official fixture at `ce6855b6cb3c40480ee0fac124ab38f686248443`.
The official fixture has since moved into broader managed/native provider
parity tests. Keeping the reproduction's fixture local prevents that move
from breaking its historical 56-case driver; the ordinary full-check
driver scans the replacement parity fixture.
`run-publication-checks.py` retains the bounded supplement; it requires the
earlier complete qualification through `LIBHFT_QUALIFIED_EVIDENCE` and a new
archive directory through `LIBHFT_PUBLICATION_EVIDENCE`.
