# Rust audit logging: expose failed content-sync operations

## Baseline and cross-engine review

The actual HP baseline is `d33cf7809`, with `audit.rs` unchanged from the
published write-fence repair `90baef2e3`. A separate scratch crate uses the public
FileLog API and the scoped syscall observer to write 600 complete records. It
observes three content-sync calls, injects EIO and ENOSPC into two of them, and
verifies the third succeeds. All 600 records remain byte-exact and the existing
`drops`, `write_failures` and `truncated` counters are zero. The separate public
API admission probe fails to compile with E0599: `sync_failures` is absent.
These are an observed baseline diagnostic gap and a missing-method proof; no
baseline getter or counter is fabricated to make the fault tests run.

At the reviewed source, C++ FileLog ignores periodic/final HftFileSyncData
results. Pure Java AuditLog adds sync/flush/close operation errors to
`writeFailures`; .NET FileLog adds final durable-flush errors to `WriteFailures`.
Those counters also claim records not written, although a failed sync does not
identify which records are lost and can fail with no offered records. A narrow
coordination request in WIP asks the other owners to align a separate sync-error
diagnostic. This record proves the Rust implementation only; the other runtimes
were read, not changed or fault-tested.

## Rust contract and implementation

`FileLog::sync_failures()` returns the cumulative number of periodic/final
`File::sync_data` calls that return an error. A separate AtomicU64 lives with the
active writer; disable joins the writer and carries its count into the owner,
where it remains available while disabled. New enable cycles start an active
count of zero on top of carried history. Failed enable, idempotent disable,
healthy append cycles and moving the producer retain that history. The const
constructor and the Send/!Sync ownership contract remain supported.

Counts describe failed operations. They do not add failed records to
`write_failures`, estimate lost records, or clear after a later successful sync.
A positive count identifies historical content-sync uncertainty. A zero count
means no sync call returned an error; it does not certify durability for bytes
still queued or written since the last sync, metadata, directory entries, close
errors or a crash. Rust's primary
[File::sync_data contract](https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_data)
may omit metadata and may use sync_all on some platforms.

Both existing sync sites now check the result and add one on error. The cadence,
final sync attempt, write continuation after a sync error and existing hard-write
fence are unchanged. Only observed hard/zero-byte write faults require fresh-file
recovery; a sync error alone does not. A write-fenced writer attempts no later
syncs, so it invents no sync error. Internal syscall retries which eventually
succeed do not contribute a returned error. The tested Linux standard library
retries EINTR within one sync_data call; the Rust API counts the returned Result,
independent of how many syscalls a platform uses.

`FileLog::record` is byte-identical to the baseline. There is no new producer
counter load, allocation, lock or syscall. There is no on-disk format, session,
telemetry fixture, callback or other-engine change. The public Session already
owns an accessible `audit` FileLog, so callers can read the new diagnostic there.
The existing unbuffered File flush and close policy remain unchanged.

## Regression coverage

The test-only Linux x86-64 fdatasync observer now supports scripted EIO, ENOSPC,
EINVAL and EINTR results or a real syscall success. Its scope remains this test
executable and one canonical fixture pathname; it forwards every other descriptor
unchanged. The errno write occurs after releasing the fault mutex. Every scripted
step is checked as applied, with no remaining steps, and exact accepted output is
compared with literal epoch timestamp text and independent payload assembly.
The seed-write barrier queues each intended record set before the writer drains.
Fixture directories delete at teardown; fault-guard teardown releases parked
writers before the logger's drain on drop.

Seven new cases prove:

- A periodic failure is visible while enabled, and a subsequent record is written.
- Two periodic failures plus one final failure report three operations with all
  600 records byte-exact and record counters unchanged.
- An empty-file final error contributes one operation; repeated disable contributes
  none, and a const-constructed disabled log starts at zero.
- EINTR followed by success at periodic and final sync reports zero errors despite
  four syscalls; EINTR followed by EIO/ENOSPC counts only two returned errors.
