code wiki / _hdl_build / nx_adnet_campaign_gate.nx
nx_adnet_campaign_gate.nx source
↩ module page · 117 lines · 8591 B
1// nx_adnet_campaign_gate.nx -- GATE for the serve decision (nx_adnet_campaign).
2// A biller is dangerous toward over-charging; a SERVE gate is dangerous toward giving inventory away and
3// toward spending a client's budget they did not authorise. So the load-bearing teeth are the REFUSALS,
4// and each is chosen so its pass and fail values differ observably:
5// * BUDGET STOPS DELIVERY -- spent >= budget must return EXHAUSTED, not merely clamp a bill. This is the
6// measured gap that motivated the lib: anb_capped clamps the invoice while serving continues forever.
7// * UNMEASURABLE SPEND REFUSES -- spent < 0 must NOT be treated as zero. Assuming zero is exactly how an
8// exhausted campaign keeps serving.
9// * A TYPO IS NOT A SYNONYM FOR LIVE -- an unrecognised status must be MALFORMED, never default-on.
10// * MALFORMED NEVER SERVES -- a row we cannot evaluate is when we are least entitled to spend money.
11// expect_exit: 0 license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "_hdl_build/nx_adnet_campaign.nx"
14import "nx_gate_verdict.nx"
15
16func tg_slen(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { i = i + 1 } return i }
17func tg_puts(s: *u8) -> i64 { sys_write(1, s, tg_slen(s)); return 0 }
18func tg_pn(v: i64) -> i64 {
19 let b: *u8 = sys_mmap(32)
20 if v == 0 { b[0] = 48 as u8; sys_write(1, b, 1); return 0 }
21 var n: i64 = 0
22 var x: i64 = v
23 while x > 0 { b[n] = ((x - (x / 10) * 10) + 48) as u8; n = n + 1; x = x / 10 }
24 let r: *u8 = sys_mmap(32)
25 var i: i64 = 0
26 while i < n { r[i] = b[n - 1 - i]; i = i + 1 }
27 sys_write(1, r, n)
28 return 0
29}
30func tg_check(name: *u8, cond: i64) -> i64 {
31 if cond == 1 { tg_puts(" PASS " as *u8) } else { tg_puts(" FAIL " as *u8) }
32 tg_puts(name); tg_puts("\n" as *u8)
33 return cond
34}
35func tg_eq(a: *u8, b: *u8) -> i64 {
36 var i: i64 = 0
37 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
38 if b[i] != (0 as u8) { return 0 }
39 return 1
40}
41
42func main() -> i64 {
43 tg_puts("=== nx_adnet_campaign_gate ===\n" as *u8)
44 var pass: i64 = 0
45 var total: i64 = 0
46
47 // flight 1000..2000, budget 5000 milli, live
48 let live: *u8 = "camp-a\tacme-widgets\t/synth/adnet_abc.png\t1000\t2000\t5000\tlive" as *u8
49 let ln: i64 = tg_slen(live)
50
51 pass = pass + tg_check("inside flight, under budget -> SERVABLE" as *u8, camp_verdict(live, ln, 1500, 100) == CAMP_SERVABLE); total = total + 1
52 pass = pass + tg_check("before start -> NOT_STARTED" as *u8, camp_verdict(live, ln, 999, 0) == CAMP_NOT_STARTED); total = total + 1
53 pass = pass + tg_check("after end -> ENDED" as *u8, camp_verdict(live, ln, 2001, 0) == CAMP_ENDED); total = total + 1
54 pass = pass + tg_check("start boundary is INCLUSIVE" as *u8, camp_verdict(live, ln, 1000, 0) == CAMP_SERVABLE); total = total + 1
55 pass = pass + tg_check("end boundary is INCLUSIVE" as *u8, camp_verdict(live, ln, 2000, 0) == CAMP_SERVABLE); total = total + 1
56
57 // ---- budget stops DELIVERY, the whole point ----
58 pass = pass + tg_check("spent == budget -> EXHAUSTED (stops serving, not just billing)" as *u8, camp_verdict(live, ln, 1500, 5000) == CAMP_EXHAUSTED); total = total + 1
59 pass = pass + tg_check("spent over budget -> EXHAUSTED" as *u8, camp_verdict(live, ln, 1500, 99999) == CAMP_EXHAUSTED); total = total + 1
60 pass = pass + tg_check("spent one under budget -> still SERVABLE" as *u8, camp_verdict(live, ln, 1500, 4999) == CAMP_SERVABLE); total = total + 1
61 pass = pass + tg_check("UNMEASURABLE spend -> MALFORMED, never assumed zero" as *u8, camp_verdict(live, ln, 1500, 0 - 1) == CAMP_MALFORMED); total = total + 1
62
63 // ---- explicit unlimited is a DECLARED fact, distinct from missing ----
64 let openb: *u8 = "camp-b\tacme-widgets\t/synth/adnet_abc.png\t1000\t-1\t-1\tlive" as *u8
65 let on: i64 = tg_slen(openb)
66 pass = pass + tg_check("end=-1 open-ended -> serves far in the future" as *u8, camp_verdict(openb, on, 99999999, 0) == CAMP_SERVABLE); total = total + 1
67 pass = pass + tg_check("budget=-1 no ceiling -> huge spend still SERVABLE" as *u8, camp_verdict(openb, on, 99999999, 99999999) == CAMP_SERVABLE); total = total + 1
68
69 // ---- paused + typo ----
70 let paused: *u8 = "camp-c\tacme-widgets\t/synth/adnet_abc.png\t1000\t2000\t5000\tpaused" as *u8
71 pass = pass + tg_check("paused -> PAUSED (not silently live)" as *u8, camp_verdict(paused, tg_slen(paused), 1500, 0) == CAMP_PAUSED); total = total + 1
72 let typo: *u8 = "camp-d\tacme-widgets\t/synth/adnet_abc.png\t1000\t2000\t5000\tliv" as *u8
73 pass = pass + tg_check("status typo -> MALFORMED, NOT a synonym for live" as *u8, camp_verdict(typo, tg_slen(typo), 1500, 0) == CAMP_MALFORMED); total = total + 1
74
75 // ---- malformed never serves ----
76 let badurl: *u8 = "camp-e\tacme-widgets\thttps://evil.example/x.png\t1000\t2000\t5000\tlive" as *u8
77 pass = pass + tg_check("third-party creative -> MALFORMED" as *u8, camp_verdict(badurl, tg_slen(badurl), 1500, 0) == CAMP_MALFORMED); total = total + 1
78 let badnum: *u8 = "camp-f\tacme-widgets\t/synth/adnet_abc.png\tzzz\t2000\t5000\tlive" as *u8
79 pass = pass + tg_check("unparseable date -> MALFORMED, not epoch 0" as *u8, camp_verdict(badnum, tg_slen(badnum), 1500, 0) == CAMP_MALFORMED); total = total + 1
80 pass = pass + tg_check("empty row -> MALFORMED" as *u8, camp_verdict("" as *u8, 0, 1500, 0) == CAMP_MALFORMED); total = total + 1
81
82 // ---- every refusal names something an operator can act on ----
83 let rb: *u8 = sys_mmap(128)
84 camp_reason(CAMP_EXHAUSTED, rb)
85 pass = pass + tg_check("refusal names an actionable reason" as *u8, tg_eq(rb, "not-serving=budget-exhausted" as *u8)); total = total + 1
86
87 // ---- THE WIRING POINT: spend now rides the ROW (field 7), so serving is O(1) and journal-free ----
88 // rows: <camp_bk> <adv> <creative> <start> <end> <budget> <status> <spent_milli>
89 let camps: *u8 = "# id adv creative start end budget status spent
90camp-a acme /synth/adnet_abc.png 1000 2000 5000 live 0
91camp-b acme /synth/adnet_abc.png 1000 2000 5000 live 5000
92camp-z acme /synth/adnet_abc.png 1000 2000 5000 paused 0
93camp-n acme /synth/adnet_abc.png 1000 2000 -1 live
94camp-m acme /synth/adnet_abc.png 1000 2000 5000 live
95" as *u8
96 let cn: i64 = tg_slen(camps)
97 pass = pass + tg_check("no campaign row -> HOUSE inventory still serves (do-no-harm)" as *u8, camp_may_serve(camps, cn, "ha-video" as *u8, 1500) == 1); total = total + 1
98 pass = pass + tg_check("empty campaign conf -> everything serves (byte-identical to today)" as *u8, camp_may_serve("" as *u8, 0, "ha-video" as *u8, 1500) == 1); total = total + 1
99 pass = pass + tg_check("live, in flight, spent 0 of 5000 -> serves" as *u8, camp_may_serve(camps, cn, "camp-a" as *u8, 1500) == 1); total = total + 1
100 pass = pass + tg_check("spent == budget IN THE ROW -> does NOT serve" as *u8, camp_may_serve(camps, cn, "camp-b" as *u8, 1500) == 0); total = total + 1
101 pass = pass + tg_check("flight ended -> does NOT serve" as *u8, camp_may_serve(camps, cn, "camp-a" as *u8, 9999) == 0); total = total + 1
102 pass = pass + tg_check("paused -> does NOT serve" as *u8, camp_may_serve(camps, cn, "camp-z" as *u8, 1500) == 0); total = total + 1
103 pass = pass + tg_check("budget=-1 + NO spend field -> serves (spend not load-bearing)" as *u8, camp_may_serve(camps, cn, "camp-n" as *u8, 1500) == 1); total = total + 1
104 pass = pass + tg_check("real budget + MISSING spend field -> REFUSES (never assumes zero)" as *u8, camp_may_serve(camps, cn, "camp-m" as *u8, 1500) == 0); total = total + 1
105 pass = pass + tg_check("malformed ad id -> does NOT serve" as *u8, camp_may_serve(camps, cn, "bad id!" as *u8, 1500) == 0); total = total + 1
106 pass = pass + tg_check("row spend reader finds field 7" as *u8, camp_row_spent("c a /synth/x.png 1 2 3 live 4242" as *u8, tg_slen("c a /synth/x.png 1 2 3 live 4242" as *u8)) == 4242); total = total + 1
107
108 tg_puts("pass=" as *u8); tg_pn(pass); tg_puts(" fail=" as *u8); tg_pn(total - pass); tg_puts("\n" as *u8)
109 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
110 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
111 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
112 let ctr__dry: *i64 = gv_ctr()
113 ctr__dry[0] = pass
114 ctr__dry[1] = total
115 let rc__dry: i64 = gv_verdict("ADNET-CAMPAIGN-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
116 sys_exit(rc__dry)
117 return rc__dry
118}