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}