/**
 * On-disk allowlist file — `.dxkit/allowlist.json` + optional sidecar
 * `.dxkit/allowlist-reasons.local.json`.
 *
 * The allowlist file is the durable contract for per-finding
 * suppressions: customer has reviewed this fingerprint, categorized
 * the reason, and accepts that the guardrail should let the finding
 * pass on future runs.
 *
 * Identity is the 16-char hex fingerprint from
 * `src/analyzers/tools/fingerprint.ts` (CLAUDE.md Rule 9). An entry
 * matches a finding when their fingerprint strings are byte-equal.
 *
 * # Two modes, one schema
 *
 * The mode is recorded in `.dxkit/policy.json` (out of scope for this
 * module — consumers pass it in via `AllowlistMode`). Both modes use
 * the same `AllowlistFile` shape on disk; sanitized mode just drops
 * the human-readable fields and pushes them to a sidecar:
 *
 *   `'full'` — every field present on the entry. Default for
 *   private repos. The committed file carries the full audit trail.
 *
 *   `'sanitized'` — entries carry `fingerprint + kind + category +
 *   addedAt + expiresAt + acknowledgedSeverity` only. The
 *   `reason` + `addedBy` fields live in the gitignored sidecar
 *   `.dxkit/allowlist-reasons.local.json`, keyed by fingerprint.
 *   Default for public repos. Loaders merge the sidecar back when
 *   present; readers tolerate its absence (no reason field, but
 *   the suppression still applies because matching is by
 *   fingerprint).
 *
 * # Validation surface
 *
 * `validateAllowlistFile` enforces every Sprint-0-locked rule:
 *   - `reason` is required (full mode) / required in sidecar
 *     (sanitized mode)
 *   - `category` is one of the five canonical values
 *   - `category` is valid for the entry's `kind`
 *   - `requiresExpiry(category)` ⇒ `expiresAt` is present + parseable
 *   - `acknowledgedSeverity` is required when `category` is
 *     `accepted-risk` AND `severity` is `high`/`critical`
 */
import type { IdentityKind } from '../baseline/producers';
import type { FindingSeverity } from '../baseline/types';
import { type AllowlistCategory } from './categories';
export declare const ALLOWLIST_SCHEMA_VERSION: "dxkit-allowlist/v1";
export type AllowlistSchemaVersion = typeof ALLOWLIST_SCHEMA_VERSION;
export declare const ALLOWLIST_REASONS_SCHEMA_VERSION: "dxkit-allowlist-reasons/v1";
export type AllowlistReasonsSchemaVersion = typeof ALLOWLIST_REASONS_SCHEMA_VERSION;
export declare const ALLOWLIST_DIR = ".dxkit";
export declare const ALLOWLIST_FILENAME = "allowlist.json";
export declare const ALLOWLIST_REASONS_FILENAME = "allowlist-reasons.local.json";
/**
 * Single source of truth for mode values. The `AllowlistMode` union
 * is derived from this array via `(typeof ...)[number]`, so adding
 * a new mode means appending one string here — the runtime checks
 * below pick it up via `ALL_MODES.includes(...)` without any
 * literal-value drift between type and runtime.
 */
export declare const ALL_MODES: readonly ["full", "sanitized"];
export type AllowlistMode = (typeof ALL_MODES)[number];
/**
 * One allowlist entry. Two-shape contract:
 *   - Full mode: every optional field may be present on disk.
 *   - Sanitized mode on disk: `reason` + `addedBy` are absent; the
 *     loader merges them in from the sidecar when present.
 *
 * The TypeScript type allows the union so a single in-memory shape
 * serves both modes; the validator enforces shape invariants.
 */
export interface AllowlistEntry {
    /** SHA-1[0:16] canonical fingerprint matching the target finding. */
    readonly fingerprint: string;
    /** Identity kind of the target finding (matches CLAUDE.md Rule 9
     *  canonical kinds). */
    readonly kind: IdentityKind;
    /** Suppression category — one of the five Sprint-0-locked enum
     *  values. */
    readonly category: AllowlistCategory;
    /** Human-readable rationale. Required in full mode; lives in
     *  sidecar in sanitized mode. */
    readonly reason?: string;
    /** Who added the entry. Required in full mode; lives in sidecar
     *  in sanitized mode. Free-form (typically email, git user, or
     *  Slack handle). */
    readonly addedBy?: string;
    /** ISO `YYYY-MM-DD` of when the entry was added. */
    readonly addedAt: string;
    /** ISO `YYYY-MM-DD` after which the entry stops suppressing the
     *  finding. Required for `accepted-risk` + `deferred` categories;
     *  optional otherwise. */
    readonly expiresAt?: string;
    /** Severity at which the suppression was acknowledged. Required
     *  when `category` is `accepted-risk` and the finding's severity
     *  is `high` or `critical` — see `validateAllowlistEntry`. */
    readonly acknowledgedSeverity?: FindingSeverity;
}
export interface AllowlistFile {
    readonly schemaVersion: AllowlistSchemaVersion;
    readonly mode: AllowlistMode;
    readonly entries: ReadonlyArray<AllowlistEntry>;
}
/**
 * The gitignored sidecar that carries the human-readable fields in
 * sanitized mode. Sparse — only the fields that need to live
 * outside the committed file appear. Loaders are lenient when the
 * sidecar is absent (no entry merges; suppression still works by
 * fingerprint).
 */
export interface AllowlistReasonsSidecar {
    readonly schemaVersion: AllowlistReasonsSchemaVersion;
    readonly reasons: Readonly<Record<string, {
        readonly reason: string;
        readonly addedBy: string;
    }>>;
}
export interface ValidationError {
    readonly fingerprint?: string;
    readonly field: string;
    readonly message: string;
}
export declare function pathForAllowlist(cwd: string): string;
export declare function pathForAllowlistReasons(cwd: string): string;
/**
 * Create an empty allowlist file in the requested mode. Useful for
 * the `vyuh-dxkit allowlist add` CLI path when no file exists yet.
 */
