# FIXP prepared frame and response lifetime review — 2026-10-17

## Scope and changes

Follow-on review of FIXP transport buffers in C++, Java, Rust and .NET Pure.
Three parallel reviewers and an independent cross-review covered the changes. Builds and
tests ran serially under one host CPU lease, with concurrency capped at 16.
Session: `codex-review-replay-1017`.

- Standalone socket sessions retire prepared output and discard unread bytes when a new
  transport connection is accepted. A refused connection transition preserves both.
  Previously a frame prepared before EOF/Terminate could be sent after a new handshake
  at the same sequence, and a partial old SOFH prefix could corrupt the new handshake.
- A failed control encoder also retires the previous preparation. This makes its lifetime
  consistent with the next-send contract, including invalid credentials and reasons.
- .NET associates a prepared step with its session and preparation generation, keeps the
  generation monotonic across Init, and checks connection state, sequence and flow before
  writing. A stale or foreign step cannot send or discard the current frame. CopyFrom
  preserves the token; Clear removes it. These checks add no per-message allocation.
- C++ runner-generated Applied/NotApplied payloads use dedicated 64-byte member storage.
  LastStep no longer exposes expired stack bytes, and a final NotApplied remains readable
  when its pump immediately sends FinishedReceiving. This adds no response copy or
  allocation; caller-supplied application payloads retain their existing ownership.

Final test commit: `2629028f5`. Final fix commit: `be4fcc1dd`.
No measured latency improvement is claimed.

## Baseline reproduction

The new regressions were committed and run before applying the production fixes, at
`e79c48a06`. Every expected failure was checked before proceeding:

| Selection | Baseline result |
| --- | --- |
| C++ generated response lifetime | ASan stack-use-after-return in SendApplicationResponse |
| C++ reconnect, partial input, failed encoder selectors | Three separate expected assertion failures |
| .NET lifecycle tests and native guard | 38 failed, 13 passed, 0 skipped |
| Java lifecycle tests and native guard | 20 failed, 13 passed, 0 skipped |
| Rust lifecycle integration | 14 failed, 6 passed, 0 ignored |

The tests cover both SOFH formats, stale and foreign prepared steps, connection refusal,
real reconnect handshakes, partial old input, and failed control encoders. Java and .NET
also cover enforced sequenced and unsequenced flows. C++ response coverage spans all three
runner roles and a final gap response immediately followed by FinishedReceiving.

## Fixed qualification

All 20 initial qualification stages passed at `d9ed42e64`:

| Selection | Development edition | Open edition |
| --- | ---: | ---: |
| .NET FIXP tests and required native guard | 2,005 passed | 1,908 passed |
| Java Fixp*Test and required native guard | 3,384 passed | 3,252 passed |
| Rust fixp:: library tests | 385 passed | 334 passed |
| Rust eight selected integration programs | 273 passed | 273 passed |
| C++ normal FIXP suite | 11 programs passed | 11 programs passed |
| C++ ASan + UBSan | 2 programs passed | 2 programs passed |

Java/.NET/Rust: 11,814 test executions, zero failures/errors/skips/ignored tests in these
selections. Both native guards passed. C++ sanitizer runs enabled
`ASAN_OPTIONS=detect_stack_use_after_return=1`; both the response sequencing and standalone
socket-session programs passed in both editions. Existing .NET allocation tests were
included in the FIXP selection. This is targeted FIXP qualification, not an all-repository
test run.

The normal C++ programs were socket_session, flow_sequencing, session_runner_initiator,
session_runner_acceptor, session_runner_multi_acceptor, runner_send_failure,
response_ownership, recovery_continuation, runner_lifecycle, runner_liveness, and
initiator_receive_progress (all with the `test_fixp_` prefix).
Rust integrations were fixp_prepared_lifecycle, fixp_socket_view_bounds,
fixp_connect_lifecycle, fixp_recovery_response_lifecycle, fixp_gap_receive_progress,
fixp_initiator_receive_progress, fixp_multi_payload_ownership, and fixp_runner_terminal_input.

## Rebase verification and evidence

Rebased onto upstream `405a1fb03`, yielding source snapshot `90ce2730c`.
The source comparison proved all C++, all Rust, the main Java build inputs and .NET native
inputs unchanged. Upstream's TxQueue change affected the .NET build, so the native build and
the entire selected .NET suite were repeated for each edition: 2,005 and 1,908 passed,
zero failures or skips. All four rebase qualification stages passed.

The subsequent rebase onto `79a0c4ab5` yielded `be4fcc1dd`. Its only tree difference from
the rechecked source snapshot was `publish/templates/rust/README.md`; engine and build-tool
inputs remained byte-identical. The changed-engine export scan passed: 12 files, 0 findings.
`git diff --check` passed.

Logs, XML/TRX reports, exact commands, source bundles, source comparisons, independent review
notes and checksums are retained outside Git at:

`~/archive/libhft/reviews/2026-10-17-codex-review-replay-1017/`

Key records: `baseline-summary.json`, `qualification-summary.json`,
`rebased-qualification-summary.json`, `stages.jsonl`, `rebase-source-proof.json`,
`final-source-proof.json`, and `publication-proof.json`. The source bundles preserve the
original pre-rebase validation commits. Raw captures and build products are not committed.

## Follow-up observation

A read-only Java TLS review identified a possible shutdown wait behind a blocking worker
write. The call chain and a bounded reproduction design are retained as
`java-tls-finding.md` in the review archive. No reproduction was run and no TLS/session fix
is claimed here. PureJavaSession is currently claimed by the configured-acceptor work.
