code wiki / (root) / nx_adoptgate_typed_candidate_t310.nx

nx_adoptgate_typed_candidate_t310.nx source

↩ module page · 199 lines · 9845 B

1// nx_adoptgate.nx -- THE ENFORCED GATE BETWEEN /api/build AND /api/promote (2026-08-07). 2// 3// WHY: the 4-step adoption recipe exists and is correct, and I ran it BY HAND and therefore ran it 4// INCONSISTENTLY -- 35 organs promoted on step 2 alone because step 3 was something I had to remember. 5// *A LAW I HAVE TO REMEMBER IS A LAW I WILL SKIP; PUT IT IN THE PATH.* 6// This collapses steps 2 and 3 into ONE call that emits ONE machine-checkable token, so a promote can be 7// gated on `APPROVED` instead of on my good intentions. 8// 9// IT DOES NOT BUILD AND IT DOES NOT PROMOTE, and that is deliberate: 10// - build must go through /api/build because only that stages the artifact where /api/promote LOOKS 11// (a direct nx_sov_build_run drops it in /tmp or _build -- provably the wrong place, and exactly the 12// artifacts-never-placed-where-consumers-look defect this whole campaign is about). 13// - promotion stays an explicit operator act. A gate that also performs the action it authorises cannot 14// be re-run to audit its own decision. 15// 16// COMPOSES THE INCUMBENTS, never reimplements them (one duplicate ruler was already built and retracted 17// this session): nx_contentdiff = NECESSARY half, nx_behaveprobe = SUFFICIENCY half. 18// 19// nx_adoptgate <target> [workload-args...] 20// -> ADOPT-GATE <target> verdict=APPROVED|REFUSED reason=... content=... behave=... evidence=... 21// exit 0 APPROVED | 1 REFUSED | 2 missing artifact | 3 usage 22// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 23import "nx_tool_run.nx" 24import "nx_contentdiff.nx" 25 26const AG_HOST: *u8 = "/volume1/homes/elderwesto/nishihost" 27const AG_CDIFF: *u8 = "/volume1/homes/elderwesto/nishihost/nx_contentdiff.elf" 28const AG_BPROBE: *u8 = "/volume1/homes/elderwesto/nishihost/nx_behaveprobe.elf" 29const AG_CAP: i64 = 262144 30const AG_TMO: i64 = 180000 31 32func agp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 33func age(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(2,s,n); return 0 } 34func agcat(d: *u8, o: i64, s: *u8) -> i64 { var x: i64=o; var i: i64=0; while s[i]!=(0 as u8){d[x]=s[i];x=x+1;i=i+1} return x } 35func agexists(p: *u8) -> i64 { let fd: i64 = sys_openat_rd(p); if fd < 0 { return 0 } sys_close(fd); return 1 } 36 37// does buf contain needle? Scans the WHOLE buffer -- never offset 0. A captured stream merges stdout and 38// stderr, so the line you want is routinely not first; classifying by the first bytes read every SUCCESS 39// as unparsed once already this session. 40func agfind(buf: *u8, n: i64, ndl: *u8) -> i64 { 41 var ln: i64 = 0 42 while ndl[ln] != (0 as u8) { ln = ln + 1 } 43 if ln == 0 { return 0 } 44 if n < ln { return 0 } 45 var i: i64 = 0 46 while i <= n - ln { 47 var k: i64 = 0 48 var hit: i64 = 1 49 while k < ln { if buf[i+k] != ndl[k] { hit = 0; k = ln } else { k = k + 1 } } 50 if hit == 1 { return 1 } 51 i = i + 1 52 } 53 return 0 54} 55 56// ---- THE REASON TRAVELS WITH THE COUNT (debt 1787235038, fixed 2026-08-20) -------------------------- 57// This gate FORKED the ruler, held its ENTIRE output in `out`, and published one verdict WORD. So every 58// downstream reader had to re-run nx_contentdiff BY HAND to learn WHICH run a candidate drops -- and 59// nx_organ_ship's allow_loss leg, which counts "LOST:" rows in THIS gate's output, therefore measured 60// nlost=0 for every candidate that has ever existed and refused every named-loss ship as "a ruler that 61// did not speak". The evidence was in hand at judge time and thrown away. (Same defect class as 62// nx_artifactdrift judge, which forked nx_contentdiff per row and kept only the verdict BIT.) 63// WARNING CARRIED FORWARD: the ruler NAMES at most CD_SHOW(=8) lost runs while COUNTING all of them, so 64// the echoed list is a PREFIX OF ITS OWN COUNT -- lost_from_live travels beside it, or a reader 65// republishes a prefix as a population. 66func agpos(buf: *u8, n: i64, ndl: *u8, from: i64) -> i64 { 67 var ln: i64 = 0 68 while ndl[ln] != (0 as u8) { ln = ln + 1 } 69 if ln == 0 { return 0 - 1 } 70 var i: i64 = from 71 if i < 0 { i = 0 } 72 while i + ln <= n { 73 var k: i64 = 0 74 var hit: i64 = 1 75 while k < ln { if buf[i+k] != ndl[k] { hit = 0; k = ln } else { k = k + 1 } } 76 if hit == 1 { return i } 77 i = i + 1 78 } 79 return 0 - 1 80} 81func agnum(v: i64) -> i64 { 82 var m: i64 = v 83 if m < 0 { agp("-" as *u8); m = 0 - m } 84 let t: *u8 = sys_mmap(32) 85 var k: i64 = 0 86 if m == 0 { t[0] = 48 as u8; k = 1 } 87 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 88 while k > 0 { k = k - 1; sys_write(1, ((t as i64) + k) as *u8, 1) } 89 return 0 90} 91// The integer the RULER published, read back from the ruler's own field name. This is not a second 92// measurement of loss -- it is the first one, quoted. A third state is REQUIRED: -1 means the ruler 93// printed no count at all, which must never read as zero. 94func agint_after(buf: *u8, n: i64, ndl: *u8) -> i64 { 95 let p: i64 = agpos(buf, n, ndl, 0) 96 if p < 0 { return 0 - 1 } 97 var ln: i64 = 0 98 while ndl[ln] != (0 as u8) { ln = ln + 1 } 99 var j: i64 = p + ln 100 var v: i64 = 0 101 var any: i64 = 0 102 var go: i64 = 1 103 while go == 1 { 104 if j >= n { go = 0 } else { 105 let c: i64 = buf[j] as i64 106 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); any = 1; j = j + 1 } else { go = 0 } } else { go = 0 } 107 } 108 } 109 if any == 0 { return 0 - 1 } 110 return v 111} 112// Echo VERBATIM every line of the captured ruler output containing `ndl`, one echo per LINE (the cursor 113// advances past the line end, so a needle occurring twice inside one run cannot double-print it). 114// Returns how many lines were echoed -- the NAMED count, which is a floor on the true one. 115func ag_echo_lines(buf: *u8, n: i64, ndl: *u8) -> i64 { 116 var shown: i64 = 0 117 var i: i64 = 0 118 var go: i64 = 1 119 while go == 1 { 120 let p: i64 = agpos(buf, n, ndl, i) 121 if p < 0 { go = 0 } else { 122 var s: i64 = p 123 var back: i64 = 1 124 while back == 1 { if s == 0 { back = 0 } else { if buf[s-1] == (10 as u8) { back = 0 } else { s = s - 1 } } } 125 var e: i64 = p 126 var fwd: i64 = 1 127 while fwd == 1 { if e >= n { fwd = 0 } else { if buf[e] == (10 as u8) { fwd = 0 } else { e = e + 1 } } } 128 sys_write(1, ((buf as i64) + s) as *u8, e - s) 129 agp("\n" as *u8) 130 shown = shown + 1 131 i = e + 1 132 if i >= n { go = 0 } 133 } 134 } 135 return shown 136} 137 138// Native composition: counts and qualification travel as data, not parsed stdout. 139func ag_content_paths(live:*u8,staged:*u8,target:*u8)->i64{ 140 var measured:NxCdResult 141 let rc:i64=cd_structured_paths_result(live,staged,CD_SHOW,&measured) 142 agp("ADOPT-GATE ");agp(target);agp(" structured_state=");agnum(measured.state) 143 agp(" content_runs_scanned=");agnum(measured.runs) 144 agp(" lost_from_live=");agnum(measured.lost) 145 agp(" lost_permil=") 146 if measured.state==0{agnum(cds_permil(measured.lost,measured.checked))}else{agnum(-1)} 147 agp(" source_removed=");agnum(measured.source_removed) 148 agp(" source_added=");agnum(measured.source_added);agp("\n") 149 if rc!=0||measured.state!=0{ 150 agp("ADOPT-GATE ");agp(target) 151 agp(" verdict=REFUSED reason=content-loss-or-unqualified content=RED\n");return 1 152 } 153 return 0 154} 155 156func main(argc: i64, argv: *i64) -> i64 { 157 if argc < 2 { age("usage: nx_adoptgate <target> [workload-args...]\n" as *u8); sys_exit(3); return 3 } 158 let tgt: *u8 = argv[1] as *u8 159 160 let live: *u8 = sys_mmap(512) 161 var lo: i64 = agcat(live, 0, AG_HOST); lo = agcat(live, lo, "/" as *u8); lo = agcat(live, lo, tgt); lo = agcat(live, lo, ".elf" as *u8) 162 live[lo] = 0 as u8 163 let stag: *u8 = sys_mmap(512) 164 var so: i64 = agcat(stag, 0, AG_HOST); so = agcat(stag, so, "/" as *u8); so = agcat(stag, so, tgt); so = agcat(stag, so, ".sov.elf.new" as *u8) 165 stag[so] = 0 as u8 166 167 if agexists(live) == 0 { agp("ADOPT-GATE " as *u8); agp(tgt); agp(" verdict=REFUSED reason=no-live-elf\n" as *u8); sys_exit(2); return 2 } 168 if agexists(stag) == 0 { agp("ADOPT-GATE " as *u8); agp(tgt); agp(" verdict=REFUSED reason=nothing-staged (run /api/build first)\n" as *u8); sys_exit(2); return 2 } 169 170 if ag_content_paths(live,stag,tgt)!=0{return 1} 171 let out: *u8 = sys_mmap(AG_CAP) 172 let ol: *i64 = sys_mmap(16) as *i64 173 let av: *i64 = sys_mmap(256) as *i64 174 175 // ---- STEP 3 (SUFFICIENCY): do the two binaries actually BEHAVE the same on a real workload? 176 av[0] = AG_BPROBE as i64 177 av[1] = tgt as i64 178 var n: i64 = 2 179 var s: i64 = 2 180 while s < argc { av[n] = argv[s] as i64; n = n + 1; s = s + 1 } 181 av[n] = 0 182 ol[0] = 0 183 tr_run_capture_cwd(AG_BPROBE, av, out, AG_CAP, ol, AG_TMO, AG_HOST) 184 let identical: i64 = agfind(out, ol[0], "IDENTICAL" as *u8) 185 let strong: i64 = agfind(out, ol[0], "STRONG" as *u8) 186 187 agp("ADOPT-GATE " as *u8); agp(tgt) 188 agp(" verdict=APPROVED content=GREEN behave=" as *u8) 189 if identical == 1 { agp("IDENTICAL" as *u8) } else { agp("DIFFERS" as *u8) } 190 agp(" evidence=" as *u8) 191 if strong == 1 { agp("STRONG" as *u8) } else { agp("WEAK" as *u8) } 192 // DIFFERS is NOT a refusal: a STALE-SAFE organ has source AHEAD of the deployed elf, so new behaviour 193 // is the POINT. What the caller needs is the difference stated, and its evidence GRADED, so a WEAK 194 // match cannot pass itself off as verification -- an identical usage line is not an identical organ. 195 if strong == 0 { agp(" note=WEAK-EVIDENCE:probe compared a usage line or an error path; pass real workload args before trusting this" as *u8) } 196 if identical == 0 { agp(" note=behaviour CHANGED (expected when source is ahead) -- read the diff before promoting" as *u8) } 197 agp("\n" as *u8) 198 return 0 199}