/** * Walk an IR + its capturedItemIds map at the end of a successful apply * and emit one `BaselineFieldEntry` per field the recipe wrote. The push * task feeds this to `writeBaseline` to persist the snapshot. * * What "wrote" means here: * - `SetField` ops — one entry per (itemRefKey, fieldId, fieldName?, * language?, version?, hash(renderedValue)). * - `SetBaseTemplates` — one entry per templateRefKey, fieldId is the * `__Base template` GUID, value is the pipe-joined base list. * - `SetStandardValues` — one entry on the template, fieldId is * `__Standard values`, value is the standard-values item GUID. * - `CreateItem.fields[]` — one entry per embedded field on a created * item (icon, display name, etc.). These ride with the item creation * and aren't separately tracked as SetField ops, but they're equally * "what was written" and re-pushing a recipe with the same fields * should diff-clean. * * Ops that don't write a per-field value (CreateItem itself, PruneChildren, * CreateSiteFromTemplate, AddItemVersion, AppendToMultiList) emit nothing * — they're structural, not value-bearing. AppendToMultiList specifically * is excluded because its merge-unique semantics mean the on-tenant value * is the UNION of every recipe that contributed; capturing one recipe's * write hash wouldn't reflect what the field would re-read as. * * The capturedItemIds is used only for `ref-recipe` / `ref-recipe-list` / * `ref-source-fields` resolution — converting recipe-internal refKeys to * the concrete GUIDs the executor wrote. Hashes are computed over the * SAME wire string `renderRefValue` would produce on the next push, so * apples-to-apples comparison at plan time. */ import type { OperationIr } from "../ir/operations.js"; import type { PlannedAction } from "./plan.js"; import type { BaselineFieldEntry } from "./baseline.js"; /** * Applied-state evidence for one push — the executed plan's actions plus * the CreateItem refKeys the apply ADOPTED (existing item, fields NOT * written). When present, `collectBaselineEntries` records only field * values the tenant verifiably holds after this push: * * - CreateItem `create` actions whose refKey was NOT adopted — the item * was freshly created with the recipe's fields. * - SetField/SetBaseTemplates/SetStandardValues `update` actions — the * desired value was written. * - `skip` actions with `skipKind: "in-sync"` — the planner proved the * tenant already held the desired value. * * Everything else (adopted creates, `unresolved` / `create-only` / * `cms-wins` skips) is deliberately NOT baselined: the tenant does not * hold the desired value there, and recording it anyway is exactly the * over-capture that manufactured permanent phantom `cms-edit` conflicts * — the recipe kept matching the (fictional) baseline while the tenant * kept "diverging" from it. An uncaptured cell simply classifies as * `first-push` next time, which applies cleanly. */ export interface AppliedEvidence { actions: readonly PlannedAction[]; adoptedItemRefKeys?: ReadonlySet; } /** * Walk every value-bearing op in `ir.operations` and collect one * `BaselineFieldEntry` per write. Returns `[]` for an IR with no * value-bearing ops (structural-only recipes — pure CreateItem + * PruneChildren shouldn't have a baseline either, the "no entries" case * just round-trips as recipe-change next plan). * * Pass `applied` (the executed plan's actions + adopted refKeys) to * capture APPLIED state instead of desired state — see * {@link AppliedEvidence}. Without it (legacy callers/tests) every * value-bearing op is captured, desired-state style. */ export declare const collectBaselineEntries: (ir: OperationIr, capturedItemIds: ReadonlyMap, applied?: AppliedEvidence) => BaselineFieldEntry[];