# FIXP terminal admission, gap scheduling and public-view review — 2026-10-14

This pass reviews FIXP termination and finalization across C++, Java, .NET and Rust,
and Rust socket-step views. Parallel source review used one session worktree. Builds
and tests run serially under the shared host lease, wait for foreign heavy processes,
and use at most 16 compiler jobs. This record covers the selected FIXP paths.

## Defects and behavior

- **Repeated initiator Terminate.** A runner can remain LIVE while its protocol is
  TERMINATING. Admission now checks both states, refusing a second send before
  encoding or transport access. It also refuses sends after a public protocol
  disconnect; Java and .NET preserve rejected runner state. The first Terminate
  remains valid during negotiation and establishment, and an unsuccessful initial
  encoding/write still permits a retry where no bytes were written.
- **Automatic output after Terminate.** Delayed FinishedSending/FinishedReceiving
  remain observable but cannot resume finalization, move its frontier or retire a
  logical session ID while waiting for the peer's Terminate. A flow-rule violation
  ends the connection without emitting another Terminate. Acceptor interception of
  Establish for an already finalized ID no longer emits an EstablishmentReject
  during the terminal wait; normal protocol admission refuses the message. The
  original timeout and peer-response behavior are covered in both SOFH formats and
  all three runner roles.
- **.NET Context flow enforcement.** Context now applies the same sequence-flow
  rule as Sequence before updating peer diagnostics. Enforced unsequenced/None
  flows are refused; enforcement-off behavior is retained. The test matrix covers
  four flows, three runner roles, both SOFH formats and enforcement on/off.
- **Rust public socket views.** Caller-editable step offsets and lengths now use
  checked addition. Invalid application views return None and invalid event-text
  views return an empty slice instead of panicking in debug builds. Release already
  rejected wrapped ranges via slice bounds; valid views remain unchanged.
- **.NET finalization gap scheduling.** Lenient finalization used a synchronous loop
  over the whole peer-specified gap, potentially billions of transport calls in one
  pump. Both modes now keep a bounded pending range and send one NotApplied chunk
  per pump, followed by FinishedReceiving only at completion. A pending gap polls
  at most once for input, admits safe controls at the FIFO head and observes EOF
  and silence expiry. Application frames remain buffered in order; a held/full
  FIFO head does not produce a false silence timeout. The legacy inbound frontier
  advances and incoming replay is cleared only after the final chunk succeeds.
  Existing sequence accounting and last-step diagnostics are preserved.
- **Aborted finalization acknowledgment.** All four implementations set the peer
  acknowledgment flag before a multi-chunk finalization reply finished. A successful
  local Terminate cleared the queue but left that flag, letting the peer's reply
  falsely retire the logical ID. Local abort now cancels only that unsent
  acknowledgment, after the Terminate write succeeds and before clearing its queue.
  Completed acknowledgments and ordinary gap notifications are retained. Failed
  sends retain the direct runners' retry behavior and each transport owner's
  existing cleanup policy.

The existing C++ immediate-close test had fabricated a LIVE runner with a disconnected
protocol. It now performs the existing encoded handshake with liveness disabled,
then checks the original successful Terminate, single close and disconnected state.

Three existing .NET fixtures now drive one pump per gap chunk, retaining the exact
wire transcript, frontier, failed-write, byte-accounting, transport-result and
retirement checks. Pending-output store refusal now expects its one cold input
read and explicitly verifies no write. The initial broad run passed all 126 new
gap cases but failed 16 older timing assumptions; its TRX and logs are preserved
separately as attempt2. Independent review confirmed the adapted assertions.

The Rust unit run then passed 378 cases and failed its equivalent immediate-close
fixture, which also fabricated a LIVE runner with a disconnected protocol. That
fixture now performs the encoded handshake with keepalive disabled and retains
its original successful-send, single-close and disconnected-state assertions.
Independent review confirmed the correction; the failing attempt3 log and source
bundle are retained. No production code changed for either fixture adaptation.

## Baseline

Initial regression source: `a087169246d8395156fbaa8794503d5a88ba36b4`, whose parent is
`86c58623a7326e35d689c1c5acc0aeba0767497a`. Production was unchanged. `test_fixp_runner_liveness` failed 518 of
3,783 assertions, all on new paths; its 50 existing test functions passed. Java had
50 failing cases and 35 passing controls; .NET had 88 failures and 65 passing controls.
Both native-loading guards passed and neither managed run skipped a test. Rust's
three debug regression programs had 24 failures and eight passing controls; release
view-bound tests passed all four controls. Rust's retired-ID Establish failures
reported the old Rejected result; the intended terminal-state refusal is ProtocolError.

