@izagood/avcs 0.29.0 → 0.30.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.
@@ -10,7 +10,13 @@
10
10
  // Determinism: same (base, variants in canonical order) ⇒ identical output on every
11
11
  // replica. The algorithm version is pinned into MATERIALIZER_VERSION so a different
12
12
  // merge implementation cannot silently diverge a replica's tree.
13
- export const MERGE3_VERSION = "text3/0.1.0";
13
+ // 0.2.0 file identity (docs/19). The merge of a single file's TEXT is unchanged; what
14
+ // changed is which file a content op applies to: the reducer now resolves paths through the
15
+ // rename closure, so an op set containing `rename_file` can reduce to a different (and
16
+ // correct) tree than it did at 0.1.0. A rename-free op set is byte-identical either way.
17
+ // The stamp moves so a replica still on the older substrate cannot silently disagree about
18
+ // a rename-bearing one — `Checkpoint.materializerVersion` makes the mismatch explicit.
19
+ export const MERGE3_VERSION = "text3/0.2.0";
14
20
  const MAX_LINES = 50_000; // beyond this, line-DP cost isn't worth it → atomic compare.
15
21
  const isBinary = (s) => s.includes("\u0000");
16
22
  const splitLines = (s) => s.split("\n");
@@ -155,6 +161,22 @@ function renderSpan(baseLines, start, end, members) {
155
161
  out.push(baseLines[i]);
156
162
  return joinLines(out);
157
163
  }
