nx_survey_serve.nx source
↩ module page · 2109 lines · 120415 B
1// nx_survey_serve.nx -- the SURVEY/POLL product surface over nx_survey_engine (pure core, no main;
2// the nx_office_serve/nx_relate_serve discipline). A one-question welfare pulse and a deep survey are
3// the SAME app -- surveys are spec DATA loaded through the admin lane, never code.
4// ROUTES (base = URL mount prefix, e.g. "/survey" behind the sovereign edge):
5// GET / open+closed survey list
6// GET /s/<id> the survey form (respondent token from ?t=<tok> or freshly minted; the
7// served app.js keeps one stable token per browser -> revote-replace works)
8// POST /s/<id> record a ballot (engine validates; revote REPLACES by construction)
9// GET /s/<id>/done thanks page
10// GET /s/<id>/results AGGREGATE-ONLY results: counts/means/bars; k-floor (@kmin) withholds
11// below quorum; text answers NEVER render here; @results admin -> 403
12// GET /app.js progressive-enhancement client (token persistence; no-JS still works)
13// POST /admin/load|close|report -- key-file-gated (CWD survey_admin.key; FAIL-CLOSED when absent)
14// POST /admin/insights -- key-gated deep analytics report (cross-tab + chi-square significance, segment
15// means, key drivers, concern flags, open-text themes) over nx_survey_stats
16// PRIVACY BY CONSTRUCTION: anon surveys never emit tokens anywhere public; results are counts only;
17// free text is admin-report only; deep breakdowns that could reveal a small cell are admin-lane only.
18// Same-origin POST guard. license_tier: ORIGINAL
19import "nx_survey_sentiment.nx"
20import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc)
21
22const SV2_MAXB: i64 = 512 // ballots the serve layer aggregates per survey (walk stays LOUD past it)
23const SV2_MAXS: i64 = 64 // surveys listed on home
24// STATIC WALK ARRAYS (2026-08-19): every se_ballots site allocated bk/bv/bl (3 x 4 KB own-VMA) per
25// call and consumed them in-call -- a per-request leak the arena cannot reclaim. One shared trio,
26// lazily mapped, reused: SOUND because every site reads ONE survey's ballots and renders before the
27// next walk (no site holds two ballot walks; sv2_home's surveys list uses the separate sk trio).
28static sv2_bk_g: *i64
29static sv2_bv_g: *i64
30static sv2_bl_g: *i64
31static sv2_sk_g: *i64
32static sv2_sv_g: *i64
33static sv2_sl_g: *i64
34func sv2_barr() -> i64 {
35 if (sv2_bk_g as i64) == 0 {
36 sv2_bk_g = sys_mmap(8 * SV2_MAXB) as *i64
37 sv2_bv_g = sys_mmap(8 * SV2_MAXB) as *i64
38 sv2_bl_g = sys_mmap(8 * SV2_MAXB) as *i64
39 }
40 return 0
41}
42func sv2_sarr() -> i64 {
43 if (sv2_sk_g as i64) == 0 {
44 sv2_sk_g = sys_mmap(8 * SV2_MAXS) as *i64
45 sv2_sv_g = sys_mmap(8 * SV2_MAXS) as *i64
46 sv2_sl_g = sys_mmap(8 * SV2_MAXS) as *i64
47 }
48 return 0
49}
50
51// REQUEST-PATH STATIC (2026-08-19 lane F): sv2_handle allocated its path buffer (SV2_PATHB bytes -- above
52// the 256 B arena line, so one own-VMA PAGE) on EVERY request. That was the measured one-page-per-GET
53// residual (+1,200 kB per 300 GETs in the 08-19 A/B, the only unconditional above-line allocation on the
54// GET path). One lazy static, rewritten per request: SOUND because the daemon serves one request at a
55// time and every request rewrites the token then NUL-terminates before any read.
56const SV2_PATHB: i64 = 400
57static sv2_path_g: *u8
58func sv2_pathb() -> i64 {
59 if (sv2_path_g as i64) == 0 { sv2_path_g = sys_mmap(SV2_PATHB) }
60 return 0
61}
62
63func sv2_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
64// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer
65// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the
66// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls).
67// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign.
68func sv2_pn(v: i64) -> i64 { nxi_out(v); return 0 }
69func sv2_memhas(buf: *u8, n: i64, needle: *u8) -> i64 {
70 let nl: i64 = se_slen(needle)
71 if nl == 0 { return 0 }
72 var i: i64 = 0
73 while i + nl <= n {
74 var k: i64 = 0
75 var hit: i64 = 1
76 while k < nl { if buf[i+k] != needle[k] { hit = 0; k = nl } else { k = k + 1 } }
77 if hit == 1 { return 1 }
78 i = i + 1
79 }
80 return 0
81}
82// escape & < > while appending
83func sv2_esc(dst: *u8, off: i64, s: *u8) -> i64 {
84 var o: i64 = off
85 var i: i64 = 0
86 while s[i] != (0 as u8) {
87 let c: i64 = s[i] as i64
88 if c == 38 { o = se_cat(dst, o, "&" as *u8) } else {
89 if c == 60 { o = se_cat(dst, o, "<" as *u8) } else {
90 if c == 62 { o = se_cat(dst, o, ">" as *u8) } else { dst[o] = s[i]; o = o + 1 } } }
91 i = i + 1
92 }
93 return o
94}
95func sv2_hexval(c: i64) -> i64 {
96 if c >= 48 { if c <= 57 { return c - 48 } }
97 if c >= 65 { if c <= 70 { return c - 55 } }
98 if c >= 97 { if c <= 102 { return c - 87 } }
99 return 0
100}
101// urldecoded form value for key from body (+ and %XX). the proven of_form_get pattern.
102func sv2_form_get(body: *u8, blen: i64, key: *u8, out: *u8, cap: i64) -> i64 {
103 let kl: i64 = se_slen(key)
104 var i: i64 = 0
105 while i < blen {
106 var atk: i64 = 0
107 if i == 0 { atk = 1 } else { if body[i-1] == (38 as u8) { atk = 1 } }
108 if atk == 1 {
109 var m: i64 = 1
110 var j: i64 = 0
111 while j < kl { if i + j >= blen { m = 0; j = kl } else { if body[i+j] != key[j] { m = 0; j = kl } else { j = j + 1 } } }
112 if m == 1 { if i + kl < blen { if body[i+kl] == (61 as u8) {
113 var q: i64 = i + kl + 1
114 var t: i64 = 0
115 var go: i64 = 1
116 while go == 1 {
117 if q >= blen { go = 0 } else {
118 let c: i64 = body[q] as i64
119 if c == 38 { go = 0 } else {
120 var ch: i64 = c
121 if c == 43 { ch = 32 }
122 if c == 37 { if q + 2 < blen { ch = sv2_hexval(body[q+1] as i64) * 16 + sv2_hexval(body[q+2] as i64); q = q + 2 } }
123 if t < cap - 1 { out[t] = ch as u8; t = t + 1 }
124 q = q + 1
125 }
126 }
127 }
128 out[t] = 0 as u8
129 return t
130 } } }
131 }
132 i = i + 1
133 }
134 out[0] = 0 as u8
135 return 0
136}
137func sv2_write_all(fd: i64, buf: *u8, n: i64) -> i64 {
138 var w: i64 = 0
139 while w < n { let k: i64 = sys_write(fd, (buf as i64 + w) as *u8, n - w); if k <= 0 { return 0 - 1 } w = w + k }
140 return 0
141}
142// read ONE full HTTP request: headers until CRLFCRLF plus Content-Length body (proven pattern)
143func sv2_read_req(fd: i64, buf: *u8, cap: i64) -> i64 {
144 var n: i64 = 0
145 var hdr_end: i64 = 0 - 1
146 var want: i64 = 0 - 1
147 var go: i64 = 1
148 while go == 1 {
149 if n >= cap - 1 { go = 0 } else {
150 let r: i64 = sys_read(fd, (buf as i64 + n) as *u8, cap - 1 - n)
151 if r <= 0 { go = 0 } else {
152 n = n + r
153 if hdr_end < 0 {
154 var i: i64 = 0
155 while i + 3 < n {
156 if buf[i] == (13 as u8) { if buf[i+1] == (10 as u8) { if buf[i+2] == (13 as u8) { if buf[i+3] == (10 as u8) { hdr_end = i + 4; i = n } } } }
157 i = i + 1
158 }
159 if hdr_end >= 0 {
160 var cl: i64 = 0 - 1
161 let key: *u8 = "Content-Length:" as *u8
162 let kl: i64 = 15
163 var j: i64 = 0
164 while j + kl < hdr_end {
165 var m: i64 = 1
166 var q: i64 = 0
167 while q < kl { if buf[j+q] != key[q] { m = 0; q = kl } else { q = q + 1 } }
168 if m == 1 {
169 var v: i64 = 0
170 var t: i64 = j + kl
171 while t < hdr_end {
172 let c: i64 = buf[t] as i64
173 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } }
174 if c == 13 { t = hdr_end }
175 t = t + 1
176 }
177 cl = v
178 j = hdr_end
179 }
180 j = j + 1
181 }
182 if cl < 0 { want = hdr_end } else { want = hdr_end + cl }
183 }
184 }
185 if want >= 0 { if n >= want { go = 0 } }
186 }
187 }
188 }
189 return n
190}
191func sv2_read_file(path: *u8, buf: *u8, cap: i64) -> i64 {
192 let fd: i64 = sys_openat_rd(path)
193 if fd < 0 { return 0 - 1 }
194 var tot: i64 = 0
195 while tot < cap { let r: i64 = sys_read(fd, (buf as i64 + tot) as *u8, cap - tot); if r <= 0 { break } tot = tot + r }
196 sys_close(fd)
197 return tot
198}
199func sv2_write_file(path: *u8, buf: *u8, n: i64) -> i64 {
200 let fd: i64 = sys_openat_wr(path, 420)
201 if fd < 0 { return 0 - 1 }
202 let rc: i64 = sv2_write_all(fd, buf, n)
203 sys_close(fd)
204 return rc
205}
206// header value (key like "Origin:") from the header region
207func sv2_hdr_get(req: *u8, reqlen: i64, key: *u8, out: *u8, cap: i64) -> i64 {
208 out[0] = 0 as u8
209 var he: i64 = reqlen
210 var i: i64 = 0
211 while i + 3 < reqlen {
212 if req[i] == (13 as u8) { if req[i+1] == (10 as u8) { if req[i+2] == (13 as u8) { if req[i+3] == (10 as u8) { he = i; i = reqlen } } } }
213 i = i + 1
214 }
215 let kl: i64 = se_slen(key)
216 var j: i64 = 0
217 while j + kl < he {
218 var m: i64 = 1
219 var q: i64 = 0
220 while q < kl { if req[j+q] != key[q] { m = 0; q = kl } else { q = q + 1 } }
221 if m == 1 {
222 var t: i64 = j + kl
223 if req[t] == (32 as u8) { t = t + 1 }
224 var o: i64 = 0
225 while t < he { if req[t] == (13 as u8) { t = he } else { if o < cap - 1 { out[o] = req[t]; o = o + 1 } t = t + 1 } }
226 out[o] = 0 as u8
227 return o
228 }
229 j = j + 1
230 }
231 return 0
232}
233func sv2_err(out: *u8, status: *u8, msg: *u8) -> i64 {
234 var o: i64 = se_cat(out, 0, "HTTP/1.1 " as *u8)
235 o = se_cat(out, o, status)
236 o = se_cat(out, o, "\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nLOUD: " as *u8)
237 o = se_cat(out, o, msg)
238 o = se_catc(out, o, 10)
239 return o
240}
241// fixed-point d.dd from x100 (e.g. 742 -> "7.42")
242func sv2_fixed2(dst: *u8, off: i64, x100: i64) -> i64 {
243 var o: i64 = se_catn(dst, off, x100 / 100)
244 o = se_catc(dst, o, 46)
245 let fr: i64 = x100 % 100
246 o = se_catn(dst, o, fr / 10)
247 o = se_catn(dst, o, fr % 10)
248 return o
249}
250
251// ---- pages ----
252func sv2_css(out: *u8, off: i64) -> i64 {
253 var o: i64 = off
254 o = se_cat(out, o, "<style>*{box-sizing:border-box}body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;background:rgb(248,249,252);color:rgb(24,26,34);max-width:760px;margin:3vh auto;padding:0 16px;line-height:1.5}\n" as *u8)
255 o = se_cat(out, o, "h1{margin:0 0 2px;font-size:1.7rem}.sub{color:rgb(105,110,125);margin:0 0 18px}a{color:rgb(42,77,143)}\n" as *u8)
256 o = se_cat(out, o, ".card{background:rgb(255,255,255);border:1px solid rgb(226,228,236);border-radius:12px;padding:16px 18px;margin:14px 0}\n" as *u8)
257 o = se_cat(out, o, ".k{display:inline-block;padding:1px 10px;border-radius:20px;font-size:.8rem;font-weight:600;color:rgb(255,255,255)}.kopen{background:rgb(26,127,55)}.kclosed{background:rgb(140,144,158)}.kaud{background:rgb(42,77,143)}\n" as *u8)
258 o = se_cat(out, o, "fieldset{border:1px solid rgb(226,228,236);border-radius:10px;margin:12px 0;padding:10px 14px}legend{font-weight:600;padding:0 6px}\n" as *u8)
259 o = se_cat(out, o, ".opt{display:block;padding:7px 10px;border-radius:8px;cursor:pointer}.opt:hover{background:rgb(240,243,250)}.opt input{margin-right:9px}\n" as *u8)
260 o = se_cat(out, o, ".scale{display:flex;flex-wrap:wrap;gap:4px}.scale label{flex:1;min-width:34px;text-align:center;border:1px solid rgb(210,214,226);border-radius:8px;padding:7px 2px;cursor:pointer}.scale input{display:block;margin:0 auto 4px}\n" as *u8)
261 o = se_cat(out, o, "textarea{width:100%;min-height:90px;font-size:.95rem;border:1px solid rgb(210,214,226);border-radius:8px;padding:10px;background:rgb(252,252,254)}\n" as *u8)
262 o = se_cat(out, o, "button{background:rgb(42,77,143);color:rgb(255,255,255);border:0;border-radius:8px;padding:10px 22px;font-size:1rem;font-weight:600;cursor:pointer}\n" as *u8)
263 o = se_cat(out, o, ".hint{color:rgb(105,110,125);font-size:.85rem;margin-top:6px}.foot{margin-top:24px;color:rgb(140,144,158);font-size:.8rem;border-top:1px solid rgb(226,228,236);padding-top:10px}\n" as *u8)
264 o = se_cat(out, o, ".row{display:flex;align-items:center;gap:10px;margin:5px 0}.lbl{flex:0 0 38%;font-size:.92rem}.barw{flex:1;background:rgb(238,241,248);border-radius:6px;height:18px;overflow:hidden}.bar{background:rgb(42,77,143);height:18px}.cnt{flex:0 0 3.2em;text-align:right;font-size:.9rem;color:rgb(105,110,125)}\n" as *u8)
265 o = se_cat(out, o, "@media(prefers-color-scheme:dark){body{background:rgb(15,16,21);color:rgb(228,230,238)}.card{background:rgb(22,24,31);border-color:rgb(40,43,54)}textarea{background:rgb(17,18,24);color:rgb(228,230,238);border-color:rgb(48,52,66)}fieldset{border-color:rgb(40,43,54)}.scale label{border-color:rgb(48,52,66)}.opt:hover{background:rgb(28,30,40)}.barw{background:rgb(28,30,40)}}\n" as *u8)
266 o = se_cat(out, o, "</style>" as *u8)
267 return o
268}
269func sv2_page_top(out: *u8, off: i64, title: *u8) -> i64 {
270 var o: i64 = off
271 o = se_cat(out, o, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<" as *u8)
272 o = se_catc(out, o, 33)
273 o = se_cat(out, o, "DOCTYPE html>\n<html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>" as *u8)
274 o = sv2_esc(out, o, title)
275 o = se_cat(out, o, "</title>" as *u8)
276 o = sv2_css(out, o)
277 o = se_cat(out, o, "</head><body>\n" as *u8)
278 return o
279}
280func sv2_page_foot(out: *u8, off: i64) -> i64 {
281 var o: i64 = off
282 o = se_cat(out, o, "<p class='foot'>Nishi Pulse — sovereign polls & surveys. One respondent = one effective ballot (updates replace, history is never destroyed); anonymous surveys aggregate only, with a k-anonymity floor; written answers go to the survey admin only.</p></body></html>\n" as *u8)
283 return o
284}
285// one home card per survey
286func sv2_home(prefix: *u8, base: *u8, out: *u8, cap: i64) -> i64 {
287 var o: i64 = sv2_page_top(out, 0, "Nishi Pulse - polls and surveys" as *u8)
288 o = se_cat(out, o, "<h1>Nishi Pulse</h1><p class='sub'>Quick one-question polls and deeper surveys — for members, customers and teams.</p>\n" as *u8)
289 sv2_sarr()
290 let sk: *i64 = sv2_sk_g
291 let sv: *i64 = sv2_sv_g
292 let sl: *i64 = sv2_sl_g
293 let n: i64 = se_surveys(prefix, sk, sv, sl, SV2_MAXS)
294 if n <= 0 { o = se_cat(out, o, "<div class='card'><p class='hint'>No surveys yet. The admin lane loads them as spec data.</p></div>" as *u8) }
295 var i: i64 = 0
296 while i < n {
297 let ks: *i64 = sys_mmap(8 * 32) as *i64
298 let vs: *i64 = sys_mmap(8 * 32) as *i64
299 let nf: i64 = canon_decode(sv[i] as *u8, sl[i], ks, vs, 32)
300 if nf > 0 {
301 let id: *u8 = sv_get(ks, vs, nf, "id" as *u8)
302 let title: *u8 = sv_get(ks, vs, nf, "title" as *u8)
303 let stt: *u8 = sv_get(ks, vs, nf, "status" as *u8)
304 let aud: *u8 = sv_get(ks, vs, nf, "audience" as *u8)
305 let nq: i64 = se_field_n(ks, vs, nf, "nq" as *u8, 0)
306 if (id as i64) != 0 { if (title as i64) != 0 { if (stt as i64) != 0 {
307 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 6px'><a href='" as *u8)
308 o = se_cat(out, o, base)
309 o = se_cat(out, o, "/s/" as *u8)
310 o = se_cat(out, o, id)
311 o = se_cat(out, o, "'>" as *u8)
312 o = sv2_esc(out, o, title)
313 o = se_cat(out, o, "</a></h3><p style='margin:0'>" as *u8)
314 var isopen: i64 = se_seq(stt, "open" as *u8)
315 if isopen == 1 { o = se_cat(out, o, "<span class='k kopen'>open</span> " as *u8) } else { o = se_cat(out, o, "<span class='k kclosed'>closed</span> " as *u8) }
316 if (aud as i64) != 0 { o = se_cat(out, o, "<span class='k kaud'>" as *u8); o = sv2_esc(out, o, aud); o = se_cat(out, o, "</span> " as *u8) }
317 o = se_catn(out, o, nq)
318 o = se_cat(out, o, " question(s) · <a href='" as *u8)
319 o = se_cat(out, o, base)
320 o = se_cat(out, o, "/s/" as *u8)
321 o = se_cat(out, o, id)
322 o = se_cat(out, o, "/results'>results</a></p></div>\n" as *u8)
323 } } }
324 }
325 i = i + 1
326 }
327 o = sv2_page_foot(out, o)
328 return o
329}
330// the survey FORM
331func sv2_form(prefix: *u8, base: *u8, id: *u8, tok: *u8, out: *u8, cap: i64) -> i64 {
332 let mks: *i64 = sys_mmap(8 * 32) as *i64
333 let mvs: *i64 = sys_mmap(8 * 32) as *i64
334 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
335 if mnf < 0 { return 0 - 1 }
336 let title: *u8 = sv_get(mks, mvs, mnf, "title" as *u8)
337 let stt: *u8 = sv_get(mks, mvs, mnf, "status" as *u8)
338 let nq: i64 = se_field_n(mks, mvs, mnf, "nq" as *u8, 0)
339 let anon: i64 = se_field_n(mks, mvs, mnf, "anon" as *u8, 1)
340 let maxtext: i64 = se_field_n(mks, mvs, mnf, "maxtext" as *u8, SE_DEF_MAXTEXT)
341 if (title as i64) == 0 { return 0 - 1 }
342 if (stt as i64) == 0 { return 0 - 1 }
343 var o: i64 = sv2_page_top(out, 0, title)
344 o = se_cat(out, o, "<p><a href='" as *u8)
345 o = se_cat(out, o, base)
346 o = se_cat(out, o, "/'>← all surveys</a></p><h1>" as *u8)
347 o = sv2_esc(out, o, title)
348 o = se_cat(out, o, "</h1>" as *u8)
349 if se_seq(stt, "open" as *u8) == 0 {
350 o = se_cat(out, o, "<div class='card'><p><span class='k kclosed'>closed</span> This survey is closed. <a href='" as *u8)
351 o = se_cat(out, o, base)
352 o = se_cat(out, o, "/s/" as *u8)
353 o = se_cat(out, o, id)
354 o = se_cat(out, o, "/results'>See the results</a>.</p></div>" as *u8)
355 o = sv2_page_foot(out, o)
356 return o
357 }
358 // quota: an open survey with a response cap shows progress; when the cap is met, the form is
359 // withheld (no new respondents) and only the results link is offered. se_ballots -1 = the effective
360 // count overflowed a quota-sized buffer, i.e. already full -> fail closed to the full card.
361 let quota: i64 = se_field_n(mks, mvs, mnf, "quota" as *u8, 0)
362 if quota > 0 {
363 let qcap: i64 = quota + 16
364 let bk: *i64 = sys_mmap(8 * qcap) as *i64
365 let bv: *i64 = sys_mmap(8 * qcap) as *i64
366 let bl: *i64 = sys_mmap(8 * qcap) as *i64
367 let nb: i64 = se_ballots(prefix, id, bk, bv, bl, qcap)
368 var full: i64 = 0
369 if nb < 0 { full = 1 }
370 if nb >= quota { full = 1 }
371 if full == 1 {
372 o = se_cat(out, o, "<div class='card'><p><span class='k kclosed'>full</span> This survey has reached its response quota. <a href='" as *u8)
373 o = se_cat(out, o, base)
374 o = se_cat(out, o, "/s/" as *u8)
375 o = se_cat(out, o, id)
376 o = se_cat(out, o, "/results'>See the results</a>.</p></div>" as *u8)
377 o = sv2_page_foot(out, o)
378 return o
379 }
380 o = se_cat(out, o, "<p class='sub'>" as *u8)
381 o = se_catn(out, o, nb)
382 o = se_cat(out, o, " of " as *u8)
383 o = se_catn(out, o, quota)
384 o = se_cat(out, o, " responses received.</p>" as *u8)
385 }
386 if anon == 1 { o = se_cat(out, o, "<p class='sub'>Anonymous — answers are aggregated; nobody sees who said what.</p>" as *u8) } else { o = se_cat(out, o, "<p class='sub'>Responses are linked to your personal link so you can update them later.</p>" as *u8) }
387 o = se_cat(out, o, "<form method='post' action='" as *u8)
388 o = se_cat(out, o, base)
389 o = se_cat(out, o, "/s/" as *u8)
390 o = se_cat(out, o, id)
391 o = se_cat(out, o, "'><input type='hidden' id='tok' name='tok' value='" as *u8)
392 o = se_cat(out, o, tok)
393 o = se_cat(out, o, "'>" as *u8)
394 var q: i64 = 1
395 while q <= nq {
396 let qks: *i64 = sys_mmap(8 * 32) as *i64
397 let qvs: *i64 = sys_mmap(8 * 32) as *i64
398 let qnf: i64 = se_qst(prefix, id, q, qks, qvs, 32)
399 if qnf > 0 {
400 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
401 let kind: *u8 = sv_get(qks, qvs, qnf, "kind" as *u8)
402 let prompt: *u8 = sv_get(qks, qvs, qnf, "prompt" as *u8)
403 if (qid as i64) != 0 { if (kind as i64) != 0 { if (prompt as i64) != 0 {
404 let kc: i64 = kind[0] as i64
405 o = se_cat(out, o, "<fieldset><legend>" as *u8)
406 o = sv2_esc(out, o, prompt)
407 o = se_cat(out, o, "</legend>" as *u8)
408 if kc == 67 {
409 let opts: *u8 = sv_get(qks, qvs, qnf, "opts" as *u8)
410 if (opts as i64) != 0 {
411 let no: i64 = se_nopts(opts)
412 let ob: *u8 = sys_mmap(96)
413 var k: i64 = 0
414 while k < no {
415 se_opt(opts, k, ob, 96)
416 o = se_cat(out, o, "<label class='opt'><input type='radio' name='" as *u8)
417 o = se_cat(out, o, qid)
418 o = se_cat(out, o, "' value='" as *u8)
419 o = se_catn(out, o, k)
420 o = se_cat(out, o, "'>" as *u8)
421 o = sv2_esc(out, o, ob)
422 o = se_cat(out, o, "</label>" as *u8)
423 k = k + 1
424 }
425 }
426 } else { if kc == 83 {
427 let lo: i64 = se_field_n(qks, qvs, qnf, "min" as *u8, 0)
428 let hi: i64 = se_field_n(qks, qvs, qnf, "max" as *u8, 10)
429 o = se_cat(out, o, "<div class='scale'>" as *u8)
430 var v: i64 = lo
431 while v <= hi {
432 o = se_cat(out, o, "<label><input type='radio' name='" as *u8)
433 o = se_cat(out, o, qid)
434 o = se_cat(out, o, "' value='" as *u8)
435 o = se_catn(out, o, v)
436 o = se_cat(out, o, "'>" as *u8)
437 o = se_catn(out, o, v)
438 o = se_cat(out, o, "</label>" as *u8)
439 v = v + 1
440 }
441 o = se_cat(out, o, "</div><p class='hint'>scale " as *u8)
442 o = se_catn(out, o, lo)
443 o = se_cat(out, o, "–" as *u8)
444 o = se_catn(out, o, hi)
445 o = se_cat(out, o, " — the question text says what the ends mean</p>" as *u8)
446 } else {
447 o = se_cat(out, o, "<textarea name='" as *u8)
448 o = se_cat(out, o, qid)
449 o = se_cat(out, o, "' maxlength='" as *u8)
450 o = se_catn(out, o, maxtext)
451 o = se_cat(out, o, "' placeholder='Optional'></textarea>" as *u8)
452 } }
453 o = se_cat(out, o, "</fieldset>" as *u8)
454 } } }
455 }
456 q = q + 1
457 }
458 o = se_cat(out, o, "<p><button type='submit'>Submit</button></p><p class='hint'>Submitting again later updates your previous answer — it is never counted twice.</p></form><script src='" as *u8)
459 o = se_cat(out, o, base)
460 o = se_cat(out, o, "/app.js'></script>" as *u8)
461 o = sv2_page_foot(out, o)
462 return o
463}
464// aggregate-only RESULTS
465func sv2_results(prefix: *u8, base: *u8, id: *u8, out: *u8, cap: i64) -> i64 {
466 let mks: *i64 = sys_mmap(8 * 32) as *i64
467 let mvs: *i64 = sys_mmap(8 * 32) as *i64
468 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
469 if mnf < 0 { return 0 - 1 }
470 let rmode: *u8 = sv_get(mks, mvs, mnf, "results" as *u8)
471 if (rmode as i64) != 0 { if se_seq(rmode, "admin" as *u8) == 1 {
472 return sv2_err(out, "403 Forbidden" as *u8, "results for this survey are admin-only" as *u8)
473 } }
474 let title: *u8 = sv_get(mks, mvs, mnf, "title" as *u8)
475 let stt: *u8 = sv_get(mks, mvs, mnf, "status" as *u8)
476 let nq: i64 = se_field_n(mks, mvs, mnf, "nq" as *u8, 0)
477 let kmin: i64 = se_field_n(mks, mvs, mnf, "kmin" as *u8, 0)
478 if (title as i64) == 0 { return 0 - 1 }
479 sv2_barr()
480 let bk: *i64 = sv2_bk_g
481 let bv: *i64 = sv2_bv_g
482 let bl: *i64 = sv2_bl_g
483 let nb: i64 = se_ballots(prefix, id, bk, bv, bl, SV2_MAXB)
484 if nb < 0 { return sv2_err(out, "500 Internal Server Error" as *u8, "tally walk hit its bound; compact the store" as *u8) }
485 var o: i64 = sv2_page_top(out, 0, title)
486 o = se_cat(out, o, "<p><a href='" as *u8)
487 o = se_cat(out, o, base)
488 o = se_cat(out, o, "/'>← all surveys</a></p><h1>" as *u8)
489 o = sv2_esc(out, o, title)
490 o = se_cat(out, o, "</h1><p class='sub'>Results — aggregate only" as *u8)
491 if (stt as i64) != 0 { if se_seq(stt, "open" as *u8) == 0 { o = se_cat(out, o, " · closed (final)" as *u8) } }
492 o = se_cat(out, o, "</p>" as *u8)
493 if nb < kmin {
494 o = se_cat(out, o, "<div class='card'><p><b>Results withheld.</b> Fewer than " as *u8)
495 o = se_catn(out, o, kmin)
496 o = se_cat(out, o, " people have responded so far; aggregates unlock at the k-anonymity floor so no individual answer can be inferred.</p></div>" as *u8)
497 o = sv2_page_foot(out, o)
498 return o
499 }
500 o = se_cat(out, o, "<div class='card'><p><b>" as *u8)
501 o = se_catn(out, o, nb)
502 o = se_cat(out, o, "</b> response(s).</p></div>" as *u8)
503 var q: i64 = 1
504 while q <= nq {
505 let qks: *i64 = sys_mmap(8 * 32) as *i64
506 let qvs: *i64 = sys_mmap(8 * 32) as *i64
507 let qnf: i64 = se_qst(prefix, id, q, qks, qvs, 32)
508 if qnf > 0 {
509 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
510 let kind: *u8 = sv_get(qks, qvs, qnf, "kind" as *u8)
511 let prompt: *u8 = sv_get(qks, qvs, qnf, "prompt" as *u8)
512 if (qid as i64) != 0 { if (kind as i64) != 0 { if (prompt as i64) != 0 {
513 let kc: i64 = kind[0] as i64
514 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 8px'>" as *u8)
515 o = sv2_esc(out, o, prompt)
516 o = se_cat(out, o, "</h3>" as *u8)
517 if kc == 67 {
518 let opts: *u8 = sv_get(qks, qvs, qnf, "opts" as *u8)
519 if (opts as i64) != 0 {
520 let no: i64 = se_nopts(opts)
521 let ob: *u8 = sys_mmap(96)
522 let vb: *u8 = sys_mmap(16)
523 var ans: i64 = 0
524 var k: i64 = 0
525 while k < no {
526 se_opt(opts, k, ob, 96)
527 se_catn(vb, 0, k)
528 var ve: i64 = 0
529 while vb[ve] != (0 as u8) { ve = ve + 1 }
530 vb[ve] = 0 as u8
531 let c: i64 = se_count_eq(bv, bl, nb, qid, vb)
532 ans = ans + c
533 var pct: i64 = 0
534 if nb > 0 { pct = c * 100 / nb }
535 o = se_cat(out, o, "<div class='row'><span class='lbl'>" as *u8)
536 o = sv2_esc(out, o, ob)
537 o = se_cat(out, o, "</span><span class='barw'><span class='bar' style='width:" as *u8)
538 o = se_catn(out, o, pct)
539 o = se_cat(out, o, "%;display:block'></span></span><span class='cnt'>" as *u8)
540 o = se_catn(out, o, c)
541 o = se_cat(out, o, "</span></div>" as *u8)
542 k = k + 1
543 }
544 if ans > 0 {
545 let mgc: i64 = st_ci_margin(ans / 2, ans)
546 o = se_cat(out, o, "<p style='margin:6px 0 0;color:rgb(105,110,125);font-size:.85rem'>95% margin of error up to ±" as *u8)
547 o = se_catn(out, o, mgc / 10)
548 o = se_cat(out, o, "% at n=" as *u8)
549 o = se_catn(out, o, ans)
550 o = se_cat(out, o, "</p>" as *u8)
551 }
552 }
553 } else { if kc == 83 {
554 let lo: i64 = se_field_n(qks, qvs, qnf, "min" as *u8, 0)
555 let hi: i64 = se_field_n(qks, qvs, qnf, "max" as *u8, 10)
556 let nst: *i64 = sys_mmap(32) as *i64
557 se_num_stats(bv, bl, nb, qid, nst)
558 o = se_cat(out, o, "<p style='margin:0 0 8px'><b>average " as *u8)
559 if nst[0] > 0 { o = sv2_fixed2(out, o, nst[1] * 100 / nst[0]) } else { o = se_cat(out, o, "-" as *u8) }
560 o = se_cat(out, o, "</b> of " as *u8)
561 o = se_catn(out, o, hi)
562 o = se_cat(out, o, " (" as *u8)
563 o = se_catn(out, o, nst[0])
564 o = se_cat(out, o, " answered)</p>" as *u8)
565 // insight strip (whole-sample, privacy-safe): top-2-box + 95% margin + NPS on 0-10 scales
566 let sxs: *i64 = sys_mmap(8 * (nb + 2)) as *i64
567 let sm: i64 = st_collect(bv, bl, nb, qid, sxs)
568 if sm > 0 {
569 let tbpm: i64 = st_topbox(sxs, sm, hi, 2)
570 let tbk: i64 = (tbpm * sm + 500) / 1000
571 let mg: i64 = st_ci_margin(tbk, sm)
572 o = se_cat(out, o, "<p style='margin:0 0 8px;color:rgb(105,110,125);font-size:.9rem'>Top-2-box <b>" as *u8)
573 o = se_catn(out, o, tbpm / 10)
574 o = se_cat(out, o, "%</b> (95% margin ±" as *u8)
575 o = se_catn(out, o, mg / 10)
576 o = se_cat(out, o, "%)" as *u8)
577 if hi == 10 {
578 let npo: *i64 = sys_mmap(8 * 8) as *i64
579 st_nps(sxs, sm, 9, 6, npo)
580 o = se_cat(out, o, " · NPS <b>" as *u8)
581 if npo[0] >= 0 { o = se_cat(out, o, "+" as *u8) }
582 o = se_catn(out, o, npo[0])
583 o = se_cat(out, o, "</b>" as *u8)
584 }
585 o = se_cat(out, o, "</p>" as *u8)
586 }
587 let vb2: *u8 = sys_mmap(16)
588 var v: i64 = lo
589 while v <= hi {
590 se_catn(vb2, 0, v)
591 var v2e: i64 = 0
592 while vb2[v2e] != (0 as u8) { v2e = v2e + 1 }
593 vb2[v2e] = 0 as u8
594 let c2: i64 = se_count_eq(bv, bl, nb, qid, vb2)
595 var pct2: i64 = 0
596 if nst[0] > 0 { pct2 = c2 * 100 / nst[0] }
597 o = se_cat(out, o, "<div class='row'><span class='lbl'>" as *u8)
598 o = se_catn(out, o, v)
599 o = se_cat(out, o, "</span><span class='barw'><span class='bar' style='width:" as *u8)
600 o = se_catn(out, o, pct2)
601 o = se_cat(out, o, "%;display:block'></span></span><span class='cnt'>" as *u8)
602 o = se_catn(out, o, c2)
603 o = se_cat(out, o, "</span></div>" as *u8)
604 v = v + 1
605 }
606 } else {
607 let tc: i64 = se_text_count(bv, bl, nb, qid)
608 o = se_cat(out, o, "<p style='margin:0'>" as *u8)
609 o = se_catn(out, o, tc)
610 o = se_cat(out, o, " written answer(s) — visible to the survey admin only (privacy by construction).</p>" as *u8)
611 } }
612 o = se_cat(out, o, "</div>" as *u8)
613 } } }
614 }
615 q = q + 1
616 }
617 // embeddable poll snippet -- surface the capability (tools-surface-in-UI). The first question renders as a
618 // compact inline poll on any external site via an iframe pointing at /embed/<id>.
619 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 6px'>Embed this poll</h3><p class='hint'>Drop the first question onto any page (your ward site, the tandem app) as a live one-question poll:</p><textarea readonly style='min-height:70px'>" as *u8)
620 o = sv2_esc(out, o, "<iframe src='" as *u8)
621 o = se_cat(out, o, "https://nishifamily.com" as *u8)
622 o = sv2_esc(out, o, base)
623 o = sv2_esc(out, o, "/embed/" as *u8)
624 o = sv2_esc(out, o, id)
625 o = sv2_esc(out, o, "' style='width:100%;max-width:420px;height:260px;border:1px solid rgb(226,228,236);border-radius:10px' title='Poll'></iframe>" as *u8)
626 o = se_cat(out, o, "</textarea></div>" as *u8)
627 o = sv2_page_foot(out, o)
628 return o
629}
630func sv2_done(base: *u8, id: *u8, out: *u8) -> i64 {
631 var o: i64 = sv2_page_top(out, 0, "Thank you" as *u8)
632 o = se_cat(out, o, "<div class='card'><h1>Thank you.</h1><p>Your response is recorded. Submitting again from this device updates it — it is never counted twice.</p><p><a href='" as *u8)
633 o = se_cat(out, o, base)
634 o = se_cat(out, o, "/s/" as *u8)
635 o = se_cat(out, o, id)
636 o = se_cat(out, o, "/results'>See the results</a> · <a href='" as *u8)
637 o = se_cat(out, o, base)
638 o = se_cat(out, o, "/'>all surveys</a></p></div>" as *u8)
639 o = sv2_page_foot(out, o)
640 return o
641}
642// admin key check: keypath file must exist, be >=8 chars trimmed, and equal the presented key. FAIL-CLOSED.
643func sv2_admin_ok(keypath: *u8, presented: *u8) -> i64 {
644 let kb: *u8 = sys_mmap(256)
645 var n: i64 = sv2_read_file(keypath, kb, 255)
646 if n < 8 { return 0 }
647 while n > 0 {
648 let c: i64 = kb[n-1] as i64
649 var ws: i64 = 0
650 if c == 10 { ws = 1 }
651 if c == 13 { ws = 1 }
652 if c == 32 { ws = 1 }
653 if c == 9 { ws = 1 }
654 if ws == 1 { n = n - 1 } else { break }
655 }
656 if n < 8 { return 0 }
657 kb[n] = 0 as u8
658 if se_slen(presented) != n { return 0 }
659 return se_seq(kb, presented)
660}
661// admin plain-text REPORT (counts + the text answers the public page withholds)
662func sv2_report(prefix: *u8, id: *u8, out: *u8, cap: i64) -> i64 {
663 let mks: *i64 = sys_mmap(8 * 32) as *i64
664 let mvs: *i64 = sys_mmap(8 * 32) as *i64
665 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
666 if mnf < 0 { return 0 - 1 }
667 let title: *u8 = sv_get(mks, mvs, mnf, "title" as *u8)
668 let stt: *u8 = sv_get(mks, mvs, mnf, "status" as *u8)
669 let nq: i64 = se_field_n(mks, mvs, mnf, "nq" as *u8, 0)
670 let anon: i64 = se_field_n(mks, mvs, mnf, "anon" as *u8, 1)
671 sv2_barr()
672 let bk: *i64 = sv2_bk_g
673 let bv: *i64 = sv2_bv_g
674 let bl: *i64 = sv2_bl_g
675 let nb: i64 = se_ballots(prefix, id, bk, bv, bl, SV2_MAXB)
676 if nb < 0 { return sv2_err(out, "500 Internal Server Error" as *u8, "tally walk hit its bound" as *u8) }
677 var o: i64 = se_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nSURVEY-REPORT " as *u8)
678 o = se_cat(out, o, id)
679 o = se_cat(out, o, "\ntitle: " as *u8)
680 if (title as i64) != 0 { o = se_cat(out, o, title) }
681 o = se_cat(out, o, "\nstatus: " as *u8)
682 if (stt as i64) != 0 { o = se_cat(out, o, stt) }
683 o = se_cat(out, o, "\nresponses: " as *u8)
684 o = se_catn(out, o, nb)
685 o = se_catc(out, o, 10)
686 var q: i64 = 1
687 while q <= nq {
688 let qks: *i64 = sys_mmap(8 * 32) as *i64
689 let qvs: *i64 = sys_mmap(8 * 32) as *i64
690 let qnf: i64 = se_qst(prefix, id, q, qks, qvs, 32)
691 if qnf > 0 {
692 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
693 let kind: *u8 = sv_get(qks, qvs, qnf, "kind" as *u8)
694 let prompt: *u8 = sv_get(qks, qvs, qnf, "prompt" as *u8)
695 if (qid as i64) != 0 { if (kind as i64) != 0 { if (prompt as i64) != 0 {
696 let kc: i64 = kind[0] as i64
697 o = se_cat(out, o, "\nQ " as *u8)
698 o = se_cat(out, o, qid)
699 o = se_cat(out, o, ": " as *u8)
700 o = se_cat(out, o, prompt)
701 o = se_catc(out, o, 10)
702 if kc == 67 {
703 let opts: *u8 = sv_get(qks, qvs, qnf, "opts" as *u8)
704 if (opts as i64) != 0 {
705 let no: i64 = se_nopts(opts)
706 let ob: *u8 = sys_mmap(96)
707 let vb: *u8 = sys_mmap(16)
708 var k: i64 = 0
709 while k < no {
710 se_opt(opts, k, ob, 96)
711 se_catn(vb, 0, k)
712 var ve: i64 = 0
713 while vb[ve] != (0 as u8) { ve = ve + 1 }
714 vb[ve] = 0 as u8
715 o = se_cat(out, o, " " as *u8)
716 o = se_cat(out, o, ob)
717 o = se_cat(out, o, ": " as *u8)
718 o = se_catn(out, o, se_count_eq(bv, bl, nb, qid, vb))
719 o = se_catc(out, o, 10)
720 k = k + 1
721 }
722 }
723 } else { if kc == 83 {
724 let nst: *i64 = sys_mmap(32) as *i64
725 se_num_stats(bv, bl, nb, qid, nst)
726 o = se_cat(out, o, " n=" as *u8)
727 o = se_catn(out, o, nst[0])
728 o = se_cat(out, o, " sum=" as *u8)
729 o = se_catn(out, o, nst[1])
730 o = se_cat(out, o, " avg=" as *u8)
731 if nst[0] > 0 { o = sv2_fixed2(out, o, nst[1] * 100 / nst[0]) } else { o = se_cat(out, o, "-" as *u8) }
732 o = se_catc(out, o, 10)
733 } else {
734 let ab: *u8 = sys_mmap(4096)
735 var i: i64 = 0
736 while i < nb {
737 if o > cap - 8192 { o = se_cat(out, o, " ... (truncated)\n" as *u8); i = nb } else {
738 if se_answer_of(bv[i] as *u8, bl[i], qid, ab, 4096) > 0 {
739 o = se_cat(out, o, " - " as *u8)
740 if anon == 0 {
741 o = se_catc(out, o, 91)
742 o = se_cat(out, o, bk[i] as *u8)
743 o = se_catc(out, o, 93)
744 o = se_catc(out, o, 32)
745 }
746 o = se_cat(out, o, ab)
747 o = se_catc(out, o, 10)
748 }
749 i = i + 1
750 }
751 }
752 } }
753 } } }
754 }
755 q = q + 1
756 }
757 return o
758}
759// serve the progressive-enhancement client from CWD (JS lives in a FILE -- the nx_cc literal trap)
760// ---- insights sub-renderers (one per section; kept separate so no single function blows the nx_cc frame
761// limit, and single-responsibility). Each takes the running offset o and returns the new o. ----
762// A+B: scale descriptive table + concern flags (bottom-2-box = who needs follow-up)
763func sv2_ins_scale(out: *u8, o0: i64, bv: *i64, bl: *i64, nb: i64, qkind: *i64, qidp: *i64, qprompt: *i64, qmax: *i64, qmin: *i64, nqg: i64) -> i64 {
764 let xs: *i64 = sys_mmap(8 * (nb + 4)) as *i64
765 let dd: *i64 = sys_mmap(8 * 8) as *i64
766 let npo: *i64 = sys_mmap(8 * 8) as *i64
767 var o: i64 = se_cat(out, o0, "<div class='card'><h3 style='margin:0 0 8px'>Scale summary</h3><table><thead><tr><th>Question</th><th>n</th><th>Mean</th><th>Median</th><th>Std dev</th><th>Top-2-box</th><th>NPS</th></tr></thead><tbody>" as *u8)
768 var i: i64 = 0
769 while i < nqg {
770 if qkind[i] == 83 {
771 let m: i64 = st_collect(bv, bl, nb, qidp[i] as *u8, xs)
772 o = se_cat(out, o, "<tr><td>" as *u8)
773 o = sv2_esc(out, o, qprompt[i] as *u8)
774 o = se_cat(out, o, "</td><td>" as *u8)
775 o = se_catn(out, o, m)
776 if m > 0 {
777 st_desc(xs, m, dd)
778 o = se_cat(out, o, "</td><td>" as *u8)
779 o = sv2_fixed2(out, o, dd[3] / 10)
780 o = se_cat(out, o, "</td><td>" as *u8)
781 o = sv2_fixed2(out, o, dd[4] / 10)
782 o = se_cat(out, o, "</td><td>" as *u8)
783 o = sv2_fixed2(out, o, dd[5] / 10)
784 o = se_cat(out, o, "</td><td>" as *u8)
785 o = se_catn(out, o, st_topbox(xs, m, qmax[i], 2) / 10)
786 o = se_cat(out, o, "%</td><td>" as *u8)
787 if qmax[i] == 10 {
788 st_nps(xs, m, 9, 6, npo)
789 if npo[0] >= 0 { o = se_cat(out, o, "+" as *u8) }
790 o = se_catn(out, o, npo[0])
791 } else { o = se_cat(out, o, "—" as *u8) }
792 o = se_cat(out, o, "</td></tr>" as *u8)
793 } else { o = se_cat(out, o, "</td><td>—</td><td>—</td><td>—</td><td>—</td><td>—</td></tr>" as *u8) }
794 }
795 i = i + 1
796 }
797 o = se_cat(out, o, "</tbody></table><p class='hint'>NPS is shown beside the full distribution, mean and margin — its predictive validity is actively studied and criticized in the banked 2024-26 literature; never read it alone (evidence gate).</p></div>" as *u8)
798 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 8px'>Concern flags (bottom-2-box)</h3><table><thead><tr><th>Question</th><th>In need</th><th>Share</th></tr></thead><tbody>" as *u8)
799 i = 0
800 while i < nqg {
801 if qkind[i] == 83 {
802 let m: i64 = st_collect(bv, bl, nb, qidp[i] as *u8, xs)
803 if m > 0 {
804 let thr: i64 = qmin[i] + 1
805 var cc: i64 = 0
806 var j: i64 = 0
807 while j < m { if xs[j] <= thr { cc = cc + 1 } j = j + 1 }
808 o = se_cat(out, o, "<tr><td>" as *u8)
809 o = sv2_esc(out, o, qprompt[i] as *u8)
810 o = se_cat(out, o, "</td><td><b>" as *u8)
811 o = se_catn(out, o, cc)
812 o = se_cat(out, o, "</b> of " as *u8)
813 o = se_catn(out, o, m)
814 o = se_cat(out, o, "</td><td>" as *u8)
815 o = se_catn(out, o, cc * 100 / m)
816 o = se_cat(out, o, "%</td></tr>" as *u8)
817 }
818 }
819 i = i + 1
820 }
821 o = se_cat(out, o, "</tbody></table><p class='hint'>Bottom-2-box = respondents at or near the low end — the follow-up list.</p></div>" as *u8)
822 return o
823}
824// C: segment breakdowns -- mean of each scale within each level of each choice question
825func sv2_ins_seg(out: *u8, o0: i64, bv: *i64, bl: *i64, nb: i64, qkind: *i64, qidp: *i64, qprompt: *i64, qopts: *i64, qnopt: *i64, nqg: i64) -> i64 {
826 var o: i64 = o0
827 let sn: *i64 = sys_mmap(8 * 16) as *i64
828 let smp: *i64 = sys_mmap(8 * 16) as *i64
829 let ob: *u8 = sys_mmap(96)
830 var si: i64 = 0
831 while si < nqg {
832 if qkind[si] == 67 {
833 var ti: i64 = 0
834 while ti < nqg {
835 if qkind[ti] == 83 {
836 let segn: i64 = qnopt[si]
837 if segn > 0 { if segn <= 12 {
838 st_segment_means(bv, bl, nb, qidp[si] as *u8, qidp[ti] as *u8, segn, sn, smp)
839 var levels: i64 = 0
840 var g: i64 = 0
841 while g < segn { if sn[g] > 0 { levels = levels + 1 } g = g + 1 }
842 if levels >= 2 {
843 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 8px'>" as *u8)
844 o = sv2_esc(out, o, qprompt[ti] as *u8)
845 o = se_cat(out, o, " — by " as *u8)
846 o = sv2_esc(out, o, qprompt[si] as *u8)
847 o = se_cat(out, o, "</h3><table><thead><tr><th>Group</th><th>n</th><th>Mean</th></tr></thead><tbody>" as *u8)
848 g = 0
849 while g < segn {
850 se_opt(qopts[si] as *u8, g, ob, 96)
851 o = se_cat(out, o, "<tr><td>" as *u8)
852 o = sv2_esc(out, o, ob)
853 o = se_cat(out, o, "</td><td>" as *u8)
854 o = se_catn(out, o, sn[g])
855 o = se_cat(out, o, "</td><td>" as *u8)
856 if sn[g] > 0 { o = sv2_fixed2(out, o, smp[g] / 10) } else { o = se_cat(out, o, "—" as *u8) }
857 o = se_cat(out, o, "</td></tr>" as *u8)
858 g = g + 1
859 }
860 o = se_cat(out, o, "</tbody></table></div>" as *u8)
861 }
862 } }
863 }
864 ti = ti + 1
865 }
866 }
867 si = si + 1
868 }
869 return o
870}
871// D: cross-tabs + chi-square significance for each pair of choice questions
872func sv2_ins_xtab(out: *u8, o0: i64, bv: *i64, bl: *i64, nb: i64, qkind: *i64, qidp: *i64, qprompt: *i64, qopts: *i64, qnopt: *i64, nqg: i64) -> i64 {
873 var o: i64 = o0
874 let ctab: *i64 = sys_mmap(8 * 200) as *i64
875 let cco: *i64 = sys_mmap(8 * 4) as *i64
876 let obc: *u8 = sys_mmap(96)
877 var a: i64 = 0
878 while a < nqg {
879 if qkind[a] == 67 {
880 var b: i64 = a + 1
881 while b < nqg {
882 if qkind[b] == 67 {
883 let ra: i64 = qnopt[a]
884 let cb: i64 = qnopt[b]
885 if ra > 0 { if cb > 0 { if ra <= 12 { if cb <= 12 {
886 let grand: i64 = st_xtab(bv, bl, nb, qidp[a] as *u8, qidp[b] as *u8, ra, cb, ctab)
887 if grand > 0 {
888 let sig: i64 = st_chisq(ctab, ra, cb, cco)
889 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 8px'>" as *u8)
890 o = sv2_esc(out, o, qprompt[a] as *u8)
891 o = se_cat(out, o, " × " as *u8)
892 o = sv2_esc(out, o, qprompt[b] as *u8)
893 o = se_cat(out, o, "</h3><table><thead><tr><th></th>" as *u8)
894 var cj: i64 = 0
895 while cj < cb { se_opt(qopts[b] as *u8, cj, obc, 96); o = se_cat(out, o, "<th>" as *u8); o = sv2_esc(out, o, obc); o = se_cat(out, o, "</th>" as *u8); cj = cj + 1 }
896 o = se_cat(out, o, "</tr></thead><tbody>" as *u8)
897 var ri: i64 = 0
898 while ri < ra {
899 se_opt(qopts[a] as *u8, ri, obc, 96)
900 o = se_cat(out, o, "<tr><td><b>" as *u8)
901 o = sv2_esc(out, o, obc)
902 o = se_cat(out, o, "</b></td>" as *u8)
903 cj = 0
904 while cj < cb { o = se_cat(out, o, "<td>" as *u8); o = se_catn(out, o, ctab[ri * cb + cj]); o = se_cat(out, o, "</td>" as *u8); cj = cj + 1 }
905 o = se_cat(out, o, "</tr>" as *u8)
906 ri = ri + 1
907 }
908 o = se_cat(out, o, "</tbody></table><p style='margin:8px 0 0'>chi-square " as *u8)
909 o = sv2_fixed2(out, o, cco[0] / 10)
910 o = se_cat(out, o, ", df " as *u8)
911 o = se_catn(out, o, cco[1])
912 o = se_cat(out, o, " — " as *u8)
913 if sig == 1 { o = se_cat(out, o, "<b>significant</b> association (p < .05)" as *u8) } else { o = se_cat(out, o, "not statistically significant at .05" as *u8) }
914 o = se_cat(out, o, "</p></div>" as *u8)
915 }
916 } } } }
917 }
918 b = b + 1
919 }
920 }
921 a = a + 1
922 }
923 return o
924}
925// ---- machine-readable results JSON (aggregate-only, k-floored, NO free text; all-integer permille, no float) ----
926func j_q(out: *u8, o: i64) -> i64 { out[o] = 34 as u8; return o + 1 }
927func j_key(out: *u8, o: i64, k: *u8) -> i64 { var oo: i64 = j_q(out, o); oo = se_cat(out, oo, k); oo = j_q(out, oo); out[oo] = 58 as u8; return oo + 1 }
928func j_str(out: *u8, o: i64, s: *u8) -> i64 {
929 var oo: i64 = j_q(out, o)
930 var i: i64 = 0
931 while s[i] != (0 as u8) {
932 let c: i64 = s[i] as i64
933 if c == 34 { out[oo] = 92 as u8; oo = oo + 1; out[oo] = 34 as u8; oo = oo + 1 } else {
934 if c == 92 { out[oo] = 92 as u8; oo = oo + 1; out[oo] = 92 as u8; oo = oo + 1 } else {
935 if c < 32 { out[oo] = 32 as u8; oo = oo + 1 } else { out[oo] = s[i]; oo = oo + 1 } } }
936 i = i + 1
937 }
938 return j_q(out, oo)
939}
940func sv2_results_json(prefix: *u8, id: *u8, out: *u8, cap: i64) -> i64 {
941 let mks: *i64 = sys_mmap(8 * 32) as *i64
942 let mvs: *i64 = sys_mmap(8 * 32) as *i64
943 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
944 if mnf < 0 { return 0 - 1 }
945 let rmode: *u8 = sv_get(mks, mvs, mnf, "results" as *u8)
946 if (rmode as i64) != 0 { if se_seq(rmode, "admin" as *u8) == 1 {
947 var eo: i64 = se_cat(out, 0, "HTTP/1.1 403 Forbidden\r\nContent-Type: application/json; charset=utf-8\r\nAccess-Control-Allow-Origin: *\r\n\r\n{" as *u8)
948 eo = j_key(out, eo, "error" as *u8)
949 eo = j_str(out, eo, "results are admin-only for this survey" as *u8)
950 out[eo] = 125 as u8
951 return eo + 1
952 } }
953 let title: *u8 = sv_get(mks, mvs, mnf, "title" as *u8)
954 let status: *u8 = sv_get(mks, mvs, mnf, "status" as *u8)
955 let nq: i64 = se_field_n(mks, mvs, mnf, "nq" as *u8, 0)
956 let kmin: i64 = se_field_n(mks, mvs, mnf, "kmin" as *u8, 0)
957 sv2_barr()
958 let bk: *i64 = sv2_bk_g
959 let bv: *i64 = sv2_bv_g
960 let bl: *i64 = sv2_bl_g
961 let nb: i64 = se_ballots(prefix, id, bk, bv, bl, SV2_MAXB)
962 if nb < 0 { return sv2_err(out, "500 Internal Server Error" as *u8, "tally walk hit its bound" as *u8) }
963 var o: i64 = se_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=utf-8\r\nAccess-Control-Allow-Origin: *\r\n\r\n{" as *u8)
964 o = j_key(out, o, "v" as *u8); o = se_cat(out, o, "1," as *u8)
965 o = j_key(out, o, "survey" as *u8); o = j_str(out, o, id); o = se_cat(out, o, "," as *u8)
966 o = j_key(out, o, "title" as *u8); if (title as i64) != 0 { o = j_str(out, o, title) } else { o = se_cat(out, o, "\"\"" as *u8) } o = se_cat(out, o, "," as *u8)
967 o = j_key(out, o, "status" as *u8); if (status as i64) != 0 { o = j_str(out, o, status) } else { o = se_cat(out, o, "\"open\"" as *u8) } o = se_cat(out, o, "," as *u8)
968 o = j_key(out, o, "responses" as *u8); o = se_catn(out, o, nb); o = se_cat(out, o, "," as *u8)
969 o = j_key(out, o, "note" as *u8); o = j_str(out, o, "aggregate-only, integer permille, free text excluded by design" as *u8); o = se_cat(out, o, "," as *u8)
970 if nb < kmin {
971 o = j_key(out, o, "withheld" as *u8); o = se_cat(out, o, "true," as *u8)
972 o = j_key(out, o, "kmin" as *u8); o = se_catn(out, o, kmin)
973 out[o] = 125 as u8
974 return o + 1
975 }
976 o = j_key(out, o, "withheld" as *u8); o = se_cat(out, o, "false," as *u8)
977 o = j_key(out, o, "questions" as *u8); o = se_cat(out, o, "[" as *u8)
978 let xs: *i64 = sys_mmap(8 * (nb + 4)) as *i64
979 let dd: *i64 = sys_mmap(8 * 8) as *i64
980 let npo: *i64 = sys_mmap(8 * 8) as *i64
981 var q: i64 = 1
982 var emitted: i64 = 0
983 while q <= nq {
984 let qks: *i64 = sys_mmap(8 * 32) as *i64
985 let qvs: *i64 = sys_mmap(8 * 32) as *i64
986 let qnf: i64 = se_qst(prefix, id, q, qks, qvs, 32)
987 if qnf > 0 {
988 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
989 let kind: *u8 = sv_get(qks, qvs, qnf, "kind" as *u8)
990 let prompt: *u8 = sv_get(qks, qvs, qnf, "prompt" as *u8)
991 if (qid as i64) != 0 { if (kind as i64) != 0 { if (prompt as i64) != 0 {
992 let kc: i64 = kind[0] as i64
993 if emitted == 1 { o = se_cat(out, o, "," as *u8) }
994 emitted = 1
995 o = se_cat(out, o, "{" as *u8)
996 o = j_key(out, o, "qid" as *u8); o = j_str(out, o, qid); o = se_cat(out, o, "," as *u8)
997 o = j_key(out, o, "kind" as *u8); o = j_str(out, o, kind); o = se_cat(out, o, "," as *u8)
998 o = j_key(out, o, "prompt" as *u8); o = j_str(out, o, prompt); o = se_cat(out, o, "," as *u8)
999 if kc == 67 {
1000 let opts: *u8 = sv_get(qks, qvs, qnf, "opts" as *u8)
1001 let no: i64 = se_nopts(opts)
1002 let ob: *u8 = sys_mmap(96)
1003 let vb: *u8 = sys_mmap(16)
1004 var ans: i64 = 0
1005 var k: i64 = 0
1006 while k < no { se_catn(vb, 0, k); var ve: i64 = 0; while vb[ve] != (0 as u8) { ve = ve + 1 } vb[ve] = 0 as u8; ans = ans + se_count_eq(bv, bl, nb, qid, vb); k = k + 1 }
1007 o = j_key(out, o, "n" as *u8); o = se_catn(out, o, ans); o = se_cat(out, o, "," as *u8)
1008 o = j_key(out, o, "options" as *u8); o = se_cat(out, o, "[" as *u8)
1009 k = 0
1010 while k < no {
1011 se_opt(opts, k, ob, 96)
1012 se_catn(vb, 0, k)
1013 var ve2: i64 = 0
1014 while vb[ve2] != (0 as u8) { ve2 = ve2 + 1 }
1015 vb[ve2] = 0 as u8
1016 let c: i64 = se_count_eq(bv, bl, nb, qid, vb)
1017 var pct: i64 = 0
1018 if ans > 0 { pct = c * 1000 / ans }
1019 if k > 0 { o = se_cat(out, o, "," as *u8) }
1020 o = se_cat(out, o, "{" as *u8)
1021 o = j_key(out, o, "label" as *u8); o = j_str(out, o, ob); o = se_cat(out, o, "," as *u8)
1022 o = j_key(out, o, "count" as *u8); o = se_catn(out, o, c); o = se_cat(out, o, "," as *u8)
1023 o = j_key(out, o, "pct_permille" as *u8); o = se_catn(out, o, pct)
1024 o = se_cat(out, o, "}" as *u8)
1025 k = k + 1
1026 }
1027 o = se_cat(out, o, "]" as *u8)
1028 } else { if kc == 83 {
1029 let lo: i64 = se_field_n(qks, qvs, qnf, "min" as *u8, 0)
1030 let hi: i64 = se_field_n(qks, qvs, qnf, "max" as *u8, 10)
1031 let m: i64 = st_collect(bv, bl, nb, qid, xs)
1032 o = j_key(out, o, "n" as *u8); o = se_catn(out, o, m); o = se_cat(out, o, "," as *u8)
1033 st_desc(xs, m, dd)
1034 o = j_key(out, o, "mean_permille" as *u8); o = se_catn(out, o, dd[3]); o = se_cat(out, o, "," as *u8)
1035 o = j_key(out, o, "median_permille" as *u8); o = se_catn(out, o, dd[4]); o = se_cat(out, o, "," as *u8)
1036 o = j_key(out, o, "stddev_permille" as *u8); o = se_catn(out, o, dd[5]); o = se_cat(out, o, "," as *u8)
1037 let tbpm: i64 = st_topbox(xs, m, hi, 2)
1038 let tbk: i64 = (tbpm * m + 500) / 1000
1039 o = j_key(out, o, "top2box_permille" as *u8); o = se_catn(out, o, tbpm); o = se_cat(out, o, "," as *u8)
1040 o = j_key(out, o, "margin_permille" as *u8); o = se_catn(out, o, st_ci_margin(tbk, m)); o = se_cat(out, o, "," as *u8)
1041 if hi == 10 { st_nps(xs, m, 9, 6, npo); o = j_key(out, o, "nps" as *u8); o = se_catn(out, o, npo[0]); o = se_cat(out, o, "," as *u8) }
1042 o = j_key(out, o, "dist" as *u8); o = se_cat(out, o, "[" as *u8)
1043 let vb3: *u8 = sys_mmap(16)
1044 var v: i64 = lo
1045 while v <= hi {
1046 se_catn(vb3, 0, v)
1047 var v3e: i64 = 0
1048 while vb3[v3e] != (0 as u8) { v3e = v3e + 1 }
1049 vb3[v3e] = 0 as u8
1050 if v > lo { o = se_cat(out, o, "," as *u8) }
1051 o = se_cat(out, o, "{" as *u8)
1052 o = j_key(out, o, "value" as *u8); o = se_catn(out, o, v); o = se_cat(out, o, "," as *u8)
1053 o = j_key(out, o, "count" as *u8); o = se_catn(out, o, se_count_eq(bv, bl, nb, qid, vb3))
1054 o = se_cat(out, o, "}" as *u8)
1055 v = v + 1
1056 }
1057 o = se_cat(out, o, "]" as *u8)
1058 } else {
1059 o = j_key(out, o, "text_count" as *u8); o = se_catn(out, o, se_text_count(bv, bl, nb, qid))
1060 } }
1061 o = se_cat(out, o, "}" as *u8)
1062 } } }
1063 }
1064 q = q + 1
1065 }
1066 o = se_cat(out, o, "]}" as *u8)
1067 return o
1068}
1069// E: key drivers -- correlation of each scale to the outcome (last scale question), ranked
1070func sv2_ins_drivers(out: *u8, o0: i64, bv: *i64, bl: *i64, nb: i64, qkind: *i64, qidp: *i64, qprompt: *i64, nqg: i64) -> i64 {
1071 var o: i64 = o0
1072 let xs: *i64 = sys_mmap(8 * (nb + 4)) as *i64
1073 let ys: *i64 = sys_mmap(8 * (nb + 4)) as *i64
1074 var outc: i64 = 0 - 1
1075 var i: i64 = 0
1076 while i < nqg { if qkind[i] == 83 { outc = i } i = i + 1 }
1077 if outc < 0 { return o }
1078 let dvi: *i64 = sys_mmap(8 * 32) as *i64
1079 let dvr: *i64 = sys_mmap(8 * 32) as *i64
1080 var nd: i64 = 0
1081 i = 0
1082 while i < nqg {
1083 if qkind[i] == 83 { if i != outc {
1084 let m: i64 = st_collect_pairs(bv, bl, nb, qidp[i] as *u8, qidp[outc] as *u8, xs, ys)
1085 if m >= 2 { dvi[nd] = i; dvr[nd] = st_corr(xs, ys, m); nd = nd + 1 }
1086 } }
1087 i = i + 1
1088 }
1089 if nd <= 0 { return o }
1090 var p: i64 = 0
1091 while p < nd {
1092 var best: i64 = p
1093 var t: i64 = p + 1
1094 while t < nd {
1095 var ar: i64 = dvr[t]
1096 if ar < 0 { ar = 0 - ar }
1097 var ab2: i64 = dvr[best]
1098 if ab2 < 0 { ab2 = 0 - ab2 }
1099 if ar > ab2 { best = t }
1100 t = t + 1
1101 }
1102 let ti2: i64 = dvi[p]; dvi[p] = dvi[best]; dvi[best] = ti2
1103 let tr2: i64 = dvr[p]; dvr[p] = dvr[best]; dvr[best] = tr2
1104 p = p + 1
1105 }
1106 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 8px'>Key drivers of “" as *u8)
1107 o = sv2_esc(out, o, qprompt[outc] as *u8)
1108 o = se_cat(out, o, "”</h3><table><thead><tr><th>Factor</th><th>Correlation</th></tr></thead><tbody>" as *u8)
1109 p = 0
1110 while p < nd {
1111 o = se_cat(out, o, "<tr><td>" as *u8)
1112 o = sv2_esc(out, o, qprompt[dvi[p]] as *u8)
1113 o = se_cat(out, o, "</td><td>" as *u8)
1114 if dvr[p] >= 0 { o = se_cat(out, o, "+" as *u8) }
1115 o = sv2_fixed2(out, o, dvr[p] / 10)
1116 o = se_cat(out, o, "</td></tr>" as *u8)
1117 p = p + 1
1118 }
1119 o = se_cat(out, o, "</tbody></table><p class='hint'>Pearson correlation (−1.00 to +1.00); the factors that move with the outcome. Correlation is not causation.</p></div>" as *u8)
1120 return o
1121}
1122// F: open-text themes (sovereign frequency)
1123func sv2_ins_themes(out: *u8, o0: i64, bv: *i64, bl: *i64, nb: i64, qkind: *i64, qidp: *i64, qprompt: *i64, nqg: i64) -> i64 {
1124 var o: i64 = o0
1125 let terms: *i64 = sys_mmap(8 * 128) as *i64
1126 let freqs: *i64 = sys_mmap(8 * 128) as *i64
1127 var i: i64 = 0
1128 while i < nqg {
1129 if qkind[i] == 84 {
1130 let nt: i64 = st_text_themes(bv, bl, nb, qidp[i] as *u8, terms, freqs, 128)
1131 if nt > 0 {
1132 var p: i64 = 0
1133 while p < nt {
1134 var best: i64 = p
1135 var t: i64 = p + 1
1136 while t < nt { if freqs[t] > freqs[best] { best = t } t = t + 1 }
1137 let ttp: i64 = terms[p]; terms[p] = terms[best]; terms[best] = ttp
1138 let tfp: i64 = freqs[p]; freqs[p] = freqs[best]; freqs[best] = tfp
1139 p = p + 1
1140 }
1141 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 8px'>Themes — " as *u8)
1142 o = sv2_esc(out, o, qprompt[i] as *u8)
1143 o = se_cat(out, o, "</h3><p>" as *u8)
1144 var lim: i64 = nt
1145 if lim > 12 { lim = 12 }
1146 p = 0
1147 while p < lim {
1148 o = se_cat(out, o, "<span style='display:inline-block;margin:3px 6px 3px 0;padding:3px 10px;border-radius:14px;background:rgb(238,241,248);color:rgb(42,77,143);font-size:.9rem'>" as *u8)
1149 o = sv2_esc(out, o, terms[p] as *u8)
1150 o = se_cat(out, o, " " as *u8)
1151 o = se_catn(out, o, freqs[p])
1152 o = se_cat(out, o, "</span>" as *u8)
1153 p = p + 1
1154 }
1155 o = se_cat(out, o, "</p><p class='hint'>Word frequency across written answers (sovereign, frequency-based). See the Tone card for emotional valence; neural topic modeling is the named next rung.</p></div>" as *u8)
1156 }
1157 }
1158 i = i + 1
1159 }
1160 return o
1161}
1162// H: OPEN-TEXT SENTIMENT (tone). Per text question, a VADER-style integer compound score aggregated across
1163// the written answers: warming / balanced / cooling + a positive/neutral/negative split. Negation- and
1164// intensifier-aware (nx_survey_sentiment), grounded in banked 2024-26 sentiment research (swc_svy_sent).
1165// Admin-only, like themes: it reads the free text that is WALLED from every public page.
1166func sv2_ins_sentiment(out: *u8, o0: i64, bv: *i64, bl: *i64, nb: i64, qkind: *i64, qidp: *i64, qprompt: *i64, nqg: i64) -> i64 {
1167 var o: i64 = o0
1168 let words: *i64 = sys_mmap(8 * SN_MAXLEX) as *i64
1169 let kinds: *i64 = sys_mmap(8 * SN_MAXLEX) as *i64
1170 let vals: *i64 = sys_mmap(8 * SN_MAXLEX) as *i64
1171 let nlex: i64 = sn_load(SN_LEX, words, kinds, vals, SN_MAXLEX)
1172 if nlex < 1 { return o }
1173 let agg: *i64 = sys_mmap(8 * 8) as *i64
1174 var i: i64 = 0
1175 while i < nqg {
1176 if qkind[i] == 84 {
1177 let n: i64 = sn_aggregate_over(bv, bl, nb, qidp[i] as *u8, words, kinds, vals, nlex, agg)
1178 if n > 0 {
1179 let mean: i64 = agg[1]
1180 let pos: i64 = agg[2]
1181 let neu: i64 = agg[3]
1182 let neg: i64 = agg[4]
1183 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 8px'>Tone — " as *u8)
1184 o = sv2_esc(out, o, qprompt[i] as *u8)
1185 o = se_cat(out, o, "</h3><p>" as *u8)
1186 if mean >= SN_POS_TH { o = se_cat(out, o, "<b style='color:rgb(26,127,55)'>Warming</b>" as *u8) } else { if mean <= SN_NEG_TH { o = se_cat(out, o, "<b style='color:rgb(176,38,38)'>Cooling</b>" as *u8) } else { o = se_cat(out, o, "<b style='color:rgb(90,94,110)'>Balanced</b>" as *u8) } }
1187 o = se_cat(out, o, " — mean tone " as *u8)
1188 if mean >= 0 { o = se_cat(out, o, "+" as *u8) }
1189 o = se_catn(out, o, mean)
1190 o = se_cat(out, o, "‰ across " as *u8)
1191 o = se_catn(out, o, n)
1192 o = se_cat(out, o, " written answer(s).</p>" as *u8)
1193 // proportional pos/neu/neg bar (percent of n; neutral = remainder so widths always sum to 100)
1194 let pp: i64 = (pos * 100) / n
1195 let gp2: i64 = (neg * 100) / n
1196 var up: i64 = 100 - pp - gp2
1197 if up < 0 { up = 0 }
1198 o = se_cat(out, o, "<div style='display:flex;height:14px;border-radius:7px;overflow:hidden;background:rgb(238,241,248)'>" as *u8)
1199 o = se_cat(out, o, "<div style='width:" as *u8); o = se_catn(out, o, pp); o = se_cat(out, o, "%;background:rgb(26,127,55)'></div>" as *u8)
1200 o = se_cat(out, o, "<div style='width:" as *u8); o = se_catn(out, o, up); o = se_cat(out, o, "%;background:rgb(200,205,216)'></div>" as *u8)
1201 o = se_cat(out, o, "<div style='width:" as *u8); o = se_catn(out, o, gp2); o = se_cat(out, o, "%;background:rgb(176,38,38)'></div>" as *u8)
1202 o = se_cat(out, o, "</div><p class='hint'>" as *u8)
1203 o = se_catn(out, o, pos); o = se_cat(out, o, " positive · " as *u8)
1204 o = se_catn(out, o, neu); o = se_cat(out, o, " neutral · " as *u8)
1205 o = se_catn(out, o, neg); o = se_cat(out, o, " negative — sovereign lexicon sentiment, negation- and intensifier-aware (grounded in banked 2024-26 research); a screening signal, not a verdict.</p></div>" as *u8)
1206 }
1207 }
1208 i = i + 1
1209 }
1210 return o
1211}
1212// G: data-quality / careless-response screening (C/IER). Attention-check pass rate (if the survey has @check
1213// items) + straight-lining over the scale block. FLAGS, never drops -- genuine low-but-varied answers are not
1214// flagged (gate-proven). Longstring is a weak signal on short surveys; the @check attention item is the strong guard.
1215func sv2_ins_quality(out: *u8, o0: i64, prefix: *u8, id: *u8, bv: *i64, bl: *i64, nb: i64, qkind: *i64, qidp: *i64, nqg: i64) -> i64 {
1216 var o: i64 = o0
1217 let mks: *i64 = sys_mmap(8 * 32) as *i64
1218 let mvs: *i64 = sys_mmap(8 * 32) as *i64
1219 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
1220 let empty: *u8 = sys_mmap(4)
1221 empty[0] = 0 as u8
1222 var checks: *u8 = empty
1223 if mnf > 0 {
1224 let cv: *u8 = sv_get(mks, mvs, mnf, "checks" as *u8)
1225 if (cv as i64) != 0 { checks = cv }
1226 }
1227 let sqids: *i64 = sys_mmap(8 * 32) as *i64
1228 var nsq: i64 = 0
1229 var i: i64 = 0
1230 while i < nqg { if qkind[i] == 83 { if nsq < 32 { sqids[nsq] = qidp[i]; nsq = nsq + 1 } } i = i + 1 }
1231 var ls_thresh: i64 = 0
1232 if nsq >= 3 { ls_thresh = nsq }
1233 var have_checks: i64 = 0
1234 if checks[0] != (0 as u8) { have_checks = 1 }
1235 if ls_thresh == 0 { if have_checks == 0 { return o } }
1236 let ro: *i64 = sys_mmap(32) as *i64
1237 sq_rollup(bv, bl, nb, checks, sqids, nsq, ls_thresh, ro)
1238 o = se_cat(out, o, "<div class='card'><h3 style='margin:0 0 8px'>Data quality (careless-response screening)</h3>" as *u8)
1239 o = se_cat(out, o, "<p style='margin:0 0 6px'><b>" as *u8)
1240 o = se_catn(out, o, ro[0])
1241 o = se_cat(out, o, "</b> clean · <b>" as *u8)
1242 o = se_catn(out, o, ro[1])
1243 o = se_cat(out, o, "</b> flagged for review of " as *u8)
1244 o = se_catn(out, o, nb)
1245 o = se_cat(out, o, " response(s)</p><table><tbody>" as *u8)
1246 if have_checks == 1 {
1247 o = se_cat(out, o, "<tr><td>Failed an attention check</td><td><b>" as *u8)
1248 o = se_catn(out, o, ro[2])
1249 o = se_cat(out, o, "</b></td></tr>" as *u8)
1250 }
1251 if ls_thresh > 0 {
1252 o = se_cat(out, o, "<tr><td>Straight-lined (all " as *u8)
1253 o = se_catn(out, o, nsq)
1254 o = se_cat(out, o, " scale answers identical)</td><td><b>" as *u8)
1255 o = se_catn(out, o, ro[3])
1256 o = se_cat(out, o, "</b></td></tr>" as *u8)
1257 }
1258 o = se_cat(out, o, "</tbody></table><p class='hint'>Flagged responses are surfaced for review, NEVER dropped. A genuinely struggling respondent with varied low answers is NOT flagged (gate-proven). Straight-lining is a weak signal on short surveys — add a `@check <qid> <answer>` instructed-response item (the evidence-backed gold standard) for a strong guard.</p></div>" as *u8)
1259 return o
1260}
1261// ---- ADMIN deep-insights report (key-gated; the analytics layer over nx_survey_stats) ----
1262// Gathers the survey's questions, then calls the section renderers above: descriptive table + concern
1263// flags, segment breakdowns (the killer insight), cross-tabs + chi-square significance, key drivers,
1264// and open-text themes. Admin-only because breakdowns can reveal small cells.
1265func sv2_insights(prefix: *u8, base: *u8, id: *u8, out: *u8, cap: i64) -> i64 {
1266 let mks: *i64 = sys_mmap(8 * 32) as *i64
1267 let mvs: *i64 = sys_mmap(8 * 32) as *i64
1268 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
1269 if mnf < 0 { return 0 - 1 }
1270 let title: *u8 = sv_get(mks, mvs, mnf, "title" as *u8)
1271 let nq: i64 = se_field_n(mks, mvs, mnf, "nq" as *u8, 0)
1272 if (title as i64) == 0 { return 0 - 1 }
1273 sv2_barr()
1274 let bk: *i64 = sv2_bk_g
1275 let bv: *i64 = sv2_bv_g
1276 let bl: *i64 = sv2_bl_g
1277 let nb: i64 = se_ballots(prefix, id, bk, bv, bl, SV2_MAXB)
1278 if nb < 0 { return sv2_err(out, "500 Internal Server Error" as *u8, "tally walk hit its bound; compact the store" as *u8) }
1279 // gather question metadata (pointers into fresh per-question decode buffers stay valid)
1280 let qkind: *i64 = sys_mmap(8 * 32) as *i64
1281 let qidp: *i64 = sys_mmap(8 * 32) as *i64
1282 let qprompt: *i64 = sys_mmap(8 * 32) as *i64
1283 let qopts: *i64 = sys_mmap(8 * 32) as *i64
1284 let qmax: *i64 = sys_mmap(8 * 32) as *i64
1285 let qmin: *i64 = sys_mmap(8 * 32) as *i64
1286 let qnopt: *i64 = sys_mmap(8 * 32) as *i64
1287 var nqg: i64 = 0
1288 var q: i64 = 1
1289 while q <= nq {
1290 if nqg < 32 {
1291 let qks: *i64 = sys_mmap(8 * 32) as *i64
1292 let qvs: *i64 = sys_mmap(8 * 32) as *i64
1293 let qnf: i64 = se_qst(prefix, id, q, qks, qvs, 32)
1294 if qnf > 0 {
1295 let kind: *u8 = sv_get(qks, qvs, qnf, "kind" as *u8)
1296 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
1297 let prompt: *u8 = sv_get(qks, qvs, qnf, "prompt" as *u8)
1298 if (kind as i64) != 0 { if (qid as i64) != 0 { if (prompt as i64) != 0 {
1299 qkind[nqg] = kind[0] as i64
1300 qidp[nqg] = qid as i64
1301 qprompt[nqg] = prompt as i64
1302 let opts: *u8 = sv_get(qks, qvs, qnf, "opts" as *u8)
1303 qopts[nqg] = opts as i64
1304 qmin[nqg] = se_field_n(qks, qvs, qnf, "min" as *u8, 0)
1305 qmax[nqg] = se_field_n(qks, qvs, qnf, "max" as *u8, 10)
1306 if (opts as i64) != 0 { qnopt[nqg] = se_nopts(opts) } else { qnopt[nqg] = 0 }
1307 nqg = nqg + 1
1308 } } }
1309 }
1310 }
1311 q = q + 1
1312 }
1313 var o: i64 = sv2_page_top(out, 0, title)
1314 o = se_cat(out, o, "<p><a href='" as *u8)
1315 o = se_cat(out, o, base)
1316 o = se_cat(out, o, "/'>← all surveys</a></p><h1>" as *u8)
1317 o = sv2_esc(out, o, title)
1318 o = se_cat(out, o, "</h1><p class='sub'>Insights report — <b>" as *u8)
1319 o = se_catn(out, o, nb)
1320 o = se_cat(out, o, "</b> response(s). Admin-only: breakdowns can reveal small groups.</p>" as *u8)
1321 if nb == 0 { o = se_cat(out, o, "<div class='card'><p>No responses yet.</p></div>" as *u8); o = sv2_page_foot(out, o); return o }
1322 o = sv2_ins_scale(out, o, bv, bl, nb, qkind, qidp, qprompt, qmax, qmin, nqg)
1323 o = sv2_ins_seg(out, o, bv, bl, nb, qkind, qidp, qprompt, qopts, qnopt, nqg)
1324 o = sv2_ins_xtab(out, o, bv, bl, nb, qkind, qidp, qprompt, qopts, qnopt, nqg)
1325 o = sv2_ins_drivers(out, o, bv, bl, nb, qkind, qidp, qprompt, nqg)
1326 o = sv2_ins_themes(out, o, bv, bl, nb, qkind, qidp, qprompt, nqg)
1327 o = sv2_ins_sentiment(out, o, bv, bl, nb, qkind, qidp, qprompt, nqg)
1328 o = sv2_ins_quality(out, o, prefix, id, bv, bl, nb, qkind, qidp, nqg)
1329 o = sv2_page_foot(out, o)
1330 return o
1331}
1332// ---- EMBEDDABLE one-question poll (the operator's original ask: "a simple one question poll on our tandem
1333// site"). Compact, self-contained (own CSS), iframe-friendly. GET = the poll; POST = record + inline results. ----
1334func sv2_embed_top(out: *u8, o0: i64, title: *u8) -> i64 {
1335 var o: i64 = se_cat(out, o0, "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<" as *u8)
1336 o = se_catc(out, o, 33)
1337 o = se_cat(out, o, "DOCTYPE html>\n<html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>" as *u8)
1338 o = sv2_esc(out, o, title)
1339 o = se_cat(out, o, "</title><style>*{box-sizing:border-box}body{font-family:-apple-system,Segoe UI,Roboto,sans-serif;margin:0;padding:14px;color:rgb(24,26,34);background:rgb(255,255,255);line-height:1.45}\n" as *u8)
1340 o = se_cat(out, o, ".q{font-weight:600;font-size:1.05rem;margin:0 0 10px}.opt{display:block;padding:8px 11px;border:1px solid rgb(222,225,233);border-radius:8px;margin:6px 0;cursor:pointer}.opt:hover{background:rgb(240,243,250)}.opt input{margin-right:9px}\n" as *u8)
1341 o = se_cat(out, o, "button{background:rgb(42,77,143);color:rgb(255,255,255);border:0;border-radius:8px;padding:9px 20px;font-size:.98rem;font-weight:600;cursor:pointer;margin-top:6px}\n" as *u8)
1342 o = se_cat(out, o, ".row{display:flex;align-items:center;gap:9px;margin:5px 0}.lbl{flex:0 0 40%;font-size:.92rem}.barw{flex:1;background:rgb(238,241,248);border-radius:6px;height:16px;overflow:hidden}.bar{background:rgb(42,77,143);height:16px;display:block}.cnt{flex:0 0 2.6em;text-align:right;font-size:.88rem;color:rgb(105,110,125)}\n" as *u8)
1343 o = se_cat(out, o, ".by{margin:12px 0 0;font-size:.78rem;color:rgb(140,144,158)}.by a{color:rgb(105,110,125)}.n{font-size:.85rem;color:rgb(105,110,125);margin:8px 0 0}\n" as *u8)
1344 o = se_cat(out, o, "@media(prefers-color-scheme:dark){body{background:rgb(22,24,31);color:rgb(228,230,238)}.opt{border-color:rgb(44,48,60)}.opt:hover{background:rgb(30,33,44)}.barw{background:rgb(36,39,50)}}</style></head><body>" as *u8)
1345 return o
1346}
1347// compact inline results (bars) for the first question of a poll
1348func sv2_embed_results(prefix: *u8, base: *u8, id: *u8, out: *u8, cap: i64) -> i64 {
1349 let mks: *i64 = sys_mmap(8 * 32) as *i64
1350 let mvs: *i64 = sys_mmap(8 * 32) as *i64
1351 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
1352 if mnf < 0 { return 0 - 1 }
1353 let kmin: i64 = se_field_n(mks, mvs, mnf, "kmin" as *u8, 0)
1354 let qks: *i64 = sys_mmap(8 * 32) as *i64
1355 let qvs: *i64 = sys_mmap(8 * 32) as *i64
1356 let qnf: i64 = se_qst(prefix, id, 1, qks, qvs, 32)
1357 if qnf < 0 { return 0 - 1 }
1358 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
1359 let kind: *u8 = sv_get(qks, qvs, qnf, "kind" as *u8)
1360 let prompt: *u8 = sv_get(qks, qvs, qnf, "prompt" as *u8)
1361 if (qid as i64) == 0 { return 0 - 1 }
1362 sv2_barr()
1363 let bk: *i64 = sv2_bk_g
1364 let bv: *i64 = sv2_bv_g
1365 let bl: *i64 = sv2_bl_g
1366 let nb: i64 = se_ballots(prefix, id, bk, bv, bl, SV2_MAXB)
1367 if nb < 0 { return sv2_err(out, "500 Internal Server Error" as *u8, "tally bound" as *u8) }
1368 var o: i64 = sv2_embed_top(out, 0, prompt)
1369 o = se_cat(out, o, "<p class='q'>" as *u8)
1370 o = sv2_esc(out, o, prompt)
1371 o = se_cat(out, o, "</p>" as *u8)
1372 if nb < kmin {
1373 o = se_cat(out, o, "<p class='n'>Thanks! Results show after " as *u8)
1374 o = se_catn(out, o, kmin)
1375 o = se_cat(out, o, " votes.</p>" as *u8)
1376 } else {
1377 let kc: i64 = kind[0] as i64
1378 if kc == 67 {
1379 let opts: *u8 = sv_get(qks, qvs, qnf, "opts" as *u8)
1380 let no: i64 = se_nopts(opts)
1381 let ob: *u8 = sys_mmap(96)
1382 let vb: *u8 = sys_mmap(16)
1383 var k: i64 = 0
1384 while k < no {
1385 se_opt(opts, k, ob, 96)
1386 se_catn(vb, 0, k)
1387 var ve: i64 = 0
1388 while vb[ve] != (0 as u8) { ve = ve + 1 }
1389 vb[ve] = 0 as u8
1390 let c: i64 = se_count_eq(bv, bl, nb, qid, vb)
1391 var pct: i64 = 0
1392 if nb > 0 { pct = c * 100 / nb }
1393 o = se_cat(out, o, "<div class='row'><span class='lbl'>" as *u8)
1394 o = sv2_esc(out, o, ob)
1395 o = se_cat(out, o, "</span><span class='barw'><span class='bar' style='width:" as *u8)
1396 o = se_catn(out, o, pct)
1397 o = se_cat(out, o, "%'></span></span><span class='cnt'>" as *u8)
1398 o = se_catn(out, o, c)
1399 o = se_cat(out, o, "</span></div>" as *u8)
1400 k = k + 1
1401 }
1402 } else {
1403 let lo: i64 = se_field_n(qks, qvs, qnf, "min" as *u8, 0)
1404 let hi: i64 = se_field_n(qks, qvs, qnf, "max" as *u8, 10)
1405 let vb2: *u8 = sys_mmap(16)
1406 var v: i64 = lo
1407 while v <= hi {
1408 se_catn(vb2, 0, v)
1409 var v2e: i64 = 0
1410 while vb2[v2e] != (0 as u8) { v2e = v2e + 1 }
1411 vb2[v2e] = 0 as u8
1412 let c2: i64 = se_count_eq(bv, bl, nb, qid, vb2)
1413 var pct2: i64 = 0
1414 if nb > 0 { pct2 = c2 * 100 / nb }
1415 o = se_cat(out, o, "<div class='row'><span class='lbl'>" as *u8)
1416 o = se_catn(out, o, v)
1417 o = se_cat(out, o, "</span><span class='barw'><span class='bar' style='width:" as *u8)
1418 o = se_catn(out, o, pct2)
1419 o = se_cat(out, o, "%'></span></span><span class='cnt'>" as *u8)
1420 o = se_catn(out, o, c2)
1421 o = se_cat(out, o, "</span></div>" as *u8)
1422 v = v + 1
1423 }
1424 }
1425 o = se_cat(out, o, "<p class='n'>" as *u8)
1426 o = se_catn(out, o, nb)
1427 o = se_cat(out, o, " response(s)</p>" as *u8)
1428 }
1429 o = se_cat(out, o, "<p class='by'>via <a href='" as *u8)
1430 o = se_cat(out, o, base)
1431 o = se_cat(out, o, "/s/" as *u8)
1432 o = se_cat(out, o, id)
1433 o = se_cat(out, o, "' target='_top'>Nishi Pulse</a></p></body></html>" as *u8)
1434 return o
1435}
1436// the poll form (first question), compact + iframe-friendly; app.js persists the token in the iframe origin.
1437func sv2_embed(prefix: *u8, base: *u8, id: *u8, tok: *u8, out: *u8, cap: i64) -> i64 {
1438 let mks: *i64 = sys_mmap(8 * 32) as *i64
1439 let mvs: *i64 = sys_mmap(8 * 32) as *i64
1440 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
1441 if mnf < 0 { return 0 - 1 }
1442 let stt: *u8 = sv_get(mks, mvs, mnf, "status" as *u8)
1443 if (stt as i64) != 0 { if se_seq(stt, "open" as *u8) == 0 { return sv2_embed_results(prefix, base, id, out, cap) } }
1444 let qks: *i64 = sys_mmap(8 * 32) as *i64
1445 let qvs: *i64 = sys_mmap(8 * 32) as *i64
1446 let qnf: i64 = se_qst(prefix, id, 1, qks, qvs, 32)
1447 if qnf < 0 { return 0 - 1 }
1448 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
1449 let kind: *u8 = sv_get(qks, qvs, qnf, "kind" as *u8)
1450 let prompt: *u8 = sv_get(qks, qvs, qnf, "prompt" as *u8)
1451 if (qid as i64) == 0 { return 0 - 1 }
1452 var o: i64 = sv2_embed_top(out, 0, prompt)
1453 o = se_cat(out, o, "<p class='q'>" as *u8)
1454 o = sv2_esc(out, o, prompt)
1455 o = se_cat(out, o, "</p><form method='post' action='" as *u8)
1456 o = se_cat(out, o, base)
1457 o = se_cat(out, o, "/embed/" as *u8)
1458 o = se_cat(out, o, id)
1459 o = se_cat(out, o, "'><input type='hidden' id='tok' name='tok' value='" as *u8)
1460 o = se_cat(out, o, tok)
1461 o = se_cat(out, o, "'>" as *u8)
1462 let kc: i64 = kind[0] as i64
1463 if kc == 67 {
1464 let opts: *u8 = sv_get(qks, qvs, qnf, "opts" as *u8)
1465 let no: i64 = se_nopts(opts)
1466 let ob: *u8 = sys_mmap(96)
1467 var k: i64 = 0
1468 while k < no {
1469 se_opt(opts, k, ob, 96)
1470 o = se_cat(out, o, "<label class='opt'><input type='radio' name='" as *u8)
1471 o = se_cat(out, o, qid)
1472 o = se_cat(out, o, "' value='" as *u8)
1473 o = se_catn(out, o, k)
1474 o = se_cat(out, o, "'>" as *u8)
1475 o = sv2_esc(out, o, ob)
1476 o = se_cat(out, o, "</label>" as *u8)
1477 k = k + 1
1478 }
1479 } else {
1480 let lo: i64 = se_field_n(qks, qvs, qnf, "min" as *u8, 0)
1481 let hi: i64 = se_field_n(qks, qvs, qnf, "max" as *u8, 10)
1482 var v: i64 = lo
1483 while v <= hi {
1484 o = se_cat(out, o, "<label class='opt'><input type='radio' name='" as *u8)
1485 o = se_cat(out, o, qid)
1486 o = se_cat(out, o, "' value='" as *u8)
1487 o = se_catn(out, o, v)
1488 o = se_cat(out, o, "'>" as *u8)
1489 o = se_catn(out, o, v)
1490 o = se_cat(out, o, "</label>" as *u8)
1491 v = v + 1
1492 }
1493 }
1494 o = se_cat(out, o, "<button type='submit'>Vote</button></form>" as *u8)
1495 o = se_cat(out, o, "<p class='by'>via <a href='" as *u8)
1496 o = se_cat(out, o, base)
1497 o = se_cat(out, o, "/s/" as *u8)
1498 o = se_cat(out, o, id)
1499 o = se_cat(out, o, "' target='_top'>Nishi Pulse</a></p><script src='" as *u8)
1500 o = se_cat(out, o, base)
1501 o = se_cat(out, o, "/app.js'></script></body></html>" as *u8)
1502 return o
1503}
1504// sv2_finalize -- transport wrapper: ensure every response carries Content-Length + Connection: close so a
1505// strict HTTP/1.1 client (our own sovereign browser, behind the buffered edge that keeps the client socket
1506// alive) reads the body cleanly instead of hanging to the timeout. Responses that already declare a
1507// Content-Length (file/js downloads) pass through untouched. Applied once in the daemon accept loop.
1508func sv2_finalize(resp: *u8, n: i64, out: *u8, cap: i64) -> i64 {
1509 var he: i64 = 0 - 1
1510 var i: i64 = 0
1511 while i + 3 < n {
1512 if resp[i] == (13 as u8) { if resp[i+1] == (10 as u8) { if resp[i+2] == (13 as u8) { if resp[i+3] == (10 as u8) { he = i; i = n } } } }
1513 i = i + 1
1514 }
1515 if he < 0 {
1516 var c0: i64 = 0
1517 while c0 < n { if c0 < cap { out[c0] = resp[c0] } c0 = c0 + 1 }
1518 return n
1519 }
1520 if sv2_memhas(resp, he, "Content-Length" as *u8) == 1 {
1521 var c1: i64 = 0
1522 while c1 < n { if c1 < cap { out[c1] = resp[c1] } c1 = c1 + 1 }
1523 return n
1524 }
1525 let bodylen: i64 = n - (he + 4)
1526 var o: i64 = 0
1527 var h: i64 = 0
1528 while h < he { out[o] = resp[h]; o = o + 1; h = h + 1 }
1529 o = se_cat(out, o, "\r\nContent-Length: " as *u8)
1530 o = se_catn(out, o, bodylen)
1531 o = se_cat(out, o, "\r\nConnection: close\r\n\r\n" as *u8)
1532 var b: i64 = he + 4
1533 while b < n { if o < cap { out[o] = resp[b]; o = o + 1 } b = b + 1 }
1534 return o
1535}
1536func sv2_serve_js(out: *u8, cap: i64) -> i64 {
1537 let fb: *u8 = sys_mmap(65536)
1538 let fl: i64 = sv2_read_file("survey_app.js" as *u8, fb, 65535)
1539 if fl <= 0 { return sv2_err(out, "404 Not Found" as *u8, "app.js missing" as *u8) }
1540 var o: i64 = se_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: application/javascript; charset=utf-8\r\nContent-Length: " as *u8)
1541 o = se_catn(out, o, fl)
1542 o = se_cat(out, o, "\r\nCache-Control: max-age=300\r\n\r\n" as *u8)
1543 var i: i64 = 0
1544 while i < fl { if o + i < cap { out[o + i] = fb[i] } i = i + 1 }
1545 return o + fl
1546}
1547
1548// the app: request bytes -> response bytes (mount-agnostic; keypath = admin key file, FAIL-CLOSED)
1549func sv2_handle(prefix: *u8, base: *u8, keypath: *u8, req: *u8, reqlen: i64, out: *u8, cap: i64) -> i64 {
1550 // path token between the first two spaces (static buffer: see sv2_pathb -- the one-page-per-GET residual)
1551 sv2_pathb()
1552 let fullpath: *u8 = sv2_path_g
1553 var i: i64 = 0
1554 while i < reqlen { if req[i] == (32 as u8) { i = i + 1; break } i = i + 1 }
1555 var t: i64 = 0
1556 while i < reqlen { if req[i] == (32 as u8) { break } if t < SV2_PATHB - 1 { fullpath[t] = req[i]; t = t + 1 } i = i + 1 }
1557 fullpath[t] = 0 as u8
1558 // capture the query (for ?t=<tok>) THEN strip it off the routing path
1559 let qbuf: *u8 = sys_mmap(256)
1560 qbuf[0] = 0 as u8
1561 var qi: i64 = 0
1562 while fullpath[qi] != (0 as u8) {
1563 if fullpath[qi] == (63 as u8) {
1564 var qo: i64 = 0
1565 var qj: i64 = qi + 1
1566 while fullpath[qj] != (0 as u8) { if qo < 255 { qbuf[qo] = fullpath[qj]; qo = qo + 1 } qj = qj + 1 }
1567 qbuf[qo] = 0 as u8
1568 fullpath[qi] = 0 as u8
1569 } else { qi = qi + 1 }
1570 }
1571 // strip the mount prefix
1572 var path: *u8 = fullpath
1573 if base[0] != (0 as u8) {
1574 var bm: i64 = 1
1575 var bi: i64 = 0
1576 while base[bi] != (0 as u8) { if fullpath[bi] != base[bi] { bm = 0; bi = se_slen(base) } else { bi = bi + 1 } }
1577 if bm == 1 { path = (fullpath as i64 + se_slen(base)) as *u8 }
1578 }
1579 var is_post: i64 = 0
1580 if req[0] == (80 as u8) { if req[1] == (79 as u8) { is_post = 1 } }
1581
1582 if is_post == 1 {
1583 // same-origin guard (when Origin is present it must contain our Host) -- EXCEPT /embed/ which is
1584 // cross-origin BY DESIGN (a one-question poll embedded on another site). Safe: an embed vote is an
1585 // anonymous poll answer, revote-replace caps inflation per token, no sensitive/state-changing action.
1586 if se_starts(path, "/embed/" as *u8) == 0 {
1587 let ob: *u8 = sys_mmap(300)
1588 let hb: *u8 = sys_mmap(300)
1589 sv2_hdr_get(req, reqlen, "Origin:" as *u8, ob, 300)
1590 sv2_hdr_get(req, reqlen, "Host:" as *u8, hb, 300)
1591 if ob[0] != (0 as u8) { if hb[0] != (0 as u8) {
1592 if sv2_memhas(ob, se_slen(ob), hb) == 0 { return sv2_err(out, "403 Forbidden" as *u8, "cross-origin POST refused" as *u8) }
1593 } }
1594 }
1595 var bs: i64 = 0 - 1
1596 var j: i64 = 0
1597 while j + 3 < reqlen {
1598 if req[j] == (13 as u8) { if req[j+1] == (10 as u8) { if req[j+2] == (13 as u8) { if req[j+3] == (10 as u8) { bs = j + 4; j = reqlen } } } }
1599 j = j + 1
1600 }
1601 if bs < 0 { return sv2_err(out, "400 Bad Request" as *u8, "no body" as *u8) }
1602 let body: *u8 = (req as i64 + bs) as *u8
1603 let blen: i64 = reqlen - bs
1604 // ---- admin lane (key-file gated, fail-closed) ----
1605 if se_starts(path, "/admin/" as *u8) == 1 {
1606 let key: *u8 = sys_mmap(256)
1607 sv2_form_get(body, blen, "key" as *u8, key, 256)
1608 if sv2_admin_ok(keypath, key) == 0 { return sv2_err(out, "403 Forbidden" as *u8, "admin key refused (fail-closed)" as *u8) }
1609 if se_seq(path, "/admin/load" as *u8) == 1 {
1610 let spec: *u8 = sys_mmap(65536)
1611 let slen: i64 = sv2_form_get(body, blen, "spec" as *u8, spec, 65536)
1612 if slen <= 0 { return sv2_err(out, "400 Bad Request" as *u8, "empty spec" as *u8) }
1613 let idb: *u8 = sys_mmap(64)
1614 let nq: i64 = se_load(prefix, spec, slen, idb, 64)
1615 if nq < 1 {
1616 var eo: i64 = sv2_err(out, "400 Bad Request" as *u8, "spec refused, code " as *u8)
1617 eo = se_catn(out, eo - 1, nq)
1618 out[eo] = 10 as u8
1619 return eo + 1
1620 }
1621 var o: i64 = se_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nLOADED id=" as *u8)
1622 o = se_cat(out, o, idb)
1623 o = se_cat(out, o, " nq=" as *u8)
1624 o = se_catn(out, o, nq)
1625 o = se_catc(out, o, 10)
1626 return o
1627 }
1628 if se_seq(path, "/admin/close" as *u8) == 1 {
1629 let idb: *u8 = sys_mmap(64)
1630 sv2_form_get(body, blen, "id" as *u8, idb, 64)
1631 if se_id_ok(idb) == 0 { return sv2_err(out, "400 Bad Request" as *u8, "bad id" as *u8) }
1632 if se_close(prefix, idb) != 0 { return sv2_err(out, "404 Not Found" as *u8, "no such survey" as *u8) }
1633 var o: i64 = se_cat(out, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nCLOSED " as *u8)
1634 o = se_cat(out, o, idb)
1635 o = se_catc(out, o, 10)
1636 return o
1637 }
1638 if se_seq(path, "/admin/report" as *u8) == 1 {
1639 let idb: *u8 = sys_mmap(64)
1640 sv2_form_get(body, blen, "id" as *u8, idb, 64)
1641 if se_id_ok(idb) == 0 { return sv2_err(out, "400 Bad Request" as *u8, "bad id" as *u8) }
1642 let n: i64 = sv2_report(prefix, idb, out, cap)
1643 if n < 0 { return sv2_err(out, "404 Not Found" as *u8, "no such survey" as *u8) }
1644 return n
1645 }
1646 if se_seq(path, "/admin/insights" as *u8) == 1 {
1647 let idb: *u8 = sys_mmap(64)
1648 sv2_form_get(body, blen, "id" as *u8, idb, 64)
1649 if se_id_ok(idb) == 0 { return sv2_err(out, "400 Bad Request" as *u8, "bad id" as *u8) }
1650 let n: i64 = sv2_insights(prefix, base, idb, out, cap)
1651 if n < 0 { return sv2_err(out, "404 Not Found" as *u8, "no such survey" as *u8) }
1652 return n
1653 }
1654 return sv2_err(out, "404 Not Found" as *u8, "no such admin route" as *u8)
1655 }
1656 // ---- public respond: POST /s/<id> ----
1657 if se_starts(path, "/s/" as *u8) == 1 {
1658 let id: *u8 = sys_mmap(64)
1659 var pq: i64 = 3
1660 var it: i64 = 0
1661 while path[pq] != (0 as u8) { if path[pq] == (47 as u8) { break } if it < 63 { id[it] = path[pq]; it = it + 1 } pq = pq + 1 }
1662 id[it] = 0 as u8
1663 if se_id_ok(id) == 0 { return sv2_err(out, "404 Not Found" as *u8, "bad survey id" as *u8) }
1664 let tok: *u8 = sys_mmap(96)
1665 sv2_form_get(body, blen, "tok" as *u8, tok, 96)
1666 // gather answers: one form field per question qid
1667 let mks: *i64 = sys_mmap(8 * 32) as *i64
1668 let mvs: *i64 = sys_mmap(8 * 32) as *i64
1669 let mnf: i64 = se_meta(prefix, id, mks, mvs, 32)
1670 if mnf < 0 { return sv2_err(out, "404 Not Found" as *u8, "no such survey" as *u8) }
1671 let nq: i64 = se_field_n(mks, mvs, mnf, "nq" as *u8, 0)
1672 let aq: *i64 = sys_mmap(8 * (nq + 2)) as *i64
1673 let av: *i64 = sys_mmap(8 * (nq + 2)) as *i64
1674 var na: i64 = 0
1675 var q: i64 = 1
1676 while q <= nq {
1677 let qks: *i64 = sys_mmap(8 * 32) as *i64
1678 let qvs: *i64 = sys_mmap(8 * 32) as *i64
1679 let qnf: i64 = se_qst(prefix, id, q, qks, qvs, 32)
1680 if qnf > 0 {
1681 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
1682 if (qid as i64) != 0 {
1683 let vb: *u8 = sys_mmap(4096)
1684 let vl: i64 = sv2_form_get(body, blen, qid, vb, 4096)
1685 if vl > 0 {
1686 // stable copy of the qid (question record buffers stay alive per request)
1687 aq[na] = qid as i64
1688 av[na] = vb as i64
1689 na = na + 1
1690 }
1691 }
1692 }
1693 q = q + 1
1694 }
1695 let rc: i64 = se_respond(prefix, id, tok, aq, av, na)
1696 if rc == 1 {
1697 var o: i64 = se_cat(out, 0, "HTTP/1.1 303 See Other\r\nLocation: " as *u8)
1698 o = se_cat(out, o, base)
1699 o = se_cat(out, o, "/s/" as *u8)
1700 o = se_cat(out, o, id)
1701 o = se_cat(out, o, "/done\r\n\r\n" as *u8)
1702 return o
1703 }
1704 if rc == (0 - 2) { return sv2_err(out, "409 Conflict" as *u8, "survey is closed" as *u8) }
1705 if rc == (0 - 8) { return sv2_err(out, "409 Conflict" as *u8, "this survey has reached its response quota (full)" as *u8) }
1706 if rc == (0 - 5) { return sv2_err(out, "400 Bad Request" as *u8, "bad respondent token" as *u8) }
1707 if rc == (0 - 3) { return sv2_err(out, "400 Bad Request" as *u8, "missing or invalid answer" as *u8) }
1708 if rc == (0 - 6) { return sv2_err(out, "400 Bad Request" as *u8, "unknown question answered" as *u8) }
1709 if rc == (0 - 1) { return sv2_err(out, "404 Not Found" as *u8, "no such survey" as *u8) }
1710 return sv2_err(out, "500 Internal Server Error" as *u8, "store write failed" as *u8)
1711 }
1712 // ---- embed poll vote: POST /embed/<id> (cross-origin allowed; first question = the poll) ----
1713 if se_starts(path, "/embed/" as *u8) == 1 {
1714 let id: *u8 = sys_mmap(64)
1715 var pq: i64 = 7
1716 var it: i64 = 0
1717 while path[pq] != (0 as u8) { if path[pq] == (47 as u8) { break } if it < 63 { id[it] = path[pq]; it = it + 1 } pq = pq + 1 }
1718 id[it] = 0 as u8
1719 if se_id_ok(id) == 0 { return sv2_err(out, "404 Not Found" as *u8, "bad poll id" as *u8) }
1720 let tok: *u8 = sys_mmap(96)
1721 sv2_form_get(body, blen, "tok" as *u8, tok, 96)
1722 let qks: *i64 = sys_mmap(8 * 32) as *i64
1723 let qvs: *i64 = sys_mmap(8 * 32) as *i64
1724 let qnf: i64 = se_qst(prefix, id, 1, qks, qvs, 32)
1725 if qnf < 0 { return sv2_err(out, "404 Not Found" as *u8, "no such poll" as *u8) }
1726 let qid: *u8 = sv_get(qks, qvs, qnf, "qid" as *u8)
1727 if (qid as i64) == 0 { return sv2_err(out, "404 Not Found" as *u8, "no such poll" as *u8) }
1728 let aq: *i64 = sys_mmap(8 * 4) as *i64
1729 let av: *i64 = sys_mmap(8 * 4) as *i64
1730 var na: i64 = 0
1731 let vb: *u8 = sys_mmap(4096)
1732 let vl: i64 = sv2_form_get(body, blen, qid, vb, 4096)
1733 if vl > 0 { aq[0] = qid as i64; av[0] = vb as i64; na = 1 }
1734 let rc: i64 = se_respond(prefix, id, tok, aq, av, na)
1735 // recorded, benign revote, or a closed poll -> show the compact tally (poll UX). bad input -> loud.
1736 if rc == 1 { return sv2_embed_results(prefix, base, id, out, cap) }
1737 if rc == (0 - 2) { return sv2_embed_results(prefix, base, id, out, cap) }
1738 if rc == (0 - 8) { return sv2_embed_results(prefix, base, id, out, cap) }
1739 if rc == (0 - 3) { return sv2_err(out, "400 Bad Request" as *u8, "pick an option to vote" as *u8) }
1740 if rc == (0 - 5) { return sv2_err(out, "400 Bad Request" as *u8, "bad token" as *u8) }
1741 return sv2_err(out, "404 Not Found" as *u8, "no such poll" as *u8)
1742 }
1743 return sv2_err(out, "404 Not Found" as *u8, "no such POST route" as *u8)
1744 }
1745
1746 // ---- GET routes ----
1747 if se_seq(path, "/app.js" as *u8) == 1 { return sv2_serve_js(out, cap) }
1748 if se_seq(path, "/" as *u8) == 1 { return sv2_home(prefix, base, out, cap) }
1749 if path[0] == (0 as u8) { return sv2_home(prefix, base, out, cap) }
1750 // embeddable one-question poll (iframe-friendly, compact): GET /embed/<id>
1751 if se_starts(path, "/embed/" as *u8) == 1 {
1752 let id: *u8 = sys_mmap(64)
1753 var pq: i64 = 7
1754 var it: i64 = 0
1755 while path[pq] != (0 as u8) { if path[pq] == (47 as u8) { break } if it < 63 { id[it] = path[pq]; it = it + 1 } pq = pq + 1 }
1756 id[it] = 0 as u8
1757 if se_id_ok(id) == 0 { return sv2_err(out, "404 Not Found" as *u8, "bad poll id" as *u8) }
1758 let tok: *u8 = sys_mmap(96)
1759 var mo: i64 = se_cat(tok, 0, "e" as *u8)
1760 mo = se_catn(tok, mo, sys_now_us())
1761 tok[mo] = 0 as u8
1762 let n: i64 = sv2_embed(prefix, base, id, tok, out, cap)
1763 if n < 0 { return sv2_err(out, "404 Not Found" as *u8, "no such poll" as *u8) }
1764 return n
1765 }
1766 if se_starts(path, "/s/" as *u8) == 1 {
1767 let id: *u8 = sys_mmap(64)
1768 var pq: i64 = 3
1769 var it: i64 = 0
1770 while path[pq] != (0 as u8) { if path[pq] == (47 as u8) { break } if it < 63 { id[it] = path[pq]; it = it + 1 } pq = pq + 1 }
1771 id[it] = 0 as u8
1772 if se_id_ok(id) == 0 { return sv2_err(out, "404 Not Found" as *u8, "bad survey id" as *u8) }
1773 let sub: *u8 = (path as i64 + 3 + it) as *u8
1774 if se_seq(sub, "/results.json" as *u8) == 1 {
1775 let n: i64 = sv2_results_json(prefix, id, out, cap)
1776 if n < 0 { return sv2_err(out, "404 Not Found" as *u8, "no such survey" as *u8) }
1777 return n
1778 }
1779 if se_seq(sub, "/results" as *u8) == 1 {
1780 let n: i64 = sv2_results(prefix, base, id, out, cap)
1781 if n < 0 { return sv2_err(out, "404 Not Found" as *u8, "no such survey" as *u8) }
1782 return n
1783 }
1784 if se_seq(sub, "/done" as *u8) == 1 { return sv2_done(base, id, out) }
1785 if sub[0] != (0 as u8) { return sv2_err(out, "404 Not Found" as *u8, "no such route" as *u8) }
1786 // token from ?t=<tok> or freshly minted (app.js swaps in the browser's stable token)
1787 let tok: *u8 = sys_mmap(96)
1788 tok[0] = 0 as u8
1789 var qp: i64 = 0
1790 while qbuf[qp] != (0 as u8) {
1791 var att: i64 = 0
1792 if qp == 0 { att = 1 } else { if qbuf[qp-1] == (38 as u8) { att = 1 } }
1793 if att == 1 { if qbuf[qp] == (116 as u8) { if qbuf[qp+1] == (61 as u8) {
1794 var to2: i64 = 0
1795 var qj2: i64 = qp + 2
1796 var go2: i64 = 1
1797 while go2 == 1 {
1798 if qbuf[qj2] == (0 as u8) { go2 = 0 } else {
1799 if qbuf[qj2] == (38 as u8) { go2 = 0 } else {
1800 if to2 < 95 { tok[to2] = qbuf[qj2]; to2 = to2 + 1 }
1801 qj2 = qj2 + 1
1802 }
1803 }
1804 }
1805 tok[to2] = 0 as u8
1806 } } }
1807 qp = qp + 1
1808 }
1809 if se_tok_ok(tok) == 0 {
1810 var mo: i64 = se_cat(tok, 0, "v" as *u8)
1811 mo = se_catn(tok, mo, sys_now_us())
1812 tok[mo] = 0 as u8
1813 }
1814 let n: i64 = sv2_form(prefix, base, id, tok, out, cap)
1815 if n < 0 { return sv2_err(out, "404 Not Found" as *u8, "no such survey" as *u8) }
1816 return n
1817 }
1818 return sv2_err(out, "404 Not Found" as *u8, "no such route" as *u8)
1819}
1820
1821// ---- offline selftest body (consumed by _hdl_build/nx_survey_serve_gate) ----
1822func sv2_ck(name: *u8, cond: i64, pass: *i64, fail: *i64) -> i64 {
1823 if cond == 1 { sv2_p(" PASS " as *u8); sv2_p(name); sv2_p("\n" as *u8); pass[0] = pass[0] + 1; return 1 }
1824 sv2_p(" FAIL " as *u8); sv2_p(name); sv2_p("\n" as *u8); fail[0] = fail[0] + 1
1825 return 0
1826}
1827// build+run one POST against sv2_handle (body must be pre-urlencoded)
1828func sv2_post(prefix: *u8, base: *u8, keypath: *u8, path: *u8, body: *u8, origin: *u8, out: *u8, cap: i64) -> i64 {
1829 let req: *u8 = sys_mmap(262144)
1830 var ro: i64 = se_cat(req, 0, "POST " as *u8)
1831 ro = se_cat(req, ro, path)
1832 ro = se_cat(req, ro, " HTTP/1.1\r\nHost: nishifamily.com" as *u8)
1833 if origin[0] != (0 as u8) {
1834 ro = se_cat(req, ro, "\r\nOrigin: " as *u8)
1835 ro = se_cat(req, ro, origin)
1836 }
1837 ro = se_cat(req, ro, "\r\nContent-Length: " as *u8)
1838 ro = se_catn(req, ro, se_slen(body))
1839 ro = se_cat(req, ro, "\r\n\r\n" as *u8)
1840 ro = se_cat(req, ro, body)
1841 return sv2_handle(prefix, base, keypath, req, ro, out, cap)
1842}
1843func sv2_get(prefix: *u8, base: *u8, keypath: *u8, path: *u8, out: *u8, cap: i64) -> i64 {
1844 let req: *u8 = sys_mmap(4096)
1845 var ro: i64 = se_cat(req, 0, "GET " as *u8)
1846 ro = se_cat(req, ro, path)
1847 ro = se_cat(req, ro, " HTTP/1.1\r\nHost: nishifamily.com\r\n\r\n" as *u8)
1848 return sv2_handle(prefix, base, keypath, req, ro, out, cap)
1849}
1850func sv2_selftest() -> i64 {
1851 sv2_p("=== NX-SURVEY-SERVE SELFTEST (forms, ballots, aggregates, k-floor, admin lane, XSS, mount) ===\n" as *u8)
1852 let pass: *i64 = sys_mmap(16) as *i64
1853 let fail: *i64 = sys_mmap(16) as *i64
1854 pass[0] = 0
1855 fail[0] = 0
1856 // hermetic store prefix + admin key file
1857 let pfx: *u8 = sys_mmap(64)
1858 var pp: i64 = se_cat(pfx, 0, "/tmp/svys_" as *u8)
1859 pp = se_catn(pfx, pp, sys_now_us())
1860 pp = se_catc(pfx, pp, 45)
1861 pfx[pp] = 0 as u8
1862 let keypath: *u8 = sys_mmap(64)
1863 var kp: i64 = se_cat(keypath, 0, "/tmp/svyk_" as *u8)
1864 kp = se_catn(keypath, kp, sys_now_us())
1865 keypath[kp] = 0 as u8
1866 sv2_write_file(keypath, "test-admin-key-2026\n" as *u8, 20)
1867 let base: *u8 = sys_mmap(4)
1868 base[0] = 0 as u8
1869 let noorig: *u8 = sys_mmap(4)
1870 noorig[0] = 0 as u8
1871 let out: *u8 = sys_mmap(1048576)
1872 // S1 admin load (title carries a <script> probe for the XSS teeth)
1873 var n: i64 = sv2_post(pfx, base, keypath, "/admin/load" as *u8, "key=test-admin-key-2026&spec=%40survey+pulse%0A%40title+Pulse+%3Cscript%3Ealert%281%29%3C%2Fscript%3E%0A%40anon+1%0A%40kmin+2%0AQ%7Cq1%7CC%7CBest+day%7CMon%7CTue%7CWed%0A" as *u8, noorig, out, 1048576)
1874 var s1: i64 = 0
1875 if sv2_memhas(out, n, "LOADED id=pulse nq=1" as *u8) == 1 { s1 = 1 }
1876 sv2_ck("S1 admin load -> LOADED nq=1" as *u8, s1, pass, fail)
1877 // S2 home lists it, escaped
1878 n = sv2_get(pfx, base, keypath, "/" as *u8, out, 1048576)
1879 var s2: i64 = 0
1880 if sv2_memhas(out, n, "<script>" as *u8) == 1 { if sv2_memhas(out, n, "<script>alert" as *u8) == 0 { if sv2_memhas(out, n, "kopen" as *u8) == 1 { s2 = 1 } } }
1881 sv2_ck("S2 home lists survey; title XSS-escaped" as *u8, s2, pass, fail)
1882 // S3 form renders radios + token
1883 n = sv2_get(pfx, base, keypath, "/s/pulse" as *u8, out, 1048576)
1884 var s3: i64 = 0
1885 if sv2_memhas(out, n, "name='q1' value='2'" as *u8) == 1 { if sv2_memhas(out, n, "name='tok'" as *u8) == 1 { if sv2_memhas(out, n, "<script>" as *u8) == 1 { s3 = 1 } } }
1886 sv2_ck("S3 form renders 3 options + hidden token; prompt escaped" as *u8, s3, pass, fail)
1887 // S3b ?t= token honored
1888 n = sv2_get(pfx, base, keypath, "/s/pulse?t=alice-tok" as *u8, out, 1048576)
1889 sv2_ck("S3b personal-link token lands in the form" as *u8, sv2_memhas(out, n, "value='alice-tok'" as *u8), pass, fail)
1890 // S4 respond -> 303 done
1891 n = sv2_post(pfx, base, keypath, "/s/pulse" as *u8, "tok=alice-tok&q1=1" as *u8, noorig, out, 1048576)
1892 var s4: i64 = 0
1893 if sv2_memhas(out, n, "303 See Other" as *u8) == 1 { if sv2_memhas(out, n, "/s/pulse/done" as *u8) == 1 { s4 = 1 } }
1894 sv2_ck("S4 respond -> 303 to done" as *u8, s4, pass, fail)
1895 // S5 k-floor: 1 ballot < kmin 2 -> withheld
1896 n = sv2_get(pfx, base, keypath, "/s/pulse/results" as *u8, out, 1048576)
1897 sv2_ck("S5 k-floor withholds below quorum" as *u8, sv2_memhas(out, n, "Results withheld" as *u8), pass, fail)
1898 // S6 second ballot -> results show
1899 sv2_post(pfx, base, keypath, "/s/pulse" as *u8, "tok=bob-tok00&q1=1" as *u8, noorig, out, 1048576)
1900 n = sv2_get(pfx, base, keypath, "/s/pulse/results" as *u8, out, 1048576)
1901 var s6: i64 = 0
1902 if sv2_memhas(out, n, "<b>2</b> response(s)" as *u8) == 1 { if sv2_memhas(out, n, "class='bar'" as *u8) == 1 { s6 = 1 } }
1903 sv2_ck("S6 at quorum: n=2 + bars render" as *u8, s6, pass, fail)
1904 // S6b anonymity: tokens never appear in public results
1905 var s6b: i64 = 1
1906 if sv2_memhas(out, n, "alice-tok" as *u8) == 1 { s6b = 0 }
1907 if sv2_memhas(out, n, "bob-tok00" as *u8) == 1 { s6b = 0 }
1908 sv2_ck("S6b tokens never rendered on results" as *u8, s6b, pass, fail)
1909 // S7 revote replaces
1910 sv2_post(pfx, base, keypath, "/s/pulse" as *u8, "tok=alice-tok&q1=2" as *u8, noorig, out, 1048576)
1911 n = sv2_get(pfx, base, keypath, "/s/pulse/results" as *u8, out, 1048576)
1912 sv2_ck("S7 revote: still 2 responses (replaced, not added)" as *u8, sv2_memhas(out, n, "<b>2</b> response(s)" as *u8), pass, fail)
1913 // S8 invalid answer 400
1914 n = sv2_post(pfx, base, keypath, "/s/pulse" as *u8, "tok=carol-tok&q1=9" as *u8, noorig, out, 1048576)
1915 sv2_ck("S8 invalid option -> 400" as *u8, sv2_memhas(out, n, "400" as *u8), pass, fail)
1916 // S9 cross-origin POST 403
1917 n = sv2_post(pfx, base, keypath, "/s/pulse" as *u8, "tok=evil-tok0&q1=1" as *u8, "http://evil.example" as *u8, out, 1048576)
1918 sv2_ck("S9 cross-origin POST -> 403" as *u8, sv2_memhas(out, n, "403" as *u8), pass, fail)
1919 // S10 admin close -> respond 409, form shows closed
1920 n = sv2_post(pfx, base, keypath, "/admin/close" as *u8, "key=test-admin-key-2026&id=pulse" as *u8, noorig, out, 1048576)
1921 var s10: i64 = 0
1922 if sv2_memhas(out, n, "CLOSED pulse" as *u8) == 1 {
1923 n = sv2_post(pfx, base, keypath, "/s/pulse" as *u8, "tok=dave-tok0&q1=1" as *u8, noorig, out, 1048576)
1924 if sv2_memhas(out, n, "409" as *u8) == 1 { s10 = 1 }
1925 }
1926 sv2_ck("S10 close freezes: respond -> 409" as *u8, s10, pass, fail)
1927 n = sv2_get(pfx, base, keypath, "/s/pulse" as *u8, out, 1048576)
1928 sv2_ck("S10b closed form points to results" as *u8, sv2_memhas(out, n, "This survey is closed" as *u8), pass, fail)
1929 // S11 admin auth: wrong key + fail-closed missing keyfile
1930 n = sv2_post(pfx, base, keypath, "/admin/close" as *u8, "key=wrong-key-here-x&id=pulse" as *u8, noorig, out, 1048576)
1931 var s11: i64 = sv2_memhas(out, n, "403" as *u8)
1932 let nokey: *u8 = "/tmp/svyk_absent_never" as *u8
1933 n = sv2_post(pfx, base, nokey, "/admin/close" as *u8, "key=test-admin-key-2026&id=pulse" as *u8, noorig, out, 1048576)
1934 var s11b: i64 = sv2_memhas(out, n, "403" as *u8)
1935 var s11ok: i64 = 0
1936 if s11 == 1 { if s11b == 1 { s11ok = 1 } }
1937 sv2_ck("S11 wrong key 403 + absent keyfile FAIL-CLOSED 403" as *u8, s11ok, pass, fail)
1938 // S12 deep survey with admin-only results
1939 n = sv2_post(pfx, base, keypath, "/admin/load" as *u8, "key=test-admin-key-2026&spec=%40survey+deep%0A%40title+Deep%0A%40anon+0%0A%40kmin+1%0A%40results+admin%0A%40maxtext+60%0AQ%7Cw1%7CS%7C0%7C10%7CFood+security%0AQ%7Cw2%7CT%7CWhat+would+help%0A" as *u8, noorig, out, 1048576)
1940 var s12: i64 = sv2_memhas(out, n, "LOADED id=deep nq=2" as *u8)
1941 n = sv2_post(pfx, base, keypath, "/s/deep" as *u8, "tok=dave-tok0&w1=3&w2=need+steady+work" as *u8, noorig, out, 1048576)
1942 var s12b: i64 = sv2_memhas(out, n, "303" as *u8)
1943 var s12ok: i64 = 0
1944 if s12 == 1 { if s12b == 1 { s12ok = 1 } }
1945 sv2_ck("S12 deep survey loads + records (scale + text)" as *u8, s12ok, pass, fail)
1946 n = sv2_get(pfx, base, keypath, "/s/deep/results" as *u8, out, 1048576)
1947 sv2_ck("S12b @results admin -> public results 403" as *u8, sv2_memhas(out, n, "403" as *u8), pass, fail)
1948 n = sv2_post(pfx, base, keypath, "/admin/report" as *u8, "key=test-admin-key-2026&id=deep" as *u8, noorig, out, 1048576)
1949 var s12c: i64 = 0
1950 if sv2_memhas(out, n, "need steady work" as *u8) == 1 { if sv2_memhas(out, n, "avg=3.00" as *u8) == 1 { if sv2_memhas(out, n, "[rsp:deep:dave-tok0]" as *u8) == 1 { s12c = 1 } } }
1951 sv2_ck("S12c admin report carries text answer + avg + identified token (anon=0)" as *u8, s12c, pass, fail)
1952 // S13 unknown survey 404
1953 n = sv2_get(pfx, base, keypath, "/s/ghost" as *u8, out, 1048576)
1954 sv2_ck("S13 unknown survey -> 404" as *u8, sv2_memhas(out, n, "404" as *u8), pass, fail)
1955 // S14 mount prefix + query strip
1956 let mbase: *u8 = "/survey" as *u8
1957 n = sv2_get(pfx, mbase, keypath, "/survey/s/deep?b=cache" as *u8, out, 1048576)
1958 var s14: i64 = 0
1959 if sv2_memhas(out, n, "action='/survey/s/deep'" as *u8) == 1 { if sv2_memhas(out, n, "/survey/app.js" as *u8) == 1 { s14 = 1 } }
1960 sv2_ck("S14 mounted at /survey: query stripped, links carry prefix" as *u8, s14, pass, fail)
1961 n = sv2_get(pfx, mbase, keypath, "/survey" as *u8, out, 1048576)
1962 sv2_ck("S14b /survey bare -> home" as *u8, sv2_memhas(out, n, "Nishi Pulse" as *u8), pass, fail)
1963 // S15 done page
1964 n = sv2_get(pfx, base, keypath, "/s/deep/done" as *u8, out, 1048576)
1965 sv2_ck("S15 done page renders" as *u8, sv2_memhas(out, n, "Thank you" as *u8), pass, fail)
1966 // ---- S16-S19 ANALYTICS: load a 5-question survey, respond, check public metrics + admin insights ----
1967 n = sv2_post(pfx, base, keypath, "/admin/load" as *u8, "key=test-admin-key-2026&spec=%40survey+ana%0A%40title+Ana%0A%40anon+0%0A%40kmin+1%0AQ%7Cfood%7CS%7C0%7C10%7CFood+security%0AQ%7Coverall%7CS%7C0%7C10%7COverall+wellbeing%0AQ%7Chouse%7CC%7CHousing%7CStable%7CRisk%7CCrisis%0AQ%7Cwork%7CC%7CWork%7CEmployed%7CUnemployed%0AQ%7Chelp%7CT%7CWhat+helps%0A" as *u8, noorig, out, 1048576)
1968 var s16load: i64 = sv2_memhas(out, n, "LOADED id=ana nq=5" as *u8)
1969 sv2_post(pfx, base, keypath, "/s/ana" as *u8, "tok=aa000000&food=10&overall=9&house=0&work=0&help=need+work" as *u8, noorig, out, 1048576)
1970 sv2_post(pfx, base, keypath, "/s/ana" as *u8, "tok=bb000000&food=3&overall=4&house=2&work=1&help=housing+help" as *u8, noorig, out, 1048576)
1971 sv2_post(pfx, base, keypath, "/s/ana" as *u8, "tok=cc000000&food=7&overall=7&house=0&work=0&help=need+work+now" as *u8, noorig, out, 1048576)
1972 sv2_post(pfx, base, keypath, "/s/ana" as *u8, "tok=dd000000&food=2&overall=3&house=1&work=1&help=more+work" as *u8, noorig, out, 1048576)
1973 sv2_ck("S16 analytics survey loaded + 4 ballots" as *u8, s16load, pass, fail)
1974 // public results carry the whole-sample insight strip (top-2-box + margin + NPS)
1975 n = sv2_get(pfx, base, keypath, "/s/ana/results" as *u8, out, 1048576)
1976 var s17: i64 = 0
1977 if sv2_memhas(out, n, "Top-2-box" as *u8) == 1 { if sv2_memhas(out, n, "margin" as *u8) == 1 { if sv2_memhas(out, n, "NPS" as *u8) == 1 { s17 = 1 } } }
1978 sv2_ck("S17 public results show top-2-box + margin + NPS" as *u8, s17, pass, fail)
1979 // admin insights report: descriptive + concern + segment breakdown + cross-tab chi-square + drivers + themes
1980 n = sv2_post(pfx, base, keypath, "/admin/insights" as *u8, "key=test-admin-key-2026&id=ana" as *u8, noorig, out, 1048576)
1981 var s18: i64 = 0
1982 if sv2_memhas(out, n, "Insights report" as *u8) == 1 {
1983 if sv2_memhas(out, n, "Scale summary" as *u8) == 1 {
1984 if sv2_memhas(out, n, "Concern flags" as *u8) == 1 {
1985 if sv2_memhas(out, n, "chi-square" as *u8) == 1 {
1986 if sv2_memhas(out, n, "Key drivers" as *u8) == 1 {
1987 if sv2_memhas(out, n, "Themes" as *u8) == 1 { s18 = 1 } } } } } }
1988 sv2_ck("S18 admin insights: summary+concern+cross-tab+drivers+themes render" as *u8, s18, pass, fail)
1989 // the open-text theme 'work' surfaces in the report
1990 sv2_ck("S18b open-text theme 'work' surfaces" as *u8, sv2_memhas(out, n, ">work " as *u8), pass, fail)
1991 // insights is admin-gated: wrong key -> 403, no leak
1992 n = sv2_post(pfx, base, keypath, "/admin/insights" as *u8, "key=wrong-key-here-x&id=ana" as *u8, noorig, out, 1048576)
1993 var s19: i64 = 0
1994 if sv2_memhas(out, n, "403" as *u8) == 1 { if sv2_memhas(out, n, "Insights report" as *u8) == 0 { s19 = 1 } }
1995 sv2_ck("S19 insights admin-gated: wrong key 403, no leak" as *u8, s19, pass, fail)
1996 // ---- S20 DATA QUALITY: a survey with a @check attention item; clean + careless responses screened ----
1997 n = sv2_post(pfx, base, keypath, "/admin/load" as *u8, "key=test-admin-key-2026&spec=%40survey+qa%0A%40title+QA%0A%40anon+1%0A%40kmin+1%0AQ%7Cs1%7CS%7C0%7C10%7CScale+one%0AQ%7Cs2%7CS%7C0%7C10%7CScale+two%0AQ%7Cs3%7CS%7C0%7C10%7CScale+three%0AQ%7Catt%7CC%7CAttention+pick+B%7CA%7CB%7CC%0A%40check+att+1%0A" as *u8, noorig, out, 1048576)
1998 var s20load: i64 = sv2_memhas(out, n, "LOADED id=qa nq=4" as *u8)
1999 sv2_post(pfx, base, keypath, "/s/qa" as *u8, "tok=qa-clean0&s1=8&s2=6&s3=7&att=1" as *u8, noorig, out, 1048576)
2000 sv2_post(pfx, base, keypath, "/s/qa" as *u8, "tok=qa-carel0&s1=5&s2=5&s3=5&att=0" as *u8, noorig, out, 1048576)
2001 sv2_ck("S20 QA survey with @check loaded + clean/careless responses" as *u8, s20load, pass, fail)
2002 n = sv2_post(pfx, base, keypath, "/admin/insights" as *u8, "key=test-admin-key-2026&id=qa" as *u8, noorig, out, 1048576)
2003 var s20: i64 = 0
2004 if sv2_memhas(out, n, "Data quality" as *u8) == 1 {
2005 if sv2_memhas(out, n, "Failed an attention check" as *u8) == 1 {
2006 if sv2_memhas(out, n, "Straight-lined" as *u8) == 1 { s20 = 1 } }
2007 }
2008 sv2_ck("S20b insights renders Data quality (attention + straight-line screening)" as *u8, s20, pass, fail)
2009 // ---- S21 finalize: strict-HTTP/1.1 Content-Length for the sovereign browser behind the buffered edge ----
2010 let fsrc: *u8 = sys_mmap(256)
2011 var fo: i64 = se_cat(fsrc, 0, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<b>hi</b>" as *u8)
2012 let fdst: *u8 = sys_mmap(512)
2013 let fn: i64 = sv2_finalize(fsrc, fo, fdst, 512)
2014 var s21: i64 = 0
2015 if sv2_memhas(fdst, fn, "Content-Length: 9" as *u8) == 1 { if sv2_memhas(fdst, fn, "<b>hi</b>" as *u8) == 1 { s21 = 1 } }
2016 sv2_ck("S21 finalize adds Content-Length (body=9) for strict clients" as *u8, s21, pass, fail)
2017 let gsrc: *u8 = sys_mmap(128)
2018 var go: i64 = se_cat(gsrc, 0, "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nabc" as *u8)
2019 let gdst: *u8 = sys_mmap(128)
2020 let gnn: i64 = sv2_finalize(gsrc, go, gdst, 128)
2021 var s21b: i64 = 0
2022 if gnn == go { s21b = 1 }
2023 sv2_ck("S21b response already carrying Content-Length passes through unchanged" as *u8, s21b, pass, fail)
2024 // ---- S22 results.json: machine-readable aggregate API on the 'ana' survey (4 ballots from S16) ----
2025 n = sv2_get(pfx, base, keypath, "/s/ana/results.json" as *u8, out, 1048576)
2026 var s22: i64 = 0
2027 if sv2_memhas(out, n, "application/json" as *u8) == 1 {
2028 if sv2_memhas(out, n, "\"survey\":\"ana\"" as *u8) == 1 {
2029 if sv2_memhas(out, n, "\"responses\":4" as *u8) == 1 {
2030 if sv2_memhas(out, n, "\"mean_permille\":5500" as *u8) == 1 {
2031 if sv2_memhas(out, n, "\"nps\":-25" as *u8) == 1 { s22 = 1 } } } }
2032 }
2033 sv2_ck("S22 results.json: exact aggregates (survey/responses/mean_permille/nps)" as *u8, s22, pass, fail)
2034 var s22b: i64 = 0
2035 if sv2_memhas(out, n, "\"label\":\"Stable\"" as *u8) == 1 { if sv2_memhas(out, n, "\"count\":2" as *u8) == 1 { if sv2_memhas(out, n, "\"text_count\":4" as *u8) == 1 { s22b = 1 } } }
2036 sv2_ck("S22b results.json: choice options + text_count present" as *u8, s22b, pass, fail)
2037 // PRIVACY: free-text answers must NEVER appear in the machine API
2038 var s22c: i64 = 1
2039 if sv2_memhas(out, n, "housing help" as *u8) == 1 { s22c = 0 }
2040 if sv2_memhas(out, n, "need work" as *u8) == 1 { s22c = 0 }
2041 sv2_ck("S22c results.json privacy: no free-text answers leak" as *u8, s22c, pass, fail)
2042 // admin-results survey -> results.json 403
2043 n = sv2_get(pfx, base, keypath, "/s/deep/results.json" as *u8, out, 1048576)
2044 sv2_ck("S22d results.json honors @results admin -> 403" as *u8, sv2_memhas(out, n, "403" as *u8), pass, fail)
2045 // ---- S23 EMBEDDABLE POLL: /embed/<id> compact iframe poll + cross-origin vote ----
2046 n = sv2_post(pfx, base, keypath, "/admin/load" as *u8, "key=test-admin-key-2026&spec=%40survey+poll1%0A%40title+Poll+One%0A%40anon+1%0AQ%7Cq1%7CC%7CFavorite%7CApple%7CBanana%7CCherry%0A" as *u8, noorig, out, 1048576)
2047 var s23load: i64 = sv2_memhas(out, n, "LOADED id=poll1 nq=1" as *u8)
2048 sv2_ck("S23 poll1 (1-Q) loaded" as *u8, s23load, pass, fail)
2049 n = sv2_get(pfx, base, keypath, "/embed/poll1" as *u8, out, 1048576)
2050 var s23b: i64 = 0
2051 if sv2_memhas(out, n, "action='/embed/poll1'" as *u8) == 1 { if sv2_memhas(out, n, "name='q1'" as *u8) == 1 { if sv2_memhas(out, n, "Apple" as *u8) == 1 { if sv2_memhas(out, n, "/app.js" as *u8) == 1 { s23b = 1 } } } }
2052 sv2_ck("S23b GET /embed/poll1 = compact poll (radios + action + app.js)" as *u8, s23b, pass, fail)
2053 // cross-origin vote (embedded on another site) MUST be accepted, not 403
2054 n = sv2_post(pfx, base, keypath, "/embed/poll1" as *u8, "tok=embedtoken1&q1=1" as *u8, "http://tandem.example" as *u8, out, 1048576)
2055 var s23c: i64 = 0
2056 if sv2_memhas(out, n, "403" as *u8) == 0 { if sv2_memhas(out, n, "response(s)" as *u8) == 1 { if sv2_memhas(out, n, "class='bar'" as *u8) == 1 { s23c = 1 } } }
2057 sv2_ck("S23c cross-origin embed vote accepted (NOT 403) -> inline results bars" as *u8, s23c, pass, fail)
2058 // the results page surfaces the embed snippet
2059 n = sv2_get(pfx, base, keypath, "/s/poll1/results" as *u8, out, 1048576)
2060 var s23d: i64 = 0
2061 if sv2_memhas(out, n, "Embed this poll" as *u8) == 1 { if sv2_memhas(out, n, "/embed/poll1" as *u8) == 1 { s23d = 1 } }
2062 sv2_ck("S23d results page surfaces the embed snippet (tools-in-UI)" as *u8, s23d, pass, fail)
2063 // ---- S24 RESPONSE QUOTA: @quota caps DISTINCT respondents; revote exempt; form shows progress/full ----
2064 n = sv2_post(pfx, base, keypath, "/admin/load" as *u8, "key=test-admin-key-2026&spec=%40survey+cap1%0A%40title+Capped%0A%40quota+2%0AQ%7Cq1%7CC%7CPick%7CYes%7CNo%0A" as *u8, noorig, out, 1048576)
2065 sv2_ck("S24 quota survey cap1 loaded (@quota 2)" as *u8, sv2_memhas(out, n, "LOADED id=cap1 nq=1" as *u8), pass, fail)
2066 n = sv2_get(pfx, base, keypath, "/s/cap1" as *u8, out, 1048576)
2067 sv2_ck("S24b open form shows progress '0 of 2 responses received'" as *u8, sv2_memhas(out, n, "0 of 2 responses received" as *u8), pass, fail)
2068 n = sv2_post(pfx, base, keypath, "/s/cap1" as *u8, "tok=qa-tok0&q1=0" as *u8, noorig, out, 1048576)
2069 sv2_ck("S24c respondent A accepted -> 303 (1 of 2)" as *u8, sv2_memhas(out, n, "303" as *u8), pass, fail)
2070 n = sv2_post(pfx, base, keypath, "/s/cap1" as *u8, "tok=qb-tok0&q1=1" as *u8, noorig, out, 1048576)
2071 sv2_ck("S24d respondent B accepted -> 303 (2 of 2 = full)" as *u8, sv2_memhas(out, n, "303" as *u8), pass, fail)
2072 n = sv2_post(pfx, base, keypath, "/s/cap1" as *u8, "tok=qc-tok0&q1=0" as *u8, noorig, out, 1048576)
2073 var s24e: i64 = 0
2074 if sv2_memhas(out, n, "409" as *u8) == 1 { if sv2_memhas(out, n, "quota" as *u8) == 1 { s24e = 1 } }
2075 sv2_ck("S24e NEW respondent C REFUSED -> 409 quota reached" as *u8, s24e, pass, fail)
2076 n = sv2_post(pfx, base, keypath, "/s/cap1" as *u8, "tok=qa-tok0&q1=1" as *u8, noorig, out, 1048576)
2077 sv2_ck("S24f EXISTING respondent A revote accepted -> 303 (exempt, never inflates)" as *u8, sv2_memhas(out, n, "303" as *u8), pass, fail)
2078 n = sv2_get(pfx, base, keypath, "/s/cap1" as *u8, out, 1048576)
2079 var s24g: i64 = 0
2080 if sv2_memhas(out, n, "reached its response quota" as *u8) == 1 { if sv2_memhas(out, n, "See the results" as *u8) == 1 { if sv2_memhas(out, n, "name='q1'" as *u8) == 0 { s24g = 1 } } }
2081 sv2_ck("S24g full form WITHHOLDS the questionnaire, offers results link" as *u8, s24g, pass, fail)
2082 // ---- S25 OPEN-TEXT SENTIMENT: the admin Tone card scores written answers (warming/cooling + split) ----
2083 n = sv2_post(pfx, base, keypath, "/admin/load" as *u8, "key=test-admin-key-2026&spec=%40survey+tone1%0A%40title+Tone+Test%0AQ%7Cc1%7CT%7CHow+are+things%3F%0A" as *u8, noorig, out, 1048576)
2084 sv2_ck("S25 tone survey (1 text question) loaded" as *u8, sv2_memhas(out, n, "LOADED id=tone1 nq=1" as *u8), pass, fail)
2085 n = sv2_post(pfx, base, keypath, "/s/tone1" as *u8, "tok=tt-a1&c1=the+staff+were+very+helpful+and+kind" as *u8, noorig, out, 1048576)
2086 n = sv2_post(pfx, base, keypath, "/s/tone1" as *u8, "tok=tt-b1&c1=not+helpful+at+all" as *u8, noorig, out, 1048576)
2087 n = sv2_post(pfx, base, keypath, "/s/tone1" as *u8, "tok=tt-c1&c1=the+meeting+is+at+noon" as *u8, noorig, out, 1048576)
2088 n = sv2_post(pfx, base, keypath, "/admin/insights" as *u8, "key=test-admin-key-2026&id=tone1" as *u8, noorig, out, 1048576)
2089 var s25: i64 = 0
2090 if sv2_memhas(out, n, "Tone —" as *u8) == 1 { if sv2_memhas(out, n, "Warming" as *u8) == 1 { if sv2_memhas(out, n, "1 positive" as *u8) == 1 { if sv2_memhas(out, n, "1 negative" as *u8) == 1 { s25 = 1 } } } }
2091 sv2_ck("S25a Tone card: Warming + 1 positive + 1 negative (mean +136, negation-aware)" as *u8, s25, pass, fail)
2092 sv2_ck("S25b mean tone +136 permille rendered" as *u8, sv2_memhas(out, n, "+136‰" as *u8), pass, fail)
2093 // negation adversary end-to-end: 'not helpful' must NOT be miscounted positive (would be 2 pos / 0 neg)
2094 var s25c: i64 = 0
2095 if sv2_memhas(out, n, "2 positive" as *u8) == 0 { if sv2_memhas(out, n, "1 negative" as *u8) == 1 { s25c = 1 } }
2096 sv2_ck("S25c NEGATION honored live: 'not helpful' scored negative, not positive" as *u8, s25c, pass, fail)
2097 // sentiment is admin-walled: the PUBLIC results page must NOT carry the Tone card
2098 n = sv2_get(pfx, base, keypath, "/s/tone1/results" as *u8, out, 1048576)
2099 var s25d: i64 = 0
2100 if sv2_memhas(out, n, "Tone —" as *u8) == 0 { s25d = 1 }
2101 sv2_ck("S25d sentiment is admin-only: public results has NO Tone card" as *u8, s25d, pass, fail)
2102 sv2_p("NX-SURVEY-SERVE SELFTEST pass=" as *u8)
2103 sv2_pn(pass[0])
2104 sv2_p(" fail=" as *u8)
2105 sv2_pn(fail[0])
2106 if fail[0] == 0 { sv2_p(" verdict=GREEN (survey surface: ballots, aggregates, k-floor, admin lane, privacy walls)\n" as *u8); return 0 }
2107 sv2_p(" verdict=RED\n" as *u8)
2108 return 1
2109}