code wiki / (root) / nx_survey_serve.nx

nx_survey_serve.nx source

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