Chapter 10 of 60

The Notebook Is Not the Program You See

Concepts

CHAPTER 10 β€” THE NOTEBOOK IS NOT THE PROGRAM YOU SEE

PART III β€” Debugging Interactive and Numerical AI

PURPOSE

Opens Part III by breaking Part II’s re-run promise on the passes-interactively/dies-on-Run-All notebook (NameError: region_map at cell 14): the displayed document vs the executed execution_count sequence, with clean-kernel Run All as the only admissible reproduction.

CENTRAL QUESTION

When the screen shows a working program and Run All shows a broken one, which record do you trust, and what probe convicts display order vs execution order?

UNIQUE CLAIM

A notebook is two programs β€” the displayed document and the kernel’s execution sequence β€” and only the gutter (execution_count gaps/repeats/non-monotonicity) plus a twice-repeated clean Run All can say which one ran; outputs are fossils of long-gone bindings, single-cell re-runs are edits not tests, and the first clean-run failure cell (not the interactive suspicion) is the conviction point β€” a gap measured at GitHub scale (24% run clean, ~4% reproduce own results, 36% of unambiguous notebooks ran out of order).

DEBUGGING OBJECT

State as execution history β€” gutter sequence, file hash, first-failure cell under clean kernels; residue (H1 stale state from edited-away cells) vs structural order dependence (H2 top-to-bottom itself broken).

CONCEPTS INTRODUCED

Display-vs-execution probe (gutter read → hash file → clean Run All ×2 → first-divergence localization); gutter as MEASUREMENT; fossil-output rule; Part III object ladder (binding→kernel namespace→dataset→tensor pipeline→training system→evaluation instrument) with rising reproduction bar (re-run → restart → lockfile rebuild → cold runs ×3 → multi-seed spreads).

CONCEPTS DEVELOPED / REUSED

First-divergence rule from Ch2 (applied to Run-All cell order); reproduction bar from Ch1/Ch4 (notebook edition: only clean Run All counts); minimization from Ch1 (code-gathering slicing = minimization on notebook history); environment parity from Ch9 (confirmed or flagged before order claims).

PREREQUISITES

Ch2 (first divergence), Ch4 (reproduction rule), Ch9 (parity), Ch1 (minimization).

LOCAL INVARIANTS

Read the gutter before reading code; distrust every output until a clean run refreshes it; single-cell greens prove nothing; first divergence under Run All wins; pin the fix as reorder-or-restore gated by Run-All CI; repeat clean runs twice (disagreement = UNKNOWN).

FAILURE MODES

Gutter blindness (reviewing text, ignoring counts); single-cell verification (warm-kernel cell-14 green); output-as-proof (green screenshots in review); reorder-by-intuition (multi-variable drags without Run All between); single-run exoneration (one green Run All as permanent health); fixing the symptom cell (cell 14 edited when cell 9 fails clean).

DIAGNOSTIC METHOD

  1. Copy the execution sequence from the gutter. 2. Hash the file. 3. Pre-write H1/H2 with distinct first-failure-location predictions. 4. Restart & Run All exactly once, record first failure; repeat. 5. Reorder-or-restore + final green Run All; file the divergence record.

RESEARCH-DERIVED IDEAS

Pimentel et al. MSR 2019 (1.4M GitHub notebooks: 24% error-free, ~4% same-results, 36% out-of-order where order unambiguous β€” bounded to public/skewed/2019-era sample); Samuel & Mietchen GigaScience 2024 (biomedical-publication notebooks, ~27k analyzed / 10,388 re-run: 1,203 error-free, 879 β‰ˆ 8.5% reproduce reported results β€” a fresher, higher-stakes replication; same picture); Wang/Kuo/Li/Zeller ASE 2020 Osiris (AST data-flow analysis reconstructs a valid execution order; 73% of 936 executable published notebooks not reproducible top-to-bottom β†’ H2 is the common case; ~82% auto-reordered, 3x prior SOTA β€” the reorder fix automated, replaces reorder-by-intuition); Chattopadhyay et al. CHI 2020 (hidden-state + nonlinear mess as top pain points); Macke et al. VLDB 2021 nbsafety (drop-in lineage kernel; 666 sessions, 117 flagged; resolving-cells ~7x likelier re-run β€” named as the built implementation); Head et al. CHI 2019 code gathering (minimal ordered slice producing a result).

