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}