# Native cursor/index SDK gap investigation — 18 October 2026

**Private report. Primary/follow-up analysis and final integration/lifecycle validation passed.**
Selected: SDK1 native hot-path changes and the stock benchmark observer. SDK2 specialization and the full four-loop observer are rejected.
The constructor-cache addition made after timing has separately validated allocation behavior; its latency is untimed. This report separates SDK changes from benchmark-consumer effects.
It is diagnostic evidence from a shared VM, without exclusive-host qualification or release-performance claims.

## Decision supported by the primary experiment

SDK1 reduces Java JNI index process-p50 latency by 4.5–13.8% across the six tested index cells, while its cursor cells range from 2.2% faster to 1.4% slower.
The Java native cursor gap remains large: 5.61–5.71× pure for metadata traversal and 3.30–3.58× for traversal plus conversion.
The packed handoff and final emitted-code proof therefore do not establish a meaningful Java cursor latency improvement.

.NET Native cursor process-p50 improves 14.4–19.4% with tiering/PGO disabled, but .NET Pure cursor also improves 7.8–15.3% in that profile.
Those are two observed SDK contrasts; the native gain cannot be attributed wholly to interop removal.
With the separate default-policy, long-warmup experiment, cursor extraction improves 5.2% on D and 5.5% on W, and W metadata traversal improves 11.5%.
D metadata traversal is inconclusive across forks, including a retained, internally stable 535 ns/op candidate process.
Default .NET W index extraction regresses 3.5% in process-p50 and 6.5% in process-p99 of batch averages.
These mixed cells rule out a blanket “native is now faster” conclusion.

## Experiment and estimator

The primary contrast replaces SDK runtime artifacts while keeping observer/probe app code, fixtures, selected typed conversions, and frozen runtimes unchanged.
No split-loop observer is included. The baseline preparation is commit `45f8f66476bed0ea7b936653e7210ea679163188`.
The measured candidate is frozen SDK1, not whatever source or staging SDK currently occupies the checkout/build directory.

| Campaign | Providers and fixtures | Rounds per cell | Fixed warmup | Measured batches/process | Completed processes | Negative-probe rechecks |
|---|---|---:|---:|---:|---:|---:|
| diagnostic-sdk | Java Pure/JNI, .NET Pure/Native; D/8/W | 10 | 200,000 | 10,000 | 960 | 192 |
| default-dotnet | .NET Pure/Native; D/W | 5 | 2,000,000 | 10,000 | 160 | 64 |

All 1,120 scheduled processes and 256 exact negative-probe rechecks passed admission; the rechecks are per selected variant/provider, not 256 unique semantic scenarios.
Four additional untimed normal-launcher runtime-admission checks ran outside those counts.
`diagnostic` explicitly disables .NET tiered compilation and tiered PGO under both DOTNET and COMPlus variable names.
`default` removes those diagnostic JIT overrides; fixed warmup and low drift do not prove which tier ran every measured batch.
Java uses the frozen JDK's normal policy. No adaptive warmup is implemented.
All benchmark cursor/index objects are constructed outside the timed loop and reused. Each timed message still includes Init plus full cursor walk/observer, or Index plus observer; constructors are excluded. The Java fixtures enter through byte[] input.

Each raw sample averages 32 operations. “p50” and “p99” below are per-process percentiles of these batch averages, in ns/op; they are not individual-message tail percentiles.
Contrast points are geometric means of round-paired process-statistic latency ratios.
The same 5,000 bootstrap draws resample whole chronological fork-round blocks (seed 20261018), preserving within-round cell correlations.
Shuffled rounds are scheduling blocks, not adjacent ABBA pairs. Intervals are nominal percentile intervals without multiplicity correction; the five-round default results are exploratory.
Equal-weight aggregates describe an artificial selected-cell mix, not a production traffic distribution or pooled p99.

## Primary results

