a11oy / packages /policy /src /gates /soundnessAxiom_gate.ts
betterwithage's picture
sync(space): complete build context — fix BUILD_ERROR (CTO)
518343a verified
Raw
History Blame
4.21 kB
// SPDX-License-Identifier: Apache-2.0
// © 2026 Lutar, Stephen P. — SZL Holdings
// ORCID: 0009-0001-0110-4173
//
// Layer 6 — a11oy policy gate for SoundnessAxiom (A1)
//
// Policy rationale:
// A receipt is allowed to pass only when all 9 Λ-axis scores meet or exceed
// the conjunctive floor of 0.90. The soundness axiom guarantees: if gate_pass(r)
// then lambda(r) >= 0.90 conjunctively. This gate enforces that claim at
// the policy layer — rejecting any receipt that would silently under-score.
//
// Lean axiom cited: `soundnessAxiom` (A1)
// Lean file: Lutar/Gate/SoundnessAxiom.lean
// Lean commit SHA: 1dca00032dfc9aa8559cc6c2e4b63192fcf52371
// Lean status: theorem (ENFORCED)
//
// Policy: if all axes >= floor → allow; else → deny
//
// References:
// Zenodo: https://doi.org/10.5281/zenodo.20119582
// Thesis §4.1: soundnessAxiom — conjunctive Λ floor
export interface SoundnessAxiomGateConfig {
/** Conjunctive floor per axis. Default: 0.90. */
floor?: number;
}
export interface SoundnessAxiomGateOpts {
/** Array of 9 axis scores in [0,1]. */
axisScores: number[];
}
export interface SoundnessAxiomDecision {
allow: boolean;
rationale: string;
formula: string;
leanTheorem: string;
leanFile: string;
leanCommitSha: string;
floor: number;
minAxis: number;
failingAxes: number[];
lambdaScore: number;
}
const LEAN_THEOREM = "soundnessAxiom";
const LEAN_FILE = "Lutar/Gate/SoundnessAxiom.lean";
const LEAN_COMMIT = "1dca00032dfc9aa8559cc6c2e4b63192fcf52371";
const DEFAULT_FLOOR = 0.90;
const AXIS_COUNT = 9;
// ── Inline formula ────────────────────────────────────────────────────────────
// A1: gate_pass(r) ⟹ ∀i ∈ {0..8}: lambda_i(r) ≥ 0.90
function _geometricMean(scores: number[]): number {
if (scores.length === 0) return 0;
const logSum = scores.reduce((acc, s) => acc + Math.log(Math.max(s, 1e-12)), 0);
return Math.exp(logSum / scores.length);
}
/**
* SoundnessAxiom (A1) policy gate.
*
* Passes only when every axis in the 9-axis Λ-vector meets or exceeds the
* configured conjunctive floor. This is the primary gating axiom of the
* SZL receipt system.
*
* Lean axiom: `soundnessAxiom` (A1)
* Lean file: Lutar/Gate/SoundnessAxiom.lean (commit 1dca00032dfc9aa8559cc6c2e4b63192fcf52371)
* Zenodo: https://doi.org/10.5281/zenodo.20119582
*/
export function soundnessAxiomGate(
config: SoundnessAxiomGateConfig = {}
): (opts: SoundnessAxiomGateOpts) => SoundnessAxiomDecision {
const floor = config.floor ?? DEFAULT_FLOOR;
if (!Number.isFinite(floor) || floor < 0 || floor > 1) {
throw new Error(`SoundnessAxiomGate: floor must be in [0,1]; got ${floor}`);
}
return function gate(opts: SoundnessAxiomGateOpts): SoundnessAxiomDecision {
const { axisScores } = opts;
if (!Array.isArray(axisScores) || axisScores.length !== AXIS_COUNT) {
throw new Error(`SoundnessAxiomGate: axisScores must be length ${AXIS_COUNT}; got ${axisScores?.length}`);
}
for (const s of axisScores) {
if (!Number.isFinite(s) || s < 0 || s > 1) {
throw new Error(`SoundnessAxiomGate: each axis score must be in [0,1]; got ${s}`);
}
}
const failingAxes = axisScores.map((s, i) => ({ s, i })).filter(x => x.s < floor).map(x => x.i);
const minAxis = Math.min(...axisScores);
const lambdaScore = _geometricMean(axisScores);
const allow = failingAxes.length === 0;
const rationale = allow
? `SoundnessAxiom (A1): all ${AXIS_COUNT} axes ≥ ${floor}; min=${minAxis.toFixed(4)}, Λ=${lambdaScore.toFixed(4)}. Receipt passes. Lean: ${LEAN_THEOREM} @${LEAN_COMMIT.slice(0, 12)}`
: `SoundnessAxiom (A1): axes [${failingAxes.join(',')}] below floor ${floor}; min=${minAxis.toFixed(4)}. Receipt denied. Lean: ${LEAN_THEOREM} @${LEAN_COMMIT.slice(0, 12)}`;
return { allow, rationale, formula: "SoundnessAxiom", leanTheorem: LEAN_THEOREM, leanFile: LEAN_FILE, leanCommitSha: LEAN_COMMIT, floor, minAxis, failingAxes, lambdaScore };
};
}