export declare function emptyAllowlistFile(mode?: AllowlistMode): AllowlistFile;
/**
 * Read the on-disk allowlist + (when present) merge the sidecar
 * reasons. Returns `null` when the main file doesn't exist —
 * callers treat that as "no allowlist configured."
 *
 * Throws on:
 *   - Malformed JSON in either file
 *   - Unrecognized `schemaVersion`
 *   - Root is not an object
 *
 * The sidecar's absence is NOT an error: customers in sanitized
 * mode may have a committed allowlist on disk without the sidecar
 * cloned (CI checkout, fresh teammate clone). The fingerprint-only
 * file is still functional as a suppression list.
 */
export declare function loadAllowlist(cwd: string): AllowlistFile | null;
/**
 * Load the gitignored reasons sidecar. Returns `null` when missing.
 * Throws on malformed JSON or wrong schemaVersion.
 */
export declare function loadAllowlistReasons(cwd: string): AllowlistReasonsSidecar | null;
/**
 * Persist the allowlist to disk. Writes the sidecar separately in
 * sanitized mode: the committed file gets the structural fields;
 * the sidecar gets `reason` + `addedBy`.
 *
 * Validation runs before writing — invalid input throws and the
 * file isn't touched. Use `validateAllowlistFile` directly when
 * you want non-throwing error reporting.
 */
export declare function saveAllowlist(cwd: string, file: AllowlistFile): void;
/** Find an entry by fingerprint. */
export declare function findEntry(file: AllowlistFile, fingerprint: string): AllowlistEntry | undefined;
/**
 * Add an entry. Returns a NEW `AllowlistFile` (immutable update).
 * Throws if `entry.fingerprint` already exists.
 */
export declare function addEntry(file: AllowlistFile, entry: AllowlistEntry): AllowlistFile;
/**
 * Remove an entry by fingerprint. Returns a NEW `AllowlistFile`.
 * Silently no-ops when the fingerprint isn't present (CLI surfaces
 * the "not found" case through a separate read step).
 */
export declare function removeEntry(file: AllowlistFile, fingerprint: string): AllowlistFile;
/**
 * Whether the allowlist suppresses a given finding. Pure
 * fingerprint match. Expiry handling is layered on top by
 * `isEntryActive` (kept separate so the guardrail can distinguish
 * "matched but expired" from "no match").
 */
export declare function matchesFinding(file: AllowlistFile, fingerprint: string): boolean;
/**
 * Whether an entry is currently active (within its expiry window).
 * Entries without `expiresAt` are always active. Entries past their
 * expiry are inactive — guardrail treats the underlying finding as
 * un-allowlisted and the next run flags the suppression as stale.
 */
export declare function isEntryActive(entry: AllowlistEntry, now?: Date): boolean;
/**
 * Days remaining until an entry expires, or `null` when it has no
 * expiry. Negative values mean already expired by that many days.
 */
export declare function daysUntilExpiry(entry: AllowlistEntry, now?: Date): number | null;
/** One entry in a soon-to-expire audit bucket, with the days
 *  remaining so callers can render time-to-expiry context. */
export interface SoonToExpire {
    readonly entry: AllowlistEntry;
    readonly daysRemaining: number;
}
/**
 * Audit report partitioning the file's entries into the three
 * actionable categories the `vyuh-dxkit allowlist audit` subcommand
 * surfaces. Pure function — no I/O, no side effects.
 *
 * - `expired` — entries past their `expiresAt`. Prune candidates.
 *   The underlying suppression no longer applies on the next
 *   guardrail run.
 * - `soonToExpire` — entries whose `expiresAt` is within
 *   `soonToExpireDays` (default 14). Customer should review whether
 *   the deferred work is still in plan and either fix the finding,
 *   extend the expiry, or remove the entry.
 * - `missingRationale` — entries with empty / whitespace-only
 *   reason. In full mode this should never happen (validator
 *   rejects); in sanitized mode it may occur when the sidecar is
 *   missing or stale.
 */
export interface AuditReport {
    readonly expired: ReadonlyArray<AllowlistEntry>;
    readonly soonToExpire: ReadonlyArray<SoonToExpire>;
    readonly missingRationale: ReadonlyArray<AllowlistEntry>;
}
export interface AuditOptions {
    readonly now?: Date;
    /** Window in days within which `expiresAt` is considered "soon."
     *  Default 14 — chosen to match a typical sprint cadence. */
    readonly soonToExpireDays?: number;
}
export declare function auditAllowlist(file: AllowlistFile, options?: AuditOptions): AuditReport;
/**
 * Remove expired entries from the file. Returns a new file (immutable)
 * plus the list of removed entries so the CLI can render what changed.
 * Pure function — no I/O.
 */
export declare function pruneExpired(file: AllowlistFile, now?: Date): {
    kept: AllowlistFile;
    removed: ReadonlyArray<AllowlistEntry>;
};
/**
 * Validate every entry in the file against the canonical taxonomy
 * + Sprint-0-locked rules. Pure function; returns an array of
 * `ValidationError` rather than throwing so callers can render
 * structured error messages.
 */
export declare function validateAllowlistFile(file: AllowlistFile): ReadonlyArray<ValidationError>;
/**
 * Validate a single entry. Exposed independently so the CLI's
 * `allowlist add` can pre-flight before mutating the file.
 */
export declare function validateAllowlistEntry(entry: AllowlistEntry, mode: AllowlistMode): ReadonlyArray<ValidationError>;
//# sourceMappingURL=file.d.ts.map