# Native .NET HA role telemetry follows writer arbitration

Native .NET reported a known standby role for a newly configured durable FIX
store before attempting its writer lock. A hard store-open error and a stopped
session also retained that false role. The Workbench could consequently show a
healthy standby where no arbitration had occurred. The binding now reports an
unknown role until a durable store opens or an actual writer-lock refusal proves
standby. Stop and durable teardown release that knowledge.

The legacy .NET telemetry fallback had the same inference problem: its boolean
primary export returns false for both an unattempted lock and a refused lock. It
now proves only a configured-store winner and leaves those ambiguous false
results unknown. The canonical telemetry API still identifies a genuine standby.

## Reproduction

The new private `NativeHaArbitrationTests` fixture has 29 cases. Five store
configurations cover mmap capacities 4,096 and 65,536, the portable file store,
QuickFIX C++ files and QuickFIX/J files. Each tests configuration without open,
a hard open failure, writer refusal and takeover, the legacy fallback and HA
disabled. Four additional cases preserve the memory/storeless run gate with
HA enabled and disabled.

The hard-error fixture places a regular marker file where the parent directory
would need to be. `TryAcquirePrimary` must throw, preserve the marker and leave
the role unknown. The ownership fixture creates two real sessions over one
path, verifies the winner and refused contender, stops the winner, acquires the
same store with the contender, then checks both stopped roles. It does not dial
a FIX peer. The fallback fixture invokes the binding's actual private fallback
with the real native boolean export; it is not an old-library compatibility run.

At baseline `6d51e1b24`, the selection has **17 passes, 20 failures and no
skips**: five failures each for unattempted arbitration, hard open errors,
released roles and legacy false-role inference. All real winner, refusal and
takeover assertions execute successfully before the later stopped-role failures.
The eight native-availability and edition guards pass. The baseline fixture and
driver are retained on the private branch
`s/codex-libhft-hp-native-ha-baseline-20261014`.

## Correction

`HftNetStore` records a boolean only when the existing primary-open operation
returns standby. Mmap, portable file and both QuickFIX file layouts set it on
lock refusal, not on hard errors. Open attempts clear stale refusal, and close
or durable teardown clears it. `hftNetHaPrimaryKnown` reads actual open/refusal
state rather than the existence of a configured pathname.

The existing writer-lock operations, primary admission gate, store formats and
send/replay paths are unchanged. The correction adds no lock and no heap
allocation. The new bookkeeping is confined to store lifecycle and telemetry;
this qualification establishes functional behavior, not a latency improvement.

## Qualification

hp ran the checks under normal-priority CPU leases, with source hashes checked
after each run. Production qualification starts at `2c219bfe4` with the exact
two-file correction captured in `source.patch`.

| Selection | Passed | Skipped or unavailable |
| --- | ---: | ---: |
| Native HA regression and native/edition guards on .NET 8.0.31 | 37 | 0 |
| Same selection on .NET 10.0.12 | 37 | 0 |
| Development native stores and managed HA role controls | 53 | 0 |
| Open-edition native stores and edition/availability guards | 20 | 0 |
| Java prerequisites and JNI availability/edition guards | 13 | 0 |
| Workbench unknown-role model across six surfaces and two scenarios | 12 | 0 |
| Complete registered Workbench unknown-role browser stage | 12 | 0 |
| Native .NET real mmap standby and takeover model | 1 lifecycle | 0 |
| Native .NET real mmap standby rendered in Chromium | 1 provider | 0 |

Selections overlap; these are execution counts, not a sum of unique tests. The
29 pro-only arbitration cases are absent from the open edition, not silently
skipped. Open sources compile independently of the private sources, and the
publication scan reports zero findings in the three changed engine/test files.
Java prerequisites execute through its Maven development profiles; the retained
Surefire reports contain the final 13-case execution.

The registered browser stage checks the before-arbitration scenario through the
model and renders the no-lock-store scenario. A supplementary native .NET
Chromium check also renders the actual configured but unstarted store. It checks
the real zero-valued role fields, the displayed `Primary role known: no` and a
fleet badge matching the model. The disconnected session is FAULTED, not a
healthy standby; disconnected state takes precedence over an unknown HA role.

Initial qualification correctly refused completion while Java prerequisites
were missing. After their build, Chromium exposed an overlong Unix socket path
under the nested scratch directory. A short, unique scratch directory beneath
the session build root fixes that launch condition. The supplementary check's
first expectation of an UNKNOWN badge was also corrected to the existing
disconnected-state precedence; its failing DOM is retained separately. None of
these corrections weakens native loading, skip checks or writer-role assertions.

Writer exclusion and takeover here use the local durable store. They establish
local lock/telemetry behavior, not store replication or a multi-host HA protocol.

After rebasing onto `9fb47f33d`, all input hashes from the full qualification
still match. At integrated pin `9b92b65bd`, a fresh native build and all 37
regression/availability/edition cases pass again with zero skips. The clean-source
pin, build commands, library hash and TRX are in `integrated-logs.tar.gz`.

## Reproduction and retained evidence

[run-focused.sh](evidence/native-ha-arbitration-20261014/run-focused.sh) takes a
session repository root, absolute output root and `baseline` or `fixed`. Use the
baseline branch for its expected 20 failures. The corrected source requires
37 passes and zero skips.

[run-qualification.sh](evidence/native-ha-arbitration-20261014/run-qualification.sh)
adds runtime, edition, Java prerequisite and full Workbench selections.
[run-before-browser.sh](evidence/native-ha-arbitration-20261014/run-before-browser.sh)
takes the repository root, completed qualification root and a fresh output root
for the supplementary DOM check. Wrap each outer invocation in `host-cpu-slot`.
The drivers preserve pins, source patches, native build commands, input hashes,
TRX/Surefire reports and test output.

Baseline, completed qualification, supplementary browser and partial-run logs
are compressed beside the drivers. Copies are retained under
`/home/yann/archive/libhft/native-ha-arbitration-20261014/`; build products and
per-run state are under
`/home/yann/build/codex-libhft-hp/libhft/native-ha-arbitration-20261014/`.
