/** * Drift detection for the System_State reconciliation scan. * * Compares one provider snapshot against the durable desired state and * classifies every desired row as matched, drifted, or missing. Anchor and * identity duplicates quarantine the affected locators instead of letting the * last row win silently, so a corrupted anchor or duplicated business key can * never prove ownership of a row. */ import type { NormalizedCell } from "../../../../contracts/encoding/types.js"; import { type SyncSheetsSnapshot, type SyncSnapshotRow } from "../../../../contracts/sheets/syncSheets.js"; import type { DesiredRow } from "./shared.js"; export type DriftKind = "drifted" | "missing"; export interface DriftTarget { readonly kind: DriftKind; readonly desired: DesiredRow; /** * The observed snapshot row behind a drifted drift, so a repair planned * for an existing row can guard on the row's CURRENT visible hash * (computed from the exact System_State fields) instead of assuming an * insert when no confirmed visible evidence exists. `undefined` for * missing drifts, where the row is not observable. */ readonly observed: SyncSnapshotRow | undefined; } /** * Anchor + business-key indices shared by drift detection and matching. * * Built once per scan from the snapshot and reused for every desired chunk, * so the snapshot side is indexed in a single Map pass instead of rebuilt * per chunk. The maps hold references to the snapshot's rows; no snapshot * data is copied. */ export interface ObservedRowIndex { readonly rowsByAnchor: ReadonlyMap; readonly ambiguousAnchors: ReadonlySet; readonly rowsByIdentity: ReadonlyMap; readonly ambiguousIdentities: ReadonlySet; } /** * Builds the deduplicated anchor/identity index for one snapshot. * * A duplicated physical anchor or business key is an anomaly, never a row * choice: the affected locators are dropped from the index so neither the * drift classifier nor the failed-head matcher can silently pick one row. */ export declare function buildObservedRowIndex(snapshot: SyncSheetsSnapshot, businessKeyField: string): ObservedRowIndex; /** * Resolves the observed snapshot row owned by one desired row. * * The primary locator's ambiguity is fatal: when the desired anchor is * duplicated, identity fallback would silently pick one of the rows that the * corrupted anchor cannot distinguish, so the binding stays unmatched. The * same quarantine applies when the desired identity appears in the snapshot's * duplicate set. Returns undefined when no row can prove ownership of this * binding. */ export declare function resolveObservedRow(index: ObservedRowIndex, desiredRow: DesiredRow, businessKeyField: string): SyncSnapshotRow | undefined; export declare function computeDrifts(args: { readonly snapshot: SyncSheetsSnapshot; readonly desired: readonly DesiredRow[]; readonly systemFields: readonly string[]; readonly sheet: { readonly registeredRange: string; readonly businessKeyField: string; }; }): readonly DriftTarget[]; /** * Classifies one chunk of desired rows against a prebuilt snapshot index. * * The chunked scan calls this per completed entity batch with the scan-wide * index; results concatenate in chunk order, which is the global * `(entity_id, field_name)` order, so chunked classification is identical to * one full-load `computeDrifts` call. Returns only drift targets; matched * rows are simply absent (the scan counts them as scanned-minus-missing). */ export declare function classifyDesiredChunk(index: ObservedRowIndex, desired: readonly DesiredRow[], systemFields: readonly string[], businessKeyField: string): readonly DriftTarget[]; /** Classifies one desired row: missing, drifted, or matched (undefined). */ export declare function classifyDesiredRow(index: ObservedRowIndex, desiredRow: DesiredRow, systemFields: readonly string[], businessKeyField: string): DriftTarget | undefined; /** Reads a business-key value from a snapshot row for unanchored fast appends. */ export declare function snapshotIdentity(row: SyncSheetsSnapshot["rows"][number], identityField: string): string | undefined; /** Reads the same visible business-key value from canonical desired state. */ export declare function desiredRowIdentity(row: DesiredRow, identityField: string): string | undefined; export declare function normalizedCellIdentity(cell: NormalizedCell | undefined): string | undefined; export declare function computeObservedHash(row: SyncSheetsSnapshot["rows"][number], systemFields: readonly string[]): string; export declare function countMatchedRows(snapshot: SyncSheetsSnapshot, desired: readonly DesiredRow[], identityField: string): number; export declare function countExtraRows(snapshot: SyncSheetsSnapshot, desired: readonly DesiredRow[], identityField: string): number; /** * Counts surplus snapshot rows from pre-accumulated desired keys. * * The chunked scan feeds one chunk at a time into `desiredAnchors` (first * row wins per anchor) and `desiredIdentities`, then calls this once: the * count is identical to `countExtraRows` while only small key strings are * retained instead of full desired rows. */ export declare function countExtraRowsForKeys(snapshot: SyncSheetsSnapshot, identityField: string, desiredAnchors: ReadonlyMap, desiredIdentities: ReadonlySet): number; //# sourceMappingURL=diff.d.ts.map