import type { RecipePlan } from "../../sync/index.js"; import type { CampaignRecipe } from "./schema.js"; /** * Compare two recipe tasks ignoring undefined-vs-absent noise. * * The recipe's `handle` is also counted as identity: when the desired * recipe carries a handle and the current task's wire labels lack * `handle:`, we treat that as a difference so `apply` runs the * UPDATE path and stamps the label. Without this gate, a recipe that * adds identity to an existing task (the common case after the * orchestrator's lazy handle backfill) would diff as noop — and the * tenant would stay unidentified, so the next rename re-creates a * duplicate. */ /** * Find the current entity corresponding to a desired one, matching on * stable identity before display name. Order: `sitecoreId` (the server * UUID — strongest key once a push has stamped it back) → `handle` (the * authored kebab id, which round-trips via the wire's `handle:` * labels) → `name` (legacy fallback for entities authored before * identity stamping existed). * * This is the linchpin for rename survival: when an operator renames a * deliverable or task in the registry, the display name no longer * matches, but the `sitecoreId`/`handle` still does — so the change * diffs as an UPDATE of the existing item instead of a CREATE of a * duplicate. Shared with the three-way merge in `baseline.ts` so the * merge and the diff agree on what "the same entity" means. */ export declare const matchByIdentity: (desired: T, current: readonly T[]) => T | undefined; /** Diff a desired campaign recipe against captured current state. */ export declare const diffCampaign: (desired: CampaignRecipe, current: CampaignRecipe | null) => RecipePlan;