# an.iterate

Iterative edit loop — free-text instruction → IR patch via Claude → re-render.

Phase 10. The spec’s signature user story:

> > “I say ‘make Maya’s laugh longer and warmer.’ Claude Code edits the IR,
> > re-renders just the affected shot, and shows me the diff. I approve.”

This module turns a free-text instruction into a structured set of patches
on the SceneIR JSON tree, validates them, applies them, persists, and (optionally)
re-renders. The vision LM is Claude Opus 4.7 with adaptive thinking;
`messages.parse()` + a Pydantic schema guarantees the patches are valid
JSON of the expected shape.

Path syntax for patches: slash-delimited JSON-pointer-style. List indices
are integers. Examples:

> “meta/title”
> “timeline/0/duration”
> “timeline/1/dialogue/0/text”
> “timeline/1/dialogue/0/emotion”

Patch operations:

> {“op”: “set”, “path”: “…”, “value”: …}     # replace (or create) value
> {“op”: “append”, “path”: “…”, “value”: …}  # append to a list
> {“op”: “delete”, “path”: “…”}                # remove an entry

The orchestrator records each iteration in `mall["decisions"]` so the
agent can review what changed across runs.

### Functions

| [`iterate`](#an.iterate.iterate)(project_dir, instruction, \*[, ...])   | Apply a free-text instruction to the scene at `project_dir`.                  |
|-------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
| [`system_prompt`](#an.iterate.system_prompt)()                                | The `an iterate` system prompt: the protocol around the generated vocabulary. |

### Classes

| [`IterateResponse`](#an.iterate.IterateResponse)(\*\*data)                       | Structured reply from Claude for a single iterate() call.   |
|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------|
| [`IterateResult`](#an.iterate.IterateResult)([success, summary, patches, ...]) | Outcome of an iterate() call.                               |
| [`Patch`](#an.iterate.Patch)(\*\*data)                                 | A single mutation against the SceneIR JSON tree.            |

### Exceptions

| [`IterateError`](#an.iterate.IterateError)   | Raised when an iterate call cannot apply its proposed patches.   |
|-----------------------------------------------------------------|------------------------------------------------------------------|

### *exception* an.iterate.IterateError

Bases: [`RuntimeError`](https://docs.python.org/3/builtins/exceptions.html#RuntimeError)

Raised when an iterate call cannot apply its proposed patches.

### *class* an.iterate.IterateResponse(\*\*data)

Bases: `BaseModel`

Structured reply from Claude for a single iterate() call.

#### model_config *: [ClassVar](https://docs.python.org/3/library/typing.html#typing.ClassVar)[ConfigDict]* *= {}*

Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].

### *class* an.iterate.IterateResult(success=True, summary='', patches=<factory>, affected_shots=<factory>, new_scene=None, validation=None, error=None)

Bases: [`object`](https://docs.python.org/3/builtins/functions.html#object)

Outcome of an iterate() call.

### *class* an.iterate.Patch(\*\*data)

Bases: `BaseModel`

A single mutation against the SceneIR JSON tree.

#### model_config *: [ClassVar](https://docs.python.org/3/library/typing.html#typing.ClassVar)[ConfigDict]* *= {}*

Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].

### an.iterate.iterate(project_dir, instruction, , apply=True, model='claude-opus-4-7', max_tokens=4096)

Apply a free-text instruction to the scene at `project_dir`.

Steps:

> 1. Load the project.
> 2. Send the current scene + the instruction to Claude.
> 3. Parse the structured patch response.
> 4. Apply patches to a deep-copied IR; validate.
> 5. If valid and `apply=True`, persist to mall[“scenes”][“main”] and
>    append to mall[“decisions”].

The caller is responsible for re-rendering: `an render` then re-renders
exactly the shots whose content changed, found by digest (the shot cache,
ADR 0004) — `IterateResult.affected_shots` is the model’s account of
which those are, for the reader, not the mechanism.

* **Return type:**
  [`IterateResult`](#an.iterate.IterateResult)

### an.iterate.system_prompt()

The `an iterate` system prompt: the protocol around the generated vocabulary.

Loads the installed genres first (their presets and methods are part of
the vocabulary). Stable for a given registry, so it caches as well as the
hand-written one did.

* **Return type:**
  [`str`](https://docs.python.org/3/builtins/stdtypes.html#str)

```pycon
>>> text = system_prompt()
>>> "walk" in text and "Rules:" in text
True
```
