/**
 * DEPRECATED / SUPERSEDED (CMT-1416): this seamlessness-spec WS2.1 advisory
 * replicator is superseded by the HLC-foundation `pref-record` replicated store
 * (`multiMachine.stateSync.preferences`, src/core/PreferencesReplicatedStore.ts).
 * Retained dark (behind `multiMachine.seamlessness.ws21PreferencesPool`) until the
 * foundation path is validated, then removed — a separate reviewable cleanup PR.
 * BOTH ship dark/default-off ⇒ zero runtime duplication; they are mutually
 * exclusive in practice (the foundation path takes precedence when its flag is on).
 *
 * PreferencesSync — WS2.1 of multi-machine seamlessness: the serve / receive /
 * merge engine behind the `preferences-sync` mesh verb. Replicates the
 * correction-learning preference store (PreferencesManager,
 * `.instar/preferences.json`) across the machine pool so a preference learned on
 * machine A is honored on machine B.
 *
 * Spec: MULTI-MACHINE-SEAMLESSNESS-SPEC §WS2.1. Mirrors the COMMITMENTS-COHERENCE
 * replication pattern (CommitmentsSync.ts) — transport-agnostic, seam-injected;
 * the server registers the serve side as the verb handler and rides the
 * PeerPresencePuller cadence on the receive side, exactly like journal-sync.
 *
 * Preferences are ADVISORY SIGNALS (session-start hints), never authority — so
 * this is read-replication only, no write-back, no election, no quorum.
 *
 * Load-bearing rules (mirrors §3.2):
 *  - Pages are seq-windowed DELTAS by `lastMutatedSeq` (asc, dedupeKey tiebreak),
 *    EXCLUSIVE cursor, capped at `syncPageBytes` with ≥1 record/page — a store
 *    replicates fully over multiple pages, never a blob whose tail strands.
 *  - Incarnation fencing: a stale incarnation is answered `incarnationChanged`;
 *    the receiver discards the replica wholesale and re-pulls from 0 (a restored
 *    `.instar/preferences.json` must never strand replication).
 *  - First-hop with teeth: the replica owner derives from the AUTHENTICATED
 *    envelope sender; any served row whose originMachineId names a DIFFERENT
 *    machine is rejected + counted (forgedRows) — a peer cannot inject rows
 *    attributed to a third machine.
 *  - Disclosure honesty: the free-text `learning` is credential-shape-scanned at
 *    serve time; a flagged row ships with `learning` REDACTED (it still
 *    replicates — usefulness never depends on the scan).
 *
 * The ONE merge difference from commitments (design fork, resolved): commitments
 * union on composite key with no cross-origin merge; preferences COLLAPSE by
 * `dedupeKey` because the same dedupeKey on two machines is the SAME learned
 * lesson observed independently. The merged view presents ONE row per dedupeKey
 * (newest `recordedAt` wins the fields; `dedupeCount` sums across origins) so the
 * session-start block never double-injects the same guidance.
 */
