/**
 * artifacts/retention — the one owner of the eviction law.
 *
 * Three adapters, one rule for what leaves a scope and why. Each adapter
 * supplies its rows; this module decides — a pure plan over plain facts, so
 * the law is testable without a store and cannot fork into three dialects.
 *
 * The policy is three independent dials, all per scope:
 *   • `ttlMs`           — every artifact gets `expiresAt = createdAt + ttl`
 *                         stamped AT MINT (expiry is stated, never sprung),
 *                         and expired rows are swept when the scope is next
 *                         touched by a put.
 *   • `maxBytesPerScope`— a byte budget; least-recently-ACCESSED leaves first
 *                         (an artifact under active render must not be the
 *                         next one evicted — the innerRunRecords recency law).
 *   • `maxCountPerScope`— a row budget, same eviction order.
 *
 * Every eviction is a FACT the plan returns ({@link SweptArtifact}), because
 * the put that triggered it reports them upward and the capability layer puts
 * each one on the record. A store that made room silently would be lying by
 * omission.
 *
 * A payload bigger than the whole byte budget is REFUSED at the door (the
 * plan says so; the adapter throws by name) — evicting an entire scope to
 * admit one oversized object would trade everything for something that may
 * itself not fit.
 */
import type { SweptArtifact } from './types.js';
/** The three dials. All optional; absent means that dial does not bind. */
export interface ArtifactRetention {
    /** Lifetime stamped onto every mint as `expiresAt` (unix ms after createdAt). */
    readonly ttlMs?: number;
    /** Byte budget per scope. */
    readonly maxBytesPerScope?: number;
    /** Row budget per scope. */
    readonly maxCountPerScope?: number;
}
/** The facts the planner needs about one held artifact. */
export interface RetainedRow {
    readonly ref: string;
    readonly kind: string;
    readonly bytes: number;
    readonly expiresAt?: number;
    /** Recency — refreshed by get/head, so eviction spares what is in use. */
    readonly lastAccessedAt: number;
}
/** What a put must do to the scope before it may admit the incoming bytes. */
export interface RetentionPlan {
    /** Rows to remove, in order, each with its reason. */
    readonly swept: readonly SweptArtifact[];
    /** When defined, the put must be REFUSED with this sentence instead. */
    readonly refusal?: string;
}
/**
 * Validate a retention policy at construction — a dial that cannot bind
 * (zero, negative, NaN) is refused where it was written, not discovered as
 * an always-empty store at the first put.
 */
export declare function assertRetention(adapter: string, retention: ArtifactRetention | undefined): void;
/**
 * Plan what must leave a scope so that `incomingBytes` may enter.
 *
 * Pure: reads rows, returns facts, mutates nothing. Rows may arrive in any
 * order; eviction order here is least-recently-accessed first.
 */
export declare function planRetention(rows: readonly RetainedRow[], incomingBytes: number, retention: ArtifactRetention | undefined, now: number): RetentionPlan;
/**
 * The `expiresAt` a mint gets: the caller's own statement and the store's
 * ttl, whichever comes SOONER. A store may tighten a promise, never extend
 * one — extending would overrule the only party who knows the data.
 */
export declare function resolveExpiresAt(callerExpiresAt: number | undefined, retention: ArtifactRetention | undefined, now: number): number | undefined;
//# sourceMappingURL=retention.d.ts.map