nx_pipeline_route.nx source
↩ module page · 332 lines · 15388 B
1// nx_pipeline_route.nx -- V2.0 P-3: /wiki/pipeline HTTP route.
2//
3// Renders NxArtifactStore (V2.0 P-2) as a tabular pipeline view. Operator
4// visits /wiki/pipeline and sees all ~230 substrate artifacts classified
5// by 5-stage pipeline (UNSTRUCTURED..REPEATABLE) with operator-existing
6// 6-field V5 taxonomy (Quadrant + Topic Type + Status + Trust + Competitive).
7//
8// COMPOSES:
9// wiki/nx_artifact_store (V2.0 P-2; data source)
10// nx_html_escape (safe value emission)
11// nx_syscalls (sys_mmap)
12//
13// COMPOSED BY:
14// wiki/nx_wiki_routes.nx (dispatches /wiki/pipeline + /wiki/pipeline/<stage>)
15//
16// V2.0 SCOPE:
17// - GET /wiki/pipeline -> all artifacts; stage histogram + per-stage counts
18// - GET /wiki/pipeline/<stage-name> -> filter by stage (uppercase stage name)
19// - V1 page format conformant HTML (wiki chrome + nishi-* meta)
20// - Bounded by NX_PROUTE_MAX_ROWS_RENDER per page
21//
22// V3+ SCOPE (TODO):
23// - Pagination + sort
24// - Filter combos (stage + kind + trust + competitive)
25// - JSON API at /wiki/pipeline.json for ops scrapers
26// - Auto-refresh on file change (V2.5 inotify hook)
27//
28// Status: V2.0. 2026-05-27.
29
30import "nx_syscalls.nx"
31import "nx_html_escape.nx"
32import "wiki/nx_artifact_store.nx"
33
34// ===== Sealed verdict surface (codes 2970-2979) =================================================
35const NX_PROUTE_OK: i64 = 0
36const NX_PROUTE_BAD_INPUT: i64 = 2970
37const NX_PROUTE_RESP_OVERFLOW: i64 = 2971
38const NX_PROUTE_BAD_STAGE_FILTER: i64 = 2972
39
40// ===== Named constants (M7) =================================================
41const NX_PROUTE_BODY_CAP: i64 = 524288 // 512 KB body
42const NX_PROUTE_MAX_ROWS_RENDER: i64 = 500 // hard cap per page
43const NX_PROUTE_INT_BUF_CAP: i64 = 32
44
45// Sealed wiki chrome (reused; matches dashboard + archive router conventions).
46const NX_PROUTE_HEAD: *u8 =
47 "<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><title>Pipeline | Nishi Wiki</title><meta name=\"nishi-canonical\" content=\"https://nishifamily.com/wiki/pipeline\"><meta name=\"nishi-title\" content=\"Pipeline\"><meta name=\"nishi-summary\" content=\"Substrate self-audit dashboard. All artifacts classified by 5-stage pipeline.\"><meta name=\"nishi-tags\" content=\"wiki,pipeline,dashboard,audit\"><meta name=\"nishi-last-modified\" content=\"2026-05-27T00:00:00Z\"><meta name=\"nishi-content-hash\" content=\"0000000000000000\"><meta name=\"nishi-page-version\" content=\"1.0.0\"><meta name=\"nishi-license\" content=\"nishi-substrate-public\"><meta name=\"nishi-author\" content=\"elderwesto\"></head><body><header><a href=\"/wiki/\">Wiki</a><a href=\"/wiki/dashboard\">Dashboard</a><a href=\"/wiki/search\">Search</a><a href=\"/wiki/pipeline\">Pipeline</a><a href=\"/wiki/archive\">Archive</a><a href=\"/wiki/admin\">Admin</a></header><main>" as *u8
48const NX_PROUTE_HEAD_N: i64 = 1107
49
50const NX_PROUTE_FOOT: *u8 = "</main></body></html>" as *u8
51const NX_PROUTE_FOOT_N: i64 = 21
52
53// ===== Decimal int helper (M7 + M10) =================================================
54
55func nx_proute_int_to_dec(v: i64, out: *u8, cap: i64) -> i64 {
56 if cap < 2 { return 0 }
57 if v == 0 { out[0] = 0x30 as u8; return 1 }
58 var n: i64 = v
59 var neg: i64 = 0
60 if n < 0 { neg = 1; n = 0 - n }
61 let tmp: *u8 = sys_mmap(NX_PROUTE_INT_BUF_CAP)
62 var k: i64 = 0
63 while n > 0 {
64 if k >= NX_PROUTE_INT_BUF_CAP - 2 { return 0 }
65 tmp[k] = (0x30 + (n % 10)) as u8
66 n = n / 10
67 k = k + 1
68 }
69 var off: i64 = 0
70 if neg == 1 { out[off] = 0x2D as u8; off = off + 1 }
71 var j: i64 = k - 1
72 while j >= 0 { out[off] = tmp[j]; off = off + 1; j = j - 1 }
73 return off
74}
75
76// ===== Output writer (bounded; M5) =================================================
77
78func nx_proute_put_raw(out: *u8, cap: i64, off: i64,
79 src: *u8, n: i64) -> i64 {
80 if off < 0 { return 0 - NX_PROUTE_BAD_INPUT }
81 if off + n > cap { return 0 - NX_PROUTE_RESP_OVERFLOW }
82 var i: i64 = 0
83 while i < n { out[off + i] = src[i]; i = i + 1 }
84 return off + n
85}
86
87func nx_proute_put_z(out: *u8, cap: i64, off: i64, s: *u8) -> i64 {
88 var n: i64 = 0
89 while s[n] != (0 as u8) {
90 if n >= cap { return 0 - NX_PROUTE_RESP_OVERFLOW }
91 n = n + 1
92 }
93 return nx_proute_put_raw(out, cap, off, s, n)
94}
95
96func nx_proute_put_escaped(out: *u8, cap: i64, off: i64,
97 src: *u8, n: i64) -> i64 {
98 if n < 1 { return off }
99 if cap - off < n * 6 + 1 { return 0 - NX_PROUTE_RESP_OVERFLOW }
100 let w: i64 = html_escape((out as i64 + off) as *u8, cap - off, src, n)
101 if w < 0 { return 0 - NX_PROUTE_RESP_OVERFLOW }
102 return off + w
103}
104
105// ===== Sealed stage filter parser =================================================
106//
107// Path-suffix sealed enum: NONE / UNSTRUCTURED / STRUCTURED / MEANINGFUL
108// / ACTIONABLE / REPEATABLE. Returns -1 on bad input.
109
110func nx_proute_parse_stage_filter(suffix: *u8, suffix_n: i64) -> i64 {
111 if suffix_n < 1 { return 0 - 1 } // -1 = no filter
112 // Match longest-first
113 if suffix_n == 12 {
114 if suffix[0] == (0x55 as u8) {
115 // "UNSTRUCTURED"
116 return NX_ART_STAGE_UNSTRUCTURED
117 }
118 }
119 if suffix_n == 11 {
120 if suffix[0] == (0x55 as u8) { return 0 - NX_PROUTE_BAD_STAGE_FILTER }
121 if suffix[0] == (0x4D as u8) { return 0 - NX_PROUTE_BAD_STAGE_FILTER }
122 }
123 if suffix_n == 10 {
124 if suffix[0] == (0x53 as u8) { return NX_ART_STAGE_STRUCTURED }
125 if suffix[0] == (0x4D as u8) { return NX_ART_STAGE_MEANINGFUL }
126 if suffix[0] == (0x41 as u8) { return NX_ART_STAGE_ACTIONABLE }
127 if suffix[0] == (0x52 as u8) { return NX_ART_STAGE_REPEATABLE }
128 }
129 return 0 - NX_PROUTE_BAD_STAGE_FILTER
130}
131
132// ===== Render histogram (count per stage) =================================================
133
134func nx_proute_render_histogram(out: *u8, cap: i64, off: i64,
135 s: *NxArtifactStore) -> i64 {
136 var o: i64 = off
137 o = nx_proute_put_z(out, cap, o,
138 "<table><thead><tr><th>Stage</th><th>Count</th></tr></thead><tbody>" as *u8)
139 if o < 0 { return o }
140
141 var stage: i64 = 0
142 while stage < NX_ART_STAGE_N {
143 o = nx_proute_put_z(out, cap, o, "<tr><td><a href=\"/wiki/pipeline/" as *u8); if o < 0 { return o }
144 o = nx_proute_put_z(out, cap, o, nx_art_stage_name(stage)); if o < 0 { return o }
145 o = nx_proute_put_z(out, cap, o, "\">" as *u8); if o < 0 { return o }
146 o = nx_proute_put_z(out, cap, o, nx_art_stage_name(stage)); if o < 0 { return o }
147 o = nx_proute_put_z(out, cap, o, "</a></td><td>" as *u8); if o < 0 { return o }
148 let buf: *u8 = sys_mmap(NX_PROUTE_INT_BUF_CAP)
149 let n: i64 = nx_proute_int_to_dec(nx_artifact_store_count_by_stage(s, stage), buf, NX_PROUTE_INT_BUF_CAP)
150 o = nx_proute_put_raw(out, cap, o, buf, n); if o < 0 { return o }
151 o = nx_proute_put_z(out, cap, o, "</td></tr>\n" as *u8); if o < 0 { return o }
152 stage = stage + 1
153 }
154 o = nx_proute_put_z(out, cap, o, "</tbody></table>\n" as *u8); if o < 0 { return o }
155 return o
156}
157
158// ===== Render artifact table (filtered or all) =================================================
159
160func nx_proute_render_table(out: *u8, cap: i64, off: i64,
161 s: *NxArtifactStore,
162 stage_filter: i64) -> i64 {
163 var o: i64 = off
164 o = nx_proute_put_z(out, cap, o,
165 "<table><thead><tr><th>Path</th><th>Kind</th><th>Stage</th><th>Lines</th><th>Composes</th></tr></thead><tbody>\n" as *u8)
166 if o < 0 { return o }
167
168 let total: i64 = nx_artifact_store_count(s)
169 var i: i64 = 0
170 var rendered: i64 = 0
171 while i < total {
172 if rendered >= NX_PROUTE_MAX_ROWS_RENDER { i = total + 1 }
173 if i < total {
174 let stage: i64 = nx_artifact_store_stage_at(s, i)
175 if stage_filter < 0 {
176 // No filter; render all
177 o = nx_proute_render_row(out, cap, o, s, i); if o < 0 { return o }
178 rendered = rendered + 1
179 }
180 if stage_filter >= 0 {
181 if stage == stage_filter {
182 o = nx_proute_render_row(out, cap, o, s, i); if o < 0 { return o }
183 rendered = rendered + 1
184 }
185 }
186 i = i + 1
187 }
188 }
189 o = nx_proute_put_z(out, cap, o, "</tbody></table>\n" as *u8); if o < 0 { return o }
190
191 // Footer: rendered count + cap warning
192 o = nx_proute_put_z(out, cap, o, "<p><small>Rendered <b>" as *u8); if o < 0 { return o }
193 let cb: *u8 = sys_mmap(NX_PROUTE_INT_BUF_CAP)
194 let cn: i64 = nx_proute_int_to_dec(rendered, cb, NX_PROUTE_INT_BUF_CAP)
195 o = nx_proute_put_raw(out, cap, o, cb, cn); if o < 0 { return o }
196 o = nx_proute_put_z(out, cap, o, "</b> of <b>" as *u8); if o < 0 { return o }
197 let tb: *u8 = sys_mmap(NX_PROUTE_INT_BUF_CAP)
198 let tn: i64 = nx_proute_int_to_dec(total, tb, NX_PROUTE_INT_BUF_CAP)
199 o = nx_proute_put_raw(out, cap, o, tb, tn); if o < 0 { return o }
200 o = nx_proute_put_z(out, cap, o, "</b> artifacts. Hard cap: 500 rows per page.</small></p>\n" as *u8); if o < 0 { return o }
201 return o
202}
203
204// ===== Render one table row =================================================
205
206func nx_proute_render_row(out: *u8, cap: i64, off: i64,
207 s: *NxArtifactStore, rowid: i64) -> i64 {
208 var o: i64 = off
209
210 // Path
211 let path_ptr: *i64 = (sys_mmap(8)) as *i64
212 let path_n: *i64 = (sys_mmap(8)) as *i64
213 path_ptr[0] = 0; path_n[0] = 0
214 let rc_p: i64 = nx_artifact_store_path_at(s, rowid, path_ptr, path_n)
215 if rc_p != NX_ART_OK { return o } // skip on lookup failure
216
217 o = nx_proute_put_z(out, cap, o, "<tr><td><code>" as *u8); if o < 0 { return o }
218 o = nx_proute_put_escaped(out, cap, o, path_ptr[0] as *u8, path_n[0]); if o < 0 { return o }
219 o = nx_proute_put_z(out, cap, o, "</code></td><td>" as *u8); if o < 0 { return o }
220
221 // Kind
222 o = nx_proute_put_z(out, cap, o, nx_art_kind_name(nx_artifact_store_kind_at(s, rowid))); if o < 0 { return o }
223 o = nx_proute_put_z(out, cap, o, "</td><td>" as *u8); if o < 0 { return o }
224
225 // Stage badge with class
226 let stage: i64 = nx_artifact_store_stage_at(s, rowid)
227 o = nx_proute_put_z(out, cap, o, "<span class=\"stage-" as *u8); if o < 0 { return o }
228 o = nx_proute_put_z(out, cap, o, nx_art_stage_name(stage)); if o < 0 { return o }
229 o = nx_proute_put_z(out, cap, o, "\">" as *u8); if o < 0 { return o }
230 o = nx_proute_put_z(out, cap, o, nx_art_stage_name(stage)); if o < 0 { return o }
231 o = nx_proute_put_z(out, cap, o, "</span></td><td>" as *u8); if o < 0 { return o }
232
233 // Lines + composes (direct field read; if accessor not exposed, walk fields)
234 let lb: *u8 = sys_mmap(NX_PROUTE_INT_BUF_CAP)
235 let ln: i64 = nx_proute_int_to_dec(s.line_count[rowid], lb, NX_PROUTE_INT_BUF_CAP)
236 o = nx_proute_put_raw(out, cap, o, lb, ln); if o < 0 { return o }
237 o = nx_proute_put_z(out, cap, o, "</td><td>" as *u8); if o < 0 { return o }
238 let cb: *u8 = sys_mmap(NX_PROUTE_INT_BUF_CAP)
239 let cn: i64 = nx_proute_int_to_dec(s.composes_count[rowid], cb, NX_PROUTE_INT_BUF_CAP)
240 o = nx_proute_put_raw(out, cap, o, cb, cn); if o < 0 { return o }
241 o = nx_proute_put_z(out, cap, o, "</td></tr>\n" as *u8); if o < 0 { return o }
242
243 return o
244}
245
246// ===== Top-level: dispatch /wiki/pipeline + /wiki/pipeline/<stage> =================================================
247
248func nx_pipeline_route(s: *NxArtifactStore,
249 url_path: *u8, url_path_n: i64,
250 resp_buf: *u8, resp_cap: i64,
251 out_resp_n: *i64) -> i64 {
252 if (s as i64) == 0 { return 0 - NX_PROUTE_BAD_INPUT }
253 if s.valid != 1 { return 0 - NX_PROUTE_BAD_INPUT }
254
255 // Allocate body buffer
256 let body: *u8 = sys_mmap(NX_PROUTE_BODY_CAP)
257 var o: i64 = 0
258 o = nx_proute_put_raw(body, NX_PROUTE_BODY_CAP, o, NX_PROUTE_HEAD, NX_PROUTE_HEAD_N)
259 if o < 0 { return o }
260
261 // Determine if this is /wiki/pipeline (root) or /wiki/pipeline/<stage>
262 var stage_filter: i64 = 0 - 1 // -1 = no filter
263
264 if url_path_n > 15 { // "/wiki/pipeline/" = 15 chars
265 // Has a stage suffix
266 let suffix_ptr: *u8 = (url_path as i64 + 15) as *u8
267 let suffix_n: i64 = url_path_n - 15
268 let parsed: i64 = nx_proute_parse_stage_filter(suffix_ptr, suffix_n)
269 if parsed < 0 {
270 // Bad stage filter; render with no filter + warning
271 o = nx_proute_put_z(body, NX_PROUTE_BODY_CAP, o,
272 "<p><strong>Warning:</strong> unrecognized stage filter; showing all.</p>\n" as *u8)
273 if o < 0 { return o }
274 }
275 if parsed >= 0 { stage_filter = parsed }
276 }
277
278 // Title
279 o = nx_proute_put_z(body, NX_PROUTE_BODY_CAP, o, "<h1>Substrate Pipeline</h1>\n" as *u8); if o < 0 { return o }
280 o = nx_proute_put_z(body, NX_PROUTE_BODY_CAP, o,
281 "<p>Per <a href=\"/wiki/nishi-intelligent-wiki-charter\">NISHI_INTELLIGENT_WIKI_CHARTER</a> 5-stage pipeline. Derived from operator-existing V5 living-docs taxonomy (Quadrant + Topic Type + Status + Trust State + Competitive State + Composes).</p>\n" as *u8)
282 if o < 0 { return o }
283
284 // Histogram
285 o = nx_proute_put_z(body, NX_PROUTE_BODY_CAP, o, "<h2>Stage Distribution</h2>\n" as *u8); if o < 0 { return o }
286 o = nx_proute_render_histogram(body, NX_PROUTE_BODY_CAP, o, s); if o < 0 { return o }
287
288 // Filter heading
289 if stage_filter >= 0 {
290 o = nx_proute_put_z(body, NX_PROUTE_BODY_CAP, o, "<h2>Artifacts in stage <code>" as *u8); if o < 0 { return o }
291 o = nx_proute_put_z(body, NX_PROUTE_BODY_CAP, o, nx_art_stage_name(stage_filter)); if o < 0 { return o }
292 o = nx_proute_put_z(body, NX_PROUTE_BODY_CAP, o, "</code></h2>\n" as *u8); if o < 0 { return o }
293 }
294 if stage_filter < 0 {
295 o = nx_proute_put_z(body, NX_PROUTE_BODY_CAP, o, "<h2>All Artifacts</h2>\n" as *u8); if o < 0 { return o }
296 }
297
298 // Table
299 o = nx_proute_render_table(body, NX_PROUTE_BODY_CAP, o, s, stage_filter); if o < 0 { return o }
300
301 // Footer chrome
302 o = nx_proute_put_raw(body, NX_PROUTE_BODY_CAP, o, NX_PROUTE_FOOT, NX_PROUTE_FOOT_N); if o < 0 { return o }
303
304 let body_len: i64 = o
305
306 // HTTP wrap (same pattern as nx_wiki_dashboard)
307 let hdr: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: " as *u8
308 let hdr_n: i64 = 71
309 let crlf2: *u8 = "\r\n\r\n" as *u8
310
311 let len_buf: *u8 = sys_mmap(NX_PROUTE_INT_BUF_CAP)
312 let ld: i64 = nx_proute_int_to_dec(body_len, len_buf, NX_PROUTE_INT_BUF_CAP)
313 let need: i64 = hdr_n + ld + 4 + body_len
314 if need > resp_cap { return 0 - NX_PROUTE_RESP_OVERFLOW }
315
316 var off: i64 = 0
317 var i: i64 = 0
318 while i < hdr_n { resp_buf[off + i] = hdr[i]; i = i + 1 }
319 off = off + hdr_n
320 i = 0
321 while i < ld { resp_buf[off + i] = len_buf[i]; i = i + 1 }
322 off = off + ld
323 i = 0
324 while i < 4 { resp_buf[off + i] = crlf2[i]; i = i + 1 }
325 off = off + 4
326 i = 0
327 while i < body_len { resp_buf[off + i] = body[i]; i = i + 1 }
328 off = off + body_len
329
330 out_resp_n[0] = off
331 return NX_PROUTE_OK
332}