ABNG — An Experimental Adaptive Belief Network Graph for CJC-Lang
Bayesian uncertainty, cryptographic dataset attestation, and adaptive structural triggers — proven by demos, honestly bounded by what’s still unfinished.
CJC-Lang
ABNG
Bayesian
determinism
attestation
experimental
ABNG (Adaptive Belief Network Graph) is an experimental cjc-abng crate exposing 75 abng_* builtins to CJC-Lang. This post walks through eleven distinct capabilities — eleven demos, six in CJC-Lang source — and what each one actually proves. Plus the honest gaps: ABNG is still experimental, the interpreter is a known bottleneck, and there’s compiler/interpreter work ahead before this becomes production-grade.
Author
Adam Ezzat
Published
May 8, 2026
Modified
May 8, 2026
🕑 ~32 min read
75abng_* builtins
11capabilities demoed
960tests passing
v12snapshot magic
TL;DR
ABNG is an experimental addition to CJC-Lang shipping in the cjc-abng crate (Phase 0.1 → 0.5). It exposes 75 abng_* builtins for building adaptive belief network graphs — Bayesian-flavored ML primitives that combine codebook-routed BLR posteriors, an SHA-256 audit chain, structural-decision triggers, and per-node provenance stamps. This post walks through eleven distinct capabilities, each proven by a working demo (six of them written in CJC-Lang source and parity-tested across both executors). It also names what’s not proven yet — and why interpreter / compiler work is on the critical path before ABNG becomes production-grade.
ABNG is experimental
ABNG is a research-grade addition to CJC-Lang. The capabilities described here are demonstrated by tests, but not yet productized: there are no benchmarks against industry baselines, no cross-platform CI verification of the determinism canaries, and no real-world case study of someone using ABNG to attest a deployed model. Treat this as a progress report, not a production recommendation. The “what’s still missing” section at the end is honest about the gaps.
On data scale: these demos prove “the capability works”, not “it works at scale”
Every demo in this post uses small datasets — between 3 and 200 training samples per scenario. The choice was deliberate: the goal is to prove each capability is real (not vacuous), under enough data to make the assertion meaningful, in a workload small enough that every test runs in milliseconds and produces a locked SHA-256 canary.
What this means concretely:
The capabilities are demonstrated at the scale tested. Where I claim the BLR posterior converges to the analytical solution, I trained on tens of samples, not thousands.
Performance at production scale (n > 10⁴) is not yet benchmarked. The asymptotic claims (e.g., per-leaf BLR is O(N · d²) vs classical GP’s O(N³)) are correct by construction — the math says so — but no demo has wall-clock measurements at large N.
The demos are reproducibility gates, not load tests. They lock determinism canaries; they don’t prove ABNG handles realistic ML-training dataset sizes.
Each demo’s data scale is named explicitly in its “Why this workload” subsection below — read those if you want to know exactly what level of evidence backs each claim.
Wall-clock benchmarks at n ∈ {10³, 10⁴, 10⁵, 10⁶} and a large-dataset case study are explicitly listed as next steps in the “What’s next” section. Until those land, every claim in this post should be read as “proven at small scale, theoretically expected to hold at larger scale, empirically unverified at production scale.”
What is ABNG?
ABNG — Adaptive Belief Network Graph — is a cjc-abng crate that lives alongside the existing CJC-Lang workspace. The goal: provide ML primitives that go beyond “fit some weights to data” by adding three orthogonal axes:
Bayesian uncertainty at every prediction (mean, epistemic leverage, aleatoric variance) — without MCMC, ensembles, or variational inference.
Cryptographic provenance of training history — every observation, every weight update, every structural decision lands in a SHA-256 audit chain that detects post-hoc tampering.
Adaptive structure — the graph’s topology genuinely changes under the workload. Nodes grow, split, merge, prune, compress, freeze under a deterministic decision-policy engine.
The architecture is a radix-style graph: a root node descends into children indexed by prefix bytes from a quantile codebook, and each leaf carries a Bayesian Linear Regression posterior over a feature basis. Predictions descend the tree, the posterior at the resolved leaf gives (mean, epistemic_leverage, aleatoric_var), and structural decisions fire periodically to adapt the tree.
flowchart LR
F["State<br/>features"] --> CB["Codebook<br/>encode"]
CB --> P["Prefix<br/>bytes"]
P --> D["Descend<br/>tree"]
D --> L["Leaf<br/>BLR"]
L --> Pred["(mean,<br/>leverage,<br/>aleatoric)"]
L -.->|every step| AC["Audit chain<br/>SHA-256"]
ABNG conceptual flow. State features → codebook prefix → descend tree → BLR posterior at leaf → (mean, leverage, aleatoric) — plus an audit chain that hashes every step.
It’s exposed to CJC-Lang as 75 abng_* builtins — abng_new, abng_set_codebook, abng_set_leaf_head, abng_set_blr_prior, abng_observe, abng_blr_predict, abng_descend, abng_decide_step, abng_stamp_provenance, etc. — so you can build and drive an ABNG graph entirely from .cjcl source.
The proof discipline
Every capability in this post has the same proof structure:
A workload — a small but non-trivial problem where the capability should manifest if it’s working.
A specific assertion that would fail if the capability didn’t work.
A locked SHA-256 canary of the resulting graph’s chain_head, gating any future regression.
Cross-pipeline determinism: most demos run through both cjc-eval (AST tree-walk) AND cjc-mir-exec (MIR register-machine) with byte-identical printed output asserted.
That last property is the strongest determinism gate the project supports. AST and MIR are completely different code paths — if any FP arithmetic, hash computation, or dispatch routing differs anywhere in the pipeline, the printed output diverges and the parity test fails.
The Bayesian Linear Regression posterior at each codebook-routed leaf returns a 3-tuple (mean, epistemic_leverage, aleatoric_var). The headline assertion is a strict spatial-partition property:
min(epistemic_leverage at edge probes) > max(epistemic_leverage at interior probes)
This is stronger than “average edge uncertainty exceeds average interior uncertainty” — it’s a no-overlap claim. After training, the worst-case interior point has lower uncertainty than the best-case edge point. The signal cleanly partitions evidence-rich vs evidence-poor regions of the input space.
The fit-quality assertion (abs_err_50 < 0.05) proves the mean also converges to the analytical solution. Without this, “leverage is uncertain at edges” would be vacuous — the mean could just be wrong everywhere.
The math
ABNG’s BLR uses a Normal-Inverse-Gamma (NIG) conjugate prior on (w, σ²). Given a prior (μ₀, Λ₀, a₀, b₀) and a stream of observations D = {(φᵢ, yᵢ)}, the conjugate update yields a closed-form posterior:
Variance contribution from posterior over w (model uncertainty)
aleatoric_var
\(b_n / (a_n - 1)\)
Observation-noise variance (data uncertainty)
Two cleanly separated uncertainty channels. epistemic_leverage shrinks as ΦᵀΦ accumulates — more data → tighter posterior over w → less model uncertainty. aleatoric_var converges to the irreducible noise floor. A vanilla MLP collapses both into a single point estimate; ABNG keeps them factored.
Why the heat equation
The 1-D heat equation ∂u/∂t = α∂²u/∂x² with Dirichlet BCs u(0,t) = u(1,t) = 0 and IC u(x,0) = sin(πx) was chosen because it has a known closed-form analytical solution:
\[u(x, t) = e^{-\alpha \pi^2 t} \sin(\pi x)\]
That choice is load-bearing. The BLR feature basis is [1, x, sin(πx), cos(πx)] — the analytical solution exp(-π²·0.1)·sin(πx) is exactly representable as a single non-zero coefficient on the sin(πx) basis function. So with enough samples, the BLR posterior mean must converge to the truth. If it doesn’t, the BLR implementation is broken — not the test. This is the same trick statisticians use to validate Bayesian implementations: pick a problem where the truth is in the basis, and the posterior is a closed-form limit.
The training schedule is deliberately asymmetric: 32 samples uniformly across [0.25, 0.75] (interior), 4 samples each in [0, 0.25) and [0.75, 1] (edges). Without this asymmetry, uniform training would give uniform leverage and the spatial-partition property would be invisible.
Data scale: 40 training samples total (32 interior + 8 edge), 5 probe points. Enough to demonstrate the spatial-partition property; not enough to claim ABNG matches state-of-the-art Bayesian PINN methods on realistic PDE problems. Validating against PINN benchmarks (NS equations, 2-D Burgers) at 10⁴+ collocation points is future work.
After training, a probe at x = 0.5 returns (mean ≈ 0.372, lev_low, ale_finite) — mean matches exp(-π²·0.1) · sin(π·0.5) = 0.372 to within 0.05. A probe at x = 0.05 returns (mean ≈ small, lev_high, ale_finite) — different leaf, less data, higher leverage.
Connection to existing techniques
Technique
Posterior representation
Cost
Decomposed epistemic/aleatoric?
Gaussian Processes
Full posterior over functions
O(n³) Cholesky
Yes (kernel + likelihood noise)
Deep ensembles
Variance across M trained nets
M× training cost
Approximately
MC dropout
Variational approx via dropout
Cheap, biased
No (entangled)
Conformal prediction
Distribution-free intervals
Cheap
No
ABNG per-leaf BLR
Closed-form NIG posterior
O(d²) per update
Yes (clean factor)
ABNG slots between GPs (full posterior, expensive) and conformal prediction (distribution-free, no decomposition). It’s most similar in spirit to Bayesian Additive Regression Trees (BART) — leaf-localized closed-form Bayesian fits — but with a quantile-codebook routing instead of stochastic tree growth, and a stronger determinism contract.
What this enables
Active learning in scientific computing — query the BLR for epistemic_leverage; sample where it’s highest.
Uncertainty-aware PINNs — refuse to predict in regions where leverage exceeds a threshold; fall back to a higher-fidelity solver. This is what the PINN community has been chasing via deep ensembles or HMC for years.
Heteroskedastic regression done right — the aleatoric/epistemic split lets you tell “this region is intrinsically noisy” from “I haven’t seen enough of this region yet.”
Uncertainty propagation downstream — feed the Student-t predictive into Monte Carlo / robust optimization pipelines.
The PINN literature has long treated Bayesian uncertainty as expensive infrastructure (HMC, variational PINNs). ABNG provides this as a closed-form per-leaf posterior — no inference machinery beyond a Cholesky update.
Demo: Tabular regression on the unit square (tests/test_abng_tabular_gp_cjcl.rs + tabular_source.rs)
What this demo proves
Five distinct properties asserted as separate tests:
max(per_leaf_n_seen) < 0.9 × N — no single leaf holds the entire dataset. Evidence is partitioned, not duplicated.
sum(per_leaf_n_seen) == N — every training point routes to exactly one leaf. No double-counting; no points lost.
max_per_leaf_bounded — the partitioning works as designed. (This is the scalability claim’s empirical foundation.)
trained_mse < 0.5 × prior_mse — the BLR posterior actually learns. Without this, the structure-of-the-graph properties would be vacuous.
lev_shrinks_with_data — leverage at a fixed probe drops from a 16-pt-trained leaf to a 64-pt-trained leaf. GP-like uncertainty contraction reproduced.
The math (cost analysis)
ABNG’s per-leaf cost decomposes cleanly:
Operation
Cost
Codebook descent
O(n_dims · log(n_bins))
BLR conjugate update at one leaf
O(d² · n_leaf) (Cholesky-factor maintenance)
Predictive at query point
O(d²) (one Cholesky solve)
Aggregate cost across N points, L leaves
O(N · d²) — linear in N
Compare to a classical GP on the same N points:
Operation
Cost
Kernel matrix K_NN
O(N²) memory
Cholesky decomposition
O(N³) compute
Predictive variance per query
O(N²)
ABNG is asymptotically O(N · d²) vs GP’s O(N³). For d = 4 (the demo’s basis dim) and N = 10⁴: ABNG ≈ 1.6×10⁵ ops; GP ≈ 10¹² ops — seven orders of magnitude.
The trade-off: ABNG’s leaves are independent — no global covariance structure. For workloads where local Bayesian behavior is sufficient (most tabular ML), this is a feature, not a bug.
Why bilinear regression on the unit square
The target is y = 0.3 + 0.5x₁ + 0.4x₂ + 0.7·x₁·x₂. Three deliberate choices:
Exactly fittable in the BLR feature basis[1, x₁, x₂, x₁·x₂]. With enough samples, BLR posterior mean → exact coefficients [0.3, 0.5, 0.4, 0.7]. This makes the MSE-reduction test meaningful: any residual MSE comes from sample efficiency, not from the model class being wrong.
2-D input with a routing dimension. The codebook routes by x₁ (4 bins) and lets x₂ become a within-leaf feature. This mirrors the canonical ABNG pattern: use one dimension to partition the input space, regress on the rest within each leaf. Common in production: route by (time-of-day, geo-region) and regress on the rest within each segment.
Unit square domain — predictable, no scaling tricks. Codebook boundaries [0.25, 0.5, 0.75] evenly partition [0, 1].
Data scale: Two trained graphs (16-point and 64-point training sets) plus a 200-point graph for the per-leaf-bound check; 32 held-out test queries for MSE comparison. The asymptotic claim “ABNG O(N · d²) beats classical GP O(N³)” is correct by construction but not yet wall-clock-benchmarked at scales where it matters (n ≥ 10⁴). Without those numbers this is a “promising primitive,” not a measured speedup vs. e.g. SVGP / sklearn’s GaussianProcessRegressor / GPy.
After 200 training points, every leaf has its own BLR posterior with mean → [0.3, 0.5, 0.4, 0.7] and increasingly sharp leverage. Sum of n_seen across all four leaves equals exactly 200; the largest leaf holds well under 90% of points.
Connection to existing techniques
Technique
Routing
Per-leaf model
Scaling
Classical GP
None (global)
Kernel
O(n³)
Sparse GP / SVGP
Inducing points
Kernel
O(n·m²) for m inducing pts
Mondrian Forest
Stochastic tree splits
Point estimate
O(n·log n)
BART
Stochastic tree splits
Bayesian posterior
O(n·log n) per tree
Locally-weighted regression
Distance-weighted
Linear fit
O(n) per query
ABNG
Quantile codebook (deterministic)
Closed-form NIG posterior
O(N · d²)
The closest analog is Mondrian Forests (Lakshminarayanan, Roy & Teh 2014) — both partition the input space and fit per-leaf models. The differences: Mondrian uses stochastic splits and point estimates; ABNG uses deterministic quantile boundaries and full closed-form Bayesian posteriors. ABNG also adds the audit chain that Mondrian doesn’t have.
What this enables
Drop-in tabular Bayesian regression at GP-prohibitive sizes (n > 10⁴ where dense Cholesky becomes painful)
Online / streaming learning — incremental BLR update is O(d²) per point, vs O(n²) incremental for classical GP rank-one updates
Snapshot-checkpointable Bayesian state — classical GPs don’t naturally serialize (you’d persist the full Cholesky factor + raw data); ABNG snapshots the entire posterior in one blob via the audit chain
Feature-engineered uncertainty quantification — the practitioner picks routing dimensions (time-of-day, region, customer segment) and the per-segment posteriors are independently calibrated
Warning
The asymptotic claim is correct by construction (the math says so) but not yet benchmarked against a classical GP at realistic sizes. Wall-clock benchmarks at n ∈ {10³, 10⁴, 10⁵} need to be run before treating ABNG as a “GP killer.”
Capability 3 — Tamper-evident training history (audit chain)
Demo: Universal property — every demo asserts it.
What every demo proves
Every state mutation in ABNG funnels through one chain step:
Every demo asserts verify_chain == Ok after training. For an n-event audit log, verify_chain walks the chain forward, recomputing new_hash at every step from the stored previous hash and the stored canonical payload, and compares to the stored value. Any single byte changed in any payload would fail the recomputation at that event AND every subsequent event — a Merkle-style cascade.
Across the 11 demos, this code path runs hundreds of times. The fact that verify_chain == Ok after every demo’s training session is the empirical proof the audit chain is sound under realistic workloads.
The threat model
The audit chain is designed against three concrete attacks:
Post-hoc rewrite. Attacker modifies a historical training observation after the fact. Detection: the modified event’s new_hash no longer matches sha256(previous_hash ‖ payload), AND every subsequent event’s stored previous_hash no longer matches the recomputed-from-modified value. Cascading detection.
Event reordering. Attacker shuffles the order of training events. Detection: each event’s payload includes its seq number; reordering puts events at the wrong seq, breaking the per-event monotonic-seq invariant.
Event substitution. Attacker replaces one event with another that produces the same new_hash. Detection: SHA-256 collision resistance — finding two payloads with the same hash is computationally infeasible.
Data scale: Universal property — every demo’s audit log (3 to ~110 events, depending on workload) gets verified. The largest audit log in the test suite is the PINN demo’s 110-event chain post-asymmetric training. The cryptographic argument scales independently of dataset size: SHA-256 chain verification is O(log(events)) per check and the security guarantees come from the hash function itself, not from the workload. So this is the one capability where “small data demos suffice” is a load-bearing claim, not a hedge.
Code sketch — what an event payload looks like
[seq u64 BE | epoch u64 BE | node_id u32 BE | kind tag u8 |
kind body (variable) |
stats_version u64 BE | stats_hash [u8; 32]]
For a BeliefUpdate event (the most common kind, fired on every abng_observe), the kind body is a single value f64-bits BE — 8 bytes. Total payload for a BeliefUpdate is 21 + 8 + 8 + 32 = 69 bytes, hashed into the chain.
The canonical encoding is byte-deterministic: big-endian on every numeric field, fixed sizes, no padding ambiguity. Two ABNG instances run with the same seed and observation order produce identical event payload bytes — and therefore identical chain hashes.
Connection to existing techniques
Technique
Hash-chain
ML-aware
Per-prediction lineage
Git
Yes (DAG of commits)
No
No
MLflow
No (timestamped log)
Yes
Manual
sigstore / in-toto
Yes (artifact attestation)
No
Per-artifact
Blockchain ML provenance
Yes (full chain on-chain)
Yes
Yes, but expensive
ABNG audit chain
Yes (linear)
Yes (every state mutation)
Yes, baked in
The closest analog is in-toto attestations in software supply-chain land — except in-toto is per-artifact (each release gets one attestation), while ABNG is per-event (every observation, every weight update).
What this enables
Forensic post-incident analysis — “Was this model trained on the bad data?” Replay the chain, check.
Regulatory replay — the model’s full training history is one cryptographically-linked blob.
Reproducibility certification — papers ship (model.snap, dataset) pairs; reviewers reproduce + verify chain in one byte comparison.
ML supply chain attestation — fine-tuning extends the chain; provenance of every weight is traceable.
Insider-threat detection — anyone modifying historical events breaks every subsequent hash, visible to anyone running verify_chain.
This is closer to Git’s content-addressed model than to traditional ML metadata stores. MLflow / Weights & Biases give you external logs you have to trust; ABNG gives you a model artifact whose history is cryptographically self-attesting.
Capability 4 — Cryptographic dataset attestation
Demo: Lineage attestation with synthetic clinical-trial data (tests/test_abng_lineage_attestation_cjcl.rs + lineage_source.rs)
What this demo proves
The headline test (lineage_attestation_three_signals_independent) proves that tampering one row of a 64-row dataset (changing patient #17’s response from 0.525 to 0.999) produces three independent SHA-256 divergences:
chain_head divergence — every audit event’s hash cascades from row 17 onward.
BLR state_hash divergence — the leaf where the tampered row routes has a different posterior.
provenance_stamp divergence — the dataset fingerprint differs by construction.
The “independent” qualifier is doing real work. Even if an attacker forged one signal (e.g., recomputed the chain to hide the change), the other two would still surface the tamper.
The threat model and why three signals matter
The classic attack against ML provenance is collusion: the attacker controls the training process, so they can produce a consistent-but-fake audit trail. Three independent signals raise the cost:
Attack
Defeats which signal?
Defeats all three?
Modify training data, recompute chain
chain_head ✓
No — BLR state diverges
Modify training data, retrain to match BLR state
chain_head ✓, BLR state ✓
No — provenance stamp diverges
Modify training data, retrain, restamp with new fingerprint
All three forgeable internally
But: fingerprint is publicly verifiable against the dataset bytes
The provenance stamp is sha256(canonical_dataset_bytes). An auditor with the original dataset can recompute this themselves — the attacker can’t forge it without also forging the dataset (which is what we’re trying to detect in the first place). The chain and BLR state are internal consistency checks; the provenance stamp is the external binding to the dataset.
Why a synthetic clinical-trial workload
Maps directly to a real industry threat model. “Lab A trains a model claiming approved dataset; attacker substitutes contaminated dataset” is the FDA model-attestation problem in one sentence.
64-row size is realistic for Phase II clinical-trial cohorts (often <200 patients per arm).
Cubic dose-response curvey = 0.2 + 0.6·dose + 0.1·dose² is biologically plausible AND exactly fittable in the BLR basis [1, dose, dose², dose³], giving us ground-truth assertions in addition to the security claims.
Tamper at row 17 specifically because dose=0.5 falls right at a codebook bin boundary — routing the tampered observation to a specific leaf, making the BLR state_hash divergence clearly attributable.
Data scale: Two graphs trained on 16-row datasets each (the CJC-Lang version of the demo uses 16 rows for interpreter speed; the Rust-side version uses 64). Tampering one row out of 16 — or one out of 64 — is the minimum-effort attack. A 1.5%-of-dataset perturbation is the strongest test possible: if even this is detectable, larger tampers are detected a fortiori. Scale-up to 10⁴ - 10⁶ row clinical-trial datasets is straightforward (the cryptographic argument is dataset-size-invariant) but not yet validated end-to-end with a real anonymized clinical-trial corpus.
Code sketch
fn train_and_stamp(seed:i64, rows:Any, fingerprint:String) ->i64{let g = build_lineage_graph(seed); abng_stamp_provenance(g,0, fingerprint);// sha256(dataset_bytes)let n = len(rows);let i =0;while i < n {let row = rows[i];let dose = row[0];let resp = row[1];let leaf = route_leaf(g, dose);let phi = Tensor.from_vec( [1.0, dose, dose * dose, dose * dose * dose], [1,4]); abng_blr_update(g, leaf, phi, Tensor.from_vec([resp], [1])); abng_observe(g, leaf, resp); i = i +1;} g}
The two graphs g_a and g_b are built identically — only the row data and fingerprint differ. Comparing (chain_head, blr_state_hash[node 3], provenance_stamp) between them yields three different hashes.
Connection to existing techniques
Technique
Cryptographic
ML-integrated
Per-prediction
MLflow / W&B logs
No
Yes
No
Sigstore / in-toto
Yes
No (artifact-only)
No
Datasheet for Datasets (Gebru et al. 2018)
No (process-only)
Yes
No
Model Cards (Mitchell et al. 2019)
No
Yes
No
MLOps governance frameworks
Variable
Yes
Manual
ABNG provenance chain
Yes
Yes
Yes (baked into predict_snap)
ABNG’s contribution is the combination: cryptographic, ML-integrated, AND per-prediction. None of the existing tools do all three in one artifact.
What this enables
Use case
Concrete flow
FDA model attestation (pharma)
Lab runs trial → trains ABNG → stamps with sha256(approved_dataset). Regulator: cjcl abng explain --model X.snap pred.snap checks all three signals in one CLI invocation
Financial model audit (SEC, Basel III)
“Prove this risk model used only data through model-validation cutoff” — chain head + dataset fingerprint answer in one byte comparison
Right to explanation = “show me the audit trail of training data that produced this decision about me”
Academic reproducibility
paper.pdf + model.snap + dataset.csv — anyone with all three can verify byte-for-byte
Insider-threat in ML supply chain
Attacker who fine-tunes a base model on contaminated data CAN’T hide the audit-chain extension
The closest non-ABNG analog is in-toto-attested ML pipelines (an active research area, see Torres-Arias et al. 2019). The difference: ABNG bakes attestation inside the model artifact, not as a sidecar.
Capability 5 — OOD detection composite
Demo: Asymmetric codebook with deliberately-missing children (tests/test_abng_ood_detection_cjcl.rs + ood_source.rs)
What this demo proves
The headline assertion is three-tier separation:
max(ood_score in dense bins) < ood_score in sparse bin < ood_score in routing-fall-off bin
Three evidence regimes are cleanly partitioned. The composite is a function of three independent inputs.
The composite combines these into a single bounded [0, 1] score. Each signal catches a different OOD mode:
Density catches inputs that fall in a known leaf but far from its training cluster.
Epistemic catches inputs where the BLR posterior says “I’m uncertain here.”
Prefix distance catches inputs that don’t fit the codebook structure at all — i.e. the descent fell off the tree because no child exists at that prefix byte.
Why deliberately omit one child
A symmetric setup (children in all 4 bins, varying training density) would only exercise the density and epistemic signals — not prefix_distance. By deliberately not adding a child for bin 3, queries in bin 3 descend with matched_prefix=0, and the prefix-distance contribution to the composite fires.
The demo asserts the composite works, not just individual signals. If it only varied density, you’d be left wondering whether the routing-distance contribution is real or just a docstring claim. The asymmetric child set proves it’s real.
Data scale: 32 dense observations per bin on bins 0 and 1, 4 sparse on bin 2, 0 on bin 3 (no leaf) — 68 total density observations. 4 probe queries (one per bin). Sufficient to demonstrate the three-tier separation property; insufficient to characterize behavior under realistic ML inputs (e.g., 1024-D image embeddings, NLP token vectors). OOD detection on high-D unstructured inputs is fundamentally outside the codebook-routing paradigm — ABNG’s OOD score requires structured input where the codebook can produce a meaningful prefix encoding.
Code sketch
fn probe_ood(g:i64, x:f64) ->f64{let x_t = Tensor.from_vec([x], [1]);let prefix = abng_encode_prefix(g, x_t);let evidence = abng_descend(g, prefix);let matched = int(evidence.get([0]));// matched_prefixlet leaf = int(evidence.get([1]));// resolved leaf idlet phi = features_1d(x); abng_ood_score(g, leaf, phi, matched,1) // composite [0, 1]}// Build phase: add children for bins 0, 1, 2 — NOT bin 3.abng_add_node(g,0,0);// bin 0 childabng_add_node(g,0,1);// bin 1 childabng_add_node(g,0,2);// bin 2 child// (no add_node for byte 3 — bin 3 queries descend with matched_prefix = 0)// Train density on bins 0 and 1 heavily, bin 2 lightly:train_leaf_density(g,1,0.125,32);// bin 0: densetrain_leaf_density(g,2,0.375,32);// bin 1: densetrain_leaf_density(g,3,0.625,4);// bin 2: sparse// bin 3: no training (no leaf)
After training, probing at x = 0.10 (bin 0, dense) gives a low ood; x = 0.40 (bin 1, dense) low; x = 0.62 (bin 2, sparse) medium; x = 0.90 (bin 3, no leaf) highest.
Connection to existing techniques
Technique
OOD signal
Bayesian-aware?
Mahalanobis distance OOD (Lee et al. 2018)
Distance from class means
No
Energy-based OOD (Liu et al. 2020)
-logsumexp(logits)
No
Deep ensemble disagreement
Variance across M models
Approximately
ODIN (Liang et al. 2018)
Temperature + input perturbation
No
Conformal prediction p-values
Distribution-free coverage
No
ABNG composite
density + epistemic + routing
Yes (BLR posterior is principled)
The closest is Mahalanobis OOD — both compute a distance-from-training-cluster score. ABNG adds the routing-failure signal on top, which catches a class of inputs Mahalanobis can’t distinguish (in-cluster but topologically out-of-routing).
What this enables
Self-driving / robotics — abstain on inputs unfamiliar to the training distribution; hand off to safe-mode.
Medical diagnosis — don’t predict on patient profiles outside the training population.
Fraud detection — novel attack patterns trigger high OOD scores.
Active learning — high-OOD inputs are highest-value labeling candidates.
Safety-critical ML — defense-in-depth against distribution shift at inference time, not just monitoring.
A query can be OOD because (a) it’s far from training points (density), (b) the model is uncertain there (epistemic), or (c) it doesn’t fit the codebook structure at all (prefix). Each tells a different operational story — and the composite collapses them into one actionable score.
After running abng_decide_step three times on a graph with two sibling children:
merge_count == 1 — exactly one Merge action fires (deterministic given the canary thresholds).
audit_log_grew — the structural mutation is recorded as an event in the chain.
verify_chain == Ok — the chain integrity holds after structural mutation.
chain_head matches the Rust-side decide_step_canary BYTE-FOR-BYTE — this is the most striking property in the entire demo set.
That last point deserves emphasis. The Rust-side decide_step_canary_tests.rs locks chain_head = d064fb08…7807. The CJC-Lang demo, running through both AST eval AND MIR exec, produces the same hex. The same workload, expressed three different ways (direct Rust API, CJC-Lang AST, CJC-Lang MIR), produces bit-identical SHA-256 output through 23+ events.
The math behind the Merge trigger
decide_step evaluates each node against six trigger conditions in order: Compress → Merge → Split → Prune → Grow → Freeze. For the Merge trigger specifically, ABNG checks two conditions on a pair of siblings (i, j):
Signature similarity: Hamming distance of the 8-byte truncated SHA-256 signatures < tau_merge. The signature is computed from a 4-channel Welford profile (prediction, uncertainty, calibration, routing). Similar profiles → similar signatures → low Hamming distance.
Posterior similarity (KL divergence):\[
\mathrm{KL}\bigl(\mathcal{N}(\mu_i, \Sigma_i)\,\|\,\mathcal{N}(\mu_j, \Sigma_j)\bigr) < \text{kl\_merge}
\] where (μᵢ, Σᵢ) are the BLR posterior parameters at node i. This is the standard multivariate Gaussian KL.
Both conditions must hold for Merge to fire. The Merge action then folds j’s observations into i via Chan/Golub/LeVeque parallel Welford merge — preserving the post-merge stats as if they’d been observed in series.
Why the two-sibling shared-signature topology
Mirrors the Rust-side decide_step_canary exactly — root + 2 children at keys 1 and 2, three observations at root (0.10, 0.25, 0.40), three decide_step calls. This lets the CJC-Lang demo serve as a direct cross-pipeline determinism gate.
Merge is the most reliably-triggered action. With identical training distributions on the two siblings, their signatures align trivially (Hamming distance = 0). Decision-policy thresholds tau_merge=0 and kl_merge=0.1 are satisfied immediately — KL between fresh-prior posteriors is exactly zero.
Small graph keeps the test fast — 4 nodes, 3 observations, 3 decide_step calls. Runs in milliseconds even through the AST interpreter.
Data scale: 3 observations + 3 decide_step calls, on a 4-node graph. This is deliberately the smallest demo in the entire suite — its job is to prove the trigger semantics fire deterministically and that the chain_head matches the Rust-side canary byte-for-byte. It’s not a demonstration that ABNG-driven structural adaptation is competitive against e.g. Mondrian Forests on 10⁵-row tabular benchmarks. That comparison is genuine future work — engineering a Grow-firing or Split-firing workload at scale, then measuring vs. baseline tree learners.
Code sketch
fn main() {let g = build_adaptive_graph(42); abng_observe(g,0,0.10); abng_observe(g,0,0.25); abng_observe(g,0,0.40);let i =0;while i <3{let counts = abng_decide_step(g);// [grow, split, merge, prune, compress, freeze] i = i +1;}let merge_count = abng_action_count(g,2);// → 1 print("merge_count: "+ to_string(merge_count)); print("chain_head: "+ abng_chain_head(g));// → d064fb08…7807}
The closest analog is Hoeffding Trees — both fire structural changes based on running statistics. The differences: Hoeffding Trees use Hoeffding-bound-based split tests; ABNG uses fixed thresholds on a 14-element decision-policy tensor. ABNG also adds the audit-chain integration that lets you prove what structural changes happened and when.
What this enables
Self-organizing ML models — graph capacity scales with workload complexity; no manual architecture design.
Streaming / online learning — non-stationary distributions cause Grow on new modes, Prune on dead branches.
Hierarchical model distillation — stable subtrees can be Frozen and exported as compiled MLPs.
Continual learning without catastrophic forgetting — Freeze stable-signature subtrees while training others.
AutoML topology search — decision-policy thresholds are hyperparameters; the trigger ladder is the search algorithm.
Warning
Honest caveat: the demo proves Merge fires deterministically. The other five trigger types (Grow, Split, Prune, Compress, Freeze) are tested at the primitive level but not demonstrated firing in a realistic workload. The “graph adapts to workload complexity” claim is half-substantiated by current demos. Closing this gap is straightforward (more demos with engineered workloads that fire each trigger type) but not yet shipped.
Capability 7 — Calibration / ECE
Demo: Hand-constructed well-calibrated vs miscalibrated outcomes (tests/test_abng_calibration_cjcl.rs + calibration_source.rs)
What this demo proves
Two graphs receive 30 calibration observations each. Same predicted probabilities (p ∈ {0.1, 0.5, 0.9}, 10 each), different outcome distributions:
Graph
Outcome convention
Expected ECE
A: well-calibrated
predicted=0.1 → 1 of 10 positive; predicted=0.9 → 9 of 10 positive
< 0.05
B: miscalibrated
predicted=0.1 → 9 of 10 positive (FLIPPED); predicted=0.9 → 1 of 10 positive
> 0.5
The headline assertion ece_a < ece_b separates the two cleanly. Plus the specific bounds: ece_a < 0.05 (well-calibrated achieves near-zero ECE) and ece_b > 0.5 (flipped outcomes produce huge ECE).
The math
ABNG uses standard 15-bin reliability-binning ECE (Naeini, Cooper, Hauskrecht 2015):
B_b is the set of predictions falling in bin b (15 equal-width bins on [0, 1])
accuracy(B_b) = fraction of correct predictions in bin b
confidence(B_b) = mean predicted probability in bin b
|B_b| / N = bin’s empirical weight
A perfectly calibrated model has accuracy(B_b) = confidence(B_b) for every populated bin → ECE = 0. The miscalibrated graph with flipped outcomes pushes accuracy ≈ 1 - confidence in each bin → ECE saturates near 0.8.
Why “flip outcomes” as the miscalibration model
Maximum-ECE case. Predicting 90% confidence and being right 10% of the time is the worst possible outcome — confidence is anti-correlated with truth. This makes the test gate ece_b > 0.5 reliable; small numerical jitter doesn’t push the result close to the threshold.
Same prediction distribution → isolates the calibration signal. Both graphs see the same (p, y) pairs modulo y. Any difference in ECE is attributable to outcomes, not to predictions or to bin populations.
Three reliability bins (0.1, 0.5, 0.9) span low/mid/high probability range without requiring 100+ observations to populate every bin.
Data scale: 30 calibration observations per graph (2 graphs total = 60 observations). This is the minimum sufficient sample size to make the three-bin reliability statistics non-trivially populated and the ECE separation ece_a < 0.05 < 0.5 < ece_b reliable. It does not validate calibration on actual classification tasks (image classifiers, transformer logits, etc.) — those workloads have thousands of (p, y) pairs and 10-100 calibration buckets. Per-leaf ECE on a real classifier is straightforward future work.
Code sketch
fn well_calibrated_run(g:i64, node:i64) {// For predicted_prob = 0.1: 1 positive, 9 negative (10% base rate).let i =0;while i <10{if i <1{ abng_calibration_observe(g, node,0.1,true);}if i >=1{ abng_calibration_observe(g, node,0.1,false);} i = i +1;}// ... same pattern for p=0.5 (5/10) and p=0.9 (9/10) ...}fn miscalibrated_run(g:i64, node:i64) {// For predicted_prob = 0.1: 9 positive, 1 negative (FLIPPED).// For predicted_prob = 0.9: 1 positive, 9 negative (FLIPPED).// ... see source for full pattern ...}let ece_a = abng_calibration_ece(g_a,0);// < 0.05let ece_b = abng_calibration_ece(g_b,0);// > 0.5
Connection to existing techniques
Technique
Calibration
Per-leaf?
Platt scaling (Platt 1999)
Post-hoc logistic regression on logits
No (global)
Isotonic regression (Zadrozny 2002)
Monotonic piecewise-constant fit
No (global)
Temperature scaling (Guo et al. 2017)
Single-parameter softmax rescale
No (global)
Beta calibration (Kull et al. 2017)
Beta distribution fit
No (global)
Histogram binning (Zadrozny 2001)
Per-bin frequency estimate
Possible per-leaf
ABNG calibration
15-bin reliability + ECE
Yes (per-node)
ABNG’s calibration is per-node — different leaves can be independently calibrated. This matters in practice: a model can be well-calibrated for European customers but miscalibrated for Asian customers (different leaves of the codebook). Per-leaf ECE makes this visible; global ECE would average it out.
What this enables
Risk-sensitive deployment — a poorly-calibrated 95%-confident prediction is operationally indistinguishable from an honest 60%-confident one — but you’ll act on it differently.
Threshold tuning — “should I act on predictions above 0.7?” depends on whether 0.7-bin predictions are actually correct 70% of the time.
Model selection in production — beyond accuracy/F1: ECE is the second-order quality metric.
Active learning targeting — high-ECE bins are evidence the model needs more training data in that probability range.
Per-segment calibration analysis — “is my model better calibrated for some user segments than others?” is answerable in one query.
Capability 8 — Distribution-drift detection
Demo: Mean shift from 0 to 0.8 on a 4-D density tracker (tests/test_abng_drift_detection_cjcl.rs + drift_source.rs)
What this demo proves
A three-phase workload: train baseline, freeze, observe under same distribution, observe under shifted distribution. Three assertions:
meaningful_shift — drift_score(post-shift) > 2 × drift_score(same-dist). Catches a regression where drift detection becomes noisy.
drift_nonneg — both scores are non-negative (no NaN, no negative noise).
The math
ABNG’s drift detection is built on a streaming diagonal-Gaussian density tracker. For features φ ∈ ℝᵈ, the tracker maintains:
Running mean: μ̂ updated via Welford
Running diagonal covariance: Σ̂_diag updated via Welford on (φᵢ - μ̂)²
freeze_drift_baseline(node) snapshots (μ̂_baseline, Σ̂_diag_baseline) at a reference point (typically end of pre-deployment training). After freezing, drift_score measures the divergence between the current tracker and the frozen baseline:
— a weighted combination of mean shift (under baseline covariance) and covariance ratio deviation. Pure mean shift (this demo’s case) gives a score dominated by the first term.
Why a pure mean shift
Translation, not rescaling isolates the mean-shift contribution to drift_score. A combined “scale and rotate” shift would test more, but the basic mean-shift case is the simplest sufficient demonstration.
0 to 0.8 is large enough to be unambiguous. Both distributions have ~0.01 jitter; shifting the center by 0.8 is 80x the jitter — the drift signal saturates well above the floor.
Same density-tracker geometry — same dimension count (4-D features), same covariance scale, same batch sizes (16 each post-baseline). Only the location changes.
Data scale: 32 baseline samples + 16 same-distribution + 16 shifted = 64 density observations total, on a single node. This proves the drift_score machinery responds correctly to a controlled mean shift; it does not characterize behavior under realistic concept drift (gradual drift, abrupt drift, recurring drift, periodic shift). Production ML drift detection is typically validated on streaming corpora with millions of points across months of real customer behavior — that’s a workload-grade benchmark, not a research demo, and it’s explicitly future work.
Code sketch
let g = build_drift_graph(7);// Phase 0: train baseline density on N(0, 0.01).let baseline_batch = density_batch(0.0,32);abng_density_observe(g,0, baseline_batch);abng_freeze_drift_baseline(g,0);// snapshot reference state// Phase A: same distribution.let same_batch = density_batch(0.0,16);abng_density_observe(g,0, same_batch);let drift_low = abng_drift_score(g,0);// Phase B: shifted distribution N(0.8, 0.01).let shifted_batch = density_batch(0.8,16);abng_density_observe(g,0, shifted_batch);let drift_high = abng_drift_score(g,0);// drift_high > 2 * drift_low (the demo's headline assertion)
Connection to existing techniques
Technique
Statistic
Streaming?
Reference required
Kolmogorov-Smirnov test
Max CDF difference
No
Yes
Population Stability Index (PSI)
Discrete distribution distance
No (batch)
Yes
Maximum Mean Discrepancy (Gretton 2012)
Kernel-based difference
Possible
Yes
Page-Hinkley test
Change-point detection
Yes
Implicit
ADWIN (Bifet 2007)
Adaptive windowing
Yes
None (auto-detect)
DDM (Gama 2004)
Drift Detection Method (error rate)
Yes
None
ABNG drift_score
Mahalanobis-style on density tracker
Streaming
Frozen baseline
The closest analog is PSI (widely used in finance) — both compute a deviation-from-baseline distribution score. The differences: PSI is for discrete histograms; ABNG operates on continuous diagonal-Gaussian density. PSI is computed batch; ABNG accumulates streamingly via Welford.
What this enables
Production ML monitoring — alert ops when input distribution drifts from training; trigger retraining.
Continual learning auto-trigger — drift signal is the cue to incorporate new data into the model.
A/B test data validation — confirm the new dataset’s distribution matches the original before deploying a new model.
chain_advanced — chain_head changed (events were properly chain-linked).
verify_chain == Ok post-compact — the new events don’t break integrity.
The math (and what’s deferred)
compact_log(g, until_seq) walks events in [0, until_seq), tracks the set of distinct node IDs touched, and for each emits one StatsSnapshot event whose payload is (node_id, sha256(canonical_bytes(node.stats))).
This sets up the read-side optimization that ships in a future phase: when smart_replay encounters a StatsSnapshot, it can:
Trust the snapshot’s stats_hash matches the node’s canonical_bytes (consistency check, shipped today).
Skip the node.observe(value) mutation for subsequent BeliefUpdate events in the same node’s pre-snapshot range, since the snapshot subsumes their cumulative effect.
Install the stats directly from the per-node section’s canonical_bytes instead of replaying from observations.
The shipped layer is the marker emission + chain-integrity preservation + smart_replay consistency check. The cycle-saving fast-forward is Phase 0.6 work.
Data scale: 5 observations across 3 nodes (smallest in the suite). This proves the marker emission is deterministic and chain-integrity-preserving. The interesting scale claim — “compact_log + smart-replay can fast-forward replay of a 10⁶-event audit log” — is deferred to Phase 0.6 along with the actual cycle-saving optimization.
Connection to existing techniques
Technique
Mechanism
Domain
Database WAL compaction
Replace old WAL records with snapshot
RDBMS
LSM tree compaction
Merge sorted runs
KV stores (RocksDB, Cassandra)
Chandy-Lamport snapshotting
Distributed-system state capture
Distributed systems
Checkpoint-restart
Periodic full-state save
HPC, ML training
ABNG compact_log + StatsSnapshot
Event-log markers, hash-chain-integrated
ML training audit logs
The closest analog is WAL log compaction in databases — periodic snapshot points that let you truncate older log entries without losing recoverability. ABNG adds the chain-integrity property: the snapshot itself is a hash-linked event, so post-compaction you can still cryptographically verify nothing was tampered with.
What this enables
Long-running training without log explosion — periodic compaction preserves checkpoints while bounding audit-log growth.
Snapshot-based debugging — each StatsSnapshot is a “save point” for forensic replay.
Foundation for the Phase 0.6 fast-forward optimization — smart-replay can fast-forward past observation runs by trusting the StatsSnapshot.
Distributed training checkpointing — workers compact-log periodically; central server replays the compact form.
Warning
The cycle-saving optimization is deferred to Phase 0.6. Today the marker layer ships with chain-integrity preservation; the actual fast-forward that would make this user-visibly faster lands later.
Read abng_node_maturity at two timepoints: fresh graph (T=0) and after 10 stable observations + 5 decide_step calls (T=1). Five assertions:
sum_at_t0 == 0.0 — fresh graph, all maturity flags are zero.
maturity_evolved — sum at T=1 strictly exceeds sum at T=0.
calib_binary AND unc_binary — positions [1] (calibration_stable) and [2] (uncertainty_stable) are strict 0/1 flags.
samples_seen_nonneg — position [0] (samples_seen) is non-negative.
trust_in_range — position [3] (trust_level) is in [0, 1].
The semantics
abng_node_maturity(g, node) returns a 4-element f64 tensor:
Index
Field
Type
Definition
0
samples_seen
non-negative count
Total observations applied to this node
1
calibration_stable
binary 0/1
True if 3-window ECE history’s \|Δ\| < ece_stability_max_delta
2
uncertainty_stable
binary 0/1
True if 3-window σ history shows ratio < sigma_stability_ratio
3
trust_level
continuous [0, 1]
Composite trust score from signature stability + maturity flags
The flags advance through decide_step calls plus the underlying signature_stable_calls counter that increments on consecutive matching signatures.
Why a stable observation stream
The maturity flags advance under two conditions:
signature_stable_calls increments when consecutive decide_step calls observe an unchanged signature
samples_seen simply counts observations
By choosing identical observations (abng_observe(g, n, 0.1) repeatedly), the signature stays exactly identical across decide_step calls — the stability counter climbs deterministically. With 10 observations + 5 decide_step calls, at least the stability flags should flip.
Realistic data (varying observations) would also work but with lower predictability — the signature might wobble enough that signature_stable_calls resets occasionally. The stable-stream choice makes the demo deterministic.
Data scale: 10 observations + 5 decide_step calls. Deliberately tiny — the workload exists to prove “maturity flags evolve from 0 → 1 under stable training,” not to characterize maturity dynamics under realistic noisy ML training. A real production-monitoring deployment would observe maturity over thousands of decide_step calls across hours/days of training.
Code sketch
let g = build_maturity_graph(42);let mat0 = abng_node_maturity(g,0);// [0, 0, 0, 0] — freshlet i =0;while i <10{ abng_observe(g,0,0.1); abng_observe(g,1,0.1); i = i +1;}let j =0;while j <5{ abng_decide_step(g); j = j +1;}let mat1 = abng_node_maturity(g,0);let r0 = mat1.get([0]);// samples_seen (count > 0)let r1 = mat1.get([1]);// calibration_stable (0 or 1)let r2 = mat1.get([2]);// uncertainty_stable (0 or 1)let r3 = mat1.get([3]);// trust_level (continuous [0, 1])
Connection to existing techniques
Technique
Convergence diagnostic
Domain
Gelman-Rubin R̂
Cross-chain variance ratio
MCMC convergence
Effective Sample Size (ESS)
Autocorrelation-based
MCMC convergence
Validation-loss plateau detection
Loss derivative threshold
Deep learning training
EarlyStopping callback
Patience + monitor metric
sklearn/keras training
ML monitoring stacks (Arize, WhyLabs)
External time-series dashboards
Production ML
ABNG node_maturity
Per-node 4-tuple
Bayesian belief graphs
The closest analog in ML practice is EarlyStopping + production monitoring — both ask “has my model converged enough to deploy?” ABNG’s contribution: per-node granularity (you can see which leaves have stabilized) and cryptographic integrity (the maturity values are derived from the same audit-tracked state, not an external log).
What this enables
Production ML monitoring dashboard — “has my model converged?” → check maturity per-node.
Auto-stop training — stop when maturity flags have all stabilized (avoid wasted compute).
Hyperparameter tuning — which learning rate makes flags settle fastest?
Convergence-failure debugging — which flag never reaches 1? — points at the failing component.
Production rollout gating — deploy-time check: only ship models where calibration_stable == 1.
Federated learning aggregation — aggregate worker updates only from nodes where maturity has stabilized.
Capability 11 — End-to-end determinism
Demo: Universal property — every CJC-Lang demo asserts it.
What every demo proves
Every CJC-Lang demo runs through both cjc-eval (AST tree-walk) AND cjc-mir-exec (MIR register-machine), then asserts byte-equal printed output. Across the 13 ABNG demo files, this constitutes ~95 byte-equal parity assertions over a vocabulary of ~30 abng_* builtins.
The strongest single instance: the adaptive triggers demo’s chain_head matches the Rust-side decide_step_canary byte-for-byte (d064fb08…7807). Same workload, three pipelines (direct Rust API + AST eval + MIR exec), bit-identical SHA-256 over 23+ audit events.
The architectural choices that make this work
Three load-bearing decisions enforced across the workspace:
Kahan compensated summation for all FP reductions (KahanAccumulatorF64). Naive Σxᵢ accumulates rounding error in O(n·ε); Kahan reduces this to O(ε). More importantly, Kahan is order-deterministic for fixed input order — no SIMD-introduced reordering, no FMA fusion. This is the single biggest source of “approximately deterministic” failure in standard ML stacks.
BTreeMap/BTreeSet only, never HashMap. Hash maps with random hashing have iteration order that depends on the per-process seed — different runs can iterate in different orders, and any code that depends on iteration order (e.g., reduction loops) becomes non-deterministic. BTreeMap iterates in sorted key order, every time.
SplitMix64 with explicit seed threading. No thread-local randomness, no time-based seeds, no platform-specific entropy. Every random draw is reproducible from (initial_seed, operation_history).
These three choices aren’t aspirational — they’re enforced by the parity gates. If anyone introduces FMA in cjc-runtime/linalg.rs tomorrow, every parity test fails simultaneously with byte-divergent output.
What this means for ML practitioners
ML reproducibility is famously hard. A typical “deterministic” PyTorch model can produce different bytes across runs due to:
CUDA atomic ops (non-deterministic reduction order on GPU)
cuDNN algorithm selection (can pick different kernels per run)
Multi-threaded CPU reductions (thread-scheduling non-determinism)
ABNG’s contribution to this conversation: byte-equality is achievable if you commit to determinism-by-construction at every layer (no FMA, no random hash, no thread-local RNG). The parity gates prove it run by run.
What this enables
Reproducibility — same seed → same model bytes; papers ship verifiable results.
Cross-platform deployment — train on Linux, deploy on Windows → byte-identical model (modulo cross-platform CI verification, which is the next piece of work).
Distributed training — deterministic operations enable provable consistency without per-step coordination.
Regulatory replay — regulator can re-run training and verify every byte.
CI / regression detection — byte-equal output makes test-failure debugging trivial — diff lines, not “it’s flaky.”
Differential testing across executor versions — new optimizer? Old vs new must produce same bytes for all valid programs.
Warning
Cross-platform determinism is not yet verified. All canary hashes were computed on Windows. They should hold on Linux/macOS (Kahan, no FMA, BTreeMap), but this isn’t yet tested in CI. Adding GitHub Actions CI that runs the suite on three platforms and compares the canaries is a one-day project — and one of the first items on the Phase 0.6 list.
Phase 0.5 in numbers
The current ABNG state is the result of Phase 0.5, which shipped:
Item
What it added
v12 wire-format bump
Per-node provenance_stamp_hash field + new 0x1C ProvenanceStamped audit kind; NodeStats::canonical_bytes extended 24B → 32B to capture the Kahan compensation register
smart_replay API
Opt-in tamper detection via StatsSnapshot consistency check (cycle-saving fast-forward layer deferred to Phase 0.6)
TOML --config files for cjcl abng train
Hand-rolled minimal TOML parser (zero external deps), full schema for graph / codebook / leaf_head / blr_prior / density / calibration / decision_policy / training / output sections
Chess RL v2.6 retrofit scaffold
Sibling test file demonstrating the API pattern; locked v2.6 weight-hash canary
6 capability demos
OOD detection, adaptive triggers, calibration, drift, compact_log, maturity — all in CJC-Lang source with AST↔︎MIR parity gates
3 application demos
PINN, tabular GP-like, lineage attestation — both Rust-direct AND CJC-Lang versions
Net impact: 906 → 960 tests, +54 in this final round, 0 failures across the whole suite.
What’s next — and why interpreter / compiler work matters
ABNG is genuinely useful for a specific niche: regulated ML training where you need Bayesian uncertainty + cryptographically-verifiable lineage on tabular data. The demos prove the niche capabilities; they don’t prove ABNG should replace PyTorch.
Three things stand between current ABNG and production readiness:
Cross-platform determinism CI
The locked canary hashes are Windows-only today. They should hold across platforms (the architecture is designed for it), but this is unverified. Adding GitHub Actions CI that runs the full test suite on Linux + macOS + Windows and compares the locked canaries is a one-day project that pays off forever — every future commit either preserves cross-platform determinism or fails CI loudly.
Performance benchmarks at scale
This is the biggest single gap between the current state and production readiness, and it deserves emphasis: none of the demos in this post test ABNG at production-scale data. Sample sizes range from 3 (adaptive triggers) to 200 (tabular GP-like). Every demo runs in milliseconds. That’s an asset for test-suite reproducibility but a liability for the “ABNG works at scale” story.
What’s specifically not yet validated:
Scale claim
Status
“ABNG O(N · d²) beats classical GP O(N³)”
Correct by construction, never wall-clock-benchmarked vs SVGP / sklearn / GPy at n ∈ {10³, 10⁴, 10⁵, 10⁶}
“Per-leaf BLR posterior converges to truth on real PDE problems”
Demonstrated on 1-D heat eq with 40 samples; never on 2-D Burgers / Navier-Stokes at 10⁴ collocation points
“Lineage attestation works on real anonymized clinical-trial data”
Demonstrated on 16-row synthetic; never on a real Phase II/III trial corpus
“Drift detection catches realistic concept drift”
Demonstrated on a controlled mean shift; never on production streaming data
“OOD score generalizes to high-D embeddings”
Demonstrated on 4-D codebook-routed inputs; fundamentally outside scope for unstructured high-D inputs
“Per-leaf calibration scales to real classifiers”
Demonstrated on 30 synthetic (p, y) pairs; never on real classifier outputs at 10³+ predictions
Closing these gaps is the bulk of Phase 0.6 + 0.7 work. Each is a real deliverable: pick a benchmark dataset / workload, write the wall-clock measurement code, run it, ship the numbers. Until that’s done, ABNG is empirically demonstrated at small scale and theoretically expected to hold at larger scale.
Compiler / interpreter performance
This is the bigger long-term constraint. The CJC-Lang interpreter is currently 5–50× slower than direct Rust API access for ABNG demos. For research / experimentation that’s fine. For production ML training at scale, it’s a hard ceiling.
The chess RL benchmark hit this wall directly — the bottleneck for v2.5 was interpreter throughput, not ML algorithm design. Specific levers, in increasing order of effort:
MIR optimizer passes — current optimizer has CF + DCE; further passes (loop-invariant code motion, common subexpression elimination, function inlining) are weeks of work each.
Hot-path specialization in cjc-runtime — ABNG already shows the pattern (75 native builtins for what users actually do). Has architectural cost: every native kernel is a feature you can’t compose out of simpler primitives.
AOT compilation — cjcl compile foo.cjcl → foo.exe. LLVM or Cranelift backend. Months of work but the only path that closes the perf gap meaningfully.
Most likely sequencing: continue ABNG (Phase 0.6 work — smart-replay fast-forward, cross-platform CI, lineage case study) while landing incremental MIR optimizer + native-kernel work. AOT is a multi-month commitment that depends on whether CJC-Lang’s long-term identity is research vehicle or production language.
Note
ABNG lives at crates/cjc-abng/ in the CJC-Lang repository. The 13 demo files are under tests/test_abng_*.rs and tests/abng_demos/. The full test count is 960; the source is open.