# Microbench plan — Stage 2 codec, cross-engine

**2026-10-02 field-work correction:** the historical campaign below passed its
host-activity gates, but its three mutation probes did not establish comparable
D/8 field work. Falcon's `parse_only` validated framing and skipped the body;
several D extraction paths consumed only a subset of the fields. Those parse
timings must not be used as a field-parser ranking. A replacement campaign uses
the `converted-values-v2` contract described below.

The current roster has **25 Stage-2 adapters**, with llfix admitted as an
encoder-only partial after its checked-in runner smoke. The replacement
converted-values-v2 campaign passes **25/25 normal runs, 320/320 isolated
probes and 554 operation rows**, with a complete clock contract and all 25
selected host windows qualified. The [corrected matrix](microbench-matrix-scan-25-20261003.html)
uses 24 clean windows from the full pass and a same-manifest hffix repair.
The [campaign record](RECORD_2026-10-02_stage2-converted-values.md#qualified-scan-ranking) retains failed checks,
corrections, exact selections, process ownership and archive receipts.

Historical campaign: **24 Stage-2 adapters**, including
libtrading and Fix8's public codecs. The [qualified scan ranking](microbench-matrix-scan-24-20261002.html)
passes **24/24 normal runs, 70/70 mutation probes and 551 operation rows** at
50,000 timed and 5,000 warm-up samples. Its rows come from four audited
same-manifest segments on isolated CPUs 18–19; every selected adapter window
has a passing 120-second preflight and zero competing process samples at or
above 5% CPU. Source pins, raw evidence, repair outcomes and archive receipts
are in the [campaign record](RECORD_2026-10-02_stage2-24-scan.md#qualified-scan-ranking).
The preceding qualified 22-adapter report and diagnostic 23-adapter campaign
remain retained as historical evidence.

This file was
`MICROBENCH_ENGINE_MATRIX.md`: its status table is
now rendered, by language, in [COVERAGE.md](COVERAGE.md#status-by-language), and who is in scope is
[TARGET_ENGINE_ROSTER.md](TARGET_ENGINE_ROSTER.md). What stays here is the method, the rules for a
row, the reasons some engines cannot have one, and the admission boundaries.

The full-bench counterpart is [PLAN_FULL_BENCH.md](PLAN_FULL_BENCH.md).

## What it measures

### Checked field work (`converted-values-v2`)

Every timed D/8 `parse_only` and `parse_extract` invocation materialises and
checks the same complete application field set through the engine's public
parser and field API. These two D/8 labels now intentionally call the same
checked operation; they are retained for table compatibility. D checks tags
11/21/55/54/60/38/40/44/59; 8 checks
37/17/11/150/39/55/54/38/151/14/6/60. W `parse_only` checks symbol, entry count
and the first bid/offer prices; W `parse_extract` checks request ID, symbol,
count and all 20 entries' side/price/size values in wire order.

FNV-1a consumes type-marked converted numbers, not just their ASCII spelling:
unsigned integers use eight little-endian bytes; decimals use eight bytes of
the unsigned value scaled by 100,000; strings retain their ASCII bytes. Each
value ends with byte 1. The independent Python oracle supplies tag 9002 (full)
or 9001 (W light), including for Java fixtures. The hash comparison is part of
every timed invocation. Public field views are converted by the adapter where
the codec exposes bytes; a raw scan of the original fixture is not evidence
that the engine parsed the fields.

Falcon's public Protocol parsers feed the hash during body traversal.

Native fixtures pass compiler barriers before each invocation; encoded outputs
are also made observable. This prevents the optimizer from substituting a
precomputed result for parsing or dropping output writes when only the length
is consumed. The barriers add no second timed field or output scan.

Each supported fixture/operation has separate text and numeric negative probes.
They preserve BodyLength, checksum and wire length but leave the expected hash
stale. The audit selects exactly one operation and requires its selection
marker, a positive error count and exit 3. A failure in another operation cannot
satisfy the probe. The parsing roster requires 320 probes: 12 per full
adapter, eight for Falcon, and 36 additional checks for libhft C++ legacy/cursor
operation rows. Encoder-only llfix adds three build rows and no parse probes.
Administrative fixtures outside D/8/W remain diagnostic operations and are
excluded from this field-work claim.

Direct parse, extract and build over the shared canonical bytes, in-process. It does not measure
TCP, logon, storage, a scheduler, or an application's callback path, so a microbench result is
never a latency claim for an engine as deployed.

The launcher is
`bench/cpp/fixbench/tools/open_source_fix_engines/open_source_fix_engine_stage2.py`; the stage
definition and its 2026-07-04 snapshot are in
[CAMPAIGN_TEST_PLAN.md](CAMPAIGN_TEST_PLAN.md#stage-2-parser--builder-microbench). `Implemented / smoke passed` means the checked-in adapter is selected by the
launcher's default engine set and passed a fresh build/run smoke test. This
validates the fixed common fixtures and reported error count, but it is neither
a full timing campaign nor, alone, a field-work parity proof. The
2026-09-29 host campaigns ran `--parity-audit` against wire-valid, stale-hash
mutations; the build/run and probe counts are in the
[campaign record](RECORD_2026-09-29_stage2-microbench.md). The full
primitive contract is `parse_only`,
`parse_extract`, and `build` for `D`, `8`, and a depth-10 `W`. Two deliberate
partial rows remain: Falcon has D/8 only, and llfix has three build operations
without a public receive parser.

## Where it stands

By language ([COVERAGE.md](COVERAGE.md#status-by-language) has the rows):

- **All six of libhft's flavours have a row** — C++, Java Pure, Java/JNI, .NET Pure, .NET Native
  and Rust. Java/JNI and .NET Native joined on 2026-09-30, once `MessageIndex` / `MessageEncoder`
  gave them a native codec to call ([LIBHFT_NATIVE_CODEC_PLAN.md](../LIBHFT_NATIVE_CODEC_PLAN.md));
  their lanes time those, one native call per message. Both are included in the
  qualified scan ranking below.
- **Nineteen other engines have a running adapter**, two of them partial
  (`falcon`, `D`/`8` only, and `llfix`, D/8/W builds only). The unlicensed robaho codec adapter was removed.
  Libtrading's public numeric-tag field API handles tags `262` and `268` even
  though its named enum omits them. Its D/8/depth-10 W adapter passed 25/25
  primitive rows and 3/3 stale-hash probes on hp with the tracked 128-field
  capacity patch; [the admission record](RECORD_2026-10-01_stage2-libtrading.md)
  retains that diagnostic smoke. Fix8's public factory, field API and encoder
  now supply D/8/depth-10 W primitives through a generated benchmark dictionary;
  [its admission record](RECORD_2026-10-02_stage2-fix8.md) retains 9/9 rows
  and 3/3 mutation probes. The corrected converted-value campaign passes
  twelve isolated probes per adapter and retains both engines' clean selected
  rows in the [25-adapter scan ranking](RECORD_2026-10-02_stage2-converted-values.md#qualified-scan-ranking).
- **Qualified same-host codec ranking:** [2026-10-03 scan](microbench-matrix-scan-25-20261003.html)
  contains 25 licensed adapter rows selected from two audited timing segments on
  isolated CPUs 18–19. Each selected row had a passing quiet-host preflight
  and no competing process sample at or above 5% CPU during its adapter
  window. The same pinned source, prepared binaries, fixtures, and sample
  counts were used throughout. [Beelink2](microbench-matrix-beelink2.html)
  and [hp](microbench-matrix-hp.html) remain diagnostic host snapshots. Do not
  combine their percentiles or read codec timings as deployed session latency. The
  [campaign record](RECORD_2026-10-02_stage2-converted-values.md#qualified-scan-ranking)
  records source pins, toolchains, field-work parity and audit outcomes. The three
  `microbench-quickfix-go-<host>.html` pages remain single-engine host validations
  ([record](RECORD_2026-09-11_quickfix-go-host-validation.md)).
- **The preceding [23-adapter scan pass](microbench-matrix-scan-23-diagnostic.html)**
  used an exclusive CPU slot and isolated cores, passed 23/23 build/run and
  67/67 parity probes at 50,000/5,000 samples, and has a complete clock
  contract. Desktop CPU activity overlapped six adapters, so its timings are
  diagnostic. A second complete same-host pass passed the same checks after a
  120-second quiet-host preflight, but VS Code and a scheduled `dnf makecache`
  job overlapped four adapters. Three targeted repair segments supplied clean
  rows for those four;
  the
  [campaign record](RECORD_2026-09-29_stage2-microbench.md#full-23-adapter-scan-pass-and-host-activity-review)
  retains the host activity findings.

## Rules for a row

An engine moves to **Implemented** only when the adapter is checked in,
selectable from the launcher's default `--engines` value, folds the shared
extraction sink, and produces a retained `compile-ok` / `run-ok` build-matrix
row. A non-comparable primitive API remains `N/A`; do not emulate its parser
with a hand-written shim merely to fill a cell. `tools/gen-bench-status --check` enforces the launcher half: an engine marked implemented or
partial in [engines.tsv](engines.tsv) must be in the launcher's default `--engines`, and the reverse.

Which engines should have a row at all:

- **Full-session engines** (`quickfix_cpp`, `quickfixj`, `quickfix_n`, `quickfix_go`) are required.
  Stage 2 measures only their exposed codec path, never their session cost.
- **Direct peers** (`nexusfix`, `philadelphia`, `artio`) are required when the adapter can invoke the
  public parser/builder without a session loop.
- **Adapter-dependent engines** (`fix8`, `llfix`, `libtrading`, `falcon`) get a row only if their
  public API can perform the identical operation under the same validation policy.
- **Codec-only libraries** (`hffix`, `fixpp`, `ferrumfix`) are required; Stage 2 is their only row.

## No comparable row yet

| Engine | Why it is absent | Next implementation decision |
|---|---|---|
| `libhft-rust-native` | Parked 2026-09-11; its native API exposed reframe/session operations, not a parser/builder primitive. | Stays parked. (Java/JNI and .NET Native had the same gap until the native codec closed it.) |
| `philadelphia-fast` | A transport configuration of `philadelphia`, rather than an independent parser/builder implementation. | Keep this distinction in the end-to-end lane. |

## Intentionally not a row

| Engine | Classification |
|---|---|
| `crossfix` | Excluded. Its obfuscated buffer API cannot be reset/reparsed fairly, so a standalone codec number would be misleading. Its honest comparison boundary is the licensed Stage-5 network lane. |
| `openfix`, `robaho_cpp_fix_engine` | Discovery only: no usable, pinned upstream build/adaptor yet. |
| `quickfix-rs` | Excluded as a separate engine: it is a Rust binding over QuickFIX C++. |
| `fix-rs` | Excluded: upstream development was suspended. |

`robaho_cpp_fix_codec` was measured in the historical 23-adapter scan campaign,
but it has no licence at the pinned commit. Its adapter was removed from the
current launcher and its timing rows are absent from the published ranking.
The original raw evidence remains archived; see
[TARGET_ENGINE_ROSTER.md](TARGET_ENGINE_ROSTER.md#licences-checked-2026-09-29).

## Ranking and admission boundaries

The current host reports include the FIX Antenna .NET Core public parser and
field serializer, and Artio codecs generated from the checked-in dictionary.
Their build/run and mutation-parity evidence appears alongside the other
adapters. Their scan timings are in the qualified host-local codec ranking;
the Beelink2 and hp snapshots remain diagnostic.

1. **Completed: scan ranking on isolated cores.** Use the launcher
   with `--parity-audit`, retain the full host report, and record the run
   conditions. The earlier scan diagnostic established build/run and field-work
   parity under contention; its timing percentiles remain diagnostic.

   To shorten the quiet window, first run `prepare_stage2_with_java.sh
   --parity-audit` from the intended checkout and workspace. The wrapper
   rebuilds libhft's Java classes, Java/JNI library, and .NET native library
   before selecting the current default adapter set. `--prepare-only` then compiles the
   normal and mutation binaries and records their runtime-file hashes. During the quiet window,
   run `--run-prepared --parity-audit --run-cpus 18,19`. The launcher verifies
   the manifest before timing and does no compilation in this phase.
   Run `stage2_host_preflight.py --run-cpus 18,19` immediately before timing
   to retain the kernel isolation, affinity, load, and active-process snapshot.
   Its pass is a conservative starting gate, not proof that the host stays
   quiet during the campaign. On scan and hp, acquire
   [`host-cpu-slot`](HOST_CPU_SLOT.md) with `wait --pool isol-no-shared` around
   both the preflight and timed run, so newly queued shared-core builds cannot
   start after the preflight. Jobs already running without a lease still need
   the host activity check.

   When repairs are needed, merge only segments from the same prepared
   manifest. `merge_stage2_scan_23.py` and `publish_stage2_scan_23.py` retain
   their historical names and default pins for the older campaign. For a new
   campaign, pass `--prepared-manifest PATH --operation-rows 554` to both
   helpers (554 is the current 25-adapter count; the historical 24-adapter
   campaign had 551). The manifest supplies the
   source pin, adapter set and exact mutation-probe set. Publication also
   requires the reviewed host-activity verdict and verifies that the raw
   per-engine CSVs agree with the summary. A merge alone remains unqualified.
   `qualify_stage2_scan.py` drives these checks after a complete ownership
   review: it recomputes observations from each raw monitor, verifies every
   reviewed sample and rejects competing activity in any selected window.

   The run is only a candidate ranking if it follows
   [CONTRACT_OPERATIONAL.md](CONTRACT_OPERATIONAL.md#pinning-rules) and
   [METHODOLOGY.md §2](METHODOLOGY.md#2-run-validity--pinning-isolation-preflight):
   - **Measured work on isolated cores only** (10–19 on SCAN). Pass
     `--run-cpus 18,19` to pin adapter runs and parity probes while builds stay
     on the launcher's housekeeping affinity. Keep the launcher itself off the
     isolated cores. The generated HTML and `stage2_run_conditions.json` record
     the requested run cores and per-adapter load readings.
   - **Nothing else on the box.** SCAN's 50 MiB L3 is shared by all 20 cores; isolation is not cache
     isolation, and a concurrent build, bench or fleet "will quietly evict its cache lines and make
     the numbers wrong while they still look plausible"
     ([CONTRACT_OPERATIONAL.md](CONTRACT_OPERATIONAL.md#what-does-matter-on-scan-l3-is-shared)).
   - **Record the conditions in the report**: the core set, the load average, and anything else
     that was running. The JSON readings alone cannot establish that the host was quiet.

   A run that misses these is still useful as a smoke test — it shows whether
   every adapter builds, runs and passes the parity audit on that host — but
   its timings are not a ranking. The first scan run started on 2026-09-30 from
   `~/code/libhft-stage2-20260929` on housekeeping cores 0–9 while a
   brokerforge chaos drill loaded the box. The later 23-adapter pass used
   isolated cores and an exclusive slot, but desktop CPU bursts overlapped
   six adapters. The second full pass used the same prepared binaries and
   sample counts; three targeted repair segments replaced its four
   contaminated rows. The unlicensed robaho row was then removed from the
   published subset without changing any measurement. The
   [qualified 22-adapter scan page](microbench-matrix-scan-22-20260930.html)
   and [campaign record](RECORD_2026-09-29_stage2-microbench.md#qualified-four-segment-scan-ranking)
   retain the selected rows, preflight and process-audit evidence, and clock contract.
2. **Java/JNI and .NET Native:** done in code — `libhft_java_jni` and `libhft_dotnet_native` run,
   pass the parity audit, and join the launcher's default list. Both passed the
   qualified scan campaign.
3. **`robaho_cpp_fix_codec`:** resolved by removing the adapter. Its historical
   timing cells remain withheld.
4. **Completed: `libtrading`.** Its public parser and unparser admit the full primitive
   matrix using numeric tags for `262` and `268`. The existing Stage-5 patch
   raises the field allocation from 48 to 128 for the depth-10 fixture. The
   hp smoke and mutation parity passed. The full scan pass also passed its
   functional gates. That 23-adapter campaign is archived with 22 clean
   selected windows; four Go-only preflights failed before timing. The current
   full ranking uses the frozen 25-adapter converted-values-v2 preparation.
   Libtrading's 25 operation rows and twelve isolated text/numeric probes pass
   and its selected host window is clean.
5. **Completed: `fix8`.** Implemented and admitted to the default roster. Its public
   `Message::factory`, field/group accessors and `Message::encode` passed all
   nine D/8/depth-10 W primitive rows and three stale-hash probes with the
   generated benchmark dictionary. Fresh source-pinned Beelink2 evidence and
   the scan SDK gate are in [the admission record](RECORD_2026-10-02_stage2-fix8.md).
   Its nine full-sample rows and twelve isolated converted-value probes pass
   in the current 25-adapter campaign, with a clean selected timing window.
   The earlier three-probe campaign and its repairs remain historical evidence.
6. **`llfix`: encoder-only partial admitted.** The checked-in adapter at the
   recorded 1.0.8 pin passed D/8/depth-10 W builds with cold framing and
   converted-value checks. SDK-generated SendingTime is included; no session
   or sequence file is opened. Receive parsing remains private and has no
   Stage 2 row. [The admission record](RECORD_2026-10-02_stage2-llfix.md)
   retains both the HP prototype and checked-in scan runner smoke. Its three
   encoder rows now pass the [qualified 25-adapter timing campaign](RECORD_2026-10-02_stage2-converted-values.md#qualified-scan-ranking).
