code wiki / (root) / nx_survey_serve.nx

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, "&amp;" as *u8) } else { 89 if c == 60 { o = se_cat(dst, o, "&lt;" as *u8) } else { 90 if c == 62 { o = se_cat(dst, o, "&gt;" 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 &mdash; sovereign polls &amp; 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 &mdash; 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) &middot; <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, "/'>&larr; 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 &mdash; 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, "&ndash;" as *u8) 444 o = se_catn(out, o, hi) 445 o = se_cat(out, o, " &mdash; 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 &mdash; 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, "/'>&larr; 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 &mdash; aggregate only" as *u8) 491 if (stt as i64) != 0 { if se_seq(stt, "open" as *u8) == 0 { o = se_cat(out, o, " &middot; 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 &plusmn;" 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 &plusmn;" 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, " &middot; 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) &mdash; 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 &mdash; 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> &middot; <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, "&mdash;" as *u8) } 792 o = se_cat(out, o, "</td></tr>" as *u8) 793 } else { o = se_cat(out, o, "</td><td>&mdash;</td><td>&mdash;</td><td>&mdash;</td><td>&mdash;</td><td>&mdash;</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 &mdash; 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 &mdash; 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, " &mdash; 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, "&mdash;" 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, " &times; " 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, " &mdash; " as *u8) 913 if sig == 1 { o = se_cat(out, o, "<b>significant</b> association (p &lt; .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 &ldquo;" as *u8) 1107 o = sv2_esc(out, o, qprompt[outc] as *u8) 1108 o = se_cat(out, o, "&rdquo;</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 (&minus;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 &mdash; " 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 &mdash; " 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, " &mdash; 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, "&permil; 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 &middot; " as *u8) 1204 o = se_catn(out, o, neu); o = se_cat(out, o, " neutral &middot; " as *u8) 1205 o = se_catn(out, o, neg); o = se_cat(out, o, " negative &mdash; 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 &middot; <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 &mdash; add a `@check &lt;qid&gt; &lt;answer&gt;` 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, "/'>&larr; 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 &mdash; <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, "&lt;script&gt;" 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, "&lt;script&gt;" 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 &mdash;" 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&permil;" 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 &mdash;" 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}