/**
 * design/381 §片2 — the engine's OWN bound on file-history growth, and the door its one knob passes
 * through.
 *
 * The hole this closes: `FileHistoryStore.reap` is a complete, contract-tested GC verb that nothing
 * in the engine ever called. Every turn of a wired session commits one more boundary, each boundary
 * is a complete mapping over the tracked set, and no seat anywhere dropped the old ones — so a long
 * session's history grew without bound, silently, which is precisely the shape the bad-value
 * loudness rule exists to forbid (a limit nobody states is still a limit; it is just the disk's).
 * The bundled backends therefore carry the bound THEMSELVES: each one commits a boundary and then
 * trims its scope back to the retention bound, and a host that wants the GC schedule for itself
 * says so (`keep: "unbounded"`) rather than discovering the default by running out of disk.
 *
 * PROVENANCE OF THE DEFAULT (not a taste call, and not a JSDoc-only constraint): the reference
 * implementation caps its own file-history at the newest 100 snapshots and evicts the rest — CC
 * 2.1.250 holds the cap as a module constant (`R1e = 100`) and applies it in the snapshot reducer
 * (`if (E.length > R1e) R = E.slice(-R1e), <evict>(E.slice(0, E.length - R1e), R, …).catch(…)`),
 * i.e. keep the newest N by commit order, hand the evicted ones to a best-effort eviction pass, and
 * never let that pass fail the turn that triggered it. Its eviction unlinks a backup only when no
 * surviving snapshot references it AND its version is not 1 — the same v1-permanence law this
 * store's `reap` already implements. design/381 §片2 carries the number over as "边界上限(默认
 * 100,store 旋钮)", which is the shape below: engine default, store-level, host-overridable.
 *
 * @see {@link import("./retention-policy.js").assertRetentionPolicy} — the sibling row stores' door.
 *   This one is separate because its admissible range is different (see {@link refuseRetentionPolicy}).
 */
export declare const DEFAULT_FILE_HISTORY_BOUNDARY_KEEP = 100;
/**
 * The file-history retention knob, as a deployment writes it. ONE knob on purpose — the graph has
 * exactly one unbounded dimension (boundaries per scope); version records and blobs are already
 * bounded by it, because `reap` drops every version no surviving boundary references (v1 and the
 * base-unavailable markers excepted, which are per tracked file, not per turn).
 *
 * Absent ⇒ {@link DEFAULT_FILE_HISTORY_BOUNDARY_KEEP}. Every other reading of a badly-formed value
 * is a REFUSAL at construction, never a quiet fallback — see {@link resolveFileHistoryRetention}.
 */
export interface FileHistoryRetentionPolicy {
    /**
     * How many of a scope's NEWEST boundaries survive each retention pass. A whole number `>= 1`, or
     * the literal `"unbounded"` to switch the store's self-trim OFF and take the GC schedule over
     * (the host then owns calling {@link import("./file-history-store.js").FileHistoryStore.reap};
     * nothing else collects boundaries).
     *
     * `0` is REFUSED rather than accepted as "keep nothing": a boundary is minted by the very commit
     * that would then reap it, so a store configured that way would answer `ok` to every `snapshot`
     * and `false` to every `canRestore` — a rewind seat that is on, reports success, and can never
     * restore anything. `"unbounded"` is the ONE spelling of "do not trim"; omitting the knob is NOT
     * that spelling — it selects the engine default and therefore trims (cross-review round 3 caught
     * this said the opposite here and in the refusal text, which would have sent an operator who
     * wanted everything kept down the path that deletes).
     */
    keep?: number | "unbounded";
}
/**
 * The retention door: resolve a deployment's policy to a boundary cap, or REFUSE loudly.
 *
 * Returns the cap (a whole number `>= 1`), or `null` for the explicit `"unbounded"` opt-out. Called
 * at CONSTRUCTION by every bundled backend, so a malformed policy fails where the operator wired it
 * rather than mid-session inside a GC pass they cannot see.
 *
 * The three ways a value can be bad each get their own named exit — none of them falls back to the
 * default, because a retention knob that quietly became a different number is the failure this whole
 * seam exists to remove:
 *  1. GARBAGE — the policy is not a knob bag, a knob is misspelled, or `keep` is not a whole number
 *     (NaN from a `Number(cfg.keep)` on a missing field, `Infinity`, `2.5`, `"100"`, `null`).
 *  2. OUT OF RANGE — a negative `keep`. (Reached by the same route as NaN in the sibling stores'
 *     history: `slice(Math.max(0, keep))` turns both into `slice(0)` = delete everything.)
 *  3. CONTRADICTORY — `keep: 0`, well-formed and in the range its siblings accept, but self-cancelling
 *     here; refused with the ONE spelling of "do not trim" named, and with what omission really does
 *     said out loud so the remediation cannot be followed into the opposite outcome.
 *
 * @param label the seam the operator configured, so the refusal names it.
 */
export declare function resolveFileHistoryRetention(label: string, policy: FileHistoryRetentionPolicy | undefined): number | null;
/**
 * Which boundaries survive the pass that `justCommitted`'s commit triggered — the SELECTION half,
 * shared so the two bundled backends (and an out-of-tree twin) cannot drift on which boundary dies.
 *
 * Returns `null` when the scope is still within its bound, which is the answer the caller wants
 * cheaply: no `reap` call at all, so a session under the cap pays one directory listing per turn and
 * nothing else. Otherwise returns the keep set for
 * {@link import("./file-history-store.js").FileHistoryStore.reap} — the newest `keep` ids, with
 * `justCommitted` PINNED into it unconditionally.
 *
 * The pin is not belt-and-braces. `orderedOldestFirst` is whatever commit order its backend can
 * PROVE, and the two bundled ones prove different amounts: the in-memory reference reads an
 * insertion-ordered map (exact), while the durable one reads each boundary's own publication stamp,
 * falling back to file mtime for records written before that field or rebuilt from an import, and
 * to the entry id for ties. A clock stepped backwards, or a host minting non-monotonic ids of its
 * own, can still invert two neighbours there.
 * An ordering that is off by one place costs a different-but-equally-legal retention choice; an
 * ordering that put the JUST-COMMITTED boundary at the bottom would cost this turn its rewind seat.
 * Pinning makes the second outcome unreachable by construction rather than by trusting the clock.
 *
 * One more place the order is only as good as its source, said plainly rather than assumed away: an
 * envelope round trip (`exportHistory` → `importHistory`) writes boundaries in CANONICAL — i.e.
 * lexicographic — entry-id order, so a scope rebuilt from one has entry-id order where it used to
 * have commit order. For engine-minted uuidv7 ids those are the same order; for a foreign graph's
 * ids they need not be, and such a scope's oldest-first reading is by id. The consequence is again
 * only WHICH legal boundary is retained, and the just-committed pin still holds.
 *
 * @param orderedOldestFirst every committed boundary id in the scope, oldest first.
 * @param justCommitted the boundary whose commit triggered the pass.
 * @param keep the resolved cap (`>= 1`).
 */
export declare function fileHistoryBoundariesToKeep(orderedOldestFirst: readonly string[], justCommitted: string, keep: number): string[] | null;