All ratios are candidate/reference latency: below 1 is faster. Native/pure above 1 means native is slower.
The full **64-cell table**, including absolute median process-p50/p99 values, SDK/baseline, paired pure controls, baseline/candidate native/pure and gap-change ratios with intervals, is [PRIMARY_CELL_TABLES.csv](native-cursor-index-gaps-20261018/PRIMARY_CELL_TABLES.csv).
The [complete comparison](native-cursor-index-gaps-20261018/NATIVE_GAP_COMPARISON.md) includes every cell's process-p50/p99 contrast; [its JSON](native-cursor-index-gaps-20261018/NATIVE_GAP_COMPARISON.json) retains round ratios, subgroup aggregates, scope and consumed-file hashes.
The relative links resolve within this private evidence package. Large source evidence remains under `/workspace/build/libhft/native-gap-20261018/`.

| Profile | Provider | Equal-weight SDK/baseline process-p50 [nominal 95%] | Process-p99 of batch averages [nominal 95%] |
|---|---|---|---|
| diagnostic | Java Pure | 0.996 [0.991, 1.001] | 0.985 [0.950, 1.020] |
| diagnostic | Java JNI | 0.950 [0.946, 0.953] | 0.906 [0.833, 0.977] |
| diagnostic | .NET Pure | 0.920 [0.918, 0.923] | 0.983 [0.958, 1.018] |
| diagnostic | .NET Native | 0.894 [0.892, 0.896] | 0.927 [0.898, 0.957] |
| default, five rounds | .NET Pure | 1.018 [1.013, 1.023] | 0.977 [0.926, 1.029] |
| default, five rounds | .NET Native | 0.972 [0.958, 1.000] | 0.989 [0.945, 1.036] |

### Controls, regressions and the retained 535 ns/op process

Java Pure's diagnostic process-p50 control intervals are within the stated ±5% reporting band, although D/8 index extraction shifts down 2.6%/1.9%.
A within-band interval describes this sample; it does not establish identical controls.
Java Pure's descriptor-prefix change is real SDK work and is measured, rather than treated as an unaffected constant.
The Java JNI index gain coexists with remaining small-frame native/pure overhead; W metadata indexing reaches 0.947× pure, while W index extraction is 0.987 [0.971, 1.002]×.
Java JNI cursor extraction D/W has point regressions of 1.4%/0.6% with intervals crossing 1; its metadata D/8 improvements are only 2.2%/1.5%.

.NET Pure moves materially in diagnostic mode: its aggregate process-p50 ratio is 0.920, with cursor cells 0.847–0.922.
The .NET Native/Pure aggregate gap changes by 0.971 [0.969, 0.975], smaller than the 0.894 native before/after ratio.
Private layout/shared code/JIT effects and shared-VM interference require attribution; the ratio of ratios is context, not a causal correction that removes control movement.

Default-policy .NET W metadata indexing is a specific misleading-gap case: native slows 0.6% while pure slows 7.7% [5.1%, 10.4%], improving native/pure from 0.743 to 0.694.
That relative gap improvement is not a native speedup.
Default native W index extraction slows 3.5% [3.4%, 3.7%] and its process-p99 batch-average ratio is 1.065 [1.008, 1.126]; pure also slows 3.6% in process-p50.
D native index extraction slows 0.9% [0.8%, 1.0%]; D metadata indexing improves 2.5% yet remains 1.372 [1.353, 1.392]× pure.
Diagnostic native W metadata indexing is nearly unchanged in p50 (0.997), while its process-p99 batch-average ratio worsens to 1.105 [1.021, 1.232].

For default D native cursor metadata traversal, baseline process-p50s by round are **341, 341, 341, 341, 340** ns/op; SDK1 values are **535, 298, 299, 300, 297**.
The first SDK1 process stays near 535 throughout: first/last-quarter medians 535.25/535.15625 ns/op, drift −0.018%.
It remains in the estimate, raw evidence and bootstrap. The paired process-p50 ratio is 0.984 [0.874, 1.243], not the ratio of the two median-process summaries.
Low within-process drift does not explain this between-process mode; current receipts cannot attribute it to a JIT tier, VM scheduling or SDK behavior.
See [fork receipts and summaries](/workspace/build/libhft/native-gap-20261018/default-dotnet/forks.csv), [the retained process receipt](/workspace/build/libhft/native-gap-20261018/default-dotnet/forks/00029/receipt.json) and [its raw chronology](/workspace/build/libhft/native-gap-20261018/default-dotnet/forks/00029/raw.csv).

## Shared-VM limits

