Skip to content

fix(messaging): stop reusable hint flicker on cell updates#9782

Merged
dmadisetti merged 1 commit into
mainfrom
kg/serialization-hint-flicker
Jun 5, 2026
Merged

fix(messaging): stop reusable hint flicker on cell updates#9782
dmadisetti merged 1 commit into
mainfrom
kg/serialization-hint-flicker

Conversation

@kirangadhave

@kirangadhave kirangadhave commented Jun 4, 2026

Copy link
Copy Markdown
Member

Problem

The reusable-definition hint (? / reusable badge on a top-level def/class cell) flickered whenever the cell received any update, and vanished permanently under lazy execution.

Root cause: serialization was the one CellNotification field that did not follow the partial-update convention. The frontend assigned it unconditionally, and the backend sent serialization=None on every queued/running/idle message. So each status update wiped the hint; under eager execution the post-execution broadcast re-set it (the flicker), and under lazy execution the cell never re-ran so the hint never came back. The reconnect/replay merge dropped it for the same reason.

Fix

Adopt a three-state contract for the hint, with msgspec.UNSET as the "unchanged" sentinel so None is free to mean "clear":

  • msgspec.UNSET (omitted on the wire) -> leave the hint unchanged
  • None (explicit null) -> clear the hint (cell is no longer a top-level def)
  • string -> set the hint

The frontend mirrors this as undefined (unchanged) vs null (clear) vs value. The post-execution hook now explicitly clears the hint when a cell stops being a top-level definition, and the session-view merge inherits the prior hint on UNSET, fixing the reconnect path too.

To clear the hint when a cell stops being a top-level def, we need to know if it was showing one before, otherwise every normal cell run would send a redundant clear. We track this on the DirectedGraph by cell id rather than CellImpl, since CellImpl is replaced when a cell's code changes, which is the exact transition we need to catch.

Tests

  • cell.test.ts: transitionCell keeps the hint when serialization is omitted, clears it on null, sets it on a string.
  • test_session_view.py: the reconnect merge keeps the old hint on UNSET and applies a None clear.
  • test_runtime.py: editing a top-level def into a plain assignment sends one clear; normal cell runs send none.

Follow-up tracked in MO-6367 to migrate remaining partial-update fields to the same convention.

Closes MO-6336

@vercel

vercel Bot commented Jun 4, 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 Jun 4, 2026 6:21am

Request Review

@github-actions github-actions Bot added the bash-focus Area to focus on during release bug bash label Jun 4, 2026
@kirangadhave kirangadhave added the bug Something isn't working label Jun 4, 2026
@kirangadhave kirangadhave marked this pull request as ready for review June 4, 2026 00:42

@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 7 files

Architecture diagram
sequenceDiagram
    participant FE as Frontend Cell Store
    participant WS as WebSocket Stream
    participant BE as Backend Notification
    participant Runner as Post-Exec Hook
    participant Session as Session View

    Note over FE,Session: Cell serialization hint three-state contract

    Runner->>BE: NEW: broadcast_serialization_cleared(cell_id)
    Note right of BE: Cell no longer a top-level def

    alt Cell IS a top-level definition
        Runner->>BE: broadcast_serialization(cell_id, str(serialization))
        BE->>WS: CellNotification(serialization="reusable")
    else Cell is NOT a top-level definition (NEW branch)
        Runner->>BE: broadcast_serialization_cleared(cell_id)
        BE->>WS: CellNotification(serialization=None)
    end

    WS-->>FE: CellNotification arrives
    alt serialization is undefined (msgspec.UNSET/omitted)
        FE->>FE: CHANGED: Skip update, hint unchanged
    else serialization is null
        FE->>FE: CHANGED: Clear hint (set to null)
    else serialization is string
        FE->>FE: Set hint to value
    end

    Note over FE,Session: Reconnect/replay path

    Session->>Session: CHANGED: merge_cell_notification()
    alt current.serialization is UNSET
        Session->>Session: CHANGED: Inherit previous.serialization
    else serialization is null or string
        Session->>Session: Pass through as-is
    end
    Session-->>FE: Snapshot with correct hint state

    Note over FE,Session: Key behavior changes
    Note over WS,BE: Before: serialization=None sent on every status update
    Note over WS,BE: After: serialization omitted (UNSET) unless changed
Loading

Re-trigger cubic

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 fixes flickering and lost “reusable definition” (serialization) hints by making CellNotification.serialization follow the project’s partial-update semantics: omitted/UNSET means “unchanged”, explicit null clears, and a string sets the hint. This stabilizes the hint across routine status updates, lazy execution, and reconnect/replay merges.

Changes:

  • Backend: make CellNotification.serialization tri-state via msgspec.UNSET, preserve prior hints during reconnect merge, and explicitly clear hints when a cell stops being a top-level definition.
  • Frontend: treat serialization as a partial-update field (ignore when omitted/undefined, apply when null or string).
  • OpenAPI: update schema/docs to remove the implicit default: null and describe the new contract.

Reviewed changes

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

Show a summary per file
File Description
packages/openapi/src/api.ts Updates generated TS OpenAPI typings/docs for the tri-state serialization field.
packages/openapi/api.yaml Updates OpenAPI schema/docs to reflect omission vs null vs string for serialization.
marimo/_session/state/session_view.py Preserves the previous serialization hint when the incoming notification leaves it UNSET (reconnect/replay).
marimo/_runtime/runner/hooks_post_execution.py Explicitly clears the hint when a cell stops being a top-level definition.
marimo/_messaging/notification.py Changes CellNotification.serialization to tri-state (msgspec.UNSET default) and documents the contract.
marimo/_messaging/notification_utils.py Adds a helper to broadcast an explicit clear (serialization=None).
frontend/src/core/cells/cell.ts Applies tri-state partial-update semantics for serialization (undefined no-op, null clear, string set).
Comment thread marimo/_messaging/notification.py
Comment thread marimo/_messaging/notification_utils.py
Treat the serialization hint as a tri-state partial update: msgspec.UNSET leaves it unchanged, None explicitly clears it, a string sets it. Previously every queued/status message carried serialization=None and wiped the hint (permanently under lazy execution); the reconnect merge also dropped it.

@dmadisetti dmadisetti left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🚀

@dmadisetti dmadisetti merged commit e763940 into main Jun 5, 2026
69 of 70 checks passed
@dmadisetti dmadisetti deleted the kg/serialization-hint-flicker branch June 5, 2026 20:26
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 bug Something isn't working

3 participants