code wiki / _hdl_build / nx_treecanon_gate.nx
nx_treecanon_gate.nx
buildroot/runtime/_hdl_build/nx_treecanon_gate.nx
about
nx_treecanon_gate.nx -- THE DIVERGENCE GATE THAT REFUSES (debt 1785622769, operator directive
2026-08-01: "ONE canonical tree per generation with a lineage manifest ... a divergence gate that
REFUSES a build when the deploying tree's copy diverges from canonical").
MEASURED 2026-08-03, and it is why this exists: NAS buildroot vs laptop nxc2 diverge on
8,258 files -- 3,705 NAS-newer, 1,838 laptop-newer, 739 NAS-only, 1,976 laptop-only. NEITHER
TREE IS A SUPERSET IN EITHER DIRECTION. A name-level diff saw only 969 of those, because the
dangerous class is files present in BOTH with DIFFERENT BYTES (5,543 of them).
⇒ ★A NAME-LEVEL TREE DIFF IS NOT A DIVERGENCE CHECK. It cannot see the failure that actually
bites -- editing a file in one tree and compiling the other copy, which is precisely how the
unfreed-mmap fix had to be applied twice (the incident that opened this debt).
SCOPE, DELIBERATE AND STATED: this gate does NOT try to reconcile the fork. Blocking on all
8,258 would block every build, and mass-merging on a size screen would be DESTRUCTIVE -- byte
size is a SCREEN, not proof of newer (banked law: newer mtime + FEWER bytes = REVERT). It
enforces a CANON LIST from knowledge/tree_canon.conf (rule 11: the policy is DATA), starting at
the crown jewels and growing by measurement. Stopping the bleeding beats an unfinished cleanup.
T0 LOADED both manifests parse and index (non-vacuity floor on the inputs)
T1 CANON READ the canon list loads and is non-empty (an empty list would pass vacuously)
T2 CANON CLEAN every canon path is present in BOTH trees and byte-identical -- REFUSES otherwise
T3 BITE the detector fires on a REAL divergent path and stays silent on a REAL identical
one, both discovered from the manifests at runtime (no hardcoded fixture)
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_gate_verdict.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 28 | const TC_HBUCKETS: i64 = 65536 |
| 29 | const TC_MAXENT: i64 = 200000 |
| 30 | const TC_BUFCAP: i64 = 16777216 |
| 31 | const TC_CONFCAP: i64 = 262144 |
| 32 | const TC_HASH_SEED: i64 = 5381 |
| 33 | const TC_SPACE: i64 = 32 |
| 34 | const TC_NL: i64 = 10 |
| 35 | const TC_CR: i64 = 13 |
| 36 | const TC_HASHCH: i64 = 35 |
| 37 | const TC_BANG: i64 = 33 // '!' row prefix: the BUILD LANE freezes every build while that row |
| 40 | const TC_D0: i64 = 48 |
| 41 | const TC_D9: i64 = 57 |
functions
| 53 | func tc_readall(path: *u8, outn: *i64, cap: i64) -> *u8 |
| 70 | func tc_hash(s: *u8) -> i64 |
| 76 | func tc_streq(a: *u8, b: *u8) -> i64 called by 1: tc_lookup |
| 84 | func tc_parse(buf: *u8, start: i64, end: i64, szout: *i64) -> *u8 called by 1: tc_index |
| 106 | func tc_index(buf: *u8, n: i64, htab: *i64, keys: *i64, sizes: *i64) -> i64 |
| 144 | func tc_lookup(rel: *u8, htab: *i64, keys: *i64, sizes: *i64) -> i64 |
| 159 | func tc_diverges(rel: *u8) -> i64 |
| 168 | func main(argc: i64, argv: *i64) -> i64 |