14. Notebook
The Eigenius notebook is the most accessible way to use the platform. It is a single-page React app served by the orchestrator at /notebooks/; cells run ESL, EigenQL, TypeScript, program invocations, and form-based charts against the live kernel via Connect-RPC; outputs auto-render as typed inspectors, result tables, layer-stack diagrams, program-trace trees, and Fluent charts.
If you have the docker stack up (docker compose up -d), you already have the notebook — it is bundled into the orchestrator image at build time and serves alongside the RPC endpoints. Open http://localhost:8080/notebooks/ in a browser.

This chapter is the operational reference for the notebook UX. Chain operations get their own chapters — see chapter 15 — Tags, branches, and history for the workspace panels that drive them and chapter 16 — Merge resolution for the merge surface. The TypeScript SDK that powers the notebook (and that you can use programmatically from outside it) is documented in chapter 17.
14.1. What’s in a notebook
A notebook is an ordered sequence of typed cells. Seven cell types are supported:
| Type | What it does | Run dispatch |
|---|---|---|
markdown | Render-only prose (Github-flavoured Markdown). Click the eye/edit toggle to switch to source view. | None — it just renders. |
esl | Eigenius Surface Language. Compiles + commits a layer on Run; output shows resource count + the new layer ID, with an expandable “View layer stack” accordion. | eigen.load(source, "application/x-esl") |
eigenql | EigenQL query against the active layer chain. Output renders in a Fluent DataGrid with column types from the synthesized Property metadata. | eigen.query(source) |
typescript | Sandboxed TS that runs in the browser with the SDK in scope. The cell’s return value is auto-rendered (Resource → inspector, ResultSet → table, Topology → layer stack, plain object → JSON tree). | new Function("eigen", "previousOutputs", "console", "React", "h", "charts", "nb", source) — seven injected names, not two: the SDK client, the previous cells’ outputs, a captured console, React and its createElement as h, the chart components, and nb notebook helpers (nb.rows(document) decodes a ResultSet). A cell may return a React element and the auto-renderer mounts it. |
program-run | Form-based program invocation: program IRI + one or more input IRIs. Single input renders as inspector + trace; multiple inputs render as a results table. | eigen.runProgramByIri(programIri, inputIri) per input |
chart | Form-based chart: pick a chart kind (grouped-bar / vertical-bar / horizontal-bar / donut / line / area), write an EigenQL query, bind axis columns by RETURN short-name. Output is the corresponding Fluent @fluentui/react-charts component. | eigen.query(query) then pivot rows into the kind’s data shape |
formalize | Prose, not code (D71). Runs the formalization service over the cell’s text and renders the per-unit outcome: claims, the DecisionPoint recording which reading was chosen against which runners-up, the AnaphorBindings, and an enc:CutItem for every unit that did not encode. ASYNCHRONOUS — a document costs minutes and several LLM round-trips, so the cell starts a task and polls it; watch it in the Tasks panel with kind Formalize. The artifact is NOT committed: generation stays decoupled from commitment, so you read and diff it, then land it with an explicit esl cell. Document id names the run’s doc-<id> working branch, which holds the document glossary and the run’s recorded proposer draws — keep it stable and a re-run replays those instead of re-asking the model. | eigen.formalizeDocument(prose, docId) → poll getTaskStatus → getFormalizationResult |
Cells are inserted via the hover-revealed + between any two cells (and above first / below last). Per-cell toolbar: type label · Run (when runnable) · ↑ / ↓ (move) · 🗑 (delete).
Notebook-level toolbar (top), left to right: editable title (required — Export… and Publish are disabled while it is empty) · an edit-metadata pencil · cell count and last-modified · New (empty notebook) · Open… (opens the published-notebook dialog, listing notebooks in the chain — not a file picker) · Import… (the file picker, <input type="file">) · Export… (browser download) · Collapse all / Expand all (one button, label follows state) · Reset (clear outputs) · Publish (commit notebook to a layer; see §14.5) · Run all (top-to-bottom, halts on first error).
Note the split: Open… is the chain, Import… is the filesystem. An older version of this guide described Open… as the file picker and Save as the download button; those are now Import… and Export….
Source: notebooks/.
14.2. Running the notebook
Production (docker stack)
docker compose up -d --buildThe orchestrator image (deploy/Dockerfile.orchestration) is multi-stage: stage 1 builds the SPA with vite build; stage 2 is the Deno orchestrator runtime with EIGENIUS_NOTEBOOK_STATIC=/app/notebooks set so the notebook serves alongside the RPC paths on port 8080. Single origin, no CORS, no separate dev server.
The orchestrator drops the notebook route when EIGENIUS_NOTEBOOK_STATIC is unset — useful for headless deployments where the notebook isn’t needed.
Development (live HMR)
cd notebooksnpm installnpm run devvite dev serves the SPA on port 5173 with hot-module reload. Connect-RPC traffic is proxied to the orchestrator on localhost:8080 (vite.config.ts), so you still need a kernel + orchestrator running. The proxy paths are /eigenius.v1.EigeniusKernel/* and /eigenius.v1.NotebookService/*.
This is the path to use when iterating on the notebook itself.
14.3. Bundled example notebooks
Three example notebooks ship with the repository. The patent-analysis demo is what loads on first start; all three are importable via the Imprt… button.
Patent-analysis demo
notebooks/examples/patent-analysis.json. Six cells:
- markdown — what the demo does
- esl — the patent ontology (
PatentClaim,PatentAnalysis,PatentBrief) plus theanalyze_patentprogram - eigenql —
MATCH ?r {} WHERE ?r LIKE "urn:eigenius:demo:patent:%"— list patent-namespace resources after the ESL load - esl — the transformer-patent input as a
resource patent:US10452978B2 : patent:PatentClaim { … }declaration - program-run — invoke
urn:eigenius:demo:patent:analyze_patentagainsturn:eigenius:demo:patent:US10452978B2 - typescript —
return await eigen.layerTopology({ includeResources: false });— auto-renders the layer stack
Click Run all. The first four cells finish in milliseconds; the program-run cell takes ~10–15 seconds (two LLM calls — CompleteJson extracts structured analysis, CompleteText writes the plain-language summary), then renders the typed PatentBrief output above an interactive trace tree (Program → Let analysis → ComponentTrace CompleteJson, Let summary → ComponentTrace CompleteText, with provider/model/token-count/latency on each component node).
Requires ANTHROPIC_API_KEY in the orchestrator’s environment. Without it (EIGENIUS_MOCK_LLM=true), the LLM components return canned responses but the rest of the flow still works end-to-end.
Kinase-institutions demo
notebooks/examples/kinase-institutions.json. The end-to-end showcase for the runtime substrate (chapter 11) — five Julia institutions installed, three cross-institution comorphisms registered, two storylines (Catalyst → DiffEq forward simulation; Symbolics → JuMP parameter fit). Setup script: notebooks/examples/kinase-institutions-setup.sh (run once, ~30–60 minutes cold for the five Julia env builds; subsequent runs are fast due to the buildah cache).
Cells 13–18 exercise both surfaces of the comorphism chain-reinsertion contract (D14 §9.3):
- ESL
programinvocation as a qualified-name function call (comorphisms:symbolics_to_jump(input)) — output commits at a deterministic content-hash IRI. - EigenQL
FIBER ... AS ?var INTO "<iri>"— output commits at a caller-named IRI.
See chapter 8 §8.4 for the storyline overview and the per-institution walkthroughs under platform/julia-institutions/ for one-at-a-time slow-walks.
Lean-verification demo
notebooks/examples/lean-verification.json. The showcase for the platform’s first verification institution. Walks the closed audit chain D28 §5.7 promises — verdict → proof term → mirror anchor → mirrored class — for a real Lean 4 proof of ∀ p : EigeniusFFI.Patient, p.weight ≥ 0 → p.weight + 10 ≥ 10. Setup script: notebooks/examples/lean-verification-setup.sh (fast — the Lean institution runs in-process so no Docker / Lake / env-image build is needed at notebook-load time).
Ten cells alternating markdown (audit-chain narrative) and EigenQL (one query per step backward through the chain). The first EigenQL cell finds the Verdict::Holds resource AutoOnLoad produced; subsequent cells trace the cross-references back to the chain-side Patient class declaration.
See chapter 8 §8.5 for the storyline overview and platform/lean-institution/ for the in-process verification-side slow-walk.
Formalizing prose (D71)
notebooks/examples/d71-formalize-prose.json. The showcase for the formalize cell: prose in, typed kernel-checked claims out. Uses the WRN paragraph from the prose-to-formulas-v2 demo deliberately — that paragraph’s expected output is a committed, byte-compared fixture, so the cell’s result can be checked against a known-correct artifact rather than eyeballed.
Six cells: intro → prerequisites → the formalize cell → how to read the output → how to land it → an EigenQL query over the landed claims. The markdown carries the three things that are otherwise easy to get wrong — that the artifact is not committed, that the doc-<id> branch is prunable scaffolding whose draws make a re-run free, and that an enc:CutItem is a recorded result rather than a failure.
Prerequisites: a lexicon snapshot, and nothing else to load. The encoding contract is in the kernel’s bootstrap chain and the claim-kind alignment is layered into the aligned snapshot (see the repo README, Building the lexicon image). A first run on a fresh Document id asks the model, so it needs a kernel built --features use-llm (just up-llm) and an ANTHROPIC_API_KEY; every later run on the same id replays the recorded draws for free.
Markdown maths
Markdown cells render $inline$ and $$display$$ LaTeX via remark-math + rehype-katex. The kinase notebook uses this extensively to render the Michaelis–Menten / competitive-inhibition equations alongside the FormulaTerm encodings of the same expressions, so the surface and the chain payload align visually.
14.4. The notebook file format
Notebooks are versioned JSON with a discriminated cell-type union. Schema in notebooks/src/persistence/notebook-format.ts:
{ format_version: 1, meta: { title?, description?, created?, modified?, eigenius_version? }, cells: [ // Source-bearing cell: { id: "<uuid>", type: "markdown" | "esl" | "eigenql" | "typescript", source: "..." }, // Program-run cell: { id: "<uuid>", type: "program-run", program_iri: "...", input_iris: ["...", ...] }, // Chart cell: { id: "<uuid>", type: "chart", query: "...", chart_kind: "grouped-bar" | "vertical-bar" | "horizontal-bar" | "donut" | "line" | "area", x_column: "...", y_column: "...", series_column?: "...", title?: "..." } ]}meta.title is required and must be non-empty — parseNotebook rejects a document without it, and the toolbar disables Export… and Publish until one is set. The other meta fields are optional.
Export… from the toolbar serialises the current store state to this JSON (with meta.modified updated to the export time) and triggers a browser download. Import… reads a file via <input type="file">, validates the shape, and replaces the store contents. Cell outputs are NOT persisted — they’re re-derived by re-running the cells.
14.5. Publish to layer
Beyond the on-disk file, a notebook can be published as resources in the kernel’s knowledge graph. Click Publish in the toolbar; the SDK translates the notebook into a notebook:Notebook resource referencing one notebook:Cell resource per cell, then loads them into a new layer. The accompanying ontology — ontologies/notebook/notebook-ontology.json — is part of the kernel’s twenty-layer boot chain (14th, after statistics), so publish succeeds without first registering anything.
IRIs are content-addressed:
- Cell IRI =
urn:eigenius:notebook:cell:<sha256>over the cell’s structural form:{cell_type, source}for source-bearing cells,{cell_type, program_iri, input_iris}for program-run cells,{cell_type, query, chart_kind, x_column, y_column, series_column, title}for chart cells - Notebook IRI =
urn:eigenius:notebook:<sha256>over{format_version, title, description, cells:[<cellIri>...]}. Excluded from the hash on purpose: timestamps andeigenius_version, so re-saving identical content yields the same Notebook IRI.
Identical cells across notebooks share a single Cell resource — useful for tracking “find every notebook that contains this exact ESL load” and similar queries. The ontology supports queries like MATCH ?n FROM Notebook WHERE ?n.title LIKE "%patent%" or MATCH ?c FROM Cell WHERE ?c.source LIKE "%CompleteJson%".
Translator source: clients/eigenius-ts/src/notebook.ts.
14.6. Auto-rendering of cell outputs
The notebook has type-driven renderers under notebooks/src/components/output/:
ResultTable— Fluent v9DataGridforQueryResponse.document(Eigon-CBOR ResultSet decoding)ResourceInspector—@id+is_atags + sorted property table for any CBOR-encoded resourceLayerStackView— vertical stack of layer boxes (head at top, root at bottom) with per-kind counts, walksPARENT_LAYERedges to recover the chainTraceTree— collapsible tree forProgramTraceresources; flattens right-leaning let-chains into siblings so the visual hierarchy matches dataflow order; surfaces input hashes, provider/model, and per-component latencyTypeScriptValueView— duck-typed dispatcher for TS-cell return values (Resource / ResultSet / RunProgramResponse / LoadResponse / Topology / DOM node / object / primitive). Also the mount point for chart-cell output:executeChartCellreturns a Fluent chart React element, whichTypeScriptValueViewrecognises viaisValidElementand renders directly.
The ESL-cell load output also has a “View layer stack” accordion that lazy-fetches the topology when expanded and renders it in LayerStackView.
Chart cells handle one Fluent quirk worth knowing about: LineChart and AreaChart only support numeric or Date x-axes natively. When the EigenQL query returns categorical x values (target names, compound IDs, …), renderChart maps each unique label to an integer index in encounter order — so the EigenQL ORDER BY controls layout — and labels each tick via the chart’s xAxis.tickText array. Per-point xAxisCalloutData carries the original label into hover callouts. ISO date strings (YYYY-MM-DD) are detected separately and converted to Date objects so date-axis charts render correctly without a categorical index.
14.7. Where it lives
notebooks/├── src/│ ├── App.tsx # FluentProvider + EigenProvider + WorkspaceShell│ ├── components/│ │ ├── workspace/WorkspaceShell.tsx # App root: chrome + panels, renders Notebook│ │ ├── Notebook.tsx # Toolbar + cell list│ │ ├── Cell.tsx # Per-cell shell (toolbar + body + output)│ │ ├── CellInsertGap.tsx # Hover-revealed "+" between cells│ │ ├── cells/ # MarkdownCell, ProgramRunCell, ChartCell form editors│ │ ├── editors/ # CodeMirror wrapper + ESL/EigenQL language modes│ │ └── output/ # The renderers listed in §14.6│ ├── persistence/notebook-format.ts # NotebookJson + parseNotebook validator│ └── runtime/│ ├── EigenProvider.tsx # React context for the SDK client│ ├── notebookStore.ts # Zustand: cells, meta, run state, run actions│ ├── resultDocument.ts # Eigon-CBOR ResultSet decoder│ └── traceResource.ts # ProgramTrace decoder├── examples/│ ├── patent-analysis.json # Seeded on first load│ ├── kinase-institutions.json # Three-part walkthrough: flat data → institutions → chain reinsertion│ └── kinase-institutions-setup.sh # Installs five Julia institutions + three comorphisms├── e2e/│ ├── patent-demo.spec.ts # Playwright golden flow│ └── kinase-charts.spec.ts # Chart-cell coverage (Part A of the kinase-institutions notebook)├── playwright.config.ts├── vite.config.ts # /notebooks/ base + Connect-RPC proxy in dev└── package.json # @eigenius/client + Fluent UI v9 + CodeMirrorThe SDK consumed by the notebook is a file: workspace dep on clients/eigenius-ts/ — see chapter 17.
14.8. CI
Two Playwright specs cover the notebook end-to-end:
notebooks/e2e/patent-demo.spec.ts— the LLM-free critical path: open/notebooks/, assert the cells render, clickRun all, assert both ESL load outputs appear, assert the EigenQL grid shows patent-namespace IRIs.notebooks/e2e/kinase-charts.spec.ts— chart-cell regression coverage: opens the consolidated kinase-institutions demo via theOpen…file picker, runs Part A’s cells (1–13) individually, asserts that.fui-cart__root× 4 (cartesian charts),.fui-hbc__root× 1 (horizontal-bar), and.fui-donut__root× 1 are mounted, and that no Part-A cell surfaced aCell failedMessageBar. Parts B/C are skipped because they require the Julia institutions setup, which CI does not run. This catches regressions in the categorical-x → numeric-index → tickText mapping that line/area charts depend on.
Both are wired up in .github/workflows/notebooks-tests.yml — brings the docker stack up with EIGENIUS_MOCK_LLM=true, runs Playwright, uploads the report on failure.
Run locally:
cd notebooksnpx playwright install chromium # one-timenpm run test:e2eThe test assumes the orchestrator stack is already up at http://localhost:8080.
14.9. Design references
- D22 — Notebook UX and TypeScript SDK — the spec this guide describes
- chapter 15 — Tags, branches, and history — the workspace panels that drive named refs + chain navigation
- chapter 16 — Merge resolution — picking a per-conflict strategy, the cascade gate, and the resolution flow
- chapter 17 — TypeScript SDK — the programmatic API the notebook is built on, also usable from your own code