code wiki / _hdl_build / nx_worklog_ingest_gate.nx

nx_worklog_ingest_gate.nx source

↩ module page · 125 lines · 6902 B

1import "nx_gate_gn.nx" 2// nx_worklog_ingest_gate.nx -- hermetic gate for the transcript ingestor. Builds a fixture .jsonl 3// (two real tool_use lines + a stop_reason:"tool_use" DECOY + a deliberately INCOMPLETE trailing 4// line) and proves the load-bearing properties mechanically: 5// T1 captures exactly the complete tool_use actions (granular, decoy excluded, partial line held). 6// T2 tool + target extracted correctly from input (file_path / command). 7// T3 (NEG/idempotence) a second run captures 0 -- the cursor prevents duplicate lines (this is what 8// lets PostToolUse + Stop + Cron all fire safely). THE load-bearing test. 9// T4 completing the partial line -> next run captures exactly it (incremental, line-boundary cursor). 10// T5 (NEG) the decoy ("tool_use" only as a stop_reason / bare value) is NEVER captured. 11// Sovereign: imports nx_syscalls + the ingest lib only. license_tier: ORIGINAL 12import "nx_syscalls.nx" 13import "nx_worklog_ingest_lib.nx" 14import "nx_gate_verdict.nx" 15 16const GDIR: *u8 = "/tmp/nx_wli_gate" 17const GT: *u8 = "/tmp/nx_wli_gate/transcript.jsonl" 18const GC: *u8 = "/tmp/nx_wli_gate/cursor.cur" 19const GW: *u8 = "/tmp/nx_wli_gate/worklog.tsv" 20 21func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 22func gstrlen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 23 24func gtrunc(path: *u8, content: *u8) -> i64 { 25 let fd: i64 = sys_openat_wr(path, 420) 26 if fd < 0 { return 0 - 1 } 27 let n: i64 = gstrlen(content) 28 if n > 0 { sys_write(fd, content, n) } 29 sys_close(fd) 30 return 0 31} 32func gappend(path: *u8, content: *u8) -> i64 { 33 let fd: i64 = sys_openat_append(path, 420) 34 if fd < 0 { return 0 - 1 } 35 sys_write(fd, content, gstrlen(content)) 36 sys_close(fd) 37 return 0 38} 39func gread(path: *u8, buf: *u8, cap: i64) -> i64 { 40 let fd: i64 = sys_openat_rd(path) 41 if fd < 0 { return 0 - 1 } 42 var total: i64 = 0 43 var nrd: i64 = sys_read(fd, buf, cap) 44 while nrd > 0 { total = total + nrd; if total >= cap { nrd = 0 } else { nrd = sys_read(fd, ((buf as i64) + total) as *u8, cap - total) } } 45 sys_close(fd) 46 return total 47} 48func gfind(buf: *u8, n: i64, pat: *u8) -> i64 { 49 var pl: i64 = 0; while pat[pl] != (0 as u8) { pl = pl + 1 } 50 if pl == 0 { return 0 - 1 } 51 var i: i64 = 0 52 while i + pl <= n { 53 var j: i64 = 0; var ok: i64 = 1 54 while j < pl { if buf[i + j] != pat[j] { ok = 0; j = pl } else { j = j + 1 } } 55 if ok == 1 { return i } 56 i = i + 1 57 } 58 return 0 - 1 59} 60func 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 } 61 62func main(argc: i64, argv: *i64) -> i64 { 63 gp("=== nx_worklog_ingest_gate (transcript capture + idempotent cursor) ===\n" as *u8) 64 sys_mkdir(GDIR, 511) 65 66 // clean slate: empty worklog + empty cursor (absent/empty -> offset 0). 67 gtrunc(GW, "" as *u8) 68 gtrunc(GC, "" as *u8) 69 70 // fixture: two real tool_use lines, a decoy, then an INCOMPLETE 4th line (no newline). 71 gtrunc(GT, "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_a\",\"name\":\"Read\",\"input\":{\"file_path\":\"/tmp/foo.md\"}}]},\"type\":\"assistant\",\"timestamp\":\"t1\"}\n" as *u8) 72 gappend(GT, "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_b\",\"name\":\"Bash\",\"input\":{\"command\":\"echo hi\",\"description\":\"d\"}}]},\"type\":\"assistant\",\"timestamp\":\"t2\"}\n" as *u8) 73 gappend(GT, "{\"message\":{\"role\":\"assistant\",\"stop_reason\":\"tool_use\",\"content\":[{\"type\":\"text\",\"text\":\"no tool here\"}]},\"type\":\"assistant\",\"timestamp\":\"t3\"}\n" as *u8) 74 gappend(GT, "{\"message\":{\"content\":[{\"type\":\"tool_use\",\"id\":\"toolu_d\",\"name\":\"Write\"," as *u8) 75 76 var pass: i64 = 0 77 var fail: i64 = 0 78 79 let wbuf: *u8 = sys_mmap(65536) 80 81 // T1: ingest captures exactly the 2 complete tool_use actions (decoy + partial excluded). 82 let c1: i64 = wli_ingest(GT, GC, GW) 83 if c1 == 2 { pass = pass + 1; gp(" T1 capture-count==2 PASS\n" as *u8) } else { fail = fail + 1; gp(" T1 capture-count FAIL got=" as *u8); gn(c1); gp("\n" as *u8) } 84 85 // T2: tool + target extracted from input (file_path for Read, command for Bash). 86 let w1: i64 = gread(GW, wbuf, 65536) 87 var t2: i64 = 1 88 if gfind(wbuf, w1, "Read" as *u8) < 0 { t2 = 0 } 89 if gfind(wbuf, w1, "/tmp/foo.md" as *u8) < 0 { t2 = 0 } 90 if gfind(wbuf, w1, "Bash" as *u8) < 0 { t2 = 0 } 91 if gfind(wbuf, w1, "echo hi" as *u8) < 0 { t2 = 0 } 92 if gfind(wbuf, w1, "ingest" as *u8) < 0 { t2 = 0 } 93 if t2 == 1 { pass = pass + 1; gp(" T2 tool+target-extracted PASS\n" as *u8) } else { fail = fail + 1; gp(" T2 extraction FAIL\n" as *u8) } 94 95 // T3 (NEG / idempotence): a second run captures NOTHING (cursor) and the log is unchanged. 96 let nl1: i64 = gcount(wbuf, w1, 10) 97 let c2: i64 = wli_ingest(GT, GC, GW) 98 let w2: i64 = gread(GW, wbuf, 65536) 99 let nl2: i64 = gcount(wbuf, w2, 10) 100 if c2 == 0 { if nl2 == nl1 { pass = pass + 1; gp(" T3 idempotent-second-run(0) PASS\n" as *u8) } else { fail = fail + 1; gp(" T3 log-grew FAIL\n" as *u8) } } else { fail = fail + 1; gp(" T3 re-captured FAIL got=" as *u8); gn(c2); gp("\n" as *u8) } 101 102 // T4: complete the partial line -> next run captures exactly it (incremental, line-boundary). 103 gappend(GT, "\"input\":{\"file_path\":\"/tmp/bar.md\"}}]},\"type\":\"assistant\",\"timestamp\":\"t4\"}\n" as *u8) 104 let c3: i64 = wli_ingest(GT, GC, GW) 105 let w3: i64 = gread(GW, wbuf, 65536) 106 var t4: i64 = 1 107 if c3 != 1 { t4 = 0 } 108 if gfind(wbuf, w3, "Write" as *u8) < 0 { t4 = 0 } 109 if gfind(wbuf, w3, "/tmp/bar.md" as *u8) < 0 { t4 = 0 } 110 if t4 == 1 { pass = pass + 1; gp(" T4 incremental-capture(Write) PASS\n" as *u8) } else { fail = fail + 1; gp(" T4 incremental FAIL c3=" as *u8); gn(c3); gp("\n" as *u8) } 111 112 // T5 (NEG): the decoy is never captured (anchor is "type":"tool_use", not the bare value). 113 if gfind(wbuf, w3, "no tool here" as *u8) < 0 { pass = pass + 1; gp(" T5 decoy-never-captured PASS\n" as *u8) } else { fail = fail + 1; gp(" T5 decoy-leaked FAIL\n" as *u8) } 114 115 gp("RESULT pass=" as *u8); gn(pass); gp(" fail=" as *u8); gn(fail) 116 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 117 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 118 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 119 let ctr__dry: *i64 = gv_ctr() 120 ctr__dry[0] = pass 121 ctr__dry[1] = pass + fail 122 let rc__dry: i64 = gv_verdict("WORKLOG-INGEST-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 123 sys_exit(rc__dry) 124 return rc__dry 125}