EXPERIMENT / LAB

Lab 10 (PROPOSED): sabotaged notebook (threshold defined, line deleted, dependent cell still passes on residue), H1 stale-residue vs H2 order-dependence with distinct first-failure predictions, gutter recorded, two clean Run Alls. H-structure: independent var = kernel history (dirty vs clean); controls = file hash/environment/data. Success = gutter sequence + two first-divergence cells + reorder/restore fix + final green Run All.

COMPANION TOOL

Execution Order Viewer β€” accepts: notebook file (cells + execution_counts + outputs) + Run-All command. Can-establish: which program ran + where display/execution diverge, under this revision only. Cannot-establish: deleted-cell bindings (Ch11), data correctness, parity (Ch9), intent truth; never substitutes output color for diagnosis.

PREVENTION ARTIFACT

Divergence record + reordered cells committed + Run-All gate in CI; stale-output suspect list.

READER OUTCOME

Reader can reconstruct the executed program from the gutter and convict stale-residue vs order-dependence with two clean runs β€” testable via Lab 10’s Run-All record.

DEPENDENCIES

Ch1, Ch2, Ch4, Ch9.

FORWARD BRIDGE

Ch11 “Hidden Notebook State” β€” inherits the invisible remainder: green in-order Run All can still pass on bindings no visible cell defines (deleted-cell residue the gutter cannot show).

EVIDENCE / RESEARCH REQUIREMENTS

region_map case constructed illustration; Pimentel numbers GitHub/2019-bounded; async-output variance β†’ two repeats required.

ANTI-CLAIMS / LIMITS

One record convicts one ordering defect under one revision; certifies no hidden-state absence (Ch11), no cross-machine reproducibility (Ch12), no correctness; UNKNOWN where rows unmeasured.

Explain this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Apply this chapter with AI

Copy this prompt into ChatGPT, Claude, Gemini, a local model, or another AI.

Part III β€” Debugging Interactive and Numerical AI

What changes in Part III

Parts I and II debugged a program you could re-run: same code, same data, same container, same outcome β€” or a substrate diff that explained the difference. The object under inspection was small and local β€” a value at a line, a frame’s state, a handoff between two functions.

Part III keeps the discipline and grows the object. Nothing about the loop changes β€” reproduce first, find the earliest divergence, move one variable, predict the result before you look, convert the fix to a contract. What changes is what the loop points at, and how hard it is to hold still. The object moves outward, one chapter at a time:

binding β†’ kernel namespace β†’ dataset β†’ tensor pipeline β†’ training system β†’ evaluation instrument

Each of these accumulates hidden state the code that names it does not show, and each behaves less deterministically than the last β€” so the reproduction bar rises with it: from “run it again” to “restart the kernel,” to “rebuild from the lockfile,” to “three cold runs on two machines,” to “repeat across seeds and report the spread.” By the end of Part III the thing being debugged is no longer a value; it is a system, observed statistically. The method that carried Part II carries all of it unchanged.

Locked env, pinned data, and a notebook that lies anyway

Chapter 9 ended with parity: same code hash, same data hash, same locked container, same outcome. The engineer in this chapter has all of that β€” pip freeze pinned, git status clean, fixtures hashed. The notebook on screen passes. Every cell shows output, no red tracebacks. Then a colleague clicks Restart & Run All and the same notebook dies on cell 14:

# interactive session (passes)          # Restart & Run All (fails)
[12] df = load("sales_v3.parquet")     [1] df = load("sales_v3.parquet")
[15] df = clean(df, dropna=True)       [2] ...
...                                    ...
[14] report = summarize(df)  # OK      [14] report = summarize(df)
                                       NameError: name 'region_map' is not defined

OBSERVATION: identical file + identical locked environment β†’ passes interactively, raises NameError on Restart & Run All. HYPOTHESIS H1 (stale state): the interactive kernel holds a region_map defined by an earlier, since-edited cell execution that no longer exists in display order. HYPOTHESIS H2 (order dependence): cells were executed in an order that differs from top-to-bottom display, and Run All exposes the true dependency. INFERENCE: none yet β€” the traceback names the missing variable, not which history produced it. Both hypotheses predict the same NameError under Run All; only an execution-history probe separates them.

