/** * Pain Diagnostic Gate Policy — PRI-446 (migrated from the plugin adapter) * * @deprecated PRI-454 — Gate A (PainDiagnosticGate) is superseded by Gate B * (TriggerController + EvidenceTriage). Do not add new callers. New admission * logic must use `evaluateTriggerController` from runtime-v2/evidence-triage. * * Reality note (PRI-763, 2026-09-12): the `painEvidenceAdmission` / * `painEvidenceAdmissionDefault` flags were RETIRED and REMOVED from the * registry (PRI-763). They never had executable consumers (PRI-651-B1 / * PRI-752 / PRI-762) and Gate B (TriggerController) is the only admission * authority — no runtime routing ever read the flags. This module is * dead-in-production, kept alive only by its own tests and the deprecated * plugin adapter. The module's own retirement (core export surface + policy * + plugin shim) is a separate Owner decision per Phase 5 discipline. * * Pure decision logic for pain-diagnostic cooldown and gate evaluation. * * This module is the single source of truth for the gate's threshold decision * tree. It is fully pure: no I/O, no Date.now(), no module-level mutable state. * All time and cooldown state is passed in as parameters. The plugin-side * pain-diagnostic-gate.ts is now a thin adapter that owns the cooldown Map and * feeds Date.now() / the last-diagnosed timestamp into this function. * * Field precedence / thresholds are copied byte-for-byte from the prior plugin * implementation; the plugin's pain-diagnostic-gate.test.ts (40 tests) passes * unchanged through the adapter as the equivalence proof. * * ERR checklist: * - ERR-001: inputs validated with Number.isFinite, not `as` casts. * - ERR-002: every decision carries reason + detail. * - EP-01: source string normalized with a Set membership check. */ /** Default cooldown window: 15 minutes. */ export declare const DEFAULT_COOLDOWN_MS: number; /** Default pain-trigger threshold. */ export declare const DEFAULT_PAIN_TRIGGER = 40; /** Default high-severity threshold (risky high-score). */ export declare const DEFAULT_HIGH_SEVERITY = 70; /** Default repeated-failure threshold (consecutive errors). */ export declare const DEFAULT_REPEATED_FAILURE = 4; /** Default semantic-pain floor. */ export declare const DEFAULT_SEMANTIC_PAIN_FLOOR = 60; declare const PAIN_DIAGNOSTIC_SOURCES: readonly ['manual', 'tool_failure', 'dispatch_error', 'gate_blocked', 'user_empathy', 'llm_paralysis', 'semantic', 'subagent_error']; export type PainDiagnosticSource = typeof PAIN_DIAGNOSTIC_SOURCES[number]; export type PainDiagnosticGateReason = 'manual' | 'high_gfi' | 'repeated_failure' | 'semantic_pain' | 'llm_paralysis' | 'risky_high_score' | 'subagent_error' | 'gate_blocked' | 'cooldown' | 'below_gate'; export interface PainDiagnosticGateInput { source: PainDiagnosticSource | string; score: number; currentGfi: number; consecutiveErrors?: number; isRisky?: boolean; errorHash?: string; sessionId?: string; nowMs?: number; cooldownMs?: number; thresholds?: { painTrigger?: number; highSeverity?: number; highGfi?: number; repeatedFailure?: number; semanticPain?: number; }; } export interface PainDiagnosticGateDecision { shouldDiagnose: boolean; reason: PainDiagnosticGateReason; episodeKey: string; detail: string; } /** * Normalize a raw source string to a known PainDiagnosticSource. * * Unknown sources are passed through unchanged (the caller may still match them * against custom thresholds) but flagged via the returned `unknown` flag so the * plugin adapter can log it. Core itself does not log. */ export declare function normalizedSource(source: string): { source: PainDiagnosticSource | string; unknown: boolean; }; /** * Build the episode key that scopes cooldown to a (session, source, hash) triple. */ export declare function buildEpisodeKey(input: PainDiagnosticGateInput): string; /** * Evaluate the pure gate decision. * * @param input - gate input (time in nowMs is the caller's responsibility) * @param lastDiagnosedAtMs - the last time this episode was diagnosed, or undefined * if never. Provided by the plugin adapter which owns the cooldown Map. * @returns the gate decision. If shouldDiagnose is true, the caller MUST record * nowMs against this episode (the plugin adapter does this in markDiagnosed). * * Note on the "unknown source" side effect: the prior plugin implementation * logged unknown sources via SystemLogger. Core cannot log, so it surfaces the * unknown flag through normalizedSource; the plugin adapter is responsible for * logging when it observes an unknown source before calling this function. */ export declare function evaluatePainDiagnosticGateDecision(input: PainDiagnosticGateInput, lastDiagnosedAtMs?: number): PainDiagnosticGateDecision; /** * Pure cooldown check for an episode. * * Used by the trigger controller (PEAT-B2) and re-exported by the plugin adapter * as isCooldownActiveForEpisode so its cooldown decision aligns with the gate. */ export interface CooldownCheckInput { /** Source string used to build the episode key. */ readonly source: string; /** Session id used to build the episode key. */ readonly sessionId?: string; /** Error hash used to build the episode key. */ readonly errorHash?: string; /** Cooldown window in ms. Falls back to DEFAULT_COOLDOWN_MS when undefined. */ readonly cooldownMs?: number; /** Current time in ms (caller-injected; core has no clock). */ readonly nowMs: number; /** Last time this episode was diagnosed, or undefined if never. */ readonly lastDiagnosedAtMs?: number; } export declare function isCooldownActive(input: CooldownCheckInput): boolean; export {}; //# sourceMappingURL=pain-diagnostic-gate-policy.d.ts.map