# Director Notes Protocol

This is the v1 protocol for turning director feedback into governable notes and
bounded edits. The authoritative code contract lives in
`recoil/api/notes_ledger.py`: that module owns `ACTION_TYPES`, `STATUSES`, the
closed enum sets, `NOTE_SCHEMA_VERSION`, and the `NoteRecord` field contract.
This document explains the operating procedure and points to that module instead
of copying its enum definitions as a second source of truth.

Live `/engine` injection is deferred. The delimited slice below is intentionally
present so future wiring can consume it, but this phase does not add the
consumer.

<!-- ENGINE-INJECT-START -->
Director notes v1: classify every note by Domain x Mechanism x ActionType, then
record modifiers: blast_tier, scope, target, reference_target, approvals, spend,
links. Ledger authority is recoil/api/notes_ledger.py. Show blast before acting.
For compound notes, handle deepest/lowest-blast changes first. T5 script
rederive is gated: do not run unless segmentation/determinism conditions are
explicitly satisfied. script_edit is consolidated: its executor always writes a
status="proposed" NoteRecord before snapshot/mutation, then flips to applied or
failed. Link proposal_id/note_id when available. v0 is manual: classify, show
blast, get approval, execute the narrowest safe action, inspect ledger/snapshot,
then report result.
<!-- ENGINE-INJECT-END -->

## Classification Axes

Every director note is classified across three required axes:

| Axis | Meaning | Authority |
|---|---|---|
| `domain` | The story or production layer the note is about: script, board, prompt, continuity, performance, and related closed-set layers. | `recoil/api/notes_ledger.py` |
| `mechanism` | The mechanism that can satisfy the note: patch, choose existing, reroll, rollback, clarify, park unsupported, and related closed-set operations. | `recoil/api/notes_ledger.py` |
| `ActionType` / `action_type` | The intent class: fix, choose, revert, constrain. | `recoil/api/notes_ledger.py` |

Modifiers qualify the action without becoming authority themselves:

| Modifier | Use |
|---|---|
| `blast_tier` | How much production state the action can disturb. See the T0-T7 ladder below. |
| `scope` | The target span: shot, shotset hash, scene, batch, episode, or global. |
| `target` | The artifact or content identity being acted on. For script edits this includes `script_sha256` and `pre_edit_sha256`. |
| `reference_target` | Optional comparison or source target. |
| `classification` | Supplemental classifier metadata. Thresholds are a forward reference to a later P2/P3 spec. |
| `links` | Cross-record linkage, including `proposal_id` when a proposal caused the note. |
| `approvals` / `spend` | Human approval and cost context. |

The ledger points to artifacts; it does not copy current board, script, prompt,
or take state as an alternate truth store.

## Hard Rules

1. **Blast shown before acting.** Any action that can mutate artifacts or spend
   money must show the blast tier before execution. The operator may still choose
   to proceed, but the action cannot hide its scope.
2. **`script_edit` writes `proposed` before mutation.** The consolidated
   `recoil/api/executors/script_edit.py` executor owns the whole transaction:
   resolve path, mint and write a `status="proposed"` `NoteRecord`, snapshot the
   pre-edit script, mutate with `atomic_write_text`, then flip the same note to
   `applied` or `failed`. Callers do not mint an external `note_id`.
3. **Compound notes run deepest-first.** If a note contains multiple actionable
   fixes, perform the narrowest/deepest edit first, then reassess before broader
   derivations. Example: patch a shot-level blocking error before rerolling a
   scene; patch script cause before rebuilding boards.
4. **T5 is gated on determinism.** T5 script rederive with segmentation risk is
   not an automatic consequence of a script file edit. Only run T5 when the
   segmentation and deterministic rederive conditions are explicitly satisfied.
   Until the later action-policy spec exists, the tier is recorded metadata, not
   an execution trigger.
5. **Fail loud.** Validation, path resolution, ledger writes, snapshots, and
   mutations must raise rather than silently continuing.