import type { PreferenceEntry } from './PreferencesManager.js';
export declare const DEFAULT_SYNC_PAGE_BYTES: number;
export declare const DEFAULT_REPLICA_STALE_WARN_MS: number;
/** Per-store bound — preferences are few; a runaway peer can't bloat the merge. */
export declare const DEFAULT_MAX_REPLICATED_PREFERENCES = 500;
export interface PreferencesSyncRequest {
    sinceSeq: number;
    incarnation?: string;
}
/** A preference as it travels: always origin-stamped + seq-stamped, possibly redacted. */
export interface ReplicatedPreference extends PreferenceEntry {
    originMachineId: string;
    /** Monotonic per-origin mutation sequence — the delta-window key. */
    lastMutatedSeq: number;
    /** True when `learning` was credential-redacted at serve time. */
    textRedacted?: boolean;
}
export interface PreferencesSyncPage {
    incarnation: string;
    replicationSeq: number;
    /** Served records (redacted + origin-stamped). Empty when caught up. */
    records: ReplicatedPreference[];
    /** EXCLUSIVE cursor for the next request. */
    nextSinceSeq: number;
    /** True when no records remain past nextSinceSeq. */
    done: boolean;
    /** Set when the requester's incarnation is stale — re-pull from 0. */
    incarnationChanged?: boolean;
}
/** A preference plus its (possibly absent → 0) replication seq, from the own store. */
export interface ServeablePreference extends PreferenceEntry {
    /** Absent on a legacy store ⇒ treated as 0 so a first sync replicates fully. */
    lastMutatedSeq?: number;
}
export interface ServePageDeps {
    ownMachineId: string;
    /** The OWN store's preferences — never replicas. */
    records: ServeablePreference[];
    advert: {
        incarnation: string;
        replicationSeq: number;
    };
    syncPageBytes?: number;
}
export declare function buildPreferencesSyncPage(req: PreferencesSyncRequest, deps: ServePageDeps): PreferencesSyncPage;
export interface ApplyResult {
    applied: number;
    /** Rows claiming an originMachineId ≠ the authenticated sender — rejected. */
    forgedRows: number;
    /** The replica was discarded wholesale (incarnation change). */
    replaced: boolean;
    /** Rows dropped because the per-peer bound was already reached. */
    dropped: number;
}
/**
 * The per-peer replica store: one JSON file per owner under
 * `state/preference-replicas/`, written ONLY by this receive path
 * (single-writer), temp-file + atomic rename, corrupt → quarantine + fresh
 * (never silently empty).
 */
export declare class PreferenceReplicaStore {
    private readonly dir;
    private readonly now;
    private readonly logger;
    private readonly maxRecords;
    private cache;
    constructor(config: {
        stateDir: string;
        now?: () => Date;
        logger?: (msg: string) => void;
        maxRecordsPerPeer?: number;
    });
    /** The cursor to request next from a peer (0 + no incarnation when fresh). */
    cursorFor(ownerMachineId: string): {
        sinceSeq: number;
        incarnation?: string;
    };
    /**
     * Apply a served page. `senderMachineId` is the AUTHENTICATED envelope
     * sender — the replica identity derives from it, never a payload field.
     */
    applyPage(senderMachineId: string, page: PreferencesSyncPage): ApplyResult;
    /** Every replica's rows, tagged with owner + receivedAt. */
    allReplicas(): {
        ownerMachineId: string;
        receivedAt: string;
        records: ReplicatedPreference[];
    }[];
    private fileFor;
    private load;
    private persist;
}
export interface MergedPreference extends PreferenceEntry {
    /** Every machine that has observed this dedupeKey (own included). */
    contributingMachines: string[];
    /** Newest contributor — whose fields won. */
    winningMachineId: string;
    /** True when ≥1 contributing row was credential-redacted. */
    textRedacted?: boolean;
}
export interface MergeDeps {
    ownMachineId: string;
    own: PreferenceEntry[];
    replicas: {
        ownerMachineId: string;
        receivedAt: string;
        records: ReplicatedPreference[];
    }[];
    /** Injectable clock for the skew cap (default `new Date()`). Tests pass a fixed now. */
    now?: () => Date;
}
/**
 * Clock-skew tolerance for merge ordering. A peer's `recordedAt` is capped at
 * `now + this` before it can win a dedupeKey collision — so a machine with a
 * fast/hostile clock (e.g. set a year ahead) cannot silently dominate EVERY
 * preference across the pool (review WS2.1 finding #2; the spec's clock-skew
 * requirement). Real-world cross-machine clock drift is seconds-to-minutes; a
 * full day is a generous ceiling that never penalizes honest machines.
 * NOTE: this is the shipped mitigation; full HLC (logical counters) is a tracked
 * follow-up. <!-- tracked: WS2.1 HLC counters -->
 */
export declare const CLOCK_SKEW_TOLERANCE_MS: number;
/**
 * Collapse own + replicas by dedupeKey into one merged row per learned
 * preference. Newest (recordedAt, origin) wins the fields; dedupeCount SUMS
 * across the distinct contributing origins (true cross-machine observation
 * count); contributingMachines lists every origin that holds the key.
 */
export declare function mergePreferenceViews(deps: MergeDeps): MergedPreference[];
//# sourceMappingURL=PreferencesSync.d.ts.map