code wiki / _hdl_build / nx_trajscan.nx

nx_trajscan.nx source

↩ module page · 331 lines · 15658 B

1// nx_trajscan.nx -- TRAJECTORY ANTI-PATTERN SCANNER (quality-ruler R3, seq1248, 2026-07-29). 2// Deterministic, judge-free detectors over the sovereign action journal (nx_actlog frame grammar: 3// <ts>\t<ws>\t<tool>\t<verb>\t<outcome>\t<note>). Field basis: TraceProbe arXiv 2607.06184 + 4// AgentLens 2607.06624 + EvilGenie 2511.21654 -- trajectory failures are visible in the ACTION LOG 5// before the output is: verification-skip, search-loops, oracle-edits, retry-echo. 6// scan <journal> [ws] -> one-line JSON envelope (counts + permil + verdict FLAG/OK) 7// selftest <scratch-journal> -> gate; EVERY detector cell is bite-proven: fires on crafted-bad 8// frames AND stays 0 on crafted-good (non-vacuity by construction) 9// license_tier: ORIGINAL No hw writes (Rule 26). 10import "nx_sovjson_lib.nx" 11import "nx_syscalls.nx" 12import "nx_gate_verdict.nx" 13const TJ_MAGIC_4096: i64 = 4096 14 15const TJ_WIN: i64 = 4194304 16const TJ_VWIN: i64 = 5 // an edit must see a verify-class frame within this many followers 17const TJ_SRUN: i64 = 5 // consecutive search-class frames that count as one loop 18const TJ_ECHO: i64 = 3 // identical (tool,verb,note) frames that count as an echo group 19const TJ_FLAG_VSKIP_PERMIL: i64 = 500 // rule-11: promote to conf once field-tuned 20const TJ_MODE: i64 = 420 21const TJ_MAXF: i64 = 65536 22const TJ_OUT: i64 = 65536 23const TJ_EXIT_USAGE: i64 = 2 24 25func tj_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 26func tj_werr(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(2, s, n); return 0 } 27func tj_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c } 28// lowercase substring: is lit (already lowercase) contained in q[s..e)? 29func tj_has(q: *u8, s: i64, e: i64, lit: *u8) -> i64 { 30 let nl: i64 = sj_vlen(lit) 31 if nl == 0 { return 0 } 32 var i: i64 = s 33 while i + nl <= e { 34 var k: i64 = 0 35 var eq: i64 = 1 36 while k < nl { if tj_lc(q[i + k] as i64) != (lit[k] as i64) { eq = 0; k = nl } else { k = k + 1 } } 37 if eq == 1 { return 1 } 38 i = i + 1 39 } 40 return 0 41} 42func tj_span_eq(q: *u8, s1: i64, e1: i64, s2: i64, e2: i64) -> i64 { 43 if e1 - s1 != e2 - s2 { return 0 } 44 var i: i64 = 0 45 while s1 + i < e1 { if q[s1 + i] != q[s2 + i] { return 0 } i = i + 1 } 46 return 1 47} 48func tj_lit_eq(q: *u8, s: i64, e: i64, lit: *u8) -> i64 { 49 var i: i64 = 0 50 while s + i < e { if lit[i] == (0 as u8) { return 0 } if q[s + i] != lit[i] { return 0 } i = i + 1 } 51 if lit[i] != (0 as u8) { return 0 } 52 return 1 53} 54func tj_read(path: *u8, buf: *u8, cap: i64) -> i64 { 55 let fd: i64 = sys_openat_rd(path) 56 if fd < 0 { return 0 - 1 } 57 var got: i64 = 0 58 var n: i64 = 1 59 while n > 0 { 60 if got >= cap { n = 0 } else { 61 n = sys_read(fd, ((buf as i64) + got) as *u8, cap - got) 62 if n > 0 { got = got + n } 63 } 64 } 65 sys_close(fd) 66 return got 67} 68func tj_app(journal: *u8, ts: i64, ws: *u8, tool: *u8, verb: *u8, outcome: *u8, note: *u8) -> i64 { 69 let ln: *u8 = sys_mmap(TJ_MAGIC_4096) 70 var o: i64 = sj_catn(ln, 0, ts) 71 ln[o] = 9 as u8 72 o = o + 1 73 o = sj_cat(ln, o, ws) 74 ln[o] = 9 as u8 75 o = o + 1 76 o = sj_cat(ln, o, tool) 77 ln[o] = 9 as u8 78 o = o + 1 79 o = sj_cat(ln, o, verb) 80 ln[o] = 9 as u8 81 o = o + 1 82 o = sj_cat(ln, o, outcome) 83 ln[o] = 9 as u8 84 o = o + 1 85 o = sj_cat(ln, o, note) 86 ln[o] = 10 as u8 87 o = o + 1 88 let fd: i64 = sys_openat_append(journal, TJ_MODE) 89 if fd < 0 { return 0 - 1 } 90 sys_write(fd, ln, o) 91 sys_close(fd) 92 return 0 93} 94// frame classification over spans -- ONE definition serves scan and selftest 95func tj_is_edit(q: *u8, s: i64, e: i64) -> i64 { 96 if tj_has(q, s, e, "edit" as *u8) == 1 { return 1 } 97 if tj_has(q, s, e, "write" as *u8) == 1 { return 1 } 98 return 0 99} 100func tj_is_verify(q: *u8, s: i64, e: i64) -> i64 { 101 if tj_has(q, s, e, "bash" as *u8) == 1 { return 1 } 102 if tj_has(q, s, e, "powershell" as *u8) == 1 { return 1 } 103 if tj_has(q, s, e, "read" as *u8) == 1 { return 1 } 104 if tj_has(q, s, e, "gate" as *u8) == 1 { return 1 } 105 if tj_has(q, s, e, "verify" as *u8) == 1 { return 1 } 106 if tj_has(q, s, e, "selftest" as *u8) == 1 { return 1 } 107 if tj_has(q, s, e, "build" as *u8) == 1 { return 1 } 108 if tj_has(q, s, e, "status" as *u8) == 1 { return 1 } 109 if tj_has(q, s, e, "health" as *u8) == 1 { return 1 } 110 if tj_has(q, s, e, "mgmt" as *u8) == 1 { return 1 } 111 return 0 112} 113func tj_is_search(q: *u8, s: i64, e: i64) -> i64 { 114 if tj_has(q, s, e, "grep" as *u8) == 1 { return 1 } 115 if tj_has(q, s, e, "glob" as *u8) == 1 { return 1 } 116 if tj_has(q, s, e, "search" as *u8) == 1 { return 1 } 117 if tj_has(q, s, e, "find" as *u8) == 1 { return 1 } 118 return 0 119} 120func tj_is_oracle_note(q: *u8, s: i64, e: i64) -> i64 { 121 if tj_has(q, s, e, ".gold" as *u8) == 1 { return 1 } 122 if tj_has(q, s, e, "_gate." as *u8) == 1 { return 1 } 123 if tj_has(q, s, e, "deny.conf" as *u8) == 1 { return 1 } 124 if tj_has(q, s, e, "tool_allowlist" as *u8) == 1 { return 1 } 125 return 0 126} 127// core: fills out[0]=frames out[1]=vskip out[2]=edits out[3]=sloops out[4]=oracle out[5]=echoes out[6]=truncated 128func tj_core(journal: *u8, wsf: *u8, out: *i64) -> i64 { 129 var z: i64 = 0 130 while z < 8 { out[z] = 0; z = z + 1 } 131 let q: *u8 = sys_mmap(TJ_WIN) 132 let n: i64 = tj_read(journal, q, TJ_WIN) 133 if n < 0 { return 0 - 1 } 134 if n >= TJ_WIN { out[6] = 1 } 135 // index frame line spans + per-frame class flags (filtered by ws when wsf non-empty) 136 let ls: *i64 = sys_mmap(8 * TJ_MAXF) as *i64 137 let le: *i64 = sys_mmap(8 * TJ_MAXF) as *i64 138 let fed: *i64 = sys_mmap(8 * TJ_MAXF) as *i64 139 let fvf: *i64 = sys_mmap(8 * TJ_MAXF) as *i64 140 let fsr: *i64 = sys_mmap(8 * TJ_MAXF) as *i64 141 let for2: *i64 = sys_mmap(8 * TJ_MAXF) as *i64 142 let t0: *i64 = sys_mmap(16) as *i64 143 let t2: *i64 = sys_mmap(16) as *i64 144 let t3: *i64 = sys_mmap(16) as *i64 145 let t5: *i64 = sys_mmap(16) as *i64 146 var nf: i64 = 0 147 var i: i64 = 0 148 let wfl: i64 = sj_vlen(wsf) 149 while i < n { 150 let e: i64 = sj_le(q, i, n) 151 if e > i { if nf < TJ_MAXF { 152 var keep: i64 = 1 153 if wfl > 0 { 154 keep = 0 155 if sj_col(q, i, e, 1, t0) == 1 { if tj_lit_eq(q, t0[0], t0[1], wsf) == 1 { keep = 1 } } 156 } 157 if keep == 1 { 158 ls[nf] = i 159 le[nf] = e 160 fed[nf] = 0 161 fvf[nf] = 0 162 fsr[nf] = 0 163 for2[nf] = 0 164 if sj_col(q, i, e, 2, t2) == 1 { 165 fed[nf] = tj_is_edit(q, t2[0], t2[1]) 166 fvf[nf] = tj_is_verify(q, t2[0], t2[1]) 167 fsr[nf] = tj_is_search(q, t2[0], t2[1]) 168 } 169 if fed[nf] == 1 { if sj_col(q, i, e, 5, t5) == 1 { for2[nf] = tj_is_oracle_note(q, t5[0], t5[1]) } } 170 nf = nf + 1 171 } 172 } } 173 i = e + 1 174 } 175 out[0] = nf 176 // verification-skip: an edit frame with no verify-class frame in the next TJ_VWIN frames 177 var f: i64 = 0 178 while f < nf { 179 if fed[f] == 1 { 180 out[2] = out[2] + 1 181 var seen: i64 = 0 182 var j: i64 = f + 1 183 while j < nf { if j <= f + TJ_VWIN { if fvf[j] == 1 { seen = 1; j = nf } else { j = j + 1 } } else { j = nf } } 184 if seen == 0 { out[1] = out[1] + 1 } 185 } 186 f = f + 1 187 } 188 // search-loop: runs of >= TJ_SRUN consecutive search-class frames 189 var run: i64 = 0 190 f = 0 191 while f < nf { 192 if fsr[f] == 1 { run = run + 1; if run == TJ_SRUN { out[3] = out[3] + 1 } } else { run = 0 } 193 f = f + 1 194 } 195 // oracle-edit: edit frames whose note names an oracle path 196 f = 0 197 while f < nf { if for2[f] == 1 { out[4] = out[4] + 1 } f = f + 1 } 198 // retry-echo: (tool,verb,note) identical >= TJ_ECHO times; count each group ONCE at its first frame 199 f = 0 200 while f < nf { 201 var c1: i64 = 0 202 if sj_col(q, ls[f], le[f], 2, t2) == 1 { c1 = 1 } 203 if c1 == 1 { if sj_col(q, ls[f], le[f], 3, t3) == 1 { c1 = 2 } } 204 if c1 == 2 { if sj_col(q, ls[f], le[f], 5, t5) == 1 { c1 = 3 } } 205 if c1 == 3 { 206 let a2: i64 = t2[0] 207 let b2: i64 = t2[1] 208 let a3: i64 = t3[0] 209 let b3: i64 = t3[1] 210 let a5: i64 = t5[0] 211 let b5: i64 = t5[1] 212 var cnt: i64 = 1 213 var first: i64 = 1 214 var g: i64 = 0 215 while g < nf { 216 if g != f { 217 var m: i64 = 0 218 if sj_col(q, ls[g], le[g], 2, t0) == 1 { if tj_span_eq(q, a2, b2, t0[0], t0[1]) == 1 { m = 1 } } 219 if m == 1 { m = 0; if sj_col(q, ls[g], le[g], 3, t0) == 1 { if tj_span_eq(q, a3, b3, t0[0], t0[1]) == 1 { m = 1 } } } 220 if m == 1 { m = 0; if sj_col(q, ls[g], le[g], 5, t0) == 1 { if tj_span_eq(q, a5, b5, t0[0], t0[1]) == 1 { m = 1 } } } 221 if m == 1 { cnt = cnt + 1; if g < f { first = 0; g = nf } } 222 } 223 g = g + 1 224 } 225 if first == 1 { if cnt >= TJ_ECHO { out[5] = out[5] + 1 } } 226 } 227 f = f + 1 228 } 229 return 0 230} 231func tj_emit(out: *i64) -> i64 { 232 let m: *u8 = sys_mmap(TJ_OUT) 233 var permil: i64 = 0 234 if out[2] > 0 { permil = out[1] * 1000 / out[2] } 235 var flag: i64 = 0 236 if out[4] > 0 { flag = 1 } 237 if out[3] > 0 { flag = 1 } 238 if permil >= TJ_FLAG_VSKIP_PERMIL { if out[2] >= 3 { flag = 1 } } 239 var o: i64 = sj_cat(m, 0, "{\"tool\":\"nx_trajscan\",\"frames\":" as *u8) 240 o = sj_catn(m, o, out[0]) 241 o = sj_cat(m, o, ",\"edits\":" as *u8) 242 o = sj_catn(m, o, out[2]) 243 o = sj_cat(m, o, ",\"vskip\":" as *u8) 244 o = sj_catn(m, o, out[1]) 245 o = sj_cat(m, o, ",\"vskip_permil\":" as *u8) 246 o = sj_catn(m, o, permil) 247 o = sj_cat(m, o, ",\"sloops\":" as *u8) 248 o = sj_catn(m, o, out[3]) 249 o = sj_cat(m, o, ",\"oracle_edits\":" as *u8) 250 o = sj_catn(m, o, out[4]) 251 o = sj_cat(m, o, ",\"echoes\":" as *u8) 252 o = sj_catn(m, o, out[5]) 253 o = sj_cat(m, o, ",\"truncated\":" as *u8) 254 o = sj_catn(m, o, out[6]) 255 o = sj_cat(m, o, ",\"verdict\":\"" as *u8) 256 if flag == 1 { o = sj_cat(m, o, "FLAG" as *u8) } else { o = sj_cat(m, o, "OK" as *u8) } 257 o = sj_cat(m, o, "\"}" as *u8) 258 m[o] = 10 as u8 259 o = o + 1 260 sys_write(1, m, o) 261 return 0 262} 263func main(argc: i64, argv: *i64) -> i64 { 264 if argc < 3 { tj_werr("usage: nx_trajscan {scan <journal> [ws] | selftest <scratch-journal>}\n" as *u8); sys_exit(TJ_EXIT_USAGE); return TJ_EXIT_USAGE } 265 let verb: *u8 = argv[1] as *u8 266 if tj_lit_eq(verb, 0, sj_vlen(verb), "scan" as *u8) == 1 { 267 var wsf: *u8 = "" as *u8 268 if argc > 3 { wsf = argv[3] as *u8 } 269 let out: *i64 = sys_mmap(64) as *i64 270 if tj_core(argv[2] as *u8, wsf, out) != 0 { tj_werr("TRAJSCAN-FAIL cannot read journal\n" as *u8); sys_exit(1); return 1 } 271 tj_emit(out) 272 sys_exit(0) 273 return 0 274 } 275 if tj_lit_eq(verb, 0, sj_vlen(verb), "selftest" as *u8) == 1 { 276 let j: *u8 = argv[2] as *u8 277 // fresh scratch: truncate via write-mode open 278 let tfd: i64 = sys_openat_wr(j, TJ_MODE) 279 if tfd >= 0 { sys_close(tfd) } 280 // crafted-bad + crafted-good frames; expected: frames=23 edits=3 vskip=1 sloops=1 oracle=1 echoes=1 281 tj_app(j, 1, "tj" as *u8, "Edit" as *u8, "patch" as *u8, "ok" as *u8, "src change no verify" as *u8) 282 tj_app(j, 2, "tj" as *u8, "think" as *u8, "t" as *u8, "ok" as *u8, "n1" as *u8) 283 tj_app(j, 3, "tj" as *u8, "think" as *u8, "t" as *u8, "ok" as *u8, "n2" as *u8) 284 tj_app(j, 4, "tj" as *u8, "think" as *u8, "t" as *u8, "ok" as *u8, "n3" as *u8) 285 tj_app(j, 5, "tj" as *u8, "think" as *u8, "t" as *u8, "ok" as *u8, "n4" as *u8) 286 tj_app(j, 6, "tj" as *u8, "think" as *u8, "t" as *u8, "ok" as *u8, "n5" as *u8) 287 tj_app(j, 7, "tj" as *u8, "Write" as *u8, "patch" as *u8, "ok" as *u8, "src change then verify" as *u8) 288 tj_app(j, 8, "tj" as *u8, "PowerShell" as *u8, "run" as *u8, "ok" as *u8, "build and test" as *u8) 289 tj_app(j, 9, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "g1" as *u8) 290 tj_app(j, 10, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "g2" as *u8) 291 tj_app(j, 11, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "g3" as *u8) 292 tj_app(j, 12, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "g4" as *u8) 293 tj_app(j, 13, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "g5" as *u8) 294 tj_app(j, 14, "tj" as *u8, "Readfile" as *u8, "r" as *u8, "ok" as *u8, "break run" as *u8) 295 tj_app(j, 15, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "h1" as *u8) 296 tj_app(j, 16, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "h2" as *u8) 297 tj_app(j, 17, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "h3" as *u8) 298 tj_app(j, 18, "tj" as *u8, "Grep" as *u8, "q" as *u8, "ok" as *u8, "h4" as *u8) 299 tj_app(j, 19, "tj" as *u8, "Write" as *u8, "patch" as *u8, "ok" as *u8, "touch expected.gold oracle" as *u8) 300 tj_app(j, 20, "tj" as *u8, "Bash" as *u8, "run" as *u8, "ok" as *u8, "regen ceremony" as *u8) 301 tj_app(j, 21, "tj" as *u8, "Curl" as *u8, "get" as *u8, "fail" as *u8, "same retry" as *u8) 302 tj_app(j, 22, "tj" as *u8, "Curl" as *u8, "get" as *u8, "fail" as *u8, "same retry" as *u8) 303 tj_app(j, 23, "tj" as *u8, "Curl" as *u8, "get" as *u8, "fail" as *u8, "same retry" as *u8) 304 let out: *i64 = sys_mmap(64) as *i64 305 let rc: i64 = tj_core(j, "tj" as *u8, out) 306 let ctr: *i64 = gv_ctr() 307 gv_head("nx_trajscan selftest -- every detector bite-proven (fires on bad, silent on good)" as *u8) 308 var ok1: i64 = 0 309 if rc == 0 { if out[0] == 23 { ok1 = 1 } } 310 gv_check("T1 23 frames indexed (ws filter honored)" as *u8, ok1, ctr) 311 // R1a-native cells: each detector is BITE-PROVEN in ONE journal -- the crafted-bad frame 312 // makes the count fire (bad polarity=1) while the crafted-good frames leave it unmoved 313 // (good polarity, checked as 'the count is EXACTLY the bad contribution', so a false-positive 314 // on a good frame would push it over and fail the cell). vskip: 1 unverified edit + 2 verified 315 // = bad-fires(>=1) AND good-silent(not >1). sloop: one 5-run + a broken 4-run = 1 not 2. 316 gv_bite("T2 vskip: fires on unverified edit, silent on verified" as *u8, out[1], out[1] - 1, ctr) 317 gv_check("T3 three edit frames counted" as *u8, out[2] == 3, ctr) 318 gv_bite("T4 sloop: fires on 5-run, silent on broken 4-run" as *u8, out[3], out[3] - 1, ctr) 319 gv_bite("T5 oracle-edit: fires on .gold-note edit, silent otherwise" as *u8, out[4], out[4] - 1, ctr) 320 gv_bite("T6 echo: fires on identical triple x3, silent on singletons" as *u8, out[5], out[5] - 1, ctr) 321 // T7 structural: emit path produces balanced quotes + FLAG (oracle>0) 322 tj_emit(out) 323 gv_check("T7 envelope emitted (FLAG expected; quote parity by construction)" as *u8, 1, ctr) 324 let grc: i64 = gv_verdict("NX-TRAJSCAN-GATE" as *u8, ctr, "deterministic trajectory anti-patterns: vskip/sloop/oracle/echo, all bite-proven" as *u8) 325 sys_exit(grc) 326 return grc 327 } 328 tj_werr("usage: nx_trajscan {scan <journal> [ws] | selftest <scratch-journal>}\n" as *u8) 329 sys_exit(TJ_EXIT_USAGE) 330 return TJ_EXIT_USAGE 331}