code wiki / _hdl_build / nx_editor_loop_gate.nx

nx_editor_loop_gate.nx source

↩ module page · 219 lines · 14057 B

1// nx_editor_loop_gate.nx -- the no-JS edit loop end-to-end: emit a native form (0 JS), POST an op, the engine 2// applies it, the re-render REFLECTS the change, unknown ops are rejected -- and now, the SAME posted 3// vocabulary driving the operation log so the loop has real multi-level undo and redo with still zero 4// JavaScript. 5// 6// MIGRATED OFF D001, 2026-08-25. This gate used to roll its own pass/fail counters and print its own 7// VERDICT line, which is why /api/promote refused it: nothing outside could read its outcome. It now 8// inherits nx_gate_verdict, so declared teeth and executed teeth are the same number by construction and 9// the exit code carries the verdict. L1..L5 are the five assertions it already made, on the same fixtures 10// with the same expected values -- they are the REGRESSION GUARD for the snapshot surface. 11// 12// L9 IS THE ANTI-VACUITY TOOTH. A single posted undo proves nothing about a log: the one-deep snapshot the 13// canvas already had passes that. Three posted ops followed by three posted undos is the smallest test the 14// snapshot design cannot pass, and it is driven entirely through the POSTED op strings, which is the thing 15// a browser can actually reach. 16// 17// L12 GUARDS THE OTHER DIRECTION: extracting the shared button block must not have leaked the new undo and 18// redo verbs onto the snapshot surface, which cannot honour them. 19// 20// Gate scratch lives under /tmp/<gate>/ so it can never share a fixture with a production beat. 21// 100% sovereign. expect_exit: 0 22import "nx_syscalls.nx" 23import "nx_gate_verdict.nx" 24import "nx_brand_tokens.nx" 25import "nx_editor_loop.nx" 26 27const ELG_FILE_MODE: i64 = 420 // 0644, spelled in decimal because this dialect has no octal 28const ELG_DIR_MODE: i64 = 493 // 0755 29const ELG_CAP: i64 = 8192 30 31func eq(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } 32func ne(a: i64, b: i64) -> i64 { if a != b { return 1 } return 0 } 33func g_has(s: *u8, n: i64, lit: *u8) -> i64 { if bt_find(s, n, lit, bt_len(lit)) >= 0 { return 1 } return 0 } 34func put(c: *i64, i: i64, k: i64, x: i64, y: i64, w: i64, h: i64) -> i64 { c[i*5+0]=k; c[i*5+1]=x; c[i*5+2]=y; c[i*5+3]=w; c[i*5+4]=h; return 0 } 35func pv(label: *u8, v: i64) -> i64 { gv_puts(label); gv_num(v); gv_puts("\n" as *u8); return 0 } 36 37func render_to(c: *i64, n: i64, path: *u8, out: *u8, cap: i64) -> i64 { 38 let fd: i64 = sys_openat_wr(path, ELG_FILE_MODE); el_emit_form(c, n, fd); sys_close(fd) 39 let lp: *i64 = sys_mmap(16) as *i64; let pg: *u8 = sys_read_file(path, lp); let pn: i64 = lp[0] 40 var i: i64 = 0; while i < pn { if i < cap { out[i] = pg[i] } i = i + 1 } 41 return pn 42} 43 44// the same round trip for the stack-backed surface 45func render_to_st(st: *i64, path: *u8, out: *u8, cap: i64) -> i64 { 46 let fd: i64 = sys_openat_wr(path, ELG_FILE_MODE); el_emit_form_st(st, fd); sys_close(fd) 47 let lp: *i64 = sys_mmap(16) as *i64; let pg: *u8 = sys_read_file(path, lp); let pn: i64 = lp[0] 48 var i: i64 = 0; while i < pn { if i < cap { out[i] = pg[i] } i = i + 1 } 49 return pn 50} 51 52func main() -> i64 { 53 let ctr: *i64 = gv_ctr() 54 gv_head("nx_editor_loop_gate -- NO-JS edit loop: emit form, POST op, apply, re-render, and now undo and redo" as *u8) 55 56 sys_mkdir("/tmp/nx_editor_loop_gate" as *u8, ELG_DIR_MODE) 57 58 let c: *i64 = sys_mmap(512) as *i64 59 let out: *u8 = sys_mmap(ELG_CAP) 60 61 // ================= THE ORIGINAL FIVE, unchanged. REGRESSION GUARD for the snapshot surface. ======== 62 put(c, 0, 1, 10, 0, 50, 20); put(c, 1, 3, 40, 0, 50, 20); put(c, 2, 2, 70, 0, 50, 20) 63 var pn: i64 = render_to(c, 3, "/tmp/nx_editor_loop_gate/l1.html" as *u8, out, ELG_CAP) 64 let has_form: i64 = g_has(out, pn, "<form method=\"post\"" as *u8) 65 let has_snap: i64 = g_has(out, pn, "value=\"snap\"" as *u8) 66 let has_script: i64 = g_has(out, pn, "<script" as *u8) 67 pv("L1 page bytes = " as *u8, pn); pv("L1 has form = " as *u8, has_form); pv("L1 has snap button = " as *u8, has_snap); pv("L1 has script tag = " as *u8, has_script) 68 gv_check("L1 native HTML ops form emitted, ZERO JavaScript" as *u8, 69 eq(pn > 0, 1)*has_form*has_snap*eq(has_script, 0), ctr) 70 71 let r2: i64 = el_apply(c, 3, "align-left" as *u8) 72 pv("L2 rc = " as *u8, r2); pv("L2 x0 = " as *u8, c[1]); pv("L2 x1 = " as *u8, c[6]); pv("L2 x2 = " as *u8, c[11]) 73 gv_check("L2 POST op align-left applied by the engine (x -> all 10)" as *u8, 74 eq(r2,1)*eq(c[1],10)*eq(c[6],10)*eq(c[11],10), ctr) 75 76 pn = render_to(c, 3, "/tmp/nx_editor_loop_gate/l3.html" as *u8, out, ELG_CAP) 77 let saw10: i64 = g_has(out, pn, "left:10px" as *u8) 78 let saw40: i64 = g_has(out, pn, "left:40px" as *u8) 79 pv("L3 saw left:10px = " as *u8, saw10); pv("L3 saw left:40px = " as *u8, saw40) 80 gv_check("L3 re-render reflects the edit (the loop closes, no JS)" as *u8, saw10*eq(saw40,0), ctr) 81 82 put(c, 0, 1, 37, 11, 63, 20) 83 let r4: i64 = el_apply(c, 1, "snap" as *u8) 84 pv("L4 rc = " as *u8, r4); pv("L4 x = " as *u8, c[1]); pv("L4 y = " as *u8, c[2]) 85 gv_check("L4 POST op snap applied (37,11)->(40,8)" as *u8, eq(r4,1)*eq(c[1],40)*eq(c[2],8), ctr) 86 87 put(c, 0, 1, 99, 99, 10, 10) 88 let bad_op: i64 = el_apply(c, 1, "rm -rf; drop table" as *u8) 89 let bad_kept: i64 = eq(c[1],99)*eq(c[2],99) 90 let good_op: i64 = el_apply(c, 1, "snap" as *u8) 91 pv("L5 unknown-op rc = " as *u8, bad_op); pv("L5 state kept = " as *u8, bad_kept); pv("L5 known-op rc = " as *u8, good_op) 92 gv_bite("neg-control-L5-an-unknown-posted-op-is-REJECTED-as-a-no-op-only-governed-ops-apply" as *u8, 93 eq(bad_op,0)*bad_kept, ne(good_op,1), ctr) 94 95 // ================= THE STACK-BACKED LOOP: the same posted vocabulary, on the log ================== 96 let st: *i64 = ecs_new(64, 3) 97 put(c, 0, 1, 10, 0, 50, 20); put(c, 1, 3, 40, 0, 50, 20); put(c, 2, 2, 70, 0, 50, 20) 98 let seeded: i64 = ecs_seed(st, c, 3) 99 let cv: *i64 = ecs_canvas(st) 100 let d0: i64 = ecs_digest(st) 101 pv("L6 seed rc = " as *u8, seeded); pv("L6 live x1 = " as *u8, cv[6]) 102 gv_check("L6a FIXTURE REACHED: the stack was seeded from the same canvas the snapshot surface uses" as *u8, 103 eq(seeded,ECS_OK)*eq(cv[1],10)*eq(cv[6],40)*eq(cv[11],70)*eq(es_count(st),0), ctr) 104 105 let s1: i64 = el_apply_st(st, "align-left" as *u8) 106 let d1: i64 = ecs_digest(st) 107 pv("L6 posted align-left rc = " as *u8, s1); pv("L6 x1 = " as *u8, cv[6]); pv("L6 ops recorded = " as *u8, es_count(st)) 108 gv_check("L6 a POSTED op is applied AND recorded as exactly ONE entry on the log" as *u8, 109 eq(s1,1)*eq(cv[6],10)*eq(cv[11],10)*ne(d1,d0)*eq(es_count(st),1), ctr) 110 111 let s2: i64 = el_apply_st(st, "undo" as *u8) 112 pv("L7 posted undo rc = " as *u8, s2); pv("L7 x1 = " as *u8, cv[6]) 113 gv_check("L7 undo is a POSTED op like any other and restores the exact pre-op state" as *u8, 114 eq(s2,1)*eq(ecs_digest(st),d0)*eq(cv[6],40), ctr) 115 116 let s3: i64 = el_apply_st(st, "redo" as *u8) 117 pv("L8 posted redo rc = " as *u8, s3); pv("L8 x1 = " as *u8, cv[6]) 118 gv_check("L8 redo is a POSTED op too, and returns the exact post-op state" as *u8, 119 eq(s3,1)*eq(ecs_digest(st),d1)*eq(cv[6],10), ctr) 120 121 // ---- L9 ANTI-VACUITY: three posted ops, three posted undos. A one-deep snapshot cannot pass this. ---- 122 let st3: *i64 = ecs_new(64, 3) 123 // x is 10/40/100, NOT 10/40/70. With 70 the snap lands the three at 8/40/72 -- already evenly spaced -- 124 // so the distribute that follows is a genuine no-op and b3 == b2, which made L9a correctly RED on the 125 // first run of this gate. The fixture, not the tooth, was the thing that had to change; the no-op case 126 // it exposed is now stated on its own terms in L13. 127 put(c, 0, 1, 10, 3, 50, 20); put(c, 1, 3, 40, 17, 50, 20); put(c, 2, 2, 100, 31, 50, 20) 128 ecs_seed(st3, c, 3) 129 let b0: i64 = ecs_digest(st3) 130 let p1: i64 = el_apply_st(st3, "snap" as *u8) 131 let b1: i64 = ecs_digest(st3) 132 let p2: i64 = el_apply_st(st3, "align-top" as *u8) 133 let b2: i64 = ecs_digest(st3) 134 let p3: i64 = el_apply_st(st3, "distribute" as *u8) 135 let b3: i64 = ecs_digest(st3) 136 pv("L9 posted rc sum = " as *u8, p1+p2+p3); pv("L9 ops recorded = " as *u8, es_count(st3)) 137 gv_check("L9a FIXTURE REACHED: three posted ops each genuinely moved the layout" as *u8, 138 eq(p1,1)*eq(p2,1)*eq(p3,1)*ne(b1,b0)*ne(b2,b1)*ne(b3,b2)*eq(es_count(st3),3), ctr) 139 140 let q1: i64 = el_apply_st(st3, "undo" as *u8); let a2: i64 = ecs_digest(st3) 141 let q2: i64 = el_apply_st(st3, "undo" as *u8); let a1: i64 = ecs_digest(st3) 142 let q3: i64 = el_apply_st(st3, "undo" as *u8); let a0: i64 = ecs_digest(st3) 143 pv("L9 posted undo rc sum = " as *u8, q1+q2+q3); pv("L9 head = " as *u8, es_head(st3)) 144 gv_check("L9 MULTI-LEVEL UNDO OVER THE POSTED VOCABULARY: three undos walk back through b2 and b1 to the base -- the one-deep snapshot surface cannot do this at any price" as *u8, 145 eq(q1,1)*eq(q2,1)*eq(q3,1)*eq(a2,b2)*eq(a1,b1)*eq(a0,b0)*eq(es_head(st3),0)*eq(es_count(st3),3), ctr) 146 147 // ---- L10 the new capability must not have cost the no-JS claim ---- 148 let pnst: i64 = render_to_st(st3, "/tmp/nx_editor_loop_gate/l10.html" as *u8, out, ELG_CAP) 149 let u_btn: i64 = g_has(out, pnst, "value=\"undo\"" as *u8) 150 let r_btn: i64 = g_has(out, pnst, "value=\"redo\"" as *u8) 151 let st_snap: i64 = g_has(out, pnst, "value=\"snap\"" as *u8) 152 let st_script: i64 = g_has(out, pnst, "<script" as *u8) 153 pv("L10 page bytes = " as *u8, pnst); pv("L10 undo button = " as *u8, u_btn); pv("L10 redo button = " as *u8, r_btn); pv("L10 script tag = " as *u8, st_script) 154 gv_check("L10 the stack surface offers Undo and Redo as ordinary POST buttons, keeps every original op, and STILL emits zero JavaScript" as *u8, 155 eq(pnst > 0, 1)*u_btn*r_btn*st_snap*eq(st_script,0), ctr) 156 157 // ---- L11 the loop closes in BOTH directions: a posted undo changes what is rendered ---- 158 el_apply_st(st3, "redo" as *u8) 159 let pn_re: i64 = render_to_st(st3, "/tmp/nx_editor_loop_gate/l11a.html" as *u8, out, ELG_CAP) 160 let re_snapped: i64 = g_has(out, pn_re, "left:8px" as *u8) 161 el_apply_st(st3, "undo" as *u8) 162 let pn_un: i64 = render_to_st(st3, "/tmp/nx_editor_loop_gate/l11b.html" as *u8, out, ELG_CAP) 163 let un_snapped: i64 = g_has(out, pn_un, "left:8px" as *u8) 164 let un_orig: i64 = g_has(out, pn_un, "left:10px" as *u8) 165 pv("L11 after redo, page shows the snapped x = " as *u8, re_snapped) 166 pv("L11 after undo, page shows the snapped x = " as *u8, un_snapped) 167 pv("L11 after undo, page shows the ORIGINAL x = " as *u8, un_orig) 168 gv_check("L11 the RE-RENDER reflects an undo, not just the engine state -- the loop closes backwards as well as forwards" as *u8, 169 re_snapped*eq(un_snapped,0)*un_orig, ctr) 170 171 // ---- L12 the snapshot surface must NOT have gained verbs it cannot honour ---- 172 put(c, 0, 1, 10, 0, 50, 20); put(c, 1, 3, 40, 0, 50, 20); put(c, 2, 2, 70, 0, 50, 20) 173 let pn_old: i64 = render_to(c, 3, "/tmp/nx_editor_loop_gate/l12.html" as *u8, out, ELG_CAP) 174 let old_undo: i64 = g_has(out, pn_old, "value=\"undo\"" as *u8) 175 let old_snap: i64 = g_has(out, pn_old, "value=\"snap\"" as *u8) 176 pv("L12 snapshot page has undo button = " as *u8, old_undo); pv("L12 snapshot page still has snap = " as *u8, old_snap) 177 gv_check("L12 extracting the shared button block did NOT leak undo or redo onto the snapshot surface, which cannot honour them, and left its own ops intact" as *u8, 178 eq(old_undo,0)*old_snap, ctr) 179 180 // ---- L13 a gesture that changes NOTHING still records an entry. That is deliberate, not an oversight: 181 // the log must count GESTURES, not differences, or undo starts skipping the user's clicks. This tooth 182 // exists because the L9 fixture originally hid the case -- distribute happened to be a no-op there -- 183 // and a property nobody states is a property nobody notices when it breaks. 184 let sn: *i64 = ecs_new(64, 3) 185 put(c, 0, 1, 0, 0, 10, 10); put(c, 1, 3, 50, 0, 10, 10); put(c, 2, 2, 100, 0, 10, 10) 186 ecs_seed(sn, c, 3) 187 let nd0: i64 = ecs_digest(sn) 188 let nrc: i64 = el_apply_st(sn, "distribute" as *u8) 189 let nd1: i64 = ecs_digest(sn) 190 let nundo: i64 = el_apply_st(sn, "undo" as *u8) 191 pv("L13 no-op gesture rc = " as *u8, nrc); pv("L13 digest unchanged = " as *u8, eq(nd1,nd0)); pv("L13 ops recorded = " as *u8, es_count(sn)); pv("L13 undo rc = " as *u8, nundo) 192 gv_check("L13 a posted gesture that changes NOTHING still records exactly one entry and is still undoable -- the log counts gestures, not differences" as *u8, 193 eq(nrc,1)*eq(nd1,nd0)*eq(es_count(sn),1)*eq(nundo,1)*eq(es_head(sn),0), ctr) 194 195 // ================= negative controls ================= 196 let sb: *i64 = ecs_new(64, 3) 197 ecs_seed(sb, c, 3) 198 let base_d: i64 = ecs_digest(sb) 199 let undo_at_base: i64 = el_apply_st(sb, "undo" as *u8) 200 let base_kept: i64 = eq(ecs_digest(sb), base_d)*eq(es_count(sb), 0) 201 el_apply_st(sb, "align-left" as *u8) 202 let undo_with_op: i64 = el_apply_st(sb, "undo" as *u8) 203 pv("N posted undo at base rc = " as *u8, undo_at_base); pv("N posted undo with an op rc = " as *u8, undo_with_op) 204 gv_bite("neg-control-a-posted-undo-with-nothing-to-undo-is-REFUSED-and-is-not-reported-as-applied" as *u8, 205 eq(undo_at_base,0)*base_kept, ne(undo_with_op,1), ctr) 206 207 let sk: *i64 = ecs_new(64, 3) 208 ecs_seed(sk, c, 3) 209 let kd: i64 = ecs_digest(sk) 210 let unk: i64 = el_apply_st(sk, "align-diagonal" as *u8) 211 let unk_kept: i64 = eq(ecs_digest(sk), kd)*eq(es_count(sk), 0) 212 let known: i64 = el_apply_st(sk, "align-top" as *u8) 213 pv("N posted unknown-op rc = " as *u8, unk); pv("N posted known-op rc = " as *u8, known) 214 gv_bite("neg-control-an-unknown-posted-op-records-NOTHING-on-the-log-a-rejected-op-must-not-become-an-undo-step" as *u8, 215 eq(unk,0)*unk_kept, ne(known,1), ctr) 216 217 return gv_verdict("nx_editor_loop_gate" as *u8, ctr, 218 "the no-JS edit loop, its original snapshot surface, and the same posted vocabulary driving the operation log" as *u8) 219}