nx_sketch_buglog.nx source
↩ module page · 338 lines · 11238 B
1// sketch_buglog.nx -- 5W+H+GLP provenance ledger primitive.
2//
3// NOT JUST FOR BUGS. This is the general provenance-emit primitive every
4// tool / sketch / capability uses to declare itself. The "bug log" is just
5// one filtered view (kind=BUG) of a broader ledger that also holds:
6//
7// kind = CAPABILITY -- primitive declares what it does
8// kind = BUG -- something failed; root-cause + fix
9// kind = IMPROVEMENT_OPP -- lineage step exists but unshipped (e.g.,
10// heule_bias_table is on sketch_hll's
11// lineage but the table isn't in code yet)
12// kind = CROSS_REF -- primitive A composes against B
13//
14// G (Genealogy) and L (Lineage) are stored as arrays of SHORT IDs that
15// reference docs/genealogy.md and docs/lineage.md. Lightweight entries,
16// full provenance, joinable for tree views.
17//
18// SCHEMA (10 fields):
19// kind entry type (sealed enum below)
20// W Who primitive name
21// W What workload / scenario / capability description
22// W When ISO timestamp + commit hash
23// W Where sealed axis enum (accuracy/memory/time/capability/usability)
24// W Why root cause (for bugs) / motivation (for capabilities)
25// H How repro + fix (bugs) / usage example (capabilities)
26// G genealogy_ids IDs into docs/genealogy.md
27// L lineage_ids IDs into docs/lineage.md (TODO items here = improvement
28// opportunities a human or AI
29// can pick up directly)
30// P performance JSON object of measured metrics
31//
32// All callers append to _offc/provenance.jsonl (one JSON line per entry).
33// Bug-table is just `grep '"kind":"BUG"' _offc/provenance.jsonl`.
34
35// nx_safety_envelope:
36// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
37// sil_target: SIL1
38// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
39// verdict: NOT_YET_EVALUATED
40
41import "nx_syscalls.nx"
42import "nx_sketch_types.nx"
43const NX_MAGIC_1000000000: i64 = 1000000000
44
45const NX_PROV_KIND_BUG: i64 = 0
46const NX_PROV_KIND_CAPABILITY: i64 = 1
47const NX_PROV_KIND_IMPROVEMENT_OPP: i64 = 2
48const NX_PROV_KIND_CROSS_REF: i64 = 3
49
50const NX_BUG_AXIS_ACCURACY: i64 = 0
51const NX_BUG_AXIS_MEMORY: i64 = 1
52const NX_BUG_AXIS_TIME: i64 = 2
53const NX_BUG_AXIS_CAPABILITY: i64 = 3
54const NX_BUG_AXIS_USABILITY: i64 = 4
55
56const NX_BUG_MAX_GENEALOGY: i64 = 64
57const NX_BUG_MAX_LINEAGE: i64 = 64
58
59struct StrSlice {
60 s: *u8,
61 len: i64,
62}
63
64struct BugLog {
65 kind: i64,
66 who: *u8,
67 who_len: i64,
68 what: *u8,
69 what_len: i64,
70 when_iso: *u8,
71 when_iso_len: i64,
72 where_axis: i64,
73 why: *u8,
74 why_len: i64,
75 how: *u8,
76 how_len: i64,
77 genealogy: *StrSlice,
78 n_genealogy: i64,
79 lineage: *StrSlice,
80 n_lineage: i64,
81 performance: *u8,
82 performance_len: i64,
83}
84
85func nx_buglog_slen(s: *u8) -> i64 {
86 var i: i64 = 0
87 while s[i] != 0 { i = i + 1 }
88 return i
89}
90
91func nx_buglog_alloc() -> *BugLog {
92 let raw: *u8 = sys_mmap(192)
93 let e: *BugLog = raw as *BugLog
94 let empty: *u8 = sys_mmap(1)
95 empty[0] = 0
96 e.kind = NX_PROV_KIND_BUG
97 e.who = empty
98 e.who_len = 0
99 e.what = empty
100 e.what_len = 0
101 e.when_iso = empty
102 e.when_iso_len = 0
103 e.where_axis = NX_BUG_AXIS_ACCURACY
104 e.why = empty
105 e.why_len = 0
106 e.how = empty
107 e.how_len = 0
108 let gen_raw: *u8 = sys_mmap(NX_BUG_MAX_GENEALOGY * 16)
109 e.genealogy = gen_raw as *StrSlice
110 e.n_genealogy = 0
111 let lin_raw: *u8 = sys_mmap(NX_BUG_MAX_LINEAGE * 16)
112 e.lineage = lin_raw as *StrSlice
113 e.n_lineage = 0
114 e.performance = empty
115 e.performance_len = 0
116 return e
117}
118
119func nx_buglog_slice_at(arr: *StrSlice, i: i64) -> *StrSlice {
120 return (arr as i64 + i * 16) as *StrSlice
121}
122
123// === base setters (must precede _z wrappers per forward-decl rule) ===
124
125func nx_buglog_set_kind(e: *BugLog, kind: i64) -> i64 {
126 if kind < 0 { return -1 }
127 if kind > NX_PROV_KIND_CROSS_REF { return -1 }
128 e.kind = kind
129 return 0
130}
131
132func nx_buglog_kind_str(kind: i64) -> *u8 {
133 if kind == NX_PROV_KIND_BUG { return "BUG" }
134 if kind == NX_PROV_KIND_CAPABILITY { return "CAPABILITY" }
135 if kind == NX_PROV_KIND_IMPROVEMENT_OPP { return "IMPROVEMENT_OPP" }
136 if kind == NX_PROV_KIND_CROSS_REF { return "CROSS_REF" }
137 return "UNKNOWN"
138}
139
140func nx_buglog_kind_strlen(kind: i64) -> i64 {
141 if kind == NX_PROV_KIND_BUG { return 3 }
142 if kind == NX_PROV_KIND_CAPABILITY { return 10 }
143 if kind == NX_PROV_KIND_IMPROVEMENT_OPP { return 15 }
144 if kind == NX_PROV_KIND_CROSS_REF { return 9 }
145 return 7
146}
147
148func nx_buglog_set_who(e: *BugLog, s: *u8, len: i64) -> i64 {
149 e.who = s
150 e.who_len = len
151 return 0
152}
153
154func nx_buglog_set_what(e: *BugLog, s: *u8, len: i64) -> i64 {
155 e.what = s
156 e.what_len = len
157 return 0
158}
159
160func nx_buglog_set_when(e: *BugLog, s: *u8, len: i64) -> i64 {
161 e.when_iso = s
162 e.when_iso_len = len
163 return 0
164}
165
166func nx_buglog_set_axis(e: *BugLog, axis: i64) -> i64 {
167 if axis < 0 { return -1 }
168 if axis > NX_BUG_AXIS_USABILITY { return -1 }
169 e.where_axis = axis
170 return 0
171}
172
173func nx_buglog_set_why(e: *BugLog, s: *u8, len: i64) -> i64 {
174 e.why = s
175 e.why_len = len
176 return 0
177}
178
179func nx_buglog_set_how(e: *BugLog, s: *u8, len: i64) -> i64 {
180 e.how = s
181 e.how_len = len
182 return 0
183}
184
185func nx_buglog_set_performance(e: *BugLog, s: *u8, len: i64) -> i64 {
186 e.performance = s
187 e.performance_len = len
188 return 0
189}
190
191func nx_buglog_add_genealogy(e: *BugLog, s: *u8, len: i64) -> i64 {
192 if e.n_genealogy >= NX_BUG_MAX_GENEALOGY { return -1 }
193 let slot: *StrSlice = nx_buglog_slice_at(e.genealogy, e.n_genealogy)
194 slot.s = s
195 slot.len = len
196 e.n_genealogy = e.n_genealogy + 1
197 return 0
198}
199
200func nx_buglog_add_lineage(e: *BugLog, s: *u8, len: i64) -> i64 {
201 if e.n_lineage >= NX_BUG_MAX_LINEAGE { return -1 }
202 let slot: *StrSlice = nx_buglog_slice_at(e.lineage, e.n_lineage)
203 slot.s = s
204 slot.len = len
205 e.n_lineage = e.n_lineage + 1
206 return 0
207}
208
209// === _z wrappers (defined AFTER base setters) ====================
210
211func nx_buglog_set_who_z(e: *BugLog, s: *u8) -> i64 {
212 return nx_buglog_set_who(e, s, nx_buglog_slen(s))
213}
214func nx_buglog_set_what_z(e: *BugLog, s: *u8) -> i64 {
215 return nx_buglog_set_what(e, s, nx_buglog_slen(s))
216}
217func nx_buglog_set_when_z(e: *BugLog, s: *u8) -> i64 {
218 return nx_buglog_set_when(e, s, nx_buglog_slen(s))
219}
220func nx_buglog_set_why_z(e: *BugLog, s: *u8) -> i64 {
221 return nx_buglog_set_why(e, s, nx_buglog_slen(s))
222}
223func nx_buglog_set_how_z(e: *BugLog, s: *u8) -> i64 {
224 return nx_buglog_set_how(e, s, nx_buglog_slen(s))
225}
226func nx_buglog_set_performance_z(e: *BugLog, s: *u8) -> i64 {
227 return nx_buglog_set_performance(e, s, nx_buglog_slen(s))
228}
229func nx_buglog_add_genealogy_z(e: *BugLog, s: *u8) -> i64 {
230 return nx_buglog_add_genealogy(e, s, nx_buglog_slen(s))
231}
232func nx_buglog_add_lineage_z(e: *BugLog, s: *u8) -> i64 {
233 return nx_buglog_add_lineage(e, s, nx_buglog_slen(s))
234}
235
236// === axis -> string ==============================================
237
238func nx_buglog_axis_str(axis: i64) -> *u8 {
239 if axis == NX_BUG_AXIS_ACCURACY { return "accuracy" }
240 if axis == NX_BUG_AXIS_MEMORY { return "memory" }
241 if axis == NX_BUG_AXIS_TIME { return "time" }
242 if axis == NX_BUG_AXIS_CAPABILITY { return "capability" }
243 if axis == NX_BUG_AXIS_USABILITY { return "usability" }
244 return "unknown"
245}
246
247func nx_buglog_axis_strlen(axis: i64) -> i64 {
248 if axis == NX_BUG_AXIS_ACCURACY { return 8 }
249 if axis == NX_BUG_AXIS_MEMORY { return 6 }
250 if axis == NX_BUG_AXIS_TIME { return 4 }
251 if axis == NX_BUG_AXIS_CAPABILITY { return 10 }
252 if axis == NX_BUG_AXIS_USABILITY { return 9 }
253 return 7
254}
255
256// === JSON emit ===================================================
257
258func nx_buglog_w(s: *u8, len: i64) -> i64 {
259 sys_write(1, s, len)
260 return 0
261}
262
263func nx_buglog_emit_field(name: *u8, name_len: i64,
264 value: *u8, value_len: i64,
265 trailing_comma: i64) -> i64 {
266 nx_buglog_w("\"", 1)
267 nx_buglog_w(name, name_len)
268 nx_buglog_w("\":\"", 3)
269 nx_buglog_w(value, value_len)
270 nx_buglog_w("\"", 1)
271 if trailing_comma == 1 { nx_buglog_w(",", 1) }
272 return 0
273}
274
275func nx_buglog_emit_id_array(name: *u8, name_len: i64,
276 arr: *StrSlice, n: i64,
277 trailing_comma: i64) -> i64 {
278 nx_buglog_w("\"", 1)
279 nx_buglog_w(name, name_len)
280 nx_buglog_w("\":[", 3)
281 var i: i64 = 0
282 while i < n {
283 let slot: *StrSlice = nx_buglog_slice_at(arr, i)
284 nx_buglog_w("\"", 1)
285 nx_buglog_w(slot.s, slot.len)
286 nx_buglog_w("\"", 1)
287 if i < n - 1 { nx_buglog_w(",", 1) }
288 i = i + 1
289 }
290 nx_buglog_w("]", 1)
291 if trailing_comma == 1 { nx_buglog_w(",", 1) }
292 return 0
293}
294
295func nx_buglog_emit_raw_json(name: *u8, name_len: i64,
296 value: *u8, value_len: i64,
297 trailing_comma: i64) -> i64 {
298 nx_buglog_w("\"", 1)
299 nx_buglog_w(name, name_len)
300 nx_buglog_w("\":", 2)
301 if value_len == 0 { nx_buglog_w("null", 4) }
302 if value_len > 0 { nx_buglog_w(value, value_len) }
303 if trailing_comma == 1 { nx_buglog_w(",", 1) }
304 return 0
305}
306
307func nx_buglog_emit(e: *BugLog) -> i64 {
308 nx_buglog_w("{", 1)
309 let kind_s: *u8 = nx_buglog_kind_str(e.kind)
310 let kind_l: i64 = nx_buglog_kind_strlen(e.kind)
311 nx_buglog_emit_field("kind", 4, kind_s, kind_l, 1)
312 nx_buglog_emit_field("who", 3, e.who, e.who_len, 1)
313 nx_buglog_emit_field("what", 4, e.what, e.what_len, 1)
314 nx_buglog_emit_field("when", 4, e.when_iso, e.when_iso_len, 1)
315 let axis_s: *u8 = nx_buglog_axis_str(e.where_axis)
316 let axis_l: i64 = nx_buglog_axis_strlen(e.where_axis)
317 nx_buglog_emit_field("where", 5, axis_s, axis_l, 1)
318 nx_buglog_emit_field("why", 3, e.why, e.why_len, 1)
319 nx_buglog_emit_field("how", 3, e.how, e.how_len, 1)
320 nx_buglog_emit_id_array("genealogy_ids", 13, e.genealogy, e.n_genealogy, 1)
321 nx_buglog_emit_id_array("lineage_ids", 11, e.lineage, e.n_lineage, 1)
322 nx_buglog_emit_raw_json("performance", 11, e.performance, e.performance_len, 0)
323 nx_buglog_w("}\n", 2)
324 return 0
325}
326
327func nx_buglog_query(e: *BugLog) -> *ApproxI64 {
328 return nx_approx_new(e.where_axis, NX_ENV_ABS, 0, NX_MAGIC_1000000000,
329 NX_MATURITY_PRODUCTION,
330 NX_ADV_HONEST)
331}
332
333func nx_buglog_n_genealogy(e: *BugLog) -> i64 { return e.n_genealogy }
334func nx_buglog_n_lineage(e: *BugLog) -> i64 { return e.n_lineage }
335
336func nx_buglog_memory_bytes(e: *BugLog) -> i64 {
337 return 192 + NX_BUG_MAX_GENEALOGY * 16 + NX_BUG_MAX_LINEAGE * 16
338}