code wiki / _hdl_build / nx_adnet_reconcile_gate.nx
nx_adnet_reconcile_gate.nx source
↩ module page · 98 lines · 5347 B
1// nx_adnet_reconcile_gate.nx -- GATE for the spend reconciler (nx_adnet_reconcile).
2// A reconciler is dangerous in two directions and both are teeth here:
3// * DATA LOSS -- it must preserve comments, blanks and rows it cannot compute, BYTE-IDENTICAL. A
4// reconciler that drops what it cannot parse is a data-loss bug wearing a maintenance-task costume.
5// * SILENT REVIVAL -- an UNMEASURABLE spend must leave the old value alone, never write 0. Zeroing an
6// exhausted campaign restarts spending against a budget that is already gone, which is the single
7// worst thing this organ could do.
8// expect_exit: 0 license_tier: ORIGINAL
9import "nx_syscalls.nx"
10import "_hdl_build/nx_adnet_reconcile.nx"
11import "nx_gate_verdict.nx"
12
13func tr_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i }
14func tr_puts(s: *u8) -> i64 { sys_write(1, s, tr_slen(s)); return 0 }
15func tr_pn(v: i64) -> i64 {
16 let b: *u8 = sys_mmap(32)
17 if v == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 }
18 var n: i64 = 0
19 var x: i64 = v
20 while x > 0 { b[n] = ((x - (x / 10) * 10) + 48) as u8; n = n + 1; x = x / 10 }
21 let r: *u8 = sys_mmap(32)
22 var i: i64 = 0
23 while i < n { r[i] = b[n - 1 - i]; i = i + 1 }
24 sys_write(1, r, n)
25 return 0
26}
27func tr_has(buf: *u8, n: i64, pat: *u8) -> i64 {
28 let pl: i64 = tr_slen(pat)
29 if pl == 0 { return 0 }
30 if pl > n { return 0 }
31 var i: i64 = 0
32 while i <= n - pl {
33 var k: i64 = 0
34 var ok: i64 = 1
35 while k < pl { if buf[i + k] != pat[k] { ok = 0; break } k = k + 1 }
36 if ok == 1 { return 1 }
37 i = i + 1
38 }
39 return 0
40}
41func tr_check(name: *u8, cond: i64) -> i64 {
42 if cond == 1 { tr_puts(" PASS " as *u8) } else { tr_puts(" FAIL " as *u8) }
43 tr_puts(name); tr_puts("\n" as *u8)
44 return cond
45}
46
47func main() -> i64 {
48 tr_puts("=== nx_adnet_reconcile_gate ===\n" as *u8)
49 var pass: i64 = 0
50 var total: i64 = 0
51 let out: *u8 = sys_mmap(8192)
52
53 // ha-video priced 5000/100; ha-world DELIBERATELY unpriced -> spend UNMEASURABLE
54 let rates: *u8 = "ha-video\t5000\t100\t-1\n" as *u8
55 let slog: *u8 = "ha-video\nha-video\nha-video\nha-video\nha-world\n" as *u8
56 let vlog: *u8 = "ha-video\n" as *u8
57 let clog: *u8 = "ha-video\n" as *u8
58 // camp rows carry a STALE spend of 9999 so a refresh is visible, and ha-world carries 4242 which must SURVIVE
59 let camps: *u8 = "# spend is owned by the reconciler beat\nha-video\tacme\t/synth/v.png\t1000\t2000\t5000\tlive\t9999\nha-world\tacme\t/synth/w.png\t1000\t2000\t5000\tlive\t4242\n" as *u8
60 let rl: i64 = tr_slen(rates)
61 let sl: i64 = tr_slen(slog)
62 let vl: i64 = tr_slen(vlog)
63 let cl: i64 = tr_slen(clog)
64 let cn: i64 = tr_slen(camps)
65
66 let n: i64 = arec_refresh(camps, cn, rates, rl, slog, sl, vlog, vl, clog, cl, out, 8192)
67 pass = pass + tr_check("conf emitted" as *u8, n > 0); total = total + 1
68
69 // measurable: viewable=1 clicks=1 cpm=5000 cpc=100 -> (1*5000)/1000 + 100 = 105
70 pass = pass + tr_check("priced campaign spend REFRESHED to 105" as *u8, tr_has(out, n, "live\t105" as *u8)); total = total + 1
71 pass = pass + tr_check("stale 9999 is GONE" as *u8, tr_has(out, n, "9999" as *u8) == 0); total = total + 1
72
73 // unmeasurable: must survive untouched, NOT be zeroed
74 pass = pass + tr_check("UNMEASURABLE spend left alone (4242 survives)" as *u8, tr_has(out, n, "4242" as *u8)); total = total + 1
75 pass = pass + tr_check("unmeasurable was NOT zeroed (no 'live\\t0')" as *u8, tr_has(out, n, "live\t0\n" as *u8) == 0); total = total + 1
76
77 // structure preserved
78 pass = pass + tr_check("comment row preserved" as *u8, tr_has(out, n, "# spend is owned by the reconciler beat" as *u8)); total = total + 1
79 pass = pass + tr_check("both campaigns still present" as *u8, tr_has(out, n, "ha-video" as *u8) + tr_has(out, n, "ha-world" as *u8) == 2); total = total + 1
80 pass = pass + tr_check("advertiser column preserved verbatim" as *u8, tr_has(out, n, "acme" as *u8)); total = total + 1
81 pass = pass + tr_check("creative column preserved verbatim" as *u8, tr_has(out, n, "/synth/v.png" as *u8)); total = total + 1
82
83 // idempotence: reconciling the OUTPUT again must be stable (a beat runs forever)
84 let out2: *u8 = sys_mmap(8192)
85 let n2: i64 = arec_refresh(out, n, rates, rl, slog, sl, vlog, vl, clog, cl, out2, 8192)
86 pass = pass + tr_check("IDEMPOTENT: second pass still 105" as *u8, tr_has(out2, n2, "live\t105" as *u8)); total = total + 1
87 pass = pass + tr_check("IDEMPOTENT: second pass still preserves 4242" as *u8, tr_has(out2, n2, "4242" as *u8)); total = total + 1
88
89 tr_puts("pass=" as *u8); tr_pn(pass); tr_puts(" fail=" as *u8); tr_pn(total - pass); tr_puts("\n" as *u8)
90 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
91 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
92 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
93 let ctr__dry: *i64 = gv_ctr()
94 ctr__dry[0] = pass
95 ctr__dry[1] = total
96 let rc__dry: i64 = gv_verdict("ADNET-RECONCILE-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
97 sys_exit(rc__dry)
98 return rc__dry
99}