code wiki / _hdl_build / nx_ws_manifest_emit_gate.nx
nx_ws_manifest_emit_gate.nx source
↩ module page · 115 lines · 7404 B
1// nx_ws_manifest_emit_gate.nx -- HERMETIC gate for the WMS durability emitter. Builds a private
2// temp dir of fake project-*.md (+ decoy non-project files), a curated stub manifest, then drives
3// wme_emit and asserts the manifest auto-index is COMPLETE and CORRECT. The load-bearing test is
4// the LOSS-DETECTOR negative control (T5/T6): after a NEW workstream appears on disk, wme_is_stale
5// MUST flag it (1), and re-emitting MUST clear it (0) -- that is "never lose a workstream" proven
6// mechanically, not asserted. Sovereign: imports nx_syscalls + the lib only. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8import "nx_ws_index_lib.nx"
9import "nx_gate_verdict.nx"
10
11const G_DIR: *u8 = "/tmp/nx_wme_gate"
12const G_MAN: *u8 = "/tmp/nx_wme_gate/manifest.md"
13
14func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
15func gn(v: i64) -> i64 {
16 let bb: *u8 = sys_mmap(28); var m: i64 = v
17 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
18 let t: *u8 = sys_mmap(28); var k: i64 = 0
19 if m == 0 { t[0] = 48 as u8; k = 1 }
20 while m > 0 { t[k] = ((48 + (m % 10)) as u8); m = m / 10; k = k + 1 }
21 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
22 sys_write(1, bb, k); return 0
23}
24func gstrlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
25
26// write `content` (NUL-term) to a path under G_DIR named `fname`.
27func gwrite(fname: *u8, content: *u8) -> i64 {
28 let path: *u8 = sys_mmap(512)
29 let dir: *u8 = G_DIR // copy const to a local: nx_cc desyncs on indexing a const *u8 directly
30 var o: i64 = 0
31 var i: i64 = 0
32 while dir[i] != (0 as u8) { path[o] = dir[i]; o = o + 1; i = i + 1 }
33 path[o] = 47 as u8; o = o + 1
34 i = 0
35 while fname[i] != (0 as u8) { path[o] = fname[i]; o = o + 1; i = i + 1 }
36 path[o] = 0 as u8
37 let fd: i64 = sys_openat_wr(path, 420)
38 if fd < 0 { return 0 - 1 }
39 sys_write(fd, content, gstrlen(content))
40 sys_close(fd)
41 return 0
42}
43
44// does the live manifest contain NUL-term `needle`?
45func gman_has(needle: *u8) -> i64 {
46 let fbuf: *u8 = sys_mmap(WME_FBUF)
47 let flen: i64 = wme_read(G_MAN, fbuf, WME_FBUF)
48 if flen <= 0 { return 0 }
49 if wme_find(fbuf, flen, needle) >= 0 { return 1 }
50 return 0
51}
52
53func main(argc: i64, argv: *i64) -> i64 {
54 gp("=== nx_ws_manifest_emit_gate (durability loss-detector) ===\n" as *u8)
55 sys_mkdir(G_DIR, 511) // 0777; ok if exists
56
57 // seed 3 workstreams + 2 decoys (must be EXCLUDED) + a curated manifest stub.
58 gwrite("project-alpha-2026-06-01.md" as *u8, "alpha body\n" as *u8)
59 gwrite("project-bravo-2026-06-02.md" as *u8, "bravo body\n" as *u8)
60 gwrite("project-charlie-2026-06-03.md" as *u8, "charlie body\n" as *u8)
61 gwrite("feedback-decoy-2026-06-01.md" as *u8, "not a workstream\n" as *u8) // wrong prefix
62 gwrite("project-notes.txt" as *u8, "wrong suffix\n" as *u8) // wrong suffix
63 gwrite("manifest.md" as *u8, "# CURATED FRONT DOOR\nCURATED-SENTINEL-XYZ keep me.\n" as *u8)
64
65 var pass: i64 = 0
66 var fail: i64 = 0
67
68 // T1: emit indexes exactly the 3 project files (decoys excluded).
69 let n1: i64 = wme_emit(G_DIR, G_MAN)
70 if n1 == 3 { pass = pass + 1; gp(" T1 emit-count==3 PASS\n" as *u8) } else { fail = fail + 1; gp(" T1 emit-count FAIL got=" as *u8); gn(n1); gp("\n" as *u8) }
71
72 // T2: every workstream present as a [[link]].
73 if gman_has("[[project-alpha-2026-06-01]]" as *u8) == 1 { if gman_has("[[project-bravo-2026-06-02]]" as *u8) == 1 { if gman_has("[[project-charlie-2026-06-03]]" as *u8) == 1 { pass = pass + 1; gp(" T2 all-three-present PASS\n" as *u8) } else { fail = fail + 1; gp(" T2 charlie MISSING FAIL\n" as *u8) } } else { fail = fail + 1; gp(" T2 bravo MISSING FAIL\n" as *u8) } } else { fail = fail + 1; gp(" T2 alpha MISSING FAIL\n" as *u8) }
74
75 // T3: decoys excluded (wrong prefix + wrong suffix never appear).
76 if gman_has("[[feedback-decoy-2026-06-01]]" as *u8) == 0 { if gman_has("[[project-notes]]" as *u8) == 0 { pass = pass + 1; gp(" T3 decoys-excluded PASS\n" as *u8) } else { fail = fail + 1; gp(" T3 project-notes leaked FAIL\n" as *u8) } } else { fail = fail + 1; gp(" T3 feedback-decoy leaked FAIL\n" as *u8) }
77
78 // T4: curated prefix preserved verbatim (the front door is never clobbered).
79 if gman_has("CURATED-SENTINEL-XYZ" as *u8) == 1 { pass = pass + 1; gp(" T4 curated-preserved PASS\n" as *u8) } else { fail = fail + 1; gp(" T4 curated-CLOBBERED FAIL\n" as *u8) }
80
81 // T5 (NEG-CONTROL / loss-detector): fresh now -> not stale.
82 if wme_is_stale(G_DIR, G_MAN) == 0 { pass = pass + 1; gp(" T5 fresh->not-stale PASS\n" as *u8) } else { fail = fail + 1; gp(" T5 fresh-but-stale FAIL\n" as *u8) }
83
84 // T6 (THE LOAD-BEARING ONE): a NEW workstream appears -> stale MUST fire (1) = loss detected.
85 gwrite("project-delta-2026-06-04.md" as *u8, "delta body\n" as *u8)
86 if wme_is_stale(G_DIR, G_MAN) == 1 { pass = pass + 1; gp(" T6 new-file->stale-detected PASS\n" as *u8) } else { fail = fail + 1; gp(" T6 loss-NOT-detected FAIL (would silently lose work)\n" as *u8) }
87
88 // T7: re-emit picks up the new one (count 4) and clears stale -> the loop self-heals.
89 let n2: i64 = wme_emit(G_DIR, G_MAN)
90 if n2 == 4 { if wme_is_stale(G_DIR, G_MAN) == 0 { pass = pass + 1; gp(" T7 re-emit-heals(4,fresh) PASS\n" as *u8) } else { fail = fail + 1; gp(" T7 still-stale-after-emit FAIL\n" as *u8) } } else { fail = fail + 1; gp(" T7 re-emit-count FAIL got=" as *u8); gn(n2); gp("\n" as *u8) }
91
92 // ---- T8: THE TOOTH THAT WOULD HAVE CAUGHT THE 2026-07-31 DATA-LOSS INCIDENT ----------------------
93 // T4 already asserted "curated content is preserved" -- but ONLY on the path where the manifest READ
94 // SUCCEEDS. The real failure was the read FAILING (wme_read returns -1 when the open fails): prefixlen
95 // stayed 0 and the emitter wrote a generated-only file over ~955 lines of hand-authored recovery notes.
96 // ★A GATE THAT ONLY EXERCISES THE HAPPY PATH OF A PROPERTY DOES NOT PROTECT THAT PROPERTY.
97 // Here we emit to a path that CANNOT be read (a directory: open-for-read fails) and require a REFUSAL,
98 // not a write. Non-vacuous by construction: before the fix this returned success having destroyed the file.
99 let unreadable: *u8 = "/tmp/wme_gate_unreadable_dir" as *u8
100 sys_mkdir(unreadable, 511)
101 let rc8: i64 = wme_emit(G_DIR, unreadable)
102 if rc8 < 0 { pass = pass + 1; gp(" T8 unreadable-manifest -> REFUSED (rc<0), nothing written PASS\n" as *u8) }
103 else { fail = fail + 1; gp(" T8 FAIL: emitter wrote despite being unable to READ the manifest -- this is the data-loss bug, got rc=" as *u8); gn(rc8); gp("\n" as *u8) }
104
105 gp("RESULT pass=" as *u8); gn(pass); gp(" fail=" as *u8); gn(fail)
106 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
107 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
108 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
109 let ctr__dry: *i64 = gv_ctr()
110 ctr__dry[0] = pass
111 ctr__dry[1] = pass + fail
112 let rc__dry: i64 = gv_verdict("WS-MANIFEST-EMIT-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
113 sys_exit(rc__dry)
114 return rc__dry
115}