nx_survey_engine.nx
buildroot/runtime/nx_survey_engine.nx
about
nx_survey_engine.nx -- the GENERAL survey/poll engine (operator 2026-07-10: "census capability to get
feedback from customers and also employees ... generalized ... surveying lds members on how their welfare
needs are doing ... simple one question poll or a deeper survey").
COMPOSES the existing substrate, no new silo: nx_survey.nx (R0 situation-management intake lib: sv_get/
sv_answer/sv_triage/sv_band stay the scoring layer) + nx_seg_store (additive, tombstone-not-delete) +
nx_canon_cid/nx_uxf_decode (canonical records). A POLL is just a 1-question survey -- same engine.
DATA MODEL (everything is DATA, rule 11/25 -- a new survey is a spec file, never code):
spec text = "@survey <id>" "@title .." "@anon 0|1" "@kmin N" "@results pub|admin" "@audience .."
"@maxtext N" + one "Q|<qid>|C|<prompt>|opt1|opt2.." / "Q|<qid>|S|<min>|<max>|<prompt>"
/ "Q|<qid>|T|<prompt>" line per question.
store keys = "svy:<id>" (meta; close = a NEW version with status=closed -- latest wins, history kept)
"qst:<id>:<n>" (question n, 1-based)
"rsp:<id>:<token>" (ballot; a REVOTE is a NEW VERSION of the SAME key -> the seg_store's
per-key latest-wins IS revote-replace: one respondent = one effective ballot, the count
can never inflate, and every prior answer stays readable (additive law). The in-memory
incumbent NEG-CONTROL for this lives in nx_connect_polls; here it is BY CONSTRUCTION.)
PRIVACY: anon surveys never surface tokens; k-floor (kmin) withholds aggregates below quorum (the
banner-survey cardinal's k-anonymity floor, here as spec DATA); text answers are admin-report-only.
No floats, no hardware writes. license_tier: ORIGINAL
dependencies 2 imports · 2 importers
imports: nx_syscalls.nxnx_survey.nx
imported by: nx_survey_engine_gate.nxnx_survey_stats.nx
structs
| none |
consts
| 23 | const SE_MAGIC_1024: i64 = 1024 |
| 24 | const SE_MAGIC_16384: i64 = 16384 |
| 25 | const SE_MAGIC_65536: i64 = 65536 |
| 26 | const SE_MAGIC_4096: i64 = 4096 |
| 28 | const SE_MAXSEG: i64 = 4096 // manifest walk bound; tally goes LOUD -1 at the bound, never silent |
| 29 | const SE_MAXQ: i64 = 24 // questions per survey (protocol bound, refused LOUD at load) |
| 30 | const SE_MAXOPT: i64 = 12 // options per choice question |
| 31 | const SE_DEF_MAXTEXT: i64 = 2000 // text-answer protocol cap; spec @maxtext may lower it |
functions
| 33 | func se_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 34 | func se_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i } |
| 35 | func se_catc(dst: *u8, off: i64, code: i64) -> i64 { dst[off] = code as u8; return off + 1 } |
| 36 | func se_catn(dst: *u8, off: i64, v: i64) -> i64 |
| 48 | func se_seq(a: *u8, b: *u8) -> i64 |
| 54 | func se_starts(s: *u8, pfx: *u8) -> i64 { var i: i64 = 0; while pfx[i] != (0 as u8) { if s[i] != pfx[i] { return 0 } i = i + 1 } return 1 } called by 1: sv2_handle |
| 56 | func se_digits(s: *u8) -> i64 |
| 68 | func se_id_ok(s: *u8) -> i64 |
| 84 | func se_tok_ok(s: *u8) -> i64 |
| 100 | func se_qid_ok(s: *u8) -> i64 called by 1: se_load |
| 116 | func se_key_svy(id: *u8, out: *u8) -> i64 { var o: i64 = se_cat(out, 0, "svy:" as *u8); o = se_cat(out, o, id); out[o] = 0 as u8; return o } |
| 117 | func se_key_qst(id: *u8, n: i64, out: *u8) -> i64 |
| 125 | func se_key_rsp(id: *u8, tok: *u8, out: *u8) -> i64 |
| 137 | func se_get_dec(prefix: *u8, key: *u8, ks: *i64, vs: *i64, maxf: i64) -> i64 |
| 146 | func se_meta(prefix: *u8, id: *u8, ks: *i64, vs: *i64, maxf: i64) -> i64 |
| 152 | func se_qst(prefix: *u8, id: *u8, n: i64, ks: *i64, vs: *i64, maxf: i64) -> i64 called by 8: se_respondmainsv2_formsv2_resultssv2_insightssv2_embed_results+2 calls 3: sys_mmapse_key_qstse_get_dec |
| 158 | func se_field_n(ks: *i64, vs: *i64, nf: i64, key: *u8, dflt: i64) -> i64 |
| 165 | func se_nopts(opts: *u8) -> i64 |
| 173 | func se_opt(opts: *u8, k: i64, out: *u8, cap: i64) -> i64 |
| 197 | func se_lfield(b: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 called by 1: se_load |
| 221 | func se_meta_val(b: *u8, ls: i64, le: i64, pfxlen: i64, out: *u8, cap: i64) -> i64 called by 1: se_load |
| 229 | func se_line_starts(b: *u8, ls: i64, le: i64, pfx: *u8) -> i64 |
| 240 | func se_load(prefix: *u8, spec: *u8, slen: i64, idout: *u8, idcap: i64) -> i64 called by 3: mainmainsv2_handle calls 19: sys_mmapse_catse_line_startsse_meta_valsv_atoise_lfield+13 |
| 455 | func se_close(prefix: *u8, id: *u8) -> i64 |
| 484 | func se_respond(prefix: *u8, id: *u8, tok: *u8, aq: *i64, av: *i64, na: i64) -> i64 |
| 602 | func se_walk_latest(prefix: *u8, keypfx: *u8, kout: *i64, vout: *i64, lout: *i64, cap: i64) -> i64 |
| 678 | func se_ballots(prefix: *u8, id: *u8, kout: *i64, vout: *i64, lout: *i64, cap: i64) -> i64 called by 7: se_respondmainsn_aggregatesv2_formsv2_resultssv2_insights+1 calls 4: sys_mmapse_catse_catcse_walk_latest |
| 702 | func se_answer_of(vp: *u8, vl: i64, qid: *u8, out: *u8, cap: i64) -> i64 |
| 720 | func se_count_eq(vout: *i64, lout: *i64, nb: i64, qid: *u8, want: *u8) -> i64 |
| 733 | func se_num_stats(vout: *i64, lout: *i64, nb: i64, qid: *u8, outs: *i64) -> i64 |
| 752 | func se_text_count(vout: *i64, lout: *i64, nb: i64, qid: *u8) -> i64 |
| 763 | func se_surveys(prefix: *u8, kout: *i64, vout: *i64, lout: *i64, cap: i64) -> i64 |