Autonomous Notebook Debug & Fix
You are debugging the notebook at $ARGUMENTS. Run it end-to-end, collect all errors,
fix them, and re-run until all cells pass. Work autonomously — don't ask the user unless
you're genuinely stuck on a design decision.
Safety Rules
You may freely read, search, and explore any files needed to understand the notebook's
dependencies and data. However, ask the user for confirmation before:
- Installing packages or modifying the conda environment
- Submitting SLURM jobs
- Deleting or overwriting data files
When in doubt, ask. The cost of pausing is low; the cost of clobbering data is high.
Environment
- Python: use the conda env
sobulk24 at /gpfs/commons/home/tbotella/miniconda3/envs/sobulk24/bin/python
- Papermill (always available):
papermill input.ipynb /dev/null --log-output 2>&1 | tee run.log
- Working dir:
cd to the notebook's directory before running.
Workflow
Phase 1 — Initial Run
Syntax check first (catches literal newlines in f-strings, unmatched parens):
python3 -c "import json,ast; [ast.parse(''.join(c['source'])) for c in json.load(open('NB.ipynb'))['cells'] if c['cell_type']=='code']"
Fix any syntax errors before running.
Run the notebook with nohup (survives SSH disconnects):
cd /path/to/notebook/dir
nohup /gpfs/commons/home/tbotella/miniconda3/envs/sobulk24/bin/python -m papermill \
notebook.ipynb /dev/null --log-output > run_stdout.txt 2>&1 &
echo "PID: $!"
Monitor progress — check tail -20 run_stdout.txt periodically.
Don't poll in a tight loop; wait 60-120s between checks for long-running notebooks.
Collect all errors once the run completes:
grep -E "ERROR:|FAILED|OK \(|^\[" run_stdout.txt
Phase 2 — Diagnose & Fix
Classify errors: distinguish root-cause bugs from cascade failures.
A cascade failure is when cell N fails because cell M (earlier) failed and didn't define
a variable. Fix only the root cause — cascades resolve automatically.
Edit .ipynb cells — use one of:
- NotebookEdit tool (preferred for small edits): find cell index via
Grep on the .ipynb JSON.
- Python JSON manipulation via Bash (for complex multi-line edits):
import json
nb = json.load(open('notebook.ipynb'))
cell = nb['cells'][INDEX]
src = ''.join(cell['source'])
# ... modify src ...
cell['source'] = [new_src]
json.dump(nb, open('notebook.ipynb', 'w'), indent=1)
- Never use the Edit tool on .ipynb files — it doesn't understand JSON cell structure.
Common bug patterns:
- Cache loading path missing variables: if a cell has
if cache: load... else: compute...,
ensure ALL variables defined in the else branch are also set in the if branch.
- Literal newlines in f-strings: JSON source arrays split strings across lines.
ast.parse() catches these. Fix by merging the JSON lines into one with \n escapes.
- Duplicate column on merge: if DataFrame A already has column X from a previous merge,
merging again with column X creates X_x and X_y. Check before merging:
df_merged = df if 'col' in df.columns else df.merge(...).
- O(n^2) membership tests:
[x for x in big_list if x in big_array] where
big_array is a numpy array → use set() or np.isin() or BLAS matrix multiply.
- Sample count mismatch: tools that process all files in a directory may include
QC-failed samples. Use
os.listdir() for the full list, then inner merge with
QC-passed samples to filter.
Syntax check again after all fixes (step 1).
Phase 3 — Verify
Re-run the notebook (same nohup pattern as step 2).
Confirm all cells pass: grep "FAILED\|ERROR" run_stdout.txt should return nothing.
If errors remain, go back to Phase 2. Limit to 3 iterations — if still failing after 3,
report remaining errors to the user.
Performance Rules
- Cache slow cells (>5min): wrap in
if cache_valid: load else: compute; save.
Use a key file with sorted sample list for invalidation.
Helpers: _cache_ok(name, sdf) checks key; _cache_save_key(name, sdf) writes key.
- Never reload data already in memory: if cell N loaded a large file, cell M should
reuse the variable, not re-read the file.
- Prefer vectorized numpy/BLAS over Python loops: for large matrix operations,
mask.astype(float32) @ matrix is orders of magnitude faster than Python indexing loops.
- Use polars for file I/O:
pl.read_csv(..., n_threads=1) in a ThreadPoolExecutor
for parallel GPFS reads when loading many files.
Output
When done, provide a concise summary:
- Total cells: X, Passed: Y, Fixed: Z
- List of fixes applied (one line each)
- Total runtime
- Any warnings or known issues remaining
1---2name: notebook-debug3description: Autonomously run a Jupyter notebook, collect all errors, fix them iteratively, and re-run until clean. Use when asked to debug, fix, or test a notebook.4---56# Autonomous Notebook Debug & Fix78You are debugging the notebook at `$ARGUMENTS`. Run it end-to-end, collect all errors,9fix them, and re-run until all cells pass. Work autonomously — don't ask the user unless10you're genuinely stuck on a design decision.1112## Safety Rules1314You may freely read, search, and explore any files needed to understand the notebook's15dependencies and data. However, **ask the user for confirmation before:**16- Installing packages or modifying the conda environment17- Submitting SLURM jobs18- Deleting or overwriting data files1920When in doubt, ask. The cost of pausing is low; the cost of clobbering data is high.2122## Environment2324- **Python**: use the conda env `sobulk24` at `/gpfs/commons/home/tbotella/miniconda3/envs/sobulk24/bin/python`25- **Papermill** (always available): `papermill input.ipynb /dev/null --log-output 2>&1 | tee run.log`26- **Working dir**: `cd` to the notebook's directory before running.2728## Workflow2930### Phase 1 — Initial Run31321. **Syntax check first** (catches literal newlines in f-strings, unmatched parens):33 ```bash34 python3 -c "import json,ast; [ast.parse(''.join(c['source'])) for c in json.load(open('NB.ipynb'))['cells'] if c['cell_type']=='code']"35 ```36 Fix any syntax errors before running.37382. **Run the notebook with nohup** (survives SSH disconnects):39 ```bash40 cd /path/to/notebook/dir41 nohup /gpfs/commons/home/tbotella/miniconda3/envs/sobulk24/bin/python -m papermill \42 notebook.ipynb /dev/null --log-output > run_stdout.txt 2>&1 &43 echo "PID: $!"44 ```45463. **Monitor progress** — check `tail -20 run_stdout.txt` periodically.47 Don't poll in a tight loop; wait 60-120s between checks for long-running notebooks.48494. **Collect all errors** once the run completes:50 ```bash51 grep -E "ERROR:|FAILED|OK \(|^\[" run_stdout.txt52 ```5354### Phase 2 — Diagnose & Fix55565. **Classify errors**: distinguish root-cause bugs from cascade failures.57 A cascade failure is when cell N fails because cell M (earlier) failed and didn't define58 a variable. Fix only the root cause — cascades resolve automatically.59606. **Edit .ipynb cells** — use one of:61 - **NotebookEdit tool** (preferred for small edits): find cell index via `Grep` on the .ipynb JSON.62 - **Python JSON manipulation via Bash** (for complex multi-line edits):63 ```python64 import json65 nb = json.load(open('notebook.ipynb'))66 cell = nb['cells'][INDEX]67 src = ''.join(cell['source'])68 # ... modify src ...69 cell['source'] = [new_src]70 json.dump(nb, open('notebook.ipynb', 'w'), indent=1)71 ```72 - **Never use the Edit tool on .ipynb files** — it doesn't understand JSON cell structure.73747. **Common bug patterns**:75 - **Cache loading path missing variables**: if a cell has `if cache: load... else: compute...`,76 ensure ALL variables defined in the `else` branch are also set in the `if` branch.77 - **Literal newlines in f-strings**: JSON source arrays split strings across lines.78 `ast.parse()` catches these. Fix by merging the JSON lines into one with `\n` escapes.79 - **Duplicate column on merge**: if DataFrame A already has column X from a previous merge,80 merging again with column X creates X_x and X_y. Check before merging:81 `df_merged = df if 'col' in df.columns else df.merge(...)`.82 - **O(n^2) membership tests**: `[x for x in big_list if x in big_array]` where83 `big_array` is a numpy array → use `set()` or `np.isin()` or BLAS matrix multiply.84 - **Sample count mismatch**: tools that process all files in a directory may include85 QC-failed samples. Use `os.listdir()` for the full list, then `inner` merge with86 QC-passed samples to filter.87888. **Syntax check again** after all fixes (step 1).8990### Phase 3 — Verify91929. **Re-run the notebook** (same nohup pattern as step 2).939410. **Confirm all cells pass**: `grep "FAILED\|ERROR" run_stdout.txt` should return nothing.95 If errors remain, go back to Phase 2. Limit to 3 iterations — if still failing after 3,96 report remaining errors to the user.9798## Performance Rules99100- **Cache slow cells (>5min)**: wrap in `if cache_valid: load else: compute; save`.101 Use a key file with sorted sample list for invalidation.102 Helpers: `_cache_ok(name, sdf)` checks key; `_cache_save_key(name, sdf)` writes key.103- **Never reload data already in memory**: if cell N loaded a large file, cell M should104 reuse the variable, not re-read the file.105- **Prefer vectorized numpy/BLAS over Python loops**: for large matrix operations,106 `mask.astype(float32) @ matrix` is orders of magnitude faster than Python indexing loops.107- **Use polars for file I/O**: `pl.read_csv(..., n_threads=1)` in a ThreadPoolExecutor108 for parallel GPFS reads when loading many files.109110## Output111112When done, provide a concise summary:113- Total cells: X, Passed: Y, Fixed: Z114- List of fixes applied (one line each)115- Total runtime116- Any warnings or known issues remaining