## Blast Tier Ladder

The `blast_tier` ladder is recorded in the note and shown before action. The
closed values are defined in `recoil/api/notes_ledger.py`; classifier thresholds
and automated policy are forward references to a later P2/P3 spec.

| Tier | Meaning |
|---|---|
| T0_NO_GENERATION | No generation or artifact mutation; bookkeeping, clarification, or no-op. |
| T1_ONE_ARTIFACT | One artifact changes, such as the v1 approved script file replacement when no derive is triggered. |
| T2_SHOTSET_OR_TAKE | One shotset, take, or similarly bounded visual output can change. |
| T3_SCENE_OR_SMALL_BATCH | A scene or small batch may change. |
| T4_BATCH_OR_EPISODE_DERIVE | A larger batch or episode-level derived set may change. |
| T5_SCRIPT_REDERIVE_WITH_SEGMENTATION_RISK | Script rederive can alter segmentation or downstream identity; requires determinism/segmentation gate approval. |
| T6_PROJECT_OR_GLOBAL_RULE | Project or global rule/canon changes can affect many future outputs. |
| T7_UNSUPPORTED | Unsupported or unsafe for automatic action; park or ask for clarification. |

## v0 Manual Procedure

1. Capture the director note verbatim.
2. Classify it by Domain x Mechanism x ActionType and attach modifiers.
3. Show the blast tier before acting, including spend and affected artifacts
   when known.
4. For compound notes, execute the deepest and lowest-blast piece first.
5. Record a `NoteRecord` through the protocol owner for the action path. For
   `script_edit`, rely on the executor-owned transaction; do not create a
   separate external note token.
6. Verify the artifact, note status, and any snapshot reference.
7. Report the result using linked `proposal_id` / `note_id` where available.

## Script Edit Transaction

The v1 approved `ScriptEditProposal` path is intentionally consolidated. The
caller validates request shape and passes `proposal_id` into
`script_edit.execute`; the executor owns the state transition:

1. Resolve project and episode script path.
2. Hash current bytes and target bytes.
3. Mint `note_id` and write `NoteRecord(status="proposed", links={"proposal_id": ...})`.
4. Snapshot the pre-edit script under `_history/script_edits/`.
5. Mutate the script with `atomic_write_text`.
6. Flip the note to `applied` and record `artifacts_edited`.

This is not globally atomic with the file mutation. The bounded divergences are
documented below and are accepted v1 behavior.

## Bounded Divergences

The proposal is already `"approved"` when the script-edit dispatch branch runs.
The following user-visible states mirror the Phase 3 S-table:

| State | Condition | Result |
|---|---|---|
| S1-S3 | Preflight failure before mutation: unresolved `episode_id`, empty script text, missing `project_id`, or script path not found. | Proposal remains `"approved"` and no note is written. This approved-then-fails property exists across the 8 proposal kinds, is accepted in v1, and is owned by REC-198. |
| S4 | Initial `write_note(proposed)` fails. | No note, no mutation, fail loud. |
| S4b | Snapshot fails after the proposed note and before mutation. | Note remains `"proposed"`, script unchanged, fail loud. |
| S5 | `atomic_write_text` fails during mutation. | Proposal remains `"approved"`, script unchanged, note is flipped to `"failed"` with error context. This is the bounded approved-plus-failed-note divergence. |
| S5b | `atomic_write_text` fails and writing the failed note also fails. | Original mutation error is re-raised; note remains `"proposed"` if the failed flip did not land; script unchanged. |
| S6 | Post-mutation applied flip fails. | Script is mutated, note remains `"proposed"`, and the state is recoverable by a future reconcile because the snapshot and `pre_edit_sha256` are preserved. |
| S7 | Success. | Script is mutated, note is `"applied"`, and `artifacts_edited` points to the pre-edit snapshot. |

These divergences are narrow. They do not change the ledger authority, do not
create a second state home, and do not add live reconcile behavior in v1.