164
+ /**
165
+ * How many lines of `a` survive into `b` — the length of their longest common
166
+ * subsequence of lines.
167
+ *
168
+ * Derived from `diffHunks` instead of adding a second algorithm: in that edit script every
169
+ * line of `a` is either matched (producing no hunk) or consumed inside some hunk's
170
+ * `[start, end)` span, so the LCS length is `a.length` minus the total span the hunks
171
+ * cover. Exposed for the capture path's rename similarity (docs/19 §3.1), and as
172
+ * language-blind as the rest of this module — it compares lines and nothing else.
173
+ */
174
+ export function lcsLineLength(a, b) {
175
+ let consumed = 0;
176
+ for (const h of diffHunks(a, b))
177
+ consumed += h.end - h.start;
178
+ return a.length - consumed;
179
+ }
158
180
  /**
159
181
  * Line-level diff (LCS) → maximal changed segments. Each Hunk replaces base[start:end)
160
182
  * with `lines`. Unchanged lines produce no hunk. Deterministic.
@@ -1 +1 @@
1
- {"version":3,"file":"merge3.js","sourceRoot":"","sources":["../../src/merge/merge3.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"merge3.js","sourceRoot":"","sources":["../../src/merge/merge3.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { Operation } from "../objects/types.ts";
2
+ export interface AliasMap {
3
+ /** Original path → final path, chased through the whole rename chain. */
4
+ readonly final: ReadonlyMap<string, string>;
5
+ /** Paths no single destination can be derived for (concurrent multi-destination
6
+ * renames, or a rename cycle). These are NOT moved — conflict detection reports
7
+ * them and a human decides. */
8
+ readonly contested: ReadonlySet<string>;
9
+ /** rename op oid → the `fromPath` that op moved. Input to the causal condition in
10
+ * {@link resolvePath}. */
11
+ readonly byOp: ReadonlyMap<string, string>;
12
+ }
13
+ /**
14
+ * Build the path alias map from a set of `rename_file` operations.
15
+ *
16
+ * `renames` should be the ops actually projected into the tree (accepted, or whatever
17
+ * `materializeStatuses` admits). `isConcurrent(a, b)` answers whether two op oids are
18
+ * causally unrelated.
19
+ *
20
+ * Two renames moving the SAME source to DIFFERENT destinations are only a genuine
21
+ * contest when they are concurrent; when they are causally ordered they are a chain the
22
+ * author walked deliberately, and the latest (canonical (lamport, oid)) hop wins. A
23
+ * contested source is left where it is and reported, never moved somewhere arbitrary —
24
+ * silent data loss is the one outcome worse than a conflict.
25
+ *
26
+ * Every iteration order here is sorted rather than insertion-derived, so the returned
27
+ * maps are byte-identical no matter what order the caller enumerated the renames in.
28
+ */
29
+ export declare function buildAliasMap(renames: Operation[], isConcurrent: (a: string, b: string) => boolean): AliasMap;
30
+ /**
31
+ * Resolve the path an op should actually be applied at.
32
+ *
33
+ * The causal condition is what keeps this from misrouting NEW files: an op that causally
34
+ * descends from the rename was written by an author who already saw the moved world, so
35
+ * the path it names is the path it means. Only an op that is concurrent with (or precedes)
36
+ * the rename is talking about the pre-move world and needs the alias.
37
+ */
38
+ export declare function resolvePath(aliases: AliasMap, path: string, opOid: string, descendsFromRename: (opOid: string, renameOid: string) => boolean): string;
39
+ //# sourceMappingURL=aliases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliases.d.ts","sourceRoot":"","sources":["../../src/reducer/aliases.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C;;oCAEgC;IAChC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC;+BAC2B;IAC3B,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAQD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,EAAE,EACpB,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,GAC9C,QAAQ,CAwDV;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,QAAQ,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,GAChE,MAAM,CAOR"}
@@ -0,0 +1,112 @@
1
+ // Path aliases: the rename closure that restores rename × edit commutativity (docs/19 §3.2).
2
+ //
3
+ // A file's identity in this core is its PATH (`OperationTarget.entityId`). That makes a
4
+ // move and a concurrent edit of the same file look like two unrelated writes to two
5
+ // unrelated paths, and which of the two the reducer sees first decides whether the tree
6
+ // comes out right — correctness left to a lamport coincidence.
7
+ //
8
+ // The fix is to stop applying a content op at the path its author NAMED and apply it at
9
+ // the path the file actually ended up at. This module computes that mapping — "original
10
+ // path → final path" — from the accepted `rename_file` closure alone. It is a pure
11
+ // function of (rename set, canonical order, causal relation), all three of which are
12
+ // already deterministic, so the mapping is deterministic too and Pass B only reads it.
13
+ //
14
+ // Deliberately NOT here: any notion of identity stored on an operation. That is a later
15
+ // stage (docs/19 §3.3) and needs a schema field; this stage DERIVES identity from the
16
+ // rename graph and needs none.
17
+ const cmp = (a, b) => {
18
+ const x = a ?? "";
19
+ const y = b ?? "";
20
+ return x < y ? -1 : x > y ? 1 : 0;
21
+ };
22
+ /**
23
+ * Build the path alias map from a set of `rename_file` operations.
24
+ *
25
+ * `renames` should be the ops actually projected into the tree (accepted, or whatever
26
+ * `materializeStatuses` admits). `isConcurrent(a, b)` answers whether two op oids are
27
+ * causally unrelated.
28
+ *
29
+ * Two renames moving the SAME source to DIFFERENT destinations are only a genuine
30
+ * contest when they are concurrent; when they are causally ordered they are a chain the
31
+ * author walked deliberately, and the latest (canonical (lamport, oid)) hop wins. A
32
+ * contested source is left where it is and reported, never moved somewhere arbitrary —
33
+ * silent data loss is the one outcome worse than a conflict.
34
+ *
35
+ * Every iteration order here is sorted rather than insertion-derived, so the returned
36
+ * maps are byte-identical no matter what order the caller enumerated the renames in.
37
+ */
38
+ export function buildAliasMap(renames, isConcurrent) {
39
+ const direct = new Map();
40
+ const byOp = new Map();
41
+ // Canonical order first: the "latest hop wins" tie-break below must not depend on the
42
+ // order the caller happened to enumerate.
43
+ const ordered = [...renames].sort((a, b) => a.lamport - b.lamport || cmp(a.oid, b.oid));
44
+ for (const r of ordered) {
45
+ const from = r.body.fromPath;
46
+ const to = r.body.path;
47
+ if (!from || !to || from === to)
48
+ continue;
49
+ const oid = r.oid;
50
+ byOp.set(oid, from);
51
+ if (!direct.has(from))
52
+ direct.set(from, []);
53
+ direct.get(from).push({ to, oid, lamport: r.lamport });
54
+ }
55
+ const contested = new Set();
56
+ const edge = new Map(); // from → to, contest-free hops only
57
+ for (const from of [...direct.keys()].sort(cmp)) {
58
+ const hops = direct.get(from);
59
+ const destinations = new Set(hops.map((h) => h.to));
60
+ if (destinations.size > 1) {
61
+ // More than one destination for one source. Concurrent ⇒ a real contest; causally
62
+ // ordered ⇒ a chain, and the last hop is the answer.
63
+ const contest = hops.some((a, i) => hops.slice(i + 1).some((b) => a.to !== b.to && isConcurrent(a.oid, b.oid)));
64
+ if (contest) {
65
+ contested.add(from);
66
+ continue;
67
+ }
68
+ }
69
+ edge.set(from, hops[hops.length - 1].to);
70
+ }
71
+ // Chain closure. A cycle (only reachable from concurrent renames, e.g. P→Q ∥ Q→P) has
72
+ // no final path by construction — contest every path it touches.
73
+ const final = new Map();
74
+ for (const start of [...edge.keys()].sort(cmp)) {
75
+ const seen = new Set([start]);
76
+ let cur = start;
77
+ let next = edge.get(cur);
78
+ while (next !== undefined && !seen.has(next)) {
79
+ seen.add(next);
80
+ cur = next;
81
+ next = edge.get(cur);
82
+ }
83
+ if (next !== undefined) {
84
+ for (const p of seen)
85
+ contested.add(p); // cycle
86
+ continue;
87
+ }
88
+ final.set(start, cur);
89
+ }
90
+ for (const p of contested)
91
+ final.delete(p);
92
+ return { final, contested, byOp };
93
+ }
94
+ /**
95
+ * Resolve the path an op should actually be applied at.
96
+ *
97
+ * The causal condition is what keeps this from misrouting NEW files: an op that causally
98
+ * descends from the rename was written by an author who already saw the moved world, so
99
+ * the path it names is the path it means. Only an op that is concurrent with (or precedes)
100
+ * the rename is talking about the pre-move world and needs the alias.
101
+ */
102
+ export function resolvePath(aliases, path, opOid, descendsFromRename) {
103
+ const to = aliases.final.get(path);
104
+ if (to === undefined)
105
+ return path;
106
+ for (const [renameOid, from] of aliases.byOp) {
107
+ if (from === path && descendsFromRename(opOid, renameOid))
108
+ return path;
109
+ }
110
+ return to;
111
+ }
112
+ //# sourceMappingURL=aliases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliases.js","sourceRoot":"","sources":["../../src/reducer/aliases.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { Evidence, Operation, Policy } from "../objects/types.ts";
2
- export declare const MATERIALIZER_VERSION = "avcs-text3/0.1.0";
2
+ export declare const MATERIALIZER_VERSION = "avcs-text3/0.2.0";
3
3
  /** A sane built-in policy used when a repo has not authored its own. */
