code wiki / _hdl_build / nx_staghyg_gate.nx
nx_staghyg_gate.nx source
↩ module page · 351 lines · 21691 B
1// nx_staghyg_gate.nx -- prove the staging-hygiene guard REFUSES a backwards promote, does NOT refuse a
2// legitimate one, and -- the tooth this gate earned the hard way -- does NOT refuse an ORDINARY EDIT.
3//
4// Inherits the nx_gate_verdict base class (D001/L009: gv_ctr/gv_head/gv_check/gv_bite/gv_verdict) so
5// nx_gate_green can judge it and it records a harness frame.
6//
7// DETERMINISTIC BY CONSTRUCTION: every fixture mtime is set explicitly via sys_utimensat and `now` is
8// passed to sg_classify as a parameter, so there are NO sleeps and NO filesystem-resolution races.
9// Each tooth writes its OWN file names, so nothing depends on whether the write helper truncates.
10//
11// THE cfg BELOW IS THE SHIPPED POLICY, NOT A CONVENIENT ONE. An earlier version of this gate passed
12// 10/10 against thresholds the shipped conf did not use, which proves the mechanism and says nothing
13// about the rule operators actually meet. T4 and T8 prove it FIRES; T11 proves it STAYS SILENT on a
14// one-run edit -- and T11 exists because the first tuning refused the very build that introduced it.
15// A guard that fires on routine work is not strict, it is training people to bypass it.
16// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
17import "nx_syscalls.nx"
18import "nx_gate_verdict.nx"
19import "nx_staging_guard.nx"
20
21const GT_NOW: i64 = 1800000000
22const GT_DAY: i64 = 86400
23
24func gt_wfd(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
25func gt_wnfd(fd: i64, v: i64) -> i64 {
26 var m: i64 = v
27 let t: *u8 = sys_mmap(32)
28 var k: i64 = 0
29 if m == 0 { t[0] = 48 as u8; k = 1 }
30 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
31 let o: *u8 = sys_mmap(32)
32 var i: i64 = 0
33 while i < k { o[i] = t[k - 1 - i]; i = i + 1 }
34 sys_write(fd, o, k)
35 sys_munmap(t, 32)
36 sys_munmap(o, 32)
37 return 0
38}
39
40// Write `n` distinct printable runs (prefix + index), newline-separated, then `pad` bare newlines.
41// A newline is non-printable to cdl_runlen, so it both SEPARATES runs and PADS bytes without adding any
42// run -- which is how a fixture is made BIGGER while carrying FEWER of the live runs. Indices
43// [oddstart, oddstart+oddcount) get a different prefix: that is the mutation dial, and its SIZE is what
44// separates an ordinary edit (1 run) from a real regression (many).
45func gt_tokens(path: *u8, prefix: *u8, n: i64, pad: i64, odd: *i64) -> i64 {
46 let fd: i64 = sys_openat_wr(path, 420)
47 if fd < 0 { return 0 }
48 var i: i64 = 0
49 while i < n {
50 var mut: i64 = 0
51 if i >= odd[0] { if i < (odd[0] + odd[1]) { mut = 1 } }
52 if mut == 1 { gt_wfd(fd, "NXSG_MUTATED_RUN_" as *u8) }
53 if mut == 0 { gt_wfd(fd, prefix) }
54 gt_wnfd(fd, 1000 + i)
55 sys_write(fd, "\n" as *u8, 1)
56 i = i + 1
57 }
58 var p: i64 = 0
59 while p < pad { sys_write(fd, "\n" as *u8, 1); p = p + 1 }
60 sys_close(fd)
61 return 1
62}
63
64func gt_odd(o: *i64, start: i64, count: i64) -> i64 { o[0] = start; o[1] = count; return 0 }
65
66func gt_fill(dst: *u8, s: *u8) -> i64 {
67 var i: i64 = 0
68 while s[i] != (0 as u8) { dst[i] = s[i]; i = i + 1 }
69 return i
70}
71
72func gt_set_mtime(path: *u8, sec: i64) -> i64 {
73 let ts: *i64 = sys_mmap(64) as *i64
74 ts[0] = sec; ts[1] = 0; ts[2] = sec; ts[3] = 0
75 return sys_utimensat(path, ts)
76}
77
78func main() -> i64 {
79 let ctr: *i64 = gv_ctr()
80 let out: *i64 = sys_mmap(8 * SG_OUT_SLOTS) as *i64
81 let none: *i64 = sys_mmap(32) as *i64
82 gt_odd(none, 0 - 1, 0)
83 let odd6: *i64 = sys_mmap(32) as *i64
84 gt_odd(odd6, 7, 6)
85 let odd1: *i64 = sys_mmap(32) as *i64
86 gt_odd(odd1, 7, 1)
87 let odd3: *i64 = sys_mmap(32) as *i64
88 gt_odd(odd3, 0, 3)
89
90 // THE SHIPPED POLICY (knowledge/staging_hygiene.conf), stated explicitly so a conf drift shows up
91 // as a FAILING TOOTH here rather than as a silently different rule in production.
92 let cfg: *i64 = sys_mmap(8 * SG_C_SLOTS) as *i64
93 cfg[SG_C_MAXAGE] = 14
94 cfg[SG_C_SHRINKTOL] = 20
95 cfg[SG_C_CAPLOSS] = 50
96 cfg[SG_C_CAPLOSSABS] = 5
97 cfg[SG_C_CAPLOSSMIN] = 2
98 cfg[SG_C_MINTOK] = 12
99 cfg[SG_C_MAXTOK] = 256
100
101 gv_head("nx_staghyg_gate -- refuses backwards promotes, allows forward ones, ignores ordinary edits" as *u8)
102
103 // ---- T1 FORWARD: newer, carries every live run -> ALLOW ----------------------------------------
104 gt_tokens("/tmp/nxsg_t1_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
105 gt_tokens("/tmp/nxsg_t1_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 500, none)
106 gt_set_mtime("/tmp/nxsg_t1_live" as *u8, GT_NOW - GT_DAY)
107 gt_set_mtime("/tmp/nxsg_t1_live.new" as *u8, GT_NOW - (GT_DAY / 2))
108 let c1: i64 = sg_classify("/tmp/nxsg_t1_live" as *u8, "/tmp/nxsg_t1_live.new" as *u8, GT_NOW, cfg, out)
109 var t1: i64 = 0
110 if c1 == SG_FORWARD { if sg_allows(c1) == 1 { t1 = 1 } }
111 gv_check("T1 forward promote ALLOWED (newer, no run loss)" as *u8, t1, ctr)
112
113 // ---- T2 BACKDATE: staged PREDATES live -> REFUSE -----------------------------------------------
114 gt_tokens("/tmp/nxsg_t2_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
115 gt_tokens("/tmp/nxsg_t2_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
116 gt_set_mtime("/tmp/nxsg_t2_live" as *u8, GT_NOW - GT_DAY)
117 gt_set_mtime("/tmp/nxsg_t2_live.new" as *u8, GT_NOW - (2 * GT_DAY))
118 let c2: i64 = sg_classify("/tmp/nxsg_t2_live" as *u8, "/tmp/nxsg_t2_live.new" as *u8, GT_NOW, cfg, out)
119 var t2: i64 = 0
120 if c2 == SG_BACKDATE { if sg_allows(c2) == 0 { t2 = 1 } }
121 gv_check("T2 BACKDATE refused (staged predates the live binary)" as *u8, t2, ctr)
122
123 // ---- T3 SHRINK WITH LOSS: newer, materially smaller AND 30 of 40 live runs gone -> REFUSE --------
124 // Since 2026-08-18 the shrink screen DEFERS to the token ruler whenever the ruler could run (see
125 // sg_classify: the compiler's whole-program DCE makes every honest rebuild 20-70% smaller, so size
126 // alone can no longer separate "pruned dead code" from "lost capability"). This fixture loses runs,
127 // so the ruler is what refuses it: the class is CAPLOSS and the promote is still refused.
128 gt_tokens("/tmp/nxsg_t3_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
129 gt_tokens("/tmp/nxsg_t3_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 10, 0, none)
130 gt_set_mtime("/tmp/nxsg_t3_live" as *u8, GT_NOW - GT_DAY)
131 gt_set_mtime("/tmp/nxsg_t3_live.new" as *u8, GT_NOW - (GT_DAY / 2))
132 let c3: i64 = sg_classify("/tmp/nxsg_t3_live" as *u8, "/tmp/nxsg_t3_live.new" as *u8, GT_NOW, cfg, out)
133 var t3: i64 = 0
134 if c3 == SG_CAPLOSS { if sg_allows(c3) == 0 { t3 = 1 } }
135 gv_check("T3 SHRINK+LOSS refused as CAPLOSS (smaller AND 30 of 40 live runs gone)" as *u8, t3, ctr)
136
137 // ---- T3b THE DCE SHAPE: newer, 72% smaller, EVERY live run kept, run count held -> ALLOW ---------
138 // This is what every rebuild looks like after nx_cc's reachability pass (B1, 2026-08-18): the code
139 // that no path reaches is gone, every string the program can print is still there. A guard that
140 // refused this would refuse the whole estate's next promote, and a refusal that fires on routine
141 // work is a bypass waiting to be typed by reflex (T11's own lesson). Live is padded with 2000 bare
142 // newlines (bytes, not runs) so the byte shrink is large while the run ruler sees no loss at all.
143 gt_tokens("/tmp/nxsg_t3b_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 2000, none)
144 gt_tokens("/tmp/nxsg_t3b_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
145 gt_set_mtime("/tmp/nxsg_t3b_live" as *u8, GT_NOW - GT_DAY)
146 gt_set_mtime("/tmp/nxsg_t3b_live.new" as *u8, GT_NOW - (GT_DAY / 2))
147 let c3b: i64 = sg_classify("/tmp/nxsg_t3b_live" as *u8, "/tmp/nxsg_t3b_live.new" as *u8, GT_NOW, cfg, out)
148 var t3b: i64 = 0
149 if c3b == SG_FORWARD { if sg_allows(c3b) == 1 { if out[SG_O_SHRINKPERMIL] > cfg[SG_C_SHRINKTOL] { if out[SG_O_MISSING] == 0 { t3b = 1 } } } }
150 gv_check("T3b DCE-SHAPE shrink ALLOWED: past the size screen, zero live runs absent, count held" as *u8, t3b, ctr)
151
152 // ---- T3c SHRINK THE RULER CANNOT VOUCH FOR: an EMPTY staged file -> REFUSE (fail-closed) ---------
153 // The screen still bites where the ruler is blind: nothing to read means nothing can vouch, and a
154 // 0-byte .new is the exact artefact a COMPILE-FAIL leaves behind. Size is then the ONLY evidence
155 // and it says backwards, so this stays a refusal.
156 gt_tokens("/tmp/nxsg_t3c_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
157 let t3cfd: i64 = sys_openat_wr("/tmp/nxsg_t3c_live.new" as *u8, 420)
158 if t3cfd >= 0 { sys_close(t3cfd) }
159 gt_set_mtime("/tmp/nxsg_t3c_live" as *u8, GT_NOW - GT_DAY)
160 gt_set_mtime("/tmp/nxsg_t3c_live.new" as *u8, GT_NOW - (GT_DAY / 2))
161 let c3c: i64 = sg_classify("/tmp/nxsg_t3c_live" as *u8, "/tmp/nxsg_t3c_live.new" as *u8, GT_NOW, cfg, out)
162 var t3c: i64 = 0
163 if c3c == SG_SHRINK { if sg_allows(c3c) == 0 { t3c = 1 } }
164 gv_check("T3c SHRINK with an unreadable/empty staged file is still REFUSED (ruler cannot vouch)" as *u8, t3c, ctr)
165
166 // ---- T4 CAPLOSS: NEWER *and* BIGGER, yet every live run is gone -> REFUSE -----------------------
167 // mtime says upgrade. size says upgrade. Only the run ruler sees the loss -- and 7 artifacts on the
168 // live deploy root are in exactly this state.
169 gt_tokens("/tmp/nxsg_t4_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
170 gt_tokens("/tmp/nxsg_t4_live.new" as *u8, "NXSG_OTHER_CAPRUN_" as *u8, 40, 2000, none)
171 gt_set_mtime("/tmp/nxsg_t4_live" as *u8, GT_NOW - GT_DAY)
172 gt_set_mtime("/tmp/nxsg_t4_live.new" as *u8, GT_NOW - (GT_DAY / 2))
173 let c4: i64 = sg_classify("/tmp/nxsg_t4_live" as *u8, "/tmp/nxsg_t4_live.new" as *u8, GT_NOW, cfg, out)
174 var t4: i64 = 0
175 if c4 == SG_CAPLOSS { if sg_allows(c4) == 0 { t4 = 1 } }
176 if out[SG_O_NEWSZ] <= out[SG_O_LIVESZ] { t4 = 0 }
177 gv_check("T4 CAPLOSS refused THOUGH NEWER AND BIGGER (size+mtime both say upgrade)" as *u8, t4, ctr)
178 gv_puts(" live=" as *u8); gv_num(out[SG_O_LIVESZ])
179 gv_puts("b staged=" as *u8); gv_num(out[SG_O_NEWSZ])
180 gv_puts("b caploss_permil=" as *u8); gv_num(out[SG_O_LOSTPERMIL])
181 gv_puts(" absent=" as *u8); gv_num(out[SG_O_MISSING])
182 gv_puts("/" as *u8); gv_num(out[SG_O_CHECKED]); gv_puts("\n" as *u8)
183
184 // ---- T5 ORPHAN: no live counterpart -> nothing to lose -> ALLOW --------------------------------
185 gt_tokens("/tmp/nxsg_t5_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
186 gt_set_mtime("/tmp/nxsg_t5_live.new" as *u8, GT_NOW - (GT_DAY / 2))
187 let c5: i64 = sg_classify("/tmp/nxsg_t5_absent" as *u8, "/tmp/nxsg_t5_live.new" as *u8, GT_NOW, cfg, out)
188 var t5: i64 = 0
189 if c5 == SG_ORPHAN { if sg_allows(c5) == 1 { t5 = 1 } }
190 gv_check("T5 ORPHAN allowed (no live binary, no capability to lose)" as *u8, t5, ctr)
191
192 // ---- T6 NONE: nothing staged -> say NONE, never invent a refusal --------------------------------
193 let c6: i64 = sg_classify("/tmp/nxsg_t1_live" as *u8, "/tmp/nxsg_t6_absent" as *u8, GT_NOW, cfg, out)
194 var t6: i64 = 0
195 if c6 == SG_NONE { t6 = 1 }
196 gv_check("T6 NONE when nothing is staged" as *u8, t6, ctr)
197
198 // ---- T7 EXPIRED: NEWER than live but past the window -> REFUSE ---------------------------------
199 gt_tokens("/tmp/nxsg_t7_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
200 gt_tokens("/tmp/nxsg_t7_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 500, none)
201 gt_set_mtime("/tmp/nxsg_t7_live" as *u8, GT_NOW - (40 * GT_DAY))
202 gt_set_mtime("/tmp/nxsg_t7_live.new" as *u8, GT_NOW - (30 * GT_DAY))
203 let c7: i64 = sg_classify("/tmp/nxsg_t7_live" as *u8, "/tmp/nxsg_t7_live.new" as *u8, GT_NOW, cfg, out)
204 var t7: i64 = 0
205 if c7 == SG_EXPIRED { if sg_allows(c7) == 0 { t7 = 1 } }
206 gv_check("T7 EXPIRED refused (newer than live, but past the staging window)" as *u8, t7, ctr)
207
208 // ---- T8 BITE PROOF: 6 changed runs must FIRE; the identical fixture must stay SILENT -----------
209 gt_tokens("/tmp/nxsg_t8_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
210 gt_tokens("/tmp/nxsg_t8_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 500, odd6)
211 gt_set_mtime("/tmp/nxsg_t8_live" as *u8, GT_NOW - GT_DAY)
212 gt_set_mtime("/tmp/nxsg_t8_live.new" as *u8, GT_NOW - (GT_DAY / 2))
213 let c8: i64 = sg_classify("/tmp/nxsg_t8_live" as *u8, "/tmp/nxsg_t8_live.new" as *u8, GT_NOW, cfg, out)
214 var bad_fires: i64 = 0
215 if c8 == SG_CAPLOSS { bad_fires = 1 }
216 var good_fires: i64 = 0
217 if c1 == SG_CAPLOSS { good_fires = 1 }
218 gv_bite("T8 caploss detector (6 changed runs vs identical)" as *u8, bad_fires, good_fires, ctr)
219
220 // ---- T11 THE ANTI-BYPASS TOOTH: ONE changed run is an ORDINARY EDIT and must be ALLOWED --------
221 // Born from a live false positive: the first tuning (10 permil, no absolute bar) scored a one-line
222 // rename at 24 permil and REFUSED the build that contained it. Detection was right; policy was not.
223 // If this tooth ever goes RED the thresholds have drifted back into refusing routine work, which is
224 // how an override becomes reflex and a guard quietly stops being one.
225 gt_tokens("/tmp/nxsg_t11_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 0, none)
226 gt_tokens("/tmp/nxsg_t11_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 40, 500, odd1)
227 gt_set_mtime("/tmp/nxsg_t11_live" as *u8, GT_NOW - GT_DAY)
228 gt_set_mtime("/tmp/nxsg_t11_live.new" as *u8, GT_NOW - (GT_DAY / 2))
229 let c11: i64 = sg_classify("/tmp/nxsg_t11_live" as *u8, "/tmp/nxsg_t11_live.new" as *u8, GT_NOW, cfg, out)
230 var t11: i64 = 0
231 if sg_allows(c11) == 1 { t11 = 1 }
232 gv_check("T11 ANTI-BYPASS: a ONE-run ordinary edit is ALLOWED, not refused" as *u8, t11, ctr)
233 gv_puts(" class=" as *u8); gv_puts(sg_class_name(c11))
234 gv_puts(" caploss_permil=" as *u8); gv_num(out[SG_O_LOSTPERMIL])
235 gv_puts(" absent=" as *u8); gv_num(out[SG_O_MISSING])
236 gv_puts(" (bars: " as *u8); gv_num(cfg[SG_C_CAPLOSS])
237 gv_puts("permil OR " as *u8); gv_num(cfg[SG_C_CAPLOSSABS]); gv_puts(" absent)\n" as *u8)
238
239 // ---- T12 SAME SIZE, DIFFERENT CONTENT is DETECTED as such -------------------------------------
240 // The mutation-class blind spot, made visible. An ==/!= operator swap preserves the byte count AND
241 // every printable run, so nothing else in this guard can see it; SG_O_IDENTICAL is the only field
242 // that separates "a harmless re-stage of the same bytes" from "something changed that I cannot see".
243 var t12: i64 = 0
244 if out[SG_O_IDENTICAL] == 0 {
245 let c12: i64 = sg_classify("/tmp/nxsg_t1_live" as *u8, "/tmp/nxsg_t1_live" as *u8, GT_NOW, cfg, out)
246 if out[SG_O_IDENTICAL] == 1 { if c12 == SG_FORWARD { t12 = 1 } }
247 }
248 gv_check("T12 byte-identity distinguishes a re-stage from an unseen change" as *u8, t12, ctr)
249
250 // ---- T15 RENAME must be ALLOWED / T16 REMOVAL must still be REFUSED ---------------------------
251 // The exact shape that refused this guard's own author three times: 3 tokens absent out of 21 =
252 // 142 permil, over the 50-permil bar and over the 2-absent floor -- but the 3 were RENAMED, not
253 // removed. T15 reproduces it (21 tokens, first 3 renamed: lost 3, GAINED 3, run count unchanged).
254 // T16 is its BITE: the same 3-token loss with NOTHING added and the run count DOWN must still be
255 // refused, so the escape can never be mistaken for a hole. A tooth that only proves the escape
256 // fires would be exactly the vacuity this session found in _mmu_gate -- the pair is the point.
257 gt_tokens("/tmp/nxsg_t15_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 21, 0, none)
258 gt_tokens("/tmp/nxsg_t15_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 21, 400, odd3)
259 gt_set_mtime("/tmp/nxsg_t15_live" as *u8, GT_NOW - GT_DAY)
260 gt_set_mtime("/tmp/nxsg_t15_live.new" as *u8, GT_NOW - (GT_DAY / 2))
261 let c15: i64 = sg_classify("/tmp/nxsg_t15_live" as *u8, "/tmp/nxsg_t15_live.new" as *u8, GT_NOW, cfg, out)
262 var t15: i64 = 0
263 if sg_allows(c15) == 1 { t15 = 1 }
264 gv_check("T15 RENAME allowed: 3 of 21 runs absent (142 permil) but 3 gained and run count held" as *u8, t15, ctr)
265 gv_puts(" class=" as *u8); gv_puts(sg_class_name(c15))
266 gv_puts(" permil=" as *u8); gv_num(out[SG_O_LOSTPERMIL])
267 gv_puts(" absent=" as *u8); gv_num(out[SG_O_MISSING])
268 gv_puts(" gained=" as *u8); gv_num(out[SG_O_GAINED])
269 gv_puts(" runs " as *u8); gv_num(out[SG_O_LIVERUNS])
270 gv_puts("->" as *u8); gv_num(out[SG_O_CANDRUNS]); gv_puts("\
271" as *u8)
272
273 gt_tokens("/tmp/nxsg_t16_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 21, 0, none)
274 gt_tokens("/tmp/nxsg_t16_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 18, 900, none)
275 gt_set_mtime("/tmp/nxsg_t16_live" as *u8, GT_NOW - GT_DAY)
276 gt_set_mtime("/tmp/nxsg_t16_live.new" as *u8, GT_NOW - (GT_DAY / 2))
277 let c16: i64 = sg_classify("/tmp/nxsg_t16_live" as *u8, "/tmp/nxsg_t16_live.new" as *u8, GT_NOW, cfg, out)
278 var t16: i64 = 0
279 if sg_allows(c16) == 0 { t16 = 1 }
280 gv_check("T16 BITE: the same 3-run loss with NOTHING gained and the count DOWN is still REFUSED" as *u8, t16, ctr)
281 gv_puts(" class=" as *u8); gv_puts(sg_class_name(c16))
282 gv_puts(" permil=" as *u8); gv_num(out[SG_O_LOSTPERMIL])
283 gv_puts(" absent=" as *u8); gv_num(out[SG_O_MISSING])
284 gv_puts(" gained=" as *u8); gv_num(out[SG_O_GAINED])
285 gv_puts(" runs " as *u8); gv_num(out[SG_O_LIVERUNS])
286 gv_puts("->" as *u8); gv_num(out[SG_O_CANDRUNS]); gv_puts("\
287" as *u8)
288
289 // ---- T14 SMALL-BINARY ANTI-BYPASS: one edit in an EIGHT-run binary must be ALLOWED ------------
290 // T11 proves a one-run edit is allowed in a 40-run fixture (25 permil). It did NOT catch the real
291 // false positive, because the victim had only EIGHT sampled runs, where ONE edit is 125 permil --
292 // over the 50-permil bar. THE TOOTH WAS RIGHT AND THE FIXTURE WAS TOO BIG. This one reproduces the
293 // actual shape that refused nx_offc_install, so the size-dependence cannot come back unnoticed.
294 gt_tokens("/tmp/nxsg_t14_live" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 8, 0, none)
295 gt_tokens("/tmp/nxsg_t14_live.new" as *u8, "NXSG_LIVE_CAPRUN_" as *u8, 8, 400, odd1)
296 gt_set_mtime("/tmp/nxsg_t14_live" as *u8, GT_NOW - GT_DAY)
297 gt_set_mtime("/tmp/nxsg_t14_live.new" as *u8, GT_NOW - (GT_DAY / 2))
298 let c14: i64 = sg_classify("/tmp/nxsg_t14_live" as *u8, "/tmp/nxsg_t14_live.new" as *u8, GT_NOW, cfg, out)
299 var t14: i64 = 0
300 if sg_allows(c14) == 1 { t14 = 1 }
301 gv_check("T14 SMALL-BINARY anti-bypass: 1 absent run of 8 (125 permil) is an ordinary edit, ALLOWED" as *u8, t14, ctr)
302 gv_puts(" class=" as *u8); gv_puts(sg_class_name(c14))
303 gv_puts(" caploss_permil=" as *u8); gv_num(out[SG_O_LOSTPERMIL])
304 gv_puts(" absent=" as *u8); gv_num(out[SG_O_MISSING])
305 gv_puts("/" as *u8); gv_num(out[SG_O_CHECKED])
306 gv_puts(" (permil bar needs >=" as *u8); gv_num(cfg[SG_C_CAPLOSSMIN]); gv_puts(" absent to fire)\n" as *u8)
307
308 // ---- T13 THE REFERENCE PREDICATE, BITE-PROVEN --------------------------------------------------
309 // `expire apply` RENAMES staged artifacts, and tool_allowlist.conf can point a REGISTERED tool at a
310 // <name>.sov.elf.new -- 45 rows do. Renaming one turns a live tool into exit 127 NOT-FOUND, so expiry
311 // must PROVE UNREFERENCED before it MOVES. This is exactly the predicate that path calls: it must FIRE
312 // on a name the registry references and stay SILENT on one it does not. Measured on the live registry
313 // the same day: 68,391 bytes read, 70 artifacts expiry-eligible, 0 of them referenced -- but a zero
314 // measured in the field proves the SILENT direction only, which is why the FIRING direction is proven
315 // here on a fixture instead of being assumed from a happy number.
316 let ab: *u8 = sys_mmap(4096)
317 let abn: i64 = gt_fill(ab, "nx_alpha\t/x/nx_alpha.elf\tGREEN\nnx_beta\t/x/nx_beta.sov.elf.new\tGREEN\n" as *u8)
318 let refhit: i64 = sg_contains(ab, abn, "nx_beta.sov.elf.new" as *u8, 19)
319 let refmiss: i64 = sg_contains(ab, abn, "nx_gamma.sov.elf.new" as *u8, 20)
320 gv_bite("T13 reference predicate (registry-referenced vs not)" as *u8, refhit, refmiss, ctr)
321
322 // ---- T9 the conf file is ACTUALLY read (not decorative) ----------------------------------------
323 // knowledge/staging_hygiene.conf sets caploss_refuse_permil=50 while the bootstrap default is 25,
324 // so reading 50 back can ONLY happen if the file was opened and parsed.
325 let lcfg: *i64 = sys_mmap(8 * SG_C_SLOTS) as *i64
326 let hadconf: i64 = sg_conf_load(lcfg)
327 var t9: i64 = 0
328 if hadconf == 1 { if lcfg[SG_C_CAPLOSS] == 50 { t9 = 1 } }
329 gv_check("T9 knowledge/staging_hygiene.conf is read (caploss 50 overrides default 25)" as *u8, t9, ctr)
330
331 // ---- T10 sg_allows is fail-closed for anything it does not recognise ---------------------------
332 var t10: i64 = 0
333 if sg_allows(SG_FORWARD) == 1 {
334 if sg_allows(SG_ORPHAN) == 1 {
335 if sg_allows(SG_BACKDATE) == 0 {
336 if sg_allows(SG_EXPIRED) == 0 {
337 if sg_allows(SG_SHRINK) == 0 {
338 if sg_allows(SG_CAPLOSS) == 0 {
339 if sg_allows(9999) == 0 { t10 = 1 }
340 }
341 }
342 }
343 }
344 }
345 }
346 gv_check("T10 sg_allows fail-closed (unknown class REFUSED)" as *u8, t10, ctr)
347
348 let rc: i64 = gv_verdict("STAGHYG-GATE" as *u8, ctr, "refuses backwards promotes, allows forward ones (including the DCE-shape shrink the token ruler vouches for), ignores ordinary edits; per-tooth lines above are the record" as *u8)
349 sys_exit(rc)
350 return rc
351}