code wiki / _hdl_build / nx_undopath_gate.nx
nx_undopath_gate.nx source
↩ module page · 479 lines · 30070 B
1// nx_undopath_gate.nx -- proves nx_undopath_lib, and CENSUSES every deploy row's reverse gear.
2//
3// THE THIRD QUESTION IN THE DEPLOY FAMILY. nx_deploycover_gate asks "is there a row at all";
4// nx_deploy_listen_gate asks "did the post-deploy check verify the INCOMING listener". This asks:
5// WHEN THE ROW'S REVERSE GEAR FIRES, DOES IT REVERSE THIS TARGET -- OR SOMEBODY ELSE'S?
6//
7// It exists because on 2026-09-03 the answer was "somebody else's" for 16 of 31 rows: they declared
8// `rollback`, and cmd_rollback() takes no target argument -- it renames sites.elf.prev over sites.elf and
9// kills sites.elf. /api/deploy's health watchdog invokes that field, so a failed deploy of any of those
10// daemons would AUTOMATICALLY have reverted the public front door and left the bad daemon live.
11//
12// ADVISORY ON THE CENSUS, HARD ON THE RULER. The pure classifier and the ratchet are teeth: they fail the
13// gate. The live census RATCHETS -- wrong-subject may only fall -- because a permanently-red detector is
14// one everybody learns to ignore, and because the honest remedy for a wrong row is a source change in the
15// control plane, not a blocked build. A RISE is RED and the baseline is NOT rewritten.
16//
17// WHY IT COMPOSES md_resolve_target INSTEAD OF PARSING THE CONF ITSELF: that function IS the resolution
18// /api/deploy performs -- primary knowledge/hosting path, cwd fallback, and the compiled-in builtin rows.
19// A hand-rolled parser would measure a DIFFERENT SUBJECT than the deploy plane actually reads, which is
20// the exact wrong-subject error this gate exists to catch. The row NAMES are enumerated from the conf
21// (that is a list, not a resolution); every rb verb comes from the shipping resolver.
22// license_tier: ORIGINAL Read-only. No hw writes (Rule 26). expect_exit: 0
23import "nx_syscalls.nx"
24import "nx_gate_verdict.nx"
25import "nx_gatekit_lib.nx"
26import "nx_undopath_lib.nx"
27import "_hdl_build/nx_mgmt_data.nx"
28
29const UG_FX: *u8 = "/tmp/nx_undopath_gate"
30const UG_FX_SRC: *u8 = "/tmp/nx_undopath_gate/fixture_hostctl.nx"
31const UG_FX_ABSENT: *u8 = "/tmp/nx_undopath_gate/does-not-exist.nx"
32const UG_FX_CONSTSRC: *u8 = "/tmp/nx_undopath_gate/fixture_consts.nx"
33// a verb whose body names its subject ONLY through a const -- the cmd_superrollback / cmd_toolchainrollback
34// shape. There is deliberately NO literal "nx_gamma.elf" inside the function body.
35const UG_FX_CONSTBODY: *u8 = "const HC_GAMMA_LIVE: *u8 = \"/srv/nx_gamma.elf\"\nconst HC_GAMMA_PREV: *u8 = \"/srv/nx_gamma.elf.prev\"\nfunc cmd_gammarollback() -> i64 {\n let r: i64 = sys_renameat(HC_GAMMA_PREV, HC_GAMMA_LIVE)\n return r\n}\nfunc cmd_other() -> i64 { return 0 }\n"
36const UG_FX_TRAILSRC: *u8 = "/tmp/nx_undopath_gate/fixture_trailing_comment.nx"
37// THE LIVE DEFECT, PLANTED: cmd_kapparollback is followed by the NEXT function's comment block, and that
38// comment names HC_LAMBDA_LIVE. A span that ends at the next `func` swallows that comment and resolves
39// nx_lambda.elf as this verb's subject -- which is exactly how cmd_loginrollback read as ambiguous while
40// being correct all along. This fixture fails against the old boundary and passes against the brace.
41const UG_FX_TRAILBODY: *u8 = "const HC_KAPPA_LIVE: *u8 = \"/srv/nx_kappa.elf\"\nconst HC_KAPPA_PREV: *u8 = \"/srv/nx_kappa.elf.prev\"\nconst HC_LAMBDA_LIVE: *u8 = \"/srv/nx_lambda.elf\"\nfunc cmd_kapparollback() -> i64 {\n return sys_renameat(HC_KAPPA_PREV, HC_KAPPA_LIVE)\n}\n\n// LAMBDA deploy prep: the supervisor's HC_LAMBDA_LIVE guard then auto-launches it.\n// Idempotent and never-brick.\nfunc cmd_lambdadeploy() -> i64 {\n return 0\n}\n"
42const UG_FX_PAIRSRC: *u8 = "/tmp/nx_undopath_gate/fixture_pair.nx"
43// a BRIDGING forward gear + its reverse gear + an UNRELATED reverse gear. The staged artifact
44// (nx_theta.sov.elf.new, in one directory) and the live artifact (nx_theta.elf, in another) differ exactly
45// as galxgw's do, so hops 1 and 2 cannot resolve the reverse gear and only the PAIR can.
46const UG_FX_PAIRBODY: *u8 = "const HC_T_SOVNEW: *u8 = \"/nishihost/nx_theta.sov.elf.new\"\nconst HC_T_NEW: *u8 = \"/srv/app/nx_theta.elf.new\"\nconst HC_T_LIVE: *u8 = \"/srv/app/nx_theta.elf\"\nconst HC_T_PREV: *u8 = \"/srv/app/nx_theta.elf.prev\"\nconst HC_I_LIVE: *u8 = \"/srv/app/nx_iota.elf\"\nconst HC_I_PREV: *u8 = \"/srv/app/nx_iota.elf.prev\"\nfunc cmd_thetadeploy() -> i64 {\n hc_copyfile(HC_T_SOVNEW, HC_T_NEW)\n return hc_publish(HC_T_NEW, HC_T_LIVE)\n}\nfunc cmd_thetarollback() -> i64 {\n return sys_renameat(HC_T_PREV, HC_T_LIVE)\n}\nfunc cmd_iotarollback() -> i64 {\n return sys_renameat(HC_I_PREV, HC_I_LIVE)\n}\n"
47const UG_HOSTCTL: *u8 = "buildroot/runtime/_hdl_build/nx_hostctl.nx"
48const UG_HOSTCTL_B: *u8 = "runtime/_hdl_build/nx_hostctl.nx"
49const UG_CONF: *u8 = "deploy_targets.conf"
50const UG_CONF_B: *u8 = "knowledge/hosting/deploy_targets.conf"
51const UG_RATCHET: *u8 = "knowledge/status/undopath.ratchet"
52const UG_RATCHET_B: *u8 = "../knowledge/status/undopath.ratchet"
53const UG_MODE: i64 = 420
54
55// fixture source: two verbs whose bodies name DIFFERENT artifacts, so the body-resolution teeth have a
56// real subject to get right and a real subject to get wrong.
57const UG_FX_BODY: *u8 = "func cmd_alpharollback() -> i64 {\n let r: i64 = sys_renameat(\"nx_alpha.elf.prev\" as *u8, \"nx_alpha.elf\" as *u8)\n return r\n}\nfunc cmd_betarollback() -> i64 {\n let r: i64 = sys_renameat(\"nx_beta.elf.prev\" as *u8, \"nx_beta.elf\" as *u8)\n return r\n}\n"
58
59const UG_ROWCAP: i64 = 64
60const UG_NAMEW: i64 = 64
61const UG_PATHCAP: i64 = 512
62const UG_SMALL: i64 = 256
63const UG_CONFCAP: i64 = 65536
64const UG_SRCCAP: i64 = 1048576
65const UG_NL: i64 = 10
66const UG_HASH: i64 = 35
67const UG_SP: i64 = 32
68
69func ug_slot(names: *u8, i: i64) -> *u8 { return ((names as i64) + i * UG_NAMEW) as *u8 }
70
71// enumerate ROW NAMES (field 0) out of the conf. A list, not a resolution -- every rb comes from
72// md_resolve_target below, so this cannot disagree with the deploy plane about what a row MEANS.
73func ug_row_names(path: *u8, names: *u8) -> i64 {
74 let box: *i64 = sys_mmap(16) as *i64
75 let buf: *u8 = sys_read_file(path, box)
76 if (buf as i64) == 0 { return 0 }
77 let n: i64 = box[0]
78 if n <= 0 { return 0 }
79 var cnt: i64 = 0
80 var i: i64 = 0
81 while i < n {
82 let e: i64 = gk_eol(buf, i, n)
83 if e > i {
84 if (buf[i] as i64) != UG_HASH {
85 if cnt < UG_ROWCAP {
86 let d: *u8 = ug_slot(names, cnt)
87 var w: i64 = 0
88 var p: i64 = i
89 while p < e {
90 if (buf[p] as i64) == UG_SP { p = e } else {
91 if w + 1 < UG_NAMEW { d[w] = buf[p]; w = w + 1 }
92 p = p + 1
93 }
94 }
95 d[w] = 0 as u8
96 if w > 0 { cnt = cnt + 1 }
97 }
98 }
99 }
100 i = e + 1
101 }
102 return cnt
103}
104
105func ug_read_src(a: *u8, b: *u8, box: *i64) -> *u8 {
106 var buf: *u8 = sys_read_file(a, box)
107 if (buf as i64) == 0 { buf = sys_read_file(b, box) }
108 return buf
109}
110
111func ug_ratchet_read(a: *u8, b: *u8) -> i64 {
112 let buf: *u8 = sys_mmap(UG_SMALL)
113 var n: i64 = gk_read(a, buf, UG_SMALL - 1)
114 if n <= 0 { n = gk_read(b, buf, UG_SMALL - 1) }
115 if n <= 0 { return 0 - 1 }
116 var v: i64 = 0
117 var seen: i64 = 0
118 var i: i64 = 0
119 while i < n {
120 let c: i64 = buf[i] as i64
121 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); seen = 1 } }
122 i = i + 1
123 }
124 if seen == 0 { return 0 - 1 }
125 return v
126}
127
128func main(argc: i64, argv: *i64) -> i64 {
129 gv_head("nx_undopath_gate -- does each deploy target's REVERSE GEAR reverse its OWN subject? (pure classifier, body resolution, ratchet, live census)" as *u8)
130 let ctr: *i64 = gv_ctr()
131 gk_mkdir(UG_FX)
132 gk_write(UG_FX_SRC, UG_FX_BODY)
133 gk_rm(UG_FX_ABSENT)
134 gv_check("fixture-reached-condition: fixture source present and the absent path absent" as *u8,
135 gk_exists(UG_FX_SRC) * (1 - gk_exists(UG_FX_ABSENT)), ctr)
136
137 // ---- PURE classifier: every branch, with the two catastrophic ones as named neg-controls ----
138 gv_check("classify: the edge gear on the EDGE row is OWN-SUBJECT (correct for exactly one row)" as *u8,
139 (up_classify(1, 1, 0, 0, 1, 0, 0) == UP_OWN) as i64, ctr)
140 gv_check("neg-control-edge-gear-on-a-NON-edge-row-is-WRONG-SUBJECT (the 16-row defect: it reverts the front door)" as *u8,
141 (up_classify(0, 1, 0, 0, 1, 0, 0) == UP_WRONG) as i64, ctr)
142 gv_check("classify: the per-row gear pointed at ITSELF is OWN-SUBJECT" as *u8,
143 (up_classify(0, 0, 1, 0, 1, 0, 0) == UP_OWN) as i64, ctr)
144 gv_check("neg-control-per-row-gear-pointed-at-ANOTHER-row-is-WRONG-SUBJECT" as *u8,
145 (up_classify(0, 0, 0, 1, 1, 0, 0) == UP_WRONG) as i64, ctr)
146 gv_check("classify: an unimplemented verb is FAILSAFE (the dispatcher's proven no-op), never OWN" as *u8,
147 (up_classify(0, 0, 0, 0, 0, 0, 0) == UP_FAILSAFE) as i64, ctr)
148 // THE VACUITY THAT WAS IN THIS RULER UNTIL 2026-09-03: a row pointed at the per-row gear read OWN from
149 // its own spelling, even where the control plane does not implement that gear. These two teeth are the
150 // guard -- declaring a reverse gear is not the same as having one.
151 gv_check("neg-control-a-per-row-gear-whose-DISPATCH-TARGET-does-not-exist-is-FAILSAFE-not-OWN (declaring a gear is not having one)" as *u8,
152 (up_classify(0, 0, 1, 0, 0, 0, 0) == UP_FAILSAFE) as i64, ctr)
153 gv_check("neg-control-the-EDGE-gear-with-no-cmd_rollback-in-the-control-plane-is-FAILSAFE-not-WRONG (an absent verb harms nobody)" as *u8,
154 (up_classify(0, 1, 0, 0, 0, 0, 0) == UP_FAILSAFE) as i64, ctr)
155 gv_check("classify: a dedicated verb whose body names THIS row's artifact is OWN-SUBJECT" as *u8,
156 (up_classify(0, 0, 0, 0, 1, 1, 0) == UP_OWN) as i64, ctr)
157 gv_check("neg-control-dedicated-verb-naming-ANOTHER-artifact-is-WRONG-SUBJECT (the docportal shape)" as *u8,
158 (up_classify(0, 0, 0, 0, 1, 0, 1) == UP_WRONG) as i64, ctr)
159 gv_check("classify: a body naming BOTH artifacts is UNPROVEN -- ambiguity abstains, never acquits" as *u8,
160 (up_classify(0, 0, 0, 0, 1, 1, 1) == UP_UNPROVEN) as i64, ctr)
161 gv_check("classify: a verb that exists but resolves to NO artifact is UNPROVEN, never OWN" as *u8,
162 (up_classify(0, 0, 0, 0, 1, 0, 0) == UP_UNPROVEN) as i64, ctr)
163 gv_check("classify: the edge gear is decided BEFORE body resolution (a body hit cannot launder it)" as *u8,
164 (up_classify(0, 1, 0, 0, 1, 1, 0) == UP_WRONG) as i64, ctr)
165 gv_bite("bite: the SAME classifier says WRONG for the edge gear off-edge and OWN for it on-edge" as *u8,
166 (up_classify(0, 1, 0, 0, 1, 0, 0) == UP_WRONG) as i64,
167 (up_classify(1, 1, 0, 0, 1, 0, 0) == UP_WRONG) as i64, ctr)
168
169 // ---- body resolution over a fixture source with two rival artifacts ----
170 let sbox: *i64 = sys_mmap(16) as *i64
171 let fsrc: *u8 = sys_read_file(UG_FX_SRC, sbox)
172 var fn: i64 = 0
173 if (fsrc as i64) != 0 { fn = sbox[0] }
174 let span: *i64 = sys_mmap(32) as *i64
175 let found: i64 = up_verb_body(fsrc, fn, "alpharollback" as *u8, span)
176 gv_check("fixture-reached-condition: the fixture source loaded and carries two rival verbs" as *u8,
177 ((fn > 0) as i64) * ((up_find(fsrc, fn, "cmd_betarollback" as *u8) >= 0) as i64), ctr)
178 gv_check("body: the span of cmd_alpharollback is found and bounded before the NEXT func" as *u8,
179 (found as i64) * ((span[1] > span[0]) as i64), ctr)
180 var body_own: i64 = 0
181 var body_other: i64 = 0
182 if found == 1 {
183 let bp: *u8 = ((fsrc as i64) + span[0]) as *u8
184 let bn: i64 = span[1] - span[0]
185 if up_find(bp, bn, "nx_alpha.elf" as *u8) >= 0 { body_own = 1 }
186 if up_find(bp, bn, "nx_beta.elf" as *u8) >= 0 { body_other = 1 }
187 }
188 gv_check("body: cmd_alpharollback's span names nx_alpha.elf and NOT nx_beta.elf (the span really is bounded)" as *u8,
189 body_own * (1 - body_other), ctr)
190 gv_check("neg-control-a-verb-that-does-not-exist-resolves-no-span" as *u8,
191 (up_verb_body(fsrc, fn, "zzznosuchrollback" as *u8, span) == 0) as i64, ctr)
192 let bnbuf: *u8 = sys_mmap(UG_SMALL)
193 up_basename("/volume1/homes/elderwesto/nishihost/nx_fin_serve.elf" as *u8, bnbuf, UG_SMALL)
194 gv_check("basename: a live path reduces to the artifact name a reverse gear must mention" as *u8,
195 up_streq(bnbuf, "nx_fin_serve.elf" as *u8), ctr)
196 // ---- the STEM and the CONST hop: the two reasons the first census left 8 rows UNPROVEN ----
197 let stbuf: *u8 = sys_mmap(UG_SMALL)
198 up_stem("nx_mgmt_api.elf.new" as *u8, stbuf, UG_SMALL)
199 gv_check("stem: a STAGED name reduces to what a reverse gear actually touches (nx_mgmt_api.elf.new -> nx_mgmt_api.elf)" as *u8,
200 up_streq(stbuf, "nx_mgmt_api.elf" as *u8), ctr)
201 up_stem("nx_torrent_daemon.sov.elf.new" as *u8, stbuf, UG_SMALL)
202 gv_check("stem: a .sov.elf.new staged name reduces to its .sov.elf stem" as *u8,
203 up_streq(stbuf, "nx_torrent_daemon.sov.elf" as *u8), ctr)
204 up_stem("/a/b/nx_plain.elf" as *u8, stbuf, UG_SMALL)
205 gv_check("neg-control-stem-leaves-a-name-with-no-.new-suffix-untouched" as *u8,
206 up_streq(stbuf, "nx_plain.elf" as *u8), ctr)
207 // fixture: a verb naming its subject ONLY through a const, which is the cmd_superrollback shape
208 gk_write(UG_FX_CONSTSRC, UG_FX_CONSTBODY)
209 let cbox: *i64 = sys_mmap(16) as *i64
210 let csrc: *u8 = sys_read_file(UG_FX_CONSTSRC, cbox)
211 var cn: i64 = 0
212 if (csrc as i64) != 0 { cn = cbox[0] }
213 let cspan: *i64 = sys_mmap(32) as *i64
214 let cfound: i64 = up_verb_body(csrc, cn, "gammarollback" as *u8, cspan)
215 gv_check("fixture-reached-condition: the const-only fixture loaded and its body carries NO literal artifact name" as *u8,
216 ((cn > 0) as i64) * (cfound as i64) * ((up_find(((csrc as i64) + cspan[0]) as *u8, cspan[1] - cspan[0], "nx_gamma.elf" as *u8) < 0) as i64), ctr)
217 let cvb: *u8 = sys_mmap(UG_PATHCAP)
218 gv_check("const: a const's VALUE is resolvable by name from the source" as *u8,
219 (up_const_value(csrc, cn, "HC_GAMMA_LIVE" as *u8, cvb, UG_PATHCAP) as i64) * up_streq(cvb, "/srv/nx_gamma.elf" as *u8), ctr)
220 gv_check("CONST HOP: a body that names its subject ONLY through a const still resolves as naming it" as *u8,
221 up_body_names(csrc, cn, cspan[0], cspan[1], "nx_gamma.elf" as *u8), ctr)
222 gv_check("neg-control-const-hop-does-not-invent-a-match-for-an-unrelated-artifact" as *u8,
223 (1 - up_body_names(csrc, cn, cspan[0], cspan[1], "nx_delta.elf" as *u8)), ctr)
224 gv_check("neg-control-an-empty-stem-never-matches (a row with no resolvable artifact cannot read as OWN)" as *u8,
225 (1 - up_body_names(csrc, cn, cspan[0], cspan[1], "" as *u8)), ctr)
226
227 // ---- SPAN BOUNDARY: a body must not absorb the NEXT function's comment block ----
228 gk_write(UG_FX_TRAILSRC, UG_FX_TRAILBODY)
229 let tbox: *i64 = sys_mmap(16) as *i64
230 let tsrc: *u8 = sys_read_file(UG_FX_TRAILSRC, tbox)
231 var tn: i64 = 0
232 if (tsrc as i64) != 0 { tn = tbox[0] }
233 let tsp: *i64 = sys_mmap(32) as *i64
234 let tfound: i64 = up_verb_body(tsrc, tn, "kapparollback" as *u8, tsp)
235 gv_check("fixture-reached-condition: the trailing-comment fixture loaded, the span resolved, and the file DOES carry the neighbour's const" as *u8,
236 ((tn > 0) as i64) * tfound * ((up_find(tsrc, tn, "HC_LAMBDA_LIVE" as *u8) >= 0) as i64), ctr)
237 gv_check("SPAN BOUNDARY: the span ends at the verb's own closing brace, so it EXCLUDES the next function's comment block" as *u8,
238 (up_find(((tsrc as i64) + tsp[0]) as *u8, tsp[1] - tsp[0], "LAMBDA deploy prep" as *u8) < 0) as i64, ctr)
239 gv_check("neg-control-a-verb-does-NOT-resolve-its-NEIGHBOUR's-artifact (the live opaquelogin false-ambiguity, bitten)" as *u8,
240 (1 - up_body_names(tsrc, tn, tsp[0], tsp[1], "nx_lambda.elf" as *u8)), ctr)
241 gv_check("SPAN BOUNDARY: and it still resolves its OWN subject through the const hop (the tightening did not blind it)" as *u8,
242 up_body_names(tsrc, tn, tsp[0], tsp[1], "nx_kappa.elf" as *u8), ctr)
243
244 // ---- HOP 3: THE PAIR. artifact-name reduction, the pair hop, and the additivity proof ----
245 let anb: *u8 = sys_mmap(UG_SMALL)
246 up_artifact_name("/srv/nx_theta.elf.new" as *u8, anb, UG_SMALL)
247 gv_check("artifact-name: a staged .new reduces to the live artifact" as *u8,
248 up_streq(anb, "nx_theta.elf" as *u8), ctr)
249 up_artifact_name("/srv/nx_theta.elf.prev" as *u8, anb, UG_SMALL)
250 gv_check("artifact-name: a banked .prev reduces to the SAME live artifact (that shared token IS the pair)" as *u8,
251 up_streq(anb, "nx_theta.elf" as *u8), ctr)
252 up_artifact_name("/srv/nx_theta.elf" as *u8, anb, UG_SMALL)
253 gv_check("neg-control-artifact-name-leaves-a-live-path-untouched" as *u8,
254 up_streq(anb, "nx_theta.elf" as *u8), ctr)
255
256 // fixture: a BRIDGING forward gear (staged name differs from the live name by a .sov infix AND a
257 // directory) beside its reverse gear -- the galxgw shape, which hops 1 and 2 both correctly miss.
258 gk_write(UG_FX_PAIRSRC, UG_FX_PAIRBODY)
259 let pbox: *i64 = sys_mmap(16) as *i64
260 let psrc: *u8 = sys_read_file(UG_FX_PAIRSRC, pbox)
261 var pn: i64 = 0
262 if (psrc as i64) != 0 { pn = pbox[0] }
263 let fsp: *i64 = sys_mmap(32) as *i64
264 let rsp: *i64 = sys_mmap(32) as *i64
265 let osp: *i64 = sys_mmap(32) as *i64
266 let ffound: i64 = up_verb_body(psrc, pn, "thetadeploy" as *u8, fsp)
267 let rfound: i64 = up_verb_body(psrc, pn, "thetarollback" as *u8, rsp)
268 let ofound: i64 = up_verb_body(psrc, pn, "iotarollback" as *u8, osp)
269 gv_check("fixture-reached-condition: the bridging-pair fixture loaded and all three verb spans resolved" as *u8,
270 ((pn > 0) as i64) * ffound * rfound * ofound, ctr)
271 // the whole point: hop 1+2 CANNOT see this, so the pair hop is not measuring something already covered
272 gv_check("fixture-reached-condition: the reverse gear does NOT name the row's STAGED stem (hops 1+2 both miss it)" as *u8,
273 (1 - up_body_names(psrc, pn, rsp[0], rsp[1], "nx_theta.sov.elf" as *u8)), ctr)
274 gv_check("PAIR HOP: a reverse gear that names the artifact its own FORWARD gear writes resolves as OWN-SUBJECT" as *u8,
275 up_pair_common(psrc, pn, fsp[0], fsp[1], rsp[0], rsp[1]), ctr)
276 gv_check("neg-control-pair-hop-does-not-pair-a-forward-gear-with-ANOTHER-artifact's-reverse-gear" as *u8,
277 (1 - up_pair_common(psrc, pn, fsp[0], fsp[1], osp[0], osp[1])), ctr)
278 gv_check("neg-control-pair-hop-on-an-empty-span-cannot-match (a missing forward verb never acquits)" as *u8,
279 (1 - up_pair_common(psrc, pn, 0, 0, rsp[0], rsp[1])), ctr)
280
281 // ADDITIVITY, PROVEN EXHAUSTIVELY OVER ALL 128 INPUT COMBINATIONS rather than asserted in a comment.
282 // The safety claim is that the pair hop can ONLY convert UNPROVEN -> OWN. If it could convert WRONG ->
283 // OWN it would acquit exactly the defect this gate exists to find, so this is the load-bearing tooth.
284 var addok: i64 = 1
285 var neutok: i64 = 1
286 var rescued: i64 = 0
287 var m: i64 = 0
288 while m < 128 {
289 let a0: i64 = m - (m / 2) * 2
290 let a1: i64 = (m / 2) - (m / 4) * 2
291 let a2: i64 = (m / 4) - (m / 8) * 2
292 let a3: i64 = (m / 8) - (m / 16) * 2
293 let a4: i64 = (m / 16) - (m / 32) * 2
294 let a5: i64 = (m / 32) - (m / 64) * 2
295 let a6: i64 = m / 64
296 let base: i64 = up_classify(a0, a1, a2, a3, a4, a5, a6)
297 let with1: i64 = up_classify_pair(a0, a1, a2, a3, a4, a5, a6, 1)
298 let with0: i64 = up_classify_pair(a0, a1, a2, a3, a4, a5, a6, 0)
299 if with0 != base { neutok = 0 }
300 if base == UP_UNPROVEN {
301 if with1 != UP_OWN { addok = 0 }
302 rescued = rescued + 1
303 } else {
304 if with1 != base { addok = 0 }
305 }
306 m = m + 1
307 }
308 gv_check("PAIR ADDITIVITY (all 128 combinations): pair_own=1 changes ONLY the UNPROVEN verdicts -- it can never turn WRONG into OWN" as *u8,
309 addok, ctr)
310 gv_check("neg-control-pair-hop-is-a-no-op-when-it-does-not-fire (pair_own=0 reproduces the incumbent verdict on all 128)" as *u8,
311 neutok, ctr)
312 gv_check("fixture-reached-condition: the additivity sweep actually visited UNPROVEN cases (a vacuous sweep would prove nothing)" as *u8,
313 (rescued > 0) as i64, ctr)
314
315 // ---- ratchet arithmetic ----
316 gv_check("ratchet: first sight baselines (floor -1, wrong 3 -> 3)" as *u8, (up_ratchet_next(0 - 1, 3) == 3) as i64, ctr)
317 gv_check("ratchet: falls (3 -> 0)" as *u8, (up_ratchet_next(3, 0) == 0) as i64, ctr)
318 gv_check("ratchet: holds (0 -> 0)" as *u8, (up_ratchet_next(0, 0) == 0) as i64, ctr)
319 gv_check("neg-control-ratchet-refuses-to-rise (0 -> 2 is -1, baseline NOT rewritten)" as *u8,
320 (up_ratchet_next(0, 2) == (0 - 1)) as i64, ctr)
321
322 // ---- LIVE CENSUS: every row, resolved through the shipping resolver ----
323 let names: *u8 = sys_mmap(UG_ROWCAP * UG_NAMEW)
324 var nrows: i64 = ug_row_names(UG_CONF, names)
325 if nrows == 0 { nrows = ug_row_names(UG_CONF_B, names) }
326 let hbox: *i64 = sys_mmap(16) as *i64
327 let hsrc: *u8 = ug_read_src(UG_HOSTCTL, UG_HOSTCTL_B, hbox)
328 var hn: i64 = 0
329 if (hsrc as i64) != 0 { hn = hbox[0] }
330 gv_need("live: deploy_targets.conf rows enumerated (a census over zero rows must never read as clean)" as *u8, (nrows > 0) as i64, ctr)
331 gv_need("live: control-plane source readable (an unreadable reverse gear cannot be judged)" as *u8, (hn > 0) as i64, ctr)
332
333 var c_own: i64 = 0
334 var c_wrong: i64 = 0
335 var c_fail: i64 = 0
336 var c_unpr: i64 = 0
337 if nrows > 0 { if hn > 0 {
338 let kindb: *i64 = sys_mmap(16) as *i64
339 let srcb: *u8 = sys_mmap(UG_PATHCAP)
340 let subb: *u8 = sys_mmap(UG_SMALL)
341 let urlb: *u8 = sys_mmap(UG_SMALL)
342 let rbb: *u8 = sys_mmap(UG_SMALL)
343 var r: i64 = 0
344 while r < nrows {
345 let nm: *u8 = ug_slot(names, r)
346 md_resolve_target(UG_CONF, nm, 0, up_len(nm), kindb, srcb, subb, urlb, rbb)
347 let is_edge: i64 = up_streq(nm, UP_EDGE_ROW)
348 let rb_edge: i64 = up_streq(rbb, UP_EDGE_GEAR)
349 var rb_self: i64 = 0
350 var rb_other: i64 = 0
351 if up_prefix(rbb, UP_PERROW_PFX) == 1 {
352 let who: *u8 = ((rbb as i64) + UP_PERROW_PFXLEN) as *u8
353 if up_streq(who, nm) == 1 { rb_self = 1 } else { rb_other = 1 }
354 }
355 var vex: i64 = 0
356 var bown: i64 = 0
357 var bother: i64 = 0
358 let othb: *u8 = sys_mmap(UG_SMALL)
359 othb[0] = 0 as u8
360 // ASK ABOUT THE VERB THAT WILL ACTUALLY RUN, for EVERY branch. The edge gear dispatches to
361 // cmd_rollback and the per-row gear to cmd_gdeploy_rollback; only the third case runs a verb
362 // named by the row itself. Resolving existence for all three is what stops a row from reading
363 // OWN-SUBJECT purely because someone wrote the right word in field 5.
364 var vname: *u8 = rbb
365 if rb_edge == 1 { vname = UP_EDGE_GEAR }
366 if rb_self == 1 { vname = UP_PERROW_VERB }
367 if rb_other == 1 { vname = UP_PERROW_VERB }
368 if up_verb_body(hsrc, hn, vname, span) == 1 { vex = 1 }
369 if rb_edge == 0 { if rb_self == 0 { if rb_other == 0 {
370 if vex == 1 {
371 let bp2: *u8 = ((hsrc as i64) + span[0]) as *u8
372 let bn2: i64 = span[1] - span[0]
373 // OWN: the verb body names THIS row's artifact STEM, directly or via a const it mentions.
374 // The stem (not the staged .new name) is what a REVERSE gear touches -- see up_stem.
375 let ownb: *u8 = sys_mmap(UG_SMALL)
376 up_stem(srcb, ownb, UG_SMALL)
377 if up_body_names(hsrc, hn, span[0], span[1], ownb) == 1 { bown = 1 }
378 // OTHER: it names some sibling row's staged artifact instead
379 var q: i64 = 0
380 while q < nrows {
381 if q != r {
382 let nm2: *u8 = ug_slot(names, q)
383 let k2: *i64 = sys_mmap(16) as *i64
384 let s2: *u8 = sys_mmap(UG_PATHCAP)
385 let u2: *u8 = sys_mmap(UG_SMALL)
386 let v2: *u8 = sys_mmap(UG_SMALL)
387 let b2: *u8 = sys_mmap(UG_SMALL)
388 md_resolve_target(UG_CONF, nm2, 0, up_len(nm2), k2, s2, u2, v2, b2)
389 let onb: *u8 = sys_mmap(UG_SMALL)
390 up_stem(s2, onb, UG_SMALL)
391 if up_len(onb) > 0 { if up_streq(onb, ownb) == 0 {
392 if up_body_names(hsrc, hn, span[0], span[1], onb) == 1 {
393 bother = 1
394 // NAME THE COLLIDING STEM. "other=1" alone sends the next reader to
395 // re-derive which of 30 siblings matched -- the reason is in hand HERE.
396 if up_len(othb) == 0 { up_stem(s2, othb, UG_SMALL) }
397 }
398 } }
399 }
400 q = q + 1
401 }
402 }
403 } } }
404 // HOP 3, THE PAIR: a reverse gear is correct iff it acts on the artifact its own FORWARD gear
405 // writes. subb is field 4 -- the deploy sub the row itself declares -- so this compares the
406 // actual pair, not the row's staged filename, which a legitimate bridge makes differ.
407 var pown: i64 = 0
408 if vex == 1 { if bown == 0 { if bother == 0 {
409 let fspan: *i64 = sys_mmap(16) as *i64
410 if up_verb_body(hsrc, hn, subb, fspan) == 1 {
411 pown = up_pair_common(hsrc, hn, fspan[0], fspan[1], span[0], span[1])
412 }
413 } } }
414 let st: i64 = up_classify_pair(is_edge, rb_edge, rb_self, rb_other, vex, bown, bother, pown)
415 if st == UP_OWN { c_own = c_own + 1 }
416 if st == UP_WRONG { c_wrong = c_wrong + 1 }
417 if st == UP_FAILSAFE { c_fail = c_fail + 1 }
418 if st == UP_UNPROVEN { c_unpr = c_unpr + 1 }
419 // a count without a worklist is not actionable: name every row that is not OWN
420 if st != UP_OWN {
421 gv_puts(" " as *u8); gv_puts(up_state_name(st))
422 gv_puts(" row=" as *u8); gv_puts(nm)
423 gv_puts(" rb=" as *u8); gv_puts(rbb)
424 // THE REASON TRAVELS WITH THE COUNT: a worklist row that does not say WHICH conjunct failed
425 // costs the next reader the whole investigation again. These flags are already in hand.
426 gv_puts(" [verb=" as *u8); gv_num(vex)
427 gv_puts(" own=" as *u8); gv_num(bown)
428 gv_puts(" other=" as *u8); gv_num(bother)
429 if bother == 1 { gv_puts(" collides_with=" as *u8); gv_puts(othb) }
430 gv_puts(" pair=" as *u8); gv_num(pown)
431 gv_puts(" fwd=" as *u8); gv_puts(subb)
432 gv_puts("]\n" as *u8)
433 }
434 r = r + 1
435 }
436 } }
437 // DECLARE THE SUBJECT. This census reads the control plane's SOURCE, so every verdict is a statement
438 // about what hostctl IMPLEMENTS -- not about what the deployed binary currently does. Those differ
439 // whenever the artifact is BEHIND its source, which is exactly the window in which a freshly declared
440 // reverse gear is real in the source and still absent from the running binary. Saying so here costs
441 // one line and stops the census being read as a liveness claim it never made.
442 gv_puts("UNDOPATH subject=SOURCE hostctl_src_bytes=" as *u8); gv_num(hn)
443 gv_puts(" (a gear counted OWN is IMPLEMENTED; whether it is DEPLOYED is nx_artifactdrift's BEHIND axis, not this gate's)\n" as *u8)
444 gv_puts("UNDOPATH rows=" as *u8); gv_num(nrows)
445 gv_puts(" own=" as *u8); gv_num(c_own)
446 gv_puts(" wrong=" as *u8); gv_num(c_wrong)
447 gv_puts(" failsafe=" as *u8); gv_num(c_fail)
448 gv_puts(" unproven=" as *u8); gv_num(c_unpr)
449 gv_puts("\n" as *u8)
450 gv_check("live: the partition SUMS to the row count (an unreconciled census is a leak)" as *u8,
451 ((c_own + c_wrong + c_fail + c_unpr) == nrows) as i64, ctr)
452 let floor: i64 = ug_ratchet_read(UG_RATCHET, UG_RATCHET_B)
453 let nxt: i64 = up_ratchet_next(floor, c_wrong)
454 gv_puts(" ratchet floor=" as *u8); gv_num(floor)
455 gv_puts(" observed_wrong=" as *u8); gv_num(c_wrong)
456 gv_puts("\n" as *u8)
457 gv_check("live-ratchet: wrong-subject rows are at or below the persisted floor (a RISE is RED and the floor is NOT rewritten)" as *u8,
458 (nxt >= 0) as i64, ctr)
459 if nxt >= 0 {
460 if floor < 0 {
461 let wb: *u8 = sys_mmap(UG_SMALL)
462 let wn: i64 = gk_catn(wb, 0, nxt)
463 wb[wn] = UG_NL as u8
464 wb[wn + 1] = 0 as u8
465 gk_write(UG_RATCHET, wb)
466 gv_puts(" UNDOPATH-RATCHET baselined at " as *u8); gv_num(nxt); gv_puts("\n" as *u8)
467 }
468 if floor > nxt {
469 let wb2: *u8 = sys_mmap(UG_SMALL)
470 let wn2: i64 = gk_catn(wb2, 0, nxt)
471 wb2[wn2] = UG_NL as u8
472 wb2[wn2 + 1] = 0 as u8
473 gk_write(UG_RATCHET, wb2)
474 gv_puts(" UNDOPATH-RATCHET tightened to " as *u8); gv_num(nxt); gv_puts("\n" as *u8)
475 }
476 }
477 return gv_verdict("nx_undopath_gate" as *u8, ctr,
478 "the pure classifier over every branch incl. both catastrophic neg-controls, verb-body span resolution on a two-rival fixture, basename reduction, ratchet arithmetic, and a live census of every deploy row resolved through the SHIPPING md_resolve_target -- naming each row that is not OWN-SUBJECT" as *u8)
479}