# Journal reset failure safety and redundant append seeks

Java and Rust now position their append journals before a destructive reset.
A failed positioning operation used to erase the previous committed epoch
while leaving its old counters and payload in RAM. Rust also stops seeking
to the file end for every journal record. This removes an unnecessary syscall
without adding locks, allocations, disk-format changes or send-path work.
The .NET review has two reproduced gaps handed to its current source owner;
those implementations are not changed here.

## Reset failures and corrections

Baseline source is 7b4a59d77. The new Java JUnit regression drives classic FIX
and FIXP public reset APIs with a delegating RandomAccessFile that injects a
reset-position IOException. Its dummy descriptor belongs to a different file;
it never opens or closes a second descriptor to the journal. Both cases fail
before the correction because truncation precedes the seek.

An independent LD_PRELOAD probe drives the same Java public APIs through real
ordinary-file syscalls. Each old reset refuses after reducing a complete
41-byte payload and legacy checkpoint to zero bytes. After correction, each
refusal preserves all 41 bytes. Closing the faulted store, reopening, resetting
successfully, appending and reopening again all succeed. Both editions drive
exactly one native seek EIO per classic/FIXP case. No reflection or substituted
descriptor is used in this independent probe.

Rust's Linux x86-64 integration executable interposes only its own fixture's
lseek/lseek64 calls. Both classic and FIXP resets fail byte preservation against
the old journal, leaving an empty file. The corrected journal seeks to zero
before set_len and writes the checkpoint sequentially. It discards queued
records only after that reset succeeds, under the existing file mutex. On a
seek failure it still fences subsequent writes, retains the RAM payload and
counters, and preserves the committed file for reopening.

Reset is not made transactional against every I/O failure. Resize, checkpoint
write or synchronization can still fail after destructive work starts. The
correction removes the avoidable positioning failure before that boundary.

## Rust append cursor ownership

Recovery establishes the append cursor before the writer starts. After that,
replay reads RAM, every journal write holds the same file mutex, reset positions
before resizing, and any append error permanently fences further writes.
Consequently append_record need not seek to the end before each record.
The file and its cursor are private to the journal; outside writes remain
outside the single-writer contract.

The two new append checks cover queue drains, checkpoints, close, replay-slot
wrap, reset and appending after reset. The baseline classic run issues 43 end
seeks and its FIXP run 11; both corrected checks require zero. Counts in the
old asynchronous writer depend on scheduling, so these are syscall checks,
not a latency or throughput comparison. Reopening validates every retained
payload and the recovered sequence state.

## Verified checks

All builds and tests use host-cpu-slot shared leases and the session build root
on /home. These are functional runs; no quiet-host timing claim is made.

| Check | Development edition | Open edition |
| --- | --- | --- |
| Rust full crate suite with 1.92.0 offline | 1,111 passed | 953 passed |
| New Rust reset and append checks included above | 4 passed | 4 passed |
| Strict all-target Clippy with 1.93.1 | passed | passed |
| Selected Java cases through normal Maven and rebuilt JNI | 183 passed | 183 passed |
| Independent Java classic and FIXP reset probes | 2 passed | 2 passed |

Rust retains one existing ignored builders doctest in each edition, with no
failed tests. Java uses OpenJDK 17.0.20.1 with release 8 compilation and native
availability required. Maven repeats the same 183 selected cases in each of
three configured Surefire executions: 549 executions per edition, not 549
unique tests. Failures, errors and skips are zero. The four changed engine
files pass the public export scrub with zero findings.

After integrating the other sessions' FIXP refusal-count and Java TLS-alias
changes, both full Rust suites, strict Clippy and all selected Java cases pass
again with the same counts. Separate integrated logs, native hashes and a full
Rust build-input manifest are retained. Later rebases import only bench
harness/evidence and release-workflow changes; all verified engine/build
inputs are unchanged and the source manifests pass their final hash checks.

## .NET recovery and writer ownership gaps

A separate net8.0 public-constructor probe, built with SDK 10.0.112 and running
on 8.0.31, injects EIO from fstat64 after the successful tail-repair ftruncate64.
Both editions refuse opening after changing four inputs: payload 14 to 13
bytes, legacy checkpoint 29 to 28, incomplete header 1 to 0, incomplete payload
13 to 0. Healthy/empty controls open normally. This tests the fallible Length
query after repair, not a managed Position setter assumed to issue lseek.
Unlike the reset failures, these cases remove torn bytes, not a valid prefix.

The writer-ownership probe uses no fault injection: it opens a FileStore and
then another through a symbolic or hard-link alias. Both aliases are accepted
in both editions. An independent C process first confirms that F_SETLK cannot
acquire the byte-range writer lock, then acquires it after the alias store
closes while the original is still live. Same-path controls refuse the second
open and retain the lock. A full-path string guard does not establish file
identity. The owner's earlier descriptor-lifetime exclusion for classic
FileStore therefore applies only to identical guard keys.

The two .NET findings are published as requests in docs/WIP_AGENT_CLAIMS.md.
Their source remains reserved. MmapStore's similar pathname-key pattern and
cross-provider closes remain source-review follow-ups, not driven findings
in this record. No .NET native library or full .NET suite is tested here.

## Evidence and remaining scope

Archives on hp retain exact old test artifacts, source pins and hashes,
public probe/interposer sources, toolchains, scripts, complete logs, JNI hashes
and Surefire reports:

- `~/archive/libhft/journal-reset-io-20261004/`
- `~/archive/libhft/dotnet-journal-recovery-io-20261004/`
- `~/archive/libhft/dotnet-writer-alias-20261004/`

The small .NET probe sources are also retained beside this record under
`evidence/journal-reset-20261004/`, so they do not exist only on this box.
Their projects take LIBHFT_REVIEW_REPO and LIBHFT_BUILD_ROOT; both must identify
the reproducer's own worktree/build root. All probes use isolated temporary
fixtures, never installed or live stores. The archives survive session cleanup.

Real device faults, power loss, Windows reset fault injection, NIC transports,
and the whole monorepo conformance matrix are not tested in this batch. The
full all-flavour review is still ongoing.
