code wiki / _hdl_build / nx_worklog_sweep_gate.nx
nx_worklog_sweep_gate.nx source
↩ module page · 160 lines · 9230 B
1import "nx_gate_gn.nx"
2// nx_worklog_sweep_gate.nx -- hermetic gate for the CRASH-HEAL sweep (wli_sweep_behind_to). Builds a
3// fake project + cursors dir with four sessions and proves the sweep re-ingests exactly the stranded
4// tail of crashed sessions and nothing else:
5// Session A -- CRASHED: cursor parked after line 2; line 3 (Write /tmp/heal_A3.md) is stranded.
6// Session B -- CLEAN: cursor == size; its action (Read /tmp/leak_B1) must NEVER be re-captured.
7// Session C -- cursor present but transcript MISSING; must skip gracefully (no crash, no count).
8// Session D -- CRASHED: cursor after line 1; line 2 (Edit /tmp/heal_D2.md) is stranded.
9// T1 sweep heals exactly 2 (A.line3 + D.line2) -- ALL crashed sessions in ONE pass.
10// T2 heals ONLY the stranded tail: heal_A3/heal_D2 present; A1/A2/D1 (pre-cursor) NOT re-ingested.
11// T3 (NEG) clean session B is skipped: leak_B1 never appears; B's cursor is untouched.
12// T4 (NEG) missing-transcript C is skipped: C's cursor stays 0, the sweep never errors.
13// T5 (NEG / idempotence) a SECOND sweep heals 0 and the log does not grow -- safe on Stop+cron+boot.
14// Sovereign: imports nx_syscalls + the ingest lib only. license_tier: ORIGINAL
15import "nx_syscalls.nx"
16import "nx_worklog_ingest_lib.nx"
17
18const SD: *u8 = "/tmp/nx_sweep_gate"
19const SPROJ: *u8 = "/tmp/nx_sweep_gate/proj"
20const SCUR: *u8 = "/tmp/nx_sweep_gate/cursors"
21const SW: *u8 = "/tmp/nx_sweep_gate/worklog.tsv"
22
23const TA: *u8 = "/tmp/nx_sweep_gate/proj/AAAAAAAA.jsonl"
24const TB: *u8 = "/tmp/nx_sweep_gate/proj/BBBBBBBB.jsonl"
25const TD: *u8 = "/tmp/nx_sweep_gate/proj/DDDDDDDD.jsonl"
26const CA: *u8 = "/tmp/nx_sweep_gate/cursors/AAAAAAAA.jsonl.cur"
27const CB: *u8 = "/tmp/nx_sweep_gate/cursors/BBBBBBBB.jsonl.cur"
28const CC: *u8 = "/tmp/nx_sweep_gate/cursors/CCCCCCCC.jsonl.cur"
29const CD: *u8 = "/tmp/nx_sweep_gate/cursors/DDDDDDDD.jsonl.cur"
30
31func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
32func gstrlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
33func gtrunc(path: *u8, content: *u8) -> i64 {
34 let fd: i64 = sys_openat_wr(path, 420)
35 if fd < 0 { return 0 - 1 }
36 let n: i64 = gstrlen(content)
37 if n > 0 { sys_write(fd, content, n) }
38 sys_close(fd)
39 return 0
40}
41func gappend(path: *u8, content: *u8) -> i64 {
42 let fd: i64 = sys_openat_append(path, 420)
43 if fd < 0 { return 0 - 1 }
44 sys_write(fd, content, gstrlen(content))
45 sys_close(fd)
46 return 0
47}
48func gread(path: *u8, buf: *u8, cap: i64) -> i64 {
49 let fd: i64 = sys_openat_rd(path)
50 if fd < 0 { return 0 - 1 }
51 var total: i64 = 0
52 var nrd: i64 = sys_read(fd, buf, cap)
53 while nrd > 0 { total = total + nrd; if total >= cap { nrd = 0 } else { nrd = sys_read(fd, ((buf as i64) + total) as *u8, cap - total) } }
54 sys_close(fd)
55 return total
56}
57func gfind(buf: *u8, n: i64, pat: *u8) -> i64 {
58 var pl: i64 = 0; while pat[pl] != (0 as u8) { pl = pl + 1 }
59 if pl == 0 { return 0 - 1 }
60 var i: i64 = 0
61 while i + pl <= n {
62 var j: i64 = 0; var ok: i64 = 1
63 while j < pl { if buf[i + j] != pat[j] { ok = 0; j = pl } else { j = j + 1 } }
64 if ok == 1 { return i }
65 i = i + 1
66 }
67 return 0 - 1
68}
69func gcount(buf: *u8, n: i64, c: i64) -> i64 { var cnt: i64 = 0; var i: i64 = 0; while i < n { if (buf[i] as i64) == c { cnt = cnt + 1 } i = i + 1 } return cnt }
70func gsize(path: *u8) -> i64 {
71 let st: *u8 = sys_mmap(256)
72 if sys_fstatat(path, st) != 0 { return 0 - 1 }
73 return (st[48] as i64) | ((st[49] as i64) << 8) | ((st[50] as i64) << 16) | ((st[51] as i64) << 24)
74}
75// truncate-to-empty WITHOUT writing -- sys_openat_wr carries O_TRUNC, so an open+close clears the file.
76// (Deliberately avoids gtrunc(path,"") -- a zero-length string literal is mis-pooled by nx_cc and
77// aliases the NEXT literal, so passing "" would write that literal's bytes. Confirmed via this gate.)
78func gclear(path: *u8) -> i64 {
79 let fd: i64 = sys_openat_wr(path, 420)
80 if fd >= 0 { sys_close(fd) }
81 return 0
82}
83
84func main(argc: i64, argv: *i64) -> i64 {
85 gp("=== nx_worklog_sweep_gate (crash-heal: re-ingest stranded tails of crashed sessions) ===\n" as *u8)
86 sys_mkdir(SD, 511)
87 sys_mkdir(SPROJ, 511)
88 sys_mkdir(SCUR, 511)
89 gclear(SW) // clean worklog every run -> deterministic
90
91 let a1: *u8 = "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"a1\",\"name\":\"Read\",\"input\":{\"file_path\":\"/tmp/A1\"}}]},\"type\":\"assistant\",\"timestamp\":\"a1\"}\n" as *u8
92 let a2: *u8 = "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"a2\",\"name\":\"Bash\",\"input\":{\"command\":\"echo A2\"}}]},\"type\":\"assistant\",\"timestamp\":\"a2\"}\n" as *u8
93 let a3: *u8 = "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"a3\",\"name\":\"Write\",\"input\":{\"file_path\":\"/tmp/heal_A3.md\"}}]},\"type\":\"assistant\",\"timestamp\":\"a3\"}\n" as *u8
94 let b1: *u8 = "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"b1\",\"name\":\"Read\",\"input\":{\"file_path\":\"/tmp/leak_B1\"}}]},\"type\":\"assistant\",\"timestamp\":\"b1\"}\n" as *u8
95 let d1: *u8 = "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"d1\",\"name\":\"Read\",\"input\":{\"file_path\":\"/tmp/D1\"}}]},\"type\":\"assistant\",\"timestamp\":\"d1\"}\n" as *u8
96 let d2: *u8 = "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"d2\",\"name\":\"Edit\",\"input\":{\"file_path\":\"/tmp/heal_D2.md\"}}]},\"type\":\"assistant\",\"timestamp\":\"d2\"}\n" as *u8
97
98 // Session A (CRASHED): write lines 1+2, park cursor at end-of-line-2, THEN append the stranded line 3.
99 gtrunc(TA, a1); gappend(TA, a2)
100 let curA: i64 = gstrlen(a1) + gstrlen(a2)
101 gappend(TA, a3)
102 wli_write_cursor(CA, curA)
103
104 // Session B (CLEAN): one line, cursor == full size -> nothing stranded.
105 gtrunc(TB, b1)
106 wli_write_cursor(CB, gstrlen(b1))
107
108 // Session C: cursor exists but NO transcript file -> missing.
109 wli_write_cursor(CC, 0)
110
111 // Session D (CRASHED): line 1 captured, line 2 stranded.
112 gtrunc(TD, d1)
113 let curD: i64 = gstrlen(d1)
114 gappend(TD, d2)
115 wli_write_cursor(CD, curD)
116
117 var pass: i64 = 0
118 var fail: i64 = 0
119 let wbuf: *u8 = sys_mmap(262144)
120
121 // T1: one sweep heals exactly the 2 stranded tails (A.line3 + D.line2).
122 let r1: i64 = wli_sweep_behind_to(SPROJ, SCUR, SW)
123 if r1 == 2 { pass = pass + 1; gp(" T1 heal-count==2 PASS\n" as *u8) } else { fail = fail + 1; gp(" T1 heal-count FAIL got=" as *u8); gn(r1); gp("\n" as *u8) }
124
125 // T2: ONLY the stranded tails were captured -- not the pre-cursor lines, not the clean session.
126 let w1: i64 = gread(SW, wbuf, 262144)
127 var t2: i64 = 1
128 if gfind(wbuf, w1, "/tmp/heal_A3.md" as *u8) < 0 { t2 = 0 } // A's stranded tail present
129 if gfind(wbuf, w1, "/tmp/heal_D2.md" as *u8) < 0 { t2 = 0 } // D's stranded tail present
130 if gfind(wbuf, w1, "/tmp/A1" as *u8) >= 0 { t2 = 0 } // A line1 was pre-cursor -> must NOT reappear
131 if gfind(wbuf, w1, "echo A2" as *u8) >= 0 { t2 = 0 } // A line2 pre-cursor -> must NOT reappear
132 if gfind(wbuf, w1, "/tmp/D1" as *u8) >= 0 { t2 = 0 } // D line1 pre-cursor -> must NOT reappear
133 if t2 == 1 { pass = pass + 1; gp(" T2 only-stranded-tail-healed PASS\n" as *u8) } else { fail = fail + 1; gp(" T2 tail-scope FAIL\n" as *u8) }
134
135 // T3 (NEG): clean session B never re-captured + its cursor untouched.
136 var t3: i64 = 1
137 if gfind(wbuf, w1, "/tmp/leak_B1" as *u8) >= 0 { t3 = 0 }
138 if wli_read_cursor(CB) != gstrlen(b1) { t3 = 0 }
139 if t3 == 1 { pass = pass + 1; gp(" T3 clean-session-skipped PASS\n" as *u8) } else { fail = fail + 1; gp(" T3 clean-session-leaked FAIL\n" as *u8) }
140
141 // T4 (NEG): missing-transcript C skipped gracefully -- cursor stays 0, no error path taken.
142 if wli_read_cursor(CC) == 0 { pass = pass + 1; gp(" T4 missing-transcript-skipped PASS\n" as *u8) } else { fail = fail + 1; gp(" T4 missing-transcript FAIL\n" as *u8) }
143
144 // T5 (NEG / idempotence): a SECOND sweep heals nothing and the log does not grow.
145 let nl1: i64 = gcount(wbuf, w1, 10)
146 let r2: i64 = wli_sweep_behind_to(SPROJ, SCUR, SW)
147 let w2: i64 = gread(SW, wbuf, 262144)
148 let nl2: i64 = gcount(wbuf, w2, 10)
149 if r2 == 0 { if nl2 == nl1 { pass = pass + 1; gp(" T5 idempotent-second-sweep(0) PASS\n" as *u8) } else { fail = fail + 1; gp(" T5 log-grew FAIL\n" as *u8) } } else { fail = fail + 1; gp(" T5 re-healed FAIL got=" as *u8); gn(r2); gp("\n" as *u8) }
150
151 // bonus rigor: A and D cursors advanced to full size (heal complete, now idempotent).
152 var t6: i64 = 1
153 if wli_read_cursor(CA) != gsize(TA) { t6 = 0 }
154 if wli_read_cursor(CD) != gsize(TD) { t6 = 0 }
155 if t6 == 1 { pass = pass + 1; gp(" T6 healed-cursors==size PASS\n" as *u8) } else { fail = fail + 1; gp(" T6 cursor-not-advanced FAIL\n" as *u8) }
156
157 gp("RESULT pass=" as *u8); gn(pass); gp(" fail=" as *u8); gn(fail)
158 if fail == 0 { gp(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 }
159 gp(" verdict=RED\n" as *u8); sys_exit(1); return 1
160}