From 87725c9ac5c9a93b6dcfceb4bdbc35a51b1555bf Mon Sep 17 00:00:00 2001 From: Aaron Stainback Date: Sun, 9 Aug 2026 16:54:35 -0400 Subject: [PATCH] =?UTF-8?q?fix(empowerment):=20restore=20tau=20to=20extern?= =?UTF-8?q?alitySafe=20=E2=80=94=20the=20missing=20parameter=20was=20a=20c?= =?UTF-8?q?onsent=20inversion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Soraya's P0 #1. The shipped predicate DROPPED the tau parameter the design specifies and hardcoded tau = 0, which reads a bystander's SILENCE AS CONSENT to the maximum harm the predicate can permit. Machine-checked consequences she found (all reproduced here): L4 — a bystander at floor 0.8 hit by -0.7 returned TRUE (0.1 >= 0): reported safe while pushed far below their own floor. L3 — mu=.95/sigma=.01 with delta=-0.9 returned TRUE: a 90% reach reduction called safe. L2 — vacuously true for every delta >= 0, and EB-8 asserts exactly that branch, so the test CANNOT FAIL. Fix per the boxing-ring table (Aaron 2026-08-09): bystander (no declared tau) -> ANY harm fails; they are the audience, not in the ring. entered the ring (declared tau) -> harm down to their declared tau is permitted. The default is the party's OWN pre-interaction floor rather than a magic constant, so the predicate reduces to `harm >= 0` and "no harm to a bystander" is true BY CONSTRUCTION. A permissive tau must be passed explicitly; it can never be inferred. Two other review findings addressed in place: - Math.min(0, delta) KEPT, with the missing justification written down: a claimed benefit to a non-consenting party is an unverified assertion about someone not at the table, so letting +0.3 offset -0.3 would let the pair self-certify a compensation the bystander never agreed to. - The floor+delta expression is now LABELLED A PROXY, naming both defects honestly: the real property needs posterior_after(third, interaction), which DOES NOT EXIST, and the addition mixes a calibration-score bound with a reach quantity (the lbf-vs-N shape). Tolerated only because the alternative is no bystander check at all — marked so the next reader does not mistake it for the real operator. (D_f 1.322 lesson.) Six tests added (EB-11..EB-16) locking the corrected semantics, including Soraya's L3/L4 counterexamples verbatim. IMPORTANT: the 10 pre-existing tests ALL still passed after a material semantic change, which is itself the evidence they were self-certifying. So I verified the new ones are not: restoring the old `tau ?? 0` makes 4 of the 6 FAIL, and the fix makes them pass again. NOT fixed here (left for Lumen, deeper in their design): the cross-aggregator gain comparison that lets a `sum` interaction outrank a harmless `min` one. Aaron's framing reclassifies it as a punch thrown OUTSIDE the ring — an entry-control failure, so the fix is not rescaling; a sacrificing interaction must not be selectable at all against parties who have not entered. Also NOT added: a capacity predicate — no wallet exists in-tree, so one would silently pass everyone. Validation: 16/16 empowerment tests, tsc 0 errors. Co-Authored-By: Claude Opus 5 (1M context) --- .../planning/empowerment-bound.test.ts | 52 +++++++++++++++++++ .../planning/empowerment-bound.ts | 41 +++++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/Core.TypeScript/planning/empowerment-bound.test.ts b/src/Core.TypeScript/planning/empowerment-bound.test.ts index 53badf8e51..0fd7b196de 100644 --- a/src/Core.TypeScript/planning/empowerment-bound.test.ts +++ b/src/Core.TypeScript/planning/empowerment-bound.test.ts @@ -108,6 +108,58 @@ describe("EmpowermentBound", () => { }); // EB-10: empowermentBound returns null for empty interaction list + // ── 081KZKYDJ9Q: the τ / consent-inversion fix (Soraya's P0) ──────────────── + // + // These lock behaviour the previous tests could not see. A material semantic + // change to externalitySafe left all 10 originals green, which is itself the + // evidence that they were self-certifying (Soraya: EB-8 asserts the vacuous + // `delta >= 0` branch and therefore cannot fail). + // + // Semantics under test — the boxing-ring table: + // bystander (no declared τ) -> ANY harm fails; they are the audience. + // entered the ring (declared τ) -> harm down to their declared τ is permitted. + + test("EB-11: Soraya L4 — a bystander at floor 0.8 hit by −0.7 is NOT safe", () => { + // The shipped version returned TRUE here (0.8 − 0.7 = 0.1 >= 0), reporting a + // bystander pushed far below their own floor as safe. That was the consent + // inversion: τ hardcoded to 0 reads silence as consent to maximal harm. + expect(externalitySafe({ mu: 0.8, sigma: 0.0 } as CalibrationPosterior, -0.7)).toBe(false); + }); + + test("EB-12: Soraya L3 — a 90% reach reduction on a well-established bystander is NOT safe", () => { + expect(externalitySafe({ mu: 0.95, sigma: 0.01 } as CalibrationPosterior, -0.9)).toBe(false); + }); + + test("EB-13: ANY harm to a bystander fails, however small (no-harm default)", () => { + // The default τ is the party's own pre-interaction floor, so the predicate + // reduces to harm >= 0. This is the property EB-9 gestured at but did not pin. + expect(externalitySafe({ mu: 0.5, sigma: 0.1 } as CalibrationPosterior, -0.01)).toBe(false); + }); + + test("EB-14: zero externality is safe; benefit is safe but carries no credit", () => { + const p = { mu: 0.5, sigma: 0.1 } as CalibrationPosterior; + expect(externalitySafe(p, 0)).toBe(true); + // Benefit is clamped away rather than offsetting: a claimed benefit to someone + // not at the table is an unverified assertion, so it may not buy harm elsewhere. + expect(externalitySafe(p, 0.5)).toBe(true); + }); + + test("EB-15: a DECLARED permissive τ permits harm — entering the ring is the only way in", () => { + const p = { mu: 0.5, sigma: 0.1 } as CalibrationPosterior; + // Same harm that fails for a bystander (EB-13 shape) succeeds once τ is declared. + expect(externalitySafe(p, -0.4)).toBe(false); + expect(externalitySafe(p, -0.4, 3, -1)).toBe(true); + }); + + test("EB-16: τ is never inferred — the permissive path requires an explicit argument", () => { + // Guards the regression that caused this: a missing parameter silently became + // τ = 0. If the default ever stops meaning "the party's own floor", EB-13 and + // this test fail together rather than passing vacuously. + const p = { mu: 0.9, sigma: 0.0 } as CalibrationPosterior; + expect(externalitySafe(p, -0.5)).toBe(false); // no τ declared -> refused + expect(externalitySafe(p, -0.5, 3, 0.0)).toBe(true); // τ = 0 declared -> permitted + }); + test("EB-10: empowermentBound returns null for empty interaction list", () => { const self = makePosterior(0.8); const other = makePosterior(0.8); diff --git a/src/Core.TypeScript/planning/empowerment-bound.ts b/src/Core.TypeScript/planning/empowerment-bound.ts index f613c11ca0..80bfa03472 100644 --- a/src/Core.TypeScript/planning/empowerment-bound.ts +++ b/src/Core.TypeScript/planning/empowerment-bound.ts @@ -240,9 +240,44 @@ export function externalitySafe( thirdPartyPosterior: CalibrationPosterior, externalityDelta: number, kTrust = 3, + // 081KZKYDJ9Q — the τ this predicate must compare against. It was MISSING, and its + // absence hardcoded τ = 0, which Soraya's review named a CONSENT INVERSION: it reads + // a bystander's silence as consent to the maximum harm the predicate can permit. + // + // The default is the BYSTANDER row of the boxing-ring table: no harm. A party that + // has not entered the ring is the audience, and the audience does not get punched. + // A party that HAS entered may declare a permissive (negative) τ — that is the whole + // point of the ring — but it must be *declared*, never inferred from silence. + // + // Ref: docs/research/2026-08-09-the-boxing-ring-consent-capacity-floor-values-arise-naturally-aaron.md + // Omitted ⇒ the party's OWN pre-interaction floor (computed below), which makes + // "no harm" true by construction rather than by choosing a magic constant. + tauThirdParty?: number, ): boolean { + // Only harm counts. A claimed BENEFIT to a non-consenting party is an unverified + // assertion made about someone who is not at the table — letting +0.3 offset −0.3 + // would let the interacting pair self-certify a compensation the bystander never + // agreed to (paternalistic benefit). Keep the clamp; this comment is the "why" that + // Soraya's review found missing. + const harm = Math.min(0, externalityDelta); + + // ⚠ PROXY — labelled, per the D_f 1.322 lesson (a proxy that stops being called one + // becomes a fabricated measurement). The property we want is + // trustBound(posterior_after(third, interaction), k) >= tau + // but `posterior_after` DOES NOT EXIST: there is no operator mapping an interaction + // to a change in a bystander's calibration posterior. Until one exists we approximate + // it as floor + harm, which is a UNITS APPROXIMATION as well as a temporal one — + // `floor` is a calibration-score bound while `externalityDelta` is a reach + // (option-space) quantity. Adding them is the lbf-vs-N shape; it is tolerated here + // ONLY because the alternative is shipping no bystander check at all, and it is + // marked so the next reader does not mistake it for the real operator. const floor = trustBound(thirdPartyPosterior, kTrust); - const afterFloor = floor + Math.min(0, externalityDelta); - // Safe if the third party's floor is not pushed below 0 - return afterFloor >= 0; + const floorAfterProxy = floor + harm; + + // Default τ = the party's own pre-interaction floor, so the predicate reduces to + // `harm >= 0` — i.e. ANY harm to a bystander fails. A declared τ below that floor is + // how a party that entered the ring permits harm to itself; it can never be inferred. + const tau = tauThirdParty ?? floor; + + return floorAfterProxy >= tau; }