code wiki / _hdl_build / nx_brief_publish.nx
nx_brief_publish.nx source
↩ module page · 251 lines · 13740 B
1// nx_brief_publish.nx -- ONE CALL publishes a research brief: VALIDATE -> WRITE -> VERIFY, or REFUSE.
2// Closes the last leg of the publish arc (task #13). Before this, publishing a brief was four manual
3// steps -- hand-write HTML, write it to the docroot, hand-edit the index, eyeball it -- and step 3 was
4// routinely skipped, so the index silently disagreed with the library it indexed.
5//
6// nx_brief_publish check <src> validate only, exit 0 ok / 3 REFUSED (dry run)
7// nx_brief_publish publish <src> <slug> validate -> write sites/nishifamily/code/<slug>.html -> read back
8//
9// THE CONTRACT IT ENFORCES (the /code/research bar, made mechanical instead of remembered):
10// R1 exactly one <h1> -- one document, one subject
11// R2 a <time datetime= -- machine-readable date, not prose
12// R3 >=3 'http' source links -- claims carry sources
13// R4 an UNVERIFIED section -- a brief with no declared gaps did not look for them
14// R5 a lineage marker -- 'Forks off' or rel='parent' or 'lineage:' (library, not a pile)
15// R6 balanced <main>/</main> -- catches a truncated write BEFORE it reaches the docroot
16// REFUSAL IS THE POINT: a brief that fails any rule is never written, so the docroot cannot hold a page
17// that violates the standard. Verification is a READ-BACK of the bytes actually on disk (never a status
18// string) -- the ecosystem law is verify by artifact bytes.
19// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
20import "nx_syscalls.nx"
21
22const BP_CAP: i64 = 1048576
23const BP_PATH: i64 = 1024
24const BP_EXIT_USAGE: i64 = 2
25const BP_EXIT_REFUSED: i64 = 3
26const BP_EXIT_IO: i64 = 4
27const BP_MIN_SRC: i64 = 3
28
29func bp_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
30func bp_num(v: i64) -> i64 {
31 let b: *u8=sys_mmap(32); var x: i64=v; var ng: i64=0
32 if x<0 { ng=1; x=0-x }
33 var i: i64=31
34 if x==0 { b[i]=48 as u8; i=i-1 }
35 while x>0 { b[i]=(48+x%10) as u8; x=x/10; i=i-1 }
36 if ng==1 { b[i]=45 as u8; i=i-1 }
37 sys_write(1,(b as i64+i+1) as *u8,31-i); return 0
38}
39func bp_vlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
40// count non-overlapping occurrences of needle in buf[0..n)
41func bp_count(buf: *u8, n: i64, needle: *u8) -> i64 {
42 let m: i64 = bp_vlen(needle)
43 if m==0 { return 0 }
44 var c: i64=0
45 var i: i64=0
46 while i+m<=n {
47 var k: i64=0
48 var hit: i64=1
49 while k<m { if buf[i+k]!=needle[k] { hit=0; k=m } else { k=k+1 } }
50 if hit==1 { c=c+1; i=i+m } else { i=i+1 }
51 }
52 return c
53}
54func bp_has(buf: *u8, n: i64, needle: *u8) -> i64 { if bp_count(buf,n,needle)>0 { return 1 } return 0 }
55
56// validate; fills v[0..5] with per-rule 1/0, returns count of FAILED rules
57func bp_validate(buf: *u8, n: i64, v: *i64) -> i64 {
58 var bad: i64 = 0
59 var i: i64 = 0
60 while i<6 { v[i]=0; i=i+1 }
61 if bp_count(buf,n,"<h1" as *u8)==1 { v[0]=1 } else { bad=bad+1 }
62 if bp_has(buf,n,"<time datetime=" as *u8)==1 { v[1]=1 } else { bad=bad+1 }
63 if bp_count(buf,n,"http" as *u8)>=BP_MIN_SRC { v[2]=1 } else { bad=bad+1 }
64 if bp_has(buf,n,"UNVERIFIED" as *u8)==1 { v[3]=1 } else { bad=bad+1 }
65 var lin: i64 = 0
66 if bp_has(buf,n,"Forks off" as *u8)==1 { lin=1 }
67 if bp_has(buf,n,"rel='parent'" as *u8)==1 { lin=1 }
68 if bp_has(buf,n,"data-rel='parent'" as *u8)==1 { lin=1 }
69 if bp_has(buf,n,"lineage:" as *u8)==1 { lin=1 }
70 if lin==1 { v[4]=1 } else { bad=bad+1 }
71 if bp_count(buf,n,"<main" as *u8)==bp_count(buf,n,"</main>" as *u8) { v[5]=1 } else { bad=bad+1 }
72 return bad
73}
74func bp_rule(name: *u8, ok: i64) -> i64 {
75 bp_puts(" " as *u8); bp_puts(name)
76 if ok==1 { bp_puts(": OK\n" as *u8) } else { bp_puts(": REFUSED\n" as *u8) }
77 return 0
78}
79func bp_report(v: *i64) -> i64 {
80 bp_rule("R1 exactly one <h1>" as *u8, v[0])
81 bp_rule("R2 machine-readable <time datetime=" as *u8, v[1])
82 bp_rule("R3 at least 3 source links" as *u8, v[2])
83 bp_rule("R4 declared UNVERIFIED section" as *u8, v[3])
84 bp_rule("R5 lineage named (Forks off / parent / lineage:)" as *u8, v[4])
85 bp_rule("R6 <main> balanced (not truncated)" as *u8, v[5])
86 return 0
87}
88
89// fork a sovereign sub-ELF with up to 11 args (the nx_aw_push pattern, as used by nx_craft_emit)
90func bp_run(elf: *u8, a1: *u8, a2: *u8, a3: *u8, a4: *u8, a5: *u8, a6: *u8, a7: *u8, a8: *u8,
91 a9: *u8, a10: *u8, a11: *u8) -> i64 {
92 let pid: i64 = sys_fork()
93 if pid == 0 {
94 let av: *i64 = sys_mmap(128) as *i64
95 av[0] = elf as i64
96 var k: i64 = 1
97 if (a1 as i64) != 0 { av[k] = a1 as i64; k = k + 1 }
98 if (a2 as i64) != 0 { av[k] = a2 as i64; k = k + 1 }
99 if (a3 as i64) != 0 { av[k] = a3 as i64; k = k + 1 }
100 if (a4 as i64) != 0 { av[k] = a4 as i64; k = k + 1 }
101 if (a5 as i64) != 0 { av[k] = a5 as i64; k = k + 1 }
102 if (a6 as i64) != 0 { av[k] = a6 as i64; k = k + 1 }
103 if (a7 as i64) != 0 { av[k] = a7 as i64; k = k + 1 }
104 if (a8 as i64) != 0 { av[k] = a8 as i64; k = k + 1 }
105 if (a9 as i64) != 0 { av[k] = a9 as i64; k = k + 1 }
106 if (a10 as i64) != 0 { av[k] = a10 as i64; k = k + 1 }
107 if (a11 as i64) != 0 { av[k] = a11 as i64; k = k + 1 }
108 av[k] = 0
109 let ev: *i64 = sys_mmap(16) as *i64
110 ev[0] = "PATH=/usr/bin:/bin" as *u8 as i64
111 ev[1] = 0
112 sys_execve(elf, av, ev)
113 sys_exit(127)
114 }
115 let st: *i64 = sys_mmap(16) as *i64
116 sys_wait4(pid, st, 0)
117 return (st[0] >> 8) & 0xff
118}
119
120func main(argc: i64, argv: *i64) -> i64 {
121 if argc < 3 {
122 // ★ THE USAGE STRING MUST STATE THE CONTRACT THE TOOL ACTUALLY ENFORCES. This printed
123 // the 3-arg form for weeks while publish REFUSES below argc 9 -- so anyone who trusted it
124 // wrote a page to the docroot and hit UNREGISTERED. A usage string that omits required
125 // arguments is not terse, it is a promise the tool does not keep.
126 bp_puts("usage: nx_brief_publish check <src.html>\n" as *u8)
127 bp_puts(" nx_brief_publish publish <src.html> <slug> <id> <area> <title> <forks_off|-> <scope>\n" as *u8)
128 bp_puts(" publish needs the FULL registration row: the page alone is written, not published.\n" as *u8)
129 bp_puts(" <area> selects the river (/<area>/research) and the ocean section; date is read\n" as *u8)
130 bp_puts(" from the brief's own <time datetime=...> so row and page cannot disagree.\n" as *u8)
131 sys_exit(BP_EXIT_USAGE); return BP_EXIT_USAGE
132 }
133 let verb: *u8 = argv[1] as *u8
134 let src: *u8 = argv[2] as *u8
135 let box: *i64 = sys_mmap(16) as *i64
136 let buf: *u8 = sys_read_file(src, box)
137 if (buf as i64)==0 { bp_puts("NX-BRIEF-PUBLISH IO: cannot read source\n" as *u8); sys_exit(BP_EXIT_IO); return BP_EXIT_IO }
138 let n: i64 = box[0]
139 bp_puts("NX-BRIEF-PUBLISH src bytes=" as *u8); bp_num(n); bp_puts("\n" as *u8)
140 let v: *i64 = sys_mmap(64) as *i64
141 let bad: i64 = bp_validate(buf, n, v)
142 bp_report(v)
143 if bad > 0 {
144 bp_puts("NX-BRIEF-PUBLISH REFUSED failed_rules=" as *u8); bp_num(bad)
145 bp_puts(" -- NOTHING WRITTEN (the docroot never holds a page that fails the standard)\n" as *u8)
146 sys_exit(BP_EXIT_REFUSED); return BP_EXIT_REFUSED
147 }
148 if bp_vlen(verb)==5 { bp_puts("NX-BRIEF-PUBLISH CHECK-OK (dry run, nothing written)\n" as *u8); sys_exit(0); return 0 }
149 if argc < 4 { bp_puts("NX-BRIEF-PUBLISH usage: publish <src> <slug> <id> <area> <title> <forks_off|-> <scope>\n" as *u8); sys_exit(BP_EXIT_USAGE); return BP_EXIT_USAGE }
150 let slug: *u8 = argv[3] as *u8
151 // slug is a bare name -- refuse any path escape by construction
152 var si: i64 = 0
153 while slug[si]!=(0 as u8) {
154 let c: i64 = slug[si] as i64
155 var ok: i64 = 0
156 if c>=97 { if c<=122 { ok=1 } }
157 if c>=48 { if c<=57 { ok=1 } }
158 if c==95 { ok=1 }
159 if ok==0 { bp_puts("NX-BRIEF-PUBLISH REFUSED: slug must be [a-z0-9_] only\n" as *u8); sys_exit(BP_EXIT_REFUSED); return BP_EXIT_REFUSED }
160 si = si + 1
161 }
162 let dst: *u8 = sys_mmap(BP_PATH)
163 var o: i64 = 0
164 let pfx: *u8 = "sites/nishifamily/code/" as *u8
165 var i: i64 = 0
166 while pfx[i]!=(0 as u8) { dst[o]=pfx[i]; o=o+1; i=i+1 }
167 i = 0
168 while slug[i]!=(0 as u8) { dst[o]=slug[i]; o=o+1; i=i+1 }
169 let sfx: *u8 = ".html" as *u8
170 i = 0
171 while sfx[i]!=(0 as u8) { dst[o]=sfx[i]; o=o+1; i=i+1 }
172 dst[o] = 0 as u8
173 let fd: i64 = sys_openat_wr(dst, 420)
174 if fd < 0 { bp_puts("NX-BRIEF-PUBLISH IO: open failed\n" as *u8); sys_exit(BP_EXIT_IO); return BP_EXIT_IO }
175 let wr: i64 = sys_write(fd, buf, n)
176 sys_close(fd)
177 if wr != n { bp_puts("NX-BRIEF-PUBLISH IO: short write\n" as *u8); sys_exit(BP_EXIT_IO); return BP_EXIT_IO }
178 // VERIFY BY ARTIFACT BYTES: read the file back off disk and compare length + full content
179 let box2: *i64 = sys_mmap(16) as *i64
180 let back: *u8 = sys_read_file(dst, box2)
181 if (back as i64)==0 { bp_puts("NX-BRIEF-PUBLISH VERIFY FAILED: cannot read back\n" as *u8); sys_exit(BP_EXIT_IO); return BP_EXIT_IO }
182 if box2[0] != n {
183 bp_puts("NX-BRIEF-PUBLISH VERIFY FAILED: byte mismatch wrote=" as *u8); bp_num(n)
184 bp_puts(" readback=" as *u8); bp_num(box2[0]); bp_puts("\n" as *u8)
185 sys_exit(BP_EXIT_IO); return BP_EXIT_IO
186 }
187 var d: i64 = 0
188 i = 0
189 while i<n { if back[i]!=buf[i] { d=d+1; i=n } else { i=i+1 } }
190 if d>0 { bp_puts("NX-BRIEF-PUBLISH VERIFY FAILED: content differs on read-back\n" as *u8); sys_exit(BP_EXIT_IO); return BP_EXIT_IO }
191 bp_puts("NX-BRIEF-PUBLISH PUBLISHED " as *u8); bp_puts(dst)
192 bp_puts(" bytes=" as *u8); bp_num(n)
193 bp_puts(" VERIFIED-BY-READBACK url=https://nishifamily.com/code/" as *u8); bp_puts(slug); bp_puts("\n" as *u8)
194 // ★REGISTRATION IS PART OF PUBLISHING (debt 1785708667, operator-caught 2026-08-02).
195 // The page is not the library: /wiki/research renders ONLY from the resbrief- plane, so a
196 // brief with no plane row is a perfect, readback-verified, UNREACHABLE page. Three shipped
197 // that way before this was fixed. Now the row and the ocean re-render happen HERE, and a
198 // publish that cannot register REFUSES LOUDLY instead of leaving a half-lane behind.
199 if argc < 9 {
200 bp_puts("NX-BRIEF-PUBLISH UNREGISTERED -- the page is written but NO READER CAN FIND IT.\n" as *u8)
201 bp_puts(" /wiki/research renders only from the resbrief- plane. Re-run with the row:\n" as *u8)
202 bp_puts(" publish <src> <slug> <id> <area> <title> <forks_off|-> <scope>\n" as *u8)
203 sys_exit(BP_EXIT_REFUSED); return BP_EXIT_REFUSED
204 }
205 let rid: *u8 = argv[4] as *u8
206 let rarea: *u8 = argv[5] as *u8
207 let rtitle: *u8 = argv[6] as *u8
208 let rforks: *u8 = argv[7] as *u8
209 let rscope: *u8 = argv[8] as *u8
210 // the DATE is taken from the brief's own <time datetime=...> -- rule R2 already proved it
211 // exists, so the row can never disagree with the page it indexes
212 let dbuf: *u8 = sys_mmap(32)
213 var dj: i64 = 0
214 let dneedle: *u8 = "<time datetime=" as *u8
215 var di: i64 = 0
216 while di < n - 26 {
217 var hit: i64 = 1
218 var dk: i64 = 0
219 while dk < 15 { if buf[di + dk] != dneedle[dk] { hit = 0; dk = 15 } else { dk = dk + 1 } }
220 if hit == 1 {
221 var ds: i64 = di + 15
222 if buf[ds] == (39 as u8) { ds = ds + 1 }
223 if buf[ds] == (34 as u8) { ds = ds + 1 }
224 while dj < 10 { dbuf[dj] = buf[ds + dj]; dj = dj + 1 }
225 di = n
226 } else { di = di + 1 }
227 }
228 dbuf[dj] = 0 as u8
229 if dj < 10 { bp_puts("NX-BRIEF-PUBLISH REFUSED: could not read the brief's own date\n" as *u8); sys_exit(BP_EXIT_REFUSED); return BP_EXIT_REFUSED }
230 let rc1: i64 = bp_run("nx_store_put.elf" as *u8, "knowledge/store/resbrief-" as *u8, "put" as *u8,
231 "brief_publish" as *u8, rid, rarea, slug, rtitle, dbuf,
232 "PUBLISHED" as *u8, rforks, rscope)
233 bp_puts(" register rc=" as *u8); bp_num(rc1); bp_puts("\n" as *u8)
234 if rc1 != 0 { bp_puts("NX-BRIEF-PUBLISH REGISTRATION FAILED -- page is live but UNREACHABLE\n" as *u8); sys_exit(BP_EXIT_IO); return BP_EXIT_IO }
235 // THE RIVER BEFORE THE OCEAN. The area index is the page a reader of that wiki actually
236 // lands on; the ocean is the cross-area surface. Rendering the ocean while leaving the river
237 // hand-maintained is the exact HALF-PLACEMENT that hid three briefs on 2026-08-02 -- written,
238 // registered, rendered into the ocean, and still absent from the index their own readers use.
239 // Publishing is one act or it is not publishing.
240 let rcr: i64 = bp_run("nx_research_river.elf" as *u8, rarea, 0 as *u8, 0 as *u8, 0 as *u8,
241 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8)
242 bp_puts(" river rc=" as *u8); bp_num(rcr); bp_puts("\n" as *u8)
243 if rcr != 0 { bp_puts("NX-BRIEF-PUBLISH RIVER RE-RENDER FAILED -- row banked, area index STALE\n" as *u8); sys_exit(BP_EXIT_IO); return BP_EXIT_IO }
244 let rc2: i64 = bp_run("nx_research_ocean.elf" as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8,
245 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8, 0 as *u8)
246 bp_puts(" ocean rc=" as *u8); bp_num(rc2); bp_puts("\n" as *u8)
247 if rc2 != 0 { bp_puts("NX-BRIEF-PUBLISH OCEAN RE-RENDER FAILED -- row banked, index stale\n" as *u8); sys_exit(BP_EXIT_IO); return BP_EXIT_IO }
248 bp_puts("NX-BRIEF-PUBLISH REGISTERED + RIVER + OCEAN RENDERED -- reachable at /" as *u8)
249 bp_puts(rarea); bp_puts("/research AND /wiki/research\n" as *u8)
250 sys_exit(0); return 0
251}