code wiki / _hdl_build / nx_evidence_pack.nx
nx_evidence_pack.nx source
↩ module page · 728 lines · 39040 B
1// nx_evidence_pack.nx -- the CONSISTENT EVIDENCE PUBLISHER (operator 2026-07-16: "publish evidence
2// consistent workstream where i can see evidence for all of this work ... clear evidence publishing,
3// consistent screenshots, recordings"). One organ turns a session's evidence artifacts into ONE
4// publishable unit under the /evidence/<run>/ namespace, tied to its workstream:
5// index.html -- plain-english page (title, workstream, verdict, figures+captions, metrics table)
6// api.json -- the machine-readable detail (dashboards speak plain english; machines get JSON)
7// ship.manifest-- nx_content_ship rows (put+verify) so publish = ONE proven sovereign command
8// Manifest lines: title=/workstream=/run=/verdict=/date=/link= meta; `img|<file>|<caption>`;
9// `metric|<name>|<value>|<note>`. CRLF tolerated. Asset refs emitted ABSOLUTE (/evidence/<run>/<file>)
10// per the edge slash-strip law (relative refs = RED in nx_page_verify).
11// usage: nx_evidence_pack <manifest> <outdir> (artifacts already staged in <outdir>)
12// license_tier: ORIGINAL
13import "nx_syscalls.nx"
14const EV_MAGIC_2048: i64 = 2048
15const EV_MAGIC_1000000: i64 = 1000000
16
17const EV_OUT_CAP: i64 = 1048576
18const EV_MAX_IMG: i64 = 24
19const EV_MAX_MET: i64 = 32
20const EV_FILE_MODE: i64 = 420 // 0644
21
22func ev_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
23func ev_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
24
25// append NUL-terminated lit to buf at off
26func ev_cat(buf: *u8, off: i64, s: *u8) -> i64 {
27 var i: i64 = 0
28 while s[i] != (0 as u8) { buf[off+i] = s[i]; i = i + 1 }
29 return off + i
30}
31// append a byte range
32func ev_catn(buf: *u8, off: i64, s: *u8, n: i64) -> i64 {
33 var i: i64 = 0
34 while i < n { buf[off+i] = s[i]; i = i + 1 }
35 return off + n
36}
37// append with minimal JSON escaping (" and \ and newline)
38func ev_catj(buf: *u8, off: i64, s: *u8, n: i64) -> i64 {
39 var o: i64 = off
40 var i: i64 = 0
41 while i < n {
42 let c: i64 = (s[i] as i64) & 255
43 if c == 34 { buf[o]=92 as u8; buf[o+1]=34 as u8; o=o+2 }
44 else { if c == 92 { buf[o]=92 as u8; buf[o+1]=92 as u8; o=o+2 }
45 else { if c == 10 { buf[o]=92 as u8; buf[o+1]=110 as u8; o=o+2 }
46 else { buf[o]=c as u8; o=o+1 } } }
47 i = i + 1
48 }
49 return o
50}
51// write decimal v at buf[o]; returns digit count
52func itoa_sb(buf: *u8, o: i64, v: i64) -> i64 {
53 if v == 0 { buf[o] = 48 as u8; return 1 }
54 let t: *u8 = sys_mmap(24 as nx_size)
55 var m: i64 = v
56 var k: i64 = 0
57 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
58 var j: i64 = 0
59 while j < k { buf[o+j] = t[k-1-j]; j = j + 1 }
60 return k
61}
62func ev_write_file(path: *u8, buf: *u8, n: i64) -> i64 {
63 let fd: i64 = sys_openat_wr(path, EV_FILE_MODE)
64 if fd < 0 { return 0 - 1 }
65 var off: i64 = 0
66 while off < n {
67 let wr: i64 = sys_write(fd, ((buf as i64) + off) as *u8, n - off)
68 if wr <= 0 { sys_close(fd); return 0 - 1 }
69 off = off + wr
70 }
71 sys_close(fd)
72 return n
73}
74
75// per-line manifest fields (offsets into the manifest buffer + lengths)
76struct EvMeta {
77 title_off: i64, title_len: i64,
78 ws_off: i64, ws_len: i64,
79 run_off: i64, run_len: i64,
80 verd_off: i64, verd_len: i64,
81 date_off: i64, date_len: i64,
82 link_off: i64, link_len: i64,
83 prod_off: i64, prod_len: i64,
84}
85
86// does line[lo..lo+n) start with prefix p (NUL-terminated)? returns prefix length or -1
87func ev_pfx(src: *u8, lo: i64, n: i64, p: *u8) -> i64 {
88 let pl: i64 = ev_slen(p)
89 if n < pl { return 0 - 1 }
90 var i: i64 = 0
91 while i < pl { if (src[lo+i]&0xff) != (p[i]&0xff) { return 0 - 1 } i = i + 1 }
92 return pl
93}
94// find next '|' from off within [off..end); returns index or end
95func ev_pipe(src: *u8, off: i64, end: i64) -> i64 {
96 var i: i64 = off
97 while i < end { if (src[i] as i64) == 124 { return i } i = i + 1 }
98 return end
99}
100
101func ev_eq(a: *u8, b: *u8) -> i64 {
102 var i: i64 = 0
103 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 }
104 if b[i] == (0 as u8) { return 1 }
105 return 0
106}
107// split line [lo..end) on '|' into up to maxf (off,len) pairs at fld; returns field count
108func ev_split(mf: *u8, lo: i64, end: i64, fld: *i64, maxf: i64) -> i64 {
109 var nf: i64 = 0
110 var s: i64 = lo
111 while s <= end {
112 if nf >= maxf { s = end + 1 }
113 else {
114 let p: i64 = ev_pipe(mf, s, end)
115 fld[nf*2] = s
116 fld[nf*2+1] = p - s
117 nf = nf + 1
118 s = p + 1
119 }
120 }
121 return nf
122}
123// chip markup for a ladder status (plain-english color: DONE green, NEXT amber, QUEUED gray)
124func ev_chip(hb: *u8, o: i64, mf: *u8, off: i64, len: i64) -> i64 {
125 var oo: i64 = o
126 var col: *u8 = "--mut\x00" as *u8
127 if len == 4 { if (mf[off]&0xff) == 68 { col = "--ok\x00" as *u8 } } // DONE
128 if len == 4 { if (mf[off]&0xff) == 78 { col = "--warn\x00" as *u8 } } // NEXT
129 oo = ev_cat(hb, oo, "<span style=\"color:var(" as *u8)
130 oo = ev_cat(hb, oo, col)
131 oo = ev_cat(hb, oo, ");font-weight:600\">" as *u8)
132 oo = ev_catn(hb, oo, ((mf as i64)+off) as *u8, len)
133 oo = ev_cat(hb, oo, "</span>" as *u8)
134 return oo
135}
136
137// INDEX MODE: regenerate the /evidence/ ROOT page + every product page from the registry SSOT
138// (<root>/evidence.reg + <root>/<slug>/ladder.manifest). Repeatable: run after every new run row.
139func ev_index(root: *u8) -> i64 {
140 let lb: *i64 = sys_mmap(16) as *i64
141 let pbuf: *u8 = sys_mmap(EV_MAGIC_2048 as nx_size)
142 var po: i64 = ev_cat(pbuf, 0, root)
143 po = ev_cat(pbuf, po, "/evidence.reg" as *u8)
144 pbuf[po] = 0 as u8
145 let rg: *u8 = sys_read_file(pbuf, lb)
146 if (rg as i64) == 0 { ev_w("cannot read evidence.reg\n" as *u8); return 3 }
147 let rn: i64 = lb[0]
148 // tables: product slug/name/desc (16x6) · run prod/id/date/result (64x8) · surface prod/url/note (32x6)
149 let pr: *i64 = sys_mmap((16*6*8) as nx_size) as *i64
150 var npr: i64 = 0
151 let ru: *i64 = sys_mmap((64*8*8) as nx_size) as *i64
152 var nru: i64 = 0
153 let su: *i64 = sys_mmap((32*6*8) as nx_size) as *i64
154 var nsu: i64 = 0
155 let fld: *i64 = sys_mmap((10*2*8) as nx_size) as *i64
156 var lo: i64 = 0
157 while lo < rn {
158 var le: i64 = lo
159 while le < rn { if (rg[le] as i64) == 10 { le = le + EV_MAGIC_1000000 } else { le = le + 1 } }
160 var lend: i64 = le
161 if le > rn { lend = le - EV_MAGIC_1000000 }
162 var n: i64 = lend - lo
163 if n > 0 { if (rg[lo+n-1] as i64) == 13 { n = n - 1 } }
164 if n > 8 { if (rg[lo] as i64) != 35 {
165 let nf: i64 = ev_split(rg, lo, lo+n, fld, 5)
166 if nf >= 4 {
167 var kind: i64 = 0
168 if fld[1] == 7 { if (rg[fld[0]]&0xff) == 112 { kind = 1 } } // product
169 if fld[1] == 3 { if (rg[fld[0]]&0xff) == 114 { if (rg[fld[0]+1]&0xff) == 117 { kind = 2 } } } // run
170 if fld[1] == 7 { if (rg[fld[0]]&0xff) == 115 { kind = 3 } } // surface
171 if kind == 1 { if npr < 16 {
172 pr[npr*6]=fld[2]; pr[npr*6+1]=fld[3]; pr[npr*6+2]=fld[4]; pr[npr*6+3]=fld[5]; pr[npr*6+4]=fld[6]; pr[npr*6+5]=fld[7]
173 npr = npr + 1
174 } }
175 if kind == 2 { if nru < 64 { if nf >= 5 {
176 ru[nru*8]=fld[2]; ru[nru*8+1]=fld[3]; ru[nru*8+2]=fld[4]; ru[nru*8+3]=fld[5]; ru[nru*8+4]=fld[6]; ru[nru*8+5]=fld[7]; ru[nru*8+6]=fld[8]; ru[nru*8+7]=fld[9]
177 nru = nru + 1
178 } } }
179 if kind == 3 { if nsu < 32 {
180 su[nsu*6]=fld[2]; su[nsu*6+1]=fld[3]; su[nsu*6+2]=fld[4]; su[nsu*6+3]=fld[5]; su[nsu*6+4]=fld[6]; su[nsu*6+5]=fld[7]
181 nsu = nsu + 1
182 } }
183 }
184 } }
185 if le > rn { lo = lend + 1 } else { lo = rn }
186 }
187
188 // ---- ROOT /evidence/index.html ----
189 let hb: *u8 = sys_mmap(EV_OUT_CAP as nx_size)
190 var o: i64 = 0
191 o = ev_cat(hb, o, "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>Nishi Evidence</title><style>:root{--bg:#0b0f14;--ink:#d4dae3;--mut:#8a93a3;--line:#232833;--ok:#3fb950;--warn:#d29922}body{background:var(--bg);color:var(--ink);font:15px/1.55 system-ui,sans-serif;margin:0}main{max-width:1000px;margin:0 auto;padding:32px 20px 64px}h1{font-size:26px;margin:.1em 0}h2{font-size:19px;margin:1.4em 0 .2em}a{color:#6aa3ff}.mut{color:var(--mut)}.card{border:1px solid var(--line);border-radius:10px;padding:14px 18px;margin:14px 0;background:#0e1319}</style></head><body><main><h1>Nishi Evidence</h1><p class=\"mut\">Every product we build, its measured state against the best tool in its class, the LADDER of work to state of the art, and dated runs of visible proof (screenshots, comparisons, recordings). One structure, no loose pages: a claim without a run here does not count.</p>" as *u8)
192 var i: i64 = 0
193 while i < npr {
194 // link the product name ONLY if its ladder page exists (no broken links by construction)
195 po = ev_cat(pbuf, 0, root)
196 po = ev_cat(pbuf, po, "/" as *u8)
197 po = ev_catn(pbuf, po, ((rg as i64)+pr[i*6]) as *u8, pr[i*6+1])
198 po = ev_cat(pbuf, po, "/ladder.manifest" as *u8)
199 pbuf[po] = 0 as u8
200 let lchk: *u8 = sys_read_file(pbuf, lb)
201 var haspage: i64 = 0
202 if (lchk as i64) != 0 { haspage = 1 }
203 o = ev_cat(hb, o, "<div class=\"card\"><h2>" as *u8)
204 if haspage == 1 {
205 o = ev_cat(hb, o, "<a href=\"/evidence/" as *u8)
206 o = ev_catn(hb, o, ((rg as i64)+pr[i*6]) as *u8, pr[i*6+1])
207 o = ev_cat(hb, o, "\">" as *u8)
208 o = ev_catn(hb, o, ((rg as i64)+pr[i*6+2]) as *u8, pr[i*6+3])
209 o = ev_cat(hb, o, "</a>" as *u8)
210 } else {
211 o = ev_catn(hb, o, ((rg as i64)+pr[i*6+2]) as *u8, pr[i*6+3])
212 }
213 o = ev_cat(hb, o, "</h2><p class=\"mut\">" as *u8)
214 o = ev_catn(hb, o, ((rg as i64)+pr[i*6+4]) as *u8, pr[i*6+5])
215 o = ev_cat(hb, o, "</p>" as *u8)
216 // live surfaces on the card (a product without runs still shows its living proof)
217 var sr: i64 = 0
218 while sr < nsu {
219 var samesr: i64 = 0
220 if su[sr*6+1] == pr[i*6+1] {
221 samesr = 1
222 var ksr: i64 = 0
223 while ksr < su[sr*6+1] { if (rg[su[sr*6]+ksr]&0xff) != (rg[pr[i*6]+ksr]&0xff) { samesr = 0; ksr = su[sr*6+1] } else { ksr = ksr + 1 } }
224 }
225 if samesr == 1 {
226 o = ev_cat(hb, o, "<p>Live: <a href=\"" as *u8)
227 o = ev_catn(hb, o, ((rg as i64)+su[sr*6+2]) as *u8, su[sr*6+3])
228 o = ev_cat(hb, o, "\">" as *u8)
229 o = ev_catn(hb, o, ((rg as i64)+su[sr*6+2]) as *u8, su[sr*6+3])
230 o = ev_cat(hb, o, "</a> — <span class=\"mut\">" as *u8)
231 o = ev_catn(hb, o, ((rg as i64)+su[sr*6+4]) as *u8, su[sr*6+5])
232 o = ev_cat(hb, o, "</span></p>" as *u8)
233 }
234 sr = sr + 1
235 }
236 // latest run = the LAST run row whose product matches
237 var last: i64 = 0 - 1
238 var r: i64 = 0
239 while r < nru {
240 if ru[r*8+1] == pr[i*6+1] {
241 var same: i64 = 1
242 var k: i64 = 0
243 while k < ru[r*8+1] { if (rg[ru[r*8]+k]&0xff) != (rg[pr[i*6]+k]&0xff) { same = 0; k = ru[r*8+1] } else { k = k + 1 } }
244 if same == 1 { last = r }
245 }
246 r = r + 1
247 }
248 if last >= 0 {
249 o = ev_cat(hb, o, "<p>Latest: <a href=\"/evidence/" as *u8)
250 o = ev_catn(hb, o, ((rg as i64)+ru[last*8+2]) as *u8, ru[last*8+3])
251 o = ev_cat(hb, o, "\">" as *u8)
252 o = ev_catn(hb, o, ((rg as i64)+ru[last*8+4]) as *u8, ru[last*8+5])
253 o = ev_cat(hb, o, "</a> — " as *u8)
254 o = ev_catn(hb, o, ((rg as i64)+ru[last*8+6]) as *u8, ru[last*8+7])
255 o = ev_cat(hb, o, "</p>" as *u8)
256 }
257 o = ev_cat(hb, o, "</div>" as *u8)
258 i = i + 1
259 }
260 o = ev_cat(hb, o, "</main></body></html>\n" as *u8)
261 po = ev_cat(pbuf, 0, root)
262 po = ev_cat(pbuf, po, "/index.html" as *u8)
263 pbuf[po] = 0 as u8
264 if ev_write_file(pbuf, hb, o) < 0 { ev_w("write root index failed\n" as *u8); return 5 }
265
266 // ---- ship_index.manifest (root put+verify; product rows appended below as pages emit) ----
267 let sb: *u8 = sys_mmap(EV_OUT_CAP as nx_size)
268 var so: i64 = 0
269 so = ev_cat(sb, so, "put|sites/nishifamily/evidence/index.html|" as *u8)
270 so = ev_cat(sb, so, root)
271 so = ev_cat(sb, so, "/index.html\n" as *u8)
272
273 // ---- per-product pages (only when <root>/<slug>/ladder.manifest exists) ----
274 i = 0
275 while i < npr {
276 po = ev_cat(pbuf, 0, root)
277 po = ev_cat(pbuf, po, "/" as *u8)
278 po = ev_catn(pbuf, po, ((rg as i64)+pr[i*6]) as *u8, pr[i*6+1])
279 po = ev_cat(pbuf, po, "/ladder.manifest" as *u8)
280 pbuf[po] = 0 as u8
281 let lm: *u8 = sys_read_file(pbuf, lb)
282 if (lm as i64) != 0 {
283 let ln: i64 = lb[0]
284 var oo: i64 = 0
285 oo = ev_cat(hb, oo, "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>" as *u8)
286 oo = ev_catn(hb, oo, ((rg as i64)+pr[i*6+2]) as *u8, pr[i*6+3])
287 oo = ev_cat(hb, oo, " — evidence</title><style>:root{--bg:#0b0f14;--ink:#d4dae3;--mut:#8a93a3;--line:#232833;--ok:#3fb950;--warn:#d29922}body{background:var(--bg);color:var(--ink);font:15px/1.55 system-ui,sans-serif;margin:0}main{max-width:1000px;margin:0 auto;padding:32px 20px 64px}h1{font-size:24px;margin:.1em 0}h2{font-size:18px;margin:1.5em 0 .4em}a{color:#6aa3ff}.mut{color:var(--mut)}.verdict{background:#161b24;border:1px solid var(--line);border-left:3px solid var(--warn);border-radius:10px;padding:14px 18px;margin:16px 0}table{border-collapse:collapse;width:100%;font-size:14px}th,td{text-align:left;padding:7px 10px;border-bottom:1px solid var(--line);vertical-align:top}th{color:var(--mut)}</style></head><body><main><p class=\"mut\"><a href=\"/evidence\">← all evidence</a></p><h1>" as *u8)
288 oo = ev_catn(hb, oo, ((rg as i64)+pr[i*6+2]) as *u8, pr[i*6+3])
289 oo = ev_cat(hb, oo, "</h1>" as *u8)
290 // ladder meta + rungs
291 var tgt_off: i64 = 0
292 var tgt_len: i64 = 0
293 var llo: i64 = 0
294 // first pass: target=
295 while llo < ln {
296 var lle: i64 = llo
297 while lle < ln { if (lm[lle] as i64) == 10 { lle = lle + EV_MAGIC_1000000 } else { lle = lle + 1 } }
298 var llend: i64 = lle
299 if lle > ln { llend = lle - EV_MAGIC_1000000 }
300 var n2: i64 = llend - llo
301 if n2 > 0 { if (lm[llo+n2-1] as i64) == 13 { n2 = n2 - 1 } }
302 let pt: i64 = ev_pfx(lm, llo, n2, "target=" as *u8)
303 if pt >= 0 { tgt_off = llo+pt; tgt_len = n2-pt }
304 if lle > ln { llo = llend + 1 } else { llo = ln }
305 }
306 if tgt_len > 0 {
307 oo = ev_cat(hb, oo, "<div class=\"verdict\"><b>Target (state of the art):</b> " as *u8)
308 oo = ev_catn(hb, oo, ((lm as i64)+tgt_off) as *u8, tgt_len)
309 oo = ev_cat(hb, oo, "</div>" as *u8)
310 }
311 oo = ev_cat(hb, oo, "<h2>The ladder — how we get there</h2><table><tr><th>status</th><th>rung</th><th>the number it moves</th><th>proof</th></tr>" as *u8)
312 llo = 0
313 while llo < ln {
314 var lle2: i64 = llo
315 while lle2 < ln { if (lm[lle2] as i64) == 10 { lle2 = lle2 + EV_MAGIC_1000000 } else { lle2 = lle2 + 1 } }
316 var llend2: i64 = lle2
317 if lle2 > ln { llend2 = lle2 - EV_MAGIC_1000000 }
318 var n3: i64 = llend2 - llo
319 if n3 > 0 { if (lm[llo+n3-1] as i64) == 13 { n3 = n3 - 1 } }
320 let pr2: i64 = ev_pfx(lm, llo, n3, "rung|" as *u8)
321 if pr2 >= 0 {
322 let nf2: i64 = ev_split(lm, llo+pr2, llo+n3, fld, 4)
323 if nf2 >= 4 {
324 oo = ev_cat(hb, oo, "<tr><td>" as *u8)
325 oo = ev_chip(hb, oo, lm, fld[0], fld[1])
326 oo = ev_cat(hb, oo, "</td><td>" as *u8)
327 oo = ev_catn(hb, oo, ((lm as i64)+fld[2]) as *u8, fld[3])
328 oo = ev_cat(hb, oo, "</td><td>" as *u8)
329 oo = ev_catn(hb, oo, ((lm as i64)+fld[4]) as *u8, fld[5])
330 oo = ev_cat(hb, oo, "</td><td class=\"mut\">" as *u8)
331 oo = ev_catn(hb, oo, ((lm as i64)+fld[6]) as *u8, fld[7])
332 oo = ev_cat(hb, oo, "</td></tr>" as *u8)
333 }
334 }
335 if lle2 > ln { llo = llend2 + 1 } else { llo = ln }
336 }
337 oo = ev_cat(hb, oo, "</table><h2>Runs — the ratchet, dated</h2><ul>" as *u8)
338 var r2: i64 = 0
339 while r2 < nru {
340 var same2: i64 = 0
341 if ru[r2*8+1] == pr[i*6+1] {
342 same2 = 1
343 var k2: i64 = 0
344 while k2 < ru[r2*8+1] { if (rg[ru[r2*8]+k2]&0xff) != (rg[pr[i*6]+k2]&0xff) { same2 = 0; k2 = ru[r2*8+1] } else { k2 = k2 + 1 } }
345 }
346 if same2 == 1 {
347 oo = ev_cat(hb, oo, "<li><a href=\"/evidence/" as *u8)
348 oo = ev_catn(hb, oo, ((rg as i64)+ru[r2*8+2]) as *u8, ru[r2*8+3])
349 oo = ev_cat(hb, oo, "\">" as *u8)
350 oo = ev_catn(hb, oo, ((rg as i64)+ru[r2*8+4]) as *u8, ru[r2*8+5])
351 oo = ev_cat(hb, oo, "</a> — " as *u8)
352 oo = ev_catn(hb, oo, ((rg as i64)+ru[r2*8+6]) as *u8, ru[r2*8+7])
353 oo = ev_cat(hb, oo, "</li>" as *u8)
354 }
355 r2 = r2 + 1
356 }
357 oo = ev_cat(hb, oo, "</ul>" as *u8)
358 // surfaces
359 var s3: i64 = 0
360 var anysurf: i64 = 0
361 while s3 < nsu {
362 var same3: i64 = 0
363 if su[s3*6+1] == pr[i*6+1] {
364 same3 = 1
365 var k3: i64 = 0
366 while k3 < su[s3*6+1] { if (rg[su[s3*6]+k3]&0xff) != (rg[pr[i*6]+k3]&0xff) { same3 = 0; k3 = su[s3*6+1] } else { k3 = k3 + 1 } }
367 }
368 if same3 == 1 {
369 if anysurf == 0 { oo = ev_cat(hb, oo, "<h2>Live surfaces</h2><ul>" as *u8); anysurf = 1 }
370 oo = ev_cat(hb, oo, "<li><a href=\"" as *u8)
371 oo = ev_catn(hb, oo, ((rg as i64)+su[s3*6+2]) as *u8, su[s3*6+3])
372 oo = ev_cat(hb, oo, "\">" as *u8)
373 oo = ev_catn(hb, oo, ((rg as i64)+su[s3*6+2]) as *u8, su[s3*6+3])
374 oo = ev_cat(hb, oo, "</a> — <span class=\"mut\">" as *u8)
375 oo = ev_catn(hb, oo, ((rg as i64)+su[s3*6+4]) as *u8, su[s3*6+5])
376 oo = ev_cat(hb, oo, "</span></li>" as *u8)
377 }
378 s3 = s3 + 1
379 }
380 if anysurf == 1 { oo = ev_cat(hb, oo, "</ul>" as *u8) }
381 oo = ev_cat(hb, oo, "</main></body></html>\n" as *u8)
382 po = ev_cat(pbuf, 0, root)
383 po = ev_cat(pbuf, po, "/" as *u8)
384 po = ev_catn(pbuf, po, ((rg as i64)+pr[i*6]) as *u8, pr[i*6+1])
385 po = ev_cat(pbuf, po, "/index.html" as *u8)
386 pbuf[po] = 0 as u8
387 if ev_write_file(pbuf, hb, oo) < 0 { ev_w("write product page failed\n" as *u8); return 5 }
388 // ship rows
389 so = ev_cat(sb, so, "put|sites/nishifamily/evidence/" as *u8)
390 so = ev_catn(sb, so, ((rg as i64)+pr[i*6]) as *u8, pr[i*6+1])
391 so = ev_cat(sb, so, "/index.html|" as *u8)
392 so = ev_cat(sb, so, root)
393 so = ev_cat(sb, so, "/" as *u8)
394 so = ev_catn(sb, so, ((rg as i64)+pr[i*6]) as *u8, pr[i*6+1])
395 so = ev_cat(sb, so, "/index.html\n" as *u8)
396 so = ev_cat(sb, so, "verify|/evidence/" as *u8)
397 so = ev_catn(sb, so, ((rg as i64)+pr[i*6]) as *u8, pr[i*6+1])
398 so = ev_cat(sb, so, "|" as *u8)
399 so = ev_catn(sb, so, ((rg as i64)+pr[i*6+2]) as *u8, pr[i*6+3])
400 so = ev_cat(sb, so, "\n" as *u8)
401 }
402 i = i + 1
403 }
404 so = ev_cat(sb, so, "verify|/evidence|Nishi Evidence\n" as *u8)
405 po = ev_cat(pbuf, 0, root)
406 po = ev_cat(pbuf, po, "/ship_index.manifest" as *u8)
407 pbuf[po] = 0 as u8
408 if ev_write_file(pbuf, sb, so) < 0 { ev_w("write ship_index failed\n" as *u8); return 5 }
409 ev_w("evidence index: root + product pages + ship_index.manifest emitted\n" as *u8)
410 return 0
411}
412
413func main(argc: i64, argv: *i64) -> i64 {
414 if argc < 3 { ev_w("usage: nx_evidence_pack <manifest> <outdir> | nx_evidence_pack index <evidence-root>\n" as *u8); sys_exit(2); return 2 }
415 if ev_eq(argv[1] as *u8, "index\x00" as *u8) == 1 {
416 let rc: i64 = ev_index(argv[2] as *u8)
417 sys_exit(rc)
418 return rc
419 }
420 let lb: *i64 = sys_mmap(16) as *i64
421 let mf: *u8 = sys_read_file(argv[1] as *u8, lb)
422 if (mf as i64) == 0 { ev_w("cannot read manifest\n" as *u8); sys_exit(3); return 3 }
423 let mn: i64 = lb[0]
424 let outdir: *u8 = argv[2] as *u8
425
426 let meta: *EvMeta = sys_mmap(96 as nx_size) as *EvMeta
427 // img/metric tables: offsets+lens (file,cap) x N / (name,val,note) x N
428 let imf: *i64 = sys_mmap((EV_MAX_IMG*4*8) as nx_size) as *i64
429 var nimg: i64 = 0
430 let met: *i64 = sys_mmap((EV_MAX_MET*6*8) as nx_size) as *i64
431 var nmet: i64 = 0
432 // recordings: rec|<frame-prefix>|<count>|<caption> -> a frame-player block + per-frame put rows
433 let rec: *i64 = sys_mmap((EV_MAX_IMG*6*8) as nx_size) as *i64
434 var nrec: i64 = 0
435
436 // ---- parse lines ----
437 var lo: i64 = 0
438 while lo < mn {
439 var le: i64 = lo
440 while le < mn { if (mf[le] as i64) == 10 { le = le + EV_MAGIC_1000000 } else { le = le + 1 } }
441 var lend: i64 = le
442 if le > mn { lend = le - EV_MAGIC_1000000 } // saw newline
443 var n: i64 = lend - lo
444 // strip trailing CR
445 if n > 0 { if (mf[lo+n-1] as i64) == 13 { n = n - 1 } }
446 if n > 0 {
447 var pl: i64 = ev_pfx(mf, lo, n, "title=" as *u8)
448 if pl >= 0 { meta.title_off = lo+pl; meta.title_len = n-pl }
449 pl = ev_pfx(mf, lo, n, "workstream=" as *u8)
450 if pl >= 0 { meta.ws_off = lo+pl; meta.ws_len = n-pl }
451 pl = ev_pfx(mf, lo, n, "run=" as *u8)
452 if pl >= 0 { meta.run_off = lo+pl; meta.run_len = n-pl }
453 pl = ev_pfx(mf, lo, n, "verdict=" as *u8)
454 if pl >= 0 { meta.verd_off = lo+pl; meta.verd_len = n-pl }
455 pl = ev_pfx(mf, lo, n, "date=" as *u8)
456 if pl >= 0 { meta.date_off = lo+pl; meta.date_len = n-pl }
457 pl = ev_pfx(mf, lo, n, "link=" as *u8)
458 if pl >= 0 { meta.link_off = lo+pl; meta.link_len = n-pl }
459 pl = ev_pfx(mf, lo, n, "product=" as *u8)
460 if pl >= 0 { meta.prod_off = lo+pl; meta.prod_len = n-pl }
461 pl = ev_pfx(mf, lo, n, "img|" as *u8)
462 if pl >= 0 { if nimg < EV_MAX_IMG {
463 let f0: i64 = lo + pl
464 let p1: i64 = ev_pipe(mf, f0, lo+n)
465 imf[nimg*4] = f0
466 imf[nimg*4+1] = p1 - f0
467 var c0: i64 = p1
468 if c0 < lo+n { c0 = c0 + 1 }
469 imf[nimg*4+2] = c0
470 imf[nimg*4+3] = lo + n - c0
471 nimg = nimg + 1
472 } }
473 pl = ev_pfx(mf, lo, n, "rec|" as *u8)
474 if pl >= 0 { if nrec < EV_MAX_IMG {
475 let r0: i64 = lo + pl
476 let rp1: i64 = ev_pipe(mf, r0, lo+n)
477 var rc0: i64 = rp1
478 if rc0 < lo+n { rc0 = rc0 + 1 }
479 let rp2: i64 = ev_pipe(mf, rc0, lo+n)
480 var rx0: i64 = rp2
481 if rx0 < lo+n { rx0 = rx0 + 1 }
482 // parse count digits
483 var cnt: i64 = 0
484 var ci2: i64 = rc0
485 while ci2 < rp2 { let cch: i64 = (mf[ci2] as i64)&255; if cch>=48 { if cch<=57 { cnt = cnt*10 + (cch-48) } } ci2 = ci2 + 1 }
486 rec[nrec*6] = r0
487 rec[nrec*6+1] = rp1 - r0
488 rec[nrec*6+2] = cnt
489 rec[nrec*6+3] = rx0
490 rec[nrec*6+4] = lo + n - rx0
491 nrec = nrec + 1
492 } }
493 pl = ev_pfx(mf, lo, n, "metric|" as *u8)
494 if pl >= 0 { if nmet < EV_MAX_MET {
495 let g0: i64 = lo + pl
496 let q1: i64 = ev_pipe(mf, g0, lo+n)
497 var v0: i64 = q1
498 if v0 < lo+n { v0 = v0 + 1 }
499 let q2: i64 = ev_pipe(mf, v0, lo+n)
500 var t0: i64 = q2
501 if t0 < lo+n { t0 = t0 + 1 }
502 met[nmet*6] = g0
503 met[nmet*6+1] = q1 - g0
504 met[nmet*6+2] = v0
505 met[nmet*6+3] = q2 - v0
506 met[nmet*6+4] = t0
507 met[nmet*6+5] = lo + n - t0
508 nmet = nmet + 1
509 } }
510 }
511 if le > mn { lo = lend + 1 } else { lo = mn }
512 }
513 if meta.run_len <= 0 { ev_w("manifest missing run=\n" as *u8); sys_exit(4); return 4 }
514
515 // ---- emit index.html (plain english; fleet dark theme; ABSOLUTE /evidence/<run>/ asset refs) ----
516 let hb: *u8 = sys_mmap(EV_OUT_CAP as nx_size)
517 var o: i64 = 0
518 o = ev_cat(hb, o, "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>" as *u8)
519 o = ev_catn(hb, o, ((mf as i64)+meta.title_off) as *u8, meta.title_len)
520 o = ev_cat(hb, o, "</title><style>:root{--bg:#0b0f14;--ink:#d4dae3;--mut:#8a93a3;--line:#232833;--ok:#3fb950;--warn:#d29922;--bad:#f85149}body{background:var(--bg);color:var(--ink);font:15px/1.55 system-ui,sans-serif;margin:0}main{max-width:1100px;margin:0 auto;padding:32px 20px 64px}h1{font-size:26px;margin:.1em 0}h2{font-size:18px;margin:1.6em 0 .5em}.chip{display:inline-block;border:1px solid var(--line);border-radius:20px;padding:3px 12px;font-size:13px;margin-right:8px;color:var(--mut)}.verdict{background:#161b24;border:1px solid var(--line);border-left:3px solid var(--warn);border-radius:10px;padding:14px 18px;margin:18px 0}figure{margin:26px 0;border:1px solid var(--line);border-radius:10px;padding:10px;background:#0e1319}figure img{max-width:100%;height:auto;display:block;border-radius:6px}figcaption{color:var(--mut);font-size:13px;padding:10px 4px 2px}table{border-collapse:collapse;width:100%;font-size:14px;margin:12px 0}th,td{text-align:left;padding:8px 12px;border-bottom:1px solid var(--line)}th{color:var(--mut)}a{color:#6aa3ff}</style></head><body><main>" as *u8)
521 // breadcrumb up to the product's ladder page (structure, not loose pages)
522 if meta.prod_len > 0 {
523 o = ev_cat(hb, o, "<p><a href=\"/evidence/" as *u8)
524 o = ev_catn(hb, o, ((mf as i64)+meta.prod_off) as *u8, meta.prod_len)
525 o = ev_cat(hb, o, "\">← product evidence + the ladder to state of the art</a></p>" as *u8)
526 }
527 o = ev_cat(hb, o, "<h1>" as *u8)
528 o = ev_catn(hb, o, ((mf as i64)+meta.title_off) as *u8, meta.title_len)
529 o = ev_cat(hb, o, "</h1><p><span class=\"chip\">workstream: " as *u8)
530 o = ev_catn(hb, o, ((mf as i64)+meta.ws_off) as *u8, meta.ws_len)
531 o = ev_cat(hb, o, "</span><span class=\"chip\">run: " as *u8)
532 o = ev_catn(hb, o, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
533 o = ev_cat(hb, o, "</span><span class=\"chip\">" as *u8)
534 o = ev_catn(hb, o, ((mf as i64)+meta.date_off) as *u8, meta.date_len)
535 o = ev_cat(hb, o, "</span></p><div class=\"verdict\"><b>Verdict:</b> " as *u8)
536 o = ev_catn(hb, o, ((mf as i64)+meta.verd_off) as *u8, meta.verd_len)
537 o = ev_cat(hb, o, "</div>" as *u8)
538 var i: i64 = 0
539 while i < nimg {
540 o = ev_cat(hb, o, "<figure><img src=\"/evidence/" as *u8)
541 o = ev_catn(hb, o, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
542 o = ev_cat(hb, o, "/" as *u8)
543 o = ev_catn(hb, o, ((mf as i64)+imf[i*4]) as *u8, imf[i*4+1])
544 o = ev_cat(hb, o, "\" alt=\"evidence\"><figcaption>" as *u8)
545 o = ev_catn(hb, o, ((mf as i64)+imf[i*4+2]) as *u8, imf[i*4+3])
546 // no-JS fallback: an explicit full-size link beside the caption (granular inspection is the
547 // whole point of evidence -- never gate it on script)
548 o = ev_cat(hb, o, " <a href=\"/evidence/" as *u8)
549 o = ev_catn(hb, o, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
550 o = ev_cat(hb, o, "/" as *u8)
551 o = ev_catn(hb, o, ((mf as i64)+imf[i*4]) as *u8, imf[i*4+1])
552 o = ev_cat(hb, o, "\">open full size</a>" as *u8)
553 o = ev_cat(hb, o, "</figcaption></figure>" as *u8)
554 i = i + 1
555 }
556 // RECORDINGS: a frame-player per rec row (prev/play/next over /evidence/<run>/<prefix><i>.png).
557 // Recordings are first-class evidence: motion, not just stills. Self-contained last-mile JS.
558 i = 0
559 while i < nrec {
560 o = ev_cat(hb, o, "<figure><img class=\"rec\" data-p=\"/evidence/" as *u8)
561 o = ev_catn(hb, o, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
562 o = ev_cat(hb, o, "/" as *u8)
563 o = ev_catn(hb, o, ((mf as i64)+rec[i*6]) as *u8, rec[i*6+1])
564 o = ev_cat(hb, o, "\" data-n=\"" as *u8)
565 let t9: *u8 = sys_mmap(24)
566 var m9: i64 = rec[i*6+2]
567 var k9: i64 = 0
568 if m9 == 0 { t9[0]=48 as u8; k9=1 }
569 while m9 > 0 { t9[k9]=(48+(m9%10)) as u8; m9=m9/10; k9=k9+1 }
570 var j9: i64 = k9 - 1
571 while j9 >= 0 { let ob9: *u8 = ((t9 as i64)+j9) as *u8; o = ev_catn(hb, o, ob9, 1); j9 = j9 - 1 }
572 o = ev_cat(hb, o, "\" src=\"/evidence/" as *u8)
573 o = ev_catn(hb, o, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
574 o = ev_cat(hb, o, "/" as *u8)
575 o = ev_catn(hb, o, ((mf as i64)+rec[i*6]) as *u8, rec[i*6+1])
576 o = ev_cat(hb, o, "0.png\" alt=\"recording\"><figcaption>" as *u8)
577 o = ev_catn(hb, o, ((mf as i64)+rec[i*6+3]) as *u8, rec[i*6+4])
578 o = ev_cat(hb, o, " · <button onclick=\"rstep(this,-1)\">◀ prev</button> <button onclick=\"rplay(this)\">▶ play</button> <button onclick=\"rstep(this,1)\">next ▶</button> <span class=\"rpos\">frame 1</span></figcaption></figure>" as *u8)
579 i = i + 1
580 }
581 if nrec > 0 {
582 o = ev_cat(hb, o, "<script>function rimg(b){return b.closest('figure').querySelector('img.rec')}function rset(im,k){var n=+im.dataset.n;k=((k%n)+n)%n;im.dataset.k=k;im.src=im.dataset.p+k+'.png';im.closest('figure').querySelector('.rpos').textContent='frame '+(k+1)}function rstep(b,d){var im=rimg(b);rset(im,(+im.dataset.k||0)+d)}function rplay(b){var im=rimg(b);var n=+im.dataset.n;var c=0;var t=setInterval(function(){rset(im,(+im.dataset.k||0)+1);c++;if(c>=n){clearInterval(t)}},700)}</script>" as *u8)
583 }
584 if nmet > 0 {
585 o = ev_cat(hb, o, "<h2>Measured</h2><table><tr><th>what</th><th>result</th><th>meaning</th></tr>" as *u8)
586 i = 0
587 while i < nmet {
588 o = ev_cat(hb, o, "<tr><td>" as *u8)
589 o = ev_catn(hb, o, ((mf as i64)+met[i*6]) as *u8, met[i*6+1])
590 o = ev_cat(hb, o, "</td><td>" as *u8)
591 o = ev_catn(hb, o, ((mf as i64)+met[i*6+2]) as *u8, met[i*6+3])
592 o = ev_cat(hb, o, "</td><td>" as *u8)
593 o = ev_catn(hb, o, ((mf as i64)+met[i*6+4]) as *u8, met[i*6+5])
594 o = ev_cat(hb, o, "</td></tr>" as *u8)
595 i = i + 1
596 }
597 o = ev_cat(hb, o, "</table>" as *u8)
598 }
599 o = ev_cat(hb, o, "<p>Machine detail: <a href=\"/evidence/" as *u8)
600 o = ev_catn(hb, o, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
601 o = ev_cat(hb, o, "/api.json\">api.json</a>" as *u8)
602 if meta.link_len > 0 {
603 o = ev_cat(hb, o, " · related: <a href=\"" as *u8)
604 o = ev_catn(hb, o, ((mf as i64)+meta.link_off) as *u8, meta.link_len)
605 o = ev_cat(hb, o, "\">" as *u8)
606 o = ev_catn(hb, o, ((mf as i64)+meta.link_off) as *u8, meta.link_len)
607 o = ev_cat(hb, o, "</a>" as *u8)
608 }
609 o = ev_cat(hb, o, "</p></main>" as *u8)
610 // last-mile ZOOM lightbox (self-contained, zero deps): click a figure -> fullscreen pan view at
611 // natural size; click the image -> cycle 100% / 200%; click the backdrop / Esc -> close.
612 o = ev_cat(hb, o, "<script>(function(){var L=[null,'200'];document.querySelectorAll('figure img').forEach(function(im){im.style.cursor='zoom-in';im.addEventListener('click',function(){var ov=document.createElement('div');ov.style.cssText='position:fixed;inset:0;background:#05070a;z-index:9;overflow:auto;cursor:zoom-out';var big=document.createElement('img');big.src=im.src;big.style.cssText='display:block;margin:0 auto;max-width:none';var z=0;big.style.width='auto';big.addEventListener('click',function(e){e.stopPropagation();z=1-z;big.style.width=(z===1)?(big.naturalWidth*2)+'px':'auto'});ov.addEventListener('click',function(){document.body.removeChild(ov)});function esc(e){if(e.key==='Escape'){if(ov.parentNode){document.body.removeChild(ov)}document.removeEventListener('keydown',esc)}}document.addEventListener('keydown',esc);ov.appendChild(big);document.body.appendChild(ov)})})})();</script>" as *u8)
613 o = ev_cat(hb, o, "</body></html>\n" as *u8)
614 let pbuf: *u8 = sys_mmap(EV_MAGIC_2048 as nx_size)
615 var po: i64 = ev_cat(pbuf, 0, outdir)
616 po = ev_cat(pbuf, po, "/index.html" as *u8)
617 pbuf[po] = 0 as u8
618 if ev_write_file(pbuf, hb, o) < 0 { ev_w("write index.html failed\n" as *u8); sys_exit(5); return 5 }
619
620 // ---- emit api.json ----
621 var jo: i64 = 0
622 let jb: *u8 = sys_mmap(EV_OUT_CAP as nx_size)
623 jo = ev_cat(jb, jo, "{\"v\":1,\"run\":\"" as *u8)
624 jo = ev_catj(jb, jo, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
625 jo = ev_cat(jb, jo, "\",\"workstream\":\"" as *u8)
626 jo = ev_catj(jb, jo, ((mf as i64)+meta.ws_off) as *u8, meta.ws_len)
627 jo = ev_cat(jb, jo, "\",\"title\":\"" as *u8)
628 jo = ev_catj(jb, jo, ((mf as i64)+meta.title_off) as *u8, meta.title_len)
629 jo = ev_cat(jb, jo, "\",\"date\":\"" as *u8)
630 jo = ev_catj(jb, jo, ((mf as i64)+meta.date_off) as *u8, meta.date_len)
631 jo = ev_cat(jb, jo, "\",\"verdict\":\"" as *u8)
632 jo = ev_catj(jb, jo, ((mf as i64)+meta.verd_off) as *u8, meta.verd_len)
633 jo = ev_cat(jb, jo, "\",\"artifacts\":[" as *u8)
634 i = 0
635 while i < nimg {
636 if i > 0 { jo = ev_cat(jb, jo, "," as *u8) }
637 jo = ev_cat(jb, jo, "{\"file\":\"" as *u8)
638 jo = ev_catj(jb, jo, ((mf as i64)+imf[i*4]) as *u8, imf[i*4+1])
639 jo = ev_cat(jb, jo, "\",\"caption\":\"" as *u8)
640 jo = ev_catj(jb, jo, ((mf as i64)+imf[i*4+2]) as *u8, imf[i*4+3])
641 jo = ev_cat(jb, jo, "\"}" as *u8)
642 i = i + 1
643 }
644 jo = ev_cat(jb, jo, "],\"metrics\":[" as *u8)
645 i = 0
646 while i < nmet {
647 if i > 0 { jo = ev_cat(jb, jo, "," as *u8) }
648 jo = ev_cat(jb, jo, "{\"name\":\"" as *u8)
649 jo = ev_catj(jb, jo, ((mf as i64)+met[i*6]) as *u8, met[i*6+1])
650 jo = ev_cat(jb, jo, "\",\"value\":\"" as *u8)
651 jo = ev_catj(jb, jo, ((mf as i64)+met[i*6+2]) as *u8, met[i*6+3])
652 jo = ev_cat(jb, jo, "\",\"note\":\"" as *u8)
653 jo = ev_catj(jb, jo, ((mf as i64)+met[i*6+4]) as *u8, met[i*6+5])
654 jo = ev_cat(jb, jo, "\"}" as *u8)
655 i = i + 1
656 }
657 jo = ev_cat(jb, jo, "]}\n" as *u8)
658 po = ev_cat(pbuf, 0, outdir)
659 po = ev_cat(pbuf, po, "/api.json" as *u8)
660 pbuf[po] = 0 as u8
661 if ev_write_file(pbuf, jb, jo) < 0 { ev_w("write api.json failed\n" as *u8); sys_exit(5); return 5 }
662
663 // ---- emit ship.manifest (nx_content_ship rows: put index+api+every artifact, then verify) ----
664 var so: i64 = 0
665 let sb: *u8 = sys_mmap(EV_OUT_CAP as nx_size)
666 // put|sites/nishifamily/evidence/<run>/<file>|<outdir>/<file>
667 var k: i64 = 0
668 while k < nimg + 2 {
669 so = ev_cat(sb, so, "put|sites/nishifamily/evidence/" as *u8)
670 so = ev_catn(sb, so, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
671 so = ev_cat(sb, so, "/" as *u8)
672 if k == 0 { so = ev_cat(sb, so, "index.html" as *u8) }
673 else { if k == 1 { so = ev_cat(sb, so, "api.json" as *u8) }
674 else { so = ev_catn(sb, so, ((mf as i64)+imf[(k-2)*4]) as *u8, imf[(k-2)*4+1]) } }
675 so = ev_cat(sb, so, "|" as *u8)
676 so = ev_cat(sb, so, outdir)
677 so = ev_cat(sb, so, "/" as *u8)
678 if k == 0 { so = ev_cat(sb, so, "index.html" as *u8) }
679 else { if k == 1 { so = ev_cat(sb, so, "api.json" as *u8) }
680 else { so = ev_catn(sb, so, ((mf as i64)+imf[(k-2)*4]) as *u8, imf[(k-2)*4+1]) } }
681 so = ev_cat(sb, so, "\n" as *u8)
682 k = k + 1
683 }
684 // verify against the CLEAN url (no /index.html): the edge 301s the explicit file path (proven
685 // live 2026-07-16 -- the first ship's verify hit 301), and nx_content_ship does not follow redirects.
686 // recording frames: one put row per frame file
687 var rk: i64 = 0
688 while rk < nrec {
689 var fi2: i64 = 0
690 while fi2 < rec[rk*6+2] {
691 so = ev_cat(sb, so, "put|sites/nishifamily/evidence/" as *u8)
692 so = ev_catn(sb, so, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
693 so = ev_cat(sb, so, "/" as *u8)
694 so = ev_catn(sb, so, ((mf as i64)+rec[rk*6]) as *u8, rec[rk*6+1])
695 so = so + itoa_sb(sb, so, fi2)
696 so = ev_cat(sb, so, ".png|" as *u8)
697 so = ev_cat(sb, so, outdir)
698 so = ev_cat(sb, so, "/" as *u8)
699 so = ev_catn(sb, so, ((mf as i64)+rec[rk*6]) as *u8, rec[rk*6+1])
700 so = so + itoa_sb(sb, so, fi2)
701 so = ev_cat(sb, so, ".png\n" as *u8)
702 fi2 = fi2 + 1
703 }
704 rk = rk + 1
705 }
706 so = ev_cat(sb, so, "verify|/evidence/" as *u8)
707 so = ev_catn(sb, so, ((mf as i64)+meta.run_off) as *u8, meta.run_len)
708 so = ev_cat(sb, so, "|" as *u8)
709 so = ev_catn(sb, so, ((mf as i64)+meta.title_off) as *u8, meta.title_len)
710 so = ev_cat(sb, so, "\n" as *u8)
711 po = ev_cat(pbuf, 0, outdir)
712 po = ev_cat(pbuf, po, "/ship.manifest" as *u8)
713 pbuf[po] = 0 as u8
714 if ev_write_file(pbuf, sb, so) < 0 { ev_w("write ship.manifest failed\n" as *u8); sys_exit(5); return 5 }
715
716 ev_w("evidence pack: index.html " as *u8)
717 ev_w("+ api.json + ship.manifest emitted (imgs=" as *u8)
718 let t: *u8 = sys_mmap(24)
719 var m: i64 = nimg
720 var kk: i64 = 0
721 if m == 0 { t[0]=48 as u8; kk=1 }
722 while m > 0 { t[kk]=(48+(m%10)) as u8; m=m/10; kk=kk+1 }
723 var jj: i64 = kk - 1
724 while jj >= 0 { sys_write(1, ((t as i64)+jj) as *u8, 1); jj = jj - 1 }
725 ev_w(")\n" as *u8)
726 sys_exit(0)
727 return 0
728}