# Managed FIX replay of imported administrative records

The managed .NET replay path retransmitted administrative messages retained
in imported QuickFIX journals. It now classifies replay candidates with the
existing application-message predicate: old administrative records become
SequenceReset gap fills, while genuine application records remain replayable.
The focused public-session test passes all 56 cases with the correction.
The SDK 8 development and open suites, both native editions, explicit native
pool checks and all nine required publication guards also pass.

## Reproduction

The unchanged runtime at `bfbc04c454e5018fbda0135755ddb19d14d4990a` fails
28 administrative cases and passes 28 application controls. Every case opens
a real TCP acceptor against a public QuickFIX/J or QuickFIX C++ store seeded
through that store's public append/checkpoint APIs. Recovery uses either a
ResendRequest or NextExpectedMsgSeqNum(789) on Logon. There is no reflection,
native session, fake transport or injected I/O failure.

The administrative cases cover all seven session types: Heartbeat, TestRequest,
ResendRequest, Reject, SequenceReset, Logout and Logon. Records at sequences
2 and 4 surround an application record at 3; missing later sequences join
the final gap. The original runtime returns four recovery frames where three
are expected: it re-encodes the two stored administrative records rather than
gap-filling them. An old SequenceReset carries NewSeqNo=99, deliberately
different from the correct generated frontier.

The controls cover D, 8, AE, BusinessMessageReject(j), and the multi-character
application types 0X, AX and 5X. Matching the first character of a type would
incorrectly suppress these last three. Each check verifies replay ordering,
sequence numbers, retained Text, OrigSendingTime, gap-fill flag/frontier and
that the session remains logged on. The tag-789 case includes the newly sent
Logon in the trailing missing range; explicit ResendRequest stops before it.

## Correction and scope

`ManagedSession.Peer.TryGetReplayable` previously required only a complete,
checksummed frame with a nonempty MsgType. The generic replay encoder can
rewrite administrative messages too. Requiring `IsApplicationType` at the
session's candidate-selection boundary prevents those records from entering
application recovery and preserves the generic encoder's existing API.

The source change is one predicate replacement plus a comment. It adds no
lock, allocation, store metadata or work to the ordinary send/receive path.
The existing missing-record coalescer provides the correct gap-fill ranges.
The correction applies to both explicit resend and automatic tag-789 recovery.
It does not change the store formats or native provider.

The focused fixed run passes all 56 cases, using the same test source and
public ProjectReference build against the correction. Both baseline-valid
and fixed builds report zero warnings/errors and retain unchanged source and
driver manifests. These are functional runs under isolated-core leases with
shared work allowed, not latency measurements.

This classification correction did not change numeric gap traversal. The
separate missing-range finding was subsequently reproduced and corrected in
[the sparse recovery review](RECORD_2026-10-11_hp-dotnet-sparse-replay.md).

## Transmit-queue test scheduling and cleanup

The first broad development run failed both
`TxQueueTests.Concurrent_large_frames_keep_their_bytes_and_order` rows:
the producer reached its unchanged 30-second deadline at frames 1809 and
1518. The failed tests left their consumer threads spinning, so that run was
stopped before it could provide a complete suite verdict. Its logs and native
builds are retained under `verification/`.

A standalone run of the unchanged test, explicitly on isolated CPU 8, fails
both rows again (2 failed, 0 passed, 0 skipped; about one minute), at frames
1917 and 168. The test's consumer busy-spins even when idle, while its producer
yields on the full, effectively one-record ring. Thread scheduling on one
isolated CPU can consume the test's deadline without a reported send fault.

The test now uses adaptive waiting on idle/full observations and always stops
and joins its consumer in a finally block. Its 5,000-message count per row,
30-second producer deadline, byte/order checks and queue counters are unchanged.
The same CPU-8 standalone run passes both rows with zero skips (reported test
duration 278 ms). `TxQueue.cs` is unchanged: this is a test scheduling/cleanup
correction, not evidence that the production queue lost or corrupted bytes.
Standalone logs are `txqueue-single-core-{baseline,fixed}.log` in the archive.

## Development and open verification

After rebasing onto the current configuration changes, revision
`4a354ab37a9bf4e23381e5f851668d0bc884df97` passes with SDK 8.0.131 and
VSTest 17.8.0. Both native editions were freshly built with GCC toolset 14;
each exports 209 hftnet entry points. The open compile check builds 142 open
sources without 67 private sources and verifies six deliberately refused
feature boundaries.

| Run | Passed | Failed | Skipped |
|---|---:|---:|---:|
| Development suite | 2329 | 0 | 3 |
| Open suite | 2103 | 0 | 2 |
| Development native pool with two workers | 3 | 0 | 0 |
| Open native pool with two workers | 3 | 0 | 0 |

The default suites skip the pool-only broadcast check. The explicit pool
runs close that gap in both editions and also verify listener release,
restart, status and connection IDs. The open run uses the ordinary
`check-dotnet-open-edition.run_open_tests` helper and its open native library,
not a relocated or substituted development library. The remaining distinct
skips are the live bypass receiver and SBE multicast loopback checks. There
is no native-library availability skip.

All nine required guard stages ran and passed with zero skips, including
native ABI/export checks, public package checks and every open .NET tool
project. This is a partial selection of 9 of 361 conformance stages, not a
complete conformance-suite verdict. The public scan of the three changed
engine files reports zero findings. Source and driver manifests stayed
unchanged during each run; the open pool supplement checks the same source
manifest independently. These are functional isolated-core qualifications,
not NIC or latency measurements.

An earlier full qualification at
`a7a3df95709d64d11057e8aef346fa43fb076b7f` used SDK 10.0.112 targeting
net8.0: 2294 development tests passed with three skips, 2068 open tests passed
with two skips, and all nine guards passed. Its smaller counts precede the
configuration regression cases added on main. It is retained separately
and is not presented as qualification by the SDK 8 compiler.

## Evidence

`~/archive/libhft/dotnet-stored-admin-20261011/baseline-valid-frames/` retains
the authoritative 28-failure/28-control run, including full failure stacks.
`fixed/` retains the corrected 56-pass run. Each has source pin/patch, source
and driver manifests, .NET toolchain and ordinary build output. Products live
under the session's external build root.

`verification-after-rebase/` retains the complete SDK 10 run.
`verification-sdk8/` retains the final SDK 8 run and the open native pool
supplement, with their exact source pins and input manifests. The final
driver integrates that supplement for subsequent qualifications. SDK 8 is
selected through `integration/tools/dotnet-sdk8/dotnet` (then
`../dotnet-sdk8-20261011/dotnet`), which invokes the existing
installed SDK rather than changing a shared SDK setting or relying on the
now-absent `~/.dotnet/dotnet`.

The earlier `baseline/` attempt also reproduced the same counts but its log
summary command could not find rg after an overly narrow PATH override. Its
inputs were independently checked before changing the fixture. It is retained
as a failed driver attempt, not the authoritative completed baseline.

The internal standalone probe links the ordinary regression class and runs
it through xUnit assertions without claiming an official suite run. Its
default expected failure count is zero; the original runtime is explicitly
invoked with `LIBHFT_REPLAY_EXPECT_FAILURES=28`. The full driver builds both
native editions, runs development/open suites and checks the nine required
publication guard stages; a selected conformance slice must be reported as
partial, never as completion of the entire conformance runner.
