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}