nx_ask_gate.nx source
↩ module page · 85 lines · 4756 B
1// nx_ask_gate.nx -- gates the user-question capability on a THROWAWAY store prefix... except nx_ask pins
2// the canonical knowledge/store/ask- prefix BY DESIGN (one queue, no ambient prefix injection), so this
3// gate runs against a scratch CWD instead: the runner must cd to a throwaway dir (the store prefix is
4// CWD-relative). Asserts the FULL contract via the CLI-level funcs: post -> id; pending shows the OPEN
5// row; answer flips it; get serves all fields; pending drains; unknown id is the store's honest ABSENT
6// (negative control). Exit 0 only on all-PASS. license_tier: ORIGINAL expect_exit: 0
7import "nx_ask_lib.nx"
8
9const AG_CHECKS: i64 = 6
10const AG_AV_BUF: i64 = 64 // synthetic argv vector buffer
11const AG_PTR_BUF: i64 = 16 // ptr+len out-param cells
12
13func ag_check(name: *u8, ok: i64, pass: *i64) -> i64 {
14 ak_puts("T " as *u8); ak_puts(name); ak_puts(" -> " as *u8)
15 if ok == 1 { ak_puts("PASS\n" as *u8); pass[0] = pass[0] + 1 } else { ak_puts("FAIL\n" as *u8) }
16 return 0
17}
18// run main() with a synthetic argv (mirrors how the tools-api invokes the organ)
19func ag_run(a1: *u8, a2: *u8, a3: *u8, argc: i64) -> i64 {
20 let av: *i64 = sys_mmap(AG_AV_BUF) as *i64
21 av[0] = "nx_ask" as *u8 as i64
22 av[AK_ARG_VERB] = a1 as i64
23 av[AK_ARG_P1] = a2 as i64
24 av[AK_ARG_P2] = a3 as i64
25 return ak_main(argc, av)
26}
27
28func main() -> i64 {
29 // HERMETICITY ROOT-FIX 2026-07-30: this gate posts AND answers a row every run against a store
30 // prefix that is CWD-relative and was never actually thrown away. Measured after ~24 sweep cycles:
31 // 3989 segments / 280MB in the throwaway fixture, oldest segment 92B vs newest 5702B, and the gate
32 // took 123329ms (it did not merely exceed the 10s budget -- it ran 123s and was still going), so
33 // ask-contract sat RED x24 cycles at 0 permille. A GATE WHOSE RUNTIME GROWS WITH THE NUMBER OF TIMES
34 // IT HAS RUN IS MEASURING ITS OWN HISTORY, NOT THE CONTRACT. Dropping the manifest resets the plane
35 // to empty (the manifest is what DEFINES live segments), so every run starts from the same state --
36 // and it leaves the old segments as manifest-ORPHANS, which is exactly what nx_store_janitor is
37 // defined to reclaim. Fixture ownership belongs to the gate, not to whoever cleans up after it.
38 sys_unlinkat("knowledge/store/ask-manifest.txt" as *u8)
39 let pass: *i64 = sys_mmap(AG_PTR_BUF) as *i64
40 pass[0] = 0
41 // T1 post a question -> rc 0 (id printed; epoch-derived)
42 ag_check("ask-posts" as *u8, (ag_run("ask" as *u8, "gate-agent" as *u8, "which backend should win?" as *u8, AK_ARGC_ASK) == 0) as i64, pass)
43 // T2 pending lists it (rc 0; the OPEN row prints)
44 ag_check("pending-lists" as *u8, (ag_run("pending" as *u8, "" as *u8, "" as *u8, AK_ARGC_PEND) == 0) as i64, pass)
45 // recover the id: it is the newest entry in ask:ids
46 let pq: *i64 = sys_mmap(AG_PTR_BUF) as *i64
47 let lq: *i64 = sys_mmap(AG_PTR_BUF) as *i64
48 let idb: *u8 = sys_mmap(AK_KEY)
49 var idl: i64 = 0
50 if ss_get("knowledge/store/ask-" as *u8, "ask:ids" as *u8, pq, lq) == 1 {
51 let ids: *u8 = pq[0] as *u8
52 let n: i64 = lq[0]
53 var s: i64 = 0
54 var i: i64 = 0
55 while i < n { if ids[i] == (AK_TAB as u8) { s = i + 1 } i = i + 1 }
56 while s < n { idb[idl] = ids[s]; idl = idl + 1; s = s + 1 }
57 }
58 idb[idl] = 0 as u8
59 // T3 get returns OPEN before any answer
60 ag_check("get-open" as *u8, (ag_run("get" as *u8, idb, "" as *u8, AK_ARGC_GET) == 0) as i64, pass)
61 // T4 answer flips it (rc 0)
62 ag_check("answer-lands" as *u8, (ag_run("answer" as *u8, idb, "backend B, it is proven" as *u8, AK_ARGC_ANS) == 0) as i64, pass)
63 // T5 the record is now ANSWERED with the text (assert the STORE, not just the rc)
64 var ok5: i64 = 0
65 let kb: *u8 = sys_mmap(AK_KEY)
66 ak_key(kb, idb)
67 if ss_get("knowledge/store/ask-" as *u8, kb, pq, lq) == 1 {
68 let rec: *u8 = pq[0] as *u8
69 let fb: *u8 = sys_mmap(AK_BUF)
70 ak_field(rec, lq[0], AK_F_STATE, fb)
71 if ak_seq(fb, "ANSWERED" as *u8) == 1 {
72 ak_field(rec, lq[0], AK_F_A, fb)
73 if ak_seq(fb, "backend B, it is proven" as *u8) == 1 { ok5 = 1 }
74 }
75 }
76 ag_check("store-answered-text" as *u8, ok5, pass)
77 // T6 NEGATIVE CONTROL: unknown id -> honest ABSENT rc
78 ag_check("unknown-id-absent" as *u8, (ag_run("get" as *u8, "99999999999" as *u8, "" as *u8, AK_ARGC_GET) == AK_RC_ABSENT) as i64, pass)
79
80 ak_puts("ASK-GATE pass=" as *u8); ak_putn(pass[0]); ak_puts("/" as *u8); ak_putn(AG_CHECKS); ak_puts(" verdict=" as *u8)
81 if pass[0] == AG_CHECKS { ak_puts("GREEN\n" as *u8); sys_exit(0); return 0 }
82 ak_puts("RED\n" as *u8)
83 sys_exit(1)
84 return 1
85}