code wiki / (root) / nx_agreebound.nx

nx_agreebound.nx source

↩ module page · 148 lines · 7871 B

1// nx_agreebound.nx -- WRITING arc, rung W-AGB-1: THE PANEL-RELIABILITY BOUND. 2// 3// Closes gap-queue rows #1 and #2 of nx_writebench in ONE rung, because they are two facets of a 4// single missing thing -- a mechanical answer to "is this verdict reliable enough to ACT on?": 5// #1 multi-detector-disagreement-bound -- ruler: detectors disagree 3.9x on violence, 3.1x on 6// insult, 1.45x on sexual, ON THE SAME CORPUS. A one-detector rule is therefore a coin flip, 7// and any single detector's rate is an INTERVAL, never a point. 8// #2 judge-reliability-alpha-floor -- ruler Table 1: LLM-vs-human Krippendorff alpha collapses 9// to 0.24 (violence) / 0.43 (insult) ON MODEL OUTPUT, well under the 0.6 substantial floor. 10// 11// ★★THE LAW THIS MECHANIZES: a panel that cannot agree cannot license enforcement. The estate has 12// been bitten repeatedly by a single confident judge (the editjudge that blessed its own mask, DEC026; 13// the compliance metric that ROSE while the picture got worse). This organ refuses to let ONE score 14// authorize an action -- structurally, at ndet<2, no matter how good that score looks. 15// 16// ★WHAT IT IS NOT: this is a RELIABILITY meter, not a content filter, and it never scores content 17// itself. It takes ratings other organs produced and answers how much they can be trusted. It has no 18// opinion about what a consenting adult asks for -- consistent with [[nx_dialogue]], the only lines 19// are legal ones, enforced upstream by rg_decision's age/consent/locale gate. A low-reliability panel 20// downgrades to REPORT-ONLY; it never escalates into a refusal it could not justify. 21// 22// ★THRESHOLDS ARE PARAMETERS (rule 11): every floor/cap is an argument, with the ruler-derived 23// defaults below named and cited. A caller tightening a bound edits DATA, never this organ. 24// 25// Krippendorff nominal alpha, EXACT INTEGER (no float anywhere), for an equal-raters-per-unit panel: 26// D_pairs = sum over units of (n_u^2 - sum_c n_uc^2) (ordered disagreeing pairs) 27// E_pairs = n^2 - sum_c n_c^2 (expected, from the marginals) 28// alpha = 1 - [D_pairs/(m-1)] * [(n-1)/E_pairs] 29// => alpha_permil = 1000 - (1000 * D_pairs * (n-1)) / ((m-1) * E_pairs) 30// Signed: alpha BELOW zero means worse than chance, and is reported as such, never clamped to 0. 31// 32// Pure integer, NO syscalls, caller owns every buffer -- same contract as nx_dialogue/nx_register. 33// license_tier: ORIGINAL 34// module: nishi-core.write.agreebound 35// depends: (none -- pure) 36// capability: WRITE_PANEL_RELIABILITY 37 38// Sentinels, deliberately outside any legal permil range so a caller cannot mistake one for a score. 39const AB_UNDEFINED: i64 = 0 - 1000000 // alpha undefined (m<2, or ZERO variation => De=0) 40const AB_RATIO_UNBOUNDED: i64 = 0 - 1 // a detector fired and another never did => ratio is infinite 41 42// Enforcement verdicts. 43const AB_ENFORCE: i64 = 0 // panel is reliable enough to act on 44const AB_REPORT_ONLY: i64 = 1 // measure and publish, but DO NOT auto-enforce 45const AB_REFUSE_SINGLE: i64 = 2 // structurally a coin flip: fewer than two independent detectors 46 47// Ruler-derived DEFAULTS (cited, overridable by argument -- never hardcoded into a decision): 48// floor 600 = Krippendorff's "substantial" 0.6, the same bar the ruler measures itself against. 49// cap 2000 = 2.0x. The ruler's own spread runs 1.45x (sexual) to 3.9x (violence); a panel wider 50// than 2x is reporting two different phenomena, not one measurement with noise. 51const AB_ALPHA_FLOOR_DEFAULT: i64 = 600 52const AB_RATIO_CAP_DEFAULT: i64 = 2000 53const AB_PERMIL: i64 = 1000 54 55// --- multi-detector disagreement (row #1) ------------------------------------------------------- 56 57// The LOW end of the interval a single detector's rate must be reported as. rates = n detector 58// rates over the SAME corpus (any unit -- permil, percent, raw counts -- so long as they share one). 59func ab_span_lo(rates: *i64, n: i64) -> i64 { 60 if n <= 0 { return 0 } 61 var lo: i64 = rates[0] 62 var i: i64 = 1 63 while i < n { if rates[i] < lo { lo = rates[i] } i = i + 1 } 64 return lo 65} 66 67// The HIGH end of that interval. 68func ab_span_hi(rates: *i64, n: i64) -> i64 { 69 if n <= 0 { return 0 } 70 var hi: i64 = rates[0] 71 var i: i64 = 1 72 while i < n { if rates[i] > hi { hi = rates[i] } i = i + 1 } 73 return hi 74} 75 76// max/min as permil (1000 = perfect agreement, 3900 = the ruler's 3.9x violence spread). 77// All-zero panel = agreement at zero => 1000, NOT a divide trap. Some-zero panel = UNBOUNDED: 78// one detector finding nothing where another finds something is not a ratio, it is a contradiction. 79func ab_ratio_permil(rates: *i64, n: i64) -> i64 { 80 if n <= 0 { return AB_PERMIL } 81 let lo: i64 = ab_span_lo(rates, n) 82 let hi: i64 = ab_span_hi(rates, n) 83 if hi == 0 { return AB_PERMIL } 84 if lo <= 0 { return AB_RATIO_UNBOUNDED } 85 return (AB_PERMIL * hi) / lo 86} 87 88// --- Krippendorff nominal alpha (row #2) -------------------------------------------------------- 89 90// ordered disagreeing pairs inside ONE unit, from its per-category counts. 91func ab_unit_disagree(counts: *i64, ncat: i64) -> i64 { 92 var tot: i64 = 0 93 var sq: i64 = 0 94 var c: i64 = 0 95 while c < ncat { tot = tot + counts[c]; sq = sq + counts[c] * counts[c]; c = c + 1 } 96 return tot * tot - sq 97} 98 99// ratings = flat nunits*nraters array of category ids in [0, ncat). Equal raters per unit. 100// marg + cnt = CALLER-OWNED scratch, ncat slots each (the house contract: a pure organ allocates 101// nothing, so it can be called from a gate, a daemon or a kernel-side context identically). 102// Returns alpha in permil, or AB_UNDEFINED when it is not defined (fewer than 2 raters, no units, 103// or ZERO variation across the whole panel -- where De=0 and "perfect agreement" is a claim the 104// statistic cannot make. Fabricating 1000 there would be the blank-decoy trap: two empty planes 105// scoring a perfect match). 106func ab_alpha_permil(ratings: *i64, nunits: i64, nraters: i64, ncat: i64, marg: *i64, cnt: *i64) -> i64 { 107 if nraters < 2 { return AB_UNDEFINED } 108 if nunits <= 0 { return AB_UNDEFINED } 109 if ncat <= 0 { return AB_UNDEFINED } 110 var c: i64 = 0 111 while c < ncat { marg[c] = 0; c = c + 1 } 112 var dpairs: i64 = 0 113 var u: i64 = 0 114 while u < nunits { 115 var k: i64 = 0 116 while k < ncat { cnt[k] = 0; k = k + 1 } 117 var r: i64 = 0 118 while r < nraters { 119 let v: i64 = ratings[u * nraters + r] 120 if v >= 0 { if v < ncat { cnt[v] = cnt[v] + 1; marg[v] = marg[v] + 1 } } 121 r = r + 1 122 } 123 dpairs = dpairs + ab_unit_disagree(cnt, ncat) 124 u = u + 1 125 } 126 var n: i64 = 0 127 var sq: i64 = 0 128 c = 0 129 while c < ncat { n = n + marg[c]; sq = sq + marg[c] * marg[c]; c = c + 1 } 130 if n < 2 { return AB_UNDEFINED } 131 let epairs: i64 = n * n - sq 132 if epairs <= 0 { return AB_UNDEFINED } 133 return AB_PERMIL - (AB_PERMIL * dpairs * (n - 1)) / ((nraters - 1) * epairs) 134} 135 136// --- the joint decision ------------------------------------------------------------------------- 137 138// ndet = how many INDEPENDENT detectors/judges produced the panel. alpha_permil may be AB_UNDEFINED; 139// ratio_permil may be AB_RATIO_UNBOUNDED. Order matters: the structural refusal is checked FIRST, so 140// a single detector can never buy enforcement with a flattering score. 141func ab_enforce(ndet: i64, alpha_permil: i64, ratio_permil: i64, alpha_floor: i64, ratio_cap: i64) -> i64 { 142 if ndet < 2 { return AB_REFUSE_SINGLE } 143 if alpha_permil == AB_UNDEFINED { return AB_REPORT_ONLY } 144 if alpha_permil < alpha_floor { return AB_REPORT_ONLY } 145 if ratio_permil == AB_RATIO_UNBOUNDED { return AB_REPORT_ONLY } 146 if ratio_permil > ratio_cap { return AB_REPORT_ONLY } 147 return AB_ENFORCE 148}