Fresh-kernel notebook execution¶
Updated: 2026-09-09. Available in ZeoCore 0.10.0.
Install the optional zeocore[notebook] extra. It includes nbclient, the Python
kernel and psutil for descendant tracking. Ordinary ZeoCore consumers do not
need it. The learner runtime must not depend on this authoring tool.
from zeo_core.integrations.notebook import NotebookExecutionRequest, execute_notebook
result = execute_notebook(NotebookExecutionRequest(
source_path="clean.ipynb",
output_path="executed.ipynb",
workspace_root="/path/to/workspace",
working_directory="/path/to/workspace",
kernel_name="python3",
timeout_seconds=120,
cell_timeout_seconds=30,
))
assert result.success
assert result.content is not None
assert result.content.status == "SUCCEEDED"
print(result.model_dump_json(indent=2))
Each call creates a new worker, a new Python kernel, an isolated HOME and a
private Python kernelspec using this environment's interpreter. Only the
explicitly provisioned python3 kernel is available; another name produces
KERNEL_UNAVAILABLE. The kernel uses local IPC transport. Notebook code runs
in the declared working directory. Source and output must remain within the
workspace and must differ; existing output is refused.
The parent enforces the whole-run deadline independently of nbclient's cell
timeout. Syntax errors, assertions, exceptions, missing/crashed kernels, cell
timeouts and whole-run timeouts cannot return success. Stale execution counters
and outputs are cleared before running. Empty executable cells cannot turn
unevaluated output into execution evidence. A notebook with no code cannot
provide execution evidence even when require_code_cells=False. Code tagged
no-execute is skipped and counted separately; an entirely skipped notebook
cannot pass.
The parent retains observed process identities and kernel sessions, freezes ordinary descendant spawning during final cleanup, kills descendants and reaps the worker after success, failure or timeout. A cleanup failure prevents output promotion. Executed output is written separately and exclusively promoted only after successful execution and cleanup. Source changes during execution fail.
The environment starts from explicit PATH and temporary HOME/Jupyter/IPython/XDG directories. Parent credentials, proxies and unrelated variables are omitted. An allowlist may carry LANG, LC_ALL, TZ or non-secret COURSE_ variables; names containing secret/token/password/key/credential/proxy markers are refused. Do not put secrets in COURSE_ values. Kernel discovery uses only the freshly created kernelspec directory, not ambient user kernel configuration.
This executes trusted course code. It is not an operating-system sandbox: code can still read accessible files, start processes and use the network. Receipts explicitly record process/network isolation as UNAVAILABLE. Ordinary kernel/child cleanup is tested; deliberate hostile daemonization, privilege changes and unrelated processes are outside this containment claim. Use an external container with enforced network policy for releases that require it.
Receipts contain source/executed hashes, kernel and executor versions, actual executed/skipped code-cell counts, fixed failure categories and cleanup status. They do not contain source, output text or raw tracebacks. No successful execution establishes student learning or human visual approval.
Run make verify. The real-kernel tests include stale-output, fresh-state,
synthetic-secret and child-process controls after every important exit path.
Test and production tracks¶
No vendor account, API key or OAuth is required. Install
zeocore[notebook]==0.10.0 in Python 3.14 and use the
managed environment launcher with --integration notebook.
Use --mode test for synthetic notebooks and --mode production for approved
trusted notebooks, with separate roots/working directories and output paths.
The child still needs the installed notebook extra; a mode does not install it.
Fixture mode does not replace the kernel automatically or block network access.
For a test E2E, execute one notebook containing assert 2 + 2 == 4, inspect its
receipt and separately written output, then execute an assertion failure and
verify there is no promoted output. Use the authoring reference
for a complete runnable example. In production, require the approved interpreter,
dependency inventory and lock identity before cells run; select only reviewed
inputs. Clean up only the selected run's artifacts after retaining its receipt.
No production credential should be accessible to a test kernel.
Execution contract after elder review¶
max_output_bytes bounds retained stream, stderr and rich-output message content
before nbclient accumulates it (default 10 MiB). max_code_cells bounds code-cell
inventory (default 1000). Exceeding either limit fails. A polled worker-RSS fuse
(default 512 MiB) protects against oversized incoming messages while the receiver
is decoding them; it is not an OS-enforced peak-memory bound. Trusted kernel code
can still allocate memory, and strict memory isolation requires an external OS
boundary. The receipt's captured byte count never exceeds the retention limit.
timeout_seconds covers worker/kernel startup and cell execution.
startup_timeout_seconds additionally bounds Jupyter's readiness wait. Cleanup
has its own declared cleanup_timeout_seconds allowance (default 5, maximum 30).
The total advertised process lifetime is startup/execution deadline plus cleanup
allowance, with ordinary scheduler/syscall overhead. Receipts name the cleanup
scope as observed process identities and sessions; complete descendant cleanup
is explicitly UNAVAILABLE. A detached child observed before kernel exit is
covered. Hostile daemonization between observations is outside that guarantee.
Kernel identity records the actual generated kernelspec SHA-256, interpreter
binary SHA-256, and a versioned environment SHA-256 covering that executable,
Python version and the installed-distribution inventory. The
kernelspec location is a managed logical path. Absolute local provenance paths
require absolute_provenance_paths=True. Only the generated private Python spec
is used; ambient same-name specs and their environment values are excluded.
expected_environment_sha256 refuses unexpected installed environments before
executing cells. environment_lock_path records the full hash and relative path
of the consumer's declared lockfile; when omitted its evidence is unavailable.
A lock reference alone does not certify that every installed dependency matches
that lock: compare the recorded actual inventory identity with an approved one.
Course-release policy should require both the lock and expected identity.
Receipts count attempted, completed, failed and intentionally skipped cells.
A timeout or crash marks unfinished attempted cells failed. no-execute cells
are intentional skips; the generic executor reports them, while the consumer
must reject undeclared skips. All-skipped and no-code inputs fail. allow_errors
accepts only False; continuing for diagnostics is not a release-acceptance mode.
Expected-failure exercises should catch and assert their precise expected
exception and evidence, then complete normally.
Execution receipts describe individual observations. Exact output hashes may legitimately differ across runs; they identify the bytes observed, not semantic equivalence or learning. Stable-content semantic comparison is a separate gate. Network isolation remains UNAVAILABLE because this executor does not enforce or measure egress policy. An external runner must supply separate measured network policy evidence; callers cannot turn an unverified assertion into ENFORCED here.