nx_importcensus.nx source
↩ module page · 171 lines · 9533 B
1// nx_importcensus.nx -- IMPORTER REACH PER SOURCE FILE, as a flat, stamped artifact (2026-08-23, compare CE2).
2//
3// WHY: about half of a /compare board's rows name a LIBRARY -- no top-level main(), no binary by design --
4// and for a library the adoption question is IMPORTER COUNT (nx_catalog's own verdict text), which nothing
5// published per name. The import graph already exists: knowledge/store/ecograph_full, walked from
6// buildroot/runtime by the nx_eco_graph_build pin and judged daily by nx_arch_board. It lives in a seg-store
7// that costs a load to ask, and a board classifies ~40 rows per emit across ~70 domains per regen beat.
8// ONE load here, one flat TSV for every consumer, is the syscalls-per-unit-of-output discipline (rule F).
9//
10// ROWS name<TAB>importers<TAB>importers_nonval<TAB>imports<TAB>first_nonval_importer
11// importers direct fan-in Ca from the graph: every source that imports this one
12// importers_nonval the same minus VALIDATION importers -- sources named *_gate.nx *_test.nx *_bench.nx
13// *_demo.nx *_probe.nx *_smoke.nx *_kat.nx *_negtest.nx (the suffix taxonomy
14// nx_eco_graph_honesty's histe() applies). A lib imported ONLY by its own gate is the
15// BUILT+UNWIRED class (LN8 eqsat, measured 2026-08-23), and THAT is the number a board
16// needs: a gate importing a lib proves the lib is tested, never that it is used.
17// imports fan-out Ce (what this source imports), printed so a reader can tell a leaf from a hub
18// first_nonval_importer one witness name (presence needs one witness), "-" when there is none
19// LAST LINE (the canonical verdict row, anchored BY POSITION -- consumers read the last line, never grep):
20// # asof=<epoch> store=<prefix> nodes=N edges=M rows=N has_nonval=A val_only=B orphan=C
21// nonval_excludes=_gate,_test,_bench,_demo,_probe,_smoke,_kat,_negtest
22// A + B + C == rows is the partition, printed by the organ and checked by its gate.
23// FRESHNESS IS THE CONSUMER'S TO JUDGE from asof=; this organ never overwrites a good artifact with a bad
24// one: an unreadable store writes NOTHING (exit 3), so a stale file stays visibly stale by its own stamp.
25//
26// usage: nx_importcensus [store-prefix] [out-path]
27// defaults knowledge/store/ecograph_full knowledge/status/importers.tsv
28// exit 0 written (tmp + fsync + rename: a reader never sees a half file) | 3 UNOBSERVABLE store | 4 write failed
29// license_tier: ORIGINAL Read-only over the estate except its one artifact. No hw writes (Rule 26).
30import "nx_syscalls.nx"
31import "nx_eco_graph.nx"
32
33const IC_DEC_MAX: i64 = 20 // digits of an i64 plus sign: the width of one rendered number
34const IC_ROW_FIXED: i64 = 4 + 3 * IC_DEC_MAX + 2 // tabs + three numbers + the "-" witness + newline
35const IC_TAIL_FIXED: i64 = 512 // the stamped last line: keys + six numbers + the suffix list
36const IC_NSUFFIX: i64 = 8
37
38func ic_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
39func ic_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
40func ic_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } return p }
41func ic_catn(d: *u8, o: i64, v: i64) -> i64 {
42 var p: i64 = o
43 var m: i64 = v
44 if m < 0 { d[p] = 45 as u8; p = p + 1; m = 0 - m }
45 let t: *u8 = sys_mmap(IC_DEC_MAX + 4)
46 var k: i64 = 0
47 if m == 0 { t[0] = 48 as u8; k = 1 }
48 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
49 var j: i64 = 0
50 while j < k { d[p] = t[k - 1 - j]; p = p + 1; j = j + 1 }
51 sys_munmap(t, IC_DEC_MAX + 4)
52 return p
53}
54func ic_putn(v: i64) -> i64 { let b: *u8 = sys_mmap(IC_DEC_MAX + 4); let n: i64 = ic_catn(b, 0, v); sys_write(1, b, n); sys_munmap(b, IC_DEC_MAX + 4); return 0 }
55
56// the validation-suffix taxonomy, as DATA the gate can read back from the artifact's last line
57func ic_suffix(i: i64) -> *u8 {
58 if i == 0 { return "_gate.nx" as *u8 }
59 if i == 1 { return "_test.nx" as *u8 }
60 if i == 2 { return "_bench.nx" as *u8 }
61 if i == 3 { return "_demo.nx" as *u8 }
62 if i == 4 { return "_probe.nx" as *u8 }
63 if i == 5 { return "_smoke.nx" as *u8 }
64 if i == 6 { return "_kat.nx" as *u8 }
65 return "_negtest.nx" as *u8
66}
67// 1 when the basename ends with a validation suffix (a gate, test, bench, demo, probe, smoke, kat, negtest)
68func ic_is_val(name: *u8) -> i64 {
69 let nl: i64 = ic_slen(name)
70 var i: i64 = 0
71 while i < IC_NSUFFIX {
72 let sfx: *u8 = ic_suffix(i)
73 let sl: i64 = ic_slen(sfx)
74 if nl > sl {
75 var k: i64 = 0
76 var same: i64 = 1
77 while k < sl { if name[nl - sl + k] != sfx[k] { same = 0; k = sl } else { k = k + 1 } }
78 if same == 1 { return 1 }
79 }
80 i = i + 1
81 }
82 return 0
83}
84func ic_name(g: *EcoGraph, idx: i64) -> *u8 { return ((g.arena as i64) + g.node_off[idx]) as *u8 }
85
86func main(argc: i64, argv: *i64) -> i64 {
87 var prefix: *u8 = "knowledge/store/ecograph_full" as *u8
88 var outp: *u8 = "knowledge/status/importers.tsv" as *u8
89 if argc > 1 { prefix = argv[1] as *u8 }
90 if argc > 2 { outp = argv[2] as *u8 }
91 let g: *EcoGraph = eg_load(prefix)
92 if (g as i64) == 0 {
93 ic_puts("NX-IMPORTCENSUS UNOBSERVABLE: store unreadable at " as *u8); ic_puts(prefix)
94 ic_puts(" -- nothing written, the existing artifact (if any) keeps its own asof= stamp\n" as *u8)
95 sys_exit(3); return 3
96 }
97 let n: i64 = g.node_count
98 // BUFFER DERIVED FROM THE DATA IT HOLDS: every row is a name plus a witness name (both from the arena,
99 // so 2 x arena_used bounds them together) plus the fixed row skeleton; then the stamped tail.
100 let cap: i64 = 2 * g.arena_used + n * IC_ROW_FIXED + IC_TAIL_FIXED + ic_slen(prefix)
101 let out: *u8 = sys_mmap(cap)
102 var o: i64 = 0
103 var has_nonval: i64 = 0
104 var val_only: i64 = 0
105 var orphan: i64 = 0
106 var i: i64 = 0
107 while i < n {
108 let ca: i64 = eg_ca(g, i)
109 var nonval: i64 = 0
110 var first: i64 = 0 - 1
111 var j: i64 = g.in_head[i]
112 let je: i64 = g.in_head[i + 1]
113 while j < je {
114 let src: i64 = g.in_list[j]
115 if ic_is_val(ic_name(g, src)) == 0 { nonval = nonval + 1; if first < 0 { first = src } }
116 j = j + 1
117 }
118 if ca == 0 { orphan = orphan + 1 } else { if nonval > 0 { has_nonval = has_nonval + 1 } else { val_only = val_only + 1 } }
119 o = ic_cat(out, o, ic_name(g, i)); out[o] = 9 as u8; o = o + 1
120 o = ic_catn(out, o, ca); out[o] = 9 as u8; o = o + 1
121 o = ic_catn(out, o, nonval); out[o] = 9 as u8; o = o + 1
122 o = ic_catn(out, o, eg_ce(g, i)); out[o] = 9 as u8; o = o + 1
123 if first >= 0 { o = ic_cat(out, o, ic_name(g, first)) } else { out[o] = 45 as u8; o = o + 1 }
124 out[o] = 10 as u8; o = o + 1
125 i = i + 1
126 }
127 let asof: i64 = sys_now_realtime_sec()
128 o = ic_cat(out, o, "# asof=" as *u8); o = ic_catn(out, o, asof)
129 o = ic_cat(out, o, " store=" as *u8); o = ic_cat(out, o, prefix)
130 o = ic_cat(out, o, " nodes=" as *u8); o = ic_catn(out, o, n)
131 o = ic_cat(out, o, " edges=" as *u8); o = ic_catn(out, o, g.edge_count)
132 o = ic_cat(out, o, " rows=" as *u8); o = ic_catn(out, o, n)
133 o = ic_cat(out, o, " has_nonval=" as *u8); o = ic_catn(out, o, has_nonval)
134 o = ic_cat(out, o, " val_only=" as *u8); o = ic_catn(out, o, val_only)
135 o = ic_cat(out, o, " orphan=" as *u8); o = ic_catn(out, o, orphan)
136 o = ic_cat(out, o, " nonval_excludes=" as *u8)
137 var si: i64 = 0
138 while si < IC_NSUFFIX {
139 if si > 0 { out[o] = 44 as u8; o = o + 1 }
140 let sfx: *u8 = ic_suffix(si)
141 let sl: i64 = ic_slen(sfx) - 3 // drop the ".nx" so the list reads as suffixes, not files
142 var c: i64 = 0
143 while c < sl { out[o] = sfx[c]; o = o + 1; c = c + 1 }
144 si = si + 1
145 }
146 out[o] = 10 as u8; o = o + 1
147 // TRUNCATE-WRITE, ATOMIC: tmp beside the target, fsync, rename -- a consumer never reads a half file
148 let tmp: *u8 = sys_mmap(ic_slen(outp) + 8)
149 var to: i64 = ic_cat(tmp, 0, outp); to = ic_cat(tmp, to, ".tmp" as *u8); tmp[to] = 0 as u8
150 let fd: i64 = sys_openat_wr(tmp, MODE_0644)
151 if fd < 0 { ic_puts("NX-IMPORTCENSUS WRITE-FAILED: cannot open " as *u8); ic_puts(tmp); ic_puts("\n" as *u8); sys_exit(4); return 4 }
152 var wrote: i64 = 0
153 while wrote < o {
154 let w: i64 = sys_write(fd, ((out as i64) + wrote) as *u8, o - wrote)
155 if w <= 0 { sys_close(fd); ic_puts("NX-IMPORTCENSUS WRITE-FAILED: short write\n" as *u8); sys_exit(4); return 4 }
156 wrote = wrote + w
157 }
158 sys_fsync(fd)
159 sys_close(fd)
160 if sys_renameat(tmp, outp) != 0 { ic_puts("NX-IMPORTCENSUS WRITE-FAILED: rename\n" as *u8); sys_exit(4); return 4 }
161 // the receipt repeats the partition so a reader of stdout and a reader of the file see one number
162 ic_puts("NX-IMPORTCENSUS rows=" as *u8); ic_putn(n)
163 ic_puts(" has_nonval=" as *u8); ic_putn(has_nonval)
164 ic_puts(" val_only=" as *u8); ic_putn(val_only)
165 ic_puts(" orphan=" as *u8); ic_putn(orphan)
166 ic_puts(" sum=" as *u8); ic_putn(has_nonval + val_only + orphan)
167 ic_puts(" edges=" as *u8); ic_putn(g.edge_count)
168 ic_puts(" asof=" as *u8); ic_putn(asof)
169 ic_puts(" wrote=" as *u8); ic_putn(o); ic_puts(" of=" as *u8); ic_puts(outp); ic_puts("\n" as *u8)
170 return 0
171}