# FIXP pending-gap receive clocks and required-response cleanup — 2026-10-16

The session used a fresh worktree on the rewritten history. Three parallel agents
reviewed C++, Java and Rust; root reviewed .NET and coordinated independent cross-reviews.
Builds ran serially under the shared host lease, with a foreign-job guard and at most
16 compiler/test workers. This is a focused continuation of the full code review.

## Confirmed defects

- Pending gap notification paths in all four implementations credited partial reads
  as complete received messages. A stalled fragment extended the silence deadline.
  Productive fragments now defer only the current pump; only complete frames restart
  the receive clock. Held FIFO heads still suppress silence while pending output drains.
  This aligns pending paths with the complete-frame contract established by the
  2026-10-13 framing review, superseding the 2026-10-08 pending-path raw-byte credit rule.
- C++ single/multi acceptor terminal response wrappers closed the transport again
  after a positive short write already retired it. The multi FinishedReceiving wrapper
  had the same defect, also repeating store cleanup and lifecycle generation changes.
  Wrapper cleanup now checks whether retirement has already happened. Explicit public
  ClosePeer behavior is unchanged.
- Rust direct multi-peer pumping consumed a replay request but left the peer live
  after a zero/error write of the initial replay header or rejection. There is no queued
  retry. The direct entry point now retires that peer on a failed required response,
  preserving diagnostics and leaving siblings usable.
- .NET's direct acceptor likewise remained live after a failed required replay header
  or rejection. It also left finalization waiting for a recovery request that never
  reached the peer. The consumed triggers have no retry queue, so every failed
  required response now retires the session. Clean refusals of optional application
  sends retain their existing retry behavior. The multi owner already released these
  failures and supplies additional controls.

## Regression design

Clock cases exercise both SOFH formats, all three runner roles, enforcement modes,
original deadline boundaries, productive late fragments, complete-control refresh,
held FIFO input, terminal input and one notification per pump. Rust additionally
covers ordinary Sequence-triggered gaps and finalization-triggered gaps.
C++ cleanup and Rust response cases include zero, negative, positive-prefix and full
writes, cleanup counts, preserved diagnostics, subsequent I/O refusal and sibling
isolation. Successful responses stay live where the protocol requires it.

## Additional observations

Java and Rust replay responses still traverse a complete retained replay range in one
call. Built-in stores bound the range by capacity; a continuation design must preserve
replay-group ordering and historical sequence numbers. No timing result or latency
improvement is claimed for this batch.

Managed owner/reset and some Java initiator paths call store Close redundantly. The
built-in stores guard or clear their underlying resources, including mmap mappings,
locks and native handles; review found no lost flush, lock or exception. These calls
are an unmeasured cleanup opportunity, not a confirmed correctness defect.

## Validation

Baseline production remained at `493edc294285cf75ae7cc90da069fd70bbcdc178`.
C++ regression source was `f28ca0303e9f626d69f3d09edbb124cfae739a44`; managed
regression source was `25b364549d7f722231f7fe9f04caa8b3d2d62e3f`. These hashes
precede the normal qualification rebase and remain in the archived source bundles.

| Baseline | Reproduced failures | Passing controls |
|---|---:|---:|
| C++ lifecycle | 36 cases / 156 assertions | 448 cases |
| C++ cleanup | 44 assertions | all original cases and successful/error/zero controls |
| Java gap/native | 72 cases | 457 cases |
| .NET gap/recovery/native | 72 cases | 211 cases |
| Rust gap | 54 cases | 72 cases |
| Rust required response | 8 cases | 60 cases |

Both managed native guards passed; there were no skipped/ignored baseline tests.
The first Rust attempt found a missing fixture transport type. The next attempt
exposed a fixture setup error: no-path multi-peer stores are provided ready by the
factory, while direct runners open their stores. The corrected factory supplies an
open in-memory store. Corrected Rust baseline source is
`a1fef0f22d093497ad1d6b6617ef0a166497944d`. Production was unchanged through
both fixture corrections. All initial attempts are retained with the final baseline.

Qualification initially used `21dc167674785a3a54280883f4731c778905404f`.
The broader Rust library run passed 376 tests and exposed three legacy macro cases
that still expected an overdue partial heartbeat to restart the receive clock.
Only their shared scenario 7 was corrected: complete the heartbeat, survive an empty
poll just before its new deadline, and expire exactly at the deadline. The dedicated
new regressions cover an empty poll before completion. No test was removed or skipped.
An independent reviewer checked the timing, FIFO controls and output assertions.

Qualification resumed at `998122141` with unchanged production and unchanged C++,
Java and .NET test inputs. A source comparison records only the Rust library-test
file and coordination claims changing since the initial qualification. The completed
successful prefix is retained, including native-library guards and report files;
the failed Rust attempt is archived separately. The remaining stages ran in full.

All 21 selected qualification stages passed.

| Qualification | Passing results |
|---|---:|
| C++ development and open | 12 programs each |
| C++ ASan + UBSan, C++11 | 484 lifecycle cases / 13,354 checks; 3,025 send-failure checks |
| Core tier tags and core gate | passed |
| .NET devel FIXP and native guard | 1819 tests |
| Java devel FIXP and native guard | 3348 tests |
| Rust devel FIXP unit | 379 tests in 1 program |
| Rust devel FIXP integration | 517 tests in 19 programs |
| .NET open FIXP and native guard | 1722 tests |
| Java open FIXP and native guard | 3216 tests |
| Rust open FIXP unit | 328 tests in 1 program |
| Rust open FIXP integration | 504 tests in 19 programs |
| Rust release regression matrices | 194 tests in 2 programs |

All four managed native-library guards passed. No selected managed test was
skipped, and no selected Rust test was ignored. The scope is the selected FIXP,
core and native-loading checks above, not the entire repository test suite.

The engine export scan passed for all 18 changed files, and `git diff --check`
passed. The archived stage ledger records each command, source hash and result.

Raw test logs, source patches, commands, source-identity proof, failed fixture
attempts, reports and digests are archived outside Git at
`~/archive/libhft/reviews/2026-10-16-codex-review-gap-1016`.


Before publication, the branch was rebased onto `72f19458d`. None of the selected
compiled roots changed upstream since the qualification base. A post-rebase comparison
confirmed identical compiled roots and all 18 changed engine file blobs; the retained
test results therefore apply to the rebased source.
