- Clean up evaluation:
    - [Done] Get rid of eval_expression (exec_eval already does the job)
    - Capture errors correctly
        - [Done] Save traceback in REPL-entered cells and continue the REPL.
        - [Done] In normal run, abort on error after writing the processed state.
        - [Done] Preserve checkpoints, if we catch an exception. In general,
          reconcile whether the notebook (the processed part) should be written
          out, if an exception occurs.
        - [Done] Exceptions thrown during checkpointing shouldn't crash the
          system, just report that checkpointing failed. (E.g., if an object is
          not pickleable.)
    - [No reason] Lift more logic from ptpython
    - [Fixed: locals = globals] Possibly related: debug usage with joblib.Parallel
- Add parse/memory benchmarks for large notebooks
    - Measure parse time, memory use, and time-to-first-execution for notebooks
      with many cells, large inline PNGs, external PNG archives, checkpoints,
      and variable caches before considering parser/executor streaming.
    - A broad generator rewrite is not justified without benchmark evidence:
      checkpoint discovery, main-guard expansion, external metadata handling,
      and blank-line cell boundaries all make streaming semantics subtle.
- Better, explicit Vim support
- Consider bundling a true KaTeX renderer for standalone HTML. [Done]
  --standalone --katex now embeds bundled KaTeX CSS, JavaScript, and fonts only
  when markdown cells contain math delimiters.

Done:

- Write external zip archives atomically
- Retire broad parser/executor generator streaming TODO; passthrough output and
  external archives address the known latency and size bottlenecks for now
- Add CLI reference documentation
- Add workflow examples and sample notebooks
- Add changelog entry for the hardening work
- Bundle KaTeX assets for standalone math HTML only when math is present
- Add Saturn external archive manifests and refuse unknown archive overwrites
- Add --force-external for intentional archive replacement
- Add KaTeX license and provenance notices for bundled assets
- Document release validation and external archive lifecycle
- Add checked-in compatibility fixtures for inline output and main-guard notebooks
- Add subprocess coverage for clean, image, rehash, and standalone KaTeX conversion
- Extract typed archive manifest and overwrite-validation helpers
- Resolve explicit relative external archives beside output notebooks
- Add subprocess coverage for extract/embed archive round-trips
- Add semantic compatibility tests for legacy inline checkpoint and variable caches
- Parse cells inside top-level `if __name__ == '__main__':` guards and skip sibling branches
- Replace argh string-annotation CLI metadata with explicit decorators
- Add mypy checks for typed leaf modules and run them in CI
- Add release validation script for pytest, Ruff, mypy, and build
- Add semantic checkpoint and variable cache round-trip tests
- Make external archive metadata portable for sibling zip files
- Reject unsafe external archive member names when loading content
- Document execution error policy
- Add CLI subprocess coverage for checkpoint and variable cache behavior
- Add Saturn notebook format documentation
- Broaden Ruff to full Pyflakes
- Add lightweight typing to leaf modules
- Introduce explicit notebook execution state object
- Expand CI Python matrix to 3.8 through 3.13
- Make external default (fn.zip) + add --inline option
- Add focused pytest coverage and keep legacy golden tests available
- Add subprocess CLI fixture coverage for stable notebooks
- Add Ruff checks and CI for pytest, Ruff, and package builds
- Fix package build metadata and exclude VCS/tooling directories from sdists
- Split HTML rendering and Jupyter conversion helpers out of __main__.py
- Restore sys.argv and sys.path after in-process notebook runs
- Extract MPI detection into a testable module
- Harden parser edge cases for empty Saturn/variable cells and external archives
- Harden optional imports, TERM handling, and traceback filename parsing
- Add breakpoint cell that drops into repl (and then resumes execution)
- Checkpointing is broken in the interactive mode.
- Add ability to save assets in an external zip file.
- Add padding logic to better preserve blank lines around cells.
- Add --html option to convert.
- Add convert command for converting and showing Jupyter notebooks.
- Maybe change formatting of the cell prefixes (#m>, #o>, #chk>)
- Investigate checkpoint size.
    - Nothing wrong here. Perhaps, it's worth compressing checkpoints.
- Write README
- Clean up parser
- Add more cell types:
    - [Done] variable memoizing code cells (specify which variables this cell produces,
      caching them and skipping computation on subsequent evaluation)
    - [Done] non-skippable cells (i.e., even if resuming from a checkpoint, these cells
      will execute, before the checkpoint is loaded)
- Capture matplotlib better (e.g., multiple figures in a loop)
- ptpython
    - history in terms of the code cells
    - clean up various warnings (e.g., the message about meta+enter)
- MPI support: if running under MPI only rank 0 should show output and save the
  notebook
- Clean up output:
    - [Done] flush lines as they are being output (test with progress bars and such)
    - [Done] check that output from C extensions is captured
    - [Done] capture and properly output stderr
    - [Fixed: rich-text parsing] debug the list output problem
- Add tests
- Add our own icat implementation
- icat output is being captured, but not if redirected to a file
- Add images command to extract PNGs out of the notebook
- Better support for ANSI codes (e.g., to handle two-line tqdm)
- Handle sys.exit
- Clean up traceback (line numbers are broken across cells)
- [Done: not a bug] fig.clear() and/or ax.clear() with Matplotlib (many cheatsheets are failing)
    - the problem was with using plt.show(), rather than fig.show()
