export type FailureSource = 'bugfix-commit' | 'agent-diagnosed' | 'ci' | 'revert' | 'regression' | 'degradation';
export type FailureSeverity = 'low' | 'medium' | 'high';
export type FailureCategory = 'concurrency' | 'config-parse' | 'wiring' | 'logic' | 'migration' | 'test-gap' | 'build-failure' | 'test-failure' | 'regression' | 'unknown';
export type AttributionMode = 'automatic' | 'one-tap' | 'inferred';
export type FailureStatus = 'open' | 'attributed' | 'analyzed' | 'resolved' | 'reopened';
/** Internal-only severity-split detail (spec §4.8). `full` never leaves via HTTP. */
export interface FailureDetail {
    redacted: string;
    full: string;
}
export interface FailureRecord {
    id: string;
    dedupeKey: string;
    occurrenceCount: number;
    detectedAt: string;
    filedBy: string;
    source: FailureSource;
    severity: FailureSeverity;
    summary: string;
    detail: FailureDetail;
    category: FailureCategory;
    initiativeId?: string;
    projectId?: string;
    specPath?: string;
    causeCommitOid?: string;
    fixCommitOid?: string;
    prNumber?: number;
    toolchainRef?: string;
    buildSkill?: string;
    provenance?: 'verified' | 'claimed' | 'unknown';
    attribution: AttributionMode;
    attributionConfidence: number;
    status: FailureStatus;
    learningId?: string;
    createdAt: string;
    updatedAt: string;
    version: number;
}
/** The shape returned over HTTP / to the dashboard — detail.full stripped. */
export interface FailureRecordApiView extends Omit<FailureRecord, 'detail'> {
    detail: {
        redacted: string;
    };
}
export type InsightStatus = 'discovered' | 'acted-on' | 'verified-effective' | 'verified-ineffective' | 'inconclusive' | 'dismissed';
export type VerifiedOutcome = 'pending' | 'effective' | 'ineffective' | 'insufficient-exposure' | 'n/a';
/** A discovered process-gap insight (spec §4.4 / §4.6.1). */
export interface InsightRecord {
    id: string;
    /** Content-stable across analyzer runs (so the same pattern updates, never re-announces). */
    identityKey: string;
    discoveredAt: string;
    summary: string;
    recommendation: string;
    supportingFailureIds: string[];
    distinctSessions: number;
    distinctCauseCommits: number;
    status: InsightStatus;
    actedOnVia?: string;
    verifyWindowStart?: string;
    verifyWindowEnd?: string;
    targetCategory?: string;
    baselineRate?: number;
    reopenCount: number;
    verifiedOutcome: VerifiedOutcome;
    /** True once this insight has been pushed to the system topic (push exactly once). */
    announced: boolean;
    createdAt: string;
    updatedAt: string;
    version: number;
}
export interface UpsertInsightInput {
    identityKey: string;
    summary: string;
    recommendation: string;
    supportingFailureIds: string[];
    distinctSessions: number;
    distinctCauseCommits: number;
    targetCategory?: string;
    baselineRate?: number;
}
export interface OpenFailureInput {
    detectedAt?: string;
    filedBy: string;
    source: FailureSource;
    severity: FailureSeverity;
    summary: string;
    detail: FailureDetail;
    category?: FailureCategory;
    initiativeId?: string;
    projectId?: string;
    specPath?: string;
    causeCommitOid?: string;
    prNumber?: number;
    toolchainRef?: string;
    buildSkill?: string;
    provenance?: 'verified' | 'claimed' | 'unknown';
    attribution: AttributionMode;
    attributionConfidence?: number;
}
export interface ListFilter {
    source?: FailureSource;
    category?: FailureCategory;
    initiativeId?: string;
    attribution?: AttributionMode;
    status?: FailureStatus;
    sinceMs?: number;
    /** Upper-bound (exclusive): detected_at < beforeMs. Drives keyset pagination (Process Health tab §3). */
    beforeMs?: number;
    limit?: number;
}
/** Filter for {@link FailureLedger.listInsights} — mirrors {@link ListFilter}'s before/limit shape. */
export interface InsightListFilter {
    status?: InsightStatus;
    /** Upper-bound (exclusive): discovered_at < beforeMs. */
    beforeMs?: number;
    /** Clamped 50 default / 1000 max (insight-specific default; narrower than list()'s 200). */
    limit?: number;
}
export type UpdateResult = {
    ok: true;
    record: FailureRecord;
} | {
    ok: false;
    conflict: true;
    current?: FailureRecord;
} | {
    ok: false;
    conflict: false;
    reason: string;
};
export type InsightUpdateResult = {
    ok: true;
    record: InsightRecord;
} | {
    ok: false;
    conflict: true;
    current?: InsightRecord;
} | {
    ok: false;
    conflict: false;
    reason: string;
};
export interface DistinctCounts {
    sessions: number;
    causeCommits: number;
}
export interface FailureLedgerOptions {
    dbPath: string;
    /** Stable machine identifier for machine-scoped IDs (spec §4.2 M2). */
    machineId?: string;
    /** Sink for fail-open write errors (defaults to console.error). */
    onError?: (where: string, err: unknown) => void;
    /**
     * Ingestion-sources spec §5: cap on `failure_occurrences` rows kept per
     * dedupeKey. The occurrence table is a bounded FORENSIC log — the analyzer
     * computes diversity from deduped `failure_records`, never from this table —
     * so pruning old rows cannot affect any analysis decision. Default 200.
     */
    maxOccurrencesPerKey?: number;
}
export declare class FailureLedger {
    private db;
    private readonly machineId;
    private readonly onError;
    private readonly maxOccurrencesPerKey;
    constructor(opts: FailureLedgerOptions);
    /** Deterministic dedupe key (spec §4.2 M5). */
    static dedupeKey(source: FailureSource, causeCommitOid: string | undefined, category: FailureCategory): string;
    private nextId;
    /**
     * Open (or upsert) a failure record. A repeat on the same dedupeKey
     * increments occurrenceCount and logs a distinct occurrence rather than
     * duplicating. Fail-open: returns null on any storage error.
     */
    open(input: OpenFailureInput): FailureRecord | null;
    get(id: string): FailureRecord | null;
    getByDedupeKey(dedupeKey: string): FailureRecord | null;
    list(filter?: ListFilter): FailureRecord[];
    /**
     * Mutate a record. `ifMatch` is MANDATORY (spec §4.2 M4) — a stale version
     * loses with {ok:false, conflict:true}. Caller retries (bounded).
     */
    update(id: string, patch: Partial<Pick<FailureRecord, 'status' | 'category' | 'fixCommitOid' | 'learningId' | 'attribution' | 'attributionConfidence' | 'provenance' | 'buildSkill' | 'initiativeId' | 'projectId' | 'specPath' | 'toolchainRef'>>, ifMatch: number): UpdateResult;
    /** Distinct sessions + cause-commits for the diversity gate (spec §4.4). */
    distinctCounts(dedupeKey: string): DistinctCounts;
    /**
     * Raw forensic-log row count (Ingestion-sources spec §5). Observability +
     * test hook for the retention cap — total rows, or rows for one dedupeKey.
     */
    countOccurrences(dedupeKey?: string): number;
    /**
     * Analyzer query path (spec §4.4): indexed group-bys directly in SQL, never a
     * full cache-rebuild + JS filter. Toolchain-blame counts are restricted to
     * `verified`-provenance, `automatic`-attribution records (claimed / one-tap /
     * inferred are excluded from blame aggregates). Also reports coverage so a
     * low-attribution rate reads as low-confidence, not as the rate.
     */
    analyze(opts?: {
        sinceMs?: number;
    }): {
        total: number;
        attributed: number;
        byCategory: Record<string, number>;
        byBuildSkill: Record<string, number>;
        unknownToolchainByAuthor: Record<string, number>;
        noFeatureLink: number;
    };
    /**
     * Upsert an insight keyed on its content-stable identityKey. A re-discovery
     * UPDATES the existing record (refreshes evidence + counts) but PRESERVES its
     * lifecycle (status/actedOnVia/verify window/announced) — so a pattern is
     * never re-announced and an in-flight loop is never reset (spec §4.5 stable-key).
     * Returns the resulting insight (fail-open: null on storage error).
     */
    upsertInsight(input: UpsertInsightInput): InsightRecord | null;
    private nextInsightSeq;
    getInsight(id: string): InsightRecord | null;
    getInsightByIdentity(identityKey: string): InsightRecord | null;
    listInsights(filter?: InsightListFilter): InsightRecord[];
    updateInsight(id: string, patch: Partial<Pick<InsightRecord, 'status' | 'actedOnVia' | 'verifyWindowStart' | 'verifyWindowEnd' | 'targetCategory' | 'baselineRate' | 'reopenCount' | 'verifiedOutcome' | 'announced'>>, ifMatch: number): InsightUpdateResult;
    private rowToInsight;
    /** Strip detail.full — the ONLY shape that may cross an HTTP boundary (spec §4.8). */
    static toApiView(record: FailureRecord): FailureRecordApiView;
    close(): void;
    private rowToRecord;
}
//# sourceMappingURL=FailureLedger.d.ts.map