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}