## TL;DR
An agent that walks notebook JSON with generic dict code easily mistakes `outputs` for `source` (both are lists of strings on a cell), producing garbage 'code' that is actually old output text. Stop hand-rolling: use `nbformat.read()` and iterate `nb.cells`, reading only `cell.source` for code and `cell.cell_type` to filter. The library enforces the schema; dict-walking reinvents it badly.

```text
agent mis-parsed notebook JSON: treated outputs as source cells, parser failed
```

## Use this when
- An agent extracts 'code' from a notebook that turns out to be output text.
- The agent's notebook handling is custom JSON code, not nbformat.
- Downstream steps fail on 'code' that is clearly printed output.

## Not for this skill when
- nbformat itself fails to parse. That is a corrupt notebook.
- A human misreads the JSON. Use the UI or nbformat.

## Steps
1. Replace the custom JSON walk with `nbformat.read(path, as_version=4)`. Verify: it parses without errors.
2. Iterate `nb.cells` and branch on `cell.cell_type`. Verify: code cells and markdown cells are distinguished.
3. Read code only from `cell.source` (join the list if needed). Verify: no output text appears in extracted code.
4. Treat `cell.outputs` as read-only display data, never as source. Verify: the agent's code path never reads outputs as code.
5. Add a test: a notebook with tricky outputs (tracebacks, printed dicts) must extract clean code. Verify: the test passes.

## Variant phrasings
### agent parse notebook json wrong
Short form. Use nbformat, not dict walks.

### notebook outputs parsed as source
The bug phrasing. cell.source is the only source.

### nbformat vs json notebook parsing
The library phrasing. The library enforces the schema.

Compatibility: nbformat 5.x. The v4 schema (cells with source/outputs) is stable.

## Why it happens
The .ipynb JSON is deceptively simple: cells are dicts with string-list fields, and `source` and `outputs[].text` look alike to generic code. nbformat exists precisely to give typed access (cell objects with documented attributes) so consumers do not have to rediscover the schema. Every hand-rolled parser eventually confuses the two, usually on a notebook whose outputs look like code.

## Edge cases / pitfalls
- Multiline strings: `source` may be a list of lines or a single string depending on the writer. nbformat normalizes this.
- Raw cells have `source` too but are not code. Filter by `cell_type == 'code'`.
- Execution counts and metadata are not code either. The agent should ignore everything except source for code extraction.
- Validating with `nbformat.validate()` catches schema drift before parsing logic runs.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_qPo7cGH3l7r7dkG-B1SLfg
