/** * Shared three-way-merge classification core. * * scai's push planner ([./plan.ts]) and pull task ([../tasks/pull.ts]) * each diff three legs — "mine" (recipe on push / disk on pull), "theirs" * (the tenant), and "baseline" (the last successfully synced state) — to * decide who moved a given field since the last sync. The two sides are * mirror images of the same decision: * * push self = recipe other = tenant (default conflict = cms-wins on * the pipeline; planner default * policy = error) * pull self = disk other = tenant (default conflict = tenant-wins * on the pipeline; pull default * policy = error) * * Both reduce to: given the hash of each leg, classify the field as * in-sync / self-ahead / other-ahead / conflict. The vocab differs * (`recipe-change`/`cms-edit` vs `disk-ahead`/`tenant-edited`) and the * push side additionally distinguishes a `first-push` no-baseline case, * but the underlying who-moved logic is identical. {@link classifyThreeWay} * is that single source of truth; the two task layers map it onto their * own vocabularies and conflict policies. */ /** * Outcome of comparing one field across the three legs. Direction-neutral * — callers rename `self`/`other` to recipe/disk and tenant/cms as * appropriate: * * - `"in-sync"` — self and other agree (no work to do). * - `"self-ahead"` — self diverged from baseline, other matches it * (a local change safe to propagate). * - `"other-ahead"` — other diverged from baseline, self matches it * (the remote side moved; don't clobber). * - `"conflict"` — both diverged (and disagree), or no baseline is * available to attribute the divergence. * - `"first-seen"` — no baseline entry exists for this field at all. * Distinct from `conflict`: push uses it as * `first-push`; pull collapses it (see * {@link classifyThreeWayPull}). */ export type ThreeWayStatus = "in-sync" | "self-ahead" | "other-ahead" | "conflict" | "first-seen"; /** * Core three-way comparison. `undefined` means the leg has no value for * this field (absent on that side / not in the baseline). * * self == other → in-sync * no baseline entry → first-seen (caller decides how * to bucket a missing baseline) * self == baseline, other != baseline → other-ahead * other == baseline, self != baseline → self-ahead * both != baseline → conflict */ export declare const classifyThreeWay: (self: string | undefined, other: string | undefined, baseline: string | undefined) => ThreeWayStatus; /** * Push-side classification vocabulary for one drifted field (see * `FieldDiffEntry.classification`). `classifyAgainstBaseline` is only * ever called once the planner already knows the field drifts (recipe != * tenant), so there is no `in-sync` outcome here. */ export type PushFieldClassification = "first-push" | "recipe-change" | "cms-edit" | "conflict"; /** * Push-side adapter over {@link classifyThreeWay}: self = recipe, * other = tenant. * * - no baseline entry → "first-push" * - tenant matches baseline (only the → "recipe-change" * recipe moved) * - recipe matches baseline (only the → "cms-edit" * author moved the tenant) * - both moved → "conflict" * * The caller (computeFieldDrift) is responsible for the no-baseline / * no-refKey short-circuit — it passes a defined `baselineHash` lookup * result here only when a baseline is loaded for the item. */ export declare const classifyPushDrift: (recipeHash: string, tenantHash: string, baselineHash: string | undefined) => PushFieldClassification; /** Pull-side classification vocabulary for one field. */ export type PullFieldStatus = "in-sync" | "disk-ahead" | "tenant-edited" | "conflict"; /** * Pull-side adapter over {@link classifyThreeWay}: self = disk, * other = tenant. Handles the no-baseline case the same way the push * side does NOT — pull is first-pull-friendly: * * - field only on disk → "disk-ahead" (a local addition, not a conflict) * - field only on tenant → "tenant-edited" (a CMS addition, not a conflict) * - both present, differ → "conflict" (genuinely ambiguous w/o baseline) * * When a baseline IS present the who-moved attribution is exact. */ export declare const classifyPullField: (diskHash: string | undefined, tenantHash: string | undefined, baselineHash: string | undefined) => PullFieldStatus;