arborist/docs/onnx-vendor-capture-immunity.md
russell@unturf.com a4f3e126f7
feat(#000049 §7 #28): tinygrad NLI backend + deterministic engine-agreement A/B; ONNX-immunity rationale
Opt-in third NLI inference backend (ARBORIST_NLI_BACKEND=tinygrad) in
qa/nli/shadow.py, parallel to torch/onnx-int8, behind the #000049
cage: shadow-only, never an audit_mode input, never auto-preempts the
proven path (guarded so it cannot regress torch/onnx). Loads the ONNX
export through tinygrad's frontend wrapped to the existing
model(**enc).logits contract so _nli_batch is byte-unchanged.

bench/scripts/nli_backend_ab.py + make bench-nli-backends: deterministic
A/B, gate is numerical agreement with the torch reference first,
latency second (a divergent engine = a different shadow signal = a
different nli_policy_hash). Instrument is honest — reports a requested
backend as unavailable rather than relabelling a fallback's numbers.

First CPU-smoke run already quantified that the deployed §7 #22 int8
export diverges Δmax≈0.42 from torch — the immunity property made
measurable, not a defect. Real tinygrad numbers pending a producer-box
run (tinygrad not an arborist dep; frontend op-coverage for the large
MNLI checkpoints unverified by design).

docs/onnx-vendor-capture-immunity.md: why the model-in-proof-path cage
makes the inference engine an interchangeable sidecar, never a trust
dependency — public-domain positioning capital. Indexed in CLAUDE.md.

Full suite 2498 passed (identical to baseline); 24/24 NLI tests green.
2026-05-19 12:34:04 -04:00

6.3 KiB

ONNX vendor-capture immunity — why the engine is not a trust dependency

What this is: the rationale for why arborist is structurally immune to ONNX-format and ONNX-Runtime vendor capture — and why that immunity was a free side-effect of a discipline adopted for honesty, not for procurement reasons. Not a ticket; a positioning/architecture reference. Public-domain intellectual capital: the argument is reproducible and inheritable by any fork or downstream client that keeps the same cage. The headline: arborist puts zero neural inference in its proof path, so whichever vendor owns the inference engine owns none of arborist's trust boundary.


1. The capture vector, stated honestly

ONNX is an open model-exchange format; ONNX Runtime (ORT) is its dominant engine. Both are governed and overwhelmingly resourced by Microsoft. The systems-programming critique that motivated this note (ORT's ~2,500-line, many-flag, many-execution-provider build) is real, but it is the small version of the risk. The large version is the trust one:

Any system that places a neural model — its embedder, its NLI head, its reranker, its LLM — inside the chain of custody for its answers inherits that model's runtime, and therefore that runtime's vendor. The vendor's roadmap, deprecation policy, license drift, build complexity, and execution-provider politics all become load- bearing for your correctness guarantee.

This is the same defect class as a logged secret or a leaked context: a dependency that should have stayed at the edge has reached into the core. The fix is not "pick a better engine." The fix is not putting the engine in the core at all.

2. Where models are, and are not, in arborist

layer what runs there model? vendor-exposed?
Merkle commit (merkle.py) SHA-256, fixed combine conventions no no
Content store / cache key (store.py, qa/keys.py) canonical encodings, 8-dim key no no
Verifier (qa/verify.py) byte-identical lexical matching (quote / span / entity / paraphrase) no no
Audit chain (audit_events) sha256(prev ‖ canonical(body)) no no
NLI shadow (qa/nli/shadow.py) entailment contradiction veto yes yes — but sidecar
Relevance shadow (qa/relevance/shadow.py) cross-encoder aboutness yes yes — but sidecar
MT edges (qa/mt/translator.py) opus-mt query/display translation yes yes — but display-only

Everything above the rule is the proof path: deterministic, hash- anchored, model-free, reproducible from python + sqlite3 alone. Everything below is a sidecar that is demote-only, never an audit_mode input, never writes providence_cache / audit_events / run_dag_root (the #000049 model-in-proof-path cage; the verifier-stays-binary and "no soft signal in the hard chain" rules in CLAUDE.md). A model in arborist can only ever lower confidence as an advisory signal — it can never mint, promote, or sign a result.

3. The immunity, precisely

Because no model output is an input to any commitment, proof, cache key, or audit row:

  • The inference engine is interchangeable. arborist already swaps it silently: qa/nli/shadow.py prefers an optimum.onnxruntime int8 graph if one exists, picks CUDAExecutionProvider / CPUExecutionProvider, and falls through to the plain transformers + torch path on any failure — with no effect on any hash. ORT is an accelerator, not an authority.
  • A hostile vendor pivot is bounded and cheap. If Microsoft deprecated ORT, relicensed it, fragmented the ONNX opset, or its build complexity became untenable, the blast radius is: re-export the pinned checkpoint to whatever engine survives (torch always works; tinygrad's ONNX frontend is a candidate — #000049 §7). The checkpoint is already version-pinned in the NLI manifest; nothing in the proof path observes the swap. Cost is hours of producer-box work, not a correctness migration.
  • Cache validity is engine-independent. A record's falsification_state and audit_mode were decided by the deterministic verifier. Re-running tomorrow on a different engine cannot change a prior STRICT/HYBRID/UNGROUNDED verdict, because the engine never produced it. (Contrast: a system whose answer is graded by an LLM must re-grade — and silently re-trust — on every model or runtime change.)

The one place a model could ever touch authority is the deliberately- unwired Phase-3 demotion hook in #000049. Even there the design forces the exposure to be named: if NLI is ever allowed to demote audit_mode, nli_policy_hash (model + k + agg + guard + θ) folds into governance_policy_hash, so the model's identity becomes a first-class, cache-partitioning, audited policy input — capture made visible and versioned, never silent.

4. This was free — and that is the point

We did not design the cage to dodge Microsoft. We designed it because a model in the proof path is a honesty defect: a false-positive STRICT is corruption, and a model that can promote will eventually hallucinate a promotion. The deletion-first guardrails (delete the part: the model does not get to be in the hard chain) were adopted for truth.

Vendor-capture immunity fell out for free. That is the recurring shape of the right architectural call: a discipline taken for one good reason pays a second, unbid dividend. A system built to never lie about grounding turns out to also be a system no inference-engine vendor can hold hostage.

5. Why this is published intellectual capital

Per the Prime Mission, this argument is a gift to the ecosystem, not a moat. Any RAG / retrieval / agent system can adopt the same split: deterministic verifier in the proof path, models as demote-only sidecars on a producer box, engine identity versioned into a policy hash only if it is ever allowed to touch authority. A fork that keeps the cage inherits the immunity verbatim — no license, no dependency, no permission required. The cage is the gift.

See also: docs/lexical-first-rationale.md (the cost-asymmetry sibling — why the cheap deterministic path is the default), docs/soft-hash-channel-analysis.md (soft-vs-hard channel threat model), and ticket #000049 §7 (the NLI cage, shadow path, and the tinygrad/ORT engine-swap bench).