Repository navigation
Replace the Loro workspace doc with a wedge-proof row registry (sidebar sync) - #18
Merged
Merged
Conversation
…orkspace doc Edge: RegistryRoom DO (SQLite rows + seq cursor, per-field HLC LWW in registry-core.ts), /registry/:orgId routes, wrangler v2 migration. Rust: comet-doc RegistryDoc (authoritative rows + pending overlay, typed WorkspaceDoc-compatible API, legacy-doc migration seeding), comet-sync RegistryClient (hello/cursor, push/ack, presence, probe/redial, backoff) + mock server; WorkspaceHost swapped onto the registry with the same public surface, presence tripwire, and SyncStatus stats. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gratenes
pushed a commit
to Gratenes/zeron
that referenced
this pull request
Sep 20, 2026
Merge upstream zeronsh/zeron v0.2.68 (preserving fork changes)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces the Loro CRDT workspace doc with a server-authoritative row registry: the sidebar index (devices, spaces, chats, session status) is now a plain table in a Durable Object with per-field last-write-wins, a monotonic change cursor, and live row broadcasts. Session docs (transcripts) stay on Loro — this only changes the sidebar plane. Design doc:
docs/registry-sync.md.Why
Three incidents in one week (2026-07-30, 08-04, 08-05) shared one root cause: the CRDT keeps history, and the workspace DO had to replay/re-export that history in wasm under a CPU limit — while loro's snapshot export deterministically RangeErrors on workerd, so the room could never self-compact. All that machinery bought masterless merge that the topology never uses: every write already flows through one DO. ~150 sessions wedged the room; the problem was never the row count, it was the remembered history.
The registry stores current state only. 10,000 sessions ≈ a few MB of rows, bounded forever. DO cold start is a table read — no replay, no wasm, no compaction, no fold budgets. The wedge class is gone by construction.
Live demo — remote engine drives the sidebar through the registry
Headed app (device A) on the left; every change below arrives from a second, headless engine ("demo-vps", device B) through a real RegistryRoom DO: space created, chat created, a mock run (Working → done indicators), two renames, and a space rename.
🎬 Full-resolution video:
docs/media/registry-sync/live-sync.mp4Design
edge/src/registry-room.ts— new DO class: applies pushed op batches atomically with per-field HLC LWW (registry-core.ts, pure + unit-tested), bumps oneseqper batch, broadcasts merged rows to every socket. Cursor sync:hello {cursor}→ only rows withseq > cursor. Tombstone GC (30d) with agcFloorfull-resync guard; nightly seq-monotonic R2 backup; per-devicepushOutcomesattribution on/registry/:orgId/statsfrom day one.crates/doc/src/registry.rs—RegistryDoc: authoritative rows + pending-op overlay (optimistic reads, offline queue), typed API drop-in forWorkspaceDoc, whole-state persistence inDocsStore. The merge fn mirrors the TS one 1:1 with shared test vectors.crates/sync/src/registry.rs—RegistryClient: hello/cursor handshake, push/ack, presence beats (memory-only, replaces%EPH), probe/redial liveness with the same deaf-socket discipline asRoomClient, reconnect backoff,RoomStatsSnapshot-compatible stats socomet syncand SyncStatus render unchanged.WorkspaceHost— same public surface; presence deafness tripwire, relay-probe fallback, and seen-cache all preserved.Conflict semantics are unchanged in practice: the loro schema resolved same-field conflicts by LWW map sets already; the registry runs the same rule as ~40 auditable lines (HLC =
ms-counter-deviceId, total order).updateops never invent rows; deletes only win when causally newer; batches apply atomically (space-delete cascades).Migration — instant, on first boot
No
registry1snapshot → read the legacyworkspace2loro snapshot → every row becomes a pending upsert with historical HLCs derived from the row's own timestamps (live writes always beat migrated values) → saved immediately. The UI reads the overlay before any server contact — zero visible gap. N devices seeding the same converged doc is idempotent. Oldws4rooms are simply never joined again (legacy/workspaceroutes remain for older builds); the legacy snapshot stays on disk for rollback.Recovery got strictly better: a wiped/reset room is detected by seq regression on the next hello and every client re-seeds it automatically with original per-field clocks — the 2026-08-05 manual repair recipe, built in and lossless.
POST /registry/:orgId/resetexists for operators and is safe to use.Testing
registry-core.test.ts, 13 cases): field LWW, tie-break by device, null-field deletes, tombstone/revive causality, guard tombstones, re-seed clock preservation, convergence over all causally-valid arrival orders, validation.crates/doc/src/registry/tests.rs, 21 cases): the same merge vectors mirrored, HLC monotonicity across clock regressions, wire-shape compatibility with the TS JSON, overlay/ack lifecycle, cascade delete, seen-marker monotonic guard, persistence round-trip, migration seeding (identical state, live-write precedence, N-device idempotence).crates/sync/tests/registry_client.rs, 10 cases, in-process mock server running the same merge fn): live two-client convergence, cursor delta on reconnect, idempotent replay of unacked writes, server-wipe auto-reseed, gcFloor full resync, presence, probe stats, DocsStore restart with offline queue, and a churn regression: 2,001 writes → 2 rows, and a fresh device syncs 2 rows, not 2,001 updates.registry_edge.rs --ignored— two Rust clients through the actual TS DO (convergence, LWW agreement across the language boundary, presence, probe, 500-write churn bounded); the two-engine--ignoredlive test;scripts/e2e-smoke.shpasses every registry step (device rows, space/chat rows, cross-device command queue).cargo test --workspacegreen; clippy clean on changed crates; edgetsc+vitestgreen;wrangler deploy --dry-runvalidates the DO migration (v2: new_sqlite_classes: [RegistryRoom]).Known-unrelated: the smoke's final step (session-doc transcript to the viewer device) stalls on this box's local workerd — it fails on
maintoo (earlier, even: the loro workspace room can't sync a single row there), and the transcript path is untouched by this PR. The demo recording above shows transcripts syncing fine in the same environment.Notes
comet sync, SyncStatus, ProbeSync, and the Devices page work unchanged.ws4rooms are still there.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.