This chapter’s question: when the screen shows a working program and Run All shows a broken one, which record do you trust, and what ordered probe convicts display order vs. execution order?

Why “it works on my screen” fails

The obvious explanation β€” “cell 14 has a bug” β€” fails because cell 14 is innocent. Three habits make this defect survive review:

  1. Reading top-to-bottom. The notebook displays cells 1β†’14, so the engineer reviews them as a script. But the kernel executed them 1, 2, 12, 15, 14, 13 β€” the execution_count numbers in the gutter say so. The program that ran is the execution sequence, not the display sequence. Chapter 4’s first-divergence rule applies to the wrong artifact if you read the screen.
  2. Re-running cells as patches. A fix is tested by re-running one cell, which appends a new execution ([16]) without invalidating the stale bindings from [12] and [15]. Each re-run layers more history the display never shows. The notebook accumulates a past that no reader can see by scrolling.
  3. Trusting outputs as state. Cell outputs are fossils β€” they record what a cell printed when it last ran, possibly under bindings that no longer exist. A green output under cell 14 proves cell 14 once passed, not that it passes now under current kernel state.

OPINION: notebooks are the only mainstream programming surface where the visible program routinely differs from the executed program. Treat every notebook as suspect until its execution history is inspected.

This is measured, not folklore. Pimentel and colleagues collected 1.4 million Jupyter notebooks from GitHub and tried to run them: only 24% executed without error, and only about 4% produced the same results they originally displayed. Among the notebooks whose intended order was unambiguous, 36% had been executed out of order at least once (Pimentel et al., 2019). A later study raised the stakes and got the same picture: Samuel and Mietchen took ~27,000 notebooks from repositories linked to biomedical papers in PubMed Central β€” code that a peer reviewer nominally vouched for β€” installed dependencies where declared, and re-ran the 10,388 that could be attempted; 1,203 finished without error and only 879 (about 8.5% of those attempted) reproduced the results the notebook itself reported (Samuel & Mietchen, 2024). The displayed-vs-executed gap this chapter debugs is the single largest reason a saved notebook does not reproduce.

The method: the display-vs-execution probe

The mental model is simple: a notebook is two programs β€” the displayed document and the executed sequence β€” and the kernel only ever ran the second one. Debugging it means reconstructing the second program from execution_count metadata, then testing whether the failure is stale residue (H1) or structural ordering (H2).

    flowchart TD
    G["read the gutter: execution_count, top to bottom"] --> H["freeze the .ipynb, note the hash"]
    H --> R1["Restart and Run All β€” clean kernel, no history"]
    R1 --> D{"where is the first failing cell?"}
    D -->|"same cell as the interactive suspicion"| H1["H1: stale residue β€” a deleted/edited-away cell defined it; fix = restore it in visible text"]
    D -->|"an earlier cell"| H2["H2: order dependence β€” top-to-bottom order violates a real dependency; fix = reorder"]
    H1 --> R2["Restart and Run All again β€” a clean green run is the only admissible reproduction"]
    H2 --> R2
  

Ordered probe, cheapest exoneration first:

  1. Read the gutter, not the screen. Scan execution_count values top-to-bottom. Gaps, repeats, or non-monotonic numbers ([12], [15], [14]) are a MEASUREMENT of out-of-order execution β€” not a style complaint. Record the true execution sequence on paper.
  2. Freeze the display program. Save the .ipynb, note the file hash. This is the artifact Run All will execute top-to-bottom.
  3. Run the discriminating intervention: one clean-kernel Run All. Kernel β†’ Restart & Run All, fresh process, no history. Prediction if H1 (stale state): Run All fails (the missing region_map was residue from an edited-away cell); the interactive pass was the artifact. Prediction if H2 (order dependence): Run All also fails but at a different first-divergence cell β€” the top-to-bottom order itself violates a dependency (e.g., cell 9 uses a frame cleaned in cell 12). Either way Run All is the control; the location of its first failure separates the hypotheses.
  4. Localize the first divergence. Under Run All, the earliest failing cell is the conviction point. Everything after it is downstream symptom β€” do not fix cell 14 if cell 9 is the first red cell under clean execution.
