code wiki / _hdl_build / nx_autonomy_meter.nx

nx_autonomy_meter.nx source

↩ module page · 215 lines · 11501 B

1// nx_autonomy_meter.nx -- X-AUT-002: the autonomy GRADE, MEASURED never asserted 2// (autonomy ladder spec 2026-06-11; declared W0 tutor-with-team scaffold -- and this 3// organ COUNTS ITSELF against the grade: it is tutor-authored, so A2 stays honest). 4// PLAIN LANGUAGE: how much of the work does the system do by itself? Four axes, 5// each 0..1000 permil, each from a ledger anyone can re-read: 6// A1 judged-DONE share : queue rows DONE carrying ||MARK= (machine-judged by 7// reconcile evidence) / all DONE rows. Hand-flipped DONEs drag the grade. 8// A2 emitter-authored share : ABSENT (named gap) -- registrations do not yet carry 9// an author= tag (row X-AUT-002a files it). Counted 0, never faked. 10// A3 daemon-run share : pulse runs fired by the autorun daemon (DAEMON-BEAT rows) 11// / all pulse runs (PULSE-RUN rows). Hand-run beats drag the grade. 12// A4 hands-off-heal share : ENG- defect rows DONE with MARK / all ENG- rows DONE. 13// Zero closed = 0 with a note, never a crash. 14// autonomy_permil = (A1+A2+A3+A4)/4 ; grade: <150 F, <500 D, <700 C, <850 B, 15// <900 A, >=900 S (the ladder's bars). 16// Durable: AUTMETER row -> knowledge/status/autonomy_meter.log. Exit 0 = measured 17// (any grade); exit 1 = could not read its world. argv[1]=queue argv[2]=pulselog 18// argv[3]=daemonlog argv[4]=outlog (gates use scratch). 19// license_tier: ORIGINAL 20import "nx_syscalls.nx" 21const K_MAGIC_1048576: i64 = 1048576 22const K_MAGIC_1048560: i64 = 1048560 23func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 24func _fp(fd: i64, s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(fd,s,n); return 0 } 25// ★FIXED 2026-08-07 -- EMITS THE SIGN. The old body did `if m<0 { m = 0-m }` and printed the 26// magnitude, so AM_AXIS_UNKNOWN (-1) rendered as "1". MEASURED: the first run of the corrected 27// meter published `a3_daemon_runs=1`, which reads as a real, terrible 1-permil score -- the exact 28// confusion the UNKNOWN sentinel was introduced to remove. The arithmetic was already right 29// (axes_known=3, permil=705); only the display lied. 30// A NUMBER PRINTER THAT DROPS THE SIGN MAKES A SENTINEL INDISTINGUISHABLE FROM A VALUE. 31// This body is copy-pasted across the organ family in two variants -- some DO write the '-' 32// (nx_intent_gate, nx_revive_gate) and some do not. Same name, same call sites, different truth. 33func _fn(fd: i64, v: i64) -> i64 { 34 let bb: *u8 = sys_mmap(32) 35 var m: i64 = v 36 var neg: i64 = 0 37 if m < 0 { m = 0 - m; neg = 1 } 38 let t: *u8 = sys_mmap(32) 39 var k: i64 = 0 40 if m == 0 { t[0] = 48 as u8; k = 1 } 41 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 42 var o: i64 = 0 43 if neg == 1 { bb[0] = 45 as u8; o = 1 } 44 var i: i64 = 0 45 while i < k { bb[o + i] = t[k - 1 - i]; i = i + 1 } 46 sys_write(fd, bb, k + o) 47 return 0 48} 49func am_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 50func am_read(path: *u8, buf: *u8, cap: i64) -> i64 { 51 let fd: i64 = sys_openat_rd(path) 52 if fd < 0 { return 0 } 53 var n: i64 = 0 54 var go: i64 = 1 55 while go == 1 { let base: i64 = buf as i64; let r: i64 = sys_read(fd, (base + n) as *u8, cap - n); if r <= 0 { go = 0 } else { n = n + r } if n >= cap { go = 0 } } 56 sys_close(fd) 57 return n 58} 59func am_slice_has(hay: *u8, a: i64, b: i64, pat: *u8) -> i64 { 60 let pl: i64 = am_len(pat) 61 if pl == 0 { return 0 } 62 var i: i64 = a 63 while i + pl <= b { 64 var k: i64 = 0 65 var hit: i64 = 1 66 while k < pl { if hay[i+k] != pat[k] { hit = 0; k = pl } else { k = k + 1 } } 67 if hit == 1 { return 1 } 68 i = i + 1 69 } 70 return 0 71} 72// count LINES holding p1 (and p2 if not the 1-byte sentinel '*') 73func am_lines2(buf: *u8, n: i64, p1: *u8, p2: *u8) -> i64 { 74 var cnt: i64 = 0 75 var i: i64 = 0 76 while i < n { 77 var le: i64 = i 78 var s: i64 = 1 79 while s == 1 { if le >= n { s = 0 } else { if buf[le] == (10 as u8) { s = 0 } else { le = le + 1 } } } 80 if am_slice_has(buf, i, le, p1) == 1 { 81 var two: i64 = 1 82 if p2[0] != (42 as u8) { two = am_slice_has(buf, i, le, p2) } 83 if two == 1 { cnt = cnt + 1 } 84 } 85 i = le + 1 86 } 87 return cnt 88} 89// Returns the axis in permil, or AM_AXIS_UNKNOWN (-1) when there is NO DATA to score. 90// 91// ★FIXED 2026-08-07. This used to `return 0` on an empty denominator, and main() averaged that 0 92// into a FIXED divisor of 4. So an axis whose ledger DOES NOT EXIST scored a hard zero and was 93// published as if it had been measured badly. 94// MEASURED: A3 reads knowledge/status/team_pulse.log and pulse_daemon.log -- BOTH ABSENT -- so a3 95// scored 0/0 -> 0 and dragged the headline down by 250 permil (a quarter of the range) while 96// looking exactly like evidence of poor daemon autonomy. 97// A MISSING LEDGER SCORING ZERO IS INDISTINGUISHABLE FROM A MEASURED ZERO. 98// This is the same defect as a vacuous gate, only inverted: there, silence scored as SUCCESS; here, 99// silence scores as FAILURE. Both are wrong for the one reason -- SILENCE IS NOT AN ANSWER. 100// Callers must exclude UNKNOWN axes from the average and DECLARE how many were scorable, so a 101// missing instrument reads as "3 of 4 axes measured", never as "we measured, and it was zero". 102const AM_AXIS_UNKNOWN: i64 = 0 - 1 103 104func am_axis(num: i64, den: i64) -> i64 { 105 if den <= 0 { return AM_AXIS_UNKNOWN } 106 // v2 small-N damping (X-AUT-002b, filed BEFORE the n=1 jump printed): one lucky 107 // heal must not print grade C -- axis_effective = axis * min(den,5) / 5 108 var dmin: i64 = den 109 if dmin > 5 { dmin = 5 } 110 return (((1000 * num) / den) * dmin) / 5 111} 112func am_grade(fd: i64, permil: i64) -> i64 { 113 if permil < 150 { _fp(fd, "F" as *u8); return 0 } 114 if permil < 500 { _fp(fd, "D" as *u8); return 0 } 115 if permil < 700 { _fp(fd, "C" as *u8); return 0 } 116 if permil < 850 { _fp(fd, "B" as *u8); return 0 } 117 if permil < 900 { _fp(fd, "A" as *u8); return 0 } 118 _fp(fd, "S" as *u8) 119 return 0 120} 121func main(argc: i64, argv: *i64) -> i64 { 122 var qp: *u8 = "knowledge/registry/assignment_queue.tsv" as *u8 123 var plp: *u8 = "knowledge/status/team_pulse.log" as *u8 124 var dlp: *u8 = "knowledge/status/pulse_daemon.log" as *u8 125 var olp: *u8 = "knowledge/status/autonomy_meter.log" as *u8 126 if argc >= 2 { qp = argv[1] as *u8 } 127 if argc >= 3 { plp = argv[2] as *u8 } 128 if argc >= 4 { dlp = argv[3] as *u8 } 129 if argc >= 5 { olp = argv[4] as *u8 } 130 _p("=== AUTONOMY METER: the grade is measured from ledgers, never asserted ===\n" as *u8) 131 let qb: *u8 = sys_mmap(K_MAGIC_1048576) 132 let qn: i64 = am_read(qp, qb, K_MAGIC_1048560) 133 if qn <= 0 { _p(" queue MISSING -- fail loud\n" as *u8); sys_exit(1); return 1 } 134 let pb: *u8 = sys_mmap(K_MAGIC_1048576) 135 let pn: i64 = am_read(plp, pb, K_MAGIC_1048560) 136 let db: *u8 = sys_mmap(K_MAGIC_1048576) 137 let dn: i64 = am_read(dlp, db, K_MAGIC_1048560) 138 let star: *u8 = "*" as *u8 139 // A1: DONE rows judged by MARK evidence / all DONE rows 140 let done_all: i64 = am_lines2(qb, qn, "\tDONE\t" as *u8, star) 141 let done_mark: i64 = am_lines2(qb, qn, "\tDONE\t" as *u8, "||MARK=" as *u8) 142 let a1: i64 = am_axis(done_mark, done_all) 143 // A2 v3: registrations carrying author= tags (the protocol starts 2026-06-12; 144 // untagged pre-protocol entries are excluded -- the share is over TAGGED only, 145 // and zero tagged entries reads 0 honestly, never ABSENT-faked-green) 146 let rb2: *u8 = sys_mmap(K_MAGIC_1048576) 147 let rn2: i64 = am_read("knowledge/status/cap_registry_durable.log" as *u8, rb2, K_MAGIC_1048560) 148 let auth_all: i64 = am_lines2(rb2, rn2, " author=" as *u8, star) 149 let auth_emit: i64 = am_lines2(rb2, rn2, " author=emitter" as *u8, star) 150 let a2: i64 = am_axis(auth_emit, auth_all) 151 // A3: daemon-fired pulse runs / all pulse runs 152 let runs_all: i64 = am_lines2(pb, pn, "PULSE-RUN " as *u8, star) 153 var runs_daemon: i64 = am_lines2(db, dn, "DAEMON-BEAT " as *u8, star) 154 if runs_daemon > runs_all { runs_daemon = runs_all } 155 let a3: i64 = am_axis(runs_daemon, runs_all) 156 // A4: ENG- defect rows healed hands-off (DONE with MARK) / ENG- rows DONE 157 let eng_done: i64 = am_lines2(qb, qn, "ENG-" as *u8, "\tDONE\t" as *u8) 158 let eng_mark: i64 = am_lines2(qb, qn, "ENG-" as *u8, "||MARK=" as *u8) 159 var eng_mark_done: i64 = eng_mark 160 if eng_done < eng_mark_done { eng_mark_done = eng_done } 161 let a4: i64 = am_axis(eng_mark_done, eng_done) 162 // Average ONLY the axes that could be scored. A fixed /4 divisor silently treats an absent 163 // instrument as a measured zero; this counts what was actually measurable and publishes the 164 // count, so a reader can tell a low grade from a partly-blind one. 165 var sum: i64 = 0 166 var known: i64 = 0 167 if a1 != AM_AXIS_UNKNOWN { sum = sum + a1; known = known + 1 } 168 if a2 != AM_AXIS_UNKNOWN { sum = sum + a2; known = known + 1 } 169 if a3 != AM_AXIS_UNKNOWN { sum = sum + a3; known = known + 1 } 170 if a4 != AM_AXIS_UNKNOWN { sum = sum + a4; known = known + 1 } 171 // Zero scorable axes is INSTRUMENT-BLIND, not a grade of 0. Refuse rather than publish. 172 if known == 0 { 173 _p(" AUTMETER INSTRUMENT-BLIND: no axis had any data -- refusing to publish a grade\n" as *u8) 174 let bfd: i64 = sys_openat_append(olp, 0x1a4) 175 if bfd >= 0 { _fp(bfd, "AUTMETER epoch=" as *u8); _fn(bfd, sys_now_realtime_sec()); _fp(bfd, " verdict=INSTRUMENT-BLIND axes_known=0 of 4 permil=NA\n" as *u8); sys_close(bfd) } 176 sys_exit(1); return 1 177 } 178 let permil: i64 = sum / known 179 let lfd: i64 = sys_openat_append(olp, 0x1a4) 180 if lfd < 0 { _p(" meter log open failed\n" as *u8); sys_exit(1); return 1 } 181 _fp(lfd, "AUTMETER epoch=" as *u8); _fn(lfd, sys_now_realtime_sec()) 182 _fp(lfd, " a1_judged_done=" as *u8); _fn(lfd, a1) 183 _fp(lfd, " a2_emitter_authored=" as *u8); _fn(lfd, a2) 184 _fp(lfd, " n2=" as *u8); _fn(lfd, auth_all) 185 _fp(lfd, " a3_daemon_runs=" as *u8); _fn(lfd, a3) 186 _fp(lfd, " a4_handsoff_heals=" as *u8); _fn(lfd, a4) 187 _fp(lfd, " n1=" as *u8); _fn(lfd, done_all) 188 _fp(lfd, " n3=" as *u8); _fn(lfd, runs_all) 189 _fp(lfd, " n4=" as *u8); _fn(lfd, eng_done) 190 _fp(lfd, " done_rows=" as *u8); _fn(lfd, done_all) 191 _fp(lfd, " pulse_runs=" as *u8); _fn(lfd, runs_all) 192 _fp(lfd, " permil=" as *u8); _fn(lfd, permil) 193 // DECLARE the scorable-axis count. A grade averaged over 3 of 4 axes is a different claim from 194 // one averaged over 4, and a reader that cannot see the difference cannot tell a genuinely low 195 // score from a partly-blind instrument. -1 in any a<N> field above means THAT AXIS HAD NO DATA. 196 _fp(lfd, " axes_known=" as *u8); _fn(lfd, known) 197 _fp(lfd, " of_4 unscorable=" as *u8); _fn(lfd, 4 - known) 198 _fp(lfd, " grade=" as *u8); am_grade(lfd, permil) 199 if known < 4 { _fp(lfd, " verdict=PARTIAL" as *u8) } 200 _fp(lfd, "\n" as *u8) 201 sys_close(lfd) 202 _p(" AUTMETER permil=" as *u8) 203 let ob: *u8 = sys_mmap(28) 204 var m: i64 = permil 205 var k: i64 = 0 206 if m == 0 { ob[0] = 48 as u8; k = 1 } 207 while m > 0 { ob[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 208 var i: i64 = 0 209 let rb: *u8 = sys_mmap(28) 210 while i < k { rb[i] = ob[k-1-i]; i = i + 1 } 211 sys_write(1, rb, k) 212 _p(" (durable: autonomy_meter.log; the grade moves only when the ledgers move)\n" as *u8) 213 sys_exit(0) 214 return 0 215}