- Failed open, owner transfer, another failed append cycle and a later healthy
  cycle preserve cumulative sync-error history.
- A periodic sync error followed by a real write fault preserves both distinct
  counters and suppresses final sync; fresh-file recovery carries history and
  a new final sync error increases only the sync count.

The fourteen existing write-fault/rotation cases also require zero invented sync
failures. The three external producer-ownership cases retain exact bytes and
slot reuse. No fault injector is added to the production library.

## Verification

The tested candidate is `7e6e0b2d16d66fb722ad3ba740ad4cb48e808c60`.
All 344 tracked Rust file hashes match the HP worktree before verification.
Cargo/Rust 1.92.0 runs offline at one job, with one test thread and absolute
session LIBHFT_BUILD_ROOT/target paths. Test and development profiles omit debug
symbols; optimization and debug assertions retain their normal test settings. Strict
all-target Clippy uses the isolated Rust 1.93.1 toolchain, offline at one job.

| Gate | Result |
|---|---|
| Actual baseline sync-error observation | 600 exact records; 2 injected sync errors; all 3 existing counters remain zero |
| Baseline public API admission | Expected E0599: sync_failures is absent |
| Focused audit syscall and producer ownership selection | 24 passed, 0 failed, 0 ignored |
| rust-engine-tests, complete default Rust Cargo suite | 1,154 passed, 0 failed, 1 existing ignored builders doctest |
| rust-open-edition-tests, complete open Rust Cargo suite | 988 passed, 0 failed, the same 1 ignored doctest |
| Strict Clippy 1.93.1, all targets with -D warnings | Passed in both editions |
| Rustfmt on final audit integration test | Passed |
| Public-export scan on both owned audit files | 2 files, 0 findings |

Each selected conformance stage reports 1 ran, 0 failed and 0 skipped. Each
report is PARTIAL/exit 2 because only one of the 359 configured monorepo stages
was selected. The counts above describe the complete Rust Cargo suites those
stages invoke. The scratch wrapper retains successful Cargo stdout/stderr and
real command/exit metadata without changing stage arguments, results or
process-group cleanup. No full-monorepo-suite pass is claimed.

The integrated code pin is `95264cec7`. Main's C++ Message/MessageBuilder
rename changes reference comments in five Rust files. A retained line-by-line
comparison proves that every differing line is a full-line comment with just
that type-name substitution; executable lines and doctest code are unchanged.
The audit runtime and fault tests remain byte-identical to the tested candidate.
Final source manifests retain all 344 integrated hashes and publication pins in
publication.json. No unchanged suite is repeated for those prose-only changes.

Owned heavy jobs run serially on HP after checking active compiler/suite
processes and at least 8 GiB available memory. The guard yields to foreign
heavy jobs and stops only its own process group if necessary. No heavy job was
added on BEELINK2. Temporary test fixtures delete on normal teardown.

The exact baseline and candidate sources, scratch-crate inputs, API failure,
syscall evidence, patches, commands, full gate reports, source manifests and
candidate/publication Git bundle are retained in
~/archive/libhft/rust-audit-sync-errors-20261004. SHA256SUMS verifies the kept
evidence; HP retains original logs in the matching archive directory.

## Boundaries and remaining parity

No durable-success watermark, errno detail, health-reset operation, persistent
fault marker, automatic fencing after sync errors or global writer exclusion is
introduced. File drop still follows the standard library's close-error policy;
content-sync errors are the only new observed operations. The counter remains
separate from the canonical session telemetry fixtures pending cross-engine
agreement. A sync syscall that has not returned remains capable of blocking the
writer; this change reports errors after return and adds no timeout guarantee.

The other audit engines need their owners' implementations and independent fault
proofs before the diagnostic is a cross-flavour contract. Real production feed/
order endpoints and venue/FIXP schema certification remain pending. Inventory
coverage scores are unchanged.
