Manual notebook validation and authoring guide
Use this guide when you want to manually confirm policy behaviour on real notebooks, including:
- creating new notebooks that should pass policy checks
- importing complex notebooks and remediating them to compliance
- validating both marimo (
.py) and Jupyter (.ipynb) workflows
This guide also serves as a practical notebook best-practice assurance and testing checklist for teams who want explicit manual validation before release.
Scope
This guide is intentionally practical. It assumes you want to test behaviour locally before publishing.
1) One-time setup for manual checks
Create a scratch area:
mkdir -p manual_checks/marimo manual_checks/jupyter
Or use the built-in recipe to scaffold known examples:
just create-manual-examples
For Just installation and recipe usage, see JUST_SETUP.md.
Run policy checks directly:
uv run pytest-notebook-quality --skip-ruff manual_checks
Optional prek hook (local/manual usage):
repos:
- repo: local
hooks:
- id: notebook-policy-manual
name: notebook-policy-manual
entry: uv run pytest-notebook-quality --skip-ruff manual_checks
language: system
pass_filenames: falseThen run:
uv run prek install
uv run prek run notebook-policy-manual --all-files
2) New notebook guide (.ipynb and .py)
Use these defaults to avoid common violations from the start. ### 2.0 Initialisation: clean isolated environment for notebook experiments Create an isolated workspace and environment before authoring new notebooks:
mkdir -p manual_checks/experiments
cd manual_checks/experiments
uv init --python 3.13
uv add --dev pytest-notebook-policy
uv add --dev 'pytest-notebook-policy[sync]'
Notes:
uv initcreates a clean local project scaffold for experiments.uv add --dev pytest-notebook-policyinstalls the policy checker for manual validation.'pytest-notebook-policy[sync]'enables paired.ipynb↔︎.pysync support (useful when testingJ010/paired-source workflows).
Quick verification:
uv run pytest --version
uv run pytest-notebook-quality --help
2.1 New Jupyter notebook (.ipynb) that is policy-friendly
Recommended structure:
- First code cell: parameter/configuration cell (
J011) - Keep code cells compact and focused (
J012) - Avoid magics/shell escapes (
J001) - Keep non-idempotent operations explicit and controlled (
J002) - Move reusable classes/functions into modules (
J013)
Suggested first code cell (parameters):
params = {
"seed": 42,
"dataset_path": "data/train.csv",
"run_mode": "dev",
}Suggested workflow:
- Keep each cell focused on one transformation step.
- Move reusable logic to
src/...modules and import it. - If you use randomness/time/UUIDs, gate it behind explicit parameters.
Validate:
uv run pytest-notebook-quality --skip-ruff path/to/notebook.ipynb
2.2 New marimo notebook (.py) that is policy-friendly
Recommended structure:
- Avoid
on_changehandlers where pure reactivity is enough (M001) - Avoid mutable module-level state (
M003) - Avoid cross-cell mutation patterns (
M005) - Keep non-idempotent calls controlled (
M006) - Keep fixtures out of notebook modules (
M004)
Suggested authoring defaults:
- treat cells as pure transformations where possible
- return new values instead of mutating shared inputs
- keep state local to cells/functions
Validate:
uv run pytest-notebook-quality --skip-ruff path/to/notebook.py
3) Second-stage checklist for complex imported notebooks
Use this when a real-world notebook fails several J-rules at once.
Stage A: Baseline
- Copy the candidate notebook into
manual_checks/.... - Run:
uv run pytest-notebook-quality --skip-ruff manual_checks/jupyter/<notebook>.ipynb
- Record initial codes and count (for example:
J001,J011,J012,J013).
Stage B: Rule-by-rule remediation order (recommended)
Apply in this order to reduce rework.
J011first: add a top-of-notebook parameter/configuration cell.J001: remove magics and shell escapes; replace with Python APIs.J013: extract inline classes/functions into importable modules.J012: split large notebooks and long cells after extraction work.J002: make non-idempotent behaviour explicit/controlled.J010(if using paired files): verify.ipynband paired.pystay in sync.
Stage C: Verification gates
After each remediation step:
uv run pytest-notebook-quality --skip-ruff manual_checks/jupyter/<notebook>.ipynb
When stable, run on the whole manual check area:
uv run pytest-notebook-quality --skip-ruff manual_checks
If using prek:
uv run prek run notebook-policy-manual --all-files
Stage D: Compliance sign-off
Consider the notebook compliant when:
- no rule violations are reported for the target files
- extracted reusable logic lives in modules (not large inline cells)
- parameter/configuration intent is clear in the first code cell
- the notebook remains readable and reviewable
4) Optional paired-source check for Jupyter notebooks
If you maintain paired .ipynb/.py notebooks, run with paired source mode:
uv run pytest --notebook-check --notebook-check-jupyter-source paired-py path/to/notebook.ipynb
Use this when you want J-rules to evaluate the paired .py representation while still enforcing sync rules.