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}