nx_swcompare_hub.nx source
↩ module page · 1259 lines · 94777 B
1// nx_swcompare_hub.nx -- GENERATOR for the /compare hub. Reads knowledge/compare/registry (data, one line per
2// comparison: title|kind|href|radar-href-or-dash|stat) and emits the SoftwareCompare landing HTML. This RETIRES
3// the hand-authored hub -> the surface is self-assembling: a workstream that ships a comparison just appends a
4// registry line + reruns this. Kills the multi-session hub-clobber problem (the registry is the SSOT; the hub is
5// always regenerated from it). Modes: no-arg = console + liar-kill (gate); "html" = emit /compare/index.html.
6// NOTE: no '#'/'!' in string literals (nx_cc trap) -> rgb() + emit '!' byte. license_tier: ORIGINAL expect_exit:0
7//
8// v3 (2026-08-24, operator: "the hub is just a big long scroll list without search or filtering or anything else
9// I'd expect from a hub page"): HUB-GRADE USABILITY, ALL DATA-DRIVEN, ALL PROGRESSIVE.
10// * every card carries MEASURED FACTS read at emit from the domain's own emitted api.json (coverage permille,
11// exceeds, present, open contracts, measured date, kind, subtitle) -- the registry's prose stays as the
12// curated line; a domain with no registry row gets a full card from its facts, never a placeholder.
13// * cards are GROUPED by knowledge/compare/hub.groups (dom|group, data); a domain with no row lands in
14// "Ungrouped", printed by name in console mode (a count without a worklist is not actionable).
15// * sticky toolbar: search (kept), group chips, kind chips, sort (coverage / exceeds / recently measured /
16// A-Z), live result count, reset, "/" focuses search, Esc clears, filter state in the URL (?q=&g=&k=&s=).
17// * responsive grid, coverage bar per card, jump-nav of groups with counts, footer partition (groups sum to cards).
18// * NO JavaScript required: without it the page is the grouped grid with anchor chips; JS only filters/sorts.
19// * json mode is ADDITIVE: every comparison now also carries kind_measured, group, coverage, exceeds, present,
20// absent, generated_unix, subtitle when known (old fields untouched -- Rule 19 contract stability).
21// * console mode prints the HUB CENSUS: regen.list domains -> carded / page / facts / grouped, ungrouped NAMED.
22// Verdict RED only on the liar-kill and on a renderable domain that produced no card (a bug in this file).
23//
24// v4 (2026-08-31): TWO DEFECTS, BOTH OF THEM "A FIX THAT LIVES IN ONE ORGAN AND NOT ITS SIBLING".
25// * LAYOUT + THEME NOW COME FROM THE ONE EMITTER. This file carried its OWN :root palette (eleven
26// hardcoded rgb() literals, DARK-ONLY -- no html[data-theme] and no prefers-color-scheme block) and
27// its OWN body rule at max-width:1180px. That made the LANDING PAGE the last page in the family
28// still on a private ruler: a 1180px dark index linking to fluid, theme-aware domain pages. It now
29// composes sc_theme_pass + sc_layout_pass from nx_swcompare_lib, exactly as nx_swcompare_matrix and
30// nx_swcompare_sota do. THE CSS IS NOT COPIED -- copying it is the duplicate-ruler defect this whole
31// change exists to remove.
32// * THE SOTA TALLY IS NO LONGER SILENTLY OMITTED (see hub_tally_from_watch).
33// Importing nx_swcompare_lib also DELETES ten helpers this file had hand-copied from it (w, wc, wn, wj,
34// wq, kv_s, kv_n, c_read, streq, scopy -- byte-identical, so the deletion is provable, not judged).
35import "nx_syscalls.nx"
36import "nx_swcompare_lib.nx"
37const K_MAGIC_65536: i64 = 65536
38const K_MAGIC_4096: i64 = 4096
39const K_MAGIC_1024: i64 = 1024
40const K_MAGIC_1536: i64 = 1536
41const K_MAGIC_2048: i64 = 2048
42const K_MAGIC_20480: i64 = 20480
43const K_MAGIC_20479: i64 = 20479
44// v3 card table: HUB_MAXCARDS slots of HUB_SLOT bytes; a slot holds the card's strings at fixed offsets.
45const HUB_MAXCARDS: i64 = 256
46const HUB_SLOT: i64 = 3072
47const HO_TITLE: i64 = 0 // 256
48const HO_KIND: i64 = 256 // 32 registry kind (live/radar/soon/matrix/auto)
49const HO_HREF: i64 = 288 // 256
50const HO_RADAR: i64 = 544 // 256
51const HO_STAT: i64 = 800 // 1024 registry prose
52const HO_DOM: i64 = 1824 // 64 domain atom (or href key for /modelcard-style pages)
53const HO_GROUP: i64 = 1888 // 64
54const HO_SUB: i64 = 1952 // 512 subtitle from api.json (or the file's @sub)
55const HO_MKIND: i64 = 2464 // 32 measured kind from api.json (matrix/sota/frontier-radar) or empty
56const HO_SLUG: i64 = 2496 // 64 group slug
57const HN: i64 = 12 // numeric fields per card (i64 each); the cn mmap derives its size from this
58const HN_COV: i64 = 0
59const HN_EXC: i64 = 1
60const HN_PRES: i64 = 2
61const HN_ABS: i64 = 3
62const HN_TOT: i64 = 4
63const HN_GEN: i64 = 5
64const HN_FACTS: i64 = 6 // 1 = api.json was read
65const HN_GROUPED: i64 = 7 // 1 = a hub.groups row matched
66const HN_UNJ: i64 = 8 // watch rows carrying no judgeable contract -- neither present nor open
67const HN_ADOPT_FULL: i64 = 10 // api.json adopted_full: organs behind this card's rows at FULL adoption
68const HN_ADOPT_PART: i64 = 11 // api.json adopted_partial: built-unpromoted, promoted-unregistered, registered-dark...
69const HN_TSRC: i64 = 9 // which ruler produced this card's tally: HT_SRC_*
70// WHICH RULER PRODUCED THE TALLY, AS A DECLARED THIRD STATE. HN_FACTS stays the plain binary "api.json
71// was read" that its four consumers already test; this is separate, because "read, but no tally was
72// derivable" and "never read" used to be the SAME observation from outside -- both emitted no numbers at
73// all, so a generator that had silently stopped tallying looked exactly like a domain with no data page.
74const HT_SRC_NONE: i64 = 0 // read, and NEITHER ruler could produce a tally -- abstain, never acquit
75const HT_SRC_TALLY: i64 = 1 // matrix-class: the tally block, axis coverage
76const HT_SRC_WATCH: i64 = 2 // sota-class: derived from the MEASURED watch[] statuses
77const HUB_API_CAP: i64 = 262144
78const HUB_MAXGROUPS: i64 = 32
79const HUB_GSLOT: i64 = 128
80const HUB_SECS_PER_DAY: i64 = 86400
81const HUB_DAYS_TO_CIVIL: i64 = 719468
82const HUB_ERA_DAYS: i64 = 146097
83// the civil-date algorithm's cycle lengths in days: a 4-year leap cycle, a 100-year cycle, and one era less a day
84const HUB_DAYS_4Y: i64 = 1460
85const HUB_DAYS_100Y: i64 = 36524
86const HUB_DAYS_ERA_LESS1: i64 = 146096
87const HUB_STAT_CAP: i64 = 1024
88const HUB_PERMILLE: i64 = 1000
89
90// w / wc / wn / streq / c_read WERE HAND-COPIED FROM nx_swcompare_lib AND ARE NOW IMPORTED FROM IT.
91// Each was BYTE-IDENTICAL to the lib's, so removing them is provable rather than judged -- the same
92// five definitions still exist, there is simply one of each now instead of two that nothing compared.
93// split line [ls,le) of buf by '|' into fbuf (5 slots x 512), return field count
94func split5(buf: *u8, ls: i64, le: i64, fbuf: *u8) -> i64 {
95 var fi: i64 = 0; var fp: i64 = 0; var q: i64 = ls
96 while q < le {
97 let ch: u8 = buf[q]
98 if ch == (124 as u8) { fbuf[fi*512 + fp] = 0 as u8; if fi < 4 { fi = fi + 1 } fp = 0 } else { if fp < 511 { fbuf[fi*512 + fp] = ch; fp = fp + 1 } }
99 q = q + 1
100 }
101 fbuf[fi*512 + fp] = 0 as u8
102 return fi + 1
103}
104
105// scopy / wj / wq / kv_s / kv_n: same story as the five above -- byte-identical copies of the lib's,
106// now imported. c_exists stays: it is this file's own, the lib has no such helper.
107func c_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
108func dom_of(href: *u8, out: *u8) -> i64 { let pfx: *u8 = "/compare/" as *u8; var p: i64 = 0; while pfx[p] != (0 as u8) { if href[p] != pfx[p] { out[0] = 0 as u8; return 0 } p = p + 1 } var o: i64 = 0; while href[p] != (0 as u8) { if href[p] == (47 as u8) { break } out[o] = href[p]; o = o + 1; p = p + 1 } out[o] = 0 as u8; return o }
109// w2: write a literal converting every apostrophe (39) to a double quote (34) -- lets JSON fragments live in
110// nishilang string literals (which cannot contain the double-quote character) as single-quoted text.
111func w2(fd: i64, s: *u8) -> i64 {
112 var i: i64 = 0
113 while s[i] != (0 as u8) { if (s[i] as i64) == 39 { wc(fd, 34) } else { wc(fd, s[i] as i64) } i = i + 1 }
114 return 0
115}
116// wref: emit {"$ref":"#/components/schemas/<nm>"} -- the '#' byte via wc(35) (nx_cc literal trap).
117func wref(fd: i64, nm: *u8) -> i64 {
118 w2(fd, "{'$ref':'" as *u8); wc(fd, 35); w2(fd, "/components/schemas/" as *u8); w(fd, nm); w2(fd, "'}" as *u8)
119 return 0
120}
121// THE TWO KNOWLEDGE TREES (measured 2026-08-24): nx_compare_regen chdirs to buildroot, so knowledge/compare/ IS the
122// canonical tree; the mgmt daemon's md_cmp_regen runs this same elf with CWD=nishihost, where knowledge/compare/ is
123// the older twin (its regen.list lacked 19 domains -> the union emitted nothing -> a 73-card hub overwrote the
124// 92-card one). Resolve the compare data dir ONCE: buildroot/knowledge/compare/ when visible, else knowledge/compare/.
125func hub_kc(out: *u8) -> i64 {
126 if c_exists("buildroot/knowledge/compare/regen.list" as *u8) == 1 { let a: i64 = scopy(out, 0, "buildroot/knowledge/compare/" as *u8); out[a] = 0 as u8; return a }
127 let b: i64 = scopy(out, 0, "knowledge/compare/" as *u8); out[b] = 0 as u8
128 return b
129}
130func hub_kcpath(out: *u8, tail: *u8) -> i64 { var o: i64 = hub_kc(out); o = scopy(out, o, tail); out[o] = 0 as u8; return o }
131// does machine-readable matrix data exist for this domain? Checks BOTH layouts: the laptop authoring copy
132// (knowledge/compare/<dom>-api.json, CWD=nxc2) and the live docroot (../sites/nishifamily/compare/<dom>/api.json,
133// CWD=nishihost/buildroot during on-NAS regen). Either counts.
134func data_exists(dbuf: *u8, pbuf: *u8) -> i64 {
135 var o: i64 = hub_kc(pbuf); o = scopy(pbuf, o, dbuf); o = scopy(pbuf, o, "-api.json" as *u8); pbuf[o] = 0 as u8
136 if c_exists(pbuf) == 1 { return 1 }
137 o = scopy(pbuf, 0, "../sites/nishifamily/compare/" as *u8); o = scopy(pbuf, o, dbuf); o = scopy(pbuf, o, "/api.json" as *u8); pbuf[o] = 0 as u8
138 if c_exists(pbuf) == 1 { return 1 }
139 // CWD=nishihost (the mgmt daemon's md_cmp_regen runs the same elf from there): the docroot is a sibling dir.
140 o = scopy(pbuf, 0, "sites/nishifamily/compare/" as *u8); o = scopy(pbuf, o, dbuf); o = scopy(pbuf, o, "/api.json" as *u8); pbuf[o] = 0 as u8
141 return c_exists(pbuf)
142}
143
144// ---- EMIT-TIME UNION (2026-08-18): the hub-orphan class made impossible by construction.
145// 18 live domains had pages but no hub card, because the registry SSOT only grows through
146// /api/compare/upsert and seats shipped pages without calling it (the assembler's regen.list
147// union wrote registry_full, which nothing promoted -- BUILT+UNWIRED). The hub now DERIVES the
148// union at emit: every regen.list domain with a real renderable (.matrix/.sota -- the assembler's
149// own rule, so no card can 404) that has no registry card gets a computed overlay card. Zero
150// writes, no auth, self-healing both ways (retire the domain and the card disappears; a curated
151// upsert replaces the overlay on the next emit). The registry stays the SSOT for curated prose.
152func hub_reg_has(sbuf: *u8, sn: i64, dom: *u8) -> i64 {
153 // needle |/compare/<dom>| -- the href field is pipe-delimited, so prefix domains cannot collide
154 let nd: *u8 = sys_mmap(256)
155 var o: i64 = 0
156 nd[o] = 124 as u8; o = o + 1
157 o = scopy(nd, o, "/compare/" as *u8)
158 o = scopy(nd, o, dom)
159 // boundary: the byte after the domain must be '|' (field end) or '/' (a trailing-slash href) -- never a
160 // longer domain name (so 'lang' cannot match 'language'); /compare/pmdash/ therefore matches pmdash.
161 var i: i64 = 0
162 while i + o + 1 <= sn {
163 var k: i64 = 0
164 var hit: i64 = 1
165 while k < o { if sbuf[i+k] != nd[k] { hit = 0; k = o } else { k = k + 1 } }
166 if hit == 1 {
167 let nb: i64 = sbuf[i+o] as i64
168 if nb == 124 { return 1 }
169 if nb == 47 { return 1 }
170 }
171 i = i + 1
172 }
173 return 0
174}
175// read a header directive (@title / @sub) from <dom>.matrix or <dom>.sota into tout; 1 = renderable, 0 = not
176func hub_dom_line(dom: *u8, pfx: *u8, plen: i64, tout: *u8, tbuf: *u8, tcap: i64) -> i64 {
177 let pb: *u8 = sys_mmap(512)
178 var o: i64 = hub_kc(pb)
179 o = scopy(pb, o, dom)
180 let o0: i64 = o
181 o = scopy(pb, o, ".matrix" as *u8); pb[o] = 0 as u8
182 var n: i64 = c_read(pb, tbuf, tcap - 1)
183 if n <= 0 {
184 o = scopy(pb, o0, ".sota" as *u8); pb[o] = 0 as u8
185 n = c_read(pb, tbuf, tcap - 1)
186 }
187 if n <= 0 { return 0 }
188 tbuf[n] = 0 as u8
189 tout[0] = 0 as u8
190 var i: i64 = 0
191 while i < n {
192 var e: i64 = i
193 while e < n { if tbuf[e] == (10 as u8) { break } e = e + 1 }
194 if (e - i) > plen {
195 var m: i64 = 1
196 var k: i64 = 0
197 while k < plen { if tbuf[i+k] != pfx[k] { m = 0; k = plen } else { k = k + 1 } }
198 if m == 1 {
199 var t: i64 = 0
200 var q: i64 = i + plen
201 while q < e { if t < 250 { tout[t] = tbuf[q]; t = t + 1 } q = q + 1 }
202 tout[t] = 0 as u8
203 return 1
204 }
205 }
206 i = e + 1
207 }
208 return 1
209}
210func hub_dom_title(dom: *u8, tout: *u8, tbuf: *u8, tcap: i64) -> i64 {
211 let r: i64 = hub_dom_line(dom, "@title " as *u8, 7, tout, tbuf, tcap)
212 if r == 1 { if tout[0] == (0 as u8) { var d: i64 = scopy(tout, 0, dom); tout[d] = 0 as u8 } }
213 return r
214}
215
216// ---- v3 helpers ----------------------------------------------------------------------------------
217func hub_slot(cards: *u8, idx: i64, off: i64) -> *u8 { return (cards as i64 + idx * HUB_SLOT + off) as *u8 }
218func hub_num(cn: *i64, idx: i64, f: i64) -> i64 { return cn[idx * HN + f] }
219func hub_setnum(cn: *i64, idx: i64, f: i64, v: i64) -> i64 { cn[idx * HN + f] = v; return 0 }
220func hub_copyn(dst: *u8, src: *u8, cap: i64) -> i64 { var i: i64 = 0; while src[i] != (0 as u8) { if i < cap - 1 { dst[i] = src[i] } i = i + 1 } if i > cap - 1 { i = cap - 1 } dst[i] = 0 as u8; return i }
221// the card KEY: the domain atom for /compare/<dom>..., else the first path segment (modelcard, editor)
222func hub_key_of(href: *u8, out: *u8) -> i64 {
223 let n: i64 = dom_of(href, out)
224 if n > 0 { return n }
225 var p: i64 = 0
226 if href[0] == (47 as u8) { p = 1 }
227 var o: i64 = 0
228 while href[p] != (0 as u8) { if href[p] == (47 as u8) { break } if o < 60 { out[o] = href[p]; o = o + 1 } p = p + 1 }
229 out[o] = 0 as u8
230 return o
231}
232// find needle in buf[0..n), return offset of the byte AFTER the needle or -1
233func hub_find(buf: *u8, n: i64, nd: *u8, nlen: i64) -> i64 {
234 var i: i64 = 0
235 while i + nlen <= n {
236 var k: i64 = 0
237 var hit: i64 = 1
238 while k < nlen { if buf[i+k] != nd[k] { hit = 0; k = nlen } else { k = k + 1 } }
239 if hit == 1 { return i + nlen }
240 i = i + 1
241 }
242 return 0 - 1
243}
244// JSON number field "key":N -> N, or -1 when absent. The needle is built as bytes so the source carries no quote.
245func jnum(buf: *u8, n: i64, key: *u8) -> i64 {
246 let nd: *u8 = sys_mmap(96)
247 var o: i64 = 0
248 nd[o] = 34 as u8; o = o + 1
249 o = scopy(nd, o, key)
250 nd[o] = 34 as u8; o = o + 1
251 nd[o] = 58 as u8; o = o + 1
252 var p: i64 = hub_find(buf, n, nd, o)
253 if p < 0 { return 0 - 1 }
254 while p < n { if buf[p] == (32 as u8) { p = p + 1 } else { break } }
255 var neg: i64 = 0
256 if p < n { if buf[p] == (45 as u8) { neg = 1; p = p + 1 } }
257 var v: i64 = 0
258 var got: i64 = 0
259 while p < n {
260 let c: i64 = buf[p] as i64
261 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); got = 1; p = p + 1 } else { break } } else { break }
262 }
263 if got == 0 { return 0 - 1 }
264 if neg == 1 { return 0 - v }
265 return v
266}
267// JSON string field "key":"..." -> out (unescaping only the two escapes the emitters produce), -1 when absent
268func jstr(buf: *u8, n: i64, key: *u8, out: *u8, cap: i64) -> i64 {
269 let nd: *u8 = sys_mmap(96)
270 var o: i64 = 0
271 nd[o] = 34 as u8; o = o + 1
272 o = scopy(nd, o, key)
273 nd[o] = 34 as u8; o = o + 1
274 nd[o] = 58 as u8; o = o + 1
275 nd[o] = 34 as u8; o = o + 1
276 var p: i64 = hub_find(buf, n, nd, o)
277 if p < 0 { out[0] = 0 as u8; return 0 - 1 }
278 var t: i64 = 0
279 while p < n {
280 let c: i64 = buf[p] as i64
281 if c == 34 { break }
282 if c == 92 {
283 p = p + 1
284 if p < n { let d: i64 = buf[p] as i64; if t < cap - 1 { if d == 110 { out[t] = 32 as u8 } else { out[t] = d as u8 } t = t + 1 } }
285 } else {
286 if t < cap - 1 { out[t] = c as u8; t = t + 1 }
287 }
288 p = p + 1
289 }
290 out[t] = 0 as u8
291 return t
292}
293// ---- THE SOTA TALLY FALLBACK (2026-08-31) ----
294// hub_facts read EVERY domain's api.json and then looked only for the matrix tally keys, so for all ten
295// sota-class domains every one of the five numbers came back -1, hub_card_html took its else branch, and
296// the card emitted no bar and no counts -- byte-indistinguishable from a domain whose api.json was never
297// read at all. THE DATA WAS NEVER MISSING, ONLY UNTALLIED: a sota api.json carries watch[], one row per
298// symbol contract, each with a status MEASURED on every publish by the same ruler (nx_symdecl_lib
299// sd_declared) that the matrix generator, the ranker and the comparewatch plane use. So this DERIVES the
300// tally from a measurement already in the document; it does not invent one, and it cannot disagree with
301// the domain's own page by construction.
302// Exact-token match: the closing quote must follow the literal, so PRESENT can never match inside a
303// longer word and a truncated tail can never half-match.
304func hub_wis(buf: *u8, n: i64, at: i64, lit: *u8) -> i64 {
305 var k: i64 = 0
306 while lit[k] != (0 as u8) {
307 if at + k >= n { return 0 }
308 if buf[at + k] != lit[k] { return 0 }
309 k = k + 1
310 }
311 if at + k >= n { return 0 }
312 if buf[at + k] != (34 as u8) { return 0 }
313 return 1
314}
315// Count watch[] rows by status into cnt[0..5]. Returns the row count, or -1 when the document carries no
316// watch array at all (which is the honest third state, not a zero).
317// SCOPED TO THE WATCH ARRAY BY CONSTRUCTION: the scan starts at the watch key rather than at byte 0, so a
318// status field anywhere else in the document cannot inflate the count. watch is the LAST key the sota
319// generator emits (measurement, ppp, refs, then watch, then the closing brace), so from that offset to the
320// end IS the watch array -- written down because it is an assumption that a later-added key would break.
321// cnt[5] is UNKNOWN and exists so an unrecognised status word lands in its OWN bucket instead of being
322// folded into a known one: the partition still sums, and a status added to the ruler later cannot silently
323// read as coverage. It is unreachable from today's emitter (wp_status_text is total over the five) and is
324// therefore a guard against a future vocabulary change, not a live case.
325func hub_wcount(buf: *u8, n: i64, cnt: *i64) -> i64 {
326 var z: i64 = 0
327 while z < 6 { cnt[z] = 0; z = z + 1 }
328 let wk: *u8 = sys_mmap(64)
329 var wo: i64 = 0
330 wk[wo] = 34 as u8; wo = wo + 1
331 wo = scopy(wk, wo, "watch" as *u8)
332 wk[wo] = 34 as u8; wo = wo + 1
333 wk[wo] = 58 as u8; wo = wo + 1
334 let wat: i64 = hub_find(buf, n, wk, wo)
335 if wat < 0 { return 0 - 1 }
336 let nd: *u8 = sys_mmap(64)
337 var o: i64 = 0
338 nd[o] = 34 as u8; o = o + 1
339 o = scopy(nd, o, "status" as *u8)
340 nd[o] = 34 as u8; o = o + 1
341 nd[o] = 58 as u8; o = o + 1
342 nd[o] = 34 as u8; o = o + 1
343 var p: i64 = wat
344 var rows: i64 = 0
345 while p < n {
346 let rel: i64 = hub_find((buf as i64 + p) as *u8, n - p, nd, o)
347 if rel < 0 { break }
348 let at: i64 = p + rel
349 // the five literals are distinct exact tokens, so at most one of these can fire; st keeps the
350 // UNKNOWN default when none does, rather than falling into whichever test ran last.
351 var st: i64 = 5
352 if hub_wis(buf, n, at, "LANDED" as *u8) == 1 { st = 0 }
353 if hub_wis(buf, n, at, "PRESENT" as *u8) == 1 { st = 1 }
354 if hub_wis(buf, n, at, "WATCHING" as *u8) == 1 { st = 2 }
355 if hub_wis(buf, n, at, "MISSING" as *u8) == 1 { st = 3 }
356 if hub_wis(buf, n, at, "ABSENT" as *u8) == 1 { st = 4 }
357 cnt[st] = cnt[st] + 1
358 rows = rows + 1
359 p = at + 1
360 }
361 return rows
362}
363func hub_tsrc_name(t: i64) -> *u8 {
364 if t == HT_SRC_TALLY { return "tally" as *u8 }
365 if t == HT_SRC_WATCH { return "watch" as *u8 }
366 return "none" as *u8
367}
368// read the domain's emitted api.json (three layouts, see data_exists) into abuf; fills the numeric facts,
369// the measured kind and the subtitle. 1 = read, 0 = no data page.
370func hub_facts(cards: *u8, cn: *i64, idx: i64, abuf: *u8, pbuf: *u8) -> i64 {
371 let dom: *u8 = hub_slot(cards, idx, HO_DOM)
372 hub_setnum(cn, idx, HN_FACTS, 0)
373 hub_setnum(cn, idx, HN_COV, 0 - 1); hub_setnum(cn, idx, HN_EXC, 0 - 1); hub_setnum(cn, idx, HN_PRES, 0 - 1)
374 hub_setnum(cn, idx, HN_ABS, 0 - 1); hub_setnum(cn, idx, HN_TOT, 0 - 1); hub_setnum(cn, idx, HN_GEN, 0 - 1)
375 hub_setnum(cn, idx, HN_UNJ, 0 - 1); hub_setnum(cn, idx, HN_TSRC, HT_SRC_NONE)
376 // -1 is ABSTAIN, never zero: a card whose api.json carries no adoption tally must not read as a domain
377 // whose organs are all unadopted, which is the direction that would slander a healthy board.
378 hub_setnum(cn, idx, HN_ADOPT_FULL, 0 - 1); hub_setnum(cn, idx, HN_ADOPT_PART, 0 - 1)
379 if dom[0] == (0 as u8) { return 0 }
380 if data_exists(dom, pbuf) == 0 { return 0 }
381 let n: i64 = c_read(pbuf, abuf, HUB_API_CAP - 1)
382 if n <= 0 { return 0 }
383 abuf[n] = 0 as u8
384 hub_setnum(cn, idx, HN_FACTS, 1)
385 jstr(abuf, n, "kind" as *u8, hub_slot(cards, idx, HO_MKIND), 32)
386 hub_setnum(cn, idx, HN_GEN, jnum(abuf, n, "generated_unix" as *u8))
387 // the matrix tally lives under "tally":{...}; its keys are unique in the document, so a flat search is exact
388 hub_setnum(cn, idx, HN_COV, jnum(abuf, n, "coverage" as *u8))
389 hub_setnum(cn, idx, HN_EXC, jnum(abuf, n, "exceeds" as *u8))
390 hub_setnum(cn, idx, HN_PRES, jnum(abuf, n, "present" as *u8))
391 hub_setnum(cn, idx, HN_ABS, jnum(abuf, n, "absent" as *u8))
392 hub_setnum(cn, idx, HN_TOT, jnum(abuf, n, "total" as *u8))
393 // ADOPTION REACHES THE HUB (2026-09-01). Every matrix board has published adopted_full and
394 // adopted_partial in its api.json since the CE2 adoption pass shipped, and this hub read seven keys
395 // and dropped exactly those two -- so the estate's own wiring number existed on 96 cards and no
396 // surface anywhere summed it. jnum is quote-anchored and returns -1 when the key is absent, so a
397 // sota-class or radar-class card that never tallies adoption keeps -1 and is counted as ABSTAINED
398 // by the rollup rather than being scored zero.
399 hub_setnum(cn, idx, HN_ADOPT_FULL, jnum(abuf, n, "adopted_full" as *u8))
400 hub_setnum(cn, idx, HN_ADOPT_PART, jnum(abuf, n, "adopted_partial" as *u8))
401 // THE MATRIX TALLY IS THE PREFERRED RULER; THE WATCH FALLBACK ONLY RUNS WHERE IT PRODUCED NOTHING, so
402 // no matrix-class card can change value here and the 85 domains that already tallied are untouched.
403 if hub_num(cn, idx, HN_COV) >= 0 { hub_setnum(cn, idx, HN_TSRC, HT_SRC_TALLY) } else {
404 let wcnt: *i64 = sys_mmap(64) as *i64
405 let wrows: i64 = hub_wcount(abuf, n, wcnt)
406 if wrows > 0 {
407 // THE MAPPING, AND WHY IT IS FAITHFUL RATHER THAN A COERCION. LANDED and PRESENT are both
408 // "the symbol is measured present on disk" -- the lib's own wp_status_class already groups
409 // them into one class -- so they are one bucket. WATCHING (contract still open) and MISSING
410 // (row names something its organ does not carry) are both "named, and not there", which is
411 // exactly what the card renders as open. ABSENT names NO contract at all, so it is neither:
412 // folding it into present would invent evidence and folding it into open would invent a
413 // commitment, and it gets its own count instead.
414 let pres: i64 = wcnt[0] + wcnt[1]
415 let opn: i64 = wcnt[2] + wcnt[3]
416 let judge: i64 = pres + opn
417 hub_setnum(cn, idx, HN_PRES, pres)
418 hub_setnum(cn, idx, HN_ABS, opn)
419 hub_setnum(cn, idx, HN_UNJ, wcnt[4] + wcnt[5])
420 hub_setnum(cn, idx, HN_TOT, wrows)
421 // per-mille over the JUDGEABLE rows only, so a row carrying no contract can neither inflate
422 // nor deflate the ratio. With no judgeable row the number stays -1 and the card ABSTAINS
423 // rather than publishing a fabricated 0, which would read as a domain that measured badly.
424 if judge > 0 { hub_setnum(cn, idx, HN_COV, pres * HUB_PERMILLE / judge) }
425 hub_setnum(cn, idx, HN_TSRC, HT_SRC_WATCH)
426 }
427 }
428 let sub: *u8 = hub_slot(cards, idx, HO_SUB)
429 if jstr(abuf, n, "subtitle" as *u8, sub, 512) < 1 { sub[0] = 0 as u8 }
430 return 1
431}
432// hub.groups: dom|group rows. 1 = matched (group copied), 0 = Ungrouped
433func hub_group(gbuf: *u8, gn: i64, key: *u8, out: *u8) -> i64 {
434 var i: i64 = 0
435 while i < gn {
436 var e: i64 = i
437 while e < gn { if gbuf[e] == (10 as u8) { break } e = e + 1 }
438 if e > i { if gbuf[i] != (35 as u8) {
439 var k: i64 = 0
440 var m: i64 = 1
441 while key[k] != (0 as u8) { if i + k >= e { m = 0; break } if gbuf[i+k] != key[k] { m = 0; break } k = k + 1 }
442 if m == 1 { if i + k < e { if gbuf[i+k] == (124 as u8) {
443 var o: i64 = 0
444 var q: i64 = i + k + 1
445 while q < e { if o < 62 { out[o] = gbuf[q]; o = o + 1 } q = q + 1 }
446 out[o] = 0 as u8
447 return 1
448 } } }
449 } }
450 i = e + 1
451 }
452 let u: i64 = scopy(out, 0, "Ungrouped" as *u8); out[u] = 0 as u8
453 return 0
454}
455func hub_slugify(src: *u8, out: *u8) -> i64 {
456 var i: i64 = 0; var o: i64 = 0; var dash: i64 = 1
457 while src[i] != (0 as u8) {
458 var c: i64 = src[i] as i64
459 var ok: i64 = 0
460 if c >= 48 { if c <= 57 { ok = 1 } }
461 if c >= 65 { if c <= 90 { c = c + 32; ok = 1 } }
462 if c >= 97 { if c <= 122 { ok = 1 } }
463 if ok == 1 { if o < 60 { out[o] = c as u8; o = o + 1 } dash = 0 } else { if dash == 0 { if o < 60 { out[o] = 45 as u8; o = o + 1 } dash = 1 } }
464 i = i + 1
465 }
466 if o > 0 { if out[o-1] == (45 as u8) { o = o - 1 } }
467 out[o] = 0 as u8
468 return o
469}
470// attribute-safe writer: apostrophe -> ' (the '#' via wc(35)), '<' -> <
471func wa(fd: i64, s: *u8) -> i64 {
472 var i: i64 = 0
473 while s[i] != (0 as u8) {
474 let c: i64 = s[i] as i64
475 if c == 39 { w(fd, "&" as *u8); wc(fd, 35); w(fd, "39;" as *u8) } else { if c == 60 { w(fd, "<" as *u8) } else { wc(fd, c) } }
476 i = i + 1
477 }
478 return 0
479}
480func wlower(fd: i64, s: *u8) -> i64 {
481 var i: i64 = 0
482 while s[i] != (0 as u8) { var c: i64 = s[i] as i64; if c >= 65 { if c <= 90 { c = c + 32 } } if c == 39 { wc(fd, 32) } else { wc(fd, c) } i = i + 1 }
483 return 0
484}
485// civil date (YYYY-MM-DD) from epoch seconds -- days-to-civil, no float, no libc
486func wdate(fd: i64, t: i64) -> i64 {
487 if t <= 0 { w(fd, "-" as *u8); return 0 }
488 let z: i64 = t / HUB_SECS_PER_DAY + HUB_DAYS_TO_CIVIL
489 let era: i64 = z / HUB_ERA_DAYS
490 let doe: i64 = z - era * HUB_ERA_DAYS
491 let yoe: i64 = (doe - doe / HUB_DAYS_4Y + doe / HUB_DAYS_100Y - doe / HUB_DAYS_ERA_LESS1) / 365
492 var y: i64 = yoe + era * 400
493 let doy: i64 = doe - (365 * yoe + yoe / 4 - yoe / 100)
494 let mp: i64 = (5 * doy + 2) / 153
495 let d: i64 = doy - (153 * mp + 2) / 5 + 1
496 var m: i64 = mp + 3
497 if mp >= 10 { m = mp - 9 }
498 if m <= 2 { y = y + 1 }
499 wn(fd, y); wc(fd, 45); if m < 10 { wc(fd, 48) } wn(fd, m); wc(fd, 45); if d < 10 { wc(fd, 48) } wn(fd, d)
500 return 0
501}
502// add one card to the table from its five registry-shaped fields; returns the new count
503func hub_add(cards: *u8, cn: *i64, count: i64, title: *u8, kind: *u8, href: *u8, radar: *u8, stat: *u8) -> i64 {
504 if count >= HUB_MAXCARDS { return count }
505 hub_copyn(hub_slot(cards, count, HO_TITLE), title, 256)
506 hub_copyn(hub_slot(cards, count, HO_KIND), kind, 32)
507 hub_copyn(hub_slot(cards, count, HO_HREF), href, 256)
508 hub_copyn(hub_slot(cards, count, HO_RADAR), radar, 256)
509 hub_copyn(hub_slot(cards, count, HO_STAT), stat, HUB_STAT_CAP)
510 hub_key_of(href, hub_slot(cards, count, HO_DOM))
511 let zs: *u8 = hub_slot(cards, count, HO_SUB); zs[0] = 0 as u8
512 let zm: *u8 = hub_slot(cards, count, HO_MKIND); zm[0] = 0 as u8
513 let zg: *u8 = hub_slot(cards, count, HO_GROUP); zg[0] = 0 as u8
514 let zl: *u8 = hub_slot(cards, count, HO_SLUG); zl[0] = 0 as u8
515 var f: i64 = 0
516 while f < HN { hub_setnum(cn, count, f, 0 - 1); f = f + 1 }
517 return count + 1
518}
519// the display kind: measured kind when facts exist, else the registry kind
520func hub_disp_kind(cards: *u8, cn: *i64, idx: i64, out: *u8) -> i64 {
521 let mk: *u8 = hub_slot(cards, idx, HO_MKIND)
522 if hub_num(cn, idx, HN_FACTS) == 1 { if mk[0] != (0 as u8) {
523 if streq(mk, "matrix" as *u8) == 1 { let a: i64 = scopy(out, 0, "measured" as *u8); out[a] = 0 as u8; return 1 }
524 if streq(mk, "frontier-radar" as *u8) == 1 { let b: i64 = scopy(out, 0, "radar" as *u8); out[b] = 0 as u8; return 1 }
525 let c: i64 = hub_copyn(out, mk, 32); return 1
526 } }
527 let k: *u8 = hub_slot(cards, idx, HO_KIND)
528 if streq(k, "auto" as *u8) == 1 { let d: i64 = scopy(out, 0, "live" as *u8); out[d] = 0 as u8; return 0 }
529 hub_copyn(out, k, 32)
530 return 0
531}
532// emit one card (html)
533func hub_card_html(cards: *u8, cn: *i64, idx: i64) -> i64 {
534 let dk: *u8 = sys_mmap(40)
535 hub_disp_kind(cards, cn, idx, dk)
536 let title: *u8 = hub_slot(cards, idx, HO_TITLE)
537 let href: *u8 = hub_slot(cards, idx, HO_HREF)
538 let radar: *u8 = hub_slot(cards, idx, HO_RADAR)
539 let stat: *u8 = hub_slot(cards, idx, HO_STAT)
540 let sub: *u8 = hub_slot(cards, idx, HO_SUB)
541 let dom: *u8 = hub_slot(cards, idx, HO_DOM)
542 let cov: i64 = hub_num(cn, idx, HN_COV)
543 w(1, "<article class='cu' data-t='" as *u8); w(1, dk); wc(1, 32); wa(1, hub_slot(cards, idx, HO_GROUP)); wc(1, 32); w(1, dom)
544 w(1, "' data-g='" as *u8); w(1, hub_slot(cards, idx, HO_SLUG))
545 w(1, "' data-k='" as *u8); w(1, dk)
546 w(1, "' data-c='" as *u8); if cov < 0 { wn(1, 0 - 1) } else { wn(1, cov) }
547 w(1, "' data-x='" as *u8); if hub_num(cn, idx, HN_EXC) < 0 { wc(1, 48) } else { wn(1, hub_num(cn, idx, HN_EXC)) }
548 w(1, "' data-u='" as *u8); if hub_num(cn, idx, HN_GEN) < 0 { wc(1, 48) } else { wn(1, hub_num(cn, idx, HN_GEN)) }
549 w(1, "' data-n='" as *u8); wlower(1, title)
550 w(1, "'>\n<a class='card' href='" as *u8); w(1, href); w(1, "'><h3>" as *u8); w(1, title)
551 w(1, " <span class='pill k-" as *u8); w(1, dk); w(1, "'>" as *u8); w(1, dk); w(1, "</span></h3>\n" as *u8)
552 if cov >= 0 {
553 w(1, "<div class='bar' aria-hidden='true'><i style='width:" as *u8); wn(1, cov / 10); w(1, "%'></i></div>\n<div class='nums'>" as *u8)
554 // THE LABEL NAMES THE RULER THAT PRODUCED THE NUMBER. A matrix coverage per-mille (share of
555 // AXES on which Nishi is present) and a sota contract per-mille (share of watch CONTRACTS
556 // measured landed) are two different measurements over two different populations. Publishing
557 // both under the single word coverage would be the subject-mismatch defect -- two witnesses of
558 // DIFFERENT subjects rendered as though they were one, which is how a reader compares numbers
559 // that were never comparable. The bucket words below (present, open) hold for both rulers.
560 if hub_num(cn, idx, HN_TSRC) == HT_SRC_WATCH { w(1, "contracts <b>" as *u8) } else { w(1, "coverage <b>" as *u8) }
561 wn(1, cov); w(1, "</b>/" as *u8); wn(1, HUB_PERMILLE)
562 if hub_num(cn, idx, HN_EXC) >= 0 { w(1, " · " as *u8); wn(1, hub_num(cn, idx, HN_EXC)); w(1, " exceed" as *u8) }
563 if hub_num(cn, idx, HN_PRES) >= 0 { w(1, " · " as *u8); wn(1, hub_num(cn, idx, HN_PRES)); w(1, " present" as *u8) }
564 if hub_num(cn, idx, HN_ABS) >= 0 { w(1, " · " as *u8); wn(1, hub_num(cn, idx, HN_ABS)); w(1, " open" as *u8) }
565 // The unjudgeable rows are printed ONLY when there are some, so every matrix-class card stays
566 // byte-identical; where they exist the card shows present + open + this summing to the rows read.
567 if hub_num(cn, idx, HN_UNJ) > 0 { w(1, " · " as *u8); wn(1, hub_num(cn, idx, HN_UNJ)); w(1, " no contract" as *u8) }
568 if hub_num(cn, idx, HN_GEN) > 0 { w(1, " · measured " as *u8); wdate(1, hub_num(cn, idx, HN_GEN)) }
569 w(1, "</div>\n" as *u8)
570 } else {
571 if hub_num(cn, idx, HN_GEN) > 0 { w(1, "<div class='nums'>" as *u8); w(1, dk); w(1, " · updated " as *u8); wdate(1, hub_num(cn, idx, HN_GEN)); w(1, "</div>\n" as *u8) }
572 }
573 if sub[0] != (0 as u8) { w(1, "<div class='sub2'>" as *u8); wa(1, sub); w(1, "</div>\n" as *u8) }
574 if stat[0] != (0 as u8) { w(1, "<div class='stat'>" as *u8); w(1, stat); w(1, "</div>\n" as *u8) }
575 w(1, "</a>\n" as *u8)
576 if streq(radar, "-" as *u8) == 0 { if radar[0] != (0 as u8) { w(1, "<p class='frl'><a href='" as *u8); w(1, radar); w(1, "'>→ Frontier Radar</a></p>\n" as *u8) } }
577 w(1, "</article>\n" as *u8)
578 return 0
579}
580// emit one card (json object, leading comma handled by the caller)
581func hub_card_json(cards: *u8, cn: *i64, idx: i64, pbuf: *u8) -> i64 {
582 let dk: *u8 = sys_mmap(40)
583 hub_disp_kind(cards, cn, idx, dk)
584 let dom: *u8 = hub_slot(cards, idx, HO_DOM)
585 let radar: *u8 = hub_slot(cards, idx, HO_RADAR)
586 wc(1, 123)
587 kv_s(1, "domain" as *u8, dom); wc(1, 44)
588 kv_s(1, "title" as *u8, hub_slot(cards, idx, HO_TITLE)); wc(1, 44)
589 kv_s(1, "kind" as *u8, hub_slot(cards, idx, HO_KIND)); wc(1, 44)
590 kv_s(1, "kind_measured" as *u8, dk); wc(1, 44)
591 kv_s(1, "group" as *u8, hub_slot(cards, idx, HO_GROUP)); wc(1, 44)
592 kv_s(1, "page" as *u8, hub_slot(cards, idx, HO_HREF)); wc(1, 44)
593 if streq(radar, "-" as *u8) == 0 { if radar[0] != (0 as u8) { kv_s(1, "radar" as *u8, radar); wc(1, 44) } }
594 if hub_num(cn, idx, HN_FACTS) == 1 { wq(1); w(1, "data" as *u8); wq(1); wc(1, 58); wq(1); w(1, "/compare/" as *u8); wj(1, dom); w(1, "/api.json" as *u8); wq(1); wc(1, 44) }
595 if hub_num(cn, idx, HN_COV) >= 0 { kv_n(1, "coverage" as *u8, hub_num(cn, idx, HN_COV)); wc(1, 44) }
596 if hub_num(cn, idx, HN_EXC) >= 0 { kv_n(1, "exceeds" as *u8, hub_num(cn, idx, HN_EXC)); wc(1, 44) }
597 if hub_num(cn, idx, HN_PRES) >= 0 { kv_n(1, "present" as *u8, hub_num(cn, idx, HN_PRES)); wc(1, 44) }
598 if hub_num(cn, idx, HN_ABS) >= 0 { kv_n(1, "absent" as *u8, hub_num(cn, idx, HN_ABS)); wc(1, 44) }
599 // THE THIRD STATE, PUBLISHED RATHER THAN INFERRED. tally_source rides on every card whose api.json
600 // was read, INCLUDING the none case, so a consumer can finally tell "read, but no tally was
601 // derivable" from "there is no data page" -- outside this field those two are the same silence, and
602 // a generator that silently stopped tallying would look exactly like a domain with no data.
603 // The watch-derived cards also carry their population and their unjudgeable count, so the partition
604 // present + absent + watch_unjudgeable = watch_rows is CHECKABLE from outside instead of trusted.
605 if hub_num(cn, idx, HN_FACTS) == 1 { kv_s(1, "tally_source" as *u8, hub_tsrc_name(hub_num(cn, idx, HN_TSRC))); wc(1, 44) }
606 if hub_num(cn, idx, HN_TSRC) == HT_SRC_WATCH {
607 if hub_num(cn, idx, HN_TOT) >= 0 { kv_n(1, "watch_rows" as *u8, hub_num(cn, idx, HN_TOT)); wc(1, 44) }
608 if hub_num(cn, idx, HN_UNJ) >= 0 { kv_n(1, "watch_unjudgeable" as *u8, hub_num(cn, idx, HN_UNJ)); wc(1, 44) }
609 }
610 if hub_num(cn, idx, HN_GEN) > 0 { kv_n(1, "generated_unix" as *u8, hub_num(cn, idx, HN_GEN)); wc(1, 44) }
611 let jsub: *u8 = hub_slot(cards, idx, HO_SUB)
612 if jsub[0] != (0 as u8) { kv_s(1, "subtitle" as *u8, jsub); wc(1, 44) }
613 kv_s(1, "summary" as *u8, hub_slot(cards, idx, HO_STAT))
614 wc(1, 125)
615 return 0
616}
617// build the card table: registry rows first, then the regen.list union (auto cards), then facts + groups.
618// Returns the card count. The group table (gt) receives the DISTINCT group names in hub.groups file order,
619// Ungrouped last; gcount[0] = number of groups.
620func hub_build(sbuf: *u8, sn: i64, cards: *u8, cn: *i64, gt: *u8, gcount: *i64) -> i64 {
621 let fbuf: *u8 = sys_mmap(K_MAGIC_4096)
622 var count: i64 = 0
623 var i: i64 = 0
624 while i < sn {
625 var j: i64 = i
626 while j < sn { if sbuf[j] == (10 as u8) { break } j = j + 1 }
627 if sbuf[i] != (35 as u8) { if j > i {
628 let nf: i64 = split5(sbuf, i, j, fbuf)
629 if nf >= 5 {
630 count = hub_add(cards, cn, count, (fbuf as i64) as *u8, (fbuf as i64 + 512) as *u8, (fbuf as i64 + K_MAGIC_1024) as *u8, (fbuf as i64 + K_MAGIC_1536) as *u8, (fbuf as i64 + K_MAGIC_2048) as *u8)
631 }
632 } }
633 i = j + 1
634 }
635 // union: every regen.list renderable missing from the registry
636 let rl: *u8 = sys_mmap(K_MAGIC_4096)
637 let rlp: *u8 = sys_mmap(512)
638 hub_kcpath(rlp, "regen.list" as *u8)
639 let rn: i64 = c_read(rlp, rl, K_MAGIC_4096 - 1)
640 let dom: *u8 = sys_mmap(256)
641 let title: *u8 = sys_mmap(256)
642 let tbuf: *u8 = sys_mmap(K_MAGIC_4096)
643 let href: *u8 = sys_mmap(300)
644 let qp: *u8 = sys_mmap(512)
645 let radar: *u8 = sys_mmap(300)
646 var p: i64 = 0
647 while p < rn {
648 var e: i64 = p
649 while e < rn { if rl[e] == (10 as u8) { break } e = e + 1 }
650 if e > p { if rl[p] != (35 as u8) {
651 var d: i64 = 0
652 var q: i64 = p
653 while q < e { if d < 250 { dom[d] = rl[q]; d = d + 1 } q = q + 1 }
654 dom[d] = 0 as u8
655 if hub_reg_has(sbuf, sn, dom) == 0 {
656 if hub_dom_title(dom, title, tbuf, K_MAGIC_4096) == 1 {
657 var h: i64 = scopy(href, 0, "/compare/" as *u8); h = scopy(href, h, dom); href[h] = 0 as u8
658 var o: i64 = hub_kc(qp); o = scopy(qp, o, dom); o = scopy(qp, o, ".q" as *u8); qp[o] = 0 as u8
659 radar[0] = 45 as u8; radar[1] = 0 as u8
660 if c_exists(qp) == 1 { var r: i64 = scopy(radar, 0, href); r = scopy(radar, r, "/frontier" as *u8); radar[r] = 0 as u8 }
661 count = hub_add(cards, cn, count, title, "auto" as *u8, href, radar, "" as *u8)
662 }
663 }
664 } }
665 p = e + 1
666 }
667 // facts + groups
668 let abuf: *u8 = sys_mmap(HUB_API_CAP)
669 let pbuf: *u8 = sys_mmap(512)
670 let gbuf: *u8 = sys_mmap(K_MAGIC_20480)
671 let gp: *u8 = sys_mmap(512)
672 hub_kcpath(gp, "hub.groups" as *u8)
673 var gn: i64 = c_read(gp, gbuf, K_MAGIC_20479)
674 if gn < 0 { gn = 0 }
675 let sub2: *u8 = sys_mmap(300)
676 var c: i64 = 0
677 while c < count {
678 hub_facts(cards, cn, c, abuf, pbuf)
679 // subtitle fallback: the renderable's own @sub line
680 let csub: *u8 = hub_slot(cards, c, HO_SUB)
681 if csub[0] == (0 as u8) {
682 if hub_dom_line(hub_slot(cards, c, HO_DOM), "@sub " as *u8, 5, sub2, tbuf, K_MAGIC_4096) == 1 { hub_copyn(csub, sub2, 512) }
683 }
684 let g: i64 = hub_group(gbuf, gn, hub_slot(cards, c, HO_DOM), hub_slot(cards, c, HO_GROUP))
685 hub_setnum(cn, c, HN_GROUPED, g)
686 hub_slugify(hub_slot(cards, c, HO_GROUP), hub_slot(cards, c, HO_SLUG))
687 c = c + 1
688 }
689 // distinct groups in file order (a group named in the file but carrying no card is simply not emitted)
690 var ng: i64 = 0
691 var gi: i64 = 0
692 while gi < gn {
693 var ge: i64 = gi
694 while ge < gn { if gbuf[ge] == (10 as u8) { break } ge = ge + 1 }
695 if ge > gi { if gbuf[gi] != (35 as u8) {
696 var bar: i64 = gi
697 while bar < ge { if gbuf[bar] == (124 as u8) { break } bar = bar + 1 }
698 if bar < ge {
699 let gname: *u8 = (gt as i64 + ng * HUB_GSLOT) as *u8
700 var o2: i64 = 0
701 var q2: i64 = bar + 1
702 while q2 < ge { if o2 < 62 { gname[o2] = gbuf[q2]; o2 = o2 + 1 } q2 = q2 + 1 }
703 gname[o2] = 0 as u8
704 var dup: i64 = 0
705 var k2: i64 = 0
706 while k2 < ng { if streq((gt as i64 + k2 * HUB_GSLOT) as *u8, gname) == 1 { dup = 1; k2 = ng } else { k2 = k2 + 1 } }
707 if dup == 0 { if ng < HUB_MAXGROUPS - 1 { ng = ng + 1 } }
708 }
709 } }
710 gi = ge + 1
711 }
712 let ug: *u8 = (gt as i64 + ng * HUB_GSLOT) as *u8
713 let un: i64 = scopy(ug, 0, "Ungrouped" as *u8); ug[un] = 0 as u8
714 ng = ng + 1
715 gcount[0] = ng
716 return count
717}
718// ---- ESTATE ROLLUP (2026-09-01, ecosystem rung EC9) ----
719// THE DEFECT THIS CLOSES: hub_facts reads coverage exceeds present absent total generated_unix and (since
720// today) the two adoption counts out of EVERY domain api.json, and hub_card_html/hub_card_json publish them
721// PER CARD. Above the card there was NOTHING -- no sum, no median, no distribution, no oldest-measured. The
722// estate published ninety-six cards of numbers and not one aggregate, so a reader could ask /compare about
723// one domain at a time and never about the estate as a whole.
724// ABSTENTION IS A FIRST-CLASS OUTPUT, NOT A ZERO. A card whose ruler produced no tally is counted in
725// HR_ABSTAINED and EXCLUDED from the percentiles; scoring it 0 would drag the estate median down with
726// domains that were never measured, which is the flattering-direction error inverted -- it would slander a
727// healthy estate. Same rule for adoption: only cards that actually published the keys enter HR_WITH_ADOPT.
728// gname empty = the whole estate; gname set = that lane only, so one function serves both bands and the
729// per-lane numbers cannot drift from the estate ones.
730const HR_DOMAINS: i64 = 0
731const HR_WITH_TALLY: i64 = 1
732const HR_COV_P50: i64 = 2
733const HR_COV_P90: i64 = 3
734const HR_SUM_PRES: i64 = 4
735const HR_SUM_OPEN: i64 = 5
736const HR_SUM_EXC: i64 = 6
737const HR_SUM_UNJ: i64 = 7
738const HR_OLDEST: i64 = 8
739const HR_ABSTAINED: i64 = 9
740const HR_ADOPT_FULL: i64 = 10
741const HR_ADOPT_PART: i64 = 11
742const HR_WITH_ADOPT: i64 = 12
743const HR_N: i64 = 13
744func hub_rollup(cards: *u8, cn: *i64, count: i64, gname: *u8, out: *i64) -> i64 {
745 var z: i64 = 0
746 while z < HR_N { out[z] = 0; z = z + 1 }
747 out[HR_OLDEST] = 0 - 1
748 let covs: *i64 = sys_mmap(HUB_MAXCARDS * 8) as *i64
749 var nc: i64 = 0
750 var i: i64 = 0
751 while i < count {
752 var take: i64 = 1
753 if gname[0] != (0 as u8) { if streq(hub_slot(cards, i, HO_GROUP), gname) == 0 { take = 0 } }
754 if take == 1 {
755 out[HR_DOMAINS] = out[HR_DOMAINS] + 1
756 let cv: i64 = hub_num(cn, i, HN_COV)
757 if cv >= 0 {
758 out[HR_WITH_TALLY] = out[HR_WITH_TALLY] + 1
759 covs[nc] = cv; nc = nc + 1
760 let pr: i64 = hub_num(cn, i, HN_PRES); if pr > 0 { out[HR_SUM_PRES] = out[HR_SUM_PRES] + pr }
761 let ab: i64 = hub_num(cn, i, HN_ABS); if ab > 0 { out[HR_SUM_OPEN] = out[HR_SUM_OPEN] + ab }
762 let ex: i64 = hub_num(cn, i, HN_EXC); if ex > 0 { out[HR_SUM_EXC] = out[HR_SUM_EXC] + ex }
763 let uj: i64 = hub_num(cn, i, HN_UNJ); if uj > 0 { out[HR_SUM_UNJ] = out[HR_SUM_UNJ] + uj }
764 } else { out[HR_ABSTAINED] = out[HR_ABSTAINED] + 1 }
765 let af: i64 = hub_num(cn, i, HN_ADOPT_FULL)
766 if af >= 0 {
767 out[HR_WITH_ADOPT] = out[HR_WITH_ADOPT] + 1
768 out[HR_ADOPT_FULL] = out[HR_ADOPT_FULL] + af
769 let ap: i64 = hub_num(cn, i, HN_ADOPT_PART)
770 if ap > 0 { out[HR_ADOPT_PART] = out[HR_ADOPT_PART] + ap }
771 }
772 let gu: i64 = hub_num(cn, i, HN_GEN)
773 if gu > 0 { if out[HR_OLDEST] < 0 { out[HR_OLDEST] = gu } else { if gu < out[HR_OLDEST] { out[HR_OLDEST] = gu } } }
774 }
775 i = i + 1
776 }
777 // insertion sort ascending. THE LOOP EXIT IS A FLAG, NEVER A SENTINEL WRITTEN INTO THE CURSOR: setting
778 // b to -1 to break would destroy the insertion position the line after the loop depends on, which is a
779 // defect this estate has written four times in one day and is the reason this comment exists.
780 var a: i64 = 1
781 while a < nc {
782 let v: i64 = covs[a]
783 var b: i64 = a - 1
784 var done: i64 = 0
785 while done == 0 {
786 if b < 0 { done = 1 } else { if covs[b] > v { covs[b+1] = covs[b]; b = b - 1 } else { done = 1 } }
787 }
788 covs[b+1] = v
789 a = a + 1
790 }
791 if nc > 0 {
792 out[HR_COV_P50] = covs[nc / 2]
793 var i90: i64 = (nc * 9) / 10
794 if i90 >= nc { i90 = nc - 1 }
795 out[HR_COV_P90] = covs[i90]
796 } else { out[HR_COV_P50] = 0 - 1; out[HR_COV_P90] = 0 - 1 }
797 return out[HR_DOMAINS]
798}
799// order the cards of one group by coverage descending (unknown coverage last), then title; fills ord, returns n
800func hub_order(cards: *u8, cn: *i64, count: i64, gname: *u8, ord: *i64) -> i64 {
801 var n: i64 = 0
802 var i: i64 = 0
803 while i < count { if streq(hub_slot(cards, i, HO_GROUP), gname) == 1 { ord[n] = i; n = n + 1 } i = i + 1 }
804 // selection sort: n <= 256
805 var a: i64 = 0
806 while a < n {
807 var best: i64 = a
808 var b: i64 = a + 1
809 while b < n {
810 let cb: i64 = hub_num(cn, ord[b], HN_COV)
811 let cbest: i64 = hub_num(cn, ord[best], HN_COV)
812 if cb > cbest { best = b }
813 b = b + 1
814 }
815 if best != a { let t: i64 = ord[a]; ord[a] = ord[best]; ord[best] = t }
816 a = a + 1
817 }
818 return n
819}
820
821// The published measurement contract describes the producer; it does not compute a second score.
822func hub_measurement_schema(fd: i64) -> i64 {
823 w2(fd, "'SotaBoardMeasurement':{'type':'object','description':'Published domain-only observation produced by nx_sota_status. The MCP nishi_compare measurement resource serves the same artifact. Presence and evidence confidence do not establish maturity or SOTA benchmark attainment.','required':['v','api','resource','generated_unix','producer','scope','policy','limitations','domains'],'properties':{'v':{'t" as *u8)
824 w2(fd, "ype':'integer','const':1},'api':{'type':'string','const':'nishi-measurement'},'resource':{'type':'string','const':'sota-board'},'generated_unix':{'type':'integer','description':'Observation generation time in Unix seconds; not the execution time of cached evidence.'},'producer':{'type':'string','const':'nx_sota_status'},'scope':{'type':'object','required':['entity_kind','graded_domains','matrix_do" as *u8)
825 w2(fd, "mains','axes_only_domains','omitted_entries','unreadable_directory_passes','local_discovery_complete','estate_total','uncovered_entities','estate_wide'],'properties':{'entity_kind':{'type':'string','const':'domain'},'graded_domains':{'type':'integer','minimum':0,'description':'Number of emitted domains; does not represent an estate inventory denominator.'},'matrix_domains':{'type':'integer','minim" as *u8)
826 w2(fd, "um':0},'axes_only_domains':{'type':'integer','minimum':0},'omitted_entries':{'type':'integer','minimum':0},'unreadable_directory_passes':{'type':'integer','minimum':0},'local_discovery_complete':{'type':'boolean','description':'True only when local discovery reports no omissions or unreadable directory passes; does not establish estate-wide coverage.'},'estate_total':{'type':'null','description':'" as *u8)
827 w2(fd, "Not yet reconciled against Compare and capability inventories.'},'uncovered_entities':{'type':'null','description':'Uncovered entities cannot be enumerated before inventory reconciliation.'},'estate_wide':{'type':'boolean','const':false}}},'policy':{'type':'object','required':['ttl_sec','minimum_method_classes'],'properties':{'ttl_sec':{'type':'integer','minimum':0},'minimum_method_classes':{'type" as *u8)
828 w2(fd, "':'integer','minimum':0}}},'limitations':{'type':'array','items':{'type':'string'},'description':'Material measurement gaps retained in the emitted observation.'},'domains':{'type':'array','items':{'type':'object','required':['domain','classification','presence','source','evidence','rung','sota_benchmark','next_investment'],'properties':{'domain':{'type':'string'},'classification':{'type':'string'" as *u8)
829 w2(fd, ",'enum':['PROVEN','RED','MECH-OK','GATED-UNVERIFIED','CLAIM-ONLY','UNPROVEN'],'description':'Existing evidence classification; not a first-byte rung or SOTA quality score.'},'presence':{'type':'object','required':['permille','present','exceed_claims','gaps','total'],'properties':{'permille':{'type':'integer','minimum':0,'maximum':1000,'description':'Floor of 1000 times (present plus exceed_claims)" as *u8)
830 w2(fd, " divided by total; zero when total is zero. Presence is not maturity.'},'present':{'type':'integer','minimum':0},'exceed_claims':{'type':'integer','minimum':0,'description':'Matrix exceed claims; does not establish independently benchmarked superiority.'},'gaps':{'type':'integer','minimum':0},'total':{'type':'integer','minimum':0,'description':'Exact classified axis denominator: present plus excee" as *u8)
831 w2(fd, "d_claims plus gaps.'}}},'source':{'type':'object','required':['path','bytes_read','state'],'properties':{'path':{'type':['string','null'],'description':'Resolved local matrix or axes path; null when no source resolved.'},'bytes_read':{'type':'integer'},'state':{'type':'string','enum':['read','unreadable_or_empty']}}},'evidence':{'type':'object','required':['has_nonempty_gate_map','stamp_epoch','ag" as *u8)
832 w2(fd, "e_seconds','freshness','referee_reason'],'properties':{'has_nonempty_gate_map':{'type':'boolean'},'stamp_epoch':{'type':['integer','null'],'description':'Cached evidence execution epoch; null when absent.'},'age_seconds':{'type':['integer','null'],'description':'Observation time minus stamp epoch; null when absent, negative for a future stamp.'},'freshness':{'type':'string','enum':['unknown','futu" as *u8)
833 w2(fd, "re_stamp','fresh','stale']},'referee_reason':{'type':['string','null']}}},'rung':{'type':'null','description':'First-byte rung bindings are not yet measured.'},'sota_benchmark':{'type':'null','description':'SOTA benchmark identity and attainment are not yet bound.'},'next_investment':{'type':'null','description':'Requires an authoritative action and effort record; no investment estimate is inferre" as *u8)
834 w2(fd, "d.'}}}}}}," as *u8)
835 return 0
836}
837
838// Publish the existing ledger declaration contract without adding another ROI calculation.
839func hub_investment_schema(fd: i64) -> i64 {
840 w2(fd, "'TokenInvestmentReport':{'type':'object','description':'Exact published nx_tokroi report from the existing eight-column ledger. Values, REALIZED/PROJECTED labels and evidence strings are declarations; not verified savings, execution history or benchmark results. No domain/capability join is asserted.','required':['organ','verb','ok','levers','realized','projected','rows','unknown_status_rows','dec" as *u8)
841 w2(fd, "lared_rows','loaded_rows','beyond_count_rows_detected','history_complete','evidence_scope','units_verified','note'],'properties':{'organ':{'type':'string','const':'nx_tokroi'},'verb':{'type':'string','const':'report'},'ok':{'type':'boolean','const':true},'levers':{'type':'array','items':{'type':'object','required':['id','lever','status','investment','baseline','after','reduction_per_use','uses_per" as *u8)
842 w2(fd, "_day','daily_reduction','payback_uses','evidence','signed_delta','break_even_uses','aggregation_eligible','evidence_verified'],'properties':{'id':{'type':'string','minLength':1},'lever':{'type':'string','minLength':1},'status':{'type':'string','description':'Ledger label. Exact REALIZED and PROJECTED labels are aggregated separately; other labels remain visible and excluded.'},'investment':{'type'" as *u8)
843 w2(fd, ":'integer','minimum':0,'description':'Declared one-off estimate; not observed implementation cost.'},'baseline':{'type':'integer','minimum':0},'after':{'type':'integer','minimum':0},'reduction_per_use':{'type':'integer','minimum':0,'description':'Legacy max(baseline-after,0), preserving the existing nonnegative field.'},'uses_per_day':{'type':'integer','minimum':0,'description':'Declared assumptio" as *u8)
844 w2(fd, "n, not observed use frequency.'},'daily_reduction':{'type':'integer','minimum':0,'description':'Declared reduction_per_use multiplied by assumed uses_per_day; not measured daily savings.'},'payback_uses':{'type':'integer','minimum':-1,'description':'Legacy floor(investment/reduction_per_use), or -1 when reduction is not positive.'},'evidence':{'type':'string','description':'Full ledger evidence st" as *u8)
845 w2(fd, "ring, neither resolved nor independently verified.'},'signed_delta':{'type':'integer','description':'baseline-after; negative values retain declared regressions.'},'break_even_uses':{'type':['integer','null'],'minimum':0,'description':'Ceiling(investment/reduction_per_use) when reduction is positive; otherwise null. Conditional arithmetic assumes comparable units; not a payoff date.'},'aggregation" as *u8)
846 w2(fd, "_eligible':{'type':'boolean','description':'Whether the exact label belongs to the two supported totals; not evidence quality.'},'evidence_verified':{'type':'boolean','const':false}}}},'realized':{'type':'object','description':'Totals for rows labelled REALIZED; the label does not establish observed savings.','required':['daily_reduction','investment','levers'],'properties':{'daily_reduction':{'ty" as *u8)
847 w2(fd, "pe':'integer','minimum':0},'investment':{'type':'integer','minimum':0},'levers':{'type':'integer','minimum':0}}},'projected':{'type':'object','description':'Separate totals for rows labelled PROJECTED.','required':['daily_reduction','investment','levers'],'properties':{'daily_reduction':{'type':'integer','minimum':0},'investment':{'type':'integer','minimum':0},'levers':{'type':'integer','minimum':" as *u8)
848 w2(fd, "0}}},'rows':{'type':'integer','minimum':0},'unknown_status_rows':{'type':'integer','minimum':0},'declared_rows':{'type':'integer','minimum':0},'loaded_rows':{'type':'integer','minimum':0},'beyond_count_rows_detected':{'type':'integer','minimum':0,'description':'Bounded beyond-count detection; does not establish full retained history.'},'history_complete':{'type':'null','description':'The report do" as *u8)
849 w2(fd, "es not verify complete historical coverage.'},'evidence_scope':{'type':'string'},'units_verified':{'type':'boolean','const':false},'note':{'type':'string'}}}," as *u8)
850 return 0
851}
852
853// Describe the existing bindings artifact without upgrading declarations to verified outcomes.
854func hub_bindings_schema(fd: i64) -> i64 {
855 w2(fd, "'DomainBindingsReport':{'type':'object','required':['v','api','resource','status','producer','domain','generated_unix','evidence_scope','plan_source','declared_rung_count','declared_unit_rows','rank_snapshot','declared_rungs','limitations'],'properties':{'v':{'type':'integer','enum':[1]},'api':{'type':'string','enum':['nishi-measurement']},'resource':{'type':'string','enum':['d" as *u8)
856 w2(fd, "omain-bindings']},'status':{'type':'string','enum':['ok']},'producer':{'type':'string','enum':['nx_sota_status']},'domain':{'type':'string'},'generated_unix':{'type':'integer'},'declared_rungs':{'type':'array','items':{'type':'object','required':['id','title','contract_symbol','declared_done_rule','executor_class','estimate_text','dependencies_text','source_record','ordered_obs" as *u8)
857 w2(fd, "ervation','finish_observation','unmapped_observation','behavior_evidence','source_binding','api_binding','mcp_binding'],'properties':{'id':{'type':'string'},'title':{'type':'string'},'source_record':{'type':'object','additionalProperties':true},'contract_symbol':{'type':['string','null']},'declared_done_rule':{'type':['string','null']},'executor_class':{'type':['string','null']" as *u8)
858 w2(fd, "},'estimate_text':{'type':['string','null']},'dependencies_text':{'type':['string','null']},'ordered_observation':{'type':['object','null'],'additionalProperties':true},'finish_observation':{'type':['object','null'],'additionalProperties':true},'unmapped_observation':{'type':['object','null'],'additionalProperties':true},'behavior_evidence':{'type':['object','null'],'additional" as *u8)
859 w2(fd, "Properties':true},'source_binding':{'type':['object','null'],'additionalProperties':true},'api_binding':{'type':['object','null'],'additionalProperties':true},'mcp_binding':{'type':['object','null'],'additionalProperties':true}},'additionalProperties':true}},'limitations':{'type':'array','items':{'type':'string'}},'evidence_scope':{'type':'string'},'plan_source':{'type':'object" as *u8)
860 w2(fd, "','additionalProperties':true},'declared_rung_count':{'type':'integer','minimum':0},'declared_unit_rows':{'type':'array','items':{'type':'string'}},'rank_snapshot':{'type':'object','additionalProperties':true}},'additionalProperties':true,'description':'Published nx_sota_status bindings artifact. Null binding/evidence fields retain unresolved joins; declared estimates and rank " as *u8)
861 w2(fd, "snapshots are not verified savings or benchmark attainment.'}," as *u8)
862 return 0
863}
864
865func hub_bindings_path(fd: i64) -> i64 {
866 w2(fd, ",'/compare/{domain}/bindings.json':{'get':{'operationId':'getDomainBindings','summary':'Declared rung bindings and independently stamped rank observations','description':'Complete published per-domain JSON under existing Compare access policy. MCP nishi_compare arguments bindings and domain return a small public-only artifact manifest; retrieve the complete HTTP resource or bou" as *u8)
867 w2(fd, "nded existing nx_fs windows, observing reader footers and manifest limitations. Declarations and independently captured rank output do not prove function behavior, source/API/MCP bindings, realized investment or SOTA attainment.','parameters':[{'name':'domain','in':'path','required':true,'schema':{'type':'string'}}],'responses':{'200':{'description':'Complete published domain-b" as *u8)
868 w2(fd, "indings JSON artifact','content':{'application/json':{'schema':" as *u8)
869 wref(fd, "DomainBindingsReport" as *u8)
870 w2(fd, "}}},'404':{'description':'Artifact absent or domain not visible under the existing access policy'}}}}" as *u8)
871 return 0
872}
873
874func main(argc: i64, argv: *i64) -> i64 {
875 var html: i64 = 0
876 if argc >= 2 { if streq(argv[1] as *u8, "html" as *u8) == 1 { html = 1 } }
877 var jsonmode: i64 = 0
878 if argc >= 2 { if streq(argv[1] as *u8, "json" as *u8) == 1 { jsonmode = 1 } }
879 var oamode: i64 = 0
880 if argc >= 2 { if streq(argv[1] as *u8, "openapi" as *u8) == 1 { oamode = 1 } }
881 let cap: i64 = K_MAGIC_65536
882 let sbuf: *u8 = sys_mmap(cap)
883 let sn: i64 = c_read("knowledge/compare/registry" as *u8, sbuf, cap)
884 let fbuf: *u8 = sys_mmap(K_MAGIC_4096)
885
886 if oamode == 1 {
887 // OpenAPI 3.1 generated IN CODE from the same registry the endpoints serve -- SSOT, cannot drift.
888 let dbuf: *u8 = sys_mmap(256)
889 let pbuf: *u8 = sys_mmap(512)
890 w2(1, "{'openapi':'3.1.0','info':{'title':'Nishi Compare API','version':'1.2.0'," as *u8)
891 w2(1, "'summary':'Mechanically measured, liar-killed software comparisons - Nishi vs the field.'," as *u8)
892 w2(1, "'description':'Machine-readable comparison data. Every Nishi cell is verified against real organ source on disk (no self-grading); competitor cells are documented capability presence; " as *u8)
893 w2(1, "each matrix is liar-killed (a faked win fails the build) and republished by nx_compare_regen only when its gate exits GREEN. Served bits-up by the sovereign Nishi stack over TLS 1.3.'," as *u8)
894 w2(1, "'x-generated-by':'nx_swcompare_hub openapi (in-code SSOT)','x-generated-unix':" as *u8)
895 wn(1, sys_now_realtime_sec())
896 w2(1, "},'servers':[{'url':'https://nishifamily.com'}],'x-available-domains':[" as *u8)
897 var i0: i64 = 0; var first0: i64 = 1
898 while i0 < sn {
899 var j0: i64 = i0
900 while j0 < sn { if sbuf[j0] == (10 as u8) { break } j0 = j0 + 1 }
901 if sbuf[i0] != (35 as u8) { if j0 > i0 {
902 let nf0: i64 = split5(sbuf, i0, j0, fbuf)
903 if nf0 >= 5 {
904 dom_of((fbuf as i64 + K_MAGIC_1024) as *u8, dbuf)
905 if dbuf[0] != (0 as u8) { if data_exists(dbuf, pbuf) == 1 {
906 if first0 == 0 { wc(1, 44) }
907 first0 = 0
908 wq(1); wj(1, dbuf); wq(1)
909 } }
910 }
911 } }
912 i0 = j0 + 1
913 }
914 w2(1, "],'paths':{'/compare/api.json':{'get':{'operationId':'getCompareIndex','summary':'List every comparison (index)','responses':{'200':{'description':'The comparison index','content':{'application/json':{'schema':" as *u8)
915 wref(1, "CompareIndex" as *u8)
916 w2(1, "}}}}}},'/compare/{domain}/api.json':{'get':{'operationId':'getCompareMatrix','summary':'Head-to-head matrix data for one domain','parameters':[{'name':'domain','in':'path','required':true,'schema':{'type':'string'}," as *u8)
917 w2(1, "'description':'Comparison domain; see x-available-domains'}],'responses':{'200':{'description':'Matrix data','content':{'application/json':{'schema':" as *u8)
918 wref(1, "CompareMatrix" as *u8)
919 w2(1, "}}},'404':{'description':'No machine-readable data for this domain'}}}},'/compare/maturity/measurement.json':{'get':{'operationId':'getSotaBoardMeasurement','summary':'Read the published domain evidence measurement','description':'The same published artifact is available through nishi_compare with argument measurement. Generated time is distinct from cached evidence time; this is not an estate-wide maturity or SOTA score.','responses':{'200':{'description':'Domain-only measurement snapshot','content':{'application/json':{'schema':" as *u8)
920 wref(1, "SotaBoardMeasurement" as *u8)
921 w2(1, "}}},'404':{'description':'No published measurement artifact'}}}},'/compare/maturity/investment.json':{'get':{'operationId':'getTokenInvestmentReport','summary':'Read the published token investment declarations','description':'Exact nx_tokroi report artifact, also served by nishi_compare with argument investment. Ledger REALIZED labels are not verified savings; units, evidence, history and domain bindings remain unverified.','responses':{'200':{'description':'Existing token ROI ledger report with declaration limits','content':{'application/json':{'schema':" as *u8)
922 wref(1, "TokenInvestmentReport" as *u8)
923 w2(1, "}}},'404':{'description':'No published investment artifact'}}}}" as *u8)
924 hub_bindings_path(1)
925 w2(1, ",'/compare/openapi.json':{'get':{'operationId':'getOpenApi','summary':'This OpenAPI document','responses':{'200':{'description':'OpenAPI 3.1 document'}}}}}," as *u8)
926 w2(1, "'components':{'schemas':{" as *u8)
927 hub_measurement_schema(1)
928 hub_investment_schema(1)
929 hub_bindings_schema(1)
930 w2(1, "'CompareIndex':{'type':'object','required':['v','api','resource','comparisons'],'properties':{'v':{'type':'integer','const':1},'api':{'type':'string'},'generated_unix':{'type':'integer'},'resource':{'type':'string'}," as *u8)
931 w2(1, "'comparisons':{'type':'array','items':" as *u8)
932 wref(1, "Comparison" as *u8)
933 w2(1, "},'_links':{'type':'object','additionalProperties':{'type':'string'}}}},'Comparison':{'type':'object','required':['domain','title','kind','page'],'properties':{'domain':{'type':'string'},'title':{'type':'string'}," as *u8)
934 w2(1, "'kind':{'type':'string','enum':['live','radar','soon','matrix','auto']},'kind_measured':{'type':'string','description':'measured | sota | radar | live | soon -- derived from the domain data page at emit'},'group':{'type':'string'},'page':{'type':'string'},'radar':{'type':'string'},'data':{'type':'string','description':'Machine-readable matrix endpoint; present only when available'}," as *u8)
935 w2(1, "'coverage':{'type':'integer','description':'permille, from the domain tally'},'exceeds':{'type':'integer'},'present':{'type':'integer'},'absent':{'type':'integer'},'generated_unix':{'type':'integer'},'subtitle':{'type':'string'},'summary':{'type':'string'}}}," as *u8)
936 w2(1, "'CompareMatrix':{'type':'object','required':['v','api','domain','kind','competitors','axes','tally'],'properties':{'v':{'type':'integer','const':1},'api':{'type':'string'},'generated_unix':{'type':'integer','description':'Wall-clock epoch seconds when this matrix was measured'}," as *u8)
937 w2(1, "'domain':{'type':'string'},'kind':{'type':'string','const':'matrix'},'title':{'type':'string'},'subtitle':{'type':'string'},'competitors':{'type':'array','items':{'type':'string'}},'axes':{'type':'array','items':" as *u8)
938 wref(1, "Axis" as *u8)
939 w2(1, "},'tally':" as *u8)
940 wref(1, "Tally" as *u8)
941 w2(1, ",'codes':{'type':'string'},'measurement':{'type':'string'}}},'Axis':{'type':'object','required':['label','nishi','cells'],'properties':{'label':{'type':'string'},'nishi':{'type':'integer','description':'0=No 1=Yes 2=Best 3=Part'},'exceed':{'type':'integer'}," as *u8)
942 w2(1, "'cells':{'type':'array','items':{'type':'integer'},'description':'One code per competitor, same order as competitors'},'note':{'type':'string'}}},'Tally':{'type':'object','properties':{'coverage':{'type':'integer','description':'permille (present+exceeds)/total'}," as *u8)
943 w2(1, "'exceeds':{'type':'integer'},'present':{'type':'integer'},'absent':{'type':'integer'},'total':{'type':'integer'}}}}}}" as *u8)
944 wc(1, 10)
945 sys_exit(0); return 0
946 }
947
948 // v3: the card table is built once for json / html / console
949 let cards: *u8 = sys_mmap(HUB_MAXCARDS * HUB_SLOT)
950 let cn: *i64 = sys_mmap(HUB_MAXCARDS * HN * 8) as *i64
951 let gt: *u8 = sys_mmap(HUB_MAXGROUPS * HUB_GSLOT)
952 let gcount: *i64 = sys_mmap(16) as *i64
953 gcount[0] = 0
954 let count: i64 = hub_build(sbuf, sn, cards, cn, gt, gcount)
955 let ng: i64 = gcount[0]
956 let ord: *i64 = sys_mmap(HUB_MAXCARDS * 8) as *i64
957
958 if jsonmode == 1 {
959 let pbuf: *u8 = sys_mmap(512)
960 wc(1, 123)
961 kv_n(1, "v" as *u8, 1); wc(1, 44)
962 kv_s(1, "api" as *u8, "nishi-compare" as *u8); wc(1, 44)
963 kv_n(1, "generated_unix" as *u8, sys_now_realtime_sec()); wc(1, 44)
964 kv_s(1, "resource" as *u8, "index" as *u8); wc(1, 44)
965 kv_n(1, "count" as *u8, count); wc(1, 44)
966 // THE ESTATE BAND -- the aggregate this hub never published. Every figure is summed from the SAME
967 // per-card numbers the cards render, so the page and the api cannot disagree. domains is emitted
968 // beside count so a reader can CHECK THE PARTITION rather than take it on trust.
969 let er: *i64 = sys_mmap(HR_N * 8) as *i64
970 hub_rollup(cards, cn, count, "" as *u8, er)
971 wq(1); w(1, "estate" as *u8); wq(1); wc(1, 58); wc(1, 123)
972 kv_n(1, "domains" as *u8, er[HR_DOMAINS]); wc(1, 44)
973 kv_n(1, "with_tally" as *u8, er[HR_WITH_TALLY]); wc(1, 44)
974 kv_n(1, "abstained" as *u8, er[HR_ABSTAINED]); wc(1, 44)
975 kv_n(1, "coverage_p50" as *u8, er[HR_COV_P50]); wc(1, 44)
976 kv_n(1, "coverage_p90" as *u8, er[HR_COV_P90]); wc(1, 44)
977 kv_n(1, "present" as *u8, er[HR_SUM_PRES]); wc(1, 44)
978 kv_n(1, "open" as *u8, er[HR_SUM_OPEN]); wc(1, 44)
979 kv_n(1, "exceeds" as *u8, er[HR_SUM_EXC]); wc(1, 44)
980 kv_n(1, "unjudgeable" as *u8, er[HR_SUM_UNJ]); wc(1, 44)
981 kv_n(1, "adopted_full" as *u8, er[HR_ADOPT_FULL]); wc(1, 44)
982 kv_n(1, "adopted_partial" as *u8, er[HR_ADOPT_PART]); wc(1, 44)
983 kv_n(1, "with_adoption" as *u8, er[HR_WITH_ADOPT]); wc(1, 44)
984 kv_n(1, "oldest_measured_unix" as *u8, er[HR_OLDEST])
985 wc(1, 125); wc(1, 44)
986 wq(1); w(1, "groups" as *u8); wq(1); wc(1, 58); wc(1, 91)
987 // PER-LANE ROLLUP. The groups array carried a NAME and a COUNT and nothing else, so a lane -- the
988 // estate's only existing subdomain declaration -- could not be asked how healthy it is. Same
989 // hub_rollup as the estate band, so a lane figure and the estate figure are computed by ONE ruler
990 // and can never drift; the buffer is hoisted out of the loop rather than mapped per group.
991 let gr: *i64 = sys_mmap(HR_N * 8) as *i64
992 var g0: i64 = 0
993 while g0 < ng {
994 let gname0: *u8 = (gt as i64 + g0 * HUB_GSLOT) as *u8
995 let n0: i64 = hub_order(cards, cn, count, gname0, ord)
996 if n0 > 0 {
997 if g0 > 0 { wc(1, 44) }
998 hub_rollup(cards, cn, count, gname0, gr)
999 wc(1, 123); kv_s(1, "group" as *u8, gname0); wc(1, 44); kv_n(1, "count" as *u8, n0); wc(1, 44)
1000 kv_n(1, "coverage_p50" as *u8, gr[HR_COV_P50]); wc(1, 44)
1001 kv_n(1, "present" as *u8, gr[HR_SUM_PRES]); wc(1, 44)
1002 kv_n(1, "open" as *u8, gr[HR_SUM_OPEN]); wc(1, 44)
1003 kv_n(1, "exceeds" as *u8, gr[HR_SUM_EXC]); wc(1, 44)
1004 kv_n(1, "adopted_full" as *u8, gr[HR_ADOPT_FULL]); wc(1, 44)
1005 kv_n(1, "adopted_partial" as *u8, gr[HR_ADOPT_PART]); wc(1, 44)
1006 kv_n(1, "abstained" as *u8, gr[HR_ABSTAINED]); wc(1, 125)
1007 }
1008 g0 = g0 + 1
1009 }
1010 wc(1, 93); wc(1, 44)
1011 wq(1); w(1, "comparisons" as *u8); wq(1); wc(1, 58); wc(1, 91)
1012 var ci: i64 = 0
1013 while ci < count {
1014 if ci > 0 { wc(1, 44) }
1015 hub_card_json(cards, cn, ci, pbuf)
1016 ci = ci + 1
1017 }
1018 wc(1, 93); wc(1, 44)
1019 wq(1); w(1, "_links" as *u8); wq(1); wc(1, 58); wc(1, 123)
1020 kv_s(1, "self" as *u8, "/compare/api.json" as *u8); wc(1, 44)
1021 kv_s(1, "openapi" as *u8, "/compare/openapi.json" as *u8); wc(1, 44)
1022 kv_s(1, "html" as *u8, "/compare" as *u8)
1023 wc(1, 125); wc(1, 125); wc(1, 10)
1024 sys_exit(0); return 0
1025 }
1026
1027 if html == 1 {
1028 // shared site chrome (nav + footer) generated by nx_site_chrome from knowledge/site/surfaces.reg.
1029 // Read as self-contained fragments; graceful fallback to a minimal nav if absent.
1030 let hdrbuf: *u8 = sys_mmap(K_MAGIC_20480)
1031 var hdn: i64 = c_read("knowledge/site/chrome_header.html" as *u8, hdrbuf, K_MAGIC_20479)
1032 if hdn < 0 { hdn = 0 }
1033 hdrbuf[hdn] = 0 as u8
1034 let ftbuf: *u8 = sys_mmap(K_MAGIC_20480)
1035 var ftn: i64 = c_read("knowledge/site/chrome_footer.html" as *u8, ftbuf, K_MAGIC_20479)
1036 if ftn < 0 { ftn = 0 }
1037 ftbuf[ftn] = 0 as u8
1038 w(1, "<" as *u8); wc(1, 33); w(1, "DOCTYPE html>\n<html lang=\"en\"><head><meta charset='utf-8'><link rel='alternate' type='application/rss+xml' title='Nishi /compare daily positions and reviews' href='/compare/feed.xml'><meta name='viewport' content='width=device-width, initial-scale=1'>\n" as *u8)
1039 w(1, "<title>Nishi Compare -- sovereign, measured software comparison</title>\n<style>\n" as *u8)
1040 // ---- THEME + LAYOUT FROM THE ONE EMITTER (2026-08-31) ----
1041 // THIS PAGE WAS THE LAST ONE ON A PRIVATE RULER, AND IT IS THE MOST-VISITED ONE. The line that
1042 // used to sit here was a THIRD design SSOT: eleven hardcoded rgb() literals defining the same
1043 // token names nx_brand_tokens already publishes, and it was DARK-ONLY -- a bare :root with no
1044 // html[data-theme='dark'] and no prefers-color-scheme block, so the /compare LANDING PAGE
1045 // ignored the viewer's theme by construction while every domain page it links to honoured it.
1046 // The body rule beside it hardcoded max-width:1180px, so a 1180px dark index linked to fluid,
1047 // theme-aware domain pages. Both are now composed from nx_swcompare_lib -- the SAME two calls
1048 // nx_swcompare_matrix and nx_swcompare_sota make. The CSS is COMPOSED, never copied: a copy is
1049 // the duplicate-ruler defect that produced six different page widths across seven generators.
1050 // sc_theme_pass is fail-closed (it exits rather than emit a zero, truncated or theme-blind
1051 // palette), so adopting it cannot silently ship this page with no colours.
1052 sc_theme_pass(1)
1053 sc_layout_pass(1)
1054 // [hidden] is NOT in sc_layout_pass and must stay here: the filter script sets el.hidden on
1055 // every card, so without this rule the no-match state renders every card instead of none.
1056 w(1, "[hidden]{display:none}\n" as *u8)
1057 // The two hub-local status hues, as var() ALIASES over SSOT tokens rather than literals, so they
1058 // follow the theme like everything else. --r is the roadmap/radar pill and --p (part) is the
1059 // estate's amber for an open item; --b is the sota pill and --ex (exceed) is the estate's
1060 // colour for a beats-the-field claim. Neither is a new colour: this file no longer owns one.
1061 w(1, ":root{--r:var(--nx-color-part);--b:var(--nx-color-exceed)}\n" as *u8)
1062 w(1, "h1{font-size:clamp(1.7rem,5vw,2.2rem);margin:0 0 6px;color:var(--fg);text-wrap:balance}.eyebrow{letter-spacing:.14em;text-transform:uppercase;font-size:.72rem;color:var(--mut);margin:22px 0 6px}.eyebrow b{color:var(--ac);font-weight:600}.sub{color:var(--mut);font-size:clamp(.95rem,2vw,1.05rem);margin:0 0 4px;max-width:var(--nx-layout-measure);text-wrap:pretty}.crumb{font-size:.82rem;margin:14px 0 2px;color:var(--mut)}a{color:var(--ac);text-decoration:none}a:hover{text-decoration:underline}\n" as *u8)
1063 // THE FOREGROUND ON AN ACCENT FILL HAD TO CHANGE WITH THE PALETTE, AND THIS IS THE ONE PLACE
1064 // ADOPTING THE SSOT COULD HAVE SHIPPED A REGRESSION. rgb(15,10,30) is a near-black that was
1065 // chosen for the OLD dark-only accent rgb(199,163,255); against the SSOT's LIGHT accent
1066 // rgb(88,64,180) it computes to about 2.4:1, i.e. it would have FAILED WCAG AA the moment this
1067 // page gained a light theme -- a contrast regression introduced BY the fix, on the skip link,
1068 // which is the one control a keyboard user meets first. var(--nx-color-bg) flips with the
1069 // theme and clears 4.5:1 in BOTH (about 6.9:1 on paper, about 7.5:1 on dark). It is also the
1070 // exact idiom nx_swcompare_matrix already uses on its own .skip-link -- the sibling had the fix.
1071 w(1, ".skip-link{position:absolute;left:-999px;top:0;background:var(--ac);color:var(--nx-color-bg);padding:12px 16px;z-index:9;border-radius:0 0 8px 0}.skip-link:focus{left:0}\n" as *u8)
1072 w(1, ".hd{border-bottom:1px solid var(--line);margin:0 0 8px}.hd nav{display:flex;gap:6px;flex-wrap:wrap;padding:10px 0}.hd a{color:var(--fg);text-decoration:none;padding:8px 12px;border-radius:8px;font-weight:600;min-height:44px;display:inline-flex;align-items:center}.hd a:hover{background:var(--soft)}\n" as *u8)
1073 // MEASURE CAP ON THE THREE PROSE BLOCKS (.sub above, .lead here, .foot below). The page is now
1074 // FLUID (--nx-layout-wrap tops out near 1760px where it used to stop at 1180px), and these are
1075 // the only three elements on the page whose text is not already constrained by a grid track --
1076 // .lead is a ~700-character paragraph, so uncapped it would run past 240 characters per line,
1077 // about 3x the WCAG 1.4.8 (AAA) 80-character ceiling. Widening the canvas WITHOUT capping the
1078 // prose is the regression the same change already shipped on the sota pages, so it is fixed
1079 // here in the same edit that causes it. The cap reads the SSOT token rather than a fresh number.
1080 w(1, ".lead{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:14px 18px;margin:16px 0;font-size:.9rem;color:var(--mut);max-width:var(--nx-layout-measure);text-wrap:pretty}.lead b{color:var(--fg)}\n" as *u8)
1081 w(1, ".tb{position:sticky;top:0;z-index:5;background:var(--bg);padding:10px 0 8px;border-bottom:1px solid var(--line);margin:0 0 14px}.tb input{width:100%;padding:12px 16px;font-size:1rem;border:1px solid var(--line);border-radius:12px;background:var(--panel);color:var(--fg);min-height:46px}.tb input:focus{outline:3px solid var(--ac);outline-offset:1px;border-color:var(--ac)}\n" as *u8)
1082 w(1, ".chips{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 0}.chip{display:inline-flex;align-items:center;min-height:34px;padding:2px 12px;border:1px solid var(--line);border-radius:20px;font-size:.8rem;color:var(--fg);background:var(--panel)}.chip:hover{border-color:var(--ac);text-decoration:none}.chip.on{background:var(--ac);color:var(--nx-color-bg);border-color:var(--ac);font-weight:600}.chip small{color:inherit;opacity:.7;margin-left:6px}\n" as *u8)
1083 w(1, ".tbrow{display:flex;flex-wrap:wrap;gap:10px 18px;align-items:center;margin:8px 0 0;font-size:.85rem;color:var(--mut)}.tbrow select{background:var(--panel);color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:6px 10px;min-height:34px}.srh{margin:0;min-height:1.1em;color:var(--mut)}.rst{color:var(--mut);text-decoration:underline}\n" as *u8)
1084 w(1, ".jump{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:.82rem;color:var(--mut);margin:0 0 6px}.grp{margin:22px 0 6px}.grp h2{font-size:1.05rem;letter-spacing:.06em;text-transform:uppercase;color:var(--mut);margin:0 0 10px;padding-bottom:6px;border-bottom:1px solid var(--line)}.grp h2 .cnt{color:var(--ac);font-weight:600;margin-left:8px}\n" as *u8)
1085 w(1, ".grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px}.cu{min-width:0}.card{display:block;height:100%;border:1px solid var(--line);border-radius:14px;padding:14px 16px;color:inherit;background:var(--panel)}.card:hover{border-color:var(--ac);text-decoration:none}.card h3{margin:0 0 4px;font-size:1.02rem;color:var(--ac);line-height:1.3}.card .stat{color:var(--mut);font-size:.8rem;margin-top:6px;display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden}.card .sub2{color:var(--fg);font-size:.84rem;margin-top:4px;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}\n" as *u8)
1086 w(1, ".bar{height:6px;background:var(--soft);border-radius:4px;overflow:hidden;margin:8px 0 6px}.bar i{display:block;height:100%;background:var(--y)}.nums{font-size:.78rem;color:var(--mut);font-family:var(--nx-font-mono);font-variant-numeric:tabular-nums}.nums b{color:var(--fg)}\n" as *u8)
1087 w(1, ".pill{display:inline-block;font-size:.64rem;padding:2px 8px;border-radius:20px;background:rgba(121,224,167,0.14);border:1px solid rgba(121,224,167,0.4);color:var(--y);vertical-align:middle;margin-left:6px;font-weight:600;letter-spacing:.06em;text-transform:uppercase}.pill.k-radar{background:rgba(255,209,122,0.12);border-color:rgba(255,209,122,0.4);color:var(--r)}.pill.k-soon{background:rgba(150,162,186,0.12);border-color:rgba(150,162,186,0.4);color:var(--mut)}.pill.k-sota{background:rgba(122,181,255,0.12);border-color:rgba(122,181,255,0.4);color:var(--b)}\n" as *u8)
1088 w(1, ".frl{margin:4px 0 0 6px;font-size:.8rem}.empty{color:var(--mut);padding:24px 0}.foot{margin-top:34px;color:var(--mut);font-size:.76rem;border-top:1px solid var(--line);padding-top:14px;font-family:var(--nx-font-mono);max-width:var(--nx-layout-measure);text-wrap:pretty}\n" as *u8)
1089 w(1, ":focus-visible{outline:3px solid var(--ac);outline-offset:2px}@media(prefers-reduced-motion:reduce){*{animation:none}}\n" as *u8)
1090 // THE PRINT SHEET GOES LAST (datavis DV7, 2026-09-15): order is its specificity -- see sc_print_css in nx_swcompare_lib
1091 sc_print_css(1)
1092 w(1, "</style></head><body>\n" as *u8)
1093 w(1, "<a class='skip-link' href='" as *u8); wc(1, 35); w(1, "main'>Skip to content</a>\n" as *u8)
1094 if hdn > 0 { w(1, hdrbuf) } else { w(1, "<header class='hd'><nav><a href='/'>Nishi Family</a><a href='/compare'>Compare</a></nav></header>" as *u8) }
1095 w(1, "<main id='main'>\n" as *u8)
1096 w(1, "<p class='crumb'><a href='/'>Nishi Family</a> › Compare</p>\n<p class='eyebrow'><b>nishifamily.com</b> · measured, not asserted</p>\n<h1>Nishi Compare</h1>\n" as *u8)
1097 var ngne: i64 = 0
1098 var gq: i64 = 0
1099 while gq < ng { if hub_order(cards, cn, count, (gt as i64 + gq * HUB_GSLOT) as *u8, ord) > 0 { ngne = ngne + 1 } gq = gq + 1 }
1100 w(1, "<p class='sub'>Nishi vs the field — <b>mechanically measured</b>, not analyst opinion. " as *u8); wn(1, count); w(1, " comparisons across " as *u8); wn(1, ngne); w(1, " groups.</p>\n" as *u8)
1101 w(1, "<div class='lead'>Every comparison is generated by a sovereign Nishi organ: each <b>Nishi</b> cell is verified against real source on disk (no self-grading), competitor cells are cited, and each matrix is <b>liar-killed</b> — a faked win fails the build. The coverage number on a card is the domain's own tally (present plus exceeds over all measured axes, permille); the open count is its named build contracts, watched on every emit. No quadrants, no pay-to-play, no trackers. Measured bits-up.</div>\n" as *u8)
1102 // sticky toolbar: search + group chips + kind chips + sort + count + reset (all anchors/controls work without JS)
1103 w2(1, "<div class='tb' id='tb'><label for='cmpq' style='position:absolute;left:-999px'>Search comparisons</label><input type='search' id='cmpq' placeholder='Search comparisons -- title, group, subtitle, rival names... (press / to focus)' autocomplete='off' spellcheck='false'>\n" as *u8)
1104 w2(1, "<div class='chips' id='gchips'>" as *u8)
1105 var g1: i64 = 0
1106 while g1 < ng {
1107 let gname1: *u8 = (gt as i64 + g1 * HUB_GSLOT) as *u8
1108 let n1: i64 = hub_order(cards, cn, count, gname1, ord)
1109 if n1 > 0 {
1110 let sl1: *u8 = sys_mmap(64); hub_slugify(gname1, sl1)
1111 w(1, "<a class='chip' href='" as *u8); wc(1, 35); w(1, "g-" as *u8); w(1, sl1); w(1, "' data-g='" as *u8); w(1, sl1); w(1, "'>" as *u8); w(1, gname1); w(1, "<small>" as *u8); wn(1, n1); w(1, "</small></a>" as *u8)
1112 }
1113 g1 = g1 + 1
1114 }
1115 w2(1, "</div>\n<div class='chips' id='kchips'>" as *u8)
1116 // kind chips from the kinds actually present
1117 let kinds: *u8 = sys_mmap(8 * 40)
1118 var nk: i64 = 0
1119 var ki: i64 = 0
1120 let dk1: *u8 = sys_mmap(40)
1121 while ki < count {
1122 hub_disp_kind(cards, cn, ki, dk1)
1123 var seen: i64 = 0
1124 var s1: i64 = 0
1125 while s1 < nk { if streq((kinds as i64 + s1 * 40) as *u8, dk1) == 1 { seen = 1; s1 = nk } else { s1 = s1 + 1 } }
1126 if seen == 0 { if nk < 8 { hub_copyn((kinds as i64 + nk * 40) as *u8, dk1, 40); nk = nk + 1 } }
1127 ki = ki + 1
1128 }
1129 var kj: i64 = 0
1130 while kj < nk {
1131 let kname: *u8 = (kinds as i64 + kj * 40) as *u8
1132 var kc: i64 = 0
1133 var kx: i64 = 0
1134 while kx < count { hub_disp_kind(cards, cn, kx, dk1); if streq(dk1, kname) == 1 { kc = kc + 1 } kx = kx + 1 }
1135 w(1, "<a class='chip' href='?k=" as *u8); w(1, kname); w(1, "' data-k='" as *u8); w(1, kname); w(1, "'>" as *u8); w(1, kname); w(1, "<small>" as *u8); wn(1, kc); w(1, "</small></a>" as *u8)
1136 kj = kj + 1
1137 }
1138 w2(1, "</div>\n<div class='tbrow'><label>Sort <select id='sort'><option value='cov'>coverage</option><option value='exc'>exceeds</option><option value='upd'>recently measured</option><option value='name'>A to Z</option></select></label><p class='srh' id='srh' role='status' aria-live='polite'>" as *u8)
1139 wn(1, count); w2(1, " comparisons</p><a class='rst' id='reset' href='/compare'>reset</a></div></div>\n" as *u8)
1140 // jump nav (no-JS group navigation)
1141 w(1, "<p class='jump'>" as *u8)
1142 var g2: i64 = 0
1143 while g2 < ng {
1144 let gname2: *u8 = (gt as i64 + g2 * HUB_GSLOT) as *u8
1145 let n2: i64 = hub_order(cards, cn, count, gname2, ord)
1146 if n2 > 0 { let sl2: *u8 = sys_mmap(64); hub_slugify(gname2, sl2); w(1, "<a href='" as *u8); wc(1, 35); w(1, "g-" as *u8); w(1, sl2); w(1, "'>" as *u8); w(1, gname2); w(1, " (" as *u8); wn(1, n2); w(1, ")</a>" as *u8) }
1147 g2 = g2 + 1
1148 }
1149 w(1, "</p>\n" as *u8)
1150 // grouped grid
1151 var emitted: i64 = 0
1152 var g3: i64 = 0
1153 while g3 < ng {
1154 let gname3: *u8 = (gt as i64 + g3 * HUB_GSLOT) as *u8
1155 let n3: i64 = hub_order(cards, cn, count, gname3, ord)
1156 if n3 > 0 {
1157 let sl3: *u8 = sys_mmap(64); hub_slugify(gname3, sl3)
1158 w(1, "<section class='grp' id='g-" as *u8); w(1, sl3); w(1, "' data-g='" as *u8); w(1, sl3); w(1, "'><h2>" as *u8); w(1, gname3); w(1, "<span class='cnt'>" as *u8); wn(1, n3); w(1, "</span></h2><div class='grid'>\n" as *u8)
1159 var x: i64 = 0
1160 while x < n3 { hub_card_html(cards, cn, ord[x]); emitted = emitted + 1; x = x + 1 }
1161 w(1, "</div></section>\n" as *u8)
1162 }
1163 g3 = g3 + 1
1164 }
1165 w(1, "<p class='empty' id='empty' hidden>No comparison matches -- clear the search or a chip.</p>\n" as *u8)
1166 w(1, "<p class='foot'>Generated by nx_swcompare_hub v3 from knowledge/compare/registry plus the regen.list union, facts read from each domain's own api.json, groups from knowledge/compare/hub.groups (" as *u8); wn(1, count); w(1, " comparisons in " as *u8); wn(1, ngne); w(1, " groups; partition " as *u8); wn(1, emitted); w(1, " of " as *u8); wn(1, count); w(1, " placed). Self-assembling — a workstream that ships a comparison appends a regen.list line and reruns. Search, filters and sort are progressive enhancement: every comparison renders without JavaScript. Sovereign Nishi stack — TLS 1.3, no third-party web server, no trackers.</p>\n" as *u8)
1167 w(1, "</main>\n" as *u8)
1168 if ftn > 0 { w(1, ftbuf) }
1169 // progressive enhancement: filter / sort / URL state. No '#' and no '!' anywhere in this literal (nx_cc trap);
1170 // apostrophes become double quotes through w2, so JS strings are written single-quoted here.
1171 w2(1, "<script>(function(){var q=document.getElementById('cmpq');var sr=document.getElementById('srh');var em=document.getElementById('empty');var so=document.getElementById('sort');if(q===null){return;}var cards=[].slice.call(document.querySelectorAll('.cu'));var groups=[].slice.call(document.querySelectorAll('.grp'));var gsel='';var ksel='';\n" as *u8)
1172 w2(1, "function setChips(id,val,attr){var el=document.getElementById(id);if(el===null){return;}var cs=el.querySelectorAll('.chip');var j=0;while(j<cs.length){if(cs[j].getAttribute(attr)===val){cs[j].classList.add('on');}else{cs[j].classList.remove('on');}j=j+1;}}\n" as *u8)
1173 w2(1, "function apply(){var v=q.value.trim().toLowerCase();var n=0;var k=0;while(k<cards.length){var c=cards[k];var ok=1;if(v.length>0){var hay=(c.getAttribute('data-t')+' '+c.textContent).toLowerCase();if(hay.indexOf(v)<0){ok=0;}}if(gsel.length>0){if(c.getAttribute('data-g')===gsel){}else{ok=0;}}if(ksel.length>0){if(c.getAttribute('data-k')===ksel){}else{ok=0;}}c.hidden=(ok===0);if(ok===1){n=n+1;}k=k+1;}\n" as *u8)
1174 w2(1, "var g=0;while(g<groups.length){var vis=groups[g].querySelectorAll('.cu:not([hidden])').length;groups[g].hidden=(vis===0);var cnt=groups[g].querySelector('.cnt');if(cnt){cnt.textContent=vis;}g=g+1;}sr.textContent=n+' of '+cards.length+' comparisons';em.hidden=(n>0);\n" as *u8)
1175 w2(1, "var u=new URLSearchParams();if(v.length>0){u.set('q',v);}if(gsel.length>0){u.set('g',gsel);}if(ksel.length>0){u.set('k',ksel);}if(so.value==='cov'){}else{u.set('s',so.value);}var qs=u.toString();var url=location.pathname;if(qs.length>0){url=url+'?'+qs;}if(window.history&&window.history.replaceState){window.history.replaceState(null,'',url);}}\n" as *u8)
1176 w2(1, "function sortAll(){var key=so.value;var g=0;while(g<groups.length){var grid=groups[g].querySelector('.grid');if(grid===null){g=g+1;continue;}var arr=[].slice.call(grid.querySelectorAll('.cu'));arr.sort(function(a,b){if(key==='name'){var an=a.getAttribute('data-n');var bn=b.getAttribute('data-n');if(an<bn){return -1;}if(an>bn){return 1;}return 0;}var at=(key==='exc')?'data-x':((key==='upd')?'data-u':'data-c');return (Number(b.getAttribute(at))-Number(a.getAttribute(at)));});var i=0;while(i<arr.length){grid.appendChild(arr[i]);i=i+1;}g=g+1;}}\n" as *u8)
1177 w2(1, "function wire(id,attr,fn){var el=document.getElementById(id);if(el===null){return;}var cs=el.querySelectorAll('.chip');var j=0;while(j<cs.length){(function(c){c.addEventListener('click',function(ev){ev.preventDefault();fn(c.getAttribute(attr));});})(cs[j]);j=j+1;}}\n" as *u8)
1178 w2(1, "wire('gchips','data-g',function(v){gsel=(gsel===v)?'':v;setChips('gchips',gsel,'data-g');apply();});wire('kchips','data-k',function(v){ksel=(ksel===v)?'':v;setChips('kchips',ksel,'data-k');apply();});q.addEventListener('input',apply);so.addEventListener('change',function(){sortAll();apply();});\n" as *u8)
1179 w2(1, "function reset(){q.value='';gsel='';ksel='';so.value='cov';setChips('gchips','','data-g');setChips('kchips','','data-k');sortAll();apply();}var rs=document.getElementById('reset');if(rs){rs.addEventListener('click',function(ev){ev.preventDefault();reset();});}\n" as *u8)
1180 w2(1, "document.addEventListener('keydown',function(e){if(e.key==='/'){if(document.activeElement===q){}else{e.preventDefault();q.focus();}}if(e.key==='Escape'){reset();}});\n" as *u8)
1181 w2(1, "var p=new URLSearchParams(location.search);if(p.get('q')){q.value=p.get('q');}if(p.get('g')){gsel=p.get('g');setChips('gchips',gsel,'data-g');}if(p.get('k')){ksel=p.get('k');setChips('kchips',ksel,'data-k');}if(p.get('s')){so.value=p.get('s');}sortAll();apply();})();</script>\n" as *u8)
1182 w(1, "</body></html>\n" as *u8)
1183 sys_exit(0); return 0
1184 }
1185
1186 // console / gate: liar-kill + the v3 HUB CENSUS (regen.list domains -> carded / page / facts / grouped)
1187 w(1, "=== NX-SWCOMPARE-HUB v3 -- generate /compare hub from registry + regen.list union + per-domain facts ===\n" as *u8)
1188 var i2: i64 = 0; var n: i64 = 0
1189 while i2 < sn {
1190 var j2: i64 = i2
1191 while j2 < sn { if sbuf[j2] == (10 as u8) { break } j2 = j2 + 1 }
1192 if sbuf[i2] != (35 as u8) {
1193 if j2 > i2 {
1194 let nf: i64 = split5(sbuf, i2, j2, fbuf)
1195 if nf >= 5 { n = n + 1 }
1196 }
1197 }
1198 i2 = j2 + 1
1199 }
1200 w(1, " registry entries="); wn(1, n); w(1, " cards="); wn(1, count); w(1, " groups="); wn(1, ng); w(1, "\n" as *u8)
1201 var facts: i64 = 0; var grouped: i64 = 0; var covered: i64 = 0; var c3: i64 = 0
1202 // with_coverage NOW SPANS TWO RULERS, so it is published with its decomposition beside it rather
1203 // than as one blended number: a reader who cannot see which ruler produced a count cannot tell a
1204 // real gain from a change in what was being counted. tally+watch+none sums to with_api_json.
1205 var src_tally: i64 = 0; var src_watch: i64 = 0; var src_none: i64 = 0
1206 while c3 < count {
1207 if hub_num(cn, c3, HN_FACTS) == 1 { facts = facts + 1
1208 if hub_num(cn, c3, HN_TSRC) == HT_SRC_TALLY { src_tally = src_tally + 1 }
1209 if hub_num(cn, c3, HN_TSRC) == HT_SRC_WATCH { src_watch = src_watch + 1 }
1210 if hub_num(cn, c3, HN_TSRC) == HT_SRC_NONE { src_none = src_none + 1 }
1211 }
1212 if hub_num(cn, c3, HN_GROUPED) == 1 { grouped = grouped + 1 }
1213 if hub_num(cn, c3, HN_COV) >= 0 { covered = covered + 1 }
1214 c3 = c3 + 1
1215 }
1216 w(1, " facts: with_api_json="); wn(1, facts); w(1, " with_coverage="); wn(1, covered); w(1, " grouped="); wn(1, grouped); w(1, " ungrouped="); wn(1, count - grouped); w(1, " (facts+nofacts and grouped+ungrouped both sum to cards)\n" as *u8)
1217 w(1, " tally_source: tally="); wn(1, src_tally); w(1, " watch="); wn(1, src_watch); w(1, " none="); wn(1, src_none); w(1, " (sums to with_api_json; none = api.json read but NO tally derivable -- an abstention, not a zero)\n" as *u8)
1218 var c4: i64 = 0
1219 while c4 < count {
1220 if hub_num(cn, c4, HN_GROUPED) == 0 { w(1, " UNGROUPED dom=" as *u8); w(1, hub_slot(cards, c4, HO_DOM)); w(1, " title=" as *u8); w(1, hub_slot(cards, c4, HO_TITLE)); w(1, " -> add a knowledge/compare/hub.groups row\n" as *u8) }
1221 c4 = c4 + 1
1222 }
1223 // every regen.list renderable must be carded (the union guarantees it; a miss here is a bug in this file)
1224 let rl2: *u8 = sys_mmap(K_MAGIC_4096)
1225 let rlp2: *u8 = sys_mmap(512)
1226 hub_kcpath(rlp2, "regen.list" as *u8)
1227 let rn2: i64 = c_read(rlp2, rl2, K_MAGIC_4096 - 1)
1228 let dom2: *u8 = sys_mmap(256)
1229 let t2: *u8 = sys_mmap(256)
1230 let tb2: *u8 = sys_mmap(K_MAGIC_4096)
1231 var rdoms: i64 = 0; var renderable: i64 = 0; var carded: i64 = 0; var missing: i64 = 0
1232 var p2: i64 = 0
1233 while p2 < rn2 {
1234 var e2: i64 = p2
1235 while e2 < rn2 { if rl2[e2] == (10 as u8) { break } e2 = e2 + 1 }
1236 if e2 > p2 { if rl2[p2] != (35 as u8) {
1237 var d2: i64 = 0; var q3: i64 = p2
1238 while q3 < e2 { if d2 < 250 { dom2[d2] = rl2[q3]; d2 = d2 + 1 } q3 = q3 + 1 }
1239 dom2[d2] = 0 as u8
1240 rdoms = rdoms + 1
1241 if hub_dom_title(dom2, t2, tb2, K_MAGIC_4096) == 1 {
1242 renderable = renderable + 1
1243 var found: i64 = 0; var c5: i64 = 0
1244 while c5 < count { if streq(hub_slot(cards, c5, HO_DOM), dom2) == 1 { found = 1; c5 = count } else { c5 = c5 + 1 } }
1245 if found == 1 { carded = carded + 1 } else { missing = missing + 1; w(1, " MISSING-CARD dom=" as *u8); w(1, dom2); w(1, "\n" as *u8) }
1246 }
1247 } }
1248 p2 = e2 + 1
1249 }
1250 w(1, " regen.list: domains="); wn(1, rdoms); w(1, " renderable="); wn(1, renderable); w(1, " carded="); wn(1, carded); w(1, " missing="); wn(1, missing); w(1, " (carded+missing=renderable)\n" as *u8)
1251 let liar_reg: i64 = (sn > 0) as i64
1252 let liar_n: i64 = (n >= 1) as i64
1253 let liar_card: i64 = (missing == 0) as i64
1254 w(1, " LIAR-KILL: registry-loaded="); wn(1, liar_reg); w(1, " has-entries="); wn(1, liar_n); w(1, " every-renderable-carded="); wn(1, liar_card); w(1, "\n" as *u8)
1255 let ok: i64 = liar_reg & liar_n & liar_card
1256 w(1, "NX-SWCOMPARE-HUB entries="); wn(1, n); w(1, " cards="); wn(1, count); w(1, " verdict=")
1257 if ok == 1 { w(1, "OK -- run with 'html' to emit the hub page\n" as *u8); sys_exit(0); return 0 }
1258 w(1, "RED (registry missing or empty, or a renderable domain has no card)\n" as *u8); sys_exit(1); return 1
1259}