@ifc-lite/merge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,179 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { IFCLITE_ATTR, computeLayerId, createProvenanceManifest, setProvenance } from '@ifc-lite/ifcx';
5
+ import { opsForComponentChange } from './three-way.js';
6
+ function resolutionKey(path, componentKey) {
7
+ return componentKey === undefined ? path : `${path}\u0000${componentKey}`;
8
+ }
9
+ /**
10
+ * Turn reviewer decisions into ops. `ours` keeps the target state (no op),
11
+ * `theirs` adopts the candidate value, `edited` takes the supplied
12
+ * replacement component value (component-scoped conflicts only).
13
+ */
14
+ export function applyResolutions(plan, inputs) {
15
+ const byKey = new Map();
16
+ for (const input of inputs) {
17
+ byKey.set(resolutionKey(input.path, input.componentKey), input);
18
+ }
19
+ const ops = [];
20
+ const resolutions = [];
21
+ const unresolved = [];
22
+ for (const conflict of plan.conflicts) {
23
+ const input = byKey.get(resolutionKey(conflict.path, conflict.componentKey));
24
+ if (!input) {
25
+ unresolved.push(conflict);
26
+ continue;
27
+ }
28
+ const record = { entity: conflict.path, choice: input.choice };
29
+ if (conflict.componentKey !== undefined)
30
+ record.componentKey = conflict.componentKey;
31
+ resolutions.push(record);
32
+ if (input.choice === 'ours')
33
+ continue;
34
+ if (input.choice === 'edited') {
35
+ if (conflict.componentKey === undefined || input.attributes === undefined) {
36
+ throw new Error(`edited resolution for ${conflict.path} requires a componentKey-scoped conflict and replacement attributes`);
37
+ }
38
+ if (conflict.componentKey.startsWith('child:') || conflict.componentKey.startsWith('inherit:')) {
39
+ // A set-component on a relation pseudo-component would serialize
40
+ // as a literal attribute instead of a children/inherits edge.
41
+ throw new Error(`edited resolution cannot target relation component ${conflict.componentKey} on ${conflict.path}; resolve ours/theirs instead`);
42
+ }
43
+ ops.push({
44
+ op: 'set-component',
45
+ path: conflict.path,
46
+ componentKey: conflict.componentKey,
47
+ attributes: input.attributes,
48
+ });
49
+ continue;
50
+ }
51
+ // choice === 'theirs'
52
+ ops.push(...opsForTheirs(conflict));
53
+ }
54
+ return { ops, resolutions, unresolved };
55
+ }
56
+ function opsForTheirs(conflict) {
57
+ if (conflict.componentKey !== undefined) {
58
+ return opsForComponentChange(conflict.path, conflict.componentKey, conflict.base?.attributes, conflict.theirs?.attributes,
59
+ // Attribute keys ours added inside this component must be nulled,
60
+ // or they survive a "theirs" resolution via per-attribute LWW.
61
+ conflict.ours?.attributes);
62
+ }
63
+ switch (conflict.kind) {
64
+ case 'modify-vs-delete': {
65
+ // A recorded (empty) theirs state means theirs STRIPPED the entity
66
+ // rather than tombstoning it — resolve with removal opinions so no
67
+ // subtree-shadowing tombstone is fabricated.
68
+ if (conflict.theirs !== undefined) {
69
+ const baseComponents = (conflict.base?.attributes ?? {});
70
+ const oursComponents = (conflict.ours?.attributes ?? {});
71
+ const ops = [];
72
+ const keys = new Set([...Object.keys(baseComponents), ...Object.keys(oursComponents)]);
73
+ for (const componentKey of keys) {
74
+ ops.push(...opsForComponentChange(conflict.path, componentKey, baseComponents[componentKey], undefined, oursComponents[componentKey]));
75
+ }
76
+ return ops;
77
+ }
78
+ // Theirs tombstoned the entity ours kept editing.
79
+ return [{ op: 'tombstone-entity', path: conflict.path }];
80
+ }
81
+ case 'delete-vs-modify': {
82
+ // Theirs kept editing what ours tombstoned: resurrect and restore.
83
+ // Ours' pre-tombstone opinions become visible again on resurrect, so
84
+ // the restore must cover the union of theirs' and ours' components —
85
+ // an ours-only component has to be tombstoned, not left shining.
86
+ const ops = [{ op: 'resurrect-entity', path: conflict.path }];
87
+ const theirsComponents = (conflict.theirs?.attributes ?? {});
88
+ const oursComponents = (conflict.ours?.attributes ?? {});
89
+ const keys = new Set([...Object.keys(theirsComponents), ...Object.keys(oursComponents)]);
90
+ for (const componentKey of keys) {
91
+ ops.push(...opsForComponentChange(conflict.path, componentKey, undefined, theirsComponents[componentKey], oursComponents[componentKey]));
92
+ }
93
+ return ops;
94
+ }
95
+ default:
96
+ throw new Error(`entity-level conflict of kind ${conflict.kind} cannot take theirs`);
97
+ }
98
+ }
99
+ /** Serialize ops as ordinary IFCX node opinions, one node per path. */
100
+ export function opsToNodes(ops) {
101
+ const byPath = new Map();
102
+ const nodeFor = (path) => {
103
+ let node = byPath.get(path);
104
+ if (!node) {
105
+ node = { path };
106
+ byPath.set(path, node);
107
+ }
108
+ return node;
109
+ };
110
+ for (const op of ops) {
111
+ const node = nodeFor(op.path);
112
+ switch (op.op) {
113
+ case 'set-component':
114
+ case 'tombstone-component':
115
+ node.attributes = { ...node.attributes, ...op.attributes };
116
+ break;
117
+ case 'tombstone-entity':
118
+ node.attributes = { ...node.attributes, [IFCLITE_ATTR.DELETED]: true };
119
+ break;
120
+ case 'resurrect-entity':
121
+ node.attributes = { ...node.attributes, [IFCLITE_ATTR.DELETED]: false };
122
+ break;
123
+ case 'set-child':
124
+ node.children = { ...node.children, [op.name]: op.child };
125
+ break;
126
+ case 'remove-child':
127
+ node.children = { ...node.children, [op.name]: null };
128
+ break;
129
+ case 'set-inherit':
130
+ node.inherits = { ...node.inherits, [op.name]: op.target };
131
+ break;
132
+ case 'remove-inherit':
133
+ node.inherits = { ...node.inherits, [op.name]: null };
134
+ break;
135
+ }
136
+ }
137
+ return [...byPath.values()].sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
138
+ }
139
+ /**
140
+ * Emit an immutable merge layer: ops as IFCX nodes plus a provenance
141
+ * manifest with `merge` filled, content-addressed by canonical blake3.
142
+ */
143
+ export function buildMergeLayer(init) {
144
+ const mergeRecord = {
145
+ candidate: init.merge.candidate,
146
+ into: init.merge.into,
147
+ resolutions: init.merge.resolutions,
148
+ waived_checks: init.merge.waived_checks ?? [],
149
+ resolver: init.merge.resolver,
150
+ };
151
+ const manifest = createProvenanceManifest({
152
+ author: init.author,
153
+ intent: init.intent,
154
+ base: init.base,
155
+ created: init.created,
156
+ parents: init.parents ?? (init.base ? [init.base.id] : []),
157
+ checks: init.checks ?? [],
158
+ merge: mergeRecord,
159
+ });
160
+ const bare = {
161
+ header: {
162
+ id: '',
163
+ ifcxVersion: init.ifcxVersion ?? 'ifcx_alpha',
164
+ dataVersion: init.dataVersion ?? '1.0.0',
165
+ author: init.author.principal,
166
+ timestamp: manifest.created,
167
+ },
168
+ imports: [],
169
+ schemas: {},
170
+ data: opsToNodes(init.ops),
171
+ };
172
+ const withManifest = setProvenance(bare, manifest);
173
+ const layerId = computeLayerId(withManifest);
174
+ return {
175
+ file: { ...withManifest, header: { ...withManifest.header, id: layerId } },
176
+ layerId,
177
+ };
178
+ }
179
+ //# sourceMappingURL=merge-layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge-layer.js","sourceRoot":"","sources":["../src/merge-layer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Rebase: state-based ops make this cheap — re-run the three-way plan
3
+ * against the new base. Prior resolutions replay automatically because
4
+ * resolution ops shadow. No operational transform, ever (05 §5.4).
5
+ */
6
+ import type { IfcxFile } from '@ifc-lite/ifcx';
7
+ import type { MergePlan, ResolutionInput } from './types.js';
8
+ import { type AppliedResolutions } from './merge-layer.js';
9
+ export interface RebaseInputs {
10
+ /** The candidate layer being rebased. */
11
+ candidate: IfcxFile;
12
+ /** The candidate's original base, ordered weakest first. */
13
+ oldBase: readonly IfcxFile[];
14
+ /** The new base to plan against, ordered weakest first. */
15
+ newBase: readonly IfcxFile[];
16
+ /** Resolutions from earlier merge attempts, replayed onto the new plan. */
17
+ priorResolutions?: readonly ResolutionInput[];
18
+ }
19
+ export interface RebaseResult {
20
+ plan: MergePlan;
21
+ /** Prior resolutions applied to the new plan (replayed automatically). */
22
+ applied: AppliedResolutions;
23
+ }
24
+ export declare function planRebase(inputs: RebaseInputs): RebaseResult;
25
+ //# sourceMappingURL=rebase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebase.d.ts","sourceRoot":"","sources":["../src/rebase.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7D,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,SAAS,EAAE,QAAQ,CAAC;IACpB,4DAA4D;IAC5D,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC7B,2DAA2D;IAC3D,OAAO,EAAE,SAAS,QAAQ,EAAE,CAAC;IAC7B,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,0EAA0E;IAC1E,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAQ7D"}
package/dist/rebase.js ADDED
@@ -0,0 +1,15 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { planThreeWayMerge } from './three-way.js';
5
+ import { applyResolutions } from './merge-layer.js';
6
+ export function planRebase(inputs) {
7
+ const plan = planThreeWayMerge({
8
+ ancestor: inputs.oldBase,
9
+ ours: inputs.newBase,
10
+ theirs: [...inputs.oldBase, inputs.candidate],
11
+ });
12
+ const applied = applyResolutions(plan, inputs.priorResolutions ?? []);
13
+ return { plan, applied };
14
+ }
15
+ //# sourceMappingURL=rebase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebase.js","sourceRoot":"","sources":["../src/rebase.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAU/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAA2B,MAAM,kBAAkB,CAAC;AAmB7E,MAAM,UAAU,UAAU,CAAC,MAAoB;IAC7C,MAAM,IAAI,GAAG,iBAAiB,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC,OAAO;QACxB,IAAI,EAAE,MAAM,CAAC,OAAO;QACpB,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC;KAC9C,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC;IACtE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Store-agnostic layer-PR merge flow: fast-forward when the candidate was
3
+ * authored against the ref's current stack, otherwise a three-way plan
4
+ * with explicit conflicts, blanket resolutions, and ref-policy
5
+ * enforcement (required checks, human approval) before completion.
6
+ *
7
+ * The flow is shared verbatim by the CLI (`ifc layer merge`, filesystem
8
+ * store) and the registry route (`POST /api/v1/refs/:name/merge`,
9
+ * collab-server) — one decision procedure, two transports, no drift.
10
+ */
11
+ import type { IfcxFile, ProvenanceBase, ProvenanceManifest } from '@ifc-lite/ifcx';
12
+ import type { MergeConflict, MergePlan, ResolutionInput } from './types.js';
13
+ export interface RefPolicy {
14
+ requireHumanApproval?: boolean;
15
+ requiredChecks?: string[];
16
+ }
17
+ export interface RefEntry {
18
+ /** Ordered layer ids, weakest first. */
19
+ layers: string[];
20
+ policy?: RefPolicy;
21
+ }
22
+ /** Minimal store surface the merge flow needs; implementations own durability. */
23
+ export interface LayerRefStore {
24
+ loadLayer(layerId: string): IfcxFile;
25
+ /** Persist a published layer; `file.header.id` is its blake3 id. */
26
+ storeLayer(file: IfcxFile): string;
27
+ getRef(name: string): RefEntry | undefined;
28
+ setRef(name: string, entry: RefEntry): void;
29
+ /** Optional id-prefix resolution (CLI convenience); identity if absent. */
30
+ resolveLayerId?(idOrPrefix: string): string;
31
+ }
32
+ export interface AncestorResolution {
33
+ /** Ordered layer documents forming the ancestor stack. */
34
+ layers: IfcxFile[];
35
+ /** Layer ids forming the ancestor stack. */
36
+ ids: string[];
37
+ /** False when manifest.base was null or matched no prefix of the ref. */
38
+ matched: boolean;
39
+ }
40
+ /**
41
+ * Resolve a candidate's `manifest.base` to a prefix of the ref's layer
42
+ * list: a stack base matches the prefix with the same stack hash, a layer
43
+ * base matches the prefix ending at that layer id.
44
+ */
45
+ export declare function resolveAncestor(store: Pick<LayerRefStore, 'loadLayer'>, refLayerIds: readonly string[], base: ProvenanceBase | null | undefined): AncestorResolution;
46
+ export interface Waiver {
47
+ spec: string;
48
+ reason: string;
49
+ }
50
+ /** Returns a failure message when the ref's policy blocks completion. */
51
+ export declare function checkRefPolicy(entry: RefEntry, manifest: ProvenanceManifest | undefined, waivers: readonly Waiver[], approvedBy: string | undefined): string | undefined;
52
+ export interface MergeInit {
53
+ candidateId: string;
54
+ into: string;
55
+ preview?: boolean;
56
+ resolve?: 'ours' | 'theirs';
57
+ /**
58
+ * Per-conflict resolutions (the review-UI flow: preview, decide each
59
+ * conflict, execute). Takes precedence over the blanket `resolve`;
60
+ * conflicts left unaddressed surface as a `conflicts` outcome.
61
+ */
62
+ resolutions?: ResolutionInput[];
63
+ waivers?: Waiver[];
64
+ approvedBy?: string;
65
+ principal?: string;
66
+ created?: string;
67
+ /** Merge a candidate whose declared base matches nothing on the ref. */
68
+ allowUnrelated?: boolean;
69
+ }
70
+ export type MergeOutcome = {
71
+ status: 'fast-forward';
72
+ refLayers: string[];
73
+ ancestorMatched: true;
74
+ } | {
75
+ status: 'preview';
76
+ plan: MergePlan;
77
+ ancestorMatched: boolean;
78
+ } | {
79
+ status: 'conflicts';
80
+ conflicts: MergeConflict[];
81
+ ancestorMatched: boolean;
82
+ } | {
83
+ status: 'policy-failure';
84
+ reason: string;
85
+ } | {
86
+ status: 'unrelated-base';
87
+ declaredBase: ProvenanceBase;
88
+ } | {
89
+ status: 'merged';
90
+ mergeLayerId: string;
91
+ refLayers: string[];
92
+ plan: MergePlan;
93
+ ancestorMatched: boolean;
94
+ };
95
+ /** Core merge flow; returns an outcome instead of exiting (transport-neutral). */
96
+ export declare function mergeIntoRef(store: LayerRefStore, init: MergeInit): MergeOutcome;
97
+ //# sourceMappingURL=ref-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ref-flow.d.ts","sourceRoot":"","sources":["../src/ref-flow.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,kBAAkB,EAAe,MAAM,gBAAgB,CAAC;AAIhG,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,WAAW,SAAS;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,QAAQ;IACvB,wCAAwC;IACxC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,CAAC;IACrC,oEAAoE;IACpE,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC3C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5C,2EAA2E;IAC3E,cAAc,CAAC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,4CAA4C;IAC5C,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,yEAAyE;IACzE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,EACvC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,IAAI,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,GACtC,kBAAkB,CAqBpB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAC5B,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,kBAAkB,GAAG,SAAS,EACxC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,MAAM,GAAG,SAAS,CAuBpB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,IAAI,CAAA;CAAE,GACtE;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,GAChE;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,aAAa,EAAE,CAAC;IAAC,eAAe,EAAE,OAAO,CAAA;CAAE,GAC7E;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,cAAc,CAAA;CAAE,GAC1D;IACE,MAAM,EAAE,QAAQ,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAwBN,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,GAAG,YAAY,CAyGhF"}
@@ -0,0 +1,180 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { computeStackHash, getProvenance } from '@ifc-lite/ifcx';
5
+ import { applyResolutions, buildMergeLayer } from './merge-layer.js';
6
+ import { planThreeWayMerge } from './three-way.js';
7
+ /**
8
+ * Resolve a candidate's `manifest.base` to a prefix of the ref's layer
9
+ * list: a stack base matches the prefix with the same stack hash, a layer
10
+ * base matches the prefix ending at that layer id.
11
+ */
12
+ export function resolveAncestor(store, refLayerIds, base) {
13
+ const load = (ids) => ids.map((id) => store.loadLayer(id));
14
+ // Null/missing base: ancestor is the empty stack ("warn" case in 09).
15
+ if (base == null)
16
+ return { layers: [], ids: [], matched: false };
17
+ if (base.kind === 'layer') {
18
+ const idx = refLayerIds.indexOf(base.id);
19
+ if (idx !== -1) {
20
+ const ids = refLayerIds.slice(0, idx + 1);
21
+ return { layers: load(ids), ids: [...ids], matched: true };
22
+ }
23
+ return { layers: [], ids: [], matched: false };
24
+ }
25
+ for (let i = 0; i <= refLayerIds.length; i++) {
26
+ const prefix = refLayerIds.slice(0, i);
27
+ if (computeStackHash(prefix) === base.id) {
28
+ return { layers: load(prefix), ids: [...prefix], matched: true };
29
+ }
30
+ }
31
+ return { layers: [], ids: [], matched: false };
32
+ }
33
+ /** Returns a failure message when the ref's policy blocks completion. */
34
+ export function checkRefPolicy(entry, manifest, waivers, approvedBy) {
35
+ const policy = entry.policy;
36
+ if (!policy)
37
+ return undefined;
38
+ const waived = new Set(waivers.map((w) => w.spec));
39
+ for (const spec of policy.requiredChecks ?? []) {
40
+ if (waived.has(spec))
41
+ continue;
42
+ const passing = (manifest?.checks ?? []).some((c) => c.spec === spec && c.result === 'pass');
43
+ if (!passing) {
44
+ return `required check "${spec}" did not pass on the candidate (waive with --waive "${spec}" --reason "...")`;
45
+ }
46
+ }
47
+ // Fail closed on unknown authorship: a candidate with no provenance
48
+ // manifest could be an agent layer with the manifest stripped — the
49
+ // approval gate must not be bypassable that way.
50
+ if (policy.requireHumanApproval &&
51
+ (manifest === undefined || manifest.author.kind === 'agent') &&
52
+ approvedBy === undefined) {
53
+ return 'ref requires human approval for agent-authored (or manifest-less) layers (pass --approved-by <principal>)';
54
+ }
55
+ return undefined;
56
+ }
57
+ /**
58
+ * True when completing this merge would rely on a waiver — a required
59
+ * check that has no passing evidence on the manifest and is only
60
+ * satisfied because it was waived. Such merges must leave a durable
61
+ * record (`manifest.merge.waived_checks`), so they cannot take the plain
62
+ * fast-forward path that appends no merge layer.
63
+ */
64
+ function waiversConsumed(entry, manifest, waivers) {
65
+ const required = entry.policy?.requiredChecks ?? [];
66
+ if (required.length === 0 || waivers.length === 0)
67
+ return false;
68
+ const waived = new Set(waivers.map((w) => w.spec));
69
+ return required.some((spec) => waived.has(spec) &&
70
+ !(manifest?.checks ?? []).some((c) => c.spec === spec && c.result === 'pass'));
71
+ }
72
+ /** Core merge flow; returns an outcome instead of exiting (transport-neutral). */
73
+ export function mergeIntoRef(store, init) {
74
+ const candidateId = store.resolveLayerId ? store.resolveLayerId(init.candidateId) : init.candidateId;
75
+ const candidate = store.loadLayer(candidateId);
76
+ const manifest = getProvenance(candidate);
77
+ const entry = store.getRef(init.into);
78
+ if (!entry)
79
+ throw new Error(`No ref named "${init.into}"`);
80
+ const oursIds = [...entry.layers];
81
+ const waivers = init.waivers ?? [];
82
+ const resolver = init.principal ?? 'unknown';
83
+ // Fast path: candidate authored against the ref's current stack. A
84
+ // merge that consumes a waiver falls through to the three-way path so
85
+ // the waiver is durably recorded on a merge layer.
86
+ if (manifest?.base?.kind === 'stack' && manifest.base.id === computeStackHash(oursIds)) {
87
+ if (init.preview) {
88
+ // Preview of a fast-forward is an empty plan.
89
+ return {
90
+ status: 'preview',
91
+ plan: { autoOps: [], conflicts: [], stats: { touched: 0, autoMerged: 0, conflicting: 0 } },
92
+ ancestorMatched: true,
93
+ };
94
+ }
95
+ const failure = checkRefPolicy(entry, manifest, waivers, init.approvedBy);
96
+ if (failure)
97
+ return { status: 'policy-failure', reason: failure };
98
+ if (!waiversConsumed(entry, manifest, waivers)) {
99
+ const refLayers = [...oursIds, candidateId];
100
+ store.setRef(init.into, { ...entry, layers: refLayers });
101
+ return { status: 'fast-forward', refLayers, ancestorMatched: true };
102
+ }
103
+ }
104
+ const ancestor = resolveAncestor(store, oursIds, manifest?.base ?? null);
105
+ // A candidate that DECLARES a base which matches nothing on the ref was
106
+ // authored against a different history: three-way planning against an
107
+ // empty ancestor would read its every op as "new" and steamroll the
108
+ // ref. Refuse unless explicitly overridden. (Baseless candidates keep
109
+ // the documented warn-and-proceed semantics — null base is a legitimate
110
+ // publish mode, not a mismatch.)
111
+ if (manifest?.base != null && !ancestor.matched && !init.preview && !init.allowUnrelated) {
112
+ return { status: 'unrelated-base', declaredBase: manifest.base };
113
+ }
114
+ const ours = oursIds.map((id) => store.loadLayer(id));
115
+ const plan = planThreeWayMerge({
116
+ ancestor: ancestor.layers,
117
+ ours,
118
+ theirs: [...ancestor.layers, candidate],
119
+ });
120
+ if (init.preview)
121
+ return { status: 'preview', plan, ancestorMatched: ancestor.matched };
122
+ let resolutionInputs = [];
123
+ if (plan.conflicts.length > 0) {
124
+ if (init.resolutions !== undefined && init.resolutions.length > 0) {
125
+ resolutionInputs = init.resolutions;
126
+ }
127
+ else if (init.resolve) {
128
+ const choice = init.resolve;
129
+ resolutionInputs = plan.conflicts.map((conflict) => {
130
+ const input = { path: conflict.path, choice };
131
+ if (conflict.componentKey !== undefined)
132
+ input.componentKey = conflict.componentKey;
133
+ return input;
134
+ });
135
+ }
136
+ else {
137
+ return { status: 'conflicts', conflicts: plan.conflicts, ancestorMatched: ancestor.matched };
138
+ }
139
+ }
140
+ const applied = applyResolutions(plan, resolutionInputs);
141
+ if (applied.unresolved.length > 0) {
142
+ return { status: 'conflicts', conflicts: applied.unresolved, ancestorMatched: ancestor.matched };
143
+ }
144
+ const failure = checkRefPolicy(entry, manifest, waivers, init.approvedBy);
145
+ if (failure)
146
+ return { status: 'policy-failure', reason: failure };
147
+ const waivedChecks = waivers.map((w) => ({
148
+ spec: w.spec,
149
+ reason: w.reason,
150
+ waivedBy: resolver,
151
+ }));
152
+ const merged = buildMergeLayer({
153
+ ops: [...plan.autoOps, ...applied.ops],
154
+ author: { kind: 'human', principal: resolver },
155
+ intent: `Merge ${candidateId} into ${init.into}`,
156
+ base: { kind: 'stack', id: computeStackHash(oursIds) },
157
+ merge: {
158
+ candidate: candidateId,
159
+ into: init.into,
160
+ resolutions: applied.resolutions,
161
+ waived_checks: waivedChecks,
162
+ resolver,
163
+ },
164
+ created: init.created,
165
+ });
166
+ // The store owns the persisted identity: a backend that canonicalizes
167
+ // or re-materializes content may return a different id than the one
168
+ // precomputed on the merge layer.
169
+ const mergeLayerId = store.storeLayer(merged.file);
170
+ const refLayers = [...oursIds, mergeLayerId];
171
+ store.setRef(init.into, { ...entry, layers: refLayers });
172
+ return {
173
+ status: 'merged',
174
+ mergeLayerId,
175
+ refLayers,
176
+ plan,
177
+ ancestorMatched: ancestor.matched,
178
+ };
179
+ }
180
+ //# sourceMappingURL=ref-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ref-flow.js","sourceRoot":"","sources":["../src/ref-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The shared layer-diff JSON (roadmap cross-cutting item): ONE shape,
3
+ * ONE implementation, consumed identically by the CLI (`ifc layer
4
+ * diff --json`), the MCP `diff_layer` tool, and the review UI. Output is
5
+ * deterministically ordered (paths and component keys sorted) so equal
6
+ * states always serialize to byte-equal JSON.
7
+ */
8
+ import type { IfcxFile } from '@ifc-lite/ifcx';
9
+ import type { StackState } from './component-state.js';
10
+ export interface ModifiedEntity {
11
+ path: string;
12
+ /** Component keys whose value changed, was added, or was removed. Sorted. */
13
+ components: string[];
14
+ }
15
+ /** The wire shape every transport emits verbatim. */
16
+ export interface StackDiff {
17
+ added: string[];
18
+ deleted: string[];
19
+ modified: ModifiedEntity[];
20
+ }
21
+ /** Diff two composed states: `from` (base) → `to` (target). */
22
+ export declare function diffStackStates(from: StackState, to: StackState): StackDiff;
23
+ /** Diff two ordered layer lists (`from` is the base side). */
24
+ export declare function diffLayerStacks(from: readonly IfcxFile[], to: readonly IfcxFile[]): StackDiff;
25
+ //# sourceMappingURL=state-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-diff.d.ts","sourceRoot":"","sources":["../src/state-diff.ts"],"names":[],"mappings":"AAIA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,cAAc,EAAE,CAAC;CAC5B;AAOD,+DAA+D;AAC/D,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,SAAS,CAsC3E;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,EAAE,EAAE,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,CAE7F"}
@@ -0,0 +1,55 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ import { componentEntries, extractStackState, snapshotOf } from './component-state.js';
5
+ function alive(state, path) {
6
+ const entity = state.get(path);
7
+ return entity !== undefined && !entity.deleted;
8
+ }
9
+ /** Diff two composed states: `from` (base) → `to` (target). */
10
+ export function diffStackStates(from, to) {
11
+ const added = [];
12
+ const deleted = [];
13
+ const modified = [];
14
+ const paths = [...new Set([...from.keys(), ...to.keys()])].sort();
15
+ for (const path of paths) {
16
+ const inFrom = alive(from, path);
17
+ const inTo = alive(to, path);
18
+ if (!inFrom && inTo) {
19
+ added.push(path);
20
+ continue;
21
+ }
22
+ if (inFrom && !inTo) {
23
+ deleted.push(path);
24
+ continue;
25
+ }
26
+ if (!inFrom && !inTo)
27
+ continue;
28
+ const fromEntity = from.get(path);
29
+ const toEntity = to.get(path);
30
+ if (!fromEntity || !toEntity)
31
+ continue;
32
+ const fromComponents = componentEntries(fromEntity);
33
+ const toComponents = componentEntries(toEntity);
34
+ const keys = [...new Set([...fromComponents.keys(), ...toComponents.keys()])].sort();
35
+ const changed = [];
36
+ for (const key of keys) {
37
+ const a = fromComponents.get(key);
38
+ const b = toComponents.get(key);
39
+ if (a === b)
40
+ continue;
41
+ const aHash = a === undefined ? undefined : snapshotOf(a).hash;
42
+ const bHash = b === undefined ? undefined : snapshotOf(b).hash;
43
+ if (aHash !== bHash)
44
+ changed.push(key);
45
+ }
46
+ if (changed.length > 0)
47
+ modified.push({ path, components: changed });
48
+ }
49
+ return { added, deleted, modified };
50
+ }
51
+ /** Diff two ordered layer lists (`from` is the base side). */
52
+ export function diffLayerStacks(from, to) {
53
+ return diffStackStates(extractStackState(from), extractStackState(to));
54
+ }
55
+ //# sourceMappingURL=state-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-diff.js","sourceRoot":"","sources":["../src/state-diff.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAW/D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAgBvF,SAAS,KAAK,CAAC,KAAiB,EAAE,IAAY;IAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;AACjD,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,eAAe,CAAC,IAAgB,EAAE,EAAc;IAC9D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAqB,EAAE,CAAC;IAEtC,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAS,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;YACpB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjB,SAAS;QACX,CAAC;QACD,IAAI,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI;YAAE,SAAS;QAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ;YAAE,SAAS;QACvC,MAAM,cAAc,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;QACpD,MAAM,YAAY,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAS,CAAC,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7F,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC;gBAAE,SAAS;YACtB,MAAM,KAAK,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC/D,MAAM,KAAK,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC/D,IAAI,KAAK,KAAK,KAAK;gBAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtC,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,eAAe,CAAC,IAAyB,EAAE,EAAuB;IAChF,OAAO,eAAe,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC;AACzE,CAAC"}