// SPDX-License-Identifier: Apache-2.0 // © 2026 Lutar, Stephen P. — SZL Holdings // ORCID: 0009-0001-0110-4173 // // Layer 6 — a11oy policy gate for SingleWitnessExclusion (T8) // // Policy rationale: // Different actors with identical digest content produce different SHA-256 // receipt hashes (different canonical JSON → different hash). Single-witness // closure fails for cross-actor pairs. This gate enforces T8: dual witness // is required whenever actor IDs differ. // // Lean derivation cited: `singleWitnessExclusion` (T8) // Lean file: Lutar/Gate/SingleWitnessExclusion.lean // Lean commit SHA: 1dca00032dfc9aa8559cc6c2e4b63192fcf52371 // Lean status: theorem (ENFORCED) // // References: // Zenodo: https://doi.org/10.5281/zenodo.20119582 // INNOVATIONS.md §2 T8: single-witness exclusion export interface SingleWitnessExclusionGateConfig { /** If false, same-actor single-witness is allowed. Default: true (require dual). */ requireDualForSameActor?: boolean; } export interface SingleWitnessExclusionGateOpts { actor1Id: string; actor2Id: string; witnessCount: number; } export interface SingleWitnessExclusionDecision { allow: boolean; rationale: string; formula: string; leanTheorem: string; leanFile: string; leanCommitSha: string; sameActor: boolean; witnessCount: number; dualRequired: boolean; lambdaScore: number; } const LEAN_THEOREM = "singleWitnessExclusion"; const LEAN_FILE = "Lutar/Gate/SingleWitnessExclusion.lean"; const LEAN_COMMIT = "1dca00032dfc9aa8559cc6c2e4b63192fcf52371"; export function singleWitnessExclusionGate( config: SingleWitnessExclusionGateConfig = {} ): (opts: SingleWitnessExclusionGateOpts) => SingleWitnessExclusionDecision { const requireDualForSameActor = config.requireDualForSameActor ?? true; return function gate(opts: SingleWitnessExclusionGateOpts): SingleWitnessExclusionDecision { const { actor1Id, actor2Id, witnessCount } = opts; if (!actor1Id || !actor2Id) throw new Error(`SingleWitnessExclusionGate: actor IDs required`); if (!Number.isInteger(witnessCount) || witnessCount < 0) { throw new Error(`SingleWitnessExclusionGate: witnessCount must be non-negative integer`); } const sameActor = actor1Id === actor2Id; const dualRequired = !sameActor || requireDualForSameActor; const allow = !dualRequired || witnessCount >= 2; const lambdaScore = allow ? 1.0 : witnessCount / 2; const rationale = allow ? `SingleWitnessExclusion (T8): actors=${sameActor ? 'same' : 'different'}; witnesses=${witnessCount} ≥ ${dualRequired ? 2 : 1}. Passes. Lean: ${LEAN_THEOREM} @${LEAN_COMMIT.slice(0, 12)}` : `SingleWitnessExclusion (T8): different actors require dual-witness; got ${witnessCount}. Denied. Lean: ${LEAN_THEOREM} @${LEAN_COMMIT.slice(0, 12)}`; return { allow, rationale, formula: "SingleWitnessExclusion", leanTheorem: LEAN_THEOREM, leanFile: LEAN_FILE, leanCommitSha: LEAN_COMMIT, sameActor, witnessCount, dualRequired, lambdaScore }; }; }