The host reports Intel Xeon Platinum 8370C @ 2.80 GHz, x86-64, kernel 6.18.55, glibc 2.41, and a four-CPU-equivalent cgroup quota (`400000 100000`) across five visible CPUs.
Timed work used CPU4; launcher/telemetry used CPUs0–3. Builds and functional tests preceded each corresponding measurement capture.
Eight diagnostic processes exceeded the first/last-quarter ±5% drift flag (seven Java Pure, one Java JNI); all were retained. No default process exceeded it.
Two diagnostic fork windows recorded container throttling (21,376 and 50,133 µs); none did in default.
CPU4 sampled steal over enclosing campaign windows was 9.34%/9.14% for diagnostic/default.
Per-provider aggregate-VM steal during fork windows spans roughly 2.75–3.79%; these counters cover different scopes and cannot assign interference to a particular measured batch or cell.
A narrow nominal interval does not remove shared-host bias, code-generation variability, input limitations or the five-round default sample size.
No historical campaign/host/profile is pooled; matching fixture sizes alone would not justify pooling.

## Implementation and semantic boundaries

Shared C++ initialization returns the MsgType span it already parsed, avoiding rediscovery through RewindBody/NextBody/Rewind while preserving first Next at tag 8.
The same 24 length/data pairs generate one C++11-compatible constexpr 4 KiB read-only table without a dynamic-static guard.
`NextMessageCursorBytes` is forced inline into the actual packed JNI wrapper; the checked step and 64-byte pointer-free continuation stay intact.

Java JNI cursor packs successful tag/offset/length into one jlong; tag zero carries status, and high unsigned tags are decoded without treating the long's sign as status.
Java retains original-array public cursor offsets and adds the array start to native frame-relative offsets; the common observer normalizes to frame-relative offsets.
The bounded byte[] index path writes directly into the reusable int[] through permitted nested critical acquisitions, validating owners/bounds before pinning and retaining fallback for owners/capacities above 256 KiB.
Both Java providers use a three-word frame/MsgType prefix; Pure therefore has an actual internal representation change.

.NET caches an executable address from one additive cold getter and issues a suppressed-GC-transition indirect call after per-step pinning.
The getter returns the existing checked symbol from the P/Invoke-owned DSO. No manual native lifetime/resolver bypass is introduced.
The old ABI-69 library, missing the getter, retains a checked per-step P/Invoke fallback; Pure never resolves the getter or imports the native library.
The private C# cursor layout gains an address word, while public `IndexedField` remains 12 bytes and native continuation remains 64 bytes.

Every cursor Next still performs one real lazy native step, including END, with no preindex, batch walk, cached END or skipped state checks.
Malformed later fields fail at Next; an index detects them during construction. Borrowed-buffer immutability/lifetime requirements remain.
The grammar validates envelope, field bounds and CheckSum syntax, without numerically validating CheckSum in these parser operations.
`cursor-index-v1` parse-only observes metadata for every field, in wire order, plus field count/frame length (`metadata-fold-v1`); extraction also checks the same selected typed conversions (`converted-values-v2`).
D/8/W are **184/216/746 bytes and 18/21/73 fields**, respectively. END adds one Next; Init adds one native call, giving 20/23/75 total native cursor calls, not field counts.
Earlier 15acc shapes also used 18/21/73 fields; no fixture-change claim is made.

## Source, runtime and compiler proof

SDK1 is a **runtime SDK replacement over baseline-compiled observer/probe binaries**, not an application rebuild.
The verified composition preserves 2,057 protected artifacts byte-for-byte and changes launcher root prefixes only.
All 50 .NET SDK DLL copies, 25 native DSO copies, and the shared Java class/JNI roots are replaced, covering four normal adapters and all 96 compiled probe apps.
The candidate has private content inodes, independent of the baseline. Original preparation bytes remain historical baseline facts; candidate source/build/test evidence is separate.
The actual measured SDK1 source archive is `sdk-frozen/snapshot/provenance/sdk-replacement/current-sdk-source/{working.patch,untracked.tar,working-state.json}`; copies are in [measured-source evidence](native-cursor-index-gaps-20261018/source-evidence/sdk1-runtime-source/). The protected `snapshot/provenance/source` describes baseline compiled observers, not the replaced SDK.
The later `selected-before-integration.patch` contains the untimed constructor cache and omits untracked files; it is labeled a tracked-only selected preview, never the SDK1 measurement patch.

