/** * Unified finding-enforcement policy (change: add-finding-enforcement-policy). * * OpenLore emits governance findings from several deterministic sources — the * pre-flight blast-radius guard, the change-impact certificate, and (this change) * the stale-decision-reference check. Each had grown its OWN config for "should * this block a commit or merely inform?" (`blastRadius.block`, * `impactCertificate.block`), so an operator had to learn N enforcement stories. * * This module is the single source of truth. It decouples two things that should * be separate: * - a finding's *intrinsic severity* — a property of the finding, owned by the * source that computes it (never altered here); * - its *enforcement class* — whether THIS repository wants it to block, owned * by `.openlore/config.json` `enforcement.policy` (a `code → class` map). * * Resolution is a pure, order-independent function with a fixed precedence: * an explicit class > source default. * Most sources default to `advisory`; sources may declare stricter defaults when * an invalid artifact would otherwise be trusted. Deterministic, no LLM (north * star `c6d1ad07`). */ import type { EnforcementClass, EnforcementConfig, BlastRadiusConfig, ImpactCertificateConfig } from '../../../types/index.js'; /** A repository's declared policy after normalization: a clean `code → class` map. */ export type EnforcementPolicy = Record; /** * A governance finding in the shape the policy can govern: a stable `code`, an * intrinsic `severity` (owned by the source — informational here, never used to * decide the class), and enough context to render it. Every finding source maps * its native finding onto this shape before the gate classifies it. */ export interface GovernanceFinding { /** Stable, documented code — the key a declared policy names. */ code: string; /** The emitting source's intrinsic severity. Never altered by the policy. */ severity: 'info' | 'warning' | 'error' | 'critical'; /** Which source produced it (for attribution in gate output). */ source: string; /** The artifact/surface/symbol the finding concerns. */ subject: string; /** Human-readable conclusion. */ message: string; /** Static, source-declared action for this code and subject. */ remediation?: string; /** Stable source-owned discriminator when one code can fire repeatedly for one subject. */ discriminator?: string; /** Grounded source location; line is omitted when the dependency artifact has none. */ location?: { path: string; line?: number; }; /** Governing decision receipt for decision-bound architecture findings. */ decision?: { id: string; title: string; rationale: string; ruleId: string; servedContentMetadata?: { provenance: 'reviewed-corpus' | 'local-unreviewed'; }; }; } /** A finding paired with the enforcement class the policy resolved for it. */ export interface ClassifiedFinding extends GovernanceFinding { enforcementClass: EnforcementClass; /** Present when a `frozen` policy was reconciled against its persisted baseline. */ baselineState?: 'frozen' | 'new'; } /** * The catalogue of stable governance finding codes the policy can name. Every * code a source emits MUST be registered here with its source-declared default * class, so (a) a declared policy that names an unknown code can be flagged, and * (b) the catalogue is the documented contract for what an operator may govern. * * `defaultClass` is source-owned. Most findings are advisory by default, while * corpus resolution and graph-shape failures are blocking because an invalid * governance graph must not be trusted as authoritative. */ export interface FindingCodeSpec { defaultClass: EnforcementClass; source: string; description: string; /** Static template; `{subject}` is replaced literally with the finding subject. */ remediation?: string; } /** Finding codes emitted by the deterministic corpus-intent delta reviewer. */ export declare const CORPUS_INTENT_FINDING_CODES: readonly ["corpus-normative-weakened", "corpus-scenario-removed", "corpus-requirement-removed", "corpus-specificity-lost", "corpus-boundary-clause-removed", "corpus-decision-status-regressed", "corpus-delta-orphaned"]; export declare const ARCHITECTURE_FINDING_CODES: readonly ["architecture-layer-violation", "architecture-forbidden-dependency", "architecture-allowed-only-violation", "architecture-required-missing", "architecture-cycle", "architecture-unreachable-breach", "architecture-orphan", "architecture-instability-inversion"]; export declare const FINDING_CODE_REGISTRY: Record; /** Whether a code is registered (so a declared policy entry is recognized). */ export declare function isKnownFindingCode(code: string): boolean; /** The source-declared default class for a code (`advisory` if unregistered). */ export declare function sourceDefaultClass(code: string): EnforcementClass; /** * The pure precedence core. Given the policy's explicit class for a code (if any) * and the source-declared default, pick the effective class: * explicit class > source default. * Order-independent and total. Exposed separately so the precedence is unit-tested * directly, including a source default of `blocking` (which no current code uses). */ export declare function applyPolicyPrecedence(explicit: EnforcementClass | undefined, sourceDefault: EnforcementClass): EnforcementClass; /** * Resolve the enforcement class for a finding. Pure function of the finding's * `code`, the declared `policy`, and its intrinsic `severity`. The severity is * NOT used to decide the class (the policy owns enforcement, the source owns * severity) — it is part of the signature so the contract is explicit and a * future severity-aware default is expressible without a signature change. * Identical inputs produce identical output regardless of policy declaration order. */ export declare function resolveEnforcementClass(code: string, policy: EnforcementPolicy | undefined, _severity?: string): EnforcementClass; /** * Normalize a raw `enforcement` config block into a clean policy map. Tolerant by * design (config is untrusted): a non-object block, non-object `policy`, or any * entry whose value is not a valid class is dropped. Never throws — a malformed * policy degrades to "no policy declared," preserving current behavior. Unknown * codes are RETAINED (a policy may name a code before its source ships); use * {@link unknownPolicyCodes} to surface them as non-failing findings. */ export declare function normalizeEnforcementPolicy(raw: EnforcementConfig | undefined): EnforcementPolicy; /** Codes named by a declared policy that no installed source emits (sorted, stable). */ export declare function unknownPolicyCodes(policy: EnforcementPolicy): string[]; /** * Lower the legacy per-surface `block: [...]` configs onto unified policy entries, * so a `blastRadius.block` / `impactCertificate.block` declaration resolves * identically to the equivalent `enforcement.policy`. The legacy sugar is a thin * equivalent of, and is superseded by, the unified policy. Returns only the * lowered entries; callers merge them UNDER an explicit `enforcement.policy` so a * direct policy entry always wins over inherited legacy sugar. */ export declare function lowerLegacyBlockConfig(config: { blastRadius?: BlastRadiusConfig; impactCertificate?: ImpactCertificateConfig; } | null | undefined): EnforcementPolicy; /** * Build the effective policy a gate consults: the lowered legacy `block` sugar * with an explicit `enforcement.policy` layered ON TOP (a direct policy entry * always wins). Both inputs are normalized/tolerant — a malformed config yields an * empty policy, never a throw. */ export declare function effectivePolicy(config: { enforcement?: EnforcementConfig; blastRadius?: BlastRadiusConfig; impactCertificate?: ImpactCertificateConfig; } | null | undefined): EnforcementPolicy; export interface GateResult { /** Every finding with its resolved class, sorted by a stable key. */ classified: ClassifiedFinding[]; blocking: ClassifiedFinding[]; advisory: ClassifiedFinding[]; /** Findings protected by a persisted frozen baseline. */ frozen: ClassifiedFinding[]; /** Deliberately silenced findings — listed as informational, never failing. */ off: ClassifiedFinding[]; /** True when an explicit blocking finding or a frozen-baseline condition fails the gate. */ gated: boolean; } /** * Classify every finding through the policy and partition by class. The gate fails * (`gated`) only when at least one finding resolves to `blocking`. Findings are * sorted by a stable key so identical inputs produce identical output. Pure — no * I/O, no LLM. */ export declare function classifyFindings(findings: readonly GovernanceFinding[], policy: EnforcementPolicy | undefined): GateResult; //# sourceMappingURL=enforcement-policy.d.ts.map