/**
 * ReleaseReadinessSentinel — Layer B of the release-readiness-visibility spec.
 *
 * Watches whether a release is blocked/stalling and makes it impossible to miss.
 * The original incident (2026-05-27): npm publishing silently stalled since
 * v1.3.26 — green merges, green CI, no published release, no alert. The publish
 * workflow's NEXT.md gate is a *silent skip*: a stalled release is
 * indistinguishable from "nothing to publish". This sentinel is the missing
 * visibility — a recurring, near-silent check that surfaces a stuck release as
 * a single, deduped, escalating Attention item.
 *
 * Design (spec §4.2):
 *   - Reads canonical `main` (FETCH_HEAD), not the local checkout, so a stale
 *     dev branch can't produce a false "all clear".
 *   - "Blocked" is decoupled from NEXT.md state (so Layer A's auto-draft can't
 *     silence the alarm by clearing the file): unreleased feature/fix commits
 *     exist AND the guide can't publish (missing/template/unreviewed), OR the
 *     analyzer reports critical/high coverage gaps.
 *   - Near-silent: silent below threshold (state + log only); ONE Attention
 *     item per stall episode above it, keyed on the OLDEST unreleased commit
 *     SHA (stable across ticks — not a resettable per-tick id), priority scaled
 *     by backlog age, 12h hysteresis on re-raise after an auto-resolve.
 *   - Fail-loud: any evaluation failure (fetch error, analyzer error) raises a
 *     low-priority Attention item — never a silent catch (that would re-create
 *     the exact bug this fixes).
 *   - Lifecycle owner: detect → surface → auto-resolve → reap, with
 *     resolveEpisodesInRange consulted by the publish-finalize path.
 *   - Repo-gated: needs an analyzable instar git repo (dev/maintainer env). On
 *     an npm-installed agent with no such repo the deps make the check inert.
 *   - Supervision: Tier 0 (mechanical computation + fixed-template wording).
 */
import { EventEmitter } from 'node:events';
/** Parsed subset of `analyze-release.js --json` the sentinel needs. */
export interface AnalyzerReport {
    lastTag: string;
    commitCount: number;
    analysis: {
        commitClassification: {
            features: number;
            fixes: number;
        };
    };
    guideCoverage: {
        criticalGaps: number;
        highGaps: number;
    };
}
export interface OldestCommit {
    sha: string;
    dateMs: number;
}
export interface ReadinessEpisode {
    /** Stable key: SHA of the oldest unreleased commit at first detection. */
    oldestSha: string;
    firstDetectedMs: number;
    /** Set when an Attention item was raised (age crossed the threshold). */
    openedMs?: number;
    attentionId?: string;
    lastPriority?: ReadinessPriority;
    resolvedMs?: number;
    resolvedReason?: 'published' | 'stale' | 'rolled-back' | 'cleared';
}
export interface ReadinessState {
    episodes: ReadinessEpisode[];
    /** Episodes resolved recently — used for the re-raise hysteresis window. */
    recentResolves: Array<{
        oldestSha: string;
        resolvedMs: number;
    }>;
    /** Last failure-episode key we signaled, to dedupe fail-loud notices. */
    lastFailureKey?: string;
    lastTickAt?: number;
    lastSignalAt?: number;
    cacheHeadSha?: string;
    canonicalRemoteOverridden?: boolean;
    /** Runtime kill-switch set by POST /release-readiness/rollback. When true,
     *  tick() no-ops without evaluating. Cleared by /release-readiness/enable. */
    disabled?: boolean;
    rollbackHistory?: Array<{
        ts: number;
        sessionId?: string;
        sourceIp?: string;
        reason?: string;
    }>;
}
export type ReadinessPriority = 'LOW' | 'MEDIUM' | 'HIGH';
export interface AttentionItem {
    id: string;
    title: string;
    summary: string;
    category?: string;
    priority?: string;
}
export interface ReleaseReadinessSentinelDeps {
    /** Fetch canonical main; resolve FETCH_HEAD sha. ok:false ⇒ unreachable. */
    fetchCanonical(): Promise<{
        ok: boolean;
        headSha?: string;
    }>;
    /** Run analyze-release.js --json --ref against the canonical ref. null ⇒ error. */
    runAnalyzer(ref: string): Promise<AnalyzerReport | null>;
    /** Oldest unreleased feature/fix commit relative to lastTag..ref. */
    oldestUnreleasedCommit(lastTag: string, ref: string): Promise<OldestCommit | null>;
    /** True when the publish gate would refuse the guide (missing/template/unreviewed). */
    guideBlocksPublish(): Promise<boolean>;
    /** Best-effort: seed NEXT.md via Layer A (analyze-release.js --draft-guide). */
    draftGuide(ref: string): Promise<void>;
    /** Post an Attention item; resolves to true on delivery. */
    postAttention(item: AttentionItem): Promise<boolean>;
    /** Resolve an open Attention item by id. */
    resolveAttention(id: string, reason: string): Promise<boolean>;
    /** Load persisted state (or a fresh empty state). */
    loadState(): ReadinessState;
    /** Persist state atomically. */
    saveState(state: ReadinessState): void;
    /** Is `sha` an ancestor of `ref`? (for resolveEpisodesInRange) */
    isAncestor(sha: string, ref: string): Promise<boolean>;
    /** Append a structured audit line (sentinel-events.jsonl). */
    audit(event: Record<string, unknown>): void;
    now(): number;
}
export interface ReleaseReadinessSentinelConfig {
    enabled?: boolean;
    tickIntervalMs?: number;
    backlogAgeDaysSilent?: number;
    backlogAgeDaysLow?: number;
    backlogAgeDaysMedium?: number;
    backlogAgeDaysHigh?: number;
    hysteresisHours?: number;
    staleEpisodeTtlDays?: number;
}
export declare class ReleaseReadinessSentinel extends EventEmitter {
    private readonly deps;
    private readonly cfg;
    private tickHandle;
    constructor(deps: ReleaseReadinessSentinelDeps, cfg?: ReleaseReadinessSentinelConfig);
    start(): void;
    stop(): void;
    /** Priority for a given backlog age, or null when below the silent threshold. */
    priorityForAge(ageDays: number): ReadinessPriority | null;
    /** The cron entry point. Top-level catch converts any error into a loud signal. */
    tick(): Promise<void>;
    /** Read-only snapshot of current state (for GET /release-readiness). */
    snapshot(): ReadinessState;
    /**
     * Runtime kill-switch (POST /release-readiness/rollback). Disables future
     * ticks, resolves all open Attention items, and is ITSELF loud — raises a
     * HIGH-priority Attention item + audits — so the rollback can never be a
     * silent way to mute the alarm (iter-3 Adversarial V5).
     */
    rollback(meta?: {
        sessionId?: string;
        sourceIp?: string;
    }): Promise<void>;
    /** Re-arm after a rollback. */
    enable(): void;
    private evaluate;
    private isBlocked;
    private raiseGapOnly;
    private failLoud;
    private resolveAll;
    /**
     * Called by the publish-finalize path (and the /rollback route via 'rolled-back').
     * Resolves every open episode whose oldestSha is an ancestor of newTagSha —
     * correct even when the oldest-unreleased SHA churned during the open window.
     */
    resolveEpisodesInRange(newTagSha: string, reason?: ReadinessEpisode['resolvedReason']): Promise<void>;
    /** TTL-reap episodes/resolves so state doesn't grow unbounded. */
    private reap;
    static emptyState(): ReadinessState;
}
//# sourceMappingURL=ReleaseReadinessSentinel.d.ts.map