| Frozen input | SHA256 |
|---|---|
| [Baseline manifest](/workspace/build/libhft/native-gap-20261018/baseline-frozen/snapshot/snapshot.json) | `54502592ba0389e117754b6e9ba9875e089b39167f98a41f6f459acc450409f4` |
| [SDK1 manifest](/workspace/build/libhft/native-gap-20261018/sdk-frozen/snapshot/snapshot.json) | `0c1c08db8e223db5088c5885e6efeea43dbd1bb6c72a3ef7ef5387c2df4b47cc` |
| Candidate source-state object | `235b92b73f1fb537bdfafc46e05d8584ea6e11e4b4e1ff98cdba4864097e796c` |
| Java source/compiler input seal | `51369bb6f681b160da84d50dee24c50efe6d1f36b7f04325702438f106c0e309` |
| .NET source/compiler input seal | `3445dbb5303cde043e1fd33d45f86c5e870d52b901b0c29027768ba39f546115` |

| Exact measured SDK1 artifact | SHA256 |
|---|---|
| Java classes tree | `578ed125f7dce97e3b57a9b5e6df654055c236f044460a9a274e463734cc3777` |
| libhftjava.so | `cb7e2e7a0b00d03a552db14fc219dd3cc83f2c3fe4a8c2cdc11ab6815503235f` |
| Atrekes.Libhft.dll | `44569642709db4a4f74cde78d8a59d6c359604a5ea1610b92c61d92301dd8f2b` |
| libhftnet.so | `b90acd3ec3e6a456389bfc87367cfc9254dcdf51806bf41d9eb1bb5bffa8c984` |

Frozen Java is Oracle JDK 21.0.12.1+1-LTS-4; Java compilation retains release 8, Maven 3.9.11 and GCC 14.2.0.
The SDK DLL targets net8.0 and was compiled with .NET SDK 10.0.402; the normal/parity observer apps target net10.0 and actually load the frozen **CoreCLR 10.0.13**.
Host traces, rather than target labels alone, confirm normal-launcher runtime selection in both variants; correctness tests on 8.0.32 are separate evidence.
Controlled rebuilds force the original Java/Maven, JNI/native Makefile and .NET compiler recipes without changing optimization flags.
Recorded pre/post source/compiler inputs, include/resource headers, dependency caches and effective build environment match.
Initial language builds lacked complete successful-build pre-input seals; later controlled rebuilds and final exact-artifact validation supply that evidence without inventing earlier seals.
These are conservative selected build-input seals with host loader/libc dependencies, not a hermetic build or release-SDK certification.

## Correctness, compatibility and emitted-code admission

Java public `MessageCursor`, `MessageIndex`, `MessageCodecStatus` and `RuntimeProvider` APIs are unchanged; no class was added/removed and exactly three of 1,222 SDK class files changed.
SDK1's internal JNI ABI fingerprint is `27f689e02a714d6f022abea6505fbc72cce5b554a047bbf7fa19feece9aafac9`.
.NET preserves ABI 69 with exactly one additive getter export (217→218), descriptor/state sizes and old-library fallback.

| Evidence phase | Passed checks |
|---|---|
| Initial Java | 248 focused executions (62 × four configured JVM runs), plus 12 -Xcheck:jni tests; native critical-array unit with both TLS settings and ASan/UBSan; 222 Java-8 open sources and ten boundary probes |
| Final SDK1 Java | Fresh no-build JVM, 58 focused -Xcheck:jni tests against the explicit final DSO; all 1,222 rebuilt class bytes match the previously tested inventory |
| Initial and final .NET | 88 tests on actual 8.0.32 and 88 on actual 10.0.13 in each phase; eight production caller/getter/old-ABI/pure-isolation probes repeated after controlled rebuild, with zero failures/skips |
| C++ | Initial and final normal + ASan/UBSan cursor/codec units; 26,664 codec checks/run; all 65,536 tags, 2,048 table slots, 24 binary pairs, raw MsgType spans, lazy errors, relocation/unaligned/corrupt-state cases |

