/**
 * ConflictStore — the durable open-conflicts ledger + operator resolution path
 * (WS2 replicated-store foundation, §7.2 append-both lifecycle + §7.3 resolution
 * delegated UP).
 *
 * Spec: docs/specs/multi-machine-replicated-store-foundation.md §7.2 (idempotent
 * append-both on (recordKey, version-pair), ONE deduped attention item,
 * recurrence past a threshold → forced operator resolution), §7.3 (the operator
 * designates a winner / supplies a merged version; the foundation NEVER picks a
 * winner — Signal vs Authority), §7.4 (a conflictId whose version-pair referenced
 * a dropped origin auto-RESOLVES + its attention item closes).
 *
 * SIGNAL vs AUTHORITY (§11): this store DETECTS + records + surfaces conflicts; it
 * NEVER picks a winner. The one mutating authority — resolveConflict — is
 * operator-driven (the route is Bearer-authenticated upstream). Recording is
 * idempotent on the stable conflictId, so re-discovering the same unresolved
 * conflict never appends a third copy and the attention surface dedupes.
 *
 * Persistence: the open-conflicts ledger is a single JSON document written
 * atomically (SafeFsExecutor.atomicWriteJsonSync) to
 * `<stateDir>/state/state-sync/conflicts.json`, so it survives restarts +
 * compaction. Bounded: a per-store cap on simultaneously-open conflicts (oldest
 * evicted with a loss counter) keeps a flood from unbounded growth.
 */
import { serializeHlcKey, type HlcTimestamp } from './HybridLogicalClock.js';
import type { ConflictDescriptor, OriginRecord } from './UnionReader.js';
/** Default recurrence threshold past which an open conflict is forced to the
 *  operator (§7.2). The conflict re-surfaces past this many independent
 *  re-discoveries while still unresolved. */
export declare const DEFAULT_CONFLICT_RECURRENCE_THRESHOLD = 5;
/** Default cap on simultaneously-open conflicts in the ledger (bounded growth);
 *  oldest-first eviction with a loss counter on overflow. */
export declare const DEFAULT_MAX_OPEN_CONFLICTS = 500;
/** One open conflict's durable lifecycle record (§7.2/§7.3). */
export interface ConflictLedgerEntry {
    conflictId: string;
    store: string;
    recordKey: string;
    /** Each preserved version's (origin, hlc-key) — content-free routing facts +
     *  the total-order stamp, never the store payload (kept off the durable ledger;
     *  the live versions are re-derivable from the union reader). */
    versions: {
        origin: string;
        hlcKey: string;
    }[];
    firstSeenAt: string;
    lastSeenAt: string;
    /** Independent re-discoveries while unresolved — drives forced-resolution. */
    recurrenceCount: number;
    /** True once recurrenceCount crosses the threshold (§7.2). */
    forcedResolution: boolean;
    /** Set when the operator resolves (§7.3) or the rollback auto-resolves (§7.4). */
    resolved: boolean;
    resolvedAt?: string;
    resolution?: 'operator-winner' | 'operator-merged' | 'origin-dropped';
    /** The origin the operator designated as winner (operator-winner only). */
    winnerOrigin?: string;
}
/** What the operator supplies at /state/resolve-conflict (§7.3). EXACTLY ONE of
 *  winnerOrigin / mergedVersion — the foundation never picks; the operator does. */
