a11oy / packages /policy /src /gates /adversarialRobustness_gate.ts
betterwithage's picture
sync(space): complete build context — fix BUILD_ERROR (CTO)
518343a verified
Raw
History Blame
5.3 kB
// SPDX-License-Identifier: Apache-2.0
// © 2026 Lutar, Stephen P. — SZL Holdings
// ORCID: 0009-0001-0110-4173
//
// Layer 6 — a11oy policy gate for AdversarialRobustness
//
// Policy rationale:
// A composed pipeline is allowed to deploy only when the end-to-end
// adversarial output perturbation bound ε₂ = L₁·L₂·δ is at or below the
// configured maximum tolerable perturbation. The Lean theorem proves that
// if each component is (δ,ε)-robust, the composition is (δ,ε₂)-robust.
// This gate enforces that the composed system does not amplify adversarial
// inputs beyond the policy tolerance.
//
// Lean theorem cited: `robustness_preserved_by_composition`
// Lean file: Lutar/Composition/AdversarialRobustness.lean
// Lean commit SHA: 1dca00032dfc9aa8559cc6c2e4b63192fcf52371
//
// Policy: if adversarialRobustness(opts).epsilon2 ≤ maxEpsilon → allow; else → deny
//
// References:
// Lean: szl-holdings/lutar-lean Lutar/Composition/AdversarialRobustness.lean
// Runtime: szl-holdings/ouroboros agentic/formulas/adversarialRobustness.ts
// Madry et al. 2018, arXiv:1706.06083
export interface PolicyDecision {
allow: boolean;
rationale: string;
formula: string;
leanTheorem: string;
leanFile: string;
leanCommitSha: string;
epsilon2: number;
composedLipschitz: number;
maxEpsilon: number;
lambdaScore: number;
}
export interface AdversarialRobustnessGateConfig {
/**
* Maximum tolerable ε₂ (end-to-end output perturbation).
* Default: 1.0 (unit ball tolerance).
*/
maxEpsilon?: number;
}
export interface AdversarialRobustnessGateOpts {
lipschitz1: number;
lipschitz2: number;
delta: number;
}
const LEAN_THEOREM = "robustness_preserved_by_composition";
const LEAN_FILE = "Lutar/Composition/AdversarialRobustness.lean";
const LEAN_COMMIT = "1dca00032dfc9aa8559cc6c2e4b63192fcf52371";
const DEFAULT_EPS = 1.0;
// ── Inline formula ────────────────────────────────────────────────────────────
// Lean: robustness_preserved_by_composition:
// IsRobust mX mY f δ ε₁ → IsRobust mY mZ g ε₁ ε₂ → IsRobust mX mZ (f∘g) δ ε₂
//
// Note (PhD audit 2026-05-29): the Lean theorem is stated for abstract metric
// spaces and does not require Lipschitz structure. This gate computes the
// Lipschitz special case: if S₁ has Lipschitz constant L₁, then ε₁ = L₁·δ; if
// S₂ has Lipschitz constant L₂, then ε₂ = L₂·ε₁ = L₁·L₂·δ. Consumers that need
// non-Lipschitz robustness certificates should instantiate the Lean theorem
// directly with their metric model rather than treating this numeric gate as
// the full theorem.
function _composedEpsilon(l1: number, l2: number, delta: number): { epsilon2: number; composedLipschitz: number } {
return { epsilon2: l1 * l2 * delta, composedLipschitz: l1 * l2 };
}
/**
* AdversarialRobustness policy gate.
*
* Allows pipeline deployment only when the composed adversarial output
* perturbation bound ε₂ ≤ maxEpsilon.
*
* Lean theorem: `robustness_preserved_by_composition`
* Lean file: Lutar/Composition/AdversarialRobustness.lean (commit 1dca00032dfc9aa8559cc6c2e4b63192fcf52371)
*/
export function adversarialRobustnessGate(
config: AdversarialRobustnessGateConfig = {}
): (opts: AdversarialRobustnessGateOpts) => PolicyDecision {
const maxEpsilon = config.maxEpsilon ?? DEFAULT_EPS;
if (!Number.isFinite(maxEpsilon) || maxEpsilon < 0) {
throw new Error(`AdversarialRobustnessGate: maxEpsilon must be ≥ 0; got ${maxEpsilon}`);
}
return function gate(opts: AdversarialRobustnessGateOpts): PolicyDecision {
const { lipschitz1, lipschitz2, delta } = opts;
if (lipschitz1 <= 0 || !Number.isFinite(lipschitz1)) {
throw new Error(`AdversarialRobustnessGate: lipschitz1 must be > 0; got ${lipschitz1}`);
}
if (lipschitz2 <= 0 || !Number.isFinite(lipschitz2)) {
throw new Error(`AdversarialRobustnessGate: lipschitz2 must be > 0; got ${lipschitz2}`);
}
if (delta <= 0 || !Number.isFinite(delta)) {
throw new Error(`AdversarialRobustnessGate: delta must be > 0; got ${delta}`);
}
const { epsilon2, composedLipschitz } = _composedEpsilon(lipschitz1, lipschitz2, delta);
const lambdaScore = 1 / (1 + epsilon2);
const allow = epsilon2 <= maxEpsilon;
const rationale = allow
? `AdversarialRobustness ε₂ = ${epsilon2.toExponential(4)} ≤ maxEpsilon ${maxEpsilon}: ` +
`composed pipeline is (${delta},${epsilon2})-robust. Lean: ${LEAN_THEOREM} @${LEAN_COMMIT.slice(0, 12)}`
: `AdversarialRobustness ε₂ = ${epsilon2.toExponential(4)} > maxEpsilon ${maxEpsilon}: ` +
`perturbation amplification exceeds policy tolerance — deny deployment. Lean: ${LEAN_THEOREM} @${LEAN_COMMIT.slice(0, 12)}`;
return {
allow,
rationale,
formula: "AdversarialRobustness",
leanTheorem: LEAN_THEOREM,
leanFile: LEAN_FILE,
leanCommitSha: LEAN_COMMIT,
epsilon2,
composedLipschitz,
maxEpsilon,
lambdaScore,
};
};
}