The final JVM run omits ABI-retry because that test builds incompatible fixture libraries; it passed in the initial execution set.
.NET constructor allocation checks record zero managed allocations across 2,000 construct/init/walk cycles after normal runtime warmup.
Final real JNI DSO inspection confirms one direct-owner validation call, no outlined checked-step call, register metadata packing without post-store reloads, and one guard-free read-only table.
Actual .NET production callers on runtimes 8/10 contain the native indirect call with pinned locals and no Next/NextNative wrapper calls; rejecting-resolver subprocesses preserve pure isolation under default and disabled tiering.
These establish implementation/code-generation facts, not a latency conclusion for every caller.

The timing driver verified complete snapshots before/after each campaign and checked summaries against raw chronology.
The comparison independently checks schedules, exact probes/rejection logs, receipts, summaries and workload/tool seals; it hashes retained raw files without reparsing every sample or rehashing every runtime byte.
See [implementation details](native-cursor-index-gaps-20261018/implementation-notes.md), [controlled rebuild readiness](native-cursor-index-gaps-20261018/controlled-rebuild/ready-receipt.json), [combined final validation](native-cursor-index-gaps-20261018/final-focused-validation.json), [Java final validation](native-cursor-index-gaps-20261018/final-java-validation/receipt.json), [.NET final validation](native-cursor-index-gaps-20261018/sdk-validation/final-revalidation/receipt.json), [final C++ checks](native-cursor-index-gaps-20261018/cursor-inline-checks/results.json), [actual JNI codegen](native-cursor-index-gaps-20261018/java-final-packed-next-receipt.json), and [snapshot composition](/workspace/build/libhft/native-gap-20261018/sdk-frozen/snapshot/provenance/sdk-replacement/composition.json).
Initial receipts and source patches/archives are retained within snapshot replacement provenance; [execution.json](native-cursor-index-gaps-20261018/execution.json) and [measurement inputs](/workspace/build/libhft/native-gap-20261018/measurement-inputs) identify the timing tools and chronology.

## Bounded follow-ups and final selection

The two follow-ups completed **400 timed processes (240 Java + 160 .NET), 160 negative-probe rechecks (96 + 64), and four additional actual .NET runtime admissions**.
Both use five rounds and the same batch statistics/whole-round inference caveats. Java uses diagnostic warmup 200,000; .NET uses default policy with warmup 2,000,000.
[Full follow-up cells](native-cursor-index-gaps-20261018/FOLLOWUP_CELL_TABLES.csv) contain 40 reference/candidate provider rows (80 absolute variant/provider cells), p50/p99, paired Pure responses and native/Pure gaps.
[Follow-up comparison](native-cursor-index-gaps-20261018/FOLLOWUP_COMPARISON.md), [machine-readable proof](native-cursor-index-gaps-20261018/FOLLOWUP_COMPARISON.json) and [detailed selection](native-cursor-index-gaps-20261018/FOLLOWUP_SELECTION.md) preserve all cells and consumed-input hashes.
These campaigns remeasure their own controls; their estimates are not pooled or paired with the primary SDK1 campaign.

| Isolated experiment | Observed benefit | Limitation / regression | Selection |
|---|---|---|---|
| Java SDK2 specialized All/Body JNI entries vs SDK1, same frozen compiled observers | Six JNI cursor p50 cells improve 1.6–4.0%; all-12-cell native aggregate 0.986 [0.981, 0.993] | No cursor cell reaches the approximately 5% practical gate; D metadata/W extraction intervals include no gain. Native batch-p99 aggregate 1.025 [0.988, 1.064] | Reject SDK2; restore the exact four SDK1 Java source backups |
| Fresh .NET four-loop observer vs fresh matching-recipe stock observer, identical SDK1/runtime bytes | Native metadata index improves 13.6% D / 28.4% W; Pure improves 23.0% D / 20.8% W | Native W cursor extraction p50 **+4.21% [3.84%, 4.62%]**, batch-p99 **+23.1% [0.76%, 50.8%]** | Reject wholesale four-loop observer; restore both original templates |

The Java candidate's remaining native/Pure cursor ratios are 5.26–5.58× for metadata and 3.26–3.46× for extraction.
Its W cursor-extraction batch-p99 increases 1.68% [0.60%, 2.74%]; no repeatable cursor tail benefit is established.
Pure Java metadata-cursor D/8/W responses are −3.7%/+2.8%/+2.8%, despite unchanged control source; three Pure forks drifted and none were removed.

