nx_dashboard.nx source
↩ module page · 195 lines · 7242 B
1// nx_dashboard.nx -- end-to-end batch-audit dashboard renderer.
2//
3// The capstone tying the sovereign-log roadmap into one consumable
4// artifact: takes a CBOR span buffer (any source), extracts every
5// BATCH_CLOSE rollup via nx_trace_query, renders an HTML page via
6// nx_html_emit, writes to fd.
7//
8// Per the user's "live fire testing + see how its testing,
9// innovating, improving" directive: this is what produces the page
10// they actually look at. Pure NishiLang from binary up; no
11// SQLite / Python / template engine.
12//
13// Caller flow:
14// 1. Compile nx_dashboard via nxc2 -> nx_dashboard.elf
15// 2. Pipe a CBOR span stream into stdin (or load from log file)
16// 3. Run nx_dashboard --fd=1 > batch_audit.html
17// 4. Open batch_audit.html in any browser
18//
19// The smoke test below constructs a synthetic 2-batch stream in
20// memory so we can exercise the end-to-end render without a live
21// batch. Production usage swaps the in-memory stream for an
22// actual log-bytes read.
23//
24// genealogy_id: cqrs_projection + grafana_dashboard_pattern
25// lineage_id: sovereign_substrate_dashboard_v1
26
27// nx_safety_envelope:
28// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
29// sil_target: SIL1
30// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
31// verdict: NOT_YET_EVALUATED
32
33import "nx_syscalls.nx"
34import "nx_runtime.nx"
35import "nx_tier.nx"
36import "nx_cbor.nx"
37import "nx_trace_emit.nx"
38import "nx_trace_query.nx"
39import "nx_html_emit.nx"
40import "nx_batch_audit_sidecar.nx"
41
42// ===== Map utilization band -> HTML badge class ===================
43//
44// SEVERE_BIAS or BROKEN -> red badge. MILD_BIAS or DRIFT -> yellow.
45// UNIFORM / GOOD -> green. EMPTY / UNKNOWN -> neutral.
46
47func _dash_util_badge_kind(util_band: nx_int) -> nx_int {
48 if util_band == NX_UTIL_UNIFORM { return NX_HTML_BADGE_GOOD }
49 if util_band == NX_UTIL_MILD_BIAS { return NX_HTML_BADGE_DRIFT }
50 if util_band == NX_UTIL_SEVERE_BIAS { return NX_HTML_BADGE_BROKEN }
51 return NX_HTML_BADGE_NEUTRAL
52}
53
54func _dash_util_badge_label(util_band: nx_int) -> *u8 {
55 if util_band == NX_UTIL_UNIFORM { return "UNIFORM" as *u8 }
56 if util_band == NX_UTIL_MILD_BIAS { return "MILD_BIAS" as *u8 }
57 if util_band == NX_UTIL_SEVERE_BIAS { return "SEVERE_BIAS" as *u8 }
58 return "EMPTY" as *u8
59}
60
61// ===== Lookup an attr value in a summary slot =====================
62//
63// summary_slot is the [trace, n_attrs, k0, v0, k1, v1, ...] layout
64// from nx_tq_extract_batch_summaries. Returns the value matching
65// key_hash, or default_val if absent.
66
67func _dash_lookup_attr(slot: *i64, key_hash: nx_int, default_val: nx_int) -> nx_int {
68 let n_attrs: nx_int = slot[1]
69 var i: nx_int = 0
70 while i < n_attrs {
71 if slot[NX_TQ_SUMMARY_HDR_FIELDS + 2 * i] == key_hash {
72 return slot[NX_TQ_SUMMARY_HDR_FIELDS + 2 * i + 1]
73 }
74 i = i + 1
75 }
76 return default_val
77}
78
79// ===== Attribute-key hashes ========================================
80//
81// Must match the hashes nx_batch_audit_sidecar uses when emitting
82// BATCH_CLOSE attrs. Documented here so the dashboard reads what
83// the sidecar wrote, with no string-table round-trip.
84
85const NX_DASH_KEY_N_SCENES: nx_int = 0x5C01
86const NX_DASH_KEY_N_VIOLATIONS: nx_int = 0xCC07
87const NX_DASH_KEY_OUTFIT_BAND: nx_int = 0x0FBA
88const NX_DASH_KEY_OUTFIT_DISTINCT: nx_int = 0xFBD1
89const NX_DASH_KEY_POSE_BAND: nx_int = 0xB05E
90const NX_DASH_KEY_POSE_DISTINCT: nx_int = 0xBD15
91
92// ===== Render one batch row ========================================
93
94func nx_dashboard_render_row(fd: nx_int, slot: *i64) -> nx_int {
95 let trace_id: nx_int = slot[0]
96 let n_scenes: nx_int = _dash_lookup_attr(slot, NX_DASH_KEY_N_SCENES, 0)
97 let n_violations: nx_int = _dash_lookup_attr(slot, NX_DASH_KEY_N_VIOLATIONS, 0)
98 let outfit_band: nx_int = _dash_lookup_attr(slot, NX_DASH_KEY_OUTFIT_BAND, 0)
99 let outfit_distinct: nx_int = _dash_lookup_attr(slot, NX_DASH_KEY_OUTFIT_DISTINCT, 0)
100 let pose_band: nx_int = _dash_lookup_attr(slot, NX_DASH_KEY_POSE_BAND, 0)
101 let pose_distinct: nx_int = _dash_lookup_attr(slot, NX_DASH_KEY_POSE_DISTINCT, 0)
102
103 nx_html_tr_open(fd)
104 nx_html_td_int(fd, trace_id)
105 nx_html_td_int(fd, n_scenes)
106 nx_html_td_int(fd, n_violations)
107 nx_html_td_int(fd, outfit_distinct)
108
109 // Outfit-band cell with badge
110 _html_write_z(fd, "<td>" as *u8)
111 nx_html_badge(fd,
112 _dash_util_badge_kind(outfit_band),
113 _dash_util_badge_label(outfit_band))
114 _html_write_z(fd, "</td>" as *u8)
115
116 nx_html_td_int(fd, pose_distinct)
117 _html_write_z(fd, "<td>" as *u8)
118 nx_html_badge(fd,
119 _dash_util_badge_kind(pose_band),
120 _dash_util_badge_label(pose_band))
121 _html_write_z(fd, "</td>" as *u8)
122
123 nx_html_tr_close(fd)
124 return 0
125}
126
127// ===== Top-level render ============================================
128//
129// Walks the CBOR buffer, extracts all BATCH_CLOSE rollups, emits a
130// full HTML page to fd. cap is the max batches we'll render in
131// one page (caller-sized).
132
133func nx_dashboard_render(fd: nx_int, title: *u8,
134 buf: *u8, n_bytes: nx_int,
135 cap: nx_int) -> nx_int {
136 // Extract summaries
137 let summaries_bytes: nx_int = cap * NX_TQ_SUMMARY_STRIDE * 8
138 let summaries: *i64 = (sys_mmap(summaries_bytes)) as *i64
139 let n_sums: nx_int = nx_tq_extract_batch_summaries(buf, n_bytes,
140 summaries, cap)
141
142 // Compute per-page totals
143 let counts: *i64 = (sys_mmap(NX_SPAN_N_KINDS * 8)) as *i64
144 let n_total_spans: nx_int = nx_tq_count_kinds(buf, n_bytes, counts)
145
146 // ===== Header ==============================================
147 nx_html_doc_open(fd, title)
148 nx_html_h1(fd, "Sovereign substrate -- batch audit")
149
150 nx_html_p(fd, "Each row is one BATCH_CLOSE span the substrate emitted. Bands routed via nx_dashboard.nx integration.")
151
152 // ===== Summary stats =======================================
153 nx_html_h2(fd, "Stream stats")
154 nx_html_table_open(fd)
155 nx_html_thead_open(fd)
156 nx_html_th(fd, "metric")
157 nx_html_th(fd, "value")
158 nx_html_thead_close(fd)
159
160 nx_html_tr_open(fd)
161 nx_html_td_text(fd, "total spans")
162 nx_html_td_int(fd, n_total_spans)
163 nx_html_tr_close(fd)
164
165 nx_html_tr_open(fd)
166 nx_html_td_text(fd, "batches found")
167 nx_html_td_int(fd, n_sums)
168 nx_html_tr_close(fd)
169
170 nx_html_table_close(fd)
171
172 // ===== Per-batch table =====================================
173 nx_html_h2(fd, "Batches")
174 nx_html_table_open(fd)
175 nx_html_thead_open(fd)
176 nx_html_th(fd, "trace")
177 nx_html_th(fd, "scenes")
178 nx_html_th(fd, "violations")
179 nx_html_th(fd, "outfit_distinct")
180 nx_html_th(fd, "outfit_band")
181 nx_html_th(fd, "pose_distinct")
182 nx_html_th(fd, "pose_band")
183 nx_html_thead_close(fd)
184
185 var i: nx_int = 0
186 while i < n_sums {
187 let slot: *i64 = ((summaries as nx_int) + i * NX_TQ_SUMMARY_STRIDE * 8) as *i64
188 nx_dashboard_render_row(fd, slot)
189 i = i + 1
190 }
191
192 nx_html_table_close(fd)
193 nx_html_doc_close(fd)
194 return n_sums
195}