nx_compare_cite.nx
buildroot/runtime/nx_compare_cite.nx
about
nx_compare_cite.nx -- CITE A /compare ROW IN ONE PUBLISH: the ref row AND its inline mark, or neither.
WHY (operator, 2026-09-02: "this should be capabilities that the nishi team and estate and ecosystem
have"). A citation on a /compare board is TWO writes in TWO files -- a `ref|key|...` row in <dom>.refs
and a `[@key]` mark inside one matrix row's note -- and nx_compare_refs_gate refuses the board when
either half is missing (UNCITED when the row lands without its mark, UNRESOLVED-MARK when the mark
lands without its row). Measured the day this was written: fifteen refs landed as one edit and five
marks then had to be placed by five sequential compare-and-swap edits, each a separate transport
round-trip, each a chance to half-land. The estate had a GATE for citations (nx_compare_refs_gate),
a FORMAT PRECHECK (nx_atlas_cite) and a VERIFIER (nx_cite_lib) -- and no WRITER. This is the writer.
WHAT IT REFUSES BY CONSTRUCTION (the fabrication class the refs law names):
* the pin is COMPUTED from the mirror file, never typed -- a reference whose mirror is absent cannot
be cited at all (REFUSED-MIRROR-ABSENT), so "a reference you did not fetch" cannot enter a register.
* the target row is named by its LABEL PREFIX and must match EXACTLY ONE matrix row (0 -> ROW-NOT-FOUND,
2+ -> ROW-AMBIGUOUS with the count), so a mark can never land on the wrong row.
* a pipe inside any prose field is refused (the row grammar has exactly 9 fields).
* both files are re-written whole under nx_atomic_publish's compare-and-swap with the sha256 THIS run
read (composed: forked, never re-implemented), so a concurrent editor is refused, never clobbered.
* ORDER: refs first, then the mark. A ref without its mark is the gate's UNCITED class (named, and
this organ is idempotent so a re-run adds the missing half); a mark without its ref would DANGLE.
* IDEMPOTENT: key present AND mark present -> ALREADY-CITED, exit 0, nothing written.
TREE RESOLUTION is composed from nx_comparetree_lib in PUBLISHED order (buildroot first, the tree the
page renders from; the authored tree second), the same order the referee reads .matrix and .refs.
usage: nx_compare_cite <domain> <key> <row-label-prefix> <cite> <url> <mirror> <class> <grounds>
nx_compare_cite @<specfile> (8 lines in the same order -- the argv-mangling-safe form the
memory rail already uses; CRLF tolerated)
receipt (last line, positional): CITED | ALREADY-CITED | REFUSED-<reason> | PARTIAL-refs-published-mark-refused
exit: 0 cited or already | 2 usage | 3 field refused (pipe, empty, non-http url) | 4 mirror absent
5 refs or matrix file unresolvable | 6 row not found or ambiguous | 7 refs publish refused
8 mark publish refused after the ref landed (re-run: idempotent, it adds only the mark)
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 4 imports · 0 importers
imports: nx_syscalls.nxnx_sha256.nxnx_comparetree_lib.nxnx_tool_run.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 40 | const CC_PUBLISH_ELF: *u8 = "/volume1/homes/elderwesto/nishihost/nx_atomic_publish.elf" |
| 41 | const CC_TMP_SUFFIX: *u8 = ".cite.tmp" |
| 42 | const CC_SUF_REFS: *u8 = ".refs" |
| 43 | const CC_SUF_MATRIX: *u8 = ".matrix" |
| 44 | const CC_MODE: i64 = 420 // 0644, the mode every compare data file already carries |
| 45 | const CC_PATHCAP: i64 = 1024 |
| 46 | const CC_ROWCAP: i64 = 16384 // one ref row: 9 fields of prose; a longer row is refused, not cut |
| 47 | const CC_CAPCAP: i64 = 65536 // captured stdout of one nx_atomic_publish fork (announced if it fills) |
| 48 | const CC_HEX: i64 = 64 |
| 49 | const CC_SPEC_FIELDS: i64 = 8 |
| 50 | const CC_PIPE: i64 = 124 |
| 51 | const CC_NL: i64 = 10 |
| 52 | const CC_CR: i64 = 13 |
| 53 | const CC_AT: i64 = 64 |
| 54 | const CC_HASH: i64 = 35 |
| 55 | const CC_ATSIGN: i64 = 64 |
| 56 | const CC_DAYSECS: i64 = 86400 |
| 57 | const CC_EX_USAGE: i64 = 2 |
| 58 | const CC_EX_FIELD: i64 = 3 |
| 59 | const CC_EX_MIRROR: i64 = 4 |
| 60 | const CC_EX_TREE: i64 = 5 |
| 61 | const CC_EX_ROW: i64 = 6 |
| 62 | const CC_EX_PUB_REFS: i64 = 7 |
| 63 | const CC_EX_PUB_MARK: i64 = 8 |
functions
| 65 | func cc_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 66 | func cc_puts(s: *u8) -> i64 { sys_write(1, s, cc_len(s)); return 0 } |
| 67 | func cc_putn(v: i64) -> i64 |
| 82 | func cc_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p } |
| 83 | func cc_catb(d: *u8, o: i64, s: *u8, n: i64) -> i64 { var i: i64 = 0; var p: i64 = o; while i < n { d[p] = s[i]; p = p + 1; i = i + 1 } d[p] = 0 as u8; return p } called by 1: main |
| 84 | func cc_has_pipe(s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { if s[i] == (CC_PIPE as u8) { return 1 } i = i + 1 } return 0 } called by 1: main |
| 85 | func cc_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: main |
| 87 | func cc_find(buf: *u8, n: i64, needle: *u8) -> i64 |
| 101 | func cc_pad2(d: *u8, o: i64, v: i64) -> i64 { d[o] = (48 + (v / 10) % 10) as u8; d[o + 1] = (48 + v % 10) as u8; return o + 2 } called by 1: cc_civil_date |
| 103 | func cc_civil_date(epoch: i64, out: *u8) -> i64 |
| 125 | func cc_hex(dig: *u8, out: *u8) -> i64 called by 1: main |
| 133 | func cc_resolve(dom: *u8, suf: *u8, path: *u8, bufp: *i64) -> i64 |
| 145 | func cc_publish(target: *u8, buf: *u8, n: i64, expect_hex: *u8, cap: *u8, capn: *i64) -> i64 |
| 163 | func cc_load_spec(path: *u8, slots: *i64) -> i64 |
| 186 | func main(argc: i64, argv: *i64) -> i64 |