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}