# Native FIXP finalization append ownership

C++ and Rust finalization appends opened the metadata file without checking its
writer fences. A replay store's own payload lock does not protect a different
file reached through its deferred `<path>.finalized` name. The correction checks
both native fences on that actual file before appending. Application save and
replay paths are unchanged.

## Confirmed failures

At pin `cc04c59ae`, the eight-case C++ fixture reports **two passes, six failures,
76 checks, 23 failed checks and zero skips** on scan, using GCC 11.5.0. The three
native payload cases accept finalization and modify another live replay store's
synced file despite its existing locks. They cover an ordinary path collision,
a hard link and a symbolic link. The existing owner's two fences remain held.

The path collision needs no links: open a candidate at `store`, then another
store at `store.finalized`, then finalize the candidate. Finalization metadata
is deferred, so the candidate's initial metadata preflight does not prevent
this later collision.

The other failures bypass a flock-only owner, an OFD byte-range-only owner and
an independent process's POSIX byte-range lock. Ordinary appends, duplicate IDs,
unowned symbolic metadata and reopen retention provide the passing controls.

The corrected Rust baseline at the same pin also reports **two passes, six
failures and zero ignored tests**. All three native payload cases accept the
append and grow the live file from **55 to 83 bytes**. Its initial fixture-layout
failure is retained separately and is not counted as regression evidence.

## Qualification

At integrated pin `2b2a1f56d933785d706f9561b35f6a792e369e3d`, hp passes
the focused development and open-edition checks using GCC **14.2.1** and
Rust **1.92.0**, under a normal shared CPU lease. Every invocation exits zero;
the C++ logs report no skips and the Rust selections report zero ignored tests.

| Invocation | Development passed | Open passed |
| --- | ---: | ---: |
| New C++ append ownership cases | 8 | 8 |
| New Rust append ownership cases | 8 | 8 |
| Rust inherited writer fences | 10 | 6 |
| Rust `--lib file_store` | 124 | 120 |
| Rust `--lib finalized` | 32 | 23 |

The Rust filters overlap; these are counts per invocation, not a unique-test sum.
C++ additionally passes its existing file store, FIXP file replay store, finalized
IDs and path fixtures. Development includes the private mmap companions:
14 open path cases, 20 open finalized-session cases, 16 private path cases and
nine private finalized-session cases. Open excludes the two private companions.
The new C++ fixture is registered in the normal correctness/open test lists.

scan independently passed the sixteen new C++/Rust development cases at
`1ecabeaeb`, plus the existing C++ companions, ten inherited-lock cases and
124 Rust file-store cases before its run stopped. That partial run does not
substitute for the complete focused development/open qualification on hp.

These are functional storage checks, not an all-flavour conformance sweep or
a latency measurement. The public-source scan finds zero issues in all five
changed source/test files.

## Ownership correction

Rust reuses `file_store::lock_one_writer` and its descriptor guard, with
crate-local visibility. C++ takes the same nonblocking flock and byte-zero
range fences in a scoped metadata append guard. Both retain ownership through
record and directory sync, release only acquired locks, and explicitly unlock
before descriptor close during normal cleanup, even when a forked child retains
the open file description.

A refused append follows the existing fail-closed policy: finalization returns
false, the handle treats every ID as finalized, and subsequent records fail
until reopen. The existing record format, payload checkpoint ordering and
directory sync are preserved. No mutex or file operation is added to ordinary
application save or replay.

These fixtures target Linux descriptor-owned native fences and POSIX locks in
another process. They do not remove the documented same-process interoperability
restriction for managed POSIX-only holders: closing an unrelated descriptor of
the same inode can release those process-owned locks. Files with no existing
writer fence are outside this admission check. Windows metadata code is unchanged.

## Reproduction and evidence

Use the tracked `evidence/native-finalized-ownership-20261014/run-checks.sh`
inside a `host-cpu-slot` lease. Pass a source root, an out-of-tree output root,
and `cpp`, `rust` or `related`. The related mode covers existing native file
store, finalization, path and inherited-lock fixtures; development includes
the private mmap finalization/path companions. `LIBHFT_OPEN=1` leaves the
private C++ include roots out and selects the Rust open edition.

The hp qualification sources and outputs are under
`/home/yann/build/codex-libhft-hp/libhft/native-finalized-ownership-20261014/`
in this session's build root. Kept source archives and copies of all raw logs are under
`/home/yann/archive/libhft/native-finalized-ownership-20261014/` on hp.
The compressed baseline, scan follow-up and hp qualification logs are tracked beside
the driver. Never move kept evidence to `/tmp` or rely on a build directory alone.
scan's daily orphan cleaner quarantined its build root because no corresponding
session worktree existed there; its logs were recovered and archived on hp.

The baseline is pinned at `cc04c59ae`, scan's fixed inputs at
`1ecabeaeb`, and hp's complete focused qualification at `2b2a1f56d`.
The qualification's POSIX-owner control re-executes the Rust test
binary rather than depending on Python. Shared fixture bytes come from the
baseline pin and are unchanged at the qualification pin. The explicit fixture
archive is required with the source archive: Git's archive directory wildcard
did not select their descendants. The hp source archive names each shared
fixture directory explicitly, so it needs no supplemental archive.

Rebasing onto `97b6dfdd7` leaves all C++ headers, the qualified C++ fixture inputs,
the entire Rust tree and the shared fixture directories unchanged from the hp
qualification pin. The unrelated C++ executor fixture changed upstream and is
not included in this qualification. Counts belong to the pin above, not to a
retest of every unrelated upstream change.
