Skip to content

feat(frontend/python): read widget state from Automerge doc instead of CommSync broadcast #809

@rgbkrk

Description

@rgbkrk

Phase B of #761: Frontend and Python read widget state from CRDT

After Phase A (#808) dual-writes widget state to both CommState and doc.comms, this phase switches the frontend and Python clients to read widget state from the Automerge document instead of the CommSync broadcast.

Frontend changes

crates/runtimed-wasm/:

  • Add get_comms_json() -> String (or get_comms() -> JsValue via serde_wasm_bindgen)
  • Return comms sorted by seq for dependency-correct widget instantiation

apps/notebook/src/:

  • Add a useWidgetDoc hook (or modify useAutomergeNotebook) that watches doc.comms changes after each sync_applied event
  • On doc change: diff comms map against WidgetStore, create/update/delete models
  • Handle CommSync broadcast as a fallback during transition (if doc.comms is empty but CommSync arrives, use it)
  • WidgetStore.createModel / updateModel / deleteModel driven by doc state, not broadcast

Key insight: The current materializeCells() pattern — read from WASM after sync, update React state — extends naturally to comms. After sync_applied, the frontend reads both cells and comms from the WASM handle.

Python changes

crates/notebook-sync/ (DocHandle):

  • Add get_comms() -> Vec<CommSnapshot> convenience method on DocHandle (calls with_doc)

crates/runtimed-py/:

  • Add session.get_widgets() / async_session.get_widgets() that reads from the doc
  • After confirm_sync(), widget state is available directly — no broadcast accumulation needed

What stays (for now)

  • CommSync broadcast still sent by daemon (removed in Phase C)
  • CommState still maintained in daemon (removed in Phase C)
  • NotebookBroadcast::Comm for comm_open/comm_msg(update)/comm_close still sent (removed in Phase C)
  • Frontend still listens for comm_custom broadcasts (these are irreducible events, never move to CRDT)

Testing

  • Open notebook with widgets → widgets render from doc state (not CommSync)
  • Second window opens → widgets appear via Automerge sync (no CommSync needed, but it's still sent as backup)
  • Python get_widgets() returns current widget state after confirm_sync()
  • Widget state update from kernel → appears in both frontend and Python via doc sync

Size

Medium-Large — frontend hook changes + WASM bindings + Python bindings.

Part of #761. Depends on #808.

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureArchitecture proposals and structural changesenhancementNew feature or requestipywidgetsWidget rendering, comm protocol, Output widgetssyncAutomerge CRDT sync protocol

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions