Notebook Edit
Feature Definition
Section titled “Feature Definition”Notebook editing is the ability to modify .ipynb files from agent tool calls.
Unlike plain source files, notebooks are structured JSON documents with coupled concerns:
- ordered cell arrays
- execution counts and outputs
- language/kernel metadata
- large embedded output payloads
A notebook edit primitive is useful for data-science workflows, but fragile if treated as ordinary text.
Why Notebook Editing Is Hard
Section titled “Why Notebook Editing Is Hard”The core problem is representation mismatch.
Users think in cells.
Patch engines usually operate on text lines.
That mismatch produces specific failure modes:
- JSON is syntactically valid, but the notebook is structurally invalid
- the model accidentally edits the wrong cell because it is “patching JSON text” instead of addressing a cell
- the notebook becomes enormous because outputs get re-embedded into the file
- the diff becomes unreadable because transient metadata churn dominates
The reliability bar for notebook edits is higher than for source code edits because the file format is both structured and noisy.
Notebook Shape (Practical Model)
Section titled “Notebook Shape (Practical Model)”In practice, an .ipynb notebook is a JSON object with at least:
cells: arraymetadata: objectnbformat: numbernbformat_minor: number
Cells are objects with at least:
cell_type: string (commonlycodeormarkdown)metadata: objectsource: string or array of strings
Code cells commonly also have:
execution_count: number or nulloutputs: array
Some notebooks also include a per-cell identifier field (commonly id), but this is not universal across all notebooks.
This matters because the best cell addressing mechanism is by a stable id, but many real notebooks require addressing by index or by content matching.
What “Notebook Edit” Should Mean
Section titled “What “Notebook Edit” Should Mean”There are two different product meanings:
- raw JSON editing
- “edit this file” using a generic patch tool
- the agent treats the notebook as text
- semantic notebook editing
- “edit cell #7” or “insert a markdown cell after the imports”
- the agent treats the notebook as a notebook and preserves invariants
The reference repos mostly implement (1).
OpenOxide should aim for (2), while retaining (1) as a fallback.
See Also
Section titled “See Also”- File Edit for the shared patch execution pipeline used by notebook-as-text fallbacks.
- File Write for full-document rewrite tradeoffs.
- Edit Formats for patch grammar details not repeated in this page.
Aider Implementation
Section titled “Aider Implementation”Aider at commit b9050e1d5faf8096eae7a46a9ecc05a86231384b does not provide a dedicated notebook-edit tool.
It edits notebooks via its generic file editing pipeline.
Generic Patch Path
Section titled “Generic Patch Path”Aider has a “patch” edit format implemented by PatchCoder in references/aider/aider/coders/patch_coder.py.
Key facts:
PatchCoder.edit_format = "patch"- it uses prompts defined in
references/aider/aider/coders/patch_prompts.py - it parses a V4A-like patch format:
*** Begin Patch*** Update File: ...- hunks introduced by
@@ +/-/lines
The prompt is explicit that the assistant should “describe the changes using the V4A diff format.”
That means notebooks are treated as normal files whose contents happen to be JSON.
What PatchCoder Actually Does
Section titled “What PatchCoder Actually Does”PatchCoder.get_edits():
- reads the assistant response text
- detects patch sentinels (
*** Begin Patch) - tolerates missing sentinels if the content looks patch-like (warns)
- identifies which files are needed for parsing (
identify_files_neededscans*** Update File:and*** Delete File:) - reads current file content for needed paths
- parses patch actions into a
Patchmodel (PatchAction,Chunk, etc.)
The parsing and application logic is line-based.
It includes fuzziness tolerance:
_norm()strips CR for CRLF compatibilityfind_context_core()attempts exact match, rstrip match, and strip match with different fuzz penaltiesfind_context()includes an EOF marker behavior (*** End of File) to anchor at end
None of these are notebook-aware.
They are generic text patch affordances.
Aider Prompting and Its Notebook Implications
Section titled “Aider Prompting and Its Notebook Implications”The patch prompt rules in references/aider/aider/coders/patch_prompts.py enforce:
- each file appears only once in the patch
- context lines must match “character for character, including indentation”
- use full file path
This is compatible with .ipynb editing, but it shifts the burden to the model:
- the model must avoid touching large output blocks
- the model must preserve JSON structure
There is no cell addressing abstraction.
Performance Note in Aider History
Section titled “Performance Note in Aider History”Aider’s history file (references/aider/aider/website/HISTORY.md) includes a release note:
- “Improved editing performance on Jupyter Notebook
.ipynbfiles.”
The codebase does not expose a notebook-specific editing tool.
So any improvements are likely in the general patch/text edit pipeline (parsing, matching, or formatting), not semantic cell transforms.
What This Means in Practice
Section titled “What This Means in Practice”Aider’s notebook edits are:
- possible
- generic
- fragile
You can get good results if you constrain the diff:
- only edit small, stable
sourceregions - avoid editing
outputsunless explicitly requested - avoid changing ordering in the
cellsarray
But there is no dedicated mechanism to make these constraints automatic.
Codex Implementation
Section titled “Codex Implementation”Codex at commit 4ab44e2c5cc54ed47e47a6729dfd8aa5a3dc2476 also edits notebooks via the general apply_patch tool.
Codex does not implement a notebook-specific tool.
Registration and Prompt Guidance
Section titled “Registration and Prompt Guidance”Codex exposes apply_patch through its tool registry.
Codex prompt instructions explicitly tell the assistant:
- to use the
apply_patchtool for edits - not to re-read files after calling
apply_patch
These instructions appear in:
references/codex/codex-rs/core/gpt_5_2_prompt.mdreferences/codex/codex-rs/protocol/src/prompts/base_instructions/default.md
Those instructions are generally correct for source code edits.
For notebooks, they are more dangerous.
Notebook correctness often requires validating the produced JSON structure, which may require re-reading or at least verifying the edited file.
Handler Flow
Section titled “Handler Flow”Codex applies patches through the same apply_patch machinery documented in features/tools/file-edit.
At a high level, apply_patch:
- parses the patch text
- verifies patch grammar
- applies hunks to files
- uses the approval/permission system for file mutations
There is no branch like:
- “if extension is
.ipynbthen do special handling”
So .ipynb is treated as any file.
Approval and Runtime Integration
Section titled “Approval and Runtime Integration”Notebook edits inherit the generic edit guardrails:
- approval overlays can show diff summaries
- sandbox/policy can restrict what paths can be modified
- errors are returned when patch hunks fail to apply
This is good.
But it does not enforce notebook semantics.
Test Surface
Section titled “Test Surface”Codex tests focus on patch correctness at the text level.
There is no dedicated “notebook edit” subsystem in the core.
So:
- no fixtures for notebooks
- no tests around stripping outputs
- no tests around cell-level operations
Notebook support is incidental through apply_patch.
OpenCode Implementation
Section titled “OpenCode Implementation”OpenCode at commit 7ed449974864361bad2c1f1405769fd2c2fcdf42 similarly uses its generic edit mechanisms for notebooks.
It does not implement cell-aware notebook edits.
Apply-Patch Pipeline
Section titled “Apply-Patch Pipeline”OpenCode exposes apply_patch via references/opencode/packages/opencode/src/tool/apply_patch.ts.
OpenCode’s tool registry chooses between patch-style editing and edit/write tools depending on model:
- GPT-family models use
apply_patch - others use
edit/write
So notebook editing on GPT models often uses the patch format.
Mechanics of patch application are documented in features/tools/file-edit.
Notebook-specific handling is not present.
Integration Effects
Section titled “Integration Effects”Because notebook edits pass through the normal edit plumbing, they gain:
- permission gating
- consistent CLI/TUI tool rendering
- filesystem watcher updates
- output truncation policies for tool outputs (separate system)
But they do not gain:
- a semantic model of cells
- stable cell addressing
- automatic output stripping
Registry Exposure
Section titled “Registry Exposure”There is no notebook_edit tool id.
Notebook edits are performed using general tools:
apply_patcheditwrite
This means:
- the model is not guided into safe notebook operations by tool shape
- the UI cannot present a notebook-specific diff view
Pitfalls & Hard Lessons
Section titled “Pitfalls & Hard Lessons”JSON-Valid Is Not Notebook-Valid
Section titled “JSON-Valid Is Not Notebook-Valid”A notebook can be valid JSON but still be invalid for Jupyter.
Common structural errors:
- missing top-level keys
- cells missing required fields
- wrong types (string vs array-of-strings in
source) - outputs containing unexpected object shapes
Generic patch tools do not catch these.
Output Cells Explode Diff Size
Section titled “Output Cells Explode Diff Size”The biggest failure mode is output bloat.
A single plotted image output can add:
- huge base64 strings
- large
datablobs - long metadata maps
This can:
- exceed tool limits
- blow up token budget
- make diffs unreadable
Execution Metadata Churn Pollutes Version Control
Section titled “Execution Metadata Churn Pollutes Version Control”Even when code changes are small, notebook diffs can include:
execution_countupdates- output timestamps
- kernel metadata noise
Agents should not change these unless explicitly asked.
Cell Index Drift Causes Wrong Edits
Section titled “Cell Index Drift Causes Wrong Edits”When addressing by index (“edit cell 10”), any insertion earlier shifts indices.
A model that is patching JSON text can accidentally:
- edit the wrong cell
- insert in the wrong location
This becomes catastrophic when many cells contain similar code.
Text-Level Patches Are Fragile Against Reformatting
Section titled “Text-Level Patches Are Fragile Against Reformatting”Some notebook writers normalize JSON formatting.
If the file is reserialized:
- whitespace changes
- key ordering changes
sourcebecomes string vs array
Text patch context matching becomes brittle.
LSP Diagnostics Often Do Not Help
Section titled “LSP Diagnostics Often Do Not Help”OpenCode’s post-edit LSP diagnostics are valuable for .ts, .rs, etc.
For .ipynb:
- most language servers do not understand notebook JSON
- you may only get generic JSON parse errors
So notebooks need a dedicated validator.
No First-Class Undo Granularity by Cell
Section titled “No First-Class Undo Granularity by Cell”Generic patch tools can undo file-level edits.
But users think in cells.
Undo at file granularity makes it hard to revert a single cell change while preserving other edits.
Tools Encourage “Don’t Re-Read” but Notebooks Often Require Verification
Section titled “Tools Encourage “Don’t Re-Read” but Notebooks Often Require Verification”Codex’s base guidance (“do not re-read files after apply_patch”) is good for speed.
For notebooks, it can produce silent corruption.
OpenOxide should treat notebook edits as a special-case where verification is default.
OpenOxide Blueprint
Section titled “OpenOxide Blueprint”The blueprint should aim for semantic notebook operations.
The references demonstrate that “just patch JSON” exists, but it is not robust.
Offer Two Modes
Section titled “Offer Two Modes”apply_patchfallback mode for raw text/JSON edits
- for edge cases
- for users who explicitly want raw diff edits
- dedicated
notebook_editsemantic mode
- for cell-level inserts/replaces/deletes
- for output stripping and normalization
- for stable diffs
notebook_edit Contract
Section titled “notebook_edit Contract”A practical tool contract is an operation list:
path: stringoperations: [...]
Operation variants:
-
replace_cellcell_id?: stringindex?: numbernew_cell_type?: "code" | "markdown" | "raw"new_source: stringpreserve_outputs?: bool
-
insert_cellindex: number(insert before index)cell_type: "code" | "markdown" | "raw"source: string
-
delete_cellcell_id?: stringindex?: number
-
clear_outputsmode: "all" | "code_cells"
-
set_metadatakey_path: string[]value: any
This contract makes the model talk in notebook terms.
Addressing Strategy
Section titled “Addressing Strategy”Preferred:
- address by stable cell id (when present)
Fallbacks:
- address by index
- address by content match:
- “first code cell whose source starts with
import pandas as pd”
- “first code cell whose source starts with
OpenOxide should expose helper reads to support these:
notebook_readreturning a compact cell list:- index
- cell_type
- id (if present)
- first N lines of source
Validation Pipeline
Section titled “Validation Pipeline”After applying semantic operations:
- parse notebook JSON into typed structs
- validate required keys and cell invariants
- optionally normalize fields:
- enforce
sourceas array-of-strings or string consistently
- enforce
- write back with stable formatting
On validation failure:
- do not write the file
- return a structured error describing the failing invariant
Diff Hygiene Policy
Section titled “Diff Hygiene Policy”Notebook diff hygiene must be policy-driven.
Policies worth supporting:
strip_outputs: default trueclear_execution_count: default truepreserve_metadata_keys: allowlist (default small)drop_transient_metadata_keys: denylist
This makes diffs reviewable.
It also prevents agent-induced output bloat.
UI and Approval Experience
Section titled “UI and Approval Experience”A notebook edit tool should produce a notebook-aware diff view:
- show which cells changed
- show inserted/deleted cell counts
- show source diffs per cell
Approval overlays should be cell-first.
Raw JSON diffs should be optional.
Atomicity and Undo
Section titled “Atomicity and Undo”Notebook edits should be applied atomically:
- write to a temp file
- fsync
- replace
Undo should operate at the semantic layer:
- capture “before cell” snapshots
- allow reverting only selected cell edits
Testing Strategy
Section titled “Testing Strategy”Unit tests:
- parse/serialize roundtrips
- cell operation application correctness
- output stripping and metadata normalization
Golden tests:
- real notebooks with:
- large outputs
- mixed cell types
- missing cell ids
- different
sourceencodings
Fuzz tests:
- random operation sequences should not produce invalid notebooks
Crates
Section titled “Crates”serde_jsonfor parsing- an internal
nbformat-compatible schema module (structs + validation) - OpenOxide patch/permission/event crates
Key Design Decisions
Section titled “Key Design Decisions”- notebooks should be semantic-first, patch-second
- validation must happen before write
- diff hygiene must be configurable
- UI should render cell-level changes