Daily Tech Briefing
AI 科技速览
每天 5 分钟内学习 AI。获取最新的人工智能新闻,理解其重要性,并学习如何将其应用于您的工作。
Hacker News · 2026/7/27 23:23:07

Convergence is not enough
AI 中文解读
Automerge这类同步工具声称能让多人协作“自动合并”,但最近一项名为Livelymerge的项目发现,合并结果虽然一致,却不一定是程序能正常运行的。核心亮点在于:一个看似简单的链表操作,在不同用户同时修改后,合并出的数据要么丢节点,要么变成死循环,直接让程序崩溃。通俗来说,就像两个人同时编辑一份文档,最终版本虽然相同,但句子全被拼接得乱七八糟。传统文档合并只处理文字,而Livelymerge试图让多个用户实时共享程序的内存数据——把每个对象、指针都拿来合并,这远远超出了原本工具的能力范围。对普通用户而言,这项研究提醒我们:协同编辑软件、在线协作工具的“自动同步”背后,可能隐藏着逻辑错误。尤其是未来更多应用支持多人同时操作复杂数据时,光靠“结果一致”远不够,还得确保合并后的数据符合逻辑。目前团队尚未找到完美解法,但这次坦诚的“翻车”案例,恰恰是推动协作技术更成熟的重要一步。
Introduction
In the Livelymerge project, Dan Ingalls, Peter Van Hardenberg, and I (Alex Warth) are building a Lively Kernel-like system whose heap — every object, class, and method — is an Automerge document. The pitch, from the first note in this series, was that this would give us merges “for free”: multiple users share the same object memory, work on it concurrently (even offline), and Automerge reconciles everything.
I also wrote that merging the state of a live system is a nontrivial problem, and that there’s no way to guarantee that objects’ invariants won’t be violated. This note looks that problem straight in the eye, with a concrete example. We don’t have a solution yet — this is an acknowledgment, not a victory lap — but I’ll sketch one possible solution that we find promising, and we’d love to hear your ideas, too.
Exhibit A: a linked list
Automerge’s promise is convergence: after two clients exchange their changes, they are guaranteed to arrive at the same state. But that state is not guaranteed to be one your program can live with. To be fair, for lots of applications — documents, todo lists, sketches — Automerge’s merge does just what you’d want. But in LM we’re doing something weirder: asking it to merge the heap of a running program, pointers and all. That’s well outside Automerge’s comfort zone, and this note is about what happens out there.
Consider a humble linked list containing 1 → 2 → 3 → 4, built the way any programmer would build it in LM: each node has a next property that points to another node. Now suppose two clients modify it concurrently:
Client A swaps 2 and 3, by writing 1.next ← 3, 2.next ← 4, and 3.next ← 2. Their list now reads 1, 3, 2, 4.
Client B swaps 3 and 4, by writing 2.next ← 4, 3.next ← null, and 4.next ← 3. Their list reads 1, 2, 4, 3.
Now let’s see what happens when these changes sync. Here’s how Automerge merges them: each client’s change is a transaction, and the merge behaves as if one transaction’s writes were applied and then the other’s — Automerge deterministically picks one of the two orders, and both clients get the same one. (So arbitrary interleavings of the individual writes are not possible, which limits the number of states we can end up in.) A write that the later transaction didn’t touch survives from the earlier one; where both transactions wrote, the later one wins.
Let’s see what the two possible orders actually produce. 1.next comes from A either way (B never wrote it), 4.next comes from B either way, but 3.next — which both transactions wrote — goes to whichever came second:
A then B (3.next = null): traversing from 1 yields “1, 3” — the list has been truncated, and nodes 2 and 4 are stranded off to the side.
B then A (3.next = 2): traversing from 1 yields “1, 3, 2, 4, 3, 2, 4, …” — the list now contains a cycle, and any code that walks it will never terminate.
Let me emphasize: Automerge did nothing wrong here. Both clients converge on the same result, deterministically, exactly as promised. The trouble is that replaying B’s writes after A’s is not the same as performing B’s intent after A’s. Client B computed those three writes by looking at the original list, 1 → 2 → 3 → 4 — a state that, post-merge, no longer exists. If B’s “swap 3 and 4” had actually run after A’s change, it would have produced different writes and a perfectly good list. The merge replays effects, not intents, and the programmer’s invariants — every node appears exactly once, no cycles, the list ends — were never written down anywhere that Automerge could see. Convergence is not enough.
This is not just about linked lists
You can dodge this particular example by not building linked lists out of next pointers. Automerge has built-in datatypes with well-behaved merge semantics — its arrays (which our object model exposes directly) merge concurrent insertions and deletions the way you’d hope, and maps of various flavors are easy to represent. We lean on this hard in practice in Morphic, t
分享
阅读原文 ↗