# Fresh .NET DAC report observations

The four .NET DAC controllers now enforce the current observation requirements
from [CONTRACT_SCENARIOS.md](CONTRACT_SCENARIOS.md). These apply to newly generated
reports. Previously retained grades remain part of their original campaign record.
**This change does not add a new physical result or complete .NET instrumentation.**

## Promotion gates

A report invocation must run on its selected DAC host inside
`host-cpu-slot wait --pool isol-no-shared`. The controller retains the host-slot
status and process names, and records the inherited quiet-lease check. Existing
root payload hashes, physical counters, application wire audit, process completion
and sample checks remain required.

Additional promotion checks require:

- Three independent 10,000-sample runs, with at least 98% of target in each.
- Both full raw primary and scheduler-delay series, with exact engine/scenario,
  internal run 1, kind `raw`, zero drops and 10,000 values in each series.
  Primary samples must be positive; scheduler-delay p99 must not exceed the interval.
- Explicit measured admin observations for both processes, including integer zeroes.
  Missing observations, negative values and JSON booleans cannot stand for counts.
  Any measured ResendRequest invalidates the report; injected or undeclared
  admin probes cannot be promoted.
- A declared prepared-wire tick-send clock on the actual feed/sample owner.
  The .NET Pure feed is its client; the other three tick arms use their server.
- PASS stability for p50, p99 and p99.9 using sample standard deviation and
  the existing 5%, 10%, 25% warning thresholds.

Rows retain `report_blockers` and remain `S5 diag` if proof is missing. A report
command exits nonzero unless every requested row is report-grade. Tiny commands
keep their functional diagnostic behavior. Fresh manifests name the report
contract version and hash copies of the controller and grading sources.

## Observation format for the pending adapters

Each process must write `artifacts/<role>/observations.json` within its retained
run bundle, for example:

```json
{
  "schema": "libhft.session-observations.v1",
  "engine": "libhft-net",
  "scenario": "tick_to_trade",
  "role": "client",
  "run": 1,
  "samples": 10000,
  "admin_in": 0,
  "admin_out": 0,
  "resend_requests": 0,
  "injected_admin_probe": false,
  "ttt_clock": "before_prepared_wire_send"
}
```

The passive role has `samples: 0`; counters must come from actual production
processing/send observations over the measured window. The example does not
provide runtime evidence. The tick-clock field is required on the tick sample
owner. Engine identities are `libhft-net`, `libhft-dotnet-native`, `quickfix_n`
and `fixantenna_net`.

The actual owner must retain `raw_samples.bin` (`samples.hftbrw` for .NET Native),
using the shared HFTBRW1 format. Its benchmark identity is `<engine>-<scenario>`;
its probes are `end_to_end` and `scheduler_delay`. The native functional reader
accepts the added delay series while continuing to accept historical primary-only
diagnostics. The Pure and Native shared harness now retains scheduler-delay
samples per run, and both executable writers include them beside the primary
series when raw output is requested. The Pure runner explicitly enables raw
retention for that option. Warmups are excluded, and copies survive the next
run's recorder reset. The other physical adapters still need corresponding raw
retention. Pure, QuickFIX/n, FIX Antenna and Native now have separately verified
measured session observers. Their updated physical payloads and fresh gate
evaluation remain pending; see the linked records in the full-bench plan.
The already admitted root payloads are preserved while this work remains open.

## Verification

Nineteen focused regression cases exercise complete synthetic report evidence,
both feed-role arrangements, missing/tampered raw data, excessive delay, absent or
invalid admin counts, identity mismatch, ResendRequest, injected traffic, missing
tick boundaries, rate/NaN failures, quiet leases and all percentile CV gates.
A retained native primary-only diagnostic explicitly fails the new observation
gates. The combined .NET, Rust and Java grading checks pass 66 tests. A report
invocation without the selected host's quiet lease exits before creating output
or contacting a benchmark host.

The shared .NET harness suite passes 94 tests with no skips, including four
raw-delay retention cases. Both Pure and Native executables build using the
cached .NET 8 SDK and the session's out-of-tree build root. A four-flow local
Pure check completes both processes successfully and retains exactly 20 primary
and 20 delay samples per flow after excluded warmups; all four canonical wire
audits pass. Its logs, raw files, audit dumps and invocation script are retained
under [dotnet-raw-retention-20261002](evidence/dotnet-raw-retention-20261002/).
This tiny check establishes output shape and functional parity, not latency
or physical Stage 5 admission. The native raw reader also has a regression
covering the added delay series and rejection of a truncated binary.

## Pure session observations

`LibhftNet.Bench --observations-file FILE` now writes the required per-role
JSON from actual `SessionCore.ProcessInbound` and completed socket-send
observations. Its client window follows the measured harness run; the passive
server starts after the final excluded warmup reply and stops after its final
measured reply. This mode requires one fixed run and one closed-loop session,
and refuses adaptive/multi-session combinations. The harness executes an
initial warmup and another before its single measured run; JSON explicitly
records `effective_warmup_cycles: 2 * --warmup`. No missing observer is replaced
with a guessed zero.

The client's tick clock is captured after the complete `W` envelope is built
and any cold dump is written, immediately before sending that prepared frame.
The public production encoder supplies TestRequest and its Heartbeat response;
admin frames are observed separately from the canonical application dump.
`--admin-probe` injects one midpoint TestRequest, waits for its matching
Heartbeat and declares `injected_admin_probe: true` on both roles. These
observer diagnostics cannot supply report-grade latency evidence.

Twelve two-process checks on the self-contained .NET 8 payload pass: four
ordinary flows with five warmups, four with no warmup and four admin probes.
Each retains 20 primary and 20 delay samples and a passing canonical audit.
Ordinary runs measure explicit zero admin counts; every probe measures one
inbound and one outbound admin message on each process, with zero ResendRequests.
Complete evidence is in
[dotnet-pure-observations-20261002](evidence/dotnet-pure-observations-20261002/).
No new physical result is claimed.

### Physical payload prepared

The self-contained build is staged separately on HP and SCAN in
`~/.cache/libhft/fullbench-dotnet-pure-session-instrumented-20261002`.
The installer `private/ops/scripts/install-dac-dotnet-pure-session-stage5.sh` pins the
entire 192-file runtime tree, source provenance and fixed namespace launcher.
Tree hashing explicitly uses the C locale: HP's default UK locale otherwise
orders the same filenames differently from the WSL build host.
It uses an atomic installation and refuses an unknown existing payload or
launcher. The legacy root payload is retained. On HP it also installs the
exact existing Solarflare counter helper when that helper is absent.

`run_scan_libhft_net_pure.py --instrumented` selects this payload. Its bounded
launcher accepts only typed scenario/count/rate arguments and writes to fixed
root-owned run directories. Runtime injection variables are cleared. Both
tiny and report runs require the selected host's quiet CPU lease; the
controller waits for the root launcher's timeout before closing its SSH
process. Fresh promotion requires the current observation gates, and the
comparison checker checks the new runtime-tree and provenance pins as well.
Physical installation and fresh report results remain pending.

The independent comparison checker now re-audits the current contract when
a fresh manifest/row declares it, including the retained grading-source hashes.
An old primary-only bundle cannot become a fresh pass through its summary
labels. An integration case accepts complete synthetic evidence, then blocks
a changed ResendRequest observation and a changed grading source. Historical
manifests keep their original contract evaluation.
