Skip to content

Record staleness reads on .code access only#9655

Merged
manzt merged 1 commit into
mainfrom
manzt/record-code-staleness
May 21, 2026
Merged

Record staleness reads on .code access only#9655
manzt merged 1 commit into
mainfrom
manzt/record-code-staleness

Conversation

@manzt

@manzt manzt commented May 21, 2026

Copy link
Copy Markdown
Collaborator

_CellsView._cell_view previously recorded a read on every wrapper build, so access patterns like:

first = ctx.cells[0]
ctx.edit_cell(first.id, ...)

passed the read-before-write guard without the agent ever reading the source. Reads now fire from accessing NotebookCell.code (and _CellsView.__repr__) goes through the wrapper so its previews still count.

`_CellsView._cell_view` previously recorded a read on every wrapper
build, so access patterns like:

```py
first = ctx.cells[0]
ctx.edit_cell(first.id, ...)
```

passed the read-before-write guard without the agent ever reading the
source. Reads now fire from accessing `NotebookCell.code` (and
`_CellsView.__repr__`) goes through the wrapper so its previews still
count.
Copilot AI review requested due to automatic review settings May 21, 2026 20:03
@vercel

vercel Bot commented May 21, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment May 21, 2026 8:04pm

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

No issues found across 2 files

Architecture diagram
sequenceDiagram
    participant Agent as Agent Code
    participant CellsView as _CellsView
    participant NotebookCell as NotebookCell
    participant Ctx as RuntimeContext
    participant Cell as _NotebookCell

    Note over Agent,Cell: Staleness Guard — Reads Recorded on .code Access Only

    Agent->>CellsView: Access cells (index, iterate, find, grep)
    CellsView->>NotebookCell: Construct wrapper (no read recorded)
    NotebookCell-->>Agent: NotebookCell wrapper
    Note over Agent,NotebookCell: Agent holds wrapper — no staleness recorded yet

    alt Agent accesses .code property
        Agent->>NotebookCell: cell.code
        NotebookCell->>NotebookCell: Check _record_read callback
        NotebookCell->>Ctx: _note_read(cell.id, cell.version)
        Ctx->>Ctx: Record staleness timestamp
        NotebookCell->>Cell: Delegate to _cell.code
        Cell-->>NotebookCell: Source code string
        NotebookCell-->>Agent: Source code
    else Agent calls __repr__ on wrapper
        Agent->>NotebookCell: repr(cell) or repr(cells)
        NotebookCell->>NotebookCell: __repr__ reads self.code
        NotebookCell->>Ctx: _note_read(cell.id, cell.version)
        Ctx->>Ctx: Record staleness timestamp
        NotebookCell->>Cell: Delegate to _cell.code
        Cell-->>NotebookCell: Code snippet
        NotebookCell-->>Agent: Formatted repr string
    else Agent edit_cell without prior .code read
        Agent->>CellsView: edit_cell(cell_id, new_code)
        CellsView->>Ctx: Check staleness for cell_id
        alt No prior read recorded
            Ctx-->>CellsView: StaleCellError
            CellsView-->>Agent: Raise StaleCellError
        else Read was recorded
            Ctx-->>CellsView: Allowed
            CellsView->>Cell: Update cell code
            Cell-->>CellsView: Success
            CellsView-->>Agent: Ack
        end
    end
Loading

Re-trigger cubic

@manzt manzt added the enhancement New feature or request label May 21, 2026
@manzt manzt merged commit d0ed8b2 into main May 21, 2026
43 of 46 checks passed
@manzt manzt deleted the manzt/record-code-staleness branch May 21, 2026 20:06

Copilot AI left a comment

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.

Pull request overview

This PR tightens the code-mode staleness guard so that a “read” is recorded only when cell source is actually accessed (via NotebookCell.code), rather than when a wrapper is merely constructed. This prevents edit-before-read patterns from incorrectly bypassing the staleness protection.

Changes:

  • Move staleness read recording from _CellsView._cell_view() to NotebookCell.code via an injected record_read callback.
  • Ensure _CellsView.__repr__ counts code previews as reads by going through the wrapper’s .code.
  • Expand staleness tests to cover indexing/iteration/find/grep/repr behavior under the new “reads only on .code access” invariant.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
marimo/_code_mode/_context.py Defers read tracking until .code is accessed; updates CellsView.__repr__ to record reads via wrapper previews.
tests/_code_mode/test_staleness.py Updates and extends tests to assert that wrappers/searches alone don’t satisfy staleness, while .code and repr previews do.
Comment on lines +387 to +389
# `repr(ctx.cells)` shows first-line code previews of every cell,
# so the agent has seen source snippets — editing without a
# separate read must succeed.
nb.edit_cell("0", "a = 99")
nb.edit_cell("1", "b = 99")
with pytest.raises(StaleCellError):
nb.edit_cell("0", "a = 99")
Comment on lines +313 to +316
Indexing, iteration, find, and grep all just hand out wrappers — they
do not by themselves prove the agent has seen the code. Without this
invariant, `first = ctx.cells[0]; ctx.edit_cell(first.id, ...)` would
blindly clobber code the agent never looked at.
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Development release published. You may be able to view the changes at https://marimo.app?v=0.23.7-dev85

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

3 participants