# display-order dependency audit (run in a scratch cell, then delete it)
# MEASUREMENT, not diagnosis: reconstructs the executed program
counts = [(cell_num, nb["cells"][cell_num]["execution_count"]) for cell_num in range(len(nb["cells"]))]
out_of_order = [c for c in counts if c[1] is None or c != sorted(counts, key=lambda x: x[1] or 0)[counts.index(c)]]
print("TRUE EXECUTION SEQUENCE:", sorted(counts, key=lambda x: (x[1] is None, x[1])))

OBSERVATION (constructed illustration, not a measured run): gutter reads [1..11, 12, 15, 14, 13, 16] top-to-bottom; region_map was defined in an earlier version of cell 12 whose current text no longer defines it. Clean Run All fails at cell 12’s successor, not cell 14. UPDATED BELIEF: H1 supported β€” interactive pass relied on residue from an overwritten cell execution. The fix is a reordering plus re-execution discipline, not a cell-14 logic change. INFERENCE: any notebook fix verified only by single-cell re-run is unverified. Only a clean Run All counts as reproduction (Chapter 4’s rule, notebook edition).

Research lineage: the executed program can be recovered, and tooled

The gap is quantified and its causes are known. Beyond Pimentel’s reproduction rates, Chattopadhyay and colleagues surveyed and interviewed notebook users and found “managing hidden state” and “the mess of nonlinear execution” among the top recurring pain points, not edge cases (Chattopadhyay et al., 2020).

Staleness can be tracked automatically. Macke and colleagues built nbsafety, a drop-in Jupyter kernel that traces fine-grained lineage β€” which cell defined each variable, and when β€” and uses liveness analysis to flag cells that are unsafe to run because they depend on stale state, plus cells that would resolve the staleness. Across 666 real notebook sessions it flagged 117 with potential safety errors, and the cells it marked as staleness-resolving were about seven times more likely than chance to be the ones users actually re-ran next (Macke et al., 2021). This is the Execution Order Viewer of this chapter and the hidden-state inspector of the next, built and evaluated.

The minimal reproducing notebook can be extracted. Head and colleagues’ code gathering tools slice a messy notebook down to the minimal ordered set of cells that actually produce a chosen result (Head et al., 2019). That is Chapter 1’s minimization applied to notebook history: once the first-divergence cell is known, gather the slice that feeds it and discard the rest.

The reordering fix can be computed, not guessed. Wang, Kuo, Li, and Zeller’s Osiris parses a notebook’s cells into an abstract syntax tree, does data-flow analysis to recover which cell defines each name and which cells consume it, and searches for an execution order in which every use follows its definition. In their sample of 936 published notebooks that were executable in principle, 73% would not reproduce under straightforward top-to-bottom execution β€” hypothesis H2 is the common case, not the exotic one β€” and Osiris reconstructed a valid order for about 82% of the executable set, roughly three times the prior state of the art (Wang et al., 2020). “Reorder-by-intuition” (a failure mode below) is what this replaces: the dependency graph, not the drag-and-drop, decides the order.

Lab 10: display-order vs. execution-order probe

PROPOSED, not executed: no author-measured results are reported. The evidence this chapter requires is the reader’s own Run-All record.

Setup. Take any notebook that passes interactively (or build one: define threshold = 0.9 in cell 8, delete that line, re-run only cell 10 which uses threshold β€” it still passes on residue). You need the gutter counts and a kernel you are willing to restart.

Task.

  1. Write H1 (stale residue: interactive pass depends on overwritten history) and H2 (order dependence: top-to-bottom order itself is broken) with distinct clean-Run-All predictions before restarting. Both predict Run-All failure; they differ on where the first failure lands and whether reordering cells fixes it.
  2. Record the gutter sequence (independent variable: execution history β€” dirty interactive vs. clean Run All; controlled variables: file hash, environment, input data).
  3. Restart & Run All exactly once. Record OBSERVATION per the table (first failing cell verbatim + error line) and UPDATED BELIEF. If the Run-All failure cell differs from the interactive suspicion, H2 is live β€” try one top-to-bottom reorder and Run All again as the follow-up single-variable run.
  4. Because kernel timing and async outputs can vary, repeat the clean Run All twice; disagreement across repeats is UNKNOWN, not a conviction β€” record both.
