You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I searched existing issues and discussions and this hasn't been proposed yet.
What problem does this solve?
The notebook runtime persists each session as an append-only run.json
(NotebookRunDocument / NotebookRunRecord[]) — great for durability and crash
recovery, but the results are locked inside the app:
A finished analysis can't be handed to a collaborator who uses JupyterLab / VS Code,
pushed to a paper repository, or rendered on GitHub / nbviewer.
The Session Notebook viewer already shows a disabled "Download as .ipynb" button
("Notebook export is coming soon", added in feat: add read-only Session notebook viewer #130), so the need is acknowledged in the
UI but not tracked as an issue yet.
Competing open workbenches in this category treat real .ipynb files + "Open in
JupyterLab" as a headline feature; for researchers whose lab workflow is already
Jupyter-based, the lack of interop is a real migration cost away from Open Science.
The good news: NotebookRunRecord is semantically almost isomorphic to an nbformat
code cell (script ≈ source, outputs ≈ outputs, executionCount ≈ execution_count), so this is a projection problem, not a storage redesign.
Proposed solution
A three-stage plan. Each stage is independently shippable; stage 1 alone activates the
existing disabled button.
Notebook-level metadata: kernelspec (python3 / ir) plus metadata.open_science (session id, environment name, app version) — so the exported
file renders out of the box in JupyterLab / VS Code / GitHub preview while keeping
enough info to trace cells back to their runs.
Mixed-kernel sessions: .ipynb allows only one kernelspec. Proposal: set the
kernelspec from the dominant kernel and tag foreign-kernel cells via metadata.open_science.kernel, plus an optional "split export by kernel" mode
(session-python.ipynb + session-r.ipynb).
Stage 2 — import + "Open in JupyterLab"
Import: drop an .ipynb → parse cells → append one NotebookRunRecord per cell
(an imported/not-yet-executed status), so users re-run cells inside Open Science.
Reuses the existing append-only structure; no new storage format.
Open in JupyterLab: the app already manages conda environments via micromamba;
installing jupyterlab into the bound environment and launching it against the
exported notebook + the session's data directory is a thin wrapper.
Stage 3 (optional) — mirror-on-run
After each run completes, incrementally rewrite an .ipynb mirror next to run.json.
The mirror is strictly a projection — external edits to the mirror are not written
back (detected external modification prompts "import as new runs" instead). This avoids
the whole bidirectional-sync problem class.
Engineering discipline
The projection is a pure function with round-trip tests: export(import(nb)) === nb for the supported subset, and export(run.json) is
idempotent. nbformat has a JSON Schema to assert against.
No Python dependency for conversion — nbformat is just JSON; generate/parse it
in TypeScript in the main process. No jupyter nbconvert subprocess.
metadata.open_science.runId links every cell back to its run record — the same
anchor a future provenance chain (Phase 2 on the roadmap) can hang off, so this
feature and provenance reinforce each other rather than competing.
Alternatives considered
Adopt .ipynb as the native storage format (like some competing workbenches):
rejected — it would be a rewrite of a working runtime (run journaling, crash
recovery, multi-kernel routing) and .ipynb is a poor fit for an append-only
execution log. Projection keeps the engine and gains the interop.
Export via jupyter nbconvert in the managed Python env: works but adds a
slow subprocess + Python dependency for what is a pure JSON transformation.
Do nothing: the disabled button stays "coming soon", and Jupyter-native labs
keep paying a migration cost to adopt Open Science.
Related roadmap items: Phase 2 "Artifact versioning and a provenance chain" — the
per-cell runId metadata proposed here is a natural provenance anchor.
Happy to contribute this: stage 1 (export + round-trip tests) is a well-scoped PR
against src/main/notebook/ + activating the button in SessionNotebookDialog.tsx. If maintainers agree with the mapping above, I can
start with a draft PR.
Preflight checklist
What problem does this solve?
The notebook runtime persists each session as an append-only
run.json(
NotebookRunDocument/NotebookRunRecord[]) — great for durability and crashrecovery, but the results are locked inside the app:
pushed to a paper repository, or rendered on GitHub / nbviewer.
("Notebook export is coming soon", added in feat: add read-only Session notebook viewer #130), so the need is acknowledged in the
UI but not tracked as an issue yet.
.ipynbfiles + "Open inJupyterLab" as a headline feature; for researchers whose lab workflow is already
Jupyter-based, the lack of interop is a real migration cost away from Open Science.
The good news:
NotebookRunRecordis semantically almost isomorphic to an nbformatcode cell (
script≈source,outputs≈outputs,executionCount≈execution_count), so this is a projection problem, not a storage redesign.Proposed solution
A three-stage plan. Each stage is independently shippable; stage 1 alone activates the
existing disabled button.
Stage 1 — one-way export (
run.json→.ipynb, nbformat 4.5)A pure projection function in the main process. No change to existing storage;
run.jsonstays the single source of truth.Field mapping:
NotebookRunRecordscriptsource(split into lines)kernelKind: python/rcodecellkernelKind: repl/bashcodecell with a shell marker (e.g.%%bash) + metadata tagexecutionCountexecution_counttext.stdout/stderrstreamoutputoutputs(images/tables)display_datawith proper mimetypes (image/png,text/html, …)text.tracebackerroroutputrunId,startedAt,environmentmetadata.open_science.*Notebook-level metadata:
kernelspec(python3 / ir) plusmetadata.open_science(session id, environment name, app version) — so the exportedfile renders out of the box in JupyterLab / VS Code / GitHub preview while keeping
enough info to trace cells back to their runs.
Mixed-kernel sessions:
.ipynballows only one kernelspec. Proposal: set thekernelspec from the dominant kernel and tag foreign-kernel cells via
metadata.open_science.kernel, plus an optional "split export by kernel" mode(
session-python.ipynb+session-r.ipynb).Stage 2 — import + "Open in JupyterLab"
.ipynb→ parse cells → append oneNotebookRunRecordper cell(an imported/not-yet-executed status), so users re-run cells inside Open Science.
Reuses the existing append-only structure; no new storage format.
installing
jupyterlabinto the bound environment and launching it against theexported notebook + the session's data directory is a thin wrapper.
Stage 3 (optional) — mirror-on-run
After each run completes, incrementally rewrite an
.ipynbmirror next torun.json.The mirror is strictly a projection — external edits to the mirror are not written
back (detected external modification prompts "import as new runs" instead). This avoids
the whole bidirectional-sync problem class.
Engineering discipline
export(import(nb)) === nbfor the supported subset, andexport(run.json)isidempotent. nbformat has a JSON Schema to assert against.
in TypeScript in the main process. No
jupyter nbconvertsubprocess.metadata.open_science.runIdlinks every cell back to its run record — the sameanchor a future provenance chain (Phase 2 on the roadmap) can hang off, so this
feature and provenance reinforce each other rather than competing.
Alternatives considered
.ipynbas the native storage format (like some competing workbenches):rejected — it would be a rewrite of a working runtime (run journaling, crash
recovery, multi-kernel routing) and
.ipynbis a poor fit for an append-onlyexecution log. Projection keeps the engine and gains the interop.
jupyter nbconvertin the managed Python env: works but adds aslow subprocess + Python dependency for what is a pure JSON transformation.
keep paying a migration cost to adopt Open Science.
Additional context
per-cell
runIdmetadata proposed here is a natural provenance anchor.against
src/main/notebook/+ activating the button inSessionNotebookDialog.tsx. If maintainers agree with the mapping above, I canstart with a draft PR.