The .NET native all-cell p50 aggregate improves 6.64%, while Pure improves 7.37%, leaving the aggregate native/Pure ratio 0.79% worse.
For D metadata indexing, native improves yet native/Pure worsens **1.351→1.514**, because Pure improves more. Neither provider is a stationary control in this observer experiment.
Native extraction indexing improves 2.4–2.8%, but those gains do not override the unrelated W cursor-extraction regression.
The emitted four methods and non-address-exposed metadata-fold accumulators explain a testable consumer-codegen hypothesis; they do not make this an SDK parsing gain.
A future structural-only consumer preserving stock extraction is unmeasured and cannot inherit the rejected candidate's numbers.

All follow-up observations remain. Three Java Pure forks exceeded ±5% drift; no JNI/.NET fork did and no follow-up fork recorded throttling.
Enclosing CPU4 steal was 9.12% for Java and 9.67% for .NET; no exclusive-host or timed-tier qualification is implied.
Rejected frozen SDK2/stock/split snapshots, recipes, codegen, functional gates and all raw measurement evidence remain external.
The .NET admission built 36 fresh SDK10/net10 apps and passed 72 actual-runtime gates before timing; reused stock negative apps retain explicit original-compilation provenance and were rechecked during the final campaign.

### Selected constructor lifecycle addition and final integration

After timing, native .NET construction gained an explicit nested static constructor reached through a non-inlined native-only helper, caching the getter address or the missing-getter fallback zero once per SDK module.
Its **latency is untimed; allocation behavior is separately validated**. It is absent from the SDK1 capsules underlying all 1,520 timing forks and 416 repeated negative probes.
Eight modern/real-legacy cases (runtimes 8.0.32/10.0.13 × default/diagnostic) each ran 4,096 warmup and 2,000 measured construct + Init + complete five-field walks against an independent oracle.
Every final case allocated zero managed bytes and raised zero missing-entry exceptions after cold initialization. Legacy cold use raised one missing-entry event; modern cold use raised none. Concurrent first use was not tested.
The **same compiled app** with untouched SDK1 and the real old ABI-69 DSO provides two expected-exit-3 controls: both produce 4,096 warmup and 2,000 measured missing-entry events; 2,000 measured cycles allocate **2,256,000 bytes on runtime 8** and **2,112,000 on runtime 10**.
Those controls establish allocation-regression sensitivity without changing the app or frozen SDK1 files; they do not measure latency.

Selected source is `3e784ddcf3c265b7cb078013e85100748164c649`, rebased on `78e3c43d9`. Fresh post-sync C++/Java/.NET build and correctness qualification passed; the final integrated binaries remain unmeasured for latency.
All reported timing identities remain baseline `45f8f66476bed0ea7b936653e7210ea679163188` plus each immutable SDK1/SDK2/observer capsule's sealed patches, never the later integrated HEAD.

| Final integration evidence | Actual passed admission |
|---|---|
| [C++ receipt](native-cursor-index-gaps-20261018/integration-cpp/receipt.json) | Two normal and two ASan/UBSan units; 26,664 codec checks/run; 15 cursor cases including 65,536 tags and 24 binary pairs; one shared read-only 4,096-byte table; 319 source inputs and compiler identities unchanged |
| [Java receipt](native-cursor-index-gaps-20261018/integration-java/validation/receipt.json) | 58 codec/cursor + 7 SPI/native-required/ABI-retry tests under -Xcheck:jni, zero failures/ignored/assumptions and actual-library mapping proof; 243 native declarations with restored SDK1 ABI; 225 open Java-8 sources/12 boundaries; critical unit with two TLS modes and ASan/UBSan |
| [.NET readiness](native-cursor-index-gaps-20261018/integration-dotnet/receipts/ready.json) | 98 tests on each actual runtime 8.0.32/10.0.13, zero failures/skips; eight production codegen/isolation/fallback probes and eight lifecycle cases; real ABI-69/Pro-1 providers with 218 exports/getter and legacy 217/no getter; 841 source/fixture and 757 compiler/reference files unchanged through validation |

Java source/compiler inputs were unchanged; Maven honestly records 90 added cache files, with no existing cache file changed or removed. The final restored JNI fingerprint remains `27f689e02a714d6f022abea6505fbc72cce5b554a047bbf7fa19feece9aafac9`.