4
4
  export declare function defaultPolicy(): Policy;
5
5
  export interface OpEvaluation {
@@ -87,6 +87,13 @@ export interface FileConflict {
87
87
  *
88
88
  * No language knowledge — merge3 compares lines. Ancestor relations (an edit built on a
89
89
  * prior edit) are not concurrent and never flagged. Deterministic over canonical order.
90
+ *
91
+ * "The same file" means the same ALIAS-RESOLVED path, not the same declared one (docs/19
92
+ * §3.2). Two concurrent edits can reach one file by two names — one naming the path from
93
+ * before a move, the other naming it after — and bucketing them by whichever name each op
94
+ * happened to use would put them in separate buckets, never compare them, and let the
95
+ * loser's overlapping change disappear without a word. With no renames in the op set,
96
+ * resolution is the identity and the buckets are exactly the declared paths, as before.
90
97
  */
91
98
  export declare function detectFileConflicts(ops: Operation[], result: ReductionResult, blobContent: Map<string, Buffer>): FileConflict[];
92
99
  /** A single group's locally-decided statuses + the conflicts/autoDecisions it emitted. */
@@ -1 +1 @@
1
- {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/reducer/reducer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/reducer/reducer.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@
11
11
  // Phase-2 AST upgrade only swaps `keysOf` (the unit of contention) and `applyOp`.
12
12
  import { sha256hex, canonicalize } from "../core/canonical.js";
13
13
  import { evaluateOp } from "./policy.js";
14
+ import { buildAliasMap, resolvePath } from "./aliases.js";
14
15
  import { merge3 } from "../merge/merge3.js";
15
16
  import { ownersFor } from "../policy/owners.js";
16
17
  import { isBinary } from "../core/bytes.js";
@@ -125,9 +126,19 @@ export function conflictIdFor(key) {
125
126
  *
126
127
  * No language knowledge — merge3 compares lines. Ancestor relations (an edit built on a
127
128
  * prior edit) are not concurrent and never flagged. Deterministic over canonical order.
129
+ *
130
+ * "The same file" means the same ALIAS-RESOLVED path, not the same declared one (docs/19
131
+ * §3.2). Two concurrent edits can reach one file by two names — one naming the path from
132
+ * before a move, the other naming it after — and bucketing them by whichever name each op
133
+ * happened to use would put them in separate buckets, never compare them, and let the
134
+ * loser's overlapping change disappear without a word. With no renames in the op set,
135
+ * resolution is the identity and the buckets are exactly the declared paths, as before.
128
136
  */
129
137
  export function detectFileConflicts(ops, result, blobContent) {
130
138
  const anc = ancestry(ops);
139
+ // Built from the same status filter the bucket loop below uses, so the aliases describe
140
+ // exactly the ops being bucketed.
141
+ const alias = aliasCtxFor(ops.filter((o) => result.statuses.get(o.oid) === "accepted"), anc);
131
142
  const resolve = (oid) => (oid ? blobContent.get(oid) ?? Buffer.alloc(0) : Buffer.alloc(0));
132
143
  const byFile = new Map();
133
144
  for (const o of ops) {
@@ -135,7 +146,7 @@ export function detectFileConflicts(ops, result, blobContent) {
135
146
  continue;
136
147
  if (result.statuses.get(o.oid) !== "accepted")
137
148
  continue;
138
- const f = o.body.path ?? o.target.entityId;
149
+ const f = resolvedPath(o, o.body.path ?? o.target.entityId, alias);
139
150
  (byFile.get(f) ?? byFile.set(f, []).get(f)).push(o);
140
151
  }
141
152
  const out = [];
@@ -222,6 +233,23 @@ function pathsOf(op) {
222
233
  function isCrossPath(op) {
223
234
  return op.body.kind === "rename_file";
224
235
  }
236
+ /** Ops that carry the file's WHOLE content (or its absence) for one path. */
237
+ const CONTENT_KINDS = new Set(["put_file", "edit_file", "delete_file"]);
238
+ /** Build the alias context for a set of PROJECTED ops (docs/19 §3.2 Pass A). Only the
239
+ * renames that actually reach the tree may move anything, so the map is derived from
240
+ * the projected set — exactly the set `applyOp` will replay. */
241
+ function aliasCtxFor(projected, anc) {
242
+ const renames = projected.filter((o) => o.body.kind === "rename_file");
243
+ const descendsFromRename = (opOid, renameOid) => anc.get(opOid)?.has(renameOid) ?? false;
244
+ const isConcurrent = (a, b) => !descendsFromRename(a, b) && !descendsFromRename(b, a);
245
+ return { aliases: buildAliasMap(renames, isConcurrent), descendsFromRename };
246
+ }
247
+ /** The path an op actually writes, after the rename closure. */
248
+ function resolvedPath(op, declared, alias) {
249
+ if (!alias)
250
+ return declared;
251
+ return resolvePath(alias.aliases, declared, op.oid, alias.descendsFromRename);
252
+ }
225
253
  /** Keep only the synth-blob entries the final tree actually references (drops the
226
254
  * intermediate splices that get overwritten). Makes synthBlobs a pure function of the
227
255
  * final tree — which is what lets the incremental path reuse base entries exactly. */
@@ -250,10 +278,11 @@ function frontier(ops, statuses, anc) {
250
278
  function materializeProjection(ops, statuses, anc, materializeStatuses, blobContent) {
251
279
  const projected = ops.filter((o) => materializeStatuses.has(statuses.get(o.oid)));
252
280
  const ordered = kahnOrder(projected, anc);
281
+ const alias = aliasCtxFor(projected, anc);
253
282
  const tree = new Map();
254
283
  const synthBlobs = new Map();
255
284
  for (const op of ordered)
256
- applyOp(tree, op, blobContent, synthBlobs);
285
+ applyOp(tree, op, blobContent, synthBlobs, alias);
257
286
  return { tree, treeHash: treeHashOf(tree), headOps: frontier(ops, statuses, anc), synthBlobs: pruneSynth(tree, synthBlobs) };
258
287
  }
259
288
  /**
@@ -269,12 +298,20 @@ function materializeProjection(ops, statuses, anc, materializeStatuses, blobCont
269
298
  function materializeIncremental(ops, statuses, anc, materializeStatuses, blobContent, base, dirtyPaths) {
270
299
  const projected = ops.filter((o) => materializeStatuses.has(statuses.get(o.oid)));
271
300
  const ordered = kahnOrder(projected, anc);
272
- // Replay only dirty-touching ops, in the SAME global order, into a fresh tree.
301
+ // The alias map is a function of the WHOLE projected rename set, never of the dirty
302
+ // subset — otherwise the warm path would route ops differently from a cold reduce.
303
+ const alias = aliasCtxFor(projected, anc);
304
+ // Replay only dirty-touching ops, in the SAME global order, into a fresh tree. `pathsOf`
305
+ // is the DECLARED path, while `applyOp` writes at the alias-RESOLVED one; the two agree
306
+ // only because `dirtyPaths` marks both ends of every projected rename (see the caller's
307
+ // cross-path rule). If that ever narrows, an op could be replayed while the path it
308
+ // actually writes keeps a stale base entry, or be skipped while its resolved path was
309
+ // dropped as dirty. `AVCS_VERIFY_INCREMENTAL=1` and the C22 cases gate it.
273
310
  const replayTree = new Map();
274
311
  const replaySynth = new Map();
275
312
  for (const op of ordered) {
276
313
  if (pathsOf(op).some((p) => dirtyPaths.has(p)))
277
- applyOp(replayTree, op, blobContent, replaySynth);
314
+ applyOp(replayTree, op, blobContent, replaySynth, alias);
278
315
  }
279
316
  // Final tree = clean base paths (not dirty) + replayed dirty paths.
280
317
  const tree = new Map();
@@ -567,6 +604,16 @@ export function reduceIncremental(snap, next) {
567
604
  if (projectedNow(oid) !== projectedBase(oid) || ancestryExtended(oid))
568
605
  for (const p of pathsOf(o))
569
606
  dirtyPaths.add(p);
607
+ // Every projected rename dirties BOTH its ends, unconditionally — even when nothing
608
+ // about the rename itself changed. This is what makes path aliases safe on the warm
609
+ // path (docs/19 §3.2, §6 R2): the alias map is a function of the whole op set, so an
610
+ // arriving rename changes where PRE-EXISTING content ops write, and it can invalidate
611
+ // a cached entry at a path NOTHING in the delta mentions — e.g. a second move of an
612
+ // already-moved source has to vacate the first destination. Since every aliased path
613
+ // is by construction one end of some projected rename, dirtying both ends covers every
614
+ // path whose value the alias closure can shift. Narrowing this rule breaks warm/cold
615
+ // agreement; `AVCS_VERIFY_INCREMENTAL=1`, incremental-equivalence and the C22 cases in
616
+ // test/rename-incremental.test.ts are what catch it.
570
617
  else if (projectedNow(oid) && isCrossPath(o))
571
618
  for (const p of pathsOf(o))
572
619
  dirtyPaths.add(p);
@@ -637,18 +684,81 @@ export function deserializeSnapshot(raw) {
637
684
  const stats = { groupsTotal: perKey.size, groupsRecomputed: 0, groupsReused: perKey.size, dirtyKeys: 0 };
638
685
  return { input, result, perKey, groupOrder: s.groupOrder, groupMembers: entriesToMap(s.groupMembers), stats };
639
686
  }
687
+ /**
688
+ * Is `o`'s tree effect fully carried by some later op in the same group, so that dropping
689
+ * `o` from the projection loses nothing?
690
+ *
691
+ * A content op (`put_file`/`edit_file`/`delete_file`) states the file's whole content for
692
+ * one path, so a causally later content op on the same key does subsume an earlier one.
693
+ * Two things do NOT:
694
+ *
695
+ * - A `rename_file` carries no content. It neither subsumes a content op nor is
696
+ * subsumed by one; a move followed by anything still has to be replayed or the file
697
+ * it moved never reaches the destination.
698
+ * - A content op on a path a rename VACATED in between. `put_file(P)` after
699
+ * `rename(P→Q)` is a different file that happens to reuse the name, so it cannot
700
+ * stand in for the `put_file(P)` whose content went to Q.
701
+ *
702
+ * Before path aliases existed this distinction was invisible, because a group's covered
703
+ * ops were dropped wholesale and a rename found nothing to move — which is why a plain
704
+ * create-then-move materialized an empty tree.
705
+ */
706
+ function subsumedInGroup(o, groupOps, anc) {
707
+ if (!CONTENT_KINDS.has(o.body.kind))
708
+ return false;
709
+ const oid = o.oid;
710
+ const descendsFrom = (x, id) => anc.get(x.oid)?.has(id) ?? false;
711
+ for (const later of groupOps) {
712
+ if (later === o || !descendsFrom(later, oid))
713
+ continue;
714
+ if (!CONTENT_KINDS.has(later.body.kind))
715
+ continue;
716
+ const separated = groupOps.some((z) => z.body.kind === "rename_file" && descendsFrom(z, oid) && descendsFrom(later, z.oid));
717
+ if (!separated)
718
+ return true;
719
+ }
720
+ return false;
721
+ }
722
+ /**
723
+ * Does this contended group's frontier consist of a move plus concurrent edits of the
724
+ * file being moved — the combination Pass B composes rather than contests?
725
+ *
726
+ * Requires the group key to be the rename's SOURCE. When the key is the DESTINATION the
727
+ * ops are genuinely fighting over who occupies that path (`rename(A→C)` ∥ `edit(C)`),
728
+ * which the alias map cannot compose and a human must settle. Renames present must all
729
+ * agree on the destination, so concurrent multi-destination moves (docs/19 C13) stay a
730
+ * conflict. And only `edit_file` composes: a concurrent `put_file` has no merge base
731
+ * (docs/15 §3), while `delete_file` ∥ rename keeps whatever it meant before (docs/19 R5).
732
+ */
733
+ function composesWithRename(key, viable) {
734
+ const path = key.startsWith("file:") ? key.slice("file:".length) : undefined;
735
+ if (path === undefined)
736
+ return false;
737
+ const renames = viable.filter((o) => o.body.kind === "rename_file");
738
+ if (renames.length === 0)
739
+ return false;
740
+ if (!renames.every((r) => (r.body.fromPath ?? r.target.entityId) === path))
741
+ return false;
742
+ if (new Set(renames.map((r) => r.body.path)).size !== 1)
743
+ return false;
744
+ return viable.every((o) => o.body.kind === "rename_file" || (o.body.kind === "edit_file" && (o.body.path ?? o.target.entityId) === path));
745
+ }
640
746
  function decideGroup(key, groupOps, anc, verdicts, evalOf, policy, conflicts, autoDecisions) {
641
747
  const out = new Map();
642
- // Frontier of this group: ops not an ancestor of another group member.
748
+ // Frontier of this group: ops not an ancestor of another group member. Only the frontier
749
+ // can contend — an op some later op was built on top of is history, not a competitor.
643
750
  const heads = groupOps.filter((o) => {
644
751
  for (const other of groupOps)
645
752
  if (other !== o && anc.get(other.oid)?.has(o.oid))
646
753
  return false;
647
754
  return true;
648
755
  });
756
+ // Being off the frontier is not the same as being redundant. A covered op is dropped
757
+ // from the projection ("superseded") only when a later op actually SUBSUMES its effect;
758
+ // otherwise it still has to be replayed or its contribution to the tree is destroyed.
649
759
  for (const o of groupOps)
650
760
  if (!heads.includes(o))
651
- out.set(o.oid, "superseded");
761
+ out.set(o.oid, subsumedInGroup(o, groupOps, anc) ? "superseded" : "accepted");
652
762
  // 1) Honor explicit human decisions first (H1) — globally, regardless of grouping.
653
763
  const forcedAccept = heads.filter((o) => verdicts.get(o.oid) === "accept");
654
764
  const forcedReject = heads.filter((o) => verdicts.get(o.oid) === "reject");
@@ -696,6 +806,15 @@ function decideGroup(key, groupOps, anc, verdicts, evalOf, policy, conflicts, au
696
806
  out.set(o.oid, "accepted");
697
807
  return out;
698
808
  }
809
+ // A move and a concurrent edit of the file being moved also compose (docs/19 §3.2):
810
+ // Pass B routes the edit to the rename's destination, so there is nothing to choose
811
+ // between — the two changes are independent and both survive. See `composesWithRename`
812
+ // for which combinations this deliberately does NOT cover.
813
+ if (viable.length > 1 && !needsHuman && composesWithRename(key, viable)) {
814
+ for (const o of viable)
815
+ out.set(o.oid, "accepted");
816
+ return out;
817
+ }
699
818
  const ranked = [...viable].sort((a, b) => {
700
819
  const d = evalOf(b, inConflict).score - evalOf(a, inConflict).score;
701
820
  if (d !== 0)
@@ -788,20 +907,31 @@ function kahnOrder(ops, anc) {
788
907
  }
789
908
  return order;
790
909
  }
791
- function applyOp(tree, op, blobContent, synthBlobs) {
910
+ /**
911
+ * Apply one projected op to the tree.
912
+ *
913
+ * Every content op is applied at its ALIAS-RESOLVED path, not at the path its body names
914
+ * (docs/19 §3.2 Pass B). That is what makes `rename_file` and `edit_file` commute: the
915
+ * edit lands where the file actually lives regardless of which of the two the canonical
916
+ * order put first. With no renames in the op set the alias map is empty, resolution is
917
+ * the identity, and this function behaves exactly as it did before.
918
+ */
919
+ function applyOp(tree, op, blobContent, synthBlobs, alias) {
792
920
  const b = op.body;
793
921
  const resolve = (oid) => synthBlobs.get(oid) ?? blobContent.get(oid) ?? Buffer.alloc(0);
922
+ const at = (declared) => resolvedPath(op, declared, alias);
794
923
  switch (b.kind) {
795
924
  case "put_file":
796
925
  if (b.path && b.blobOid)
797
- tree.set(b.path, b.blobOid);
926
+ tree.set(at(b.path), b.blobOid);
798
927
  break;
799
928
  case "edit_file": {
800
929
  if (!b.path || !b.blobOid)
801
930
  break;
931
+ const path = at(b.path);
802
932
  const opNew = resolve(b.blobOid);
803
933
  const opBase = b.baseBlobOid ? resolve(b.baseBlobOid) : Buffer.alloc(0);
804
- const currentOid = tree.get(b.path);
934
+ const currentOid = tree.get(path);
805
935
  const current = currentOid !== undefined ? resolve(currentOid) : opBase;
806
936
  // Binary route: a NUL byte means line-merge is meaningless. Skip merge3 and keep a
807
937
  // deterministic incumbent — leave the existing tree entry (currentOid) untouched if
@@ -811,7 +941,7 @@ function applyOp(tree, op, blobContent, synthBlobs) {
811
941
  break; // incumbent stays; tree unchanged
812
942
  const synthOid = `blob_${sha256hex(opNew).slice(0, 32)}`;
813
943
  synthBlobs.set(synthOid, opNew);
814
- tree.set(b.path, synthOid);
944
+ tree.set(path, synthOid);
815
945
  break;
816
946
  }
817
947
  // Apply this op's patch (opBase→opNew) onto the accumulated content. Disjoint
@@ -822,21 +952,33 @@ function applyOp(tree, op, blobContent, synthBlobs) {
822
952
  const mergedBuf = Buffer.from(m.merged, "utf8");
823
953
  const synthOid = `blob_${sha256hex(mergedBuf).slice(0, 32)}`;
824
954
  synthBlobs.set(synthOid, mergedBuf);
825
- tree.set(b.path, synthOid);
955
+ tree.set(path, synthOid);
826
956
  break;
827
957
  }
828
958
  case "delete_file":
829
- tree.delete(b.path ?? op.target.entityId);
959
+ tree.delete(at(b.path ?? op.target.entityId));
830
960
  break;
831
- case "rename_file":
832
- if (b.fromPath && b.path) {
833
- const blob = tree.get(b.fromPath);
834
- if (blob !== undefined) {
835
- tree.delete(b.fromPath);
836
- tree.set(b.path, blob);
837
- }
961
+ case "rename_file": {
962
+ if (!b.fromPath || !b.path)
963
+ break;
964
+ // A rename the alias map resolved has nothing left to move: every content op that
965
+ // predates or is concurrent with it was already routed to the destination, so the
966
+ // source path was never written. This replaces the old "move whatever happens to be
967
+ // at fromPath right now", which is precisely where the order dependence lived — the
968
+ // answer depended on how many content ops the canonical order had applied by then.
969
+ //
970
+ // A rename the map could NOT resolve (a contested source: concurrent moves to
971
+ // different destinations, or a cycle) keeps the original behaviour, so a contest
972
+ // that a human resolves down to one rename still moves the file.
973
+ if (alias?.aliases.final.has(b.fromPath))
974
+ break;
975
+ const blob = tree.get(b.fromPath);
976
+ if (blob !== undefined) {
977
+ tree.delete(b.fromPath);
978
+ tree.set(b.path, blob);
838
979
  }
839
980
  break;
981
+ }
840
982
  case "note":
841
983
  break;
842
984
  }