code wiki / _hdl_build / _ale_plan_gate.nx

_ale_plan_gate.nx source

↩ module page · 369 lines · 15201 B

1// _ale_plan_gate.nx -- the ALE-R2a gate: sovereign agent-core PLAN-phase organ. 2// Diff-lane / no-mocks: runs the REAL sovereign planner nx_ale_plan.elf (built by 3// nx_cc_sovereign->nxasm_x86_main, NO gcc) over REAL ALE-format task fixtures and asserts: 4// (1) INGEST -- over task_good the organ emits a PLAN file of ordered 5// "step|<n>|<verb>|<arg>" lines (>=3 step lines, all step|-prefixed) 6// (2) ORDERED -- the emitted step numbers are exactly 1,2,3 contiguous (no gaps) 7// (3) GROUNDED -- every step's <arg> equals a task field VALUE present in the spec 8// (each plan arg appears in the task spec as a "task|<field>|<arg>" 9// value-tail line -> no step references a field the spec didn't declare) 10// (4) ARTIFACT-TERMINAL -- the FINAL step is write_artifact and its <arg> is the task's 11// declared artifact_path value (the plan aims at the scored deliverable) 12// (5) DETERMINISTIC -- two consecutive runs on the SAME spec emit BYTE-IDENTICAL plans 13// (6) TAMPER REJECTED -- over task_missing_artifact (no artifact_path) the organ REFUSES: 14// child exits NON-ZERO and writes NO plan (the grounding check bites, 15// negative control mirroring the nondet-stub tamper in _ale_grade_gate) 16// Evidence -> knowledge/status/ale_plan.log (ALEPLANGATE row; the queue row's ||MARK= reads it). 17// Sovereign orchestration (fork/dup3/execve/wait4). The planner's purity (no clock/rand) IS the 18// oracle: a pure grounded decomposer needs no external oracle, exactly as ALE-R1a established. 19// license_tier: ORIGINAL 20import "nx_syscalls.nx" 21 22func g_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 23func g_fp(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 } 24func g_fn(fd: i64, v: i64) -> i64 { let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m }; let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48; k = 1 }; while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }; var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }; sys_write(fd, bb, k); return 0 } 25 26// run nx_ale_plan.elf <task> <planout>; return child exit code (decoded from wait status) 27func g_run_plan(task: *u8, planout: *u8) -> i64 { 28 let pid: i64 = sys_fork() 29 if pid == 0 { 30 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4) 31 if dn >= 0 { sys_dup3(dn, 1, 0); sys_dup3(dn, 2, 0) } 32 let argv: *i64 = sys_mmap(32) as *i64 33 argv[0] = "_offc/nx_ale_plan.elf" as *u8 as i64 34 argv[1] = task as i64 35 argv[2] = planout as i64 36 argv[3] = 0 37 let envp: *i64 = sys_mmap(16) as *i64 38 envp[0] = 0 39 sys_execve("_offc/nx_ale_plan.elf" as *u8, argv, envp) 40 sys_exit(127) 41 } 42 let st: *i64 = sys_mmap(16) as *i64 43 sys_wait4(pid, st, 0) 44 return (st[0] >> 8) & 0xff 45} 46 47// read whole file into buf (cap), return byte count (0 on open-fail / empty) 48func g_read(path: *u8, buf: *u8, cap: i64) -> i64 { 49 let fd: i64 = sys_openat_rd(path) 50 if fd < 0 { return 0 } 51 var n: i64 = 0 52 var go: i64 = 1 53 while go == 1 { 54 let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - 1 - n) 55 if r <= 0 { go = 0 } else { n = n + r } 56 if n >= cap - 1 { go = 0 } 57 } 58 sys_close(fd) 59 return n 60} 61 62func g_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 63 64// does buf[pos..] match pat (len pl)? 1/0 65func g_match(buf: *u8, n: i64, pos: i64, pat: *u8, pl: i64) -> i64 { 66 if pos + pl > n { return 0 } 67 var k: i64 = 0 68 while k < pl { if buf[pos + k] != pat[k] { return 0 } k = k + 1 } 69 return 1 70} 71 72// is pos a line start? (pos==0 or previous byte is newline) 73func g_is_bol(buf: *u8, pos: i64) -> i64 { 74 if pos == 0 { return 1 } 75 if buf[pos - 1] == (10 as u8) { return 1 } 76 return 0 77} 78 79// count lines in buf that begin with prefix 80func g_count_prefix(buf: *u8, n: i64, prefix: *u8) -> i64 { 81 let pl: i64 = g_len(prefix) 82 var c: i64 = 0 83 var i: i64 = 0 84 while i < n { 85 if g_is_bol(buf, i) == 1 { 86 if g_match(buf, n, i, prefix, pl) == 1 { c = c + 1 } 87 } 88 i = i + 1 89 } 90 return c 91} 92 93// two files byte-identical? 1/0 94func g_files_eq(pa: *u8, pb: *u8) -> i64 { 95 let ba: *u8 = sys_mmap(262144) 96 let bb: *u8 = sys_mmap(262144) 97 let na: i64 = g_read(pa, ba, 262144) 98 let nb: i64 = g_read(pb, bb, 262144) 99 if na != nb { return 0 } 100 if na == 0 { return 0 } 101 var i: i64 = 0 102 while i < na { if ba[i] != bb[i] { return 0 } i = i + 1 } 103 return 1 104} 105 106// ORDERED: the i-th step line (i from 1) in the plan begins with the literal "step|<i>|". 107// Builds the expected prefix "step|<num>|" for num and confirms a line with that exact 108// prefix exists; the planner emits exactly maxn steps 1..maxn so contiguity == all present. 109func g_step_num_present(buf: *u8, n: i64, num: i64) -> i64 { 110 let pb: *u8 = sys_mmap(64) 111 pb[0] = 115 as u8 // s 112 pb[1] = 116 as u8 // t 113 pb[2] = 101 as u8 // e 114 pb[3] = 112 as u8 // p 115 pb[4] = 124 as u8 // | 116 // append decimal num 117 var m: i64 = num 118 let t: *u8 = sys_mmap(28) 119 var k: i64 = 0 120 if m == 0 { t[0] = 48; k = 1 } 121 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 122 var w: i64 = 5 123 var j: i64 = 0 124 while j < k { pb[w] = t[k - 1 - j]; w = w + 1; j = j + 1 } 125 pb[w] = 124 as u8 // | 126 w = w + 1 127 pb[w] = 0 as u8 128 let pl: i64 = w 129 var i: i64 = 0 130 while i < n { 131 if g_is_bol(buf, i) == 1 { 132 if g_match(buf, n, i, pb, pl) == 1 { return 1 } 133 } 134 i = i + 1 135 } 136 return 0 137} 138 139// GROUNDED helper: extract the <arg> (text after the 3rd '|' to end-of-line) of the plan 140// step line whose 2nd field is num, into out (cap). Returns arg length, or -1 if absent. 141// A plan line is "step|<num>|<verb>|<arg>"; we walk to the 3rd pipe then copy to newline. 142func g_step_arg(buf: *u8, n: i64, num: i64, out: *u8, cap: i64) -> i64 { 143 // locate the line start of "step|<num>|" 144 let pb: *u8 = sys_mmap(64) 145 pb[0] = 115 as u8; pb[1] = 116 as u8; pb[2] = 101 as u8; pb[3] = 112 as u8; pb[4] = 124 as u8 146 var m: i64 = num 147 let t: *u8 = sys_mmap(28) 148 var k: i64 = 0 149 if m == 0 { t[0] = 48; k = 1 } 150 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 151 var w: i64 = 5 152 var j: i64 = 0 153 while j < k { pb[w] = t[k - 1 - j]; w = w + 1; j = j + 1 } 154 pb[w] = 124 as u8; w = w + 1; pb[w] = 0 as u8 155 let pl: i64 = w 156 var start: i64 = 0 - 1 157 var i: i64 = 0 158 while i < n { 159 if g_is_bol(buf, i) == 1 { 160 if g_match(buf, n, i, pb, pl) == 1 { start = i; i = n } 161 } 162 i = i + 1 163 } 164 if start < 0 { return 0 - 1 } 165 // from start, skip 3 pipes ('|') then copy to newline 166 var p: i64 = start 167 var pipes: i64 = 0 168 var go: i64 = 1 169 while go == 1 { 170 if p >= n { go = 0 } else { 171 if buf[p] == (124 as u8) { pipes = pipes + 1; p = p + 1; if pipes == 3 { go = 0 } } else { p = p + 1 } 172 } 173 } 174 if pipes < 3 { return 0 - 1 } 175 var c: i64 = 0 176 var g2: i64 = 1 177 while g2 == 1 { 178 if p >= n { g2 = 0 } else { 179 if buf[p] == (10 as u8) { g2 = 0 } else { 180 if c < cap - 1 { out[c] = buf[p]; c = c + 1 } 181 p = p + 1 182 } 183 } 184 } 185 out[c] = 0 as u8 186 return c 187} 188 189// GROUNDED check: does the task spec contain a line "task|<anything>|<arg>" whose value-tail 190// equals arg exactly (value ends at end-of-line)? 1/0. Proves the plan arg traces to a 191// declared task field value (no hallucinated context). We scan task| lines and compare the 192// substring after the 2nd '|' to arg. 193func g_arg_in_task(tbuf: *u8, tn: i64, arg: *u8) -> i64 { 194 let al: i64 = g_len(arg) 195 var i: i64 = 0 196 while i < tn { 197 if g_is_bol(tbuf, i) == 1 { 198 if g_match(tbuf, tn, i, "task|" as *u8, 5) == 1 { 199 // skip to after the 2nd '|' (the value start) 200 var p: i64 = i 201 var pipes: i64 = 0 202 var go: i64 = 1 203 while go == 1 { 204 if p >= tn { go = 0 } else { 205 if tbuf[p] == (124 as u8) { pipes = pipes + 1; p = p + 1; if pipes == 2 { go = 0 } } else { p = p + 1 } 206 } 207 } 208 if pipes == 2 { 209 if g_match(tbuf, tn, p, arg, al) == 1 { 210 let after: i64 = p + al 211 if after >= tn { return 1 } 212 if tbuf[after] == (10 as u8) { return 1 } 213 } 214 } 215 } 216 } 217 i = i + 1 218 } 219 return 0 220} 221 222func main() -> i64 { 223 let task_good: *u8 = "knowledge/specs/ale_plan_examples/task_good.txt" as *u8 224 let task_bad: *u8 = "knowledge/specs/ale_plan_examples/task_missing_artifact.txt" as *u8 225 let p1: *u8 = "/tmp/_aleplan_p1" as *u8 226 let p2: *u8 = "/tmp/_aleplan_p2" as *u8 227 let pbad: *u8 = "/tmp/_aleplan_bad_neverwritten" as *u8 228 g_p("=== ALE-plan gate (ALE-R2a: sovereign agent-core PLAN phase) ===\n" as *u8) 229 230 // run the planner over the good task into p1, and a 2nd time into p2 (determinism) 231 let rc1: i64 = g_run_plan(task_good, p1) 232 let rc2: i64 = g_run_plan(task_good, p2) 233 234 // load the emitted plan and the task spec 235 let plan: *u8 = sys_mmap(262144) 236 let pn: i64 = g_read(p1, plan, 262144) 237 let tbuf: *u8 = sys_mmap(262144) 238 let tn: i64 = g_read(task_good, tbuf, 262144) 239 240 // (1) INGEST: child exited 0 and emitted >=3 "step|"-prefixed lines 241 let nsteps: i64 = g_count_prefix(plan, pn, "step|" as *u8) 242 var c1: i64 = 0 243 if rc1 == 0 { if pn > 0 { if nsteps >= 3 { c1 = 1 } } } 244 245 // (2) ORDERED: step numbers 1,2,3 present AND no step|4| (exactly the 3-step sequence, 246 // contiguous from 1 with no gaps -- a real ordered sequence, not a bag) 247 var c2: i64 = 0 248 if g_step_num_present(plan, pn, 1) == 1 { 249 if g_step_num_present(plan, pn, 2) == 1 { 250 if g_step_num_present(plan, pn, 3) == 1 { 251 if g_step_num_present(plan, pn, 4) == 0 { if nsteps == 3 { c2 = 1 } } 252 } 253 } 254 } 255 256 // (3) GROUNDED: each step's <arg> equals a task field VALUE present in the spec 257 let arg1: *u8 = sys_mmap(8192) 258 let arg2: *u8 = sys_mmap(8192) 259 let arg3: *u8 = sys_mmap(8192) 260 let la1: i64 = g_step_arg(plan, pn, 1, arg1, 8192) 261 let la2: i64 = g_step_arg(plan, pn, 2, arg2, 8192) 262 let la3: i64 = g_step_arg(plan, pn, 3, arg3, 8192) 263 var c3: i64 = 0 264 if la1 > 0 { if la2 > 0 { if la3 > 0 { 265 if g_arg_in_task(tbuf, tn, arg1) == 1 { 266 if g_arg_in_task(tbuf, tn, arg2) == 1 { 267 if g_arg_in_task(tbuf, tn, arg3) == 1 { c3 = 1 } 268 } 269 } 270 } } } 271 272 // (4) ARTIFACT-TERMINAL: the FINAL step (step 3, the highest) is write_artifact and its 273 // <arg> equals the task's declared artifact_path value. Confirm via: step|3|write_artifact| 274 // line present AND step-3 arg appears as the task|artifact_path| value-tail. 275 var has_wa: i64 = 0 276 var i: i64 = 0 277 while i < pn { 278 if g_is_bol(plan, i) == 1 { 279 if g_match(plan, pn, i, "step|3|write_artifact|" as *u8, 22) == 1 { has_wa = 1 } 280 } 281 i = i + 1 282 } 283 // the artifact_path value must equal step-3 arg AND be the task's declared artifact_path 284 // confirm task|artifact_path|<arg3> line exists (arg3 is the declared deliverable) 285 let pfx: *u8 = sys_mmap(64) 286 pfx[0] = 116 as u8; pfx[1] = 97 as u8; pfx[2] = 115 as u8; pfx[3] = 107 as u8; pfx[4] = 124 as u8 // task| 287 // append "artifact_path|" 288 let apw: *u8 = "artifact_path|" as *u8 289 var aw: i64 = 5 290 var z: i64 = 0 291 while apw[z] != (0 as u8) { pfx[aw] = apw[z]; aw = aw + 1; z = z + 1 } 292 pfx[aw] = 0 as u8 293 // find that prefix line and compare its value-tail to arg3 294 var matchart: i64 = 0 295 var q: i64 = 0 296 while q < tn { 297 if g_is_bol(tbuf, q) == 1 { 298 if g_match(tbuf, tn, q, pfx, aw) == 1 { 299 if g_match(tbuf, tn, q + aw, arg3, la3) == 1 { 300 let after: i64 = q + aw + la3 301 if after >= tn { matchart = 1 } else { if tbuf[after] == (10 as u8) { matchart = 1 } } 302 } 303 } 304 } 305 q = q + 1 306 } 307 var c4: i64 = 0 308 if has_wa == 1 { if matchart == 1 { c4 = 1 } } 309 310 // (5) DETERMINISTIC: p1 and p2 byte-identical 311 var c5: i64 = 0 312 if rc1 == 0 { if rc2 == 0 { if g_files_eq(p1, p2) == 1 { c5 = 1 } } } 313 314 // (6) TAMPER REJECTED: missing-artifact spec -> child non-zero AND no plan written. 315 // pbad is a scratch path the gate NEVER writes the good plan to, so an empty/absent 316 // file here means the organ refused to emit. (organ exits 1 BEFORE opening the file.) 317 let rcbad: i64 = g_run_plan(task_bad, pbad) 318 let badbuf: *u8 = sys_mmap(4096) 319 let badn: i64 = g_read(pbad, badbuf, 4096) 320 var tamper_rejected: i64 = 0 321 if rcbad != 0 { if badn == 0 { tamper_rejected = 1 } } 322 323 g_p(" rc_good=" as *u8); g_fn(1, rc1) 324 g_p(" nsteps=" as *u8); g_fn(1, nsteps) 325 g_p(" det(eq=" as *u8); g_fn(1, g_files_eq(p1, p2)); g_p(")" as *u8) 326 g_p(" rc_bad=" as *u8); g_fn(1, rcbad) 327 g_p(" bad_plan_bytes=" as *u8); g_fn(1, badn); g_p("\n" as *u8) 328 g_p(" c1_ingest=" as *u8); g_fn(1, c1) 329 g_p(" c2_ordered=" as *u8); g_fn(1, c2) 330 g_p(" c3_grounded=" as *u8); g_fn(1, c3) 331 g_p(" c4_artifact_terminal=" as *u8); g_fn(1, c4) 332 g_p(" c5_deterministic=" as *u8); g_fn(1, c5) 333 g_p(" tamper_rejected=" as *u8); g_fn(1, tamper_rejected) 334 g_p("\n" as *u8) 335 336 var allok: i64 = 1 337 if c1 == 0 { allok = 0 } 338 if c2 == 0 { allok = 0 } 339 if c3 == 0 { allok = 0 } 340 if c4 == 0 { allok = 0 } 341 if c5 == 0 { allok = 0 } 342 if tamper_rejected == 0 { allok = 0 } 343 344 let lfd: i64 = sys_openat_append("knowledge/status/ale_plan.log" as *u8, 0x1a4) 345 if allok == 1 { 346 g_p("ALEPLANGATE verdict=GREEN (ingest ordered grounded artifact-terminal deterministic; missing-field tamper REJECTED)\n" as *u8) 347 if lfd >= 0 { 348 g_fp(lfd, "ALEPLANGATE verdict=GREEN ingest=1 ordered=1 grounded=1 artifact_terminal=1 deterministic=1 tamper_rejected=1 rung=ALE-R2a epoch=" as *u8) 349 g_fn(lfd, sys_now_realtime_sec()) 350 g_fp(lfd, "\n" as *u8) 351 sys_close(lfd) 352 } 353 sys_exit(0) 354 return 0 355 } 356 g_p("ALEPLANGATE verdict=RED (a check did not fire)\n" as *u8) 357 if lfd >= 0 { 358 g_fp(lfd, "ALEPLANGATE verdict=RED c1=" as *u8); g_fn(lfd, c1) 359 g_fp(lfd, " c2=" as *u8); g_fn(lfd, c2) 360 g_fp(lfd, " c3=" as *u8); g_fn(lfd, c3) 361 g_fp(lfd, " c4=" as *u8); g_fn(lfd, c4) 362 g_fp(lfd, " c5=" as *u8); g_fn(lfd, c5) 363 g_fp(lfd, " tamper_rejected=" as *u8); g_fn(lfd, tamper_rejected) 364 g_fp(lfd, "\n" as *u8) 365 sys_close(lfd) 366 } 367 sys_exit(1) 368 return 1 369}