## TL;DR
Renaming a variable with find-and-replace misses references inside strings, misses dynamically constructed names, and can hit substrings of other names, leaving one stale reference that crashes with NameError at runtime. Do renames structurally: parse every code cell's AST, rename the binding and all true references, then grep the notebook for the old name and explain each remaining hit. The grep-after-rename is the acceptance test.

```text
agent failed to rename a variable across cells: missed one reference, NameError crash
```

## Use this when
- A renamed variable still crashes with NameError on the old name.
- The agent renames with text search-and-replace.
- The notebook has many cells sharing variables.

## Not for this skill when
- The rename was wrong semantically. That is a logic error.
- A human renames in the UI. Use a proper rename refactoring.

## Steps
1. Parse all code cells with `ast` and find the binding and its references. Verify: you have the complete reference set.
2. Rename binding and references structurally (or with a refactoring library). Verify: the AST shows the new name everywhere.
3. Grep the raw notebook for the old name. Verify: every remaining hit is explained (string literal, comment, unrelated).
4. Execute the notebook top to bottom on a fresh kernel. Verify: no NameError.
5. Record the rename in the agent's report. Verify: the human can review what changed.

## Variant phrasings
### rename variable across notebook cells
Short form. AST rename plus grep verification.

### agent rename missed reference NameError
The bug phrasing. Text replace is not a rename.

### cross-cell variable rename notebook
General phrasing. Verify with execution.

Compatibility: Python 3, nbformat 5.x. ast-based rename works for straightforward bindings.

## Why it happens
Notebooks scatter a variable's lifetime across cells, so a rename must span the whole document, and text replacement cannot distinguish the variable from the same characters in a string, a comment, or a longer name. One missed reference is a NameError that only fires when that cell runs, which may be far from the rename. Structural renaming plus a verification grep closes the gap.

## Edge cases / pitfalls
- Dynamic references (`globals()[name]`, `eval`) defeat AST renaming. Flag them for human review.
- Renaming inside magic commands (`%time df`) needs special handling; the magic body is not parsed Python.
- A rename that collides with an existing name creates a worse bug. Check the new name is free first.
- Jupyter's own rename refactoring (in recent Lab) handles single notebooks well; use it when a human is driving.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_eeh549P9jb71IBkXxDKIeA
