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}