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}