nx_lorebook_gate.nx source
↩ module page · 111 lines · 7538 B
1// nx_lorebook_gate.nx -- the REFEREE for companionchat CC3 (em_lorebook in nx_lorebook.nx): a keyed entry is injected only when
2// the turn touches one of its triggers; the match is ASCII case-insensitive; an entry fires once per turn; a turn touching
3// nothing writes zero bytes; a malformed row is counted and never fires; and an over-budget turn REFUSES LOUDLY with the
4// numbers, injecting no partial entry. GPU-free, in-process, fixture confs under /tmp/<gate>/ written at run time.
5// license_tier: ORIGINAL expect_exit: 0
6import "nx_syscalls.nx"
7import "nx_gate_verdict.nx"
8import "nx_lorebook.nx"
9
10const LG_DIR: *u8 = "/tmp/nx_lorebook_gate" as *u8
11const LG_CONF: *u8 = "/tmp/nx_lorebook_gate/lore.conf" as *u8
12const LG_CONF_TIGHT: *u8 = "/tmp/nx_lorebook_gate/lore_tight.conf" as *u8
13const LG_CONF_NOBUDGET: *u8 = "/tmp/nx_lorebook_gate/lore_nobudget.conf" as *u8
14const LG_CONF_ABSENT: *u8 = "/tmp/nx_lorebook_gate/does_not_exist.conf" as *u8
15const LG_MODE_DIR: i64 = 511
16const LG_MODE_FILE: i64 = 420
17const LG_OUT_CAP: i64 = 8192
18const LG_STATS_CAP: i64 = 64
19const LG_ROWS: *u8 = "# fixture lorebook\n@budget 600\nbeach|beach, shore|The cove west of the house has black sand and a tide pool she loves.\ncafe|cafe,coffee|Marisol's cafe on the corner does the cardamom latte she orders.\nbroken|only two fields\n" as *u8
20const LG_ROWS_TIGHT: *u8 = "@budget 40\nbeach|beach|The cove west of the house has black sand and a tide pool she loves.\n" as *u8
21const LG_ROWS_NOBUDGET: *u8 = "beach|beach|The cove west of the house has black sand and a tide pool she loves.\n" as *u8
22const LG_BEACH_TELL: *u8 = "black sand" as *u8
23const LG_CAFE_TELL: *u8 = "cardamom" as *u8
24const LG_REFUSED_TELL: *u8 = "[WORLD refused" as *u8
25const LG_HEAD_TELL: *u8 = "[WORLD --" as *u8
26const LG_TIGHT_BUDGET_TEXT: *u8 = " budget of 40 " as *u8
27
28func lg_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
29func lg_find(buf: *u8, n: i64, needle: *u8) -> i64 {
30 let nl: i64 = lg_len(needle)
31 if nl <= 0 { return 0 - 1 }
32 var i: i64 = 0
33 while i + nl <= n {
34 var j: i64 = 0
35 var same: i64 = 1
36 var scan: i64 = 1
37 while scan == 1 { if j >= nl { scan = 0 } else { if buf[i + j] != needle[j] { same = 0; scan = 0 } else { j = j + 1 } } }
38 if same == 1 { return i }
39 i = i + 1
40 }
41 return 0 - 1
42}
43func lg_write(path: *u8, body: *u8) -> i64 {
44 let fd: i64 = sys_openat_wr(path, LG_MODE_FILE)
45 if fd < 0 { return 0 - 1 }
46 sys_write(fd, body, lg_len(body))
47 sys_close(fd)
48 return 0
49}
50// one run of the contract against a conf and a turn; returns bytes written, out/stats filled
51func lg_run(conf: *u8, turn: *u8, out: *u8, stats: *i64) -> i64 {
52 return em_lorebook_from(conf, turn, lg_len(turn), out, LG_OUT_CAP, stats)
53}
54
55func main() -> i64 {
56 gv_head("nx_lorebook_gate -- CC3: keyed world facts injected only when touched, once per turn, under a budget that refuses loudly" as *u8)
57 let ctr: *i64 = gv_ctr()
58 sys_mkdir(LG_DIR, LG_MODE_DIR)
59 sys_unlinkat(LG_CONF_ABSENT)
60 let w1: i64 = lg_write(LG_CONF, LG_ROWS)
61 let w2: i64 = lg_write(LG_CONF_TIGHT, LG_ROWS_TIGHT)
62 let w3: i64 = lg_write(LG_CONF_NOBUDGET, LG_ROWS_NOBUDGET)
63 gv_check("setup-fixture-confs-written" as *u8, (w1 == 0) & (w2 == 0) & (w3 == 0), ctr)
64
65 let out: *u8 = sys_mmap(LG_OUT_CAP)
66 let st: *i64 = sys_mmap(LG_STATS_CAP) as *i64
67
68 // T1 a touched entry is injected, an untouched one is not, and the parse counted what the fixture holds
69 let n1: i64 = lg_run(LG_CONF, "let's go to the beach this afternoon" as *u8, out, st)
70 gv_check("fixture-reached-condition-two-rows-parsed-and-one-malformed-counted" as *u8, (st[LB_ST_ROWS] == 2) & (st[LB_ST_MALFORMED] == 1), ctr)
71 gv_check("trigger-in-turn-injects-its-entry-under-the-world-header" as *u8, (n1 > 0) & (lg_find(out, n1, LG_BEACH_TELL) >= 0) & (lg_find(out, n1, LG_HEAD_TELL) == 0), ctr)
72 gv_check("neg-control-untouched-entry-is-not-injected" as *u8, (n1 > 0) & (lg_find(out, n1, LG_CAFE_TELL) < 0) & (st[LB_ST_FIRED] == 1), ctr)
73
74 // T2 case-insensitive, and the second trigger of a row works after trimming
75 let n2: i64 = lg_run(LG_CONF, "BEACH day?" as *u8, out, st)
76 gv_check("match-is-ascii-case-insensitive" as *u8, (n2 > 0) & (st[LB_ST_FIRED] == 1) & (lg_find(out, n2, LG_BEACH_TELL) >= 0), ctr)
77 let n2b: i64 = lg_run(LG_CONF, "walk along the shore?" as *u8, out, st)
78 gv_check("second-trigger-of-a-row-matches-after-trimming" as *u8, (n2b > 0) & (st[LB_ST_FIRED] == 1) & (lg_find(out, n2b, LG_BEACH_TELL) >= 0), ctr)
79
80 // T3 NEG-CONTROLS: nothing touched, empty turn, absent conf -- all zero bytes
81 let n3: i64 = lg_run(LG_CONF, "how was your day" as *u8, out, st)
82 gv_check("neg-control-turn-touching-no-trigger-writes-zero-bytes" as *u8, (n3 == 0) & (st[LB_ST_FIRED] == 0) & (st[LB_ST_ROWS] == 2), ctr)
83 let n3b: i64 = em_lorebook_from(LG_CONF, "beach" as *u8, 0, out, LG_OUT_CAP, st)
84 gv_check("neg-control-empty-turn-writes-zero-bytes" as *u8, (n3b == 0) & (st[LB_ST_FIRED] == 0), ctr)
85 let n3c: i64 = lg_run(LG_CONF_ABSENT, "beach" as *u8, out, st)
86 gv_check("neg-control-absent-conf-writes-zero-bytes-and-parses-no-rows" as *u8, (n3c == 0) & (st[LB_ST_ROWS] == 0) & (st[LB_ST_FIRED] == 0), ctr)
87
88 // T4 two entries fire in conf order; one entry fires once when two of its triggers match
89 let n4: i64 = lg_run(LG_CONF, "coffee at the beach" as *u8, out, st)
90 let pb: i64 = lg_find(out, n4, LG_BEACH_TELL)
91 let pc: i64 = lg_find(out, n4, LG_CAFE_TELL)
92 gv_check("two-touched-entries-both-inject-in-conf-order" as *u8, (st[LB_ST_FIRED] == 2) & (pb >= 0) & (pc > pb), ctr)
93 let n4b: i64 = lg_run(LG_CONF, "beach shore beach" as *u8, out, st)
94 gv_check("an-entry-fires-once-however-many-of-its-triggers-match" as *u8, (n4b > 0) & (st[LB_ST_FIRED] == 1), ctr)
95
96 // T5 the malformed row never fires even when its own words appear in the turn
97 let n5: i64 = lg_run(LG_CONF, "only two fields" as *u8, out, st)
98 gv_check("malformed-row-is-counted-and-never-fires" as *u8, (n5 == 0) & (st[LB_ST_MALFORMED] == 1) & (st[LB_ST_FIRED] == 0), ctr)
99
100 // T6 over budget: the fixture's budget is below the one entry, the refusal names both numbers, nothing partial lands
101 let n6: i64 = lg_run(LG_CONF_TIGHT, "beach" as *u8, out, st)
102 gv_check("fixture-reached-condition-tight-budget-is-below-the-touched-entry" as *u8, (st[LB_ST_FIRED] == 1) & (st[LB_ST_WANTED] > st[LB_ST_BUDGET]) & (st[LB_ST_BUDGET] == 40), ctr)
103 gv_check("over-budget-refuses-loudly-naming-wanted-and-budget" as *u8, (n6 > 0) & (st[LB_ST_REFUSED] == 1) & (lg_find(out, n6, LG_REFUSED_TELL) == 0) & (lg_find(out, n6, LG_TIGHT_BUDGET_TEXT) >= 0), ctr)
104 gv_check("neg-control-over-budget-injects-no-partial-entry" as *u8, (n6 > 0) & (lg_find(out, n6, LG_BEACH_TELL) < 0) & (lg_find(out, n6, LG_HEAD_TELL) < 0), ctr)
105
106 // T7 the default budget applies when the conf carries no @budget row, and an in-budget turn is not refused
107 let n7: i64 = lg_run(LG_CONF_NOBUDGET, "beach" as *u8, out, st)
108 gv_check("default-budget-applies-when-no-budget-row-and-in-budget-is-not-refused" as *u8, (n7 > 0) & (st[LB_ST_BUDGET] == LB_BUDGET_DEFAULT) & (st[LB_ST_REFUSED] == 0), ctr)
109
110 return gv_verdict("LOREBOOK-GATE" as *u8, ctr, "CC3 done-rule: keyed entries as data, injected on trigger only, once per turn, zero bytes when untouched, malformed rows counted not guessed, over-budget refuses loudly with no partial injection" as *u8)
111}