Skip to content

[nd 2/5] Wire NotebookDocument into the session#8843

Merged
manzt merged 6 commits intomanzt/nd-modelfrom
manzt/nd-session
Mar 25, 2026
Merged

[nd 2/5] Wire NotebookDocument into the session#8843
manzt merged 6 commits intomanzt/nd-modelfrom
manzt/nd-session

Conversation

@manzt
Copy link
Copy Markdown
Collaborator

@manzt manzt commented Mar 24, 2026

With #8842, this PR gives it a home. session.document is populated from the CellManager at startup and added to the Session protocol so all session code can reach it.

The key piece is the interception in session.notify(). Kernel notifications arrive as serialized bytes via the stream distributor, so we peek at the JSON tag to identify document transactions, deserialize them, apply to session.document (which stamps the version), and re-broadcast the versioned result. Server-side callers (like the file watcher) pass typed objects and hit the same apply() path.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Mar 25, 2026 9:03pm

Request Review

# Kernel notifications arrive as bytes via the stream distributor;
# server-side callers (e.g. file-watch) pass typed objects.
if isinstance(operation, bytes) and (
operation.find(b'"notebook-document-transaction"') >= 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this fragile if this is inside some text corpus?

transaction=applied,
)
except (ValueError, KeyError):
LOGGER.warning("Failed to apply document transaction")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we print the error here

)
)
except Exception:
LOGGER.warning("Failed to decode document transaction")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we print the error here too

name=cd.name,
config=cd.config,
)
for cd in cell_manager.cell_data()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cell_manager.build_document per discussion?

The document is populated from the CellManager at session startup and
added to the Session protocol so endpoints, extensions, and the file
watcher can access it via `session.document`.

CellManager and NotebookDocument currently track overlapping state.
Reconciling them is deferred until the document is wired into all
consumers.
manzt added 4 commits March 25, 2026 16:58
Reducer middleware maps cell actions (create, delete, move, reorder,
code edit, rename) to typed document ops, debounces them, and flushes
to `POST /api/document/transaction` where they are applied to the
session's NotebookDocument. This keeps the server-side document in sync
with user edits without requiring explicit save.

Server → frontend broadcast is a follow-up.
Maps document ops to cell reducer actions sequentially so cross-op
dependencies work (e.g., create then move in one transaction).
Create+delete pairs for the same cell are filtered as no-ops to avoid a
crash in the delete reducer on unrendered cells.
Previously only the frontend's `prepareForRunCell` action (user clicks
Run) snapshotted `lastCodeRun`. Cells executed by the kernel (e.g. via
code_mode) bypassed this and stayed visually stale even after successful
execution. Snapshotting in `handleCellMessage` when status is "queued"
closes this gap.
@manzt manzt force-pushed the manzt/nd-session branch from 1be085c to d0c7932 Compare March 25, 2026 20:59
manzt added a commit that referenced this pull request Mar 25, 2026
Prev #8842, #8843, #8844. This PR closes the kernel → session loop.

**Transaction emission.** `_apply_ops` previously assembled three
separate legacy notifications (`UpdateCellCodesNotification` grouped by
stale status, plus `UpdateCellIdsNotification`). Now it builds typed
`Op`s via `_plan_to_document_ops()` and broadcasts a **single**
`NotebookDocumentTransactionNotification`.

**Document context.** The execution endpoint snapshots
`session.document.cells` into `ExecuteScratchpadCommand.notebook_cells`.
The kernel handler sets a `ContextVar` from it so `AsyncCodeModeContext`
can read cell state from the document.`_CellsView` now delegates
entirely to the document and returns `NotebookCell` directly, removing
the `NotebookCellData` wrapper and the module-level `_cell_names` dict.
Prev #8842, #8843. This closes the frontend ↔ session loop in three
steps:

**Frontend → session.** A reducer middleware intercepts cell actions
(create, delete, move, rename, set-config) as the user performs them,
batches them into transaction ops, debounces (400ms), and flushes to
`POST /api/document/transaction`. A suppress guard prevents echo loops
when applying server-originated ops.

**Session → frontend.** `applyTransactionOps` maps each op to the
corresponding cell reducer action.

**Queued-cell staleness.** When the kernel queues a cell for execution,
the frontend now snapshots `lastCodeRun`. Previously only the
user-initiated "Run" button did this, so kernel-initiated runs (e.g. via
code_mode) left cells visually stale even after successful execution.
@manzt manzt merged commit 96315bc into manzt/nd-model Mar 25, 2026
27 of 44 checks passed
@manzt manzt deleted the manzt/nd-session branch March 25, 2026 21:03
manzt added a commit that referenced this pull request Mar 26, 2026
This PR introduces the core data model, with wiring to session (#8843),
frontend (#8844), and kernel (#8845).

`NotebookDocument` is an ordered list of `NotebookCell` entries that
applies `Transaction`s atomically. A transaction is a tuple of typed ops
(`CreateCell`, `DeleteCell`, `MoveCell`, `ReorderCells`, `SetCode`,
`SetName`, `SetConfig`) with a `source` tag identifying the writer (e.g.
`"frontend"`, `"kernel"`, `"file-watch"`).

The document validates ops for conflicts (e.g. delete + update on the
same cell) before applying. Version is `None` on creation and stamped by
`apply()`.

```python
doc = NotebookDocument([NotebookCell(id="a", code="x = 1", ...)])
tx = Transaction(ops=(SetCode(cell_id="a", code="x = 2"),), source="kernel")
applied = doc.apply(tx)  # stamps version → Transaction(version=1)
```

---------

Co-authored-by: Shahmir Varqha <Sham9871@gmail.com>
Co-authored-by: Myles Scolnick <myles@marimo.io>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bash-focus Area to focus on during release bug bash enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants