nx_field_seedgen.nx source
↩ module page · 148 lines · 7942 B
1// nx_field_seedgen.nx -- DERIVE A DOMAIN'S SEEDS FROM ITS OWN BOARD (/compare/fieldwatch fleet campaign, 2026-09-05).
2// The first fieldbeat skipped 110 of 111 domains for having no .seeds file. Every one of them already declares its
3// population -- the @cols rivals of <dom>.matrix -- and pins their pages in <dom>.refs. This program writes <dom>.seeds
4// rows of kind named from those two files (decisions in nx_field_seedgen_lib), so the next beat discovers a field for
5// every board without a seat authoring URLs, and it never guesses: an unsourced column is a counted comment, a file a
6// seat curated by hand (no marker line) is refused untouched, and a domain with nothing sourced gets NO file so the
7// beat keeps skipping it cleanly. Discovery seeds (wiki-raw, gh-topic, md-list) remain per-domain curation.
8// Usage: nx_field_seedgen <dom> | --all [--dir <compare-dir>] [--list <regen-list>]
9// Receipt per domain: SEEDGEN dom= class= cols= sourced= unsourced= out=; summary SEEDGEN-OK ... sum= (partition printed)
10// Exits: 0 | 1 a domain was UNWRITABLE | 2 usage | 3 refused (single domain: hand-authored, no matrix, no cols or nothing sourced;
11// --all: the list was unreadable or named no domain)
12// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
13import "nx_syscalls.nx"
14import "nx_field_seedgen_lib.nx"
15
16const SGP_ROOT: *u8 = "/volume1/homes/elderwesto/nishihost/"
17const SGP_DIR_DEFAULT: *u8 = "buildroot/knowledge/compare/"
18const SGP_LIST_DEFAULT: *u8 = "buildroot/knowledge/compare/regen.list"
19const SGP_PATH_CAP: i64 = 1024
20const SGP_EXIT_RED: i64 = 1
21const SGP_EXIT_USAGE: i64 = 2
22const SGP_EXIT_REFUSE: i64 = 3
23const SGP_OUTFD: i64 = 1
24const SGP_ERRFD: i64 = 2
25const SGP_T_COLS: i64 = 0 // totals vector slots
26const SGP_T_SOURCED: i64 = 1
27const SGP_T_UNSOURCED: i64 = 2
28const SGP_T_N: i64 = 3
29
30func sgp_out(s: *u8) -> i64 { fb_puts(SGP_OUTFD, s); return 0 }
31func sgp_err(s: *u8) -> i64 { fb_puts(SGP_ERRFD, s); return 0 }
32func sgp_read(path: *u8, lenout: *i64) -> *u8 {
33 let lp: *i64 = sys_mmap(16) as *i64
34 let b: *u8 = sys_read_file(path, lp)
35 if (b as i64) == 0 { lenout[0] = 0; return 0 as *u8 }
36 lenout[0] = lp[0]
37 return b
38}
39func sgp_path(dst: *u8, dir: *u8, dom: *u8, ext: *u8) -> i64 {
40 var o: i64 = fb_cat(dst, 0, dir, SGP_PATH_CAP)
41 o = fb_cat(dst, o, dom, SGP_PATH_CAP)
42 return fb_cat(dst, o, ext, SGP_PATH_CAP)
43}
44
45// ONE domain: read matrix + refs + any existing seeds, decide, write. Returns the class; tot accumulates cols/sourced/unsourced.
46func sg_one(dir: *u8, dom: *u8, ts: i64, tot: *i64) -> i64 {
47 let mp: *u8 = sys_mmap(SGP_PATH_CAP)
48 let rp: *u8 = sys_mmap(SGP_PATH_CAP)
49 let sp: *u8 = sys_mmap(SGP_PATH_CAP)
50 sgp_path(mp, dir, dom, ".matrix" as *u8)
51 sgp_path(rp, dir, dom, ".refs" as *u8)
52 sgp_path(sp, dir, dom, ".seeds" as *u8)
53 let sl: *i64 = sys_mmap(16) as *i64
54 let sb: *u8 = sgp_read(sp, sl)
55 if (sb as i64) != 0 { if sg_is_derived(sb, sl[0]) == 0 { return SG_CLASS_HAND } }
56 let ml: *i64 = sys_mmap(16) as *i64
57 let mb: *u8 = sgp_read(mp, ml)
58 if (mb as i64) == 0 { return SG_CLASS_NO_MATRIX }
59 let spans: *i64 = sys_mmap(8 * 2 * SG_MAX_COLS) as *i64
60 let ncols: i64 = sg_cols(mb, ml[0], spans)
61 if ncols < 1 { return SG_CLASS_NO_COLS }
62 tot[SGP_T_COLS] = tot[SGP_T_COLS] + ncols
63 let rl: *i64 = sys_mmap(16) as *i64
64 var rb: *u8 = sgp_read(rp, rl)
65 if (rb as i64) == 0 { rb = sys_mmap(1); rl[0] = 0 }
66 // dry pass into a scratch fd is not needed: emit counts as it writes, so write to the file and remove it if nothing sourced
67 let fd: i64 = sys_openat_wr(sp, MODE_0644)
68 if fd < 0 { return SG_CLASS_UNWRITABLE }
69 let counts: *i64 = sys_mmap(16) as *i64
70 sg_emit(fd, dom, ts, mb, spans, ncols, rb, rl[0], counts)
71 sys_close(fd)
72 tot[SGP_T_SOURCED] = tot[SGP_T_SOURCED] + counts[0]
73 tot[SGP_T_UNSOURCED] = tot[SGP_T_UNSOURCED] + counts[1]
74 if counts[0] < 1 { sys_unlinkat(sp); return SG_CLASS_NO_SOURCED }
75 return SG_CLASS_GENERATED
76}
77
78func main(argc: i64, argv: *i64) -> i64 {
79 if argc < 2 { sgp_err("usage: nx_field_seedgen <dom> | --all [--dir <compare-dir>] [--list <regen-list>]\n" as *u8); return SGP_EXIT_USAGE }
80 sys_chdir(SGP_ROOT)
81 sgp_out("[nx_field_seedgen] CWD anchored to " as *u8); sgp_out(SGP_ROOT); sgp_out("\n" as *u8)
82 var dir: *u8 = SGP_DIR_DEFAULT
83 var list: *u8 = SGP_LIST_DEFAULT
84 var all: i64 = 0
85 var dom: *u8 = 0 as *u8
86 var a: i64 = 1
87 while a < argc {
88 let s: *u8 = argv[a] as *u8
89 if fb_streq(s, "--all" as *u8) == 1 { all = 1 } else {
90 if fb_streq(s, "--dir" as *u8) == 1 { if a + 1 < argc { dir = argv[a + 1] as *u8; a = a + 1 } } else {
91 if fb_streq(s, "--list" as *u8) == 1 { if a + 1 < argc { list = argv[a + 1] as *u8; a = a + 1 } } else { dom = s }
92 }
93 }
94 a = a + 1
95 }
96 if all == 0 { if (dom as i64) == 0 { sgp_err("usage: nx_field_seedgen <dom> | --all [--dir <compare-dir>] [--list <regen-list>]\n" as *u8); return SGP_EXIT_USAGE } }
97 let tsb: *i64 = sys_mmap(16) as *i64
98 sys_clock_gettime_real(tsb)
99 let ts: i64 = tsb[0]
100 let tot: *i64 = sys_mmap(8 * SGP_T_N) as *i64
101 let cls: *i64 = sys_mmap(8 * SG_CLASS_N) as *i64
102 var n: i64 = 0
103 var names: *u8 = 0 as *u8
104 if all == 1 {
105 let lp: *i64 = sys_mmap(16) as *i64
106 let lb: *u8 = sgp_read(list, lp)
107 if (lb as i64) == 0 { sgp_err("SEEDGEN-REFUSE list unreadable: " as *u8); sgp_err(list); sgp_err("\n" as *u8); return SGP_EXIT_REFUSE }
108 names = sys_mmap(FB_NAME_CAP * FB_MAX_DOMAINS)
109 let capped: *i64 = sys_mmap(16) as *i64
110 n = fb_list_domains(lb, lp[0], names, capped)
111 if n < 1 { sgp_err("SEEDGEN-REFUSE the list names no domain\n" as *u8); return SGP_EXIT_REFUSE }
112 if capped[0] == 1 { sgp_out("SEEDGEN-CAPPED the list holds more than FB_MAX_DOMAINS rows; domains= is a FLOOR\n" as *u8) }
113 } else { n = 1 }
114 var i: i64 = 0
115 var last: i64 = 0
116 while i < n {
117 var d: *u8 = dom
118 if all == 1 { d = fb_domain_at(names, i) }
119 let before: i64 = tot[SGP_T_SOURCED]
120 let ubefore: i64 = tot[SGP_T_UNSOURCED]
121 let cbefore: i64 = tot[SGP_T_COLS]
122 let c: i64 = sg_one(dir, d, ts, tot)
123 cls[c] = cls[c] + 1
124 last = c
125 sgp_out("SEEDGEN dom=" as *u8); sgp_out(d)
126 sgp_out(" class=" as *u8); sgp_out(sg_class_name(c))
127 sgp_out(" cols=" as *u8); fb_putn(SGP_OUTFD, tot[SGP_T_COLS] - cbefore)
128 sgp_out(" sourced=" as *u8); fb_putn(SGP_OUTFD, tot[SGP_T_SOURCED] - before)
129 sgp_out(" unsourced=" as *u8); fb_putn(SGP_OUTFD, tot[SGP_T_UNSOURCED] - ubefore)
130 sgp_out(" out=" as *u8); sgp_out(dir); sgp_out(d); sgp_out(".seeds\n" as *u8)
131 i = i + 1
132 }
133 sgp_out("SEEDGEN-OK domains=" as *u8); fb_putn(SGP_OUTFD, n)
134 sgp_out(" generated=" as *u8); fb_putn(SGP_OUTFD, cls[SG_CLASS_GENERATED])
135 sgp_out(" hand=" as *u8); fb_putn(SGP_OUTFD, cls[SG_CLASS_HAND])
136 sgp_out(" no_matrix=" as *u8); fb_putn(SGP_OUTFD, cls[SG_CLASS_NO_MATRIX])
137 sgp_out(" no_cols=" as *u8); fb_putn(SGP_OUTFD, cls[SG_CLASS_NO_COLS])
138 sgp_out(" no_sourced=" as *u8); fb_putn(SGP_OUTFD, cls[SG_CLASS_NO_SOURCED])
139 sgp_out(" unwritable=" as *u8); fb_putn(SGP_OUTFD, cls[SG_CLASS_UNWRITABLE])
140 sgp_out(" sum=" as *u8); fb_putn(SGP_OUTFD, cls[SG_CLASS_GENERATED] + cls[SG_CLASS_HAND] + cls[SG_CLASS_NO_MATRIX] + cls[SG_CLASS_NO_COLS] + cls[SG_CLASS_NO_SOURCED] + cls[SG_CLASS_UNWRITABLE])
141 sgp_out(" cols_total=" as *u8); fb_putn(SGP_OUTFD, tot[SGP_T_COLS])
142 sgp_out(" sourced=" as *u8); fb_putn(SGP_OUTFD, tot[SGP_T_SOURCED])
143 sgp_out(" unsourced=" as *u8); fb_putn(SGP_OUTFD, tot[SGP_T_UNSOURCED])
144 sgp_out("\n" as *u8)
145 if cls[SG_CLASS_UNWRITABLE] > 0 { return SGP_EXIT_RED }
146 if all == 0 { if last != SG_CLASS_GENERATED { return SGP_EXIT_REFUSE } }
147 return 0
148}