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 · 3 importers
imports: nx_syscalls.nxnx_survey.nx
imported by: nx_cell_lib.nxnx_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 |
| 38 | const SE_WALK_CAPMAX: i64 = 512 |
| 39 | const SE_WALK_RING: i64 = 4 |
| 40 | const SE_WALK_KEYB: i64 = 160 // per-key pool bytes (key <=150 + NUL, headroom named) |
| 41 | const SE_WALK_KBSCR: i64 = 176 // per-key hash/compare scratch |
| 46 | const SE_MAXQ: i64 = 24 // questions per survey (protocol bound, refused LOUD at load) |
| 47 | const SE_MAXOPT: i64 = 12 // options per choice question |
| 48 | const SE_DEF_MAXTEXT: i64 = 2000 // text-answer protocol cap; spec @maxtext may lower it |
functions
| 50 | func se_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 51 | 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 } |
| 52 | func se_catc(dst: *u8, off: i64, code: i64) -> i64 { dst[off] = code as u8; return off + 1 } |
| 53 | func se_catn(dst: *u8, off: i64, v: i64) -> i64 |
| 65 | func se_seq(a: *u8, b: *u8) -> i64 |
| 71 | 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 } |
| 73 | func se_digits(s: *u8) -> i64 |
| 85 | func se_id_ok(s: *u8) -> i64 |
| 101 | func se_tok_ok(s: *u8) -> i64 |
| 117 | func se_qid_ok(s: *u8) -> i64 called by 1: se_load |
| 133 | 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 } |
| 134 | func se_key_qst(id: *u8, n: i64, out: *u8) -> i64 |
| 142 | func se_key_rsp(id: *u8, tok: *u8, out: *u8) -> i64 |
| 162 | func se_get_dec(prefix: *u8, key: *u8, ks: *i64, vs: *i64, maxf: i64) -> i64 |
| 173 | func se_meta(prefix: *u8, id: *u8, ks: *i64, vs: *i64, maxf: i64) -> i64 |
| 179 | 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 |
| 185 | func se_field_n(ks: *i64, vs: *i64, nf: i64, key: *u8, dflt: i64) -> i64 |
| 192 | func se_nopts(opts: *u8) -> i64 |
| 200 | func se_opt(opts: *u8, k: i64, out: *u8, cap: i64) -> i64 |
| 224 | func se_lfield(b: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 called by 1: se_load |
| 248 | func se_meta_val(b: *u8, ls: i64, le: i64, pfxlen: i64, out: *u8, cap: i64) -> i64 |
| 256 | func se_line_starts(b: *u8, ls: i64, le: i64, pfx: *u8) -> i64 |
| 267 | func se_load(prefix: *u8, spec: *u8, slen: i64, idout: *u8, idcap: i64) -> i64 called by 4: cp_instrumentmainmainsv2_handle calls 19: sys_mmapse_catse_line_startsse_meta_valsv_atoise_lfield+13 |
| 482 | func se_close(prefix: *u8, id: *u8) -> i64 |
| 511 | func se_respond(prefix: *u8, id: *u8, tok: *u8, aq: *i64, av: *i64, na: i64) -> i64 |
| 629 | func se_walk_latest(prefix: *u8, keypfx: *u8, kout: *i64, vout: *i64, lout: *i64, cap: i64) -> i64 |
| 718 | func se_ballots(prefix: *u8, id: *u8, kout: *i64, vout: *i64, lout: *i64, cap: i64) -> i64 called by 8: cp_cell_ballotsse_respondmainsn_aggregatesv2_formsv2_results+2 calls 4: sys_mmapse_catse_catcse_walk_latest |
| 742 | func se_answer_of(vp: *u8, vl: i64, qid: *u8, out: *u8, cap: i64) -> i64 |
| 760 | func se_count_eq(vout: *i64, lout: *i64, nb: i64, qid: *u8, want: *u8) -> i64 |
| 773 | func se_num_stats(vout: *i64, lout: *i64, nb: i64, qid: *u8, outs: *i64) -> i64 |
| 792 | func se_text_count(vout: *i64, lout: *i64, nb: i64, qid: *u8) -> i64 |
| 803 | func se_surveys(prefix: *u8, kout: *i64, vout: *i64, lout: *i64, cap: i64) -> i64 |