export interface ConflictResolution {
    /** Designate an existing version (by its origin) as the winner. */
    winnerOrigin?: string;
    /** Supply a merged version (opaque store payload) — written as a normal record
     *  by the caller; this store only records that the conflict is resolved. */
    mergedVersion?: Record<string, unknown>;
}
/** The seams ConflictStore needs (DI'd so it is unit-testable without disk). */
export interface ConflictStoreSeams {
    /** Absolute path to the agent `.instar/` stateDir (the ledger lives under it). */
    stateDir: string;
    /** Injected wall clock (ISO timestamps). */
    now: () => Date;
    /** Optional: raise ONE deduped attention item for a conflict (caller wires the
     *  real attention queue; idempotent on conflictId by the caller). Signal-only. */
    raiseAttention?: (entry: ConflictLedgerEntry) => void;
    /** Optional structured logger (default no-op). */
    log?: (event: string, detail: Record<string, unknown>) => void;
    /** Optional fs read seam (tests); defaults to node:fs. */
    readFileSync?: (p: string) => string;
    existsSync?: (p: string) => boolean;
    /** Recurrence threshold + open cap overrides. */
    recurrenceThreshold?: number;
    maxOpenConflicts?: number;
}
export declare class ConflictStore {
    private readonly stateDir;
    private readonly now;
    private readonly raiseAttention?;
    private readonly log;
    private readonly readFileSync;
    private readonly existsSync;
    private readonly recurrenceThreshold;
    private readonly maxOpen;
    private doc;
    constructor(seams: ConflictStoreSeams);
    private ledgerPath;
    private load;
    private persist;
    /**
     * Record a detected conflict (§7.2). IDEMPOTENT on the stable conflictId:
     *  - first sighting ⇒ append a new open entry + raise ONE attention item.
     *  - re-sighting of an unresolved conflict ⇒ bump recurrenceCount + lastSeenAt
     *    (NEVER a third copy); cross the threshold ⇒ forcedResolution=true + a
     *    re-surfaced attention item.
     *  - re-sighting of a RESOLVED conflict ⇒ no-op (it was decided; a fresh
     *    divergence would carry a DIFFERENT conflictId).
     * Returns the live ledger entry.
     */
    recordConflict(store: string, descriptor: ConflictDescriptor): ConflictLedgerEntry;
    /** Evict oldest-first OPEN conflicts past the cap (bounded growth, §7.2). A
     *  resolved entry is retained briefly for audit but does not count toward the
     *  OPEN cap; the cap counts unresolved entries only. */
    private evictToBound;
    /**
     * Resolve a conflict (§7.3 — the operator's authority, never the foundation's).
     * EXACTLY ONE of `winnerOrigin` / `mergedVersion` must be supplied. The store
     * records the resolution; the CALLER writes the operator's chosen/merged record
     * as a normal replicated record (it replicates like any other). Returns the
     * updated entry, or null if the conflictId is unknown.
     */
    resolveConflict(conflictId: string, resolution: ConflictResolution): ConflictLedgerEntry | null;
    /**
     * Auto-resolve every OPEN conflict whose version set referenced a dropped origin
     * (§7.4 rollback-unmerge). After an un-merge the dropped origin no longer
     * contributes, so a conflict that only existed BECAUSE of it ceases to exist —
     * we mark it resolved ('origin-dropped') and return the closed conflictIds so
     * the caller can close their attention items. Idempotent.
     *
     * Conservative correctness: we resolve a conflict iff REMOVING the dropped
     * origin's versions leaves AT MOST ONE remaining origin (so no concurrent pair
     * remains). A conflict among OTHER origins that merely also included the dropped
     * one stays open (the divergence between the survivors is real) — it will
     * re-record on the next union read with a fresh id over the surviving versions.
     */
    autoResolveForDroppedOrigin(droppedOrigin: string): string[];
    /** Every open (unresolved) conflict (§7.3 dashboard surface). */
    listOpen(): ConflictLedgerEntry[];
    /** Every conflict (open + recently-resolved), for the audit surface. */
    listAll(): ConflictLedgerEntry[];
    /** One conflict by id, or undefined. */
    getConflict(conflictId: string): ConflictLedgerEntry | undefined;
    /** Monotonic count of open conflicts evicted by the bound (surfaced in degradation). */
    get lossCounter(): number;
}
/** Re-export the version-stamp helper so a caller serializing a resolution can
 *  produce the same hlc key shape the ledger stores. */
export { serializeHlcKey };
export type { HlcTimestamp, OriginRecord };
//# sourceMappingURL=ConflictStore.d.ts.map