nx_heavyio_lib.nx source
↩ module page · 375 lines · 19788 B
1// nx_heavyio_lib.nx -- THE HEAVY-I/O CONCURRENCY BOUND, in the PRIMITIVE layer (runtime/) so every bulk-I/O
2// producer can reach it (the seq1450 layering rule: primitives may not import the organ layer).
3//
4// WHY (measured 2026-09-02): the estate's storm ruler nx_ioadmit_lib is an INSTANTANEOUS, PER-CALLER check.
5// The clock scheduler, nx_compare_regen's fork burst, seg-compact, a daemon restart and a seat-launched job
6// EACH read "not stormed" at their own instant and all proceed -- and the storm is their SUM, which none of
7// them individually caused (a TOCTOU / thundering-herd gap). Operator: "this is not sota or best of breed
8// coordination and discipline for hub spoke resource management". A bound must count what is RUNNING across
9// every surface, not what was true at one caller's instant.
10//
11// IT COMPOSES THE TWO INCUMBENTS, NEVER A THIRD RULER: nx_poolgov.pg_width derives K from THIS box (hw budget,
12// PROPORTIONAL squeeze above the load ceiling, fail-safe to wmin) and pg_count_procs counts the running heavy
13// class across every surface; nx_ioadmit_lib.ioa_spawn_budget stays as the storm witness, a SECOND conjunct.
14// Data (rule 11): knowledge/heavyio.conf --
15// producer <proc-name-needle> one row per heavy-I/O producer (the class this bound governs)
16// slots_per_cpu <n> floor <n> max_centi <n> wmin <n> wmax <n> size the width (reasoning in the conf)
17// hio_decide() is PURE and gate-driven; hio_admit() is the thin /proc wrapper. Three states, never a guess:
18// HIO_ADMIT 1 | HIO_DEFER 0 | HIO_UNOBSERVABLE (-1): conf or /proc unreadable. THE CALLER OWNS THE
19// UNOBSERVABLE POLICY and must NAME it: a periodic beat announces and proceeds (a missing conf must never
20// silence the estate's publishers -- the clock's own ioadmit dispatch check still guards the storm); an
21// interactive spawner may refuse.
22// license_tier: ORIGINAL Read-only. No hw writes (Rule 26). lib (no main)
23import "nx_syscalls.nx"
24import "nx_poolgov.nx"
25import "nx_ioadmit_lib.nx"
26import "nx_lease_lib.nx" // the K-slot POOL is K atomic leases (2026-09-03): acquisition is CAS, never read-then-act
27
28const HIO_CONF: *u8 = "knowledge/heavyio.conf"
29const HIO_ADMIT: i64 = 1
30const HIO_DEFER: i64 = 0
31const HIO_UNOBSERVABLE: i64 = 0 - 1
32const HIO_MAXP: i64 = 32 // producer rows the conf may carry; the reader announces when it fills
33const HIO_NAMEW: i64 = 64 // one producer needle slot (comm names are 15 bytes; cmdline needles longer)
34const HIO_CONFCAP: i64 = 65536
35const HIO_SPACE: i64 = 32
36const HIO_NL: i64 = 10
37const HIO_HASH: i64 = 35
38// cfg[] layout (i64 slots)
39const HIO_C_NPROD: i64 = 0
40const HIO_C_SLOTS: i64 = 1
41const HIO_C_FLOOR: i64 = 2
42const HIO_C_MAXC: i64 = 3
43const HIO_C_WMIN: i64 = 4
44const HIO_C_WMAX: i64 = 5
45const HIO_C_FILLED: i64 = 6 // 1 when HIO_MAXP was reached (rows beyond it were NOT read -- announced)
46const HIO_C_TTL: i64 = 7 // slot_ttl <sec>: crash guard for a HUNG-BUT-ALIVE holder (a dead one frees at once)
47const HIO_C_SLOTS_N: i64 = 8
48const HIO_C_BYTES: i64 = 64 // HIO_C_SLOTS_N * 8
49// out[] layout for hio_admit: 0 running 1 width 2 storm_budget 3 slot (-1 none). Adopters allocate HIO_C_BYTES (64),
50// which covers all four; HIO_OUT_BYTES names the minimum for new callers.
51const HIO_O_RUNNING: i64 = 0
52const HIO_O_WIDTH: i64 = 1
53const HIO_O_STORM: i64 = 2
54const HIO_O_SLOT: i64 = 3
55const HIO_OUT_BYTES: i64 = 32
56// THE POOL ROOT IS RESOLVED, NEVER ASSUMED (measured 2026-09-03: nx_compare_regen chdirs to buildroot, where
57// knowledge/heavyio.conf is ABSENT, so its admission read UNOBSERVABLE and it ran unbounded while the census said
58// ADOPTED). Probe the conf at knowledge/ then ../knowledge/; the lease dir follows the SAME root, so every CWD
59// shares ONE pool. hio_root returns 1 = knowledge/ 2 = ../knowledge/ 0 = neither (UNOBSERVABLE).
60const HIO_ROOT_A: *u8 = "knowledge/"
61const HIO_ROOT_B: *u8 = "../knowledge/"
62const HIO_CONF_LEAF: *u8 = "heavyio.conf"
63const HIO_LEASE_LEAF: *u8 = "lease/"
64const HIO_SLOT_PFX: *u8 = "heavyio-slot-"
65const HIO_OWNER_PFX: *u8 = "pid"
66const HIO_PROC_PFX: *u8 = "/proc/"
67const HIO_PROC_LEAF: *u8 = "/stat"
68const HIO_PATHCAP: i64 = 256
69const HIO_TTL_DEFAULT: i64 = 900 // used ONLY when the conf carries no slot_ttl row; the conf explains 900
70const HIO_POOL_NONE: i64 = 0 - 1 // every slot held by a LIVE holder -> DEFER
71const HIO_POOL_IO: i64 = 0 - 2 // every slot attempt failed with io (lease root unwritable) -> UNOBSERVABLE, never a stall
72
73func hio_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
74func hio_cat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; var p: i64 = o; while s[i] != (0 as u8) { d[p] = s[i]; p = p + 1; i = i + 1 } return p }
75func hio_catn(d: *u8, o: i64, v: i64) -> i64 { let t: *u8 = sys_mmap(28); var m: i64 = v; var p: i64 = o; if m < 0 { d[p] = 45 as u8; p = p + 1; m = 0 - m } var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; while i < k { d[p] = t[k-1-i]; p = p + 1; i = i + 1 } return p }
76func hio_eol(q: *u8, i: i64, n: i64) -> i64 { var e: i64 = i; var s: i64 = 1; while s == 1 { if e >= n { s = 0 } else { if q[e] == (HIO_NL as u8) { s = 0 } else { e = e + 1 } } } return e }
77func hio_keyis(q: *u8, s: i64, e: i64, key: *u8) -> i64 {
78 var i: i64 = 0
79 while key[i] != (0 as u8) { if s + i >= e { return 0 } if q[s + i] != key[i] { return 0 } i = i + 1 }
80 if s + i >= e { return 0 }
81 if q[s + i] != (HIO_SPACE as u8) { return 0 }
82 return 1
83}
84func hio_atoi(q: *u8, s: i64, e: i64) -> i64 {
85 var v: i64 = 0; var i: i64 = s; var seen: i64 = 0
86 while i < e { let c: i64 = q[i] as i64; if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); seen = 1; i = i + 1 } else { i = e } } else { i = e } }
87 if seen == 0 { return 0 - 1 }
88 return v
89}
90func hio_slot(names: *u8, i: i64) -> *u8 { return ((names as i64) + i * HIO_NAMEW) as *u8 }
91
92// Read the conf. cfg: HIO_C_SLOTS_N i64 slots; names: HIO_MAXP * HIO_NAMEW bytes. Returns 1, or 0 when the
93// conf is absent/unreadable/empty (the caller reports UNOBSERVABLE). Keys absent from the file read -1 so a
94// half-written conf cannot silently size the pool with a zero.
95func hio_conf_read(path: *u8, cfg: *i64, names: *u8) -> i64 {
96 var k: i64 = 0
97 while k < HIO_C_SLOTS_N { cfg[k] = 0 - 1; k = k + 1 }
98 cfg[HIO_C_NPROD] = 0
99 cfg[HIO_C_FILLED] = 0
100 let fd: i64 = sys_openat_rd(path)
101 if fd < 0 { return 0 }
102 let buf: *u8 = sys_mmap(HIO_CONFCAP)
103 var n: i64 = 0
104 var go: i64 = 1
105 while go == 1 { let r: i64 = sys_read(fd, ((buf as i64) + n) as *u8, HIO_CONFCAP - n); if r <= 0 { go = 0 } else { n = n + r } if n >= HIO_CONFCAP { go = 0 } }
106 sys_close(fd)
107 if n <= 0 { return 0 }
108 var i: i64 = 0
109 while i < n {
110 let le: i64 = hio_eol(buf, i, n)
111 if le > i { if (buf[i] as i64) != HIO_HASH {
112 if hio_keyis(buf, i, le, "producer" as *u8) == 1 {
113 let np: i64 = cfg[HIO_C_NPROD]
114 if np < HIO_MAXP {
115 let d: *u8 = hio_slot(names, np)
116 var p: i64 = i + 9
117 var w: i64 = 0
118 while p < le { if buf[p] == (HIO_SPACE as u8) { p = le } else { if w < HIO_NAMEW - 1 { d[w] = buf[p]; w = w + 1 } p = p + 1 } }
119 d[w] = 0 as u8
120 if w > 0 { cfg[HIO_C_NPROD] = np + 1 }
121 } else { cfg[HIO_C_FILLED] = 1 }
122 }
123 if hio_keyis(buf, i, le, "slots_per_cpu" as *u8) == 1 { cfg[HIO_C_SLOTS] = hio_atoi(buf, i + 14, le) }
124 if hio_keyis(buf, i, le, "floor" as *u8) == 1 { cfg[HIO_C_FLOOR] = hio_atoi(buf, i + 6, le) }
125 if hio_keyis(buf, i, le, "max_centi" as *u8) == 1 { cfg[HIO_C_MAXC] = hio_atoi(buf, i + 10, le) }
126 if hio_keyis(buf, i, le, "wmin" as *u8) == 1 { cfg[HIO_C_WMIN] = hio_atoi(buf, i + 5, le) }
127 if hio_keyis(buf, i, le, "wmax" as *u8) == 1 { cfg[HIO_C_WMAX] = hio_atoi(buf, i + 5, le) }
128 if hio_keyis(buf, i, le, "slot_ttl" as *u8) == 1 { cfg[HIO_C_TTL] = hio_atoi(buf, i + 9, le) }
129 } }
130 i = le + 1
131 }
132 return 1
133}
134
135// PURE: the decision, gate-driven. running = heavy producers observed running (INCLUDING the caller, which is
136// itself one of them); width = the derived K; storm_budget = ioa_spawn_budget (0 = the D-state line is reached).
137// Any negative input is an unobservable sensor and yields UNOBSERVABLE -- not knowing is never a verdict.
138func hio_decide(running: i64, width: i64, storm_budget: i64) -> i64 {
139 if running < 0 { return HIO_UNOBSERVABLE }
140 if width < 0 { return HIO_UNOBSERVABLE }
141 if storm_budget < 0 { return HIO_UNOBSERVABLE }
142 if storm_budget == 0 { return HIO_DEFER }
143 if running > width { return HIO_DEFER }
144 return HIO_ADMIT
145}
146
147// The derived K for this box, from the conf: pg_width(hw budget, 1 estate-wide instance, live load, ceiling, wmin, wmax).
148func hio_width(cfg: *i64) -> i64 {
149 if cfg[HIO_C_SLOTS] < 0 { return 0 - 1 }
150 if cfg[HIO_C_FLOOR] < 0 { return 0 - 1 }
151 if cfg[HIO_C_MAXC] < 0 { return 0 - 1 }
152 if cfg[HIO_C_WMIN] < 0 { return 0 - 1 }
153 if cfg[HIO_C_WMAX] < 0 { return 0 - 1 }
154 let budget: i64 = pg_budget_from_hw(cfg[HIO_C_SLOTS], cfg[HIO_C_FLOOR])
155 return pg_width(budget, 1, pg_load_centi(), cfg[HIO_C_MAXC], cfg[HIO_C_WMIN], cfg[HIO_C_WMAX])
156}
157
158// How many heavy producers are running RIGHT NOW across every launch surface (sum over the conf's class).
159// COUNTED WITH ioa_count_comm, NOT pg_count_procs -- MEASURED 2026-09-03 by this lib's own gate: pg_count_procs
160// ends `if cnt < 1 { return 1 }` because its contract is "instances INCLUDING at least myself" for a per-instance
161// pool (its one adopter, nx_torrent_get, IS an instance). Summed over a class that floor-of-1 yields one phantom
162// per needle (two fixture needles nothing ran read running=2). ioa_count_comm returns the TRUE count (0 when
163// none, -1 only when /proc is unreadable) on the kernel's 15-byte comm, and it is the same ruler the torrent
164// daemon's spawn admission already uses. ★READ THE COUNTER'S CONTRACT BEFORE SUMMING IT.
165func hio_running(cfg: *i64, names: *u8) -> i64 {
166 let np: i64 = cfg[HIO_C_NPROD]
167 if np <= 0 { return 0 - 1 }
168 var total: i64 = 0
169 var i: i64 = 0
170 while i < np { let c: i64 = ioa_count_comm(hio_slot(names, i)); if c < 0 { return 0 - 1 } total = total + c; i = i + 1 }
171 return total
172}
173
174// The storm witness as the second conjunct: the SAME ruler the build gate and the clock use.
175func hio_storm_budget() -> i64 {
176 let m: *i64 = sys_mmap(16) as *i64
177 if ioa_measure(m) != 0 { return 0 - 1 }
178 return ioa_spawn_budget(m[0], m[1], IOA_BLOCKED_PER_CPU, IOA_RESERVE_SLOTS)
179}
180
181// The thin wrapper a producer calls at ENTRY, before its bulk I/O. out[0]=running out[1]=width out[2]=storm_budget
182// so the caller can ANNOUNCE its numbers (a verdict without its figures is not actionable).
183// ---- THE K-SLOT POOL (2026-09-03): the TOCTOU fix. hio_decide above compares a COUNT read at one instant; N callers
184// can each read "under width" and all proceed. A slot is ACQUIRED atomically (nx_lease_lib: mkdir + CAS-verified
185// stamp), so at most K holders exist BY CONSTRUCTION regardless of check timing. The storm witness stays the FIRST
186// conjunct (never take a slot on a stormed box). A DEAD holder is reclaimed at once by pid-liveness (the stamp nonce
187// carries the pid); a HUNG-BUT-ALIVE holder frees after slot_ttl. No adopter has to release at exit -- but may.
188// Stated imprecision: a pid reused within slot_ttl by an unrelated live process keeps a dead holder's slot until
189// the ttl expires (bounded by the ttl, never unbounded).
190func hio_pid_alive(pid: i64) -> i64 {
191 if pid <= 0 { return 0 }
192 let p: *u8 = sys_mmap(HIO_PATHCAP)
193 var o: i64 = hio_cat(p, 0, HIO_PROC_PFX)
194 o = hio_catn(p, o, pid)
195 o = hio_cat(p, o, HIO_PROC_LEAF)
196 p[o] = 0 as u8
197 let fd: i64 = sys_openat_rd(p)
198 if fd < 0 { return 0 }
199 sys_close(fd)
200 return 1
201}
202// the pid encoded in an owner string of OUR shape (pid<n>); 0 for any other owner (never reclaimed by liveness, ttl only)
203func hio_owner_pid(owner: *u8) -> i64 {
204 if owner[0] != (112 as u8) { return 0 }
205 if owner[1] != (105 as u8) { return 0 }
206 if owner[2] != (100 as u8) { return 0 }
207 var v: i64 = 0
208 var i: i64 = 3
209 var seen: i64 = 0
210 while owner[i] != (0 as u8) {
211 let c: i64 = owner[i] as i64
212 if c < 48 { return 0 }
213 if c > 57 { return 0 }
214 v = v * 10 + (c - 48)
215 seen = 1
216 i = i + 1
217 }
218 if seen == 0 { return 0 }
219 return v
220}
221func hio_owner(out: *u8) -> i64 {
222 var o: i64 = hio_cat(out, 0, HIO_OWNER_PFX)
223 o = hio_catn(out, o, ls_pid())
224 out[o] = 0 as u8
225 return o
226}
227func hio_slot_name(i: i64, out: *u8) -> i64 {
228 var o: i64 = hio_cat(out, 0, HIO_SLOT_PFX)
229 o = hio_catn(out, o, i)
230 out[o] = 0 as u8
231 return o
232}
233// resolve the root by probing the conf; fills root and returns 1 (knowledge/) | 2 (../knowledge/) | 0 (neither)
234func hio_root(root: *u8) -> i64 {
235 let p: *u8 = sys_mmap(HIO_PATHCAP)
236 var o: i64 = hio_cat(p, 0, HIO_ROOT_A)
237 o = hio_cat(p, o, HIO_CONF_LEAF)
238 p[o] = 0 as u8
239 var fd: i64 = sys_openat_rd(p)
240 if fd >= 0 { sys_close(fd); let ra: i64 = hio_cat(root, 0, HIO_ROOT_A); root[ra] = 0 as u8; return 1 }
241 o = hio_cat(p, 0, HIO_ROOT_B)
242 o = hio_cat(p, o, HIO_CONF_LEAF)
243 p[o] = 0 as u8
244 fd = sys_openat_rd(p)
245 if fd >= 0 { sys_close(fd); let rb: i64 = hio_cat(root, 0, HIO_ROOT_B); root[rb] = 0 as u8; return 2 }
246 root[0] = 0 as u8
247 return 0
248}
249func hio_lease_root(root: *u8, out: *u8) -> i64 {
250 var o: i64 = hio_cat(out, 0, root)
251 o = hio_cat(out, o, HIO_LEASE_LEAF)
252 out[o] = 0 as u8
253 return o
254}
255// try slots 0..width-1 under lroot; returns the slot index HELD, or -1 when every slot is held by a LIVE holder.
256// A slot whose holder pid is dead is released on the dead holder's behalf (owner string read from its stamp) and retaken.
257func hio_pool_acquire(lroot: *u8, width: i64, ttl: i64) -> i64 {
258 if width <= 0 { return HIO_POOL_NONE }
259 // The lease primitive FAILS TOWARD NOT HOLDING on io (its claim folds EEXIST and io into "lost", by design), so a
260 // pool cannot learn "unobservable" through acquire. Probe the root ITSELF: mkdir it, then open it -- a root that
261 // cannot be opened is a pool that cannot be seen, and that is UNOBSERVABLE, never "every slot held".
262 ls_mkdir(lroot)
263 let rfd: i64 = sys_openat_rd(lroot)
264 if rfd < 0 { return HIO_POOL_IO }
265 sys_close(rfd)
266 let owner: *u8 = sys_mmap(HIO_PATHCAP)
267 hio_owner(owner)
268 let name: *u8 = sys_mmap(HIO_PATHCAP)
269 let hb: *u8 = sys_mmap(HIO_PATHCAP)
270 var i: i64 = 0
271 while i < width {
272 hio_slot_name(i, name)
273 var r: i64 = ls_acquire_root(lroot, name, owner, ttl, hb)
274 if r == 0 { return i }
275 if r == 1 {
276 // hb carries the holder OWNER (ls_acquire_root fills it on busy); our owners are pid<n> by construction
277 let hp: i64 = hio_owner_pid(hb)
278 if hp > 0 { if hio_pid_alive(hp) == 0 {
279 ls_release_root(lroot, name, hb)
280 r = ls_acquire_root(lroot, name, owner, ttl, hb)
281 if r == 0 { return i }
282 } }
283 }
284 i = i + 1
285 }
286 return HIO_POOL_NONE
287}
288func hio_pool_release(lroot: *u8, slot: i64) -> i64 {
289 if slot < 0 { return 1 }
290 let owner: *u8 = sys_mmap(HIO_PATHCAP)
291 hio_owner(owner)
292 let name: *u8 = sys_mmap(HIO_PATHCAP)
293 hio_slot_name(slot, name)
294 return ls_release_root(lroot, name, owner)
295}
296// PURE: the pool decision, gate-driven. storm first (never take a slot on a stormed box), then the slot. A negative
297// width or storm is an unobservable sensor; slot -1 with a positive width = every slot held = DEFER.
298func hio_decide_pool(width: i64, storm_budget: i64, slot: i64) -> i64 {
299 if width < 0 { return HIO_UNOBSERVABLE }
300 if storm_budget < 0 { return HIO_UNOBSERVABLE }
301 if storm_budget == 0 { return HIO_DEFER }
302 if slot == HIO_POOL_IO { return HIO_UNOBSERVABLE }
303 if slot < 0 { return HIO_DEFER }
304 return HIO_ADMIT
305}
306// The thin wrapper a producer calls at ENTRY. out: running / width / storm_budget / slot. On ADMIT the caller HOLDS
307// slot out[3] for its lifetime (freed by exit through pid-liveness, or early via hio_release). hio_admit_conf keeps
308// its path argument for the gate's fixtures (root = the path's own dir is the caller's job); hio_admit resolves the root.
309func hio_admit_conf_root(root: *u8, path: *u8, out: *i64) -> i64 {
310 let cfg: *i64 = sys_mmap(HIO_C_BYTES) as *i64
311 let names: *u8 = sys_mmap(HIO_MAXP * HIO_NAMEW)
312 out[HIO_O_RUNNING] = 0 - 1; out[HIO_O_WIDTH] = 0 - 1; out[HIO_O_STORM] = 0 - 1; out[HIO_O_SLOT] = 0 - 1
313 if hio_conf_read(path, cfg, names) == 0 { return HIO_UNOBSERVABLE }
314 out[HIO_O_RUNNING] = hio_running(cfg, names)
315 out[HIO_O_WIDTH] = hio_width(cfg)
316 out[HIO_O_STORM] = hio_storm_budget()
317 if out[HIO_O_WIDTH] < 0 { return HIO_UNOBSERVABLE }
318 if out[HIO_O_STORM] < 0 { return HIO_UNOBSERVABLE }
319 if out[HIO_O_STORM] == 0 { return HIO_DEFER }
320 var ttl: i64 = cfg[HIO_C_TTL]
321 if ttl < 0 { ttl = HIO_TTL_DEFAULT }
322 let lroot: *u8 = sys_mmap(HIO_PATHCAP)
323 hio_lease_root(root, lroot)
324 out[HIO_O_SLOT] = hio_pool_acquire(lroot, out[HIO_O_WIDTH], ttl)
325 return hio_decide_pool(out[HIO_O_WIDTH], out[HIO_O_STORM], out[HIO_O_SLOT])
326}
327func hio_admit_conf(path: *u8, out: *i64) -> i64 { return hio_admit_conf_root(HIO_ROOT_A, path, out) }
328func hio_admit(out: *i64) -> i64 {
329 let root: *u8 = sys_mmap(HIO_PATHCAP)
330 if hio_root(root) == 0 {
331 out[HIO_O_RUNNING] = 0 - 1; out[HIO_O_WIDTH] = 0 - 1; out[HIO_O_STORM] = 0 - 1; out[HIO_O_SLOT] = 0 - 1
332 return HIO_UNOBSERVABLE
333 }
334 let path: *u8 = sys_mmap(HIO_PATHCAP)
335 var o: i64 = hio_cat(path, 0, root)
336 o = hio_cat(path, o, HIO_CONF_LEAF)
337 path[o] = 0 as u8
338 return hio_admit_conf_root(root, path, out)
339}
340// early release for adopters that want the slot back before exit (optional: exit frees it by pid-liveness)
341func hio_release(out: *i64) -> i64 {
342 let root: *u8 = sys_mmap(HIO_PATHCAP)
343 if hio_root(root) == 0 { return 1 }
344 let lroot: *u8 = sys_mmap(HIO_PATHCAP)
345 hio_lease_root(root, lroot)
346 let r: i64 = hio_pool_release(lroot, out[HIO_O_SLOT])
347 out[HIO_O_SLOT] = 0 - 1
348 return r
349}
350
351// Announce line for the caller: HEAVYIO running=N width=K storm_budget=S verdict=ADMIT|DEFER|UNOBSERVABLE
352func hio_announce(fd: i64, verdict: i64, out: *i64) -> i64 {
353 let b: *u8 = sys_mmap(256)
354 var o: i64 = 0
355 o = hio_cat(b, o, "HEAVYIO running=" as *u8); o = hio_catn(b, o, out[0])
356 o = hio_cat(b, o, " width=" as *u8); o = hio_catn(b, o, out[1])
357 o = hio_cat(b, o, " storm_budget=" as *u8); o = hio_catn(b, o, out[2])
358 o = hio_cat(b, o, " slot=" as *u8); o = hio_catn(b, o, out[3])
359 o = hio_cat(b, o, " verdict=" as *u8)
360 if verdict == HIO_ADMIT { o = hio_cat(b, o, "ADMIT" as *u8) }
361 if verdict == HIO_DEFER { o = hio_cat(b, o, "DEFER" as *u8) }
362 if verdict == HIO_UNOBSERVABLE { o = hio_cat(b, o, "UNOBSERVABLE" as *u8) }
363 o = hio_cat(b, o, "\n" as *u8)
364 sys_write(fd, b, o)
365 return 0
366}
367
368// RATCHET arithmetic for the adoption tooth (PURE): the floor only rises. Returns the floor to WRITE, or -1
369// when the observed count is BELOW the floor (a regression -- the gate goes RED and the baseline is NOT rewritten).
370func hio_ratchet_next(floor: i64, adopters: i64) -> i64 {
371 if adopters < 0 { return 0 - 1 }
372 if floor < 0 { return adopters }
373 if adopters < floor { return 0 - 1 }
374 return adopters
375}