code wiki / _hdl_build / nx_connect_events_lib.nx

nx_connect_events_lib.nx source

↩ module page · 150 lines · 5799 B

1// nx_connect_events_lib.nx -- the real-world EVENTS + RSVP engine, extracted as a shared library so the 2// verifying gate (nx_connect_events) and the LIVE app (nx_connect_serve /events) bind ONE implementation. 3// Until this split the live page ran a hand-written admission check with NO CAPACITY AT ALL -- an event 4// could be RSVP'd without limit, while this gate-proven engine sat unused. 5// 6// Symbols are moved VERBATIM, deliberately un-renamed: renaming is what silently de-grounded three rows of 7// the public compare scorecard on the earlier splits (seq689), since matrix rows are verified by an 8// implementing-symbol on disk. connect.matrix's events row was repointed at THIS file in the same change. 9// 10// ADMISSION ORDER IS LOAD-BEARING: membership, then idempotency, then the categorical youth wall, and only 11// THEN capacity. Checking capacity first would let a nearly-full event become the reason a safety rule was 12// skipped. Age is FAIL-CLOSED: a user with no verified band counts as a MINOR, so safety defaults down. 13// RSVP codes: 1 admitted; 2 already-in (idempotent); -1 not a group member; -2 full; -3 two-deep denied; 14// -4 minor on a non-youth event. license_tier: ORIGINAL 15const EV_MAX: i64 = 16 16const RS_MAX: i64 = 128 17const MB_MAX: i64 = 64 18const UA_MAX: i64 = 64 19 20// ctx slots (i64-cast pointers; bundle-into-struct idiom for the >6-arg trap) 21const CX_RSEV: i64 = 0 22const CX_RSUS: i64 = 1 23const CX_RSAC: i64 = 2 24const CX_NRS: i64 = 3 25const CX_EVGRP: i64 = 4 26const CX_EVCAP: i64 = 5 27const CX_EVYTH: i64 = 6 28const CX_MBGRP: i64 = 7 29const CX_MBUS: i64 = 8 30const CX_NMB: i64 = 9 31const CX_UAUS: i64 = 10 32const CX_UAMIN: i64 = 11 33const CX_NUA: i64 = 12 34 35func cxp(ctx: *i64, i: i64) -> *i64 { return ctx[i] as *i64 } 36 37func ev_is_member(ctx: *i64, group: i64, user: i64) -> i64 { 38 let mg: *i64 = cxp(ctx, CX_MBGRP) 39 let mu: *i64 = cxp(ctx, CX_MBUS) 40 let nm: *i64 = cxp(ctx, CX_NMB) 41 var i: i64=0 42 while i<nm[0] { if mg[i]==group { if mu[i]==user { return 1 } } i=i+1 } 43 return 0 44} 45// FAIL-CLOSED age: a user with NO verified band is treated as a MINOR (safety defaults down, not up) 46func ev_is_minor(ctx: *i64, user: i64) -> i64 { 47 let uu: *i64 = cxp(ctx, CX_UAUS) 48 let um: *i64 = cxp(ctx, CX_UAMIN) 49 let nu: *i64 = cxp(ctx, CX_NUA) 50 var i: i64=0 51 while i<nu[0] { if uu[i]==user { return um[i] } i=i+1 } 52 return 1 53} 54func ev_count_active(ctx: *i64, e: i64) -> i64 { 55 let re: *i64 = cxp(ctx, CX_RSEV) 56 let ra: *i64 = cxp(ctx, CX_RSAC) 57 let nr: *i64 = cxp(ctx, CX_NRS) 58 var c: i64=0 59 var i: i64=0 60 while i<nr[0] { if re[i]==e { if ra[i]==1 { c=c+1 } } i=i+1 } 61 return c 62} 63func ev_count_adults(ctx: *i64, e: i64) -> i64 { 64 let re: *i64 = cxp(ctx, CX_RSEV) 65 let ru: *i64 = cxp(ctx, CX_RSUS) 66 let ra: *i64 = cxp(ctx, CX_RSAC) 67 let nr: *i64 = cxp(ctx, CX_NRS) 68 var c: i64=0 69 var i: i64=0 70 while i<nr[0] { 71 if re[i]==e { if ra[i]==1 { if ev_is_minor(ctx, ru[i])==0 { c=c+1 } } } 72 i=i+1 73 } 74 return c 75} 76func ev_count_minors(ctx: *i64, e: i64) -> i64 { 77 let re: *i64 = cxp(ctx, CX_RSEV) 78 let ru: *i64 = cxp(ctx, CX_RSUS) 79 let ra: *i64 = cxp(ctx, CX_RSAC) 80 let nr: *i64 = cxp(ctx, CX_NRS) 81 var c: i64=0 82 var i: i64=0 83 while i<nr[0] { 84 if re[i]==e { if ra[i]==1 { if ev_is_minor(ctx, ru[i])==1 { c=c+1 } } } 85 i=i+1 86 } 87 return c 88} 89func ev_has_active(ctx: *i64, e: i64, user: i64) -> i64 { 90 let re: *i64 = cxp(ctx, CX_RSEV) 91 let ru: *i64 = cxp(ctx, CX_RSUS) 92 let ra: *i64 = cxp(ctx, CX_RSAC) 93 let nr: *i64 = cxp(ctx, CX_NRS) 94 var i: i64=0 95 while i<nr[0] { if re[i]==e { if ru[i]==user { if ra[i]==1 { return 1 } } } i=i+1 } 96 return 0 97} 98// RSVP codes: 1 admitted; 2 already-in (idempotent, roster unchanged); -1 not a group member; 99// -2 event full; -3 two-deep denied (minor while adults<2); -4 minor on a non-youth event. 100func ev_rsvp(ctx: *i64, e: i64, user: i64) -> i64 { 101 let eg: *i64 = cxp(ctx, CX_EVGRP) 102 let ec: *i64 = cxp(ctx, CX_EVCAP) 103 let ey: *i64 = cxp(ctx, CX_EVYTH) 104 if ev_is_member(ctx, eg[e], user)==0 { return 0-1 } 105 if ev_has_active(ctx, e, user)==1 { return 2 } 106 if ev_is_minor(ctx, user)==1 { 107 if ey[e]==0 { return 0-4 } 108 if ev_count_adults(ctx, e) < 2 { return 0-3 } 109 } 110 if ev_count_active(ctx, e) >= ec[e] { return 0-2 } 111 let re: *i64 = cxp(ctx, CX_RSEV) 112 let ru: *i64 = cxp(ctx, CX_RSUS) 113 let ra: *i64 = cxp(ctx, CX_RSAC) 114 let nr: *i64 = cxp(ctx, CX_NRS) 115 let k: i64 = nr[0] 116 re[k]=e; ru[k]=user; ra[k]=1 117 nr[0]=k+1 118 return 1 119} 120func ev_cancel(ctx: *i64, e: i64, user: i64) -> i64 { 121 let re: *i64 = cxp(ctx, CX_RSEV) 122 let ru: *i64 = cxp(ctx, CX_RSUS) 123 let ra: *i64 = cxp(ctx, CX_RSAC) 124 let nr: *i64 = cxp(ctx, CX_NRS) 125 var i: i64=0 126 while i<nr[0] { if re[i]==e { if ru[i]==user { if ra[i]==1 { ra[i]=0; return 1 } } } i=i+1 } 127 return 0 128} 129// a youth event roster is SAFE iff it has no minors, or >=2 adults (recheck applies after cancels) 130func ev_roster_safe(ctx: *i64, e: i64) -> i64 { 131 if ev_count_minors(ctx, e)==0 { return 1 } 132 if ev_count_adults(ctx, e)>=2 { return 1 } 133 return 0 134} 135// NEG-CONTROL incumbent admission (no structural wall): member + capacity ONLY -- any adult/minor mix 136func ev_rsvp_nowall(ctx: *i64, e: i64, user: i64) -> i64 { 137 let eg: *i64 = cxp(ctx, CX_EVGRP) 138 let ec: *i64 = cxp(ctx, CX_EVCAP) 139 if ev_is_member(ctx, eg[e], user)==0 { return 0-1 } 140 if ev_has_active(ctx, e, user)==1 { return 2 } 141 if ev_count_active(ctx, e) >= ec[e] { return 0-2 } 142 let re: *i64 = cxp(ctx, CX_RSEV) 143 let ru: *i64 = cxp(ctx, CX_RSUS) 144 let ra: *i64 = cxp(ctx, CX_RSAC) 145 let nr: *i64 = cxp(ctx, CX_NRS) 146 let k: i64 = nr[0] 147 re[k]=e; ru[k]=user; ra[k]=1 148 nr[0]=k+1 149 return 1 150}