The original baseline JSON used a moving origin/main ref as its base; the corrected
manifest uses the actual regression commit parent. The tested HEAD and engine tree did not change.
Both manifests are retained with the raw logs and original source bundle.

Secondary regression source: `92c2fcaa7992a11e9b926089479ca652183530da`.
The initial terminal-send fixes are present. C++ fails exactly 20 of 5,825 assertions
across 12 pending-abort rows; its previous 61 functions and 48 new completed/retry
controls pass. Rust fails exactly three new abort tests with 32 controls passing.
The first managed attempts exposed two incorrect fixture assumptions: .NET's
multi-owner deliberately releases every SendError, and Java's acceptor control
buffer can encode a 300-byte reason. Those tests now assert owner cleanup and use
an oversized 1,024-byte reason. Production was unchanged for these corrections.
Corrected managed baseline source: `62f608293e3665f48dcf2a6e8401b5e597575050`.
The corrected Java baseline has six intended cancellation failures and 85 passes;
.NET has 108 gap/abort failures and 19 passes. Both required-native guards pass, with
no skipped tests. The initial incorrect fixture attempts and corrected reruns are
retained separately. The new .NET fixture uses a transport send budget, so even a
maximum unsigned sequence gap fails quickly on the old synchronous loop.

## Qualification

Final qualification source: `1a66335cbd953b1115e6450675d38a9fd6070b57`. All 19 selected stages pass.
The first nine stages ran at `9b99553b00763b84185f05d79d8d635f55f49b9a`;
their complete tested C++/native source is unchanged. The .NET and Java development stages ran at
`dc84ef914502cb3a77f822b095ab4d31e441c19a`; their tested source is unchanged.
The Rust unit-test fixture was corrected afterward. Three .NET test fixtures and an upstream documentation file
changed between the first nine stages and those managed runs. Source
manifests identify the pin for each stage and retain every changed blob.

Upstream FAST presence-map changes were integrated before the open-edition runs.
Rust development unit and integration checks were repeated on the merged source;
all earlier FIXP production/test blobs remained identical. The earlier passing
Rust logs and source are retained under the premerge prefix.

| Check | Development edition | Open edition |
|---|---:|---:|
| C++ FIXP programs | 12 passed | 12 passed |
| .NET FIXP and matching provider cases | 1675 passed | 1578 passed |
| Java FIXP cases | 3276 passed | 3144 passed |
| Rust FIXP unit cases | 379 passed | 328 passed |
| Rust FIXP integration cases | 375 passed | 362 passed |

C++ liveness also passes ASan and UBSan with recovery disabled: 64 test functions,
5,825 assertions. CORE tier checking passes for 91 headers; the CORE loopback
build has 51 reachable headers, with no private/conformance dependencies. Rust's
three release regression programs pass all 44 cases.

Both .NET and Java editions pass their required-native guards. Every selected
managed test ran, with zero skips, failures or errors; Rust reports zero ignored
or failed tests. The .NET filter also matches provider tests whose names contain
FixProvider; those are included in the table. Java totals count unique identities
in the final Surefire XML, rather than repeated executions. All 126 new .NET gap
cases pass in both editions. The export scan passes for all 33 changed engine files.

This is targeted qualification of the changed FIXP paths, not a full repository
conformance sweep. The bounded notification loop removes unbounded per-pump work;
no numeric latency comparison is claimed.

Native builds retain an existing GCC 15 array-bounds warning in the multi-acceptor
retransmit helper, also present in the baseline. Source review traced every caller
to the public bounds guard and found no reachable out-of-range index. The warning
and its review evidence are retained; no warning suppression was added.

Publication was rebased onto `aec5bf1636b3e3d09c20f072b655fc979fd1fe11`. Its additional changes
are separate Chronicle process-qualification fixtures/tools and documentation.
The selected engine source trees, FIXP fixtures and Java build descriptor remain
identical; all 33 reviewed source/test blobs were verified after rebasing.

## Evidence

Evidence is retained outside Git at
`/home/yann/archive/libhft/reviews/2026-10-14-codex-review-binary-1014`:
source manifests and Git bundles, exact changed-source snapshots, stage drivers and
logs, managed XML/TRX reports, review notes and a verified SHA-256 manifest.
The guards add bounded checks to control paths; this batch makes no end-to-end latency claim.