| Untimed final integrated artifact | SHA256 |
|---|---|
| Main .NET DLL | `6bea574783ba84abe09c5421dbf51b8a27f139e9876b027732573cd93b60c98a` |
| Pro .NET DLL | `9a61849db7a43e89748a858eda53ea693f67fdff147c4faae20aa33b1ac02ff1` |
| .NET native DSO | `3ed4713c8b8c9f6f6c18fcd86c582915628badb938b5f57b0bfe3ef0562144ff` |
| JNI DSO | `f7e51fc816e853b3d2ec1e4d32d6eab2386cb6d8b79ba025a43f41fab862867b` |


Final synchronization preserved the validated source commit as an ancestor: merge `5b95cb992` incorporates upstream `5795b7587`. The later upstream edits affect Rust/private qualification tools, documentation and only the unused Rust mmap-fixture target in the C++ rules; Java/.NET sources, shared C++ headers and both qualified C++ unit sources remain byte-identical to `3e784ddcf`. No additional latency claim is made for the merge. The cloud shell setup now explicitly selects `oss/cpp`, `private/cpp` and the Java companion roots, matching the inputs already supplied to the validation commands.

A later push-time synchronization, merge `c7795e01d`, incorporates upstream `287b94f79` (Rust venue/conformance relocation and documentation/tool-path updates). The qualified production source scopes remain byte-identical to the validated commit; the additional C++ Makefile change only relocates an unused Rust SBE fixture target. [Synchronization proof](native-cursor-index-gaps-20261018/integration/root/final-push-synchronization.json) records the comparison scopes.

## Remaining gap work and evidence location

The completed evidence supports the selected implementation and its stated limits. No additional performance campaign was run after the bounded follow-up.
The following five rows identify future work, not claimed gains or additions to the completed bounded campaign.

| Priority | Remaining question | Required contract/evidence |
|---|---|---|
| 1 | Per-field JNI boundary and initialization copy cost | Profile real public-API callers, owner checks and the 20/23/75 full-walk crossings. Preserve lazy Next/error timing, bounds and borrowed-buffer lifetime. A separately named coarse projection/batch API is optional future work and cannot replace or claim a win for the equivalent cursor. |
| 2 | Default .NET D metadata-index gap and consumer shape | Capture the timed hot method's JIT tier/code shape before attributing 1.372× Pure to pinning/calli. A later structural-only consumer must keep stock extraction, the complete metadata fold/walk/END, sinks and verification, with its own measurements. |
| 3 | Stable 535 ns/op process and batch-tail variability | Retain it and all drift/throttling evidence; independent randomization and controlled-host/tier evidence are needed to distinguish process modes and scheduling. Batch-p99 remains a percentile of averages, not an individual-message tail. |
| 4 | Allocation pressure and JNI critical-array/input-form behavior | Test realistic GC pressure, borrowed-buffer lifetimes, byte[]/direct-buffer forms, bounded critical ownership and oversized fallbacks; do not extrapolate from warm reuse or the separate constructor-cache checks. |
| 5 | Workload matrix beyond three fixtures | Vary message shape, length/data pairs, malformed/lazy prefixes and application conversion selections, pairing same-campaign Pure controls and preserving cursor/index access distinctions. |

This record is filed at `internal/docs/bench/RECORD_2026-10-18_native-cursor-index-gaps.md`, alongside existing private Stage2/performance records.
Sibling data directory `native-cursor-index-gaps-20261018/` contains `PRIMARY_CELL_TABLES.csv`, final follow-up cell CSVs, comparison Markdown/JSON, helper recipes/seals, selected small source patches and validation receipts.
Raw 10,000-batch process files and runtime capsules remain external under `/workspace/build/libhft/native-gap-20261018/`; keep their explicit paths and snapshot/consumed-input hashes in the packaged evidence. Do not copy this host-specific evidence into public SDK documentation.
The original evidence package was prepared externally from existing evidence through light CPU0–3 file work without executing builds, tests or benchmarks. Final filing adds this report and its selected evidence to the private repository; it does not rerun or extend the campaigns. [Package instructions and seals](native-cursor-index-gaps-20261018/PACKAGE_README.md) describe the filed layout and retained external evidence.
