nx_market_ladder_lib.nx source
↩ module page · 398 lines · 19498 B
1// nx_market_ladder_lib.nx -- THE MARKET-ENTRY VERDICT AS ONE RULER (2026-08-24, /compare PT6: ml_entry_verdict).
2// license_tier: ORIGINAL No hw writes (Rule 26). LIB (no main).
3//
4// THE OPERATOR ASK (2026-08-24): the ecosystem establishes best-in-class per rung (hobbyist -> small business ->
5// market leader -> innovation -> research) and says ENTER, HOLD or GROW-FIRST ITSELF. A ladder row in <dom>.plan
6// carries its narrative in prose and its REQUIREMENT as data: a 7th field naming the rung ids that must be LANDED
7// before that level is entered (ladder|level|best|have|grow|verdict|requires). This lib resolves each required
8// rung id -> its symbol (the plan's rung row) -> the organ that must carry it (the matrix row with that symbol,
9// `_ABSENT_:` stripped) -> declared or not, by THE ONE SYMBOL RULER (nx_symdecl_lib sd_declared, the same
10// function the page, the watch plane and the ranker use), so the verdict cannot disagree with the page.
11// all required rungs landed -> ENTER / some -> GROW-FIRST (binding = the first rung not landed) / none -> HOLD
12// empty requires -> NO-REQUIREMENT (the level is open by construction, e.g. hobbyist)
13// a rung id with no rung row, or a symbol with no matrix row -> UNRESOLVED (the plan and matrix disagree: named,
14// never guessed, never silently ENTER)
15// Both the generator (nx_swcompare_lib plan_pass) and the CLI (nx_market_ladder) call ml_verdict, so the page and
16// the command line prove the same code.
17import "nx_syscalls.nx"
18import "nx_symdecl_lib.nx"
19
20const ML_ENTER: i64 = 0
21const ML_GROW_FIRST: i64 = 1
22const ML_HOLD: i64 = 2
23const ML_NO_REQUIREMENT: i64 = 3
24const ML_UNRESOLVED: i64 = 4
25const ML_PROSE: i64 = 0 - 1
26const ML_PIPE: i64 = 124
27const ML_NL: i64 = 10
28const ML_HASH: i64 = 35
29const ML_AT: i64 = 64
30const ML_COMMA: i64 = 44
31const ML_SPACE: i64 = 32
32const ML_SLASH: i64 = 47
33const ML_SRC_ROOT: *u8 = "buildroot/" // matrix organ paths are RELATIVE TO buildroot/ (the two-trees law) -- the same prefix nx_compare_rank's rk_measure applies
34const ML_SRC_ROOT_CAP: i64 = 16
35const ML_ABSENT_PREFIX: *u8 = "_ABSENT_:"
36const ML_ABSENT_PREFIX_LEN: i64 = 9
37const ML_F_RUNG_ID: i64 = 1
38const ML_F_RUNG_SYM: i64 = 3
39const ML_F_MATRIX_ORGAN: i64 = 1
40const ML_F_MATRIX_SYM: i64 = 2
41const ML_PATH_CAP: i64 = 1024
42const ML_SYM_CAP: i64 = 256
43const ML_OUT_CODE: i64 = 0
44const ML_OUT_REQUIRED: i64 = 1
45const ML_OUT_LANDED: i64 = 2
46const ML_OUT_BINDING_OFF: i64 = 3
47const ML_OUT_BINDING_LEN: i64 = 4
48const ML_OUT_UNRESOLVED: i64 = 5
49const ML_OUT_SLOTS: i64 = 8
50const ML_SLOT: i64 = 8
51const ML_PLAN_SUFFIX: *u8 = ".plan"
52const ML_MATRIX_SUFFIX: *u8 = ".matrix"
53const ML_PLAN_SUFFIX_LEN: i64 = 5
54// EVIDENCE TOKENS (2026-08-24): a rung SYMBOL proves code exists, never that the physical bench ran. Two more token
55// kinds let a level require evidence: `gate:<name>` = the LAST harness.jrnl verdict for that gate name is GREEN
56// (nx_gate_verdict journals every run), `rows:<path>` = the data file holds at least one measured (non-comment) row.
57// Measured the day this shipped: with symbols alone the small-business level read ENTER while ZERO swatches existed.
58const ML_TOK_GATE: *u8 = "gate:"
59const ML_TOK_GATE_LEN: i64 = 5
60const ML_TOK_ROWS: *u8 = "rows:"
61const ML_TOK_ROWS_LEN: i64 = 5
62const ML_HARNESS_JRNL: *u8 = "knowledge/status/harness.jrnl"
63const ML_HARNESS_F_NAME: i64 = 2
64const ML_HARNESS_F_VERDICT: i64 = 4
65const ML_TAB: i64 = 9
66const ML_ROOT_PROBE: *u8 = "buildroot/knowledge/compare/regen.list" // exists only when the CWD is the nishihost root
67
68func ml_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
69// pipe field idx of line [s,e) -> box; 1 if present
70func ml_field(buf: *u8, s: i64, e: i64, idx: i64, box: *i64) -> i64 {
71 var p: i64 = s
72 var f: i64 = 0
73 while f < idx {
74 var go: i64 = 1
75 while go == 1 { if p >= e { go = 0 } else { if (buf[p] as i64) == ML_PIPE { p = p + 1; go = 0 } else { p = p + 1 } } }
76 f = f + 1
77 }
78 if p > e { box[0] = 0; box[1] = 0; return 0 }
79 var q: i64 = p
80 var g2: i64 = 1
81 while g2 == 1 { if q >= e { g2 = 0 } else { if (buf[q] as i64) == ML_PIPE { g2 = 0 } else { q = q + 1 } } }
82 box[0] = p
83 box[1] = q - p
84 return 1
85}
86func ml_span_same(a: *u8, ao: i64, an: i64, b: *u8, bo: i64, bn: i64) -> i64 {
87 if an != bn { return 0 }
88 var i: i64 = 0
89 while i < an { if a[ao + i] != b[bo + i] { return 0 } i = i + 1 }
90 return 1
91}
92func ml_span_starts(buf: *u8, off: i64, len: i64, pfx: *u8) -> i64 {
93 let pl: i64 = ml_slen(pfx)
94 if len < pl { return 0 }
95 var i: i64 = 0
96 while i < pl { if buf[off + i] != pfx[i] { return 0 } i = i + 1 }
97 return 1
98}
99// rung row for id (span in req) -> symbol span in plan buf; 1 if found
100func ml_find_rung_sym(plan: *u8, n: i64, req: *u8, ido: i64, idn: i64, out: *i64) -> i64 {
101 let box: *i64 = sys_mmap(ML_SLOT * 2) as *i64
102 var p: i64 = 0
103 while p < n {
104 var e: i64 = p
105 while e < n { if (plan[e] as i64) == ML_NL { break } e = e + 1 }
106 if e > p { if (plan[p] as i64) != ML_HASH {
107 ml_field(plan, p, e, 0, box)
108 if ml_span_starts(plan, box[0], box[1], "rung" as *u8) == 1 { if box[1] == 4 {
109 ml_field(plan, p, e, ML_F_RUNG_ID, box)
110 if ml_span_same(plan, box[0], box[1], req, ido, idn) == 1 {
111 if ml_field(plan, p, e, ML_F_RUNG_SYM, box) == 1 { out[0] = box[0]; out[1] = box[1]; return 1 }
112 return 0
113 }
114 } }
115 } }
116 p = e + 1
117 }
118 return 0
119}
120// matrix row whose symbol (after `_ABSENT_:`) equals the span -> organ span in matrix buf; 1 if found
121func ml_find_matrix_organ(mx: *u8, n: i64, sym: *u8, so: i64, sn: i64, out: *i64) -> i64 {
122 let box: *i64 = sys_mmap(ML_SLOT * 2) as *i64
123 var p: i64 = 0
124 while p < n {
125 var e: i64 = p
126 while e < n { if (mx[e] as i64) == ML_NL { break } e = e + 1 }
127 if e > p { if (mx[p] as i64) != ML_HASH { if (mx[p] as i64) != ML_AT {
128 if ml_field(mx, p, e, ML_F_MATRIX_SYM, box) == 1 {
129 var off: i64 = box[0]
130 var len: i64 = box[1]
131 if ml_span_starts(mx, off, len, ML_ABSENT_PREFIX) == 1 { off = off + ML_ABSENT_PREFIX_LEN; len = len - ML_ABSENT_PREFIX_LEN }
132 if ml_span_same(mx, off, len, sym, so, sn) == 1 {
133 if ml_field(mx, p, e, ML_F_MATRIX_ORGAN, box) == 1 { out[0] = box[0]; out[1] = box[1]; return 1 }
134 return 0
135 }
136 }
137 } } }
138 p = e + 1
139 }
140 return 0
141}
142// copy a span to a NUL-terminated scratch
143func ml_span_cstr(buf: *u8, off: i64, len: i64, dst: *u8, cap: i64) -> i64 {
144 var i: i64 = 0
145 while i < len { if i < cap - 1 { dst[i] = buf[off + i] } i = i + 1 }
146 if len < cap { dst[len] = 0 as u8 } else { dst[cap - 1] = 0 as u8 }
147 return len
148}
149// the sibling matrix path of a plan path (".plan" -> ".matrix"); 1 if the suffix matched
150func ml_sibling_matrix(plan_path: *u8, out: *u8, cap: i64) -> i64 {
151 let n: i64 = ml_slen(plan_path)
152 if n < ML_PLAN_SUFFIX_LEN { return 0 }
153 var i: i64 = 0
154 while i < ML_PLAN_SUFFIX_LEN { if plan_path[n - ML_PLAN_SUFFIX_LEN + i] != ML_PLAN_SUFFIX[i] { return 0 } i = i + 1 }
155 let base: i64 = n - ML_PLAN_SUFFIX_LEN
156 if base + ml_slen(ML_MATRIX_SUFFIX) + 1 > cap { return 0 }
157 i = 0
158 while i < base { out[i] = plan_path[i]; i = i + 1 }
159 var j: i64 = 0
160 while ML_MATRIX_SUFFIX[j] != (0 as u8) { out[base + j] = ML_MATRIX_SUFFIX[j]; j = j + 1 }
161 out[base + j] = 0 as u8
162 return 1
163}
164// THE VERDICT. req = the requires field (comma-separated rung ids, spaces tolerated). out slots per ML_OUT_*.
165// THE ORGAN-PATH RULE, one owner: a matrix organ column is relative to buildroot/ (nishihost/runtime is a 68-file
166// twin, not the source tree -- the same prefix nx_compare_rank's rk_measure applies); an absolute path (a gate
167// fixture) passes through unchanged. Returns the length written. Testable in-process, which is how the gate proves
168// the rule without a fixture inside the source tree.
169func ml_organ_path(orgc: *u8, out: *u8, cap: i64) -> i64 {
170 var o: i64 = 0
171 if (orgc[0] as i64) != ML_SLASH {
172 while ML_SRC_ROOT[o] != (0 as u8) { if o < cap - 1 { out[o] = ML_SRC_ROOT[o] } o = o + 1 }
173 }
174 var i: i64 = 0
175 while orgc[i] != (0 as u8) { if o + i < cap - 1 { out[o + i] = orgc[i] } i = i + 1 }
176 if o + i > cap - 1 { i = cap - 1 - o }
177 out[o + i] = 0 as u8
178 return o + i
179}
180// gate:<name> -> is the LAST harness.jrnl row for that gate name GREEN? (nx_gate_verdict appends ts harness name run VERDICT p/t)
181// EVIDENCE PATHS LIVE AT THE NISHIHOST ROOT (the gates journal there; the wash ledger is written there). The CLI runs
182// from that root; the regen chdirs to buildroot/, where the same bare path resolves into the OTHER knowledge tree and
183// every gate: token silently reads not-landed. MEASURED 2026-08-24: the served page said 4/7 landed while the CLI said
184// 6/7 for the same plan. One resolver, both token kinds: at the root the path is used as-is, under buildroot/ it is ../.
185const ML_UP: *u8 = "../"
186func ml_evidence_path(src: *u8, out: *u8, cap: i64) -> i64 {
187 var o: i64 = 0
188 if ml_at_root() == 0 { while ML_UP[o] != (0 as u8) { if o < cap - 1 { out[o] = ML_UP[o] } o = o + 1 } }
189 var i: i64 = 0
190 while src[i] != (0 as u8) { if o + i < cap - 1 { out[o + i] = src[i] } i = i + 1 }
191 if o + i > cap - 1 { i = cap - 1 - o }
192 out[o + i] = 0 as u8
193 return o + i
194}
195func ml_gate_green(name: *u8) -> i64 {
196 let lp: *i64 = sys_mmap(ML_SLOT * 2) as *i64
197 let jp: *u8 = sys_mmap(ML_PATH_CAP)
198 ml_evidence_path(ML_HARNESS_JRNL, jp, ML_PATH_CAP)
199 let b: *u8 = sys_read_file(jp, lp)
200 if (b as i64) == 0 { return 0 }
201 let n: i64 = lp[0]
202 let box: *i64 = sys_mmap(ML_SLOT * 2) as *i64
203 var last: i64 = 0
204 var p: i64 = 0
205 let nl: i64 = ml_slen(name)
206 while p < n {
207 var e: i64 = p
208 while e < n { if (b[e] as i64) == ML_NL { break } e = e + 1 }
209 if e > p {
210 // tab field 2 = name, field 4 = verdict
211 var f: i64 = 0
212 var q: i64 = p
213 var name_off: i64 = 0 - 1
214 var name_len: i64 = 0
215 var verd_off: i64 = 0 - 1
216 var verd_len: i64 = 0
217 var fs: i64 = p
218 while q <= e {
219 var fend: i64 = 0
220 if q == e { fend = 1 } else { if (b[q] as i64) == ML_TAB { fend = 1 } }
221 if fend == 1 {
222 if f == ML_HARNESS_F_NAME { name_off = fs; name_len = q - fs }
223 if f == ML_HARNESS_F_VERDICT { verd_off = fs; verd_len = q - fs }
224 f = f + 1
225 fs = q + 1
226 }
227 q = q + 1
228 }
229 if name_off >= 0 { if name_len == nl { if ml_span_same(b, name_off, name_len, name, 0, nl) == 1 {
230 if verd_off >= 0 { if ml_span_starts(b, verd_off, verd_len, "GREEN" as *u8) == 1 { last = 1 } else { last = 0 } }
231 } } }
232 }
233 p = e + 1
234 }
235 sys_free_file(b, n)
236 return last
237}
238// rows:<path> -> does the data file hold at least one measured (non-comment, non-blank) row?
239func ml_rows_present(path: *u8) -> i64 {
240 let lp: *i64 = sys_mmap(ML_SLOT * 2) as *i64
241 let rp: *u8 = sys_mmap(ML_PATH_CAP)
242 ml_evidence_path(path, rp, ML_PATH_CAP)
243 let b: *u8 = sys_read_file(rp, lp)
244 if (b as i64) == 0 { return 0 }
245 let n: i64 = lp[0]
246 var found: i64 = 0
247 var p: i64 = 0
248 while p < n {
249 var e: i64 = p
250 while e < n { if (b[e] as i64) == ML_NL { break } e = e + 1 }
251 if e > p { if (b[p] as i64) != ML_HASH {
252 var q: i64 = p
253 while q < e { if (b[q] as i64) != ML_SPACE { if (b[q] as i64) != ML_TAB { found = 1 } } q = q + 1 }
254 } }
255 if found == 1 { p = n } else { p = e + 1 }
256 }
257 sys_free_file(b, n)
258 return found
259}
260// WHICH TREE AM I STANDING IN? The CLI runs from the nishihost root (a relative organ path needs the buildroot/ prefix;
261// nishihost/runtime is a stale 68-file twin that must NOT be read); the regen chdirs to buildroot/ (the same path
262// resolves as-is, and a prefix would double it). The probe is the regen roster itself: it exists only under buildroot/.
263func ml_at_root() -> i64 {
264 let fd: i64 = sys_openat_rd(ML_ROOT_PROBE)
265 if fd < 0 { return 0 }
266 sys_close(fd)
267 return 1
268}
269func ml_entry_verdict(matrix_path: *u8, plan_path: *u8, req: *u8, out: *i64) -> i64 {
270 out[ML_OUT_CODE] = ML_NO_REQUIREMENT
271 out[ML_OUT_REQUIRED] = 0
272 out[ML_OUT_LANDED] = 0
273 out[ML_OUT_BINDING_OFF] = 0
274 out[ML_OUT_BINDING_LEN] = 0
275 out[ML_OUT_UNRESOLVED] = 0
276 let rn: i64 = ml_slen(req)
277 if rn == 0 { return ML_NO_REQUIREMENT }
278 let lp: *i64 = sys_mmap(ML_SLOT * 2) as *i64
279 let lm: *i64 = sys_mmap(ML_SLOT * 2) as *i64
280 let plan: *u8 = sys_read_file(plan_path, lp)
281 let mx: *u8 = sys_read_file(matrix_path, lm)
282 if (plan as i64) == 0 { out[ML_OUT_CODE] = ML_UNRESOLVED; return ML_UNRESOLVED }
283 if (mx as i64) == 0 { out[ML_OUT_CODE] = ML_UNRESOLVED; return ML_UNRESOLVED }
284 let symspan: *i64 = sys_mmap(ML_SLOT * 2) as *i64
285 let orgspan: *i64 = sys_mmap(ML_SLOT * 2) as *i64
286 let symc: *u8 = sys_mmap(ML_SYM_CAP)
287 let orgc: *u8 = sys_mmap(ML_PATH_CAP)
288 var required: i64 = 0
289 var landed: i64 = 0
290 var unresolved: i64 = 0
291 var binding_set: i64 = 0
292 var p: i64 = 0
293 while p < rn {
294 var e: i64 = p
295 while e < rn { if (req[e] as i64) == ML_COMMA { break } e = e + 1 }
296 var s0: i64 = p
297 var s1: i64 = e
298 while s0 < s1 { if (req[s0] as i64) == ML_SPACE { s0 = s0 + 1 } else { break } }
299 while s1 > s0 { if (req[s1 - 1] as i64) == ML_SPACE { s1 = s1 - 1 } else { break } }
300 if s1 > s0 {
301 required = required + 1
302 var ok: i64 = 0
303 var handled: i64 = 0
304 if s1 - s0 > ML_TOK_GATE_LEN { if ml_span_starts(req, s0, s1 - s0, ML_TOK_GATE) == 1 {
305 handled = 1
306 ml_span_cstr(req, s0 + ML_TOK_GATE_LEN, s1 - s0 - ML_TOK_GATE_LEN, symc, ML_SYM_CAP)
307 if ml_gate_green(symc) == 1 { ok = 1 }
308 } }
309 if handled == 0 { if s1 - s0 > ML_TOK_ROWS_LEN { if ml_span_starts(req, s0, s1 - s0, ML_TOK_ROWS) == 1 {
310 handled = 1
311 ml_span_cstr(req, s0 + ML_TOK_ROWS_LEN, s1 - s0 - ML_TOK_ROWS_LEN, orgc, ML_PATH_CAP)
312 if ml_rows_present(orgc) == 1 { ok = 1 }
313 } } }
314 if handled == 0 { if ml_find_rung_sym(plan, lp[0], req, s0, s1 - s0, symspan) == 1 {
315 if ml_find_matrix_organ(mx, lm[0], plan, symspan[0], symspan[1], orgspan) == 1 {
316 ml_span_cstr(plan, symspan[0], symspan[1], symc, ML_SYM_CAP)
317 ml_span_cstr(mx, orgspan[0], orgspan[1], orgc, ML_PATH_CAP)
318 // A RELATIVE organ path is relative to buildroot/ (nishihost/runtime is a 68-file twin, NOT the
319 // source tree): read verbatim from the nishihost root every rung measured landed=0 (communitypulse,
320 // 2026-08-24, all 12 declared symbols read HOLD). Absolute paths (the gate's /tmp fixtures) pass through,
321 // which is why the gate could not see this -- its fixtures never exercise a relative path.
322 let orgp: *u8 = sys_mmap(ML_PATH_CAP + ML_SRC_ROOT_CAP)
323 // the prefix applies at the nishihost root only; inside buildroot/ (the regen's CWD) it would double the path
324 if ml_at_root() == 1 { ml_organ_path(orgc, orgp, ML_PATH_CAP + ML_SRC_ROOT_CAP) }
325 else { ml_span_cstr(orgc, 0, ml_slen(orgc), orgp, ML_PATH_CAP + ML_SRC_ROOT_CAP) }
326 if sd_declared(orgp, symc) == 1 { ok = 1 }
327 } else { unresolved = unresolved + 1 }
328 } else { unresolved = unresolved + 1 } }
329 if ok == 1 { landed = landed + 1 }
330 else { if binding_set == 0 { out[ML_OUT_BINDING_OFF] = s0; out[ML_OUT_BINDING_LEN] = s1 - s0; binding_set = 1 } }
331 }
332 p = e + 1
333 }
334 out[ML_OUT_REQUIRED] = required
335 out[ML_OUT_LANDED] = landed
336 out[ML_OUT_UNRESOLVED] = unresolved
337 var code: i64 = ML_NO_REQUIREMENT
338 if required > 0 {
339 if unresolved > 0 { code = ML_UNRESOLVED }
340 else { if landed == required { code = ML_ENTER } else { if landed == 0 { code = ML_HOLD } else { code = ML_GROW_FIRST } } }
341 }
342 out[ML_OUT_CODE] = code
343 return code
344}
345func ml_code_text(code: i64) -> *u8 {
346 if code == ML_ENTER { return "ENTER" as *u8 }
347 if code == ML_GROW_FIRST { return "GROW-FIRST" as *u8 }
348 if code == ML_HOLD { return "HOLD" as *u8 }
349 if code == ML_NO_REQUIREMENT { return "NO-REQUIREMENT" as *u8 }
350 if code == ML_UNRESOLVED { return "UNRESOLVED" as *u8 }
351 return "PROSE" as *u8
352}
353// the authored verdict word at the head of a verdict cell: ENTER / GROW-FIRST / HOLD, else ML_PROSE
354func ml_authored_code(verdict: *u8) -> i64 {
355 if ml_span_starts(verdict, 0, ml_slen(verdict), "ENTER" as *u8) == 1 { return ML_ENTER }
356 if ml_span_starts(verdict, 0, ml_slen(verdict), "GROW-FIRST" as *u8) == 1 { return ML_GROW_FIRST }
357 if ml_span_starts(verdict, 0, ml_slen(verdict), "HOLD" as *u8) == 1 { return ML_HOLD }
358 return ML_PROSE
359}
360func ml_wfd(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
361func ml_wnfd(fd: i64, v: i64) -> i64 {
362 var m: i64 = v
363 if m < 0 { ml_wfd(fd, "-" as *u8); m = 0 - m }
364 let t: *u8 = sys_mmap(32)
365 var k: i64 = 0
366 if m == 0 { t[0] = 48 as u8; k = 1 }
367 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
368 let o: *u8 = sys_mmap(32)
369 var i: i64 = 0
370 while i < k { o[i] = t[k - 1 - i]; i = i + 1 }
371 sys_write(fd, o, k)
372 return 0
373}
374// THE PAGE'S COMPUTED CELL (called by nx_swcompare_lib plan_pass for a ladder row with a requires field): the same
375// ml_entry_verdict the CLI runs, rendered as `computed: X (n/m landed, binding R)` after the authored verdict, so
376// the published page and the command line cannot disagree. Writes nothing when the plan has no matrix sibling.
377func ml_render_computed_html(fd: i64, plan_path: *u8, req: *u8) -> i64 {
378 let mx: *u8 = sys_mmap(ML_PATH_CAP)
379 if ml_sibling_matrix(plan_path, mx, ML_PATH_CAP) == 0 { return 0 }
380 let out: *i64 = sys_mmap(ML_SLOT * ML_OUT_SLOTS) as *i64
381 let code: i64 = ml_entry_verdict(mx, plan_path, req, out)
382 ml_wfd(fd, "<br><span class='ct'>computed: " as *u8)
383 ml_wfd(fd, ml_code_text(code))
384 if out[ML_OUT_REQUIRED] > 0 {
385 ml_wfd(fd, " (" as *u8); ml_wnfd(fd, out[ML_OUT_LANDED]); ml_wfd(fd, "/" as *u8); ml_wnfd(fd, out[ML_OUT_REQUIRED]); ml_wfd(fd, " landed" as *u8)
386 if out[ML_OUT_BINDING_LEN] > 0 { ml_wfd(fd, ", binding " as *u8); sys_write(fd, (req as i64 + out[ML_OUT_BINDING_OFF]) as *u8, out[ML_OUT_BINDING_LEN]) }
387 ml_wfd(fd, ")" as *u8)
388 }
389 ml_wfd(fd, "</span>" as *u8)
390 return 1
391}
392// does the authored word agree with the computed code? NO-REQUIREMENT agrees with ENTER; prose never disagrees
393func ml_agrees(authored: i64, computed: i64) -> i64 {
394 if authored == ML_PROSE { return 1 }
395 if computed == ML_NO_REQUIREMENT { if authored == ML_ENTER { return 1 } return 0 }
396 if authored == computed { return 1 }
397 return 0
398}