Run Kernel OBSERVATION (first failure) UPDATED BELIEF
dirty interactive warm ___ (passes? cell ___ output) baseline, inadmissible as proof
clean Run All #1 restarted ___ convicts H1 (same file fails clean) or H2 (failure moves upstream)
clean Run All #2 restarted ___ confirms repeatability; mismatch β†’ UNKNOWN, investigate flakiness

Success criterion. A written gutter sequence, two clean Run-All records with the first-divergence cell named, and the one-line reorder-or-restore fix verified by a final green Run All. A single-cell re-run green is explicitly not completion.

Companion tool: Execution Order Viewer

What it accepts: the notebook file (cells + execution_count metadata + cell outputs) and the named repro command (Restart & Run All). What it performs: it renders the true execution sequence alongside display order, flags gaps/repeats/non-monotonic counts, marks outputs whose producing execution no longer matches current cell text, and records the first-divergence cell of the latest clean Run All. What it can establish: which program actually ran (the ordered execution list) and where display order and execution order diverge β€” under the examined file revision only. What it cannot establish: hidden bindings from deleted cells (that is Chapter 11’s inspector), data correctness, environment parity (Chapter 9), or intent truth β€” a green Run All proves order-consistency, not correctness. It never substitutes agreement or output color for diagnosis. How its output changes your next action: a flagged divergence routes to the reorder-or-restore fix plus a Run-All gate in CI; a clean sequence with a still-failing Run All routes to Chapters 11–12 (hidden state, reproducibility) with order exonerated in writing.

Paper form, sufficient for this chapter:

File hash: ___   Env lock ref: ___
Gutter sequence (display cell β†’ exec count): ___
DIVERGENCE(S): gaps ___ / repeats ___ / non-monotonic ___
Stale-output suspects (output newer than cell text?): ___
Clean Run All #1 first failure: cell ___ err ___
Clean Run All #2 first failure: cell ___ err ___
CONVICTION: H1 stale-residue / H2 order-dependence (circle; evidence line: ___)

A software implementation of much of this already exists: nbsafety (Macke et al., above) replaces the kernel and flags stale and staleness-resolving cells live. Where it is not in the reader’s stack, this record is the tool. The ordering discipline precedes any automation.

Reusable procedure: every notebook gets this before trust

  1. Read the gutter β€” copy the execution sequence before reading any code.
  2. Distrust outputs β€” treat each output as a fossil until a clean run refreshes it.
  3. Clean-kernel Run All as the only admissible reproduction; single-cell re-runs are edits, not tests.
  4. First divergence under Run All is the defect location; downstream cells are symptoms.
  5. Pin the fix β€” reordered cells committed, or restored definition re-added, gated by a Run-All check (Chapter 12’s checklist entry).

Failure modes

  • Gutter blindness. Reviewing cell text without glancing at execution_count. The numbers are the program; the text is the rumor.
  • Single-cell verification. “Fixed it β€” cell 14 passes now” after re-running only cell 14 on a warm kernel. That run inherited the same residue the bug came from.
  • Output-as-proof. Screenshots of green cells in a review thread. Outputs persist after the state that produced them is gone.
  • Reorder-by-intuition. Dragging cells until it “looks right” without a clean Run All between moves β€” multiple variables per intervention, Chapter 4’s verdict: inconclusive.
  • Single-run exoneration. One green Run All declared as permanent health. Kernel versions, data files, and seeds drift; the gate must run every time (Chapter 12).
  • Fixing the symptom cell. Editing cell 14’s summarize() when the first clean-run failure is upstream. Downstream-symptom treatment is never diagnosis.

Limits, per contract: one execution-order record convicts one ordering defect under one file revision; it does not certify hidden-state absence (Chapter 11), data correctness (Chapter 13), or future reproducibility. UNKNOWN where any Run-All row is unmeasured.

