code wiki / _hdl_build / nx_actlog.nx

nx_actlog.nx source

↩ module page · 1334 lines · 59646 B

1// nx_actlog.nx -- ACTION LOG + WORKFLOW SEQUENCE MINER (ws=ws-intel3, 2026-07-20). 2// F217 completion (loop design-of-record R3): every cycle action journaled (ws/tool/verb/ 3// outcome) + the nx_workflow_mine half = n-gram mining over per-workstream action sequences. 4// Recurring tool:verb bigrams/trigrams with support >= minsup are the EMPIRICAL workflow 5// candidates (executed patterns, not combinatorial proposals -- the complement of 6// nx_atlas_discover). The loop: sessions/seats/plans LOG actions -> mine surfaces patterns 7// -> proposals enter nx_pm_intake -> PM/LLM seat triages -> catalog verb. Sequences NEVER 8// cross workstream boundaries (per-ws successor chains). State derived, append-only, 9// conflict-free (O_APPEND; ws_sync lineage). 10// Frame: <ts>\t<ws>\t<tool>\t<verb>\t<outcome>\t<note> 11// SCALE LAW: windowed reads + declared envelope (window_bytes/truncated/bigrams_capped/ 12// trigrams_capped); O(n^3) pair scan honest for journal-window scale, declared. 13// license_tier: ORIGINAL No hw writes (Rule 26). 14// log <journal> <ws> <tool> <verb> <outcome> <note> -> ACTLOG OK 15// mine <journal> [minsup] -> JSON (default minsup 2) 16// freq <journal> -> JSON per-tool:verb counts, ranked 17// (the fallback-log ranking verb; F872 rung 2) 18// harden <journal> [minsup] [filer] -> every tool:verb recurring >= minsup 19// auto-files a hardening debt via the 20// ALLOWLIST-RESOLVED filer (default nx_debt; 21// count-free canonical desc => idempotent) 22// selftest <scratch-journal> -> gate T1..T9 (caller pre-cleans) 23import "nx_sovjson_lib.nx" 24import "nx_syscalls.nx" 25import "nx_gate_verdict.nx" 26import "nx_tool_run.nx" 27const AL_MAGIC_5381: i64 = 5381 28const AL_MAGIC_1100: i64 = 1100 29const AL_MAGIC_1101: i64 = 1101 30const AL_MAGIC_1102: i64 = 1102 31const AL_MAGIC_1103: i64 = 1103 32const AL_MAGIC_1104: i64 = 1104 33const AL_MAGIC_1105: i64 = 1105 34const AL_MAGIC_1106: i64 = 1106 35const AL_MAGIC_1107: i64 = 1107 36const AL_MAGIC_1108: i64 = 1108 37const AL_MAGIC_1110: i64 = 1110 38const AL_MAGIC_1111: i64 = 1111 39const AL_MAGIC_1112: i64 = 1112 40const AL_MAGIC_1113: i64 = 1113 41const AL_MAGIC_1114: i64 = 1114 42const AL_MAGIC_1115: i64 = 1115 43const AL_MAGIC_4096: i64 = 4096 44const AL_MAGIC_1500: i64 = 1500 45const AL_MAGIC_1024: i64 = 1024 46const AL_MAGIC_4090: i64 = 4090 47 48const AL_WIN: i64 = 4194304 49const AL_OUT: i64 = 262144 50const AL_SOFT: i64 = 49152 51const AL_NAME_MAX: i64 = 120 52const AL_MINSUP_DEF: i64 = 2 53const AL_BIGRAM_CAP: i64 = 30 54const AL_TRIGRAM_CAP: i64 = 15 55const AL_FREQ_CAP: i64 = 30 56const AL_FREQ_SLOTS: i64 = 256 57const AL_HARDEN_SEV: i64 = 5 58const AL_HARDEN_CAP: i64 = 10 59const AL_ALLOW_BUF: i64 = 131072 60const AL_IX_TVMAX: i64 = 4096 61const AL_IX_HSLOTS: i64 = 8193 62const AL_IX_GMAX: i64 = 16384 63const AL_IX_GHSLOTS: i64 = 32771 64const AL_IX_ARENA: i64 = 1048576 65const AL_IX_HMOD: i64 = 1000003 66const AL_IX_KEYB: i64 = 8192 67const AL_IX_RING: i64 = 9 68const AL_IX_MAXGAP: i64 = 8 69 70// al_cat/al_catn/al_vlen/al_le/al_col DELEGATE to the shared nx_sovjson_lib (sj_*) -- dup logic eaten (TS04-family) 71func al_cat(d: *u8, o: i64, s: *u8) -> i64 { return sj_cat(d, o, s) } 72func al_catn(d: *u8, o: i64, v: i64) -> i64 { return sj_catn(d, o, v) } 73func al_vlen(s: *u8) -> i64 { return sj_vlen(s) } 74func al_atoi_z(s: *u8) -> i64 { var v: i64=0; var i: i64=0; while s[i]!=(0 as u8){ let c: i64=s[i] as i64; if c>=48 { if c<=57 { v=v*10+(c-48) } } i=i+1 } return v } 75func al_read(path: *u8, buf: *u8, cap: i64) -> i64 { 76 let fd: i64 = sys_openat_rd(path) 77 if fd < 0 { return 0 } 78 var n: i64 = 0 79 var go: i64 = 1 80 while go == 1 { let r: i64 = sys_read(fd, ((buf as i64)+n) as *u8, cap-n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap { go = 0 } } 81 sys_close(fd) 82 return n 83} 84func al_append(journal: *u8, ts: i64, ws: *u8, tool: *u8, verb: *u8, outcome: *u8, note: *u8) -> i64 { 85 var t: i64 = ts 86 if t < 0 { t = sys_now_realtime_sec() } 87 let ln: *u8 = sys_mmap(AL_MAGIC_4096) 88 var o: i64 = 0 89 o = al_catn(ln, o, t) 90 ln[o]=9 as u8; o=o+1 91 o = al_cat(ln, o, ws) 92 ln[o]=9 as u8; o=o+1 93 o = al_cat(ln, o, tool) 94 ln[o]=9 as u8; o=o+1 95 o = al_cat(ln, o, verb) 96 ln[o]=9 as u8; o=o+1 97 o = al_cat(ln, o, outcome) 98 ln[o]=9 as u8; o=o+1 99 var i: i64 = 0 100 var used: i64 = 0 101 while note[i]!=(0 as u8) { 102 if used < AL_MAGIC_1500 { 103 let c: i64 = note[i] as i64 104 if c==9 { ln[o]=32 as u8; o=o+1 } else { if c==10 { ln[o]=32 as u8; o=o+1 } else { if c==13 { ln[o]=32 as u8; o=o+1 } else { ln[o]=c as u8; o=o+1 } } } 105 used = used + 1 106 } 107 i = i + 1 108 } 109 ln[o]=10 as u8; o=o+1 110 let fd: i64 = sys_openat_append(journal, 0x1a4) 111 if fd < 0 { return -1 } 112 sys_write(fd, ln, o) 113 sys_close(fd) 114 return 0 115} 116func al_le(q: *u8, i: i64, n: i64) -> i64 { return sj_le(q, i, n) } 117func al_col(q: *u8, ls: i64, le: i64, c: i64, out: *i64) -> i64 { return sj_col(q, ls, le, c, out) } 118func al_span_eq(q: *u8, s1: i64, e1: i64, s2: i64, e2: i64) -> i64 { 119 if e1-s1 != e2-s2 { return 0 } 120 var i: i64 = 0 121 while s1+i < e1 { if q[s1+i]!=q[s2+i] { return 0 } i=i+1 } 122 return 1 123} 124func al_lit_eq(q: *u8, s: i64, e: i64, lit: *u8) -> i64 { 125 var i: i64 = 0 126 while s+i < e { if lit[i]==(0 as u8) { return 0 } if q[s+i]!=lit[i] { return 0 } i=i+1 } 127 if lit[i]!=(0 as u8) { return 0 } 128 return 1 129} 130func al_frames(q: *u8, n: i64) -> i64 { 131 var c: i64 = 0 132 var i: i64 = 0 133 while i < n { let le: i64 = al_le(q,i,n); c=c+1; i = le + 1 } 134 return c 135} 136// successor of frame at ls: next frame (after this line) whose ws (col1) equals this frame's ws. 137// returns line-start or -1. 138func al_succ(q: *u8, cx: *i64, ls: i64) -> i64 { 139 let n: i64 = cx[0] 140 let w1: *i64 = sys_mmap(16) as *i64 141 let w2: *i64 = sys_mmap(16) as *i64 142 let le0: i64 = al_le(q,ls,n) 143 if al_col(q,ls,le0,1,w1)==0 { return -1 } 144 var i: i64 = le0 + 1 145 while i < n { 146 let le: i64 = al_le(q,i,n) 147 if al_col(q,i,le,1,w2)==1 { if al_span_eq(q,w2[0],w2[1],w1[0],w1[1])==1 { return i } } 148 i = le + 1 149 } 150 return -1 151} 152// tool:verb of frame at l1 equals frame at l2? 153func al_tv_eq(q: *u8, cx: *i64, l1: i64, l2: i64) -> i64 { 154 let n: i64 = cx[0] 155 let a: *i64 = sys_mmap(16) as *i64 156 let b: *i64 = sys_mmap(16) as *i64 157 let e1: i64 = al_le(q,l1,n) 158 let e2: i64 = al_le(q,l2,n) 159 if al_col(q,l1,e1,2,a)==0 { return 0 } 160 if al_col(q,l2,e2,2,b)==0 { return 0 } 161 if al_span_eq(q,a[0],a[1],b[0],b[1])==0 { return 0 } 162 if al_col(q,l1,e1,3,a)==0 { return 0 } 163 if al_col(q,l2,e2,3,b)==0 { return 0 } 164 if al_span_eq(q,a[0],a[1],b[0],b[1])==0 { return 0 } 165 return 1 166} 167// bigram at (i,j) content-equal to bigram at (i2,j2)? 168func al_bg_eq(q: *u8, cx: *i64, i: i64, j: i64, i2: i64, j2: i64) -> i64 { 169 if al_tv_eq(q,cx,i,i2)==0 { return 0 } 170 if al_tv_eq(q,cx,j,j2)==0 { return 0 } 171 return 1 172} 173// is (i,j) the FIRST occurrence of its bigram content? scan earlier frames' successor pairs. 174func al_bg_first(q: *u8, cx: *i64, i: i64, j: i64) -> i64 { 175 let n: i64 = cx[0] 176 var k: i64 = 0 177 while k < i { 178 let ke: i64 = al_le(q,k,n) 179 let m: i64 = al_succ(q,cx,k) 180 if m >= 0 { if al_bg_eq(q,cx,k,m,i,j)==1 { return 0 } } 181 k = ke + 1 182 } 183 return 1 184} 185func al_bg_support(q: *u8, cx: *i64, i: i64, j: i64) -> i64 { 186 let n: i64 = cx[0] 187 var c: i64 = 0 188 var k: i64 = 0 189 while k < n { 190 let ke: i64 = al_le(q,k,n) 191 let m: i64 = al_succ(q,cx,k) 192 if m >= 0 { if al_bg_eq(q,cx,k,m,i,j)==1 { c=c+1 } } 193 k = ke + 1 194 } 195 return c 196} 197// trigram (i,j,k2) first + support 198func al_tg_first(q: *u8, cx: *i64, i: i64, j: i64, k2: i64) -> i64 { 199 let n: i64 = cx[0] 200 var a: i64 = 0 201 while a < i { 202 let ae: i64 = al_le(q,a,n) 203 let b: i64 = al_succ(q,cx,a) 204 if b >= 0 { 205 let c: i64 = al_succ(q,cx,b) 206 if c >= 0 { if al_bg_eq(q,cx,a,b,i,j)==1 { if al_tv_eq(q,cx,c,k2)==1 { return 0 } } } 207 } 208 a = ae + 1 209 } 210 return 1 211} 212func al_tg_support(q: *u8, cx: *i64, i: i64, j: i64, k2: i64) -> i64 { 213 let n: i64 = cx[0] 214 var cnt: i64 = 0 215 var a: i64 = 0 216 while a < n { 217 let ae: i64 = al_le(q,a,n) 218 let b: i64 = al_succ(q,cx,a) 219 if b >= 0 { 220 let c: i64 = al_succ(q,cx,b) 221 if c >= 0 { if al_bg_eq(q,cx,a,b,i,j)==1 { if al_tv_eq(q,cx,c,k2)==1 { cnt=cnt+1 } } } 222 } 223 a = ae + 1 224 } 225 return cnt 226} 227func al_jesc(d: *u8, o: i64, q: *u8, s: i64, e: i64, maxb: i64) -> i64 { 228 var p: i64 = o 229 var i: i64 = s 230 var used: i64 = 0 231 while i < e { 232 if used >= maxb { i = e } else { 233 let c: i64 = q[i] as i64 234 if c == 34 { d[p]=92 as u8; p=p+1; d[p]=34 as u8; p=p+1 } else { if c == 92 { d[p]=92 as u8; p=p+1; d[p]=92 as u8; p=p+1 } else { if c < 32 { d[p]=32 as u8; p=p+1 } else { d[p]=c as u8; p=p+1 } } } 235 used = used + 2 236 i = i + 1 237 } 238 } 239 return p 240} 241func al_key(d: *u8, o: i64, name: *u8) -> i64 { 242 d[o]=34 as u8 243 var p: i64 = o + 1 244 p = al_cat(d,p,name) 245 d[p]=34 as u8 246 p = p + 1 247 d[p]=58 as u8 248 p = p + 1 249 return p 250} 251func al_qlit(d: *u8, o: i64, s: *u8) -> i64 { 252 var p: i64 = o 253 d[p]=34 as u8; p=p+1 254 p = al_cat(d,p,s) 255 d[p]=34 as u8; p=p+1 256 return p 257} 258// emit "tool:verb" of frame at l as an escaped json string 259func al_emit_tv(d: *u8, o: i64, q: *u8, cx: *i64, l: i64) -> i64 { 260 let n: i64 = cx[0] 261 let sp: *i64 = sys_mmap(16) as *i64 262 var p: i64 = o 263 let le: i64 = al_le(q,l,n) 264 d[p]=34 as u8; p=p+1 265 if al_col(q,l,le,2,sp)==1 { p = al_jesc(d,p,q,sp[0],sp[1],AL_NAME_MAX) } 266 d[p]=58 as u8; p=p+1 267 if al_col(q,l,le,3,sp)==1 { p = al_jesc(d,p,q,sp[0],sp[1],AL_NAME_MAX) } 268 d[p]=34 as u8; p=p+1 269 return p 270} 271func al_mine_json(q: *u8, cx: *i64, minsup: i64, d: *u8) -> i64 { 272 let n: i64 = cx[0] 273 var p: i64 = 0 274 d[p]=123 as u8; p=p+1 275 p = al_key(d,p,"v" as *u8) 276 p = al_catn(d,p,1) 277 d[p]=44 as u8; p=p+1 278 p = al_key(d,p,"tool" as *u8) 279 p = al_qlit(d,p,"nx_actlog" as *u8) 280 d[p]=44 as u8; p=p+1 281 p = al_key(d,p,"journal" as *u8) 282 d[p]=34 as u8; p=p+1 283 let jp: *u8 = cx[5] as *u8 284 let jl: i64 = al_vlen(jp) 285 p = al_jesc(d,p,jp,0,jl,200) 286 d[p]=34 as u8; p=p+1 287 d[p]=44 as u8; p=p+1 288 p = al_key(d,p,"journal_bytes" as *u8) 289 p = al_catn(d,p,n) 290 d[p]=44 as u8; p=p+1 291 p = al_key(d,p,"window_bytes" as *u8) 292 p = al_catn(d,p,AL_WIN) 293 d[p]=44 as u8; p=p+1 294 p = al_key(d,p,"truncated" as *u8) 295 p = al_catn(d,p,cx[4]) 296 d[p]=44 as u8; p=p+1 297 p = al_key(d,p,"minsup" as *u8) 298 p = al_catn(d,p,minsup) 299 d[p]=44 as u8; p=p+1 300 let fr: i64 = al_frames(q,n) 301 p = al_key(d,p,"frames" as *u8) 302 p = al_catn(d,p,fr) 303 d[p]=44 as u8; p=p+1 304 // bigrams 305 p = al_key(d,p,"bigrams" as *u8) 306 d[p]=91 as u8; p=p+1 307 var bcount: i64 = 0 308 var bcapped: i64 = 0 309 var bfirst: i64 = 1 310 var i: i64 = 0 311 while i < n { 312 let le: i64 = al_le(q,i,n) 313 let j: i64 = al_succ(q,cx,i) 314 if j >= 0 { if al_bg_first(q,cx,i,j)==1 { 315 let sup: i64 = al_bg_support(q,cx,i,j) 316 if sup >= minsup { 317 if bcount >= AL_BIGRAM_CAP { bcapped=1 } else { if p > AL_SOFT { bcapped=1 } else { 318 if bfirst==0 { d[p]=44 as u8; p=p+1 } 319 bfirst=0 320 d[p]=123 as u8; p=p+1 321 p = al_key(d,p,"a" as *u8) 322 p = al_emit_tv(d,p,q,cx,i) 323 d[p]=44 as u8; p=p+1 324 p = al_key(d,p,"b" as *u8) 325 p = al_emit_tv(d,p,q,cx,j) 326 d[p]=44 as u8; p=p+1 327 p = al_key(d,p,"support" as *u8) 328 p = al_catn(d,p,sup) 329 d[p]=125 as u8; p=p+1 330 bcount = bcount + 1 331 } } 332 } 333 } } 334 i = le + 1 335 } 336 d[p]=93 as u8; p=p+1 337 d[p]=44 as u8; p=p+1 338 p = al_key(d,p,"bigrams_capped" as *u8) 339 p = al_catn(d,p,bcapped) 340 d[p]=44 as u8; p=p+1 341 // trigrams 342 p = al_key(d,p,"trigrams" as *u8) 343 d[p]=91 as u8; p=p+1 344 var tcount: i64 = 0 345 var tcapped: i64 = 0 346 var tfirst: i64 = 1 347 var a: i64 = 0 348 while a < n { 349 let ae: i64 = al_le(q,a,n) 350 let b: i64 = al_succ(q,cx,a) 351 if b >= 0 { 352 let c: i64 = al_succ(q,cx,b) 353 if c >= 0 { if al_tg_first(q,cx,a,b,c)==1 { 354 let sup2: i64 = al_tg_support(q,cx,a,b,c) 355 if sup2 >= minsup { 356 if tcount >= AL_TRIGRAM_CAP { tcapped=1 } else { if p > AL_SOFT { tcapped=1 } else { 357 if tfirst==0 { d[p]=44 as u8; p=p+1 } 358 tfirst=0 359 d[p]=123 as u8; p=p+1 360 p = al_key(d,p,"a" as *u8) 361 p = al_emit_tv(d,p,q,cx,a) 362 d[p]=44 as u8; p=p+1 363 p = al_key(d,p,"b" as *u8) 364 p = al_emit_tv(d,p,q,cx,b) 365 d[p]=44 as u8; p=p+1 366 p = al_key(d,p,"c" as *u8) 367 p = al_emit_tv(d,p,q,cx,c) 368 d[p]=44 as u8; p=p+1 369 p = al_key(d,p,"support" as *u8) 370 p = al_catn(d,p,sup2) 371 d[p]=125 as u8; p=p+1 372 tcount = tcount + 1 373 } } 374 } 375 } } 376 } 377 a = ae + 1 378 } 379 d[p]=93 as u8; p=p+1 380 d[p]=44 as u8; p=p+1 381 p = al_key(d,p,"trigrams_capped" as *u8) 382 p = al_catn(d,p,tcapped) 383 d[p]=125 as u8; p=p+1 384 d[p]=10 as u8; p=p+1 385 return p 386} 387func al_find(d: *u8, dn: i64, lit: *u8) -> i64 { 388 let ll: i64 = al_vlen(lit) 389 if ll == 0 { return 0 } 390 var i: i64 = 0 391 while i + ll <= dn { 392 var k: i64 = 0 393 var ok: i64 = 1 394 while k < ll { if d[i+k]!=lit[k] { ok=0; k=ll } else { k=k+1 } } 395 if ok==1 { return 1 } 396 i = i + 1 397 } 398 return 0 399} 400 401// per-tool:verb FREQUENCY over the WHOLE journal (ws-agnostic) -- the fallback-log RANKING verb. 402// "which primary/backup engages most" is a frequency question the sequence miner structurally 403// cannot answer (a lone fallback row has no successor bigram). Ranked by bounded selection 404// (exact, no sort infra), capped + declared like every emitter (scale-law envelope). 405func al_tv_count(q: *u8, cx: *i64, l: i64) -> i64 { 406 let n: i64 = cx[0] 407 var c: i64 = 0 408 var k: i64 = 0 409 while k < n { 410 let ke: i64 = al_le(q,k,n) 411 if al_tv_eq(q,cx,l,k)==1 { c=c+1 } 412 k = ke + 1 413 } 414 return c 415} 416func al_tv_first(q: *u8, cx: *i64, l: i64) -> i64 { 417 var k: i64 = 0 418 while k < l { 419 let ke: i64 = al_le(q,k,cx[0]) 420 if al_tv_eq(q,cx,l,k)==1 { return 0 } 421 k = ke + 1 422 } 423 return 1 424} 425func al_freq_json(q: *u8, cx: *i64, d: *u8) -> i64 { 426 let n: i64 = cx[0] 427 let starts: *i64 = sys_mmap(8 * AL_FREQ_SLOTS) as *i64 428 let counts: *i64 = sys_mmap(8 * AL_FREQ_SLOTS) as *i64 429 let done: *i64 = sys_mmap(8 * AL_FREQ_SLOTS) as *i64 430 var nf: i64 = 0 431 var fcapped: i64 = 0 432 var i: i64 = 0 433 while i < n { 434 let le: i64 = al_le(q,i,n) 435 if al_tv_first(q,cx,i)==1 { 436 if nf >= AL_FREQ_SLOTS { fcapped=1 } else { 437 starts[nf]=i 438 counts[nf]=al_tv_count(q,cx,i) 439 nf=nf+1 440 } 441 } 442 i = le + 1 443 } 444 var p: i64 = 0 445 d[p]=123 as u8; p=p+1 446 p = al_key(d,p,"v" as *u8); p = al_catn(d,p,1); d[p]=44 as u8; p=p+1 447 p = al_key(d,p,"tool" as *u8); p = al_qlit(d,p,"nx_actlog" as *u8); d[p]=44 as u8; p=p+1 448 p = al_key(d,p,"averb" as *u8); p = al_qlit(d,p,"freq" as *u8); d[p]=44 as u8; p=p+1 449 p = al_key(d,p,"journal" as *u8) 450 d[p]=34 as u8; p=p+1 451 let jp: *u8 = cx[5] as *u8 452 let jl: i64 = al_vlen(jp) 453 p = al_jesc(d,p,jp,0,jl,200) 454 d[p]=34 as u8; p=p+1 455 d[p]=44 as u8; p=p+1 456 p = al_key(d,p,"journal_bytes" as *u8); p = al_catn(d,p,n); d[p]=44 as u8; p=p+1 457 p = al_key(d,p,"truncated" as *u8); p = al_catn(d,p,cx[4]); d[p]=44 as u8; p=p+1 458 p = al_key(d,p,"frames" as *u8); p = al_catn(d,p,al_frames(q,n)); d[p]=44 as u8; p=p+1 459 p = al_key(d,p,"distinct" as *u8); p = al_catn(d,p,nf); d[p]=44 as u8; p=p+1 460 p = al_key(d,p,"freq" as *u8) 461 d[p]=91 as u8; p=p+1 462 var shown: i64 = 0 463 var efirst: i64 = 1 464 var pass: i64 = 0 465 while pass < nf { 466 if shown < AL_FREQ_CAP { if p <= AL_SOFT { 467 var mi: i64 = 0 - 1 468 var mx: i64 = 0 - 1 469 var k: i64 = 0 470 while k < nf { 471 if done[k]==0 { if counts[k] > mx { mx = counts[k]; mi = k } } 472 k = k + 1 473 } 474 if mi >= 0 { 475 done[mi] = 1 476 if efirst==0 { d[p]=44 as u8; p=p+1 } 477 efirst = 0 478 d[p]=123 as u8; p=p+1 479 p = al_key(d,p,"tv" as *u8) 480 p = al_emit_tv(d,p,q,cx,starts[mi]) 481 d[p]=44 as u8; p=p+1 482 p = al_key(d,p,"count" as *u8) 483 p = al_catn(d,p,mx) 484 d[p]=125 as u8; p=p+1 485 shown = shown + 1 486 } 487 } } 488 pass = pass + 1 489 } 490 d[p]=93 as u8; p=p+1 491 d[p]=44 as u8; p=p+1 492 p = al_key(d,p,"shown" as *u8); p = al_catn(d,p,shown); d[p]=44 as u8; p=p+1 493 p = al_key(d,p,"freq_capped" as *u8); p = al_catn(d,p,fcapped) 494 d[p]=125 as u8; p=p+1 495 d[p]=10 as u8; p=p+1 496 return p 497} 498 499// harden: the F872 rung-3 CLOSER -- every tool:verb recurring >= minsup auto-files a hardening 500// debt through the ALLOWLIST-RESOLVED filer (never a hardcoded sibling path -- the execve-127 501// lesson: allowlist rows are not name-derivable). The desc is CANONICAL and COUNT-FREE so 502// nx_debt's content-idempotent add (F868) makes every re-run a NO-OP for already-filed 503// recurrences: a beat cannot spam. The filer is INJECTABLE (argecho in the gate) so the 504// selftest never writes the production debt plane. 505// DELEGATES to the shared base (rule-15 extraction 2026-07-23) -- the ORIGINAL of the 7 copies 506func al_allow_path(name: *u8, outp: *u8) -> i64 { return sj_allow_path(name, outp) } 507func al_harden_json(q: *u8, cx: *i64, minsup: i64, sev: i64, filer: *u8, d: *u8) -> i64 { 508 let n: i64 = cx[0] 509 let starts: *i64 = sys_mmap(8 * AL_FREQ_SLOTS) as *i64 510 let counts: *i64 = sys_mmap(8 * AL_FREQ_SLOTS) as *i64 511 var nf: i64 = 0 512 var i: i64 = 0 513 while i < n { 514 let le: i64 = al_le(q,i,n) 515 if al_tv_first(q,cx,i)==1 { if nf < AL_FREQ_SLOTS { 516 starts[nf]=i 517 counts[nf]=al_tv_count(q,cx,i) 518 nf=nf+1 519 } } 520 i = le + 1 521 } 522 let fpath: *u8 = sys_mmap(512) 523 let fok: i64 = al_allow_path(filer, fpath) 524 var p: i64 = 0 525 d[p]=123 as u8; p=p+1 526 p = al_key(d,p,"v" as *u8); p = al_catn(d,p,1); d[p]=44 as u8; p=p+1 527 p = al_key(d,p,"tool" as *u8); p = al_qlit(d,p,"nx_actlog" as *u8); d[p]=44 as u8; p=p+1 528 p = al_key(d,p,"averb" as *u8); p = al_qlit(d,p,"harden" as *u8); d[p]=44 as u8; p=p+1 529 p = al_key(d,p,"minsup" as *u8); p = al_catn(d,p,minsup); d[p]=44 as u8; p=p+1 530 p = al_key(d,p,"sev" as *u8); p = al_catn(d,p,sev); d[p]=44 as u8; p=p+1 531 p = al_key(d,p,"filer" as *u8) 532 d[p]=34 as u8; p=p+1 533 p = al_jesc(d,p,filer,0,al_vlen(filer),120) 534 d[p]=34 as u8; p=p+1 535 d[p]=44 as u8; p=p+1 536 p = al_key(d,p,"filer_resolved" as *u8); p = al_catn(d,p,fok); d[p]=44 as u8; p=p+1 537 p = al_key(d,p,"filed" as *u8) 538 d[p]=91 as u8; p=p+1 539 var recur: i64 = 0 540 var attempted: i64 = 0 541 var hcapped: i64 = 0 542 var efirst: i64 = 1 543 var k: i64 = 0 544 while k < nf { 545 if counts[k] >= minsup { 546 recur = recur + 1 547 if attempted >= AL_HARDEN_CAP { hcapped=1 } else { if fok==1 { 548 let tvb: *u8 = sys_mmap(256) 549 let sp2: *i64 = sys_mmap(16) as *i64 550 let le2: i64 = al_le(q,starts[k],n) 551 var to: i64 = 0 552 if al_col(q,starts[k],le2,2,sp2)==1 { var z: i64=sp2[0]; while z<sp2[1] { if to<100 { tvb[to]=q[z]; to=to+1 } z=z+1 } } 553 tvb[to]=58 as u8 554 to=to+1 555 if al_col(q,starts[k],le2,3,sp2)==1 { var z2: i64=sp2[0]; while z2<sp2[1] { if to<220 { tvb[to]=q[z2]; to=to+1 } z2=z2+1 } } 556 tvb[to]=0 as u8 557 let desc: *u8 = sys_mmap(AL_MAGIC_1024) 558 var dso: i64 = 0 559 dso = al_cat(desc,dso,"FALLBACK-RECURRENCE " as *u8) 560 dso = al_cat(desc,dso,tvb) 561 dso = al_cat(desc,dso," (auto-filed by nx_actlog harden): this backup method engages repeatedly -- root-cause and HARDEN THE PRIMARY; current counts via nx_actlog freq, per-engagement why/evidence in the journal rows" as *u8) 562 desc[dso]=0 as u8 563 let sevs: *u8 = sys_mmap(8) 564 var so: i64 = al_catn(sevs,0,sev) 565 sevs[so]=0 as u8 566 let av: *i64 = sys_mmap(64) as *i64 567 av[0]=fpath as i64 568 av[1]="add" as *u8 as i64 569 av[2]=sevs as i64 570 av[3]="fallback" as *u8 as i64 571 av[4]=desc as i64 572 av[5]=0 573 let cout: *u8 = sys_mmap(AL_MAGIC_4096) 574 let colen: *i64 = sys_mmap(8) as *i64 575 let ex: i64 = tr_run_capture(fpath, av, cout, AL_MAGIC_4090, colen) 576 if efirst==0 { d[p]=44 as u8; p=p+1 } 577 efirst=0 578 d[p]=123 as u8; p=p+1 579 p = al_key(d,p,"tv" as *u8) 580 d[p]=34 as u8; p=p+1 581 p = al_jesc(d,p,tvb,0,al_vlen(tvb),240) 582 d[p]=34 as u8; p=p+1 583 d[p]=44 as u8; p=p+1 584 p = al_key(d,p,"exit" as *u8); p = al_catn(d,p,ex); d[p]=44 as u8; p=p+1 585 p = al_key(d,p,"out" as *u8) 586 d[p]=34 as u8; p=p+1 587 var ce: i64 = colen[0] 588 if ce > 400 { ce = 400 } 589 p = al_jesc(d,p,cout,0,ce,800) 590 d[p]=34 as u8; p=p+1 591 d[p]=125 as u8; p=p+1 592 attempted = attempted + 1 593 } } 594 } 595 k = k + 1 596 } 597 d[p]=93 as u8; p=p+1 598 d[p]=44 as u8; p=p+1 599 p = al_key(d,p,"recurring" as *u8); p = al_catn(d,p,recur); d[p]=44 as u8; p=p+1 600 p = al_key(d,p,"attempted" as *u8); p = al_catn(d,p,attempted); d[p]=44 as u8; p=p+1 601 p = al_key(d,p,"harden_capped" as *u8); p = al_catn(d,p,hcapped) 602 d[p]=125 as u8; p=p+1 603 d[p]=10 as u8; p=p+1 604 return p 605} 606 607// ---------- seq626 O(F) INDEXED MINER ---------- 608// One hash-interned pass replaces the O(F^2/F^3) pair scans that died (rc=-1) on the first 609// real-scale journal (8184 frames, F964 backfill). The slow al_succ/al_bg_*/al_tg_*/al_tv_* 610// primitives above are KEPT: the selftest computes supports through them (T4/T5) while the 611// fast emitters produce the JSON (T6/T7/T8) -- fast path must agree with the slow oracle on 612// the fixture. Emission order preserved: first-occurrence, count-desc selection for freq. 613func al_z_eq(a: *u8, b: *u8) -> i64 { 614 var i: i64 = 0 615 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 616 if b[i] != (0 as u8) { return 0 } 617 return 1 618} 619func al_z_hash(s: *u8) -> i64 { 620 var h: i64 = AL_MAGIC_5381 621 var i: i64 = 0 622 while s[i] != (0 as u8) { h = (h * 31 + (s[i] as i64)) % AL_IX_HMOD; i = i + 1 } 623 return h 624} 625// string intern: open-addressing hash holds id+1; ids are first-seen order; strings in arena. 626func al_ix_sintern(z: *u8, hash: *i64, off: *i64, hslots: i64, maxn: i64, ar: *u8, an: *i64, nn: *i64) -> i64 { 627 var h: i64 = al_z_hash(z) % hslots 628 var probes: i64 = 0 629 while probes < hslots { 630 let v: i64 = hash[h] 631 if v == 0 { 632 if nn[0] >= maxn { return -1 } 633 let zl: i64 = al_vlen(z) 634 if an[0] + zl + 1 >= AL_IX_ARENA { return -1 } 635 let id: i64 = nn[0] 636 off[id] = an[0] 637 var k: i64 = 0 638 while k <= zl { ar[an[0] + k] = z[k]; k = k + 1 } 639 an[0] = an[0] + zl + 1 640 hash[h] = id + 1 641 nn[0] = id + 1 642 return id 643 } 644 if al_z_eq(((ar as i64) + off[v-1]) as *u8, z) == 1 { return v - 1 } 645 h = h + 1 646 if h >= hslots { h = 0 } 647 probes = probes + 1 648 } 649 return -1 650} 651// numeric-key intern (bigram/trigram packed keys) 652func al_ix_kintern(key: i64, hash: *i64, keys: *i64, hslots: i64, maxn: i64, nn: *i64) -> i64 { 653 var h: i64 = key % hslots 654 if h < 0 { h = 0 - h } 655 var probes: i64 = 0 656 while probes < hslots { 657 let v: i64 = hash[h] 658 if v == 0 { 659 if nn[0] >= maxn { return -1 } 660 let id: i64 = nn[0] 661 keys[id] = key 662 hash[h] = id + 1 663 nn[0] = id + 1 664 return id 665 } 666 if keys[v-1] == key { return v - 1 } 667 h = h + 1 668 if h >= hslots { h = 0 } 669 probes = probes + 1 670 } 671 return -1 672} 673// ix ptr slots: 0 tvhash 1 tvoff 2 tvcnt 3 ntv 4 arena 5 aused 6 wshash 7 wsoff 8 wslast 9 wslast2 674// 10 nws 11 bghash 12 bgkey 13 bgcnt 14 nbg 15 tghash 16 tgkey 17 tgcnt 18 ntg 19 frames 20 overflow 675func al_ix_build(q: *u8, cx: *i64, ix: *i64, maxgap: i64) -> i64 { 676 ix[0] = sys_mmap(8 * AL_IX_HSLOTS) as i64 677 ix[1] = sys_mmap(8 * AL_IX_TVMAX) as i64 678 ix[2] = sys_mmap(8 * AL_IX_TVMAX) as i64 679 ix[3] = sys_mmap(16) as i64 680 ix[4] = sys_mmap(AL_IX_ARENA) as i64 681 ix[5] = sys_mmap(16) as i64 682 ix[6] = sys_mmap(8 * AL_IX_HSLOTS) as i64 683 ix[7] = sys_mmap(8 * AL_IX_TVMAX) as i64 684 ix[8] = sys_mmap(8 * AL_IX_TVMAX * AL_IX_RING) as i64 685 ix[9] = sys_mmap(8 * AL_IX_TVMAX) as i64 686 ix[10] = sys_mmap(16) as i64 687 ix[11] = sys_mmap(8 * AL_IX_GHSLOTS) as i64 688 ix[12] = sys_mmap(8 * AL_IX_GMAX) as i64 689 ix[13] = sys_mmap(8 * AL_IX_GMAX) as i64 690 ix[14] = sys_mmap(16) as i64 691 ix[15] = sys_mmap(8 * AL_IX_GHSLOTS) as i64 692 ix[16] = sys_mmap(8 * AL_IX_GMAX) as i64 693 ix[17] = sys_mmap(8 * AL_IX_GMAX) as i64 694 ix[18] = sys_mmap(16) as i64 695 ix[19] = sys_mmap(16) as i64 696 ix[20] = sys_mmap(16) as i64 697 let n: i64 = cx[0] 698 let sp: *i64 = sys_mmap(16) as *i64 699 let tvb: *u8 = sys_mmap(512) 700 let wsb: *u8 = sys_mmap(256) 701 let fp: *i64 = ix[19] as *i64 702 let ov: *i64 = ix[20] as *i64 703 var i: i64 = 0 704 while i < n { 705 let le: i64 = al_le(q,i,n) 706 fp[0] = fp[0] + 1 707 var ok: i64 = 0 708 var wl: i64 = 0 709 var to: i64 = 0 710 if al_col(q,i,le,1,sp)==1 { 711 var z: i64 = sp[0] 712 while z < sp[1] { if wl < 200 { wsb[wl] = q[z]; wl = wl + 1 } z = z + 1 } 713 wsb[wl] = 0 as u8 714 if al_col(q,i,le,2,sp)==1 { 715 var z2: i64 = sp[0] 716 while z2 < sp[1] { if to < 240 { tvb[to] = q[z2]; to = to + 1 } z2 = z2 + 1 } 717 tvb[to] = 58 as u8 718 to = to + 1 719 if al_col(q,i,le,3,sp)==1 { 720 var z3: i64 = sp[0] 721 while z3 < sp[1] { if to < 480 { tvb[to] = q[z3]; to = to + 1 } z3 = z3 + 1 } 722 tvb[to] = 0 as u8 723 ok = 1 724 } 725 } 726 } 727 if ok == 1 { 728 let tid: i64 = al_ix_sintern(tvb, ix[0] as *i64, ix[1] as *i64, AL_IX_HSLOTS, AL_IX_TVMAX, ix[4] as *u8, ix[5] as *i64, ix[3] as *i64) 729 let wid: i64 = al_ix_sintern(wsb, ix[6] as *i64, ix[7] as *i64, AL_IX_HSLOTS, AL_IX_TVMAX, ix[4] as *u8, ix[5] as *i64, ix[10] as *i64) 730 if tid >= 0 { if wid >= 0 { 731 let tvcnt: *i64 = ix[2] as *i64 732 tvcnt[tid] = tvcnt[tid] + 1 733 let ring: *i64 = ix[8] as *i64 734 let wln: *i64 = ix[9] as *i64 735 let cnt: i64 = wln[wid] 736 var dd: i64 = 1 737 while dd <= maxgap + 1 { 738 if dd <= cnt { 739 let pv: i64 = ring[wid * AL_IX_RING + ((cnt - dd) % AL_IX_RING)] 740 if pv > 0 { 741 let bkey: i64 = (pv - 1) * AL_IX_KEYB + tid 742 let bid: i64 = al_ix_kintern(bkey, ix[11] as *i64, ix[12] as *i64, AL_IX_GHSLOTS, AL_IX_GMAX, ix[14] as *i64) 743 if bid >= 0 { let bc: *i64 = ix[13] as *i64; bc[bid] = bc[bid] + 1 } else { ov[0] = 1 } 744 if dd == 1 { if cnt >= 2 { 745 let p2v: i64 = ring[wid * AL_IX_RING + ((cnt - 2) % AL_IX_RING)] 746 if p2v > 0 { 747 let tkey: i64 = ((p2v - 1) * AL_IX_KEYB + (pv - 1)) * AL_IX_KEYB + tid 748 let tgid: i64 = al_ix_kintern(tkey, ix[15] as *i64, ix[16] as *i64, AL_IX_GHSLOTS, AL_IX_GMAX, ix[18] as *i64) 749 if tgid >= 0 { let tc: *i64 = ix[17] as *i64; tc[tgid] = tc[tgid] + 1 } else { ov[0] = 1 } 750 } 751 } } 752 } 753 } 754 dd = dd + 1 755 } 756 ring[wid * AL_IX_RING + (cnt % AL_IX_RING)] = tid + 1 757 wln[wid] = cnt + 1 758 } else { ov[0] = 1 } } else { ov[0] = 1 } 759 } 760 i = le + 1 761 } 762 return 0 763} 764func al_ix_tv_emit(d: *u8, o: i64, ar: *u8, off: i64) -> i64 { 765 var p: i64 = o 766 d[p]=34 as u8; p=p+1 767 let zl: i64 = al_vlen(((ar as i64) + off) as *u8) 768 p = al_jesc(d,p,ar,off,off+zl,240) 769 d[p]=34 as u8; p=p+1 770 return p 771} 772func al_freq_fast(q: *u8, cx: *i64, d: *u8) -> i64 { 773 let n: i64 = cx[0] 774 let ix: *i64 = sys_mmap(256) as *i64 775 al_ix_build(q, cx, ix, 0) 776 let ntvp: *i64 = ix[3] as *i64 777 let nf: i64 = ntvp[0] 778 let cnts: *i64 = ix[2] as *i64 779 let offs: *i64 = ix[1] as *i64 780 let ar: *u8 = ix[4] as *u8 781 let fp: *i64 = ix[19] as *i64 782 let ov: *i64 = ix[20] as *i64 783 let done: *i64 = sys_mmap(8 * AL_IX_TVMAX) as *i64 784 var p: i64 = 0 785 d[p]=123 as u8; p=p+1 786 p = al_key(d,p,"v" as *u8); p = al_catn(d,p,1); d[p]=44 as u8; p=p+1 787 p = al_key(d,p,"tool" as *u8); p = al_qlit(d,p,"nx_actlog" as *u8); d[p]=44 as u8; p=p+1 788 p = al_key(d,p,"averb" as *u8); p = al_qlit(d,p,"freq" as *u8); d[p]=44 as u8; p=p+1 789 p = al_key(d,p,"journal" as *u8) 790 d[p]=34 as u8; p=p+1 791 let jp: *u8 = cx[5] as *u8 792 p = al_jesc(d,p,jp,0,al_vlen(jp),200) 793 d[p]=34 as u8; p=p+1 794 d[p]=44 as u8; p=p+1 795 p = al_key(d,p,"journal_bytes" as *u8); p = al_catn(d,p,n); d[p]=44 as u8; p=p+1 796 p = al_key(d,p,"truncated" as *u8); p = al_catn(d,p,cx[4]); d[p]=44 as u8; p=p+1 797 p = al_key(d,p,"frames" as *u8); p = al_catn(d,p,fp[0]); d[p]=44 as u8; p=p+1 798 p = al_key(d,p,"distinct" as *u8); p = al_catn(d,p,nf); d[p]=44 as u8; p=p+1 799 p = al_key(d,p,"freq" as *u8) 800 d[p]=91 as u8; p=p+1 801 var shown: i64 = 0 802 var efirst: i64 = 1 803 var pass: i64 = 0 804 while pass < nf { 805 if shown < AL_FREQ_CAP { if p <= AL_SOFT { 806 var mi: i64 = 0 - 1 807 var mx: i64 = 0 - 1 808 var k: i64 = 0 809 while k < nf { 810 if done[k]==0 { if cnts[k] > mx { mx = cnts[k]; mi = k } } 811 k = k + 1 812 } 813 if mi >= 0 { 814 done[mi] = 1 815 if efirst==0 { d[p]=44 as u8; p=p+1 } 816 efirst = 0 817 d[p]=123 as u8; p=p+1 818 p = al_key(d,p,"tv" as *u8) 819 p = al_ix_tv_emit(d,p,ar,offs[mi]) 820 d[p]=44 as u8; p=p+1 821 p = al_key(d,p,"count" as *u8) 822 p = al_catn(d,p,mx) 823 d[p]=125 as u8; p=p+1 824 shown = shown + 1 825 } 826 } } 827 pass = pass + 1 828 } 829 d[p]=93 as u8; p=p+1 830 d[p]=44 as u8; p=p+1 831 p = al_key(d,p,"shown" as *u8); p = al_catn(d,p,shown); d[p]=44 as u8; p=p+1 832 p = al_key(d,p,"index_overflow" as *u8); p = al_catn(d,p,ov[0]); d[p]=44 as u8; p=p+1 833 p = al_key(d,p,"freq_capped" as *u8); p = al_catn(d,p,ov[0]) 834 d[p]=125 as u8; p=p+1 835 d[p]=10 as u8; p=p+1 836 return p 837} 838// al_bg_is_closed: CLOSED-PATTERN test (CloSpan/BIDE class, the research standard for NON-REDUNDANT 839// sequential mining). A bigram (a,b) is NON-closed (redundant) if some trigram containing it 840// CONTIGUOUSLY -- as prefix (a,b,*) or suffix (*,a,b) -- has the SAME support: its count is fully 841// explained by a longer pattern, so reporting it adds noise. Trigrams are maximal here (no 4-grams) 842// => emitted as closed-up-to-length-3 (declared). Returns 1 closed, 0 subsumed. 843func al_bg_is_closed(a: i64, b: i64, sup: i64, tgkey: *i64, tgcnt: *i64, ntg: i64) -> i64 { 844 var i: i64 = 0 845 while i < ntg { 846 if tgcnt[i] == sup { 847 let ta: i64 = tgkey[i] / (AL_IX_KEYB * AL_IX_KEYB) 848 let tb: i64 = (tgkey[i] / AL_IX_KEYB) % AL_IX_KEYB 849 let tc: i64 = tgkey[i] % AL_IX_KEYB 850 if ta == a { if tb == b { return 0 } } 851 if tb == a { if tc == b { return 0 } } 852 } 853 i = i + 1 854 } 855 return 1 856} 857func al_mine_fast(q: *u8, cx: *i64, minsup: i64, maxgap: i64, closed: i64, d: *u8) -> i64 { 858 let n: i64 = cx[0] 859 let ix: *i64 = sys_mmap(256) as *i64 860 al_ix_build(q, cx, ix, maxgap) 861 let offs: *i64 = ix[1] as *i64 862 let ar: *u8 = ix[4] as *u8 863 let fp: *i64 = ix[19] as *i64 864 let ov: *i64 = ix[20] as *i64 865 let nbgp: *i64 = ix[14] as *i64 866 let bgkey: *i64 = ix[12] as *i64 867 let bgcnt: *i64 = ix[13] as *i64 868 let ntgp: *i64 = ix[18] as *i64 869 let tgkey: *i64 = ix[16] as *i64 870 let tgcnt: *i64 = ix[17] as *i64 871 let tvcnt: *i64 = ix[2] as *i64 872 var p: i64 = 0 873 d[p]=123 as u8; p=p+1 874 p = al_key(d,p,"v" as *u8) 875 p = al_catn(d,p,1) 876 d[p]=44 as u8; p=p+1 877 p = al_key(d,p,"tool" as *u8) 878 p = al_qlit(d,p,"nx_actlog" as *u8) 879 d[p]=44 as u8; p=p+1 880 p = al_key(d,p,"journal" as *u8) 881 d[p]=34 as u8; p=p+1 882 let jp: *u8 = cx[5] as *u8 883 p = al_jesc(d,p,jp,0,al_vlen(jp),200) 884 d[p]=34 as u8; p=p+1 885 d[p]=44 as u8; p=p+1 886 p = al_key(d,p,"journal_bytes" as *u8) 887 p = al_catn(d,p,n) 888 d[p]=44 as u8; p=p+1 889 p = al_key(d,p,"window_bytes" as *u8) 890 p = al_catn(d,p,AL_WIN) 891 d[p]=44 as u8; p=p+1 892 p = al_key(d,p,"truncated" as *u8) 893 p = al_catn(d,p,cx[4]) 894 d[p]=44 as u8; p=p+1 895 p = al_key(d,p,"minsup" as *u8) 896 p = al_catn(d,p,minsup) 897 d[p]=44 as u8; p=p+1 898 p = al_key(d,p,"maxgap" as *u8) 899 p = al_catn(d,p,maxgap) 900 d[p]=44 as u8; p=p+1 901 p = al_key(d,p,"closed" as *u8) 902 p = al_catn(d,p,closed) 903 d[p]=44 as u8; p=p+1 904 p = al_key(d,p,"frames" as *u8) 905 p = al_catn(d,p,fp[0]) 906 d[p]=44 as u8; p=p+1 907 p = al_key(d,p,"bigrams" as *u8) 908 d[p]=91 as u8; p=p+1 909 var bcount: i64 = 0 910 var bcapped: i64 = 0 911 var bfirst: i64 = 1 912 var bi: i64 = 0 913 while bi < nbgp[0] { 914 var bg_ok: i64 = 0 915 if bgcnt[bi] >= minsup { bg_ok = 1 } 916 if bg_ok == 1 { if closed == 1 { if al_bg_is_closed(bgkey[bi] / AL_IX_KEYB, bgkey[bi] % AL_IX_KEYB, bgcnt[bi], tgkey, tgcnt, ntgp[0]) == 0 { bg_ok = 0 } } } 917 if bg_ok == 1 { 918 if bcount >= AL_BIGRAM_CAP { bcapped=1 } else { if p > AL_SOFT { bcapped=1 } else { 919 if bfirst==0 { d[p]=44 as u8; p=p+1 } 920 bfirst=0 921 d[p]=123 as u8; p=p+1 922 p = al_key(d,p,"a" as *u8) 923 p = al_ix_tv_emit(d,p,ar,offs[bgkey[bi] / AL_IX_KEYB]) 924 d[p]=44 as u8; p=p+1 925 p = al_key(d,p,"b" as *u8) 926 p = al_ix_tv_emit(d,p,ar,offs[bgkey[bi] % AL_IX_KEYB]) 927 d[p]=44 as u8; p=p+1 928 p = al_key(d,p,"support" as *u8) 929 p = al_catn(d,p,bgcnt[bi]) 930 d[p]=44 as u8; p=p+1 931 // association-rule CONFIDENCE = P(b follows a) = support / freq(a). Total-independent 932 // strength: surfaces STRONG transitions vs frequent-by-chance ones (Agrawal et al.). 933 p = al_key(d,p,"confidence_permil" as *u8) 934 var conf: i64 = 0 935 let ca: i64 = bgkey[bi] / AL_IX_KEYB 936 if tvcnt[ca] > 0 { conf = bgcnt[bi] * 1000 / tvcnt[ca] } 937 p = al_catn(d,p,conf) 938 d[p]=125 as u8; p=p+1 939 bcount = bcount + 1 940 } } 941 } 942 bi = bi + 1 943 } 944 d[p]=93 as u8; p=p+1 945 d[p]=44 as u8; p=p+1 946 p = al_key(d,p,"bigrams_capped" as *u8) 947 p = al_catn(d,p,bcapped) 948 d[p]=44 as u8; p=p+1 949 p = al_key(d,p,"trigrams" as *u8) 950 d[p]=91 as u8; p=p+1 951 var tcount: i64 = 0 952 var tcapped: i64 = 0 953 var tfirst: i64 = 1 954 var ti: i64 = 0 955 while ti < ntgp[0] { 956 if tgcnt[ti] >= minsup { 957 if tcount >= AL_TRIGRAM_CAP { tcapped=1 } else { if p > AL_SOFT { tcapped=1 } else { 958 if tfirst==0 { d[p]=44 as u8; p=p+1 } 959 tfirst=0 960 d[p]=123 as u8; p=p+1 961 p = al_key(d,p,"a" as *u8) 962 p = al_ix_tv_emit(d,p,ar,offs[tgkey[ti] / (AL_IX_KEYB * AL_IX_KEYB)]) 963 d[p]=44 as u8; p=p+1 964 p = al_key(d,p,"b" as *u8) 965 p = al_ix_tv_emit(d,p,ar,offs[(tgkey[ti] / AL_IX_KEYB) % AL_IX_KEYB]) 966 d[p]=44 as u8; p=p+1 967 p = al_key(d,p,"c" as *u8) 968 p = al_ix_tv_emit(d,p,ar,offs[tgkey[ti] % AL_IX_KEYB]) 969 d[p]=44 as u8; p=p+1 970 p = al_key(d,p,"support" as *u8) 971 p = al_catn(d,p,tgcnt[ti]) 972 d[p]=125 as u8; p=p+1 973 tcount = tcount + 1 974 } } 975 } 976 ti = ti + 1 977 } 978 d[p]=93 as u8; p=p+1 979 d[p]=44 as u8; p=p+1 980 p = al_key(d,p,"trigrams_capped" as *u8) 981 p = al_catn(d,p,tcapped) 982 d[p]=44 as u8; p=p+1 983 p = al_key(d,p,"index_overflow" as *u8) 984 p = al_catn(d,p,ov[0]) 985 d[p]=125 as u8; p=p+1 986 d[p]=10 as u8; p=p+1 987 return p 988} 989 990// ---------- F968 BENCH: miner accuracy vs CONSTRUCTED ground truth ---------- 991// Synthetic journals with PLANTED patterns = an exact oracle by construction (no external dep): 992// expected supports are known arithmetic facts about the plant. Adversarial families: gapped plants 993// (found ONLY at sufficient maxgap, never at gap 0), ws-split decoys (never cross), pure-noise 994// (nothing fabricated), self-loops + trigrams (exact counts). A wrong support ANYWHERE = RED. 995func al_bench_path(dst: *u8, prefix: *u8, now: i64, sfx: *u8) -> i64 { 996 var o: i64 = al_cat(dst, 0, prefix) 997 o = al_catn(dst, o, now) 998 o = al_cat(dst, o, sfx) 999 dst[o] = 0 as u8 1000 // seq672 FIX: TRUNCATE the bench fixture so two selftest runs in the SAME second (now collides) 1001 // start empty instead of doubling appended rows -> the exact-support bench assertions stay idempotent. 1002 let fd: i64 = sys_openat_wr(dst, 0x1a4) 1003 if fd >= 0 { sys_close(fd) } 1004 return 0 1005} 1006func al_bench_case(ctr: *i64, name: *u8, jr: *u8, minsup: i64, maxgap: i64, needle: *u8, want: i64) -> i64 { 1007 let q: *u8 = sys_mmap(AL_WIN) 1008 let cx: *i64 = sys_mmap(64) as *i64 1009 let n: i64 = al_read(jr, q, AL_WIN - 8) 1010 cx[0]=n 1011 cx[1]=0 1012 cx[2]=0 1013 cx[3]=0 1014 cx[4]=0 1015 cx[5]=jr as i64 1016 let d: *u8 = sys_mmap(AL_OUT) 1017 let dl: i64 = al_mine_fast(q,cx,minsup,maxgap,0,d) 1018 var got: i64 = 0 1019 if dl > 0 { got = al_find(d,dl,needle) } 1020 var ok: i64 = 0 1021 if got == want { ok = 1 } 1022 gv_check(name, ok, ctr) 1023 return ok 1024} 1025func al_bench(prefix: *u8) -> i64 { 1026 let ctr: *i64 = gv_ctr() 1027 gv_head("nx_actlog bench -- miner accuracy vs planted-pattern EXACT oracle (F968)" as *u8) 1028 let now: i64 = sys_now_realtime_sec() 1029 let jA: *u8 = sys_mmap(512) 1030 al_bench_path(jA, prefix, now, ".bA" as *u8) 1031 let jB: *u8 = sys_mmap(512) 1032 al_bench_path(jB, prefix, now, ".bB" as *u8) 1033 let jC: *u8 = sys_mmap(512) 1034 al_bench_path(jC, prefix, now, ".bC" as *u8) 1035 let jD: *u8 = sys_mmap(512) 1036 al_bench_path(jD, prefix, now, ".bD" as *u8) 1037 let jE: *u8 = sys_mmap(512) 1038 al_bench_path(jE, prefix, now, ".bE" as *u8) 1039 let jF: *u8 = sys_mmap(512) 1040 al_bench_path(jF, prefix, now, ".bF" as *u8) 1041 // jA: X:a Y:b alternating x3 in w1 (contiguous plant, K=3) 1042 al_append(jA, 900, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1043 al_append(jA, 901, "w1" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1044 al_append(jA, 902, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1045 al_append(jA, 903, "w1" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1046 al_append(jA, 904, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1047 al_append(jA, 905, "w1" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1048 al_bench_case(ctr, "C1 contiguous plant support exact 3 at maxgap 0" as *u8, jA, 2, 0, "\"a\":\"X:a\",\"b\":\"Y:b\",\"support\":3" as *u8, 1) 1049 al_bench_case(ctr, "C2 gap window never inflates the adjacent plant (still 3 at maxgap 1)" as *u8, jA, 2, 1, "\"a\":\"X:a\",\"b\":\"Y:b\",\"support\":3" as *u8, 1) 1050 // jB: X:a N:z Y:b x3 in w1 (gapped plant, gap=1, K=3) 1051 al_append(jB, 910, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1052 al_append(jB, 911, "w1" as *u8, "N" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1053 al_append(jB, 912, "w1" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1054 al_append(jB, 913, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1055 al_append(jB, 914, "w1" as *u8, "N" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1056 al_append(jB, 915, "w1" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1057 al_append(jB, 916, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1058 al_append(jB, 917, "w1" as *u8, "N" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1059 al_append(jB, 918, "w1" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1060 al_bench_case(ctr, "C3 gapped plant INVISIBLE at maxgap 0 (no fabrication)" as *u8, jB, 2, 0, "\"a\":\"X:a\",\"b\":\"Y:b\"" as *u8, 0) 1061 al_bench_case(ctr, "C4 gapped plant found exact 3 at maxgap 1" as *u8, jB, 2, 1, "\"a\":\"X:a\",\"b\":\"Y:b\",\"support\":3" as *u8, 1) 1062 al_bench_case(ctr, "C5 wider window stays exact (3 at maxgap 2, no overcount)" as *u8, jB, 2, 2, "\"a\":\"X:a\",\"b\":\"Y:b\",\"support\":3" as *u8, 1) 1063 // jC: X:a in w1 / Y:b in w2 alternating x3 (ws-split decoy) 1064 al_append(jC, 920, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1065 al_append(jC, 921, "w2" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1066 al_append(jC, 922, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1067 al_append(jC, 923, "w2" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1068 al_append(jC, 924, "w1" as *u8, "X" as *u8, "a" as *u8, "ok" as *u8, "-" as *u8) 1069 al_append(jC, 925, "w2" as *u8, "Y" as *u8, "b" as *u8, "ok" as *u8, "-" as *u8) 1070 al_bench_case(ctr, "C6 ws-split decoy never counted even at maxgap 2 (isolation)" as *u8, jC, 2, 2, "\"a\":\"X:a\",\"b\":\"Y:b\"" as *u8, 0) 1071 // jD: 5 distinct singleton tvs (pure noise) 1072 al_append(jD, 930, "w1" as *u8, "T1" as *u8, "v" as *u8, "ok" as *u8, "-" as *u8) 1073 al_append(jD, 931, "w1" as *u8, "T2" as *u8, "v" as *u8, "ok" as *u8, "-" as *u8) 1074 al_append(jD, 932, "w1" as *u8, "T3" as *u8, "v" as *u8, "ok" as *u8, "-" as *u8) 1075 al_append(jD, 933, "w1" as *u8, "T4" as *u8, "v" as *u8, "ok" as *u8, "-" as *u8) 1076 al_append(jD, 934, "w1" as *u8, "T5" as *u8, "v" as *u8, "ok" as *u8, "-" as *u8) 1077 al_bench_case(ctr, "C7 pure noise yields ZERO patterns at minsup 2" as *u8, jD, 2, 2, "\"support\"" as *u8, 0) 1078 // jE: P:p Q:q R:r x2 (trigram plant, K=2) 1079 al_append(jE, 940, "w1" as *u8, "P" as *u8, "p" as *u8, "ok" as *u8, "-" as *u8) 1080 al_append(jE, 941, "w1" as *u8, "Q" as *u8, "q" as *u8, "ok" as *u8, "-" as *u8) 1081 al_append(jE, 942, "w1" as *u8, "R" as *u8, "r" as *u8, "ok" as *u8, "-" as *u8) 1082 al_append(jE, 943, "w1" as *u8, "P" as *u8, "p" as *u8, "ok" as *u8, "-" as *u8) 1083 al_append(jE, 944, "w1" as *u8, "Q" as *u8, "q" as *u8, "ok" as *u8, "-" as *u8) 1084 al_append(jE, 945, "w1" as *u8, "R" as *u8, "r" as *u8, "ok" as *u8, "-" as *u8) 1085 al_bench_case(ctr, "C8 trigram plant support exact 2" as *u8, jE, 2, 0, "\"a\":\"P:p\",\"b\":\"Q:q\",\"c\":\"R:r\",\"support\":2" as *u8, 1) 1086 // jF: S:s x4 (self-loop, 3 adjacent pairs) 1087 al_append(jF, 950, "w1" as *u8, "S" as *u8, "s" as *u8, "ok" as *u8, "-" as *u8) 1088 al_append(jF, 951, "w1" as *u8, "S" as *u8, "s" as *u8, "ok" as *u8, "-" as *u8) 1089 al_append(jF, 952, "w1" as *u8, "S" as *u8, "s" as *u8, "ok" as *u8, "-" as *u8) 1090 al_append(jF, 953, "w1" as *u8, "S" as *u8, "s" as *u8, "ok" as *u8, "-" as *u8) 1091 al_bench_case(ctr, "C9 self-loop support exact 3 (4 frames, 3 adjacent pairs)" as *u8, jF, 2, 0, "\"a\":\"S:s\",\"b\":\"S:s\",\"support\":3" as *u8, 1) 1092 let rc: i64 = gv_verdict("ACTLOG-BENCH" as *u8, ctr, "planted-pattern oracle: exact supports, honest gaps, ws isolation, zero fabrication" as *u8) 1093 return rc 1094} 1095 1096// ---------- selftest gate (T1..T9, deterministic) ---------- 1097func al_selftest(journal: *u8) -> i64 { 1098 let ctr: *i64 = gv_ctr() 1099 gv_head("nx_actlog selftest -- action journal + sequence mining teeth (SELF-cleans scratch)" as *u8) 1100 // seq672 FIX: truncate the scratch journal to empty so a RE-RUN is idempotent (T1 empty-journal 1101 // held on every run). Was "caller pre-cleans" -> a 2nd run failed T1 -> FALSE erosion in the 1102 // harness miner. Truncate-open (O_WRONLY_CT) not unlinkat (the AT_FDCWD dirfd=0 relative-path bug). 1103 let tfd: i64 = sys_openat_wr(journal, 0x1a4) 1104 if tfd >= 0 { sys_close(tfd) } 1105 let q: *u8 = sys_mmap(AL_WIN) 1106 let cx: *i64 = sys_mmap(64) as *i64 1107 let rcap: i64 = AL_WIN - 8 1108 // T1 empty -> zero frames 1109 var n: i64 = al_read(journal,q,rcap) 1110 cx[0]=n; cx[1]=0; cx[2]=0; cx[3]=0; cx[4]=0; cx[5]=journal as i64 1111 var ok1: i64 = 0 1112 if n==0 { ok1=1 } 1113 gv_check("T1 empty journal zero frames" as *u8, ok1, ctr) 1114 // seed w1: A:x -> B:y -> A:x -> B:y interleaved with w2: C:z after the first frame 1115 al_append(journal, 1000, "w1" as *u8, "A" as *u8, "x" as *u8, "ok" as *u8, "n1" as *u8) 1116 al_append(journal, 1001, "w2" as *u8, "C" as *u8, "z" as *u8, "ok" as *u8, "n2" as *u8) 1117 al_append(journal, 1002, "w1" as *u8, "B" as *u8, "y" as *u8, "ok" as *u8, "n3" as *u8) 1118 al_append(journal, 1003, "w1" as *u8, "A" as *u8, "x" as *u8, "ok" as *u8, "n4" as *u8) 1119 al_append(journal, 1004, "w1" as *u8, "B" as *u8, "y" as *u8, "ok" as *u8, "n5" as *u8) 1120 n = al_read(journal,q,rcap) 1121 cx[0]=n 1122 var ok2: i64 = 0 1123 let fr: i64 = al_frames(q,n) 1124 if fr==5 { ok2=1 } 1125 gv_check("T2 five frames logged" as *u8, ok2, ctr) 1126 // T3 ws isolation: successor of frame0 (w1 A:x) is the w1 B:y frame, NOT the w2 C:z frame 1127 var ok3: i64 = 0 1128 let s0: i64 = al_succ(q,cx,0) 1129 if s0 >= 0 { 1130 let sp: *i64 = sys_mmap(16) as *i64 1131 let se: i64 = al_le(q,s0,n) 1132 if al_col(q,s0,se,2,sp)==1 { if al_lit_eq(q,sp[0],sp[1],"B" as *u8)==1 { ok3=1 } } 1133 } 1134 gv_check("T3 successor stays inside workstream" as *u8, ok3, ctr) 1135 // T4 bigram support: (A:x -> B:y) in w1 occurs twice 1136 var ok4: i64 = 0 1137 let s0b: i64 = al_succ(q,cx,0) 1138 if s0b >= 0 { let sup: i64 = al_bg_support(q,cx,0,s0b); if sup==2 { ok4=1 } } 1139 gv_check("T4 repeated bigram support exact 2" as *u8, ok4, ctr) 1140 // T5 trigram support: (A:x -> B:y -> A:x) occurs once 1141 var ok5: i64 = 0 1142 if s0b >= 0 { 1143 let s1: i64 = al_succ(q,cx,s0b) 1144 if s1 >= 0 { let sup3: i64 = al_tg_support(q,cx,0,s0b,s1); if sup3==1 { ok5=1 } } 1145 } 1146 gv_check("T5 trigram support exact 1" as *u8, ok5, ctr) 1147 // T6 threshold honesty: mine with minsup=99 emits zero bigrams 1148 let d: *u8 = sys_mmap(AL_OUT) 1149 let dl6: i64 = al_mine_fast(q,cx,99,0,0,d) 1150 var ok6: i64 = 0 1151 if dl6 > 0 { if al_find(d,dl6,"\"support\"" as *u8)==0 { ok6=1 } } 1152 gv_check("T6 minsup 99 yields no patterns no fabrication" as *u8, ok6, ctr) 1153 // T7 real mine JSON: support-2 bigram present + envelope keys 1154 let dl7: i64 = al_mine_fast(q,cx,2,0,0,d) 1155 var ok7: i64 = 0 1156 if dl7 > 0 { if d[0]==(123 as u8) { if al_find(d,dl7,"bigrams_capped" as *u8)==1 { if al_find(d,dl7,"A:x" as *u8)==1 { if al_find(d,dl7,"support" as *u8)==1 { ok7=1 } } } } } 1157 gv_check("T7 mine JSON carries pattern plus declared envelope" as *u8, ok7, ctr) 1158 // T8 freq: per-tool:verb frequency over the seeded journal -- A:x 2, B:y 2, C:z 1, distinct 3. 1159 // Non-vacuous two ways: C:z has NO successor so a sequence-only regression cannot emit it, and 1160 // a dedup regression (first-check broken) reads distinct 5 not 3. 1161 let dl8: i64 = al_freq_fast(q,cx,d) 1162 var ok8: i64 = 0 1163 if dl8 > 0 { if al_find(d,dl8,"\"distinct\":3" as *u8)==1 { if al_find(d,dl8,"\"tv\":\"C:z\",\"count\":1" as *u8)==1 { if al_find(d,dl8,"\"tv\":\"A:x\",\"count\":2" as *u8)==1 { ok8=1 } } } } 1164 gv_check("T8 freq ranks per-tool:verb counts (A:x 2, C:z 1, distinct 3 -- dedup + non-sequence coverage)" as *u8, ok8, ctr) 1165 // T9 harden: A:x and B:y recur (count 2 >= minsup 2), C:z does not. With the INJECTED argecho 1166 // filer, exactly those two file -- their echoed argv must carry the REAL canonical desc (a fork 1167 // that never ran leaves out empty), and C:z must be absent (a broken recurrence filter files 3). 1168 let dl9: i64 = al_harden_json(q,cx,2,5,"argecho" as *u8,d) 1169 var ok9: i64 = 0 1170 if dl9 > 0 { if al_find(d,dl9,"\"recurring\":2" as *u8)==1 { if al_find(d,dl9,"FALLBACK-RECURRENCE A:x" as *u8)==1 { if al_find(d,dl9,"FALLBACK-RECURRENCE B:y" as *u8)==1 { if al_find(d,dl9,"C:z" as *u8)==0 { ok9=1 } } } } } 1171 gv_check("T9 harden files EXACTLY the recurring tvs through the injected filer (canonical desc echoed back; C:z absent)" as *u8, ok9, ctr) 1172 // T10/T11 gap-constrained mining (F966 rung 1): w3 = A:x, C:z, B:y -- A->B has ONE intervening frame 1173 al_append(journal, 1010, "w3" as *u8, "A" as *u8, "x" as *u8, "ok" as *u8, "g1" as *u8) 1174 al_append(journal, 1011, "w3" as *u8, "C" as *u8, "z" as *u8, "ok" as *u8, "g2" as *u8) 1175 al_append(journal, 1012, "w3" as *u8, "B" as *u8, "y" as *u8, "ok" as *u8, "g3" as *u8) 1176 n = al_read(journal,q,rcap) 1177 cx[0]=n 1178 let dl10: i64 = al_mine_fast(q,cx,2,0,0,d) 1179 var ok10: i64 = 0 1180 if dl10 > 0 { if al_find(d,dl10,"\"a\":\"A:x\",\"b\":\"B:y\",\"support\":2" as *u8)==1 { ok10=1 } } 1181 gv_check("T10 maxgap 0 never counts the gapped pair (support stays 2)" as *u8, ok10, ctr) 1182 let dl11: i64 = al_mine_fast(q,cx,2,1,0,d) 1183 var ok11: i64 = 0 1184 if dl11 > 0 { if al_find(d,dl11,"\"a\":\"A:x\",\"b\":\"B:y\",\"support\":3" as *u8)==1 { if al_find(d,dl11,"\"maxgap\":1" as *u8)==1 { ok11=1 } } } 1185 gv_check("T11 maxgap 1 finds the interleaved pair (support 3, declared)" as *u8, ok11, ctr) 1186 // T12: the F968 bench itself must be all-exact (accuracy measured, not asserted) 1187 let rb: i64 = al_bench(journal) 1188 var ok12: i64 = 0 1189 if rb == 0 { ok12 = 1 } 1190 gv_check("T12 bench: planted-pattern accuracy harness all-exact" as *u8, ok12, ctr) 1191 // T13/T14 CLOSED PATTERNS (CloSpan/BIDE, benchmarked vs a hand-computed closure oracle). w4 = G:x 1192 // H:y I:z x3 (contiguous) so G:x->H:y and H:y->I:z are SUBSUMED by the trigram G:x->H:y->I:z (equal 1193 // support 3) = NON-closed. w5 = P:x Q:y R:z / P:x Q:y S:z so P:x->Q:y (sup 2) has NO equal-support 1194 // trigram (both P:x->Q:y->{R,S} are sup 1) = CLOSED. Fresh tvs (G/H/I/P/Q/R/S) avoid main-journal collisions. 1195 al_append(journal, AL_MAGIC_1100, "w4" as *u8, "G" as *u8, "x" as *u8, "ok" as *u8, "-" as *u8) 1196 al_append(journal, AL_MAGIC_1101, "w4" as *u8, "H" as *u8, "y" as *u8, "ok" as *u8, "-" as *u8) 1197 al_append(journal, AL_MAGIC_1102, "w4" as *u8, "I" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1198 al_append(journal, AL_MAGIC_1103, "w4" as *u8, "G" as *u8, "x" as *u8, "ok" as *u8, "-" as *u8) 1199 al_append(journal, AL_MAGIC_1104, "w4" as *u8, "H" as *u8, "y" as *u8, "ok" as *u8, "-" as *u8) 1200 al_append(journal, AL_MAGIC_1105, "w4" as *u8, "I" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1201 al_append(journal, AL_MAGIC_1106, "w4" as *u8, "G" as *u8, "x" as *u8, "ok" as *u8, "-" as *u8) 1202 al_append(journal, AL_MAGIC_1107, "w4" as *u8, "H" as *u8, "y" as *u8, "ok" as *u8, "-" as *u8) 1203 al_append(journal, AL_MAGIC_1108, "w4" as *u8, "I" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1204 al_append(journal, AL_MAGIC_1110, "w5" as *u8, "P" as *u8, "x" as *u8, "ok" as *u8, "-" as *u8) 1205 al_append(journal, AL_MAGIC_1111, "w5" as *u8, "Q" as *u8, "y" as *u8, "ok" as *u8, "-" as *u8) 1206 al_append(journal, AL_MAGIC_1112, "w5" as *u8, "R" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1207 al_append(journal, AL_MAGIC_1113, "w5" as *u8, "P" as *u8, "x" as *u8, "ok" as *u8, "-" as *u8) 1208 al_append(journal, AL_MAGIC_1114, "w5" as *u8, "Q" as *u8, "y" as *u8, "ok" as *u8, "-" as *u8) 1209 al_append(journal, AL_MAGIC_1115, "w5" as *u8, "S" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1210 n = al_read(journal,q,rcap) 1211 cx[0]=n 1212 let dlnc: i64 = al_mine_fast(q,cx,2,0,0,d) 1213 var ok13: i64 = 0 1214 if dlnc > 0 { if al_find(d,dlnc,"\"a\":\"G:x\",\"b\":\"H:y\",\"support\":3" as *u8)==1 { if al_find(d,dlnc,"\"closed\":0" as *u8)==1 { ok13=1 } } } 1215 gv_check("T13 non-closed mode emits the subsumed bigram G:x->H:y (support 3)" as *u8, ok13, ctr) 1216 let dlc: i64 = al_mine_fast(q,cx,2,0,1,d) 1217 var ok14: i64 = 0 1218 // the SUBSUMED bigram's specific form ("support" right after "b") must be gone; the trigram 1219 // G:x->H:y->I:z ("c" after "b") legitimately remains and must NOT be matched as the bigram. 1220 if dlc > 0 { if al_find(d,dlc,"\"a\":\"G:x\",\"b\":\"H:y\",\"support\"" as *u8)==0 { if al_find(d,dlc,"\"a\":\"P:x\",\"b\":\"Q:y\",\"support\":2" as *u8)==1 { if al_find(d,dlc,"\"closed\":1" as *u8)==1 { ok14=1 } } } } 1221 gv_check("T14 closed mode DROPS the subsumed bigram, KEEPS the closed P:x->Q:y (CloSpan/BIDE)" as *u8, ok14, ctr) 1222 // T15 CONFIDENCE (association-rule strength, EXACT + total-independent). w6 = M N M N M P => M appears 1223 // 3x, M->N 2x, M->P 1x => confidence(M:x->N:y)=support(2)/freq(M)(3)=666 permil regardless of journal size. 1224 al_append(journal, AL_MAGIC_1100, "w6" as *u8, "M" as *u8, "x" as *u8, "ok" as *u8, "-" as *u8) 1225 al_append(journal, AL_MAGIC_1100, "w6" as *u8, "N" as *u8, "y" as *u8, "ok" as *u8, "-" as *u8) 1226 al_append(journal, AL_MAGIC_1100, "w6" as *u8, "M" as *u8, "x" as *u8, "ok" as *u8, "-" as *u8) 1227 al_append(journal, AL_MAGIC_1100, "w6" as *u8, "N" as *u8, "y" as *u8, "ok" as *u8, "-" as *u8) 1228 al_append(journal, AL_MAGIC_1100, "w6" as *u8, "M" as *u8, "x" as *u8, "ok" as *u8, "-" as *u8) 1229 al_append(journal, AL_MAGIC_1100, "w6" as *u8, "P" as *u8, "z" as *u8, "ok" as *u8, "-" as *u8) 1230 n = al_read(journal,q,rcap) 1231 cx[0]=n 1232 let dlcf: i64 = al_mine_fast(q,cx,2,0,0,d) 1233 var ok15: i64 = 0 1234 if dlcf > 0 { if al_find(d,dlcf,"\"a\":\"M:x\",\"b\":\"N:y\",\"support\":2,\"confidence_permil\":666" as *u8)==1 { ok15=1 } } 1235 gv_check("T15 confidence(M:x->N:y)=666 permil (support 2 / freq(M) 3 -- association-rule strength, exact)" as *u8, ok15, ctr) 1236 let rc: i64 = gv_verdict("ACTLOG-GATE" as *u8, ctr, "action journal + sequence + frequency + harden + gap + bench + closed-pattern + confidence teeth green" as *u8) 1237 return rc 1238} 1239 1240func main(argc: i64, argv: *i64) -> i64 { 1241 if argc < 3 { gv_puts("usage: nx_actlog {log <journal> <ws> <tool> <verb> <outcome> <note> | mine <journal> [minsup] [maxgap] [closed] | freq <journal> | harden <journal> [minsup] [filer] | bench <scratch-prefix> | selftest <journal>}\n" as *u8); sys_exit(2); return 2 } 1242 let verb: *u8 = argv[1] as *u8 1243 let journal: *u8 = argv[2] as *u8 1244 let vl: i64 = al_vlen(verb) 1245 if al_lit_eq(verb,0,vl,"selftest" as *u8)==1 { let rc: i64 = al_selftest(journal); sys_exit(rc); return rc } 1246 if al_lit_eq(verb,0,vl,"log" as *u8)==1 { 1247 if argc < 8 { gv_puts("log needs <journal> <ws> <tool> <verb> <outcome> <note>\n" as *u8); sys_exit(2); return 2 } 1248 let ws: *u8 = argv[3] as *u8 1249 let tool: *u8 = argv[4] as *u8 1250 let averb: *u8 = argv[5] as *u8 1251 let outcome: *u8 = argv[6] as *u8 1252 let note: *u8 = argv[7] as *u8 1253 let rc: i64 = al_append(journal, -1, ws, tool, averb, outcome, note) 1254 if rc==0 { gv_puts("ACTLOG OK ws=" as *u8); gv_puts(ws); gv_puts(" " as *u8); gv_puts(tool); gv_puts(":" as *u8); gv_puts(averb); gv_puts("\n" as *u8); sys_exit(0); return 0 } 1255 gv_puts("append FAILED\n" as *u8) 1256 sys_exit(1) 1257 return 1 1258 } 1259 if al_lit_eq(verb,0,vl,"mine" as *u8)==1 { 1260 let q: *u8 = sys_mmap(AL_WIN) 1261 let rcap: i64 = AL_WIN - 8 1262 let n: i64 = al_read(journal,q,rcap) 1263 let cx: *i64 = sys_mmap(64) as *i64 1264 cx[0]=n 1265 cx[1]=sys_now_realtime_sec() 1266 cx[2]=0 1267 cx[3]=0 1268 cx[4]=0 1269 if n >= rcap { cx[4]=1 } 1270 cx[5]=journal as i64 1271 var minsup: i64 = AL_MINSUP_DEF 1272 if argc >= 4 { let mz: i64 = al_atoi_z(argv[3] as *u8); if mz > 0 { minsup = mz } } 1273 var maxgap: i64 = 0 1274 if argc >= 5 { let gz: i64 = al_atoi_z(argv[4] as *u8); if gz > 0 { maxgap = gz } } 1275 if maxgap > AL_IX_MAXGAP { maxgap = AL_IX_MAXGAP } 1276 var closed: i64 = 0 1277 if argc >= 6 { let cz: i64 = al_atoi_z(argv[5] as *u8); if cz > 0 { closed = 1 } } 1278 let d: *u8 = sys_mmap(AL_OUT) 1279 let dl: i64 = al_mine_fast(q,cx,minsup,maxgap,closed,d) 1280 sys_write(1,d,dl) 1281 sys_exit(0) 1282 return 0 1283 } 1284 if al_lit_eq(verb,0,vl,"freq" as *u8)==1 { 1285 let q: *u8 = sys_mmap(AL_WIN) 1286 let rcap: i64 = AL_WIN - 8 1287 let n: i64 = al_read(journal,q,rcap) 1288 let cx: *i64 = sys_mmap(64) as *i64 1289 cx[0]=n 1290 cx[1]=0 1291 cx[2]=0 1292 cx[3]=0 1293 cx[4]=0 1294 if n >= rcap { cx[4]=1 } 1295 cx[5]=journal as i64 1296 let d: *u8 = sys_mmap(AL_OUT) 1297 let dl: i64 = al_freq_fast(q,cx,d) 1298 sys_write(1,d,dl) 1299 sys_exit(0) 1300 return 0 1301 } 1302 if al_lit_eq(verb,0,vl,"harden" as *u8)==1 { 1303 let q: *u8 = sys_mmap(AL_WIN) 1304 let rcap: i64 = AL_WIN - 8 1305 let n: i64 = al_read(journal,q,rcap) 1306 let cx: *i64 = sys_mmap(64) as *i64 1307 cx[0]=n 1308 cx[1]=0 1309 cx[2]=0 1310 cx[3]=0 1311 cx[4]=0 1312 if n >= rcap { cx[4]=1 } 1313 cx[5]=journal as i64 1314 var minsup: i64 = AL_MINSUP_DEF 1315 if argc >= 4 { let mz: i64 = al_atoi_z(argv[3] as *u8); if mz > 0 { minsup = mz } } 1316 let filer: *u8 = sys_mmap(128) 1317 var fo2: i64 = 0 1318 if argc >= 5 { fo2 = al_cat(filer,0,argv[4] as *u8) } else { fo2 = al_cat(filer,0,"nx_debt" as *u8) } 1319 filer[fo2]=0 as u8 1320 let d: *u8 = sys_mmap(AL_OUT) 1321 let dl: i64 = al_harden_json(q,cx,minsup,AL_HARDEN_SEV,filer,d) 1322 sys_write(1,d,dl) 1323 sys_exit(0) 1324 return 0 1325 } 1326 if al_lit_eq(verb,0,vl,"bench" as *u8)==1 { 1327 let rb: i64 = al_bench(journal) 1328 sys_exit(rb) 1329 return rb 1330 } 1331 gv_puts("unknown verb\n" as *u8) 1332 sys_exit(2) 1333 return 2 1334}