nx_loadceil_lib.nx source
↩ module page · 370 lines · 16511 B
1// nx_loadceil_lib.nx -- DERIVE THE BUILD-ADMISSION LOAD CEILING FROM THE BOX'S OWN HEALTHY-BEAT HISTORY,
2// function side (LV3, loadgov.plan, 2026-08-24). Split from the CLI so nx_loadceil_gate composes it in-process.
3//
4// Removes a real magic number: the ceiling sits DISABLED at an unreachable max_centiload (mgmt 100000,
5// runner 1000000) because, until nx_resmon began logging load1_centi on every beat, there was no history to
6// calibrate it from. That history is now live (resmon.log carries load1_centi= and state= per row), so the
7// ceiling can be a MEASURED quantile of the loads at which the box was actually GREEN.
8//
9// THE ARITHMETIC THIS COUPLES TO, READ FROM ba_verdict: a build QUEUEs when load1 > max_centiload *
10// BA_HARD_FACTOR (=2). So the STORM TRIGGER is 2*max_centiload; this derives the trigger from a high quantile
11// of GREEN load and reports max_centiload = trigger / BA_HARD_FACTOR. Every ba_verdict failure is a QUEUE
12// (defer), never a hard DENY -- so a mis-calibration defers builds, it cannot brick the lane; the safe
13// direction is UP and this derives a HIGH quantile with the whole distribution returned for audit.
14//
15// NO MAGIC: quantile level and minimum sample are conf rows; LC_HARD_FACTOR is mirrored from the coupled
16// organ and NAMED (the gate asserts recommend*factor == trigger). Below the minimum GREEN sample it REFUSES
17// rather than arm from too little history -- the permanently-red-detector failure the plan's risk row names.
18//
19// GENERALISED 2026-08-25 (lane E). The shape here -- parse a line-oriented log, take a per-line key= integer,
20// filter by a per-line marker, sort, nearest-rank quantile, abstain below a sample floor -- is not specific
21// to load. It is now parameterised (lc_collect_kv / lc_recommend_kv) so a SECOND magic number is killed by
22// the SAME calibrator instead of a second ruler: nx_replyreserve derives edge_window.conf reply_reserve_ms
23// from reply_ms= on lane=sync-promoted actlog rows. lc_collect / lc_recommend / lc_line_green are now thin
24// delegates passing the original literals, so the load-ceiling path is unchanged BY CONSTRUCTION rather than
25// by assertion.
26// Three additions the second caller forced, each a defect the first caller never hit:
27// lc_conf_line_num -- LINE-ANCHORED conf lookup mirroring nx_tool_exec_allow.tea_conf_int, the contract
28// that actually owns knowledge/edge_window.conf. The whole-buffer lc_conf_num is WRONG there: the keys
29// edge_window_ms and reply_reserve_ms both occur inside that file PROSE above their rows, so a
30// first-occurrence scan reads the comment, finds no digits, and silently returns the default.
31// lc_read_tail -- a journal is APPEND-ONLY, so a head read samples its PAST. Measured 2026-08-25: every
32// reply_ms= row sat in the last 25 KB of a 30 MB actlog, and a 4 MB read from offset 0 finds ZERO of
33// them -- an abstention for the wrong reason, from an organ that could then never arm itself.
34// lc_first_int -- first integer ANYWHERE after a key on its line, for space-separated conf rows.
35// license_tier: ORIGINAL
36
37import "nx_syscalls.nx"
38
39const LC_LOGCAP: i64 = 4194304
40const LC_MAXBEATS: i64 = 65536
41const LC_CONFCAP: i64 = 8192
42const LC_NL: i64 = 10
43const LC_ZERO: i64 = 48
44const LC_NINE: i64 = 57
45// mirrored from nx_build_admit.BA_HARD_FACTOR -- the storm multiplier the ceiling couples to.
46const LC_HARD_FACTOR: i64 = 2
47const LC_SEEK_SET: i64 = 0
48const LC_SEEK_END: i64 = 2
49
50// verdict codes
51const LC_RECOMMENDED: i64 = 0
52const LC_UNREADABLE: i64 = 2
53const LC_INSUFFICIENT: i64 = 3
54
55// out[] slots filled by lc_recommend
56const LC_O_VERDICT: i64 = 0
57const LC_O_GREEN: i64 = 1
58const LC_O_TOTAL: i64 = 2
59const LC_O_MISSING: i64 = 3
60const LC_O_CAPPED: i64 = 4
61const LC_O_MIN: i64 = 5
62const LC_O_P50: i64 = 6
63const LC_O_P90: i64 = 7
64const LC_O_P99: i64 = 8
65const LC_O_MAX: i64 = 9
66const LC_O_TRIGGER: i64 = 10
67const LC_O_REC: i64 = 11
68const LC_O_Q: i64 = 12
69const LC_O_MINN: i64 = 13
70const LC_O_N: i64 = 16
71
72// meta[] slots filled by lc_read_tail
73const LC_M_SIZE: i64 = 0
74const LC_M_WHOLE: i64 = 1
75const LC_M_BYTES: i64 = 2
76const LC_M_N: i64 = 4
77
78func lc_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
79func lc_read(path: *u8, buf: *u8, cap: i64) -> i64 {
80 let fd: i64 = sys_openat_rd(path)
81 if fd < 0 { return 0 - 1 }
82 let n: i64 = sys_read(fd, buf, cap)
83 sys_close(fd)
84 return n
85}
86// Read the LAST cap-1 bytes of a file and drop the partial first line, so the caller parses whole rows from
87// buf[0]. A whole-file read is returned unchanged when the file fits. meta: size / whole(1=entire file) /
88// bytes delivered. Returns bytes usable from buf[0], or -1 when the file cannot be opened or seeked.
89// WHY: an append-only journal RECENT rows are the subject; a head read of a capped window samples the past.
90func lc_read_tail(path: *u8, buf: *u8, cap: i64, meta: *i64) -> i64 {
91 var z: i64 = 0
92 while z < LC_M_N { meta[z] = 0; z = z + 1 }
93 let fd: i64 = sys_openat_rd(path)
94 if fd < 0 { return 0 - 1 }
95 let sz: i64 = sys_lseek(fd, 0, LC_SEEK_END)
96 if sz < 0 { sys_close(fd); return 0 - 1 }
97 meta[LC_M_SIZE] = sz
98 var start: i64 = 0
99 var whole: i64 = 1
100 if sz > cap - 1 { start = sz - (cap - 1); whole = 0 }
101 sys_lseek(fd, start, LC_SEEK_SET)
102 var tot: i64 = 0
103 var go: i64 = 1
104 while go == 1 {
105 let r: i64 = sys_read(fd, ((buf as i64) + tot) as *u8, cap - tot - 1)
106 if r <= 0 { go = 0 } else { tot = tot + r; if tot >= cap - 1 { go = 0 } }
107 }
108 sys_close(fd)
109 buf[tot] = 0 as u8
110 meta[LC_M_WHOLE] = whole
111 var off: i64 = 0
112 if whole == 0 {
113 var p: i64 = 0
114 var seek: i64 = 1
115 while seek == 1 {
116 if p >= tot { seek = 0 } else {
117 if buf[p] == (LC_NL as u8) { off = p + 1; seek = 0 } else { p = p + 1 }
118 }
119 }
120 }
121 if off > 0 {
122 var k: i64 = 0
123 while k < tot - off { buf[k] = buf[off + k]; k = k + 1 }
124 buf[tot - off] = 0 as u8
125 }
126 meta[LC_M_BYTES] = tot - off
127 return tot - off
128}
129// integer value of `key=` on line [lo,hi); -1 when the key is absent or has no digits.
130func lc_line_num(buf: *u8, lo: i64, hi: i64, key: *u8) -> i64 {
131 let kl: i64 = lc_slen(key)
132 var i: i64 = lo
133 var at: i64 = 0 - 1
134 while i + kl <= hi {
135 if at < 0 {
136 var j: i64 = 0
137 var ok: i64 = 1
138 while j < kl { if buf[i+j] != key[j] { ok = 0; j = kl } else { j = j + 1 } }
139 if ok == 1 { at = i + kl }
140 }
141 i = i + 1
142 }
143 if at < 0 { return 0 - 1 }
144 var v: i64 = 0
145 var seen: i64 = 0
146 var p: i64 = at
147 var go: i64 = 1
148 while go == 1 {
149 if p >= hi { go = 0 } else {
150 let c: i64 = buf[p] as i64
151 if c >= LC_ZERO { if c <= LC_NINE { v = v * 10 + (c - LC_ZERO); seen = 1; p = p + 1 } else { go = 0 } } else { go = 0 }
152 }
153 }
154 if seen == 0 { return 0 - 1 }
155 return v
156}
157// first non-negative integer anywhere in [lo,hi); -1 when the range holds no digit. For space-separated
158// conf rows (reply_reserve_ms 1000), where the value does not abut its key.
159func lc_first_int(buf: *u8, lo: i64, hi: i64) -> i64 {
160 var p: i64 = lo
161 var v: i64 = 0 - 1
162 while p < hi {
163 if v < 0 {
164 let c: i64 = buf[p] as i64
165 if c >= LC_ZERO { if c <= LC_NINE {
166 var acc: i64 = 0
167 var q: i64 = p
168 var go: i64 = 1
169 while go == 1 {
170 if q >= hi { go = 0 } else {
171 let d: i64 = buf[q] as i64
172 if d >= LC_ZERO { if d <= LC_NINE { acc = acc * 10 + (d - LC_ZERO); q = q + 1 } else { go = 0 } } else { go = 0 }
173 }
174 }
175 v = acc
176 } }
177 }
178 p = p + 1
179 }
180 return v
181}
182// 1 when line [lo,hi) contains `key`; an EMPTY key means "no filter" and matches every line.
183func lc_line_has(buf: *u8, lo: i64, hi: i64, key: *u8) -> i64 {
184 let kl: i64 = lc_slen(key)
185 if kl == 0 { return 1 }
186 var i: i64 = lo
187 var found: i64 = 0
188 while i + kl <= hi {
189 if found == 0 {
190 var j: i64 = 0
191 var ok: i64 = 1
192 while j < kl { if buf[i+j] != key[j] { ok = 0; j = kl } else { j = j + 1 } }
193 if ok == 1 { found = 1 }
194 }
195 i = i + 1
196 }
197 return found
198}
199// 1 when line [lo,hi) contains "state=GREEN"
200func lc_line_green(buf: *u8, lo: i64, hi: i64) -> i64 {
201 return lc_line_has(buf, lo, hi, "state=GREEN" as *u8)
202}
203func lc_conf_num(cb: *u8, cn: i64, key: *u8, dflt: i64) -> i64 {
204 let v: i64 = lc_line_num(cb, 0, cn, key)
205 if v < 0 { return dflt }
206 return v
207}
208// first integer after `key` on the FIRST line that STARTS WITH key; dflt when no such line has an integer.
209// Mirrors nx_tool_exec_allow.tea_conf_int, the parser that owns knowledge/edge_window.conf. Line-anchored
210// because that file names its own keys in prose ABOVE their rows -- a whole-buffer scan reads the comment.
211func lc_conf_line_num(cb: *u8, cn: i64, key: *u8, dflt: i64) -> i64 {
212 let kl: i64 = lc_slen(key)
213 var ls: i64 = 0
214 var got: i64 = dflt
215 var done: i64 = 0
216 while ls < cn {
217 var le: i64 = ls
218 var scan: i64 = 1
219 while scan == 1 {
220 if le >= cn { scan = 0 } else {
221 if cb[le] == (LC_NL as u8) { scan = 0 } else { le = le + 1 }
222 }
223 }
224 if done == 0 {
225 if le - ls >= kl {
226 var m: i64 = 1
227 var i: i64 = 0
228 while i < kl { if cb[ls+i] != key[i] { m = 0; i = kl } else { i = i + 1 } }
229 if m == 1 {
230 let v: i64 = lc_first_int(cb, ls + kl, le)
231 if v >= 0 { got = v; done = 1 }
232 }
233 }
234 }
235 ls = le + 1
236 }
237 return got
238}
239// Insertion sort, ascending.
240// FIXED 2026-08-25 (lane E), found by nx_replyreserve_gate on its first run. The inner loop used to exit
241// by CLOBBERING ITS OWN CURSOR -- `else { j = 0 - 1 }` as a stand-in for break -- and the line after it is
242// `a[j+1] = key`. So every element that was ALREADY in order relative to its predecessor was written to
243// a[0] instead of to its own slot: the array kept its original contents everywhere except position 0,
244// which ended up holding the LAST key examined.
245// WHY NOTHING CAUGHT IT: on an already-ascending input every quantile at index >= 1 is still correct by
246// luck, and only a[0] is wrong -- so p50/p90/p99 all read right and ONLY the minimum lied. The incumbent
247// gate's fixture was ascending AND it asserted the max but never the min, which is precisely the blind
248// spot. Both fixtures are now shuffled and the min is asserted.
249// NOTE ON COST, because this fix is not free: the broken loop always exited on its first test, so it was
250// O(n) and never actually moved anything. A correct insertion sort is O(n^2). At the sample sizes both
251// callers see (hundreds to low thousands) that is under a millisecond, but LC_MAXBEATS is 65536 and a full
252// array there would be ~4e9 operations. Bounded and named rather than silently inherited.
253// COST RESOLVED 2026-08-25 (debt 1787668206): the corrected insertion sort above was O(n^2), which at
254// LC_MAXBEATS=65536 is ~4e9 operations -- a 35-day fuse, since resmon.log gains ~288 rows/day. Replaced
255// with an in-place iterative HEAPSORT: O(n log n) worst case, no recursion, no allocation, same ascending
256// order and the same (a, n) contract, so both callers are unchanged. At 65536 this is ~2e6 comparisons
257// instead of ~4e9. Budget the complexity before writing rather than inherit it.
258// NOTE THE EXIT DISCIPLINE: every loop here ends on an explicit FLAG, never by writing a sentinel into
259// its own cursor. That shortcut is precisely what corrupted this function -- `else { j = 0 - 1 }` left
260// a[j+1] writing to a[0] for every already-ordered element -- and it is the estate's recurring defect.
261func lc_sift(a: *i64, root: i64, n: i64) -> i64 {
262 var r: i64 = root
263 var go: i64 = 1
264 while go == 1 {
265 var big: i64 = r
266 let l: i64 = 2 * r + 1
267 let rt: i64 = 2 * r + 2
268 if l < n { if a[l] > a[big] { big = l } }
269 if rt < n { if a[rt] > a[big] { big = rt } }
270 if big == r { go = 0 } else {
271 let t: i64 = a[r]; a[r] = a[big]; a[big] = t
272 r = big
273 }
274 }
275 return 0
276}
277func lc_sort(a: *i64, n: i64) -> i64 {
278 if n < 2 { return 0 }
279 var s: i64 = n / 2 - 1
280 while s >= 0 { lc_sift(a, s, n); s = s - 1 }
281 var e: i64 = n - 1
282 while e > 0 {
283 let t: i64 = a[0]; a[0] = a[e]; a[e] = t
284 lc_sift(a, 0, e)
285 e = e - 1
286 }
287 return 0
288}
289// nearest-rank permil-quantile of a sorted-ascending array (q=990 -> p99); -1 on empty.
290func lc_quant(a: *i64, n: i64, q_permil: i64) -> i64 {
291 if n <= 0 { return 0 - 1 }
292 var idx: i64 = (q_permil * (n - 1)) / 1000
293 if idx < 0 { idx = 0 }
294 if idx >= n { idx = n - 1 }
295 return a[idx]
296}
297// GENERIC collector: fill vals[] with the value_key integer of every line that also carries filter_key
298// (empty filter_key = every line). counts[0]=lines counts[1]=lines-without-the-value counts[2]=capped.
299func lc_collect_kv(buf: *u8, n: i64, vals: *i64, cap: i64, counts: *i64, value_key: *u8, filter_key: *u8) -> i64 {
300 var g: i64 = 0
301 var total: i64 = 0
302 var missing: i64 = 0
303 var capped: i64 = 0
304 var lo: i64 = 0
305 var i: i64 = 0
306 while i <= n {
307 var eol: i64 = 0
308 if i == n { eol = 1 }
309 if i < n { if buf[i] == (LC_NL as u8) { eol = 1 } }
310 if eol == 1 {
311 if i > lo {
312 if total >= LC_MAXBEATS { capped = 1 } else {
313 total = total + 1
314 let val: i64 = lc_line_num(buf, lo, i, value_key)
315 if val < 0 { missing = missing + 1 } else {
316 if lc_line_has(buf, lo, i, filter_key) == 1 { if g < cap { vals[g] = val; g = g + 1 } else { capped = 1 } }
317 }
318 }
319 }
320 lo = i + 1
321 }
322 i = i + 1
323 }
324 counts[0] = total
325 counts[1] = missing
326 counts[2] = capped
327 return g
328}
329// parse a resmon-log buffer, fill greens[] with load1_centi of each GREEN beat; returns green count.
330// counts[0]=total counts[1]=missing-load counts[2]=capped(1 if a bound was hit).
331func lc_collect(buf: *u8, n: i64, greens: *i64, cap: i64, counts: *i64) -> i64 {
332 return lc_collect_kv(buf, n, greens, cap, counts, "load1_centi=" as *u8, "state=GREEN" as *u8)
333}
334// THE GENERIC RULER, pure over a buffer: fills out[LC_O_*] and returns a verdict code. No I/O, no stdout, so
335// a gate composes it on a planted buffer and asserts every value. LC_O_TRIGGER is the value at the requested
336// quantile; LC_O_REC is that divided by LC_HARD_FACTOR (meaningful to the load-ceiling caller -- a caller
337// with no such coupling reads LC_O_TRIGGER and applies its own derivation).
338func lc_recommend_kv(buf: *u8, n: i64, q: i64, minn: i64, value_key: *u8, filter_key: *u8, out: *i64) -> i64 {
339 var k: i64 = 0
340 while k < LC_O_N { out[k] = 0; k = k + 1 }
341 out[LC_O_Q] = q
342 out[LC_O_MINN] = minn
343 if n <= 0 { out[LC_O_VERDICT] = LC_UNREADABLE; return LC_UNREADABLE }
344 let greens: *i64 = sys_mmap(LC_MAXBEATS * 8) as *i64
345 let counts: *i64 = sys_mmap(8 * 4) as *i64
346 let g: i64 = lc_collect_kv(buf, n, greens, LC_MAXBEATS, counts, value_key, filter_key)
347 lc_sort(greens, g)
348 out[LC_O_GREEN] = g
349 out[LC_O_TOTAL] = counts[0]
350 out[LC_O_MISSING] = counts[1]
351 out[LC_O_CAPPED] = counts[2]
352 if g > 0 {
353 out[LC_O_MIN] = greens[0]
354 out[LC_O_P50] = lc_quant(greens, g, 500)
355 out[LC_O_P90] = lc_quant(greens, g, 900)
356 out[LC_O_P99] = lc_quant(greens, g, 990)
357 out[LC_O_MAX] = greens[g-1]
358 }
359 if g < minn { out[LC_O_VERDICT] = LC_INSUFFICIENT; return LC_INSUFFICIENT }
360 let trigger: i64 = lc_quant(greens, g, q)
361 out[LC_O_TRIGGER] = trigger
362 out[LC_O_REC] = trigger / LC_HARD_FACTOR
363 out[LC_O_VERDICT] = LC_RECOMMENDED
364 return LC_RECOMMENDED
365}
366// THE RULER, pure over a buffer: fills out[LC_O_*] and returns a verdict code. No I/O, no stdout, so the
367// gate composes it on a planted buffer and asserts every value.
368func lc_recommend(buf: *u8, n: i64, q: i64, minn: i64, out: *i64) -> i64 {
369 return lc_recommend_kv(buf, n, q, minn, "load1_centi=" as *u8, "state=GREEN" as *u8, out)
370}