/** The distilled kind. Validated against this allow-list — the LLM cannot widen it. */
export type CorrectionKind = 'infra-gap' | 'user-preference' | 'noise';
export type CorrectionStatus = 'open' | 'acted-on' | 'verified' | 'inconclusive' | 'reopened';
export interface CorrectionRecord {
    id: string;
    dedupeKey: string;
    kind: CorrectionKind;
    occurrenceCount: number;
    detectedAt: string;
    /** Post-scrubbed distilled lesson. INTERNAL-ONLY — never crosses HTTP. */
    learning: string;
    /** Post-scrubbed one-line summary. The ONLY text the API serves. */
    scrubbedSummary: string;
    /** UTC date bucket (YYYY-MM-DD), code-derived from detectedAt. */
    dayBucket: string;
    /** Layer-0 total weight at record time — the code-determined provenance. */
    deterministicWeight: number;
    /** LLM-assigned confidence [0,1] — ADVISORY, never alone admits a record. */
    llmConfidence: number;
    topicId: number | null;
    sessionId: string | null;
    /** Lifecycle */
    status: CorrectionStatus;
    routedVia?: string;
    verifyWindowStart?: string;
    verifyWindowEnd?: string;
    reopenCount: number;
    createdAt: string;
    updatedAt: string;
    version: number;
}
/** The shape served over HTTP — `learning` stripped, only scrubbed_summary + metadata. */
export interface CorrectionRecordApiView {
    id: string;
    dedupeKey: string;
    kind: CorrectionKind;
    occurrenceCount: number;
    detectedAt: string;
    scrubbedSummary: string;
    dayBucket: string;
    deterministicWeight: number;
    llmConfidence: number;
    topicId: number | null;
    status: CorrectionStatus;
    routedVia?: string;
    verifyWindowStart?: string;
    verifyWindowEnd?: string;
    reopenCount: number;
    createdAt: string;
    updatedAt: string;
    version: number;
}
export interface RecordCorrectionInput {
    kind: CorrectionKind;
    learning: string;
    scrubbedSummary: string;
    deterministicWeight: number;
    llmConfidence?: number;
    topicId?: number | null;
    sessionId?: string | null;
    detectedAt?: string;
}
export interface ListCorrectionFilter {
    kind?: CorrectionKind;
    status?: CorrectionStatus;
    sinceMs?: number;
    /** Upper-bound (exclusive): detected_at < beforeMs (keyset pagination). */
    beforeMs?: number;
    limit?: number;
}
export type CorrectionUpdateResult = {
    ok: true;
    record: CorrectionRecord;
} | {
    ok: false;
    conflict: true;
    current?: CorrectionRecord;
} | {
    ok: false;
    conflict: false;
    reason: string;
};
export interface CorrectionLedgerOptions {
    dbPath: string;
    machineId?: string;
    onError?: (where: string, err: unknown) => void;
    /** Cap on forensic occurrence rows per dedupeKey (default 200). */
    maxOccurrencesPerKey?: number;
}
/** Default forensic-log retention per dedupeKey (mirrors FailureLedger §5). */
export declare const DEFAULT_MAX_OCCURRENCES_PER_KEY = 200;
export declare class CorrectionLedger {
    private db;
    private readonly machineId;
    private readonly onError;
    private readonly maxOccurrencesPerKey;
    constructor(opts: CorrectionLedgerOptions);
    /**
     * Canonical normalization of a distilled learning before hashing: lowercase,
     * strip punctuation, collapse whitespace, drop stop-words, sort the remaining
     * tokens. Two semantically-identical learnings phrased differently collapse to
     * the SAME hash. Stability is a unit-tested invariant (spec §3.4).
     */
    static normalizeLearning(learning: string): string;
    /** SHA-256 over the canonical normalized form. */
    static normalizedLearningHash(learning: string): string;
    /** Deterministic dedupe key: `kind:normalizedLearningHash` (spec §3.4). */
    static dedupeKey(kind: CorrectionKind, learning: string): string;
    /** UTC day bucket (YYYY-MM-DD), code-derived from an ISO timestamp. */
    static dayBucketOf(isoOrMs: string | number): string;
    private nextId;
    /**
     * Record (or upsert) a distilled correction. A repeat on the same dedupeKey
     * increments occurrenceCount + logs a distinct occurrence (feeds the
     * distinct-day / distinct-topic gates) rather than duplicating. Fail-open:
     * returns null on any storage error.
     */
    record(input: RecordCorrectionInput): CorrectionRecord | null;
    get(id: string): CorrectionRecord | null;
    getByDedupeKey(dedupeKey: string): CorrectionRecord | null;
    list(filter?: ListCorrectionFilter): CorrectionRecord[];
    /**
     * Mutate a record's lifecycle. `ifMatch` is mandatory (OCC) — a stale version
     * loses with {ok:false, conflict:true}.
     */
    update(id: string, patch: Partial<Pick<CorrectionRecord, 'status' | 'routedVia' | 'verifyWindowStart' | 'verifyWindowEnd' | 'reopenCount'>>, ifMatch: number): CorrectionUpdateResult;
    /**
     * Distinct calendar days + distinct topics for the recurrence gate (spec §3.5).
     * Computed from the bounded occurrences table. Restart-proof (keys on UTC
     * day_bucket, NOT a session count instar's frequent restarts would inflate).
     * `qualifying` only counts occurrences whose deterministic_weight ≥ threshold
     * (the code-determined provenance filter — an LLM-only-confident occurrence
     * never alone admits a record to the gate).
     */
    distinctCounts(dedupeKey: string, weightThreshold?: number): {
        distinctDays: number;
        distinctTopics: number;
        qualifyingOccurrences: number;
    };
    /** Total deduped record count — health metric for distinct-key growth (spec §3.4). */
    countRecords(): number;
    /** Raw forensic-log row count (observability + retention-cap test hook). */
    countOccurrences(dedupeKey?: string): number;
    /**
     * Names of the indexes present on the occurrences table — observability + a
     * test hook pinning that the distinct-days composite index (spec §10 Slice-2
     * NEW-4) is actually created. Read-only.
     */
    listOccurrenceIndexes(): string[];
    /** Strip `learning` (raw distilled text) — the ONLY shape allowed over HTTP (spec §3.4). */
    static toApiView(record: CorrectionRecord): CorrectionRecordApiView;
    close(): void;
    private rowToRecord;
}
//# sourceMappingURL=CorrectionLedger.d.ts.map