References

  • JoΓ£o Felipe Pimentel, Leonardo Murta, Vanessa Braganholo, and Juliana Freire. A Large-Scale Study About Quality and Reproducibility of Jupyter Notebooks. Proceedings of the 16th International Conference on Mining Software Repositories (MSR), 2019, pp. 507–517. https://doi.org/10.1109/MSR.2019.00077
  • Souti Chattopadhyay, Ishita Prasad, Austin Z. Henley, Anita Sarma, and Titus Barik. What’s Wrong with Computational Notebooks? Pain Points, Needs, and Design Opportunities. Proceedings of the 2020 CHI Conference on Human Factors in Computing Systems, 2020. https://doi.org/10.1145/3313831.3376729
  • Stephen Macke, Hongpu Gong, Doris Jung-Lin Lee, Andrew Head, Doris Xin, and Aditya Parameswaran. Fine-Grained Lineage for Safer Notebook Interactions. Proceedings of the VLDB Endowment 14(6), 2021, pp. 1093–1101. https://doi.org/10.14778/3447689.3447712
  • Andrew Head, Fred Hohman, Titus Barik, Steven M. Drucker, and Robert DeLine. Managing Messes in Computational Notebooks. Proceedings of the 2019 CHI Conference on Human Factors in Computing Systems, 2019. https://doi.org/10.1145/3290605.3300500
  • Jiawei Wang, Tzu-Yang Kuo, Li Li, and Andreas Zeller. Assessing and Restoring Reproducibility of Jupyter Notebooks. Proceedings of the 35th IEEE/ACM International Conference on Automated Software Engineering (ASE), 2020, pp. 138–149. https://doi.org/10.1145/3324884.3416585
  • Sheeba Samuel and Daniel Mietchen. Computational Reproducibility of Jupyter Notebooks from Biomedical Publications. GigaScience 13, 2024, giad113. https://doi.org/10.1093/gigascience/giad113

Debugging Checklist

  • Gutter execution_count sequence recorded before any code reading?
  • File hash noted; environment parity (Ch 9) confirmed or flagged?
  • H1/H2 with distinct first-failure predictions written before restart?
  • Two clean Restart-&-Run-All runs recorded with first-divergence cell?
  • Fix is reorder-or-restore, verified by final green Run All (not single-cell re-run)?
  • No output screenshot or single-run outcome treated as proof?
  • Divergence record kept as the regression artifact?

What This Chapter Established

  • The displayed notebook vs. the executed program: execution_count order is the program the kernel ran; display order is documentation until a clean Run All promotes it. Empirically the gap is large and durable: ~24% of GitHub notebooks run without error, ~4% reproduce their own results, ~36% ran out of order (Pimentel et al.); ~8.5% of biomedical-publication notebooks reproduce their reported results (Samuel & Mietchen); ~73% of executable published notebooks do not reproduce top-to-bottom (Wang et al.).
  • The problem is tooled: nbsafety (Macke et al.) tracks staleness live; code-gathering (Head et al.) slices to the minimal reproducing cell set β€” Chapter 1 minimization on notebook history; Osiris (Wang et al.) computes a valid execution order from data-flow analysis for ~82% of executable notebooks β€” the reorder fix, automated.
  • The display-vs-execution probe (gutter read β†’ hash file β†’ clean Run All Γ—2 β†’ first-divergence localization), demonstrated on the passes-interactively/fails-on-Run-All region_map case β€” constructed illustration, no measured runs claimed.
  • Lab 10 as a proposed Run-All record the reader executes; the Execution Order Viewer contract (accepts/performs/can-establish/cannot-establish/next-action).
  • What was NOT proved: absence of hidden state (deleted-cell residue is Chapter 11), cross-machine reproducibility (Chapter 12), or data/model correctness under a green Run All.
  • Forward link: even a green, in-order Run All can pass on bindings no visible cell defines β€” residue the gutter cannot show. That invisible remainder is next.

Next

Restart & Run All now passes, cells run top-to-bottom, the gutter is monotonic β€” and the notebook still cannot be trusted. Somewhere in its history a cell was deleted, a variable redefined out of order, an import shadowed and never re-run: state the kernel holds that no visible cell explains. The next chapter hunts what the execution sequence cannot show β€” hidden notebook state.