code wiki / (root) / nx_market_ladder_lib.nx

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}