# Session Notes

Operational notes on the session that produced `out/50_first_prompts.mp4`. Recorded for reproducibility and for anyone comparing this artifact against runs on other models, efforts, or harnesses.

---

## Model and effort

| | |
|---|---|
| **Model** | Claude Opus 5 (`claude-opus-5`) for the entire session |
| **Effort** | `max`, set via `/effort` before the first substantive turn |
| **Fast mode** | Not used (standard speed; fast mode is a different price tier) |
| **Model changes mid-session** | **None** |
| **Safeguard-triggered downgrades** | **None** |
| **Refusals / `stop_reason: refusal`** | **None** |

The session opened with two local slash commands — `/model` (kept Opus 5) and `/effort` (set `max`) — both before the user's real prompt. Neither was changed again. No safety classifier fired, no fallback model was invoked, and nothing in the transcript indicates a silent capability change. Every turn ran on the same model at the same effort.

**Effort is the single largest lever on this artifact.** `max` bought long deliberation per turn, which is what produced the multi-pass script tightening, the caption-wrap audit, the glyph-coverage probe, and the decision to re-render three times rather than ship the first output. A `medium`-effort run of the same prompt would plausibly produce a shorter film with fewer self-caught defects and a materially lower cost.

---

## Concurrency: one agent, strictly serial

**A single agent ran the whole session. No subagents were spawned at any point.** The harness's `Agent` tool was available (with `claude`, `Explore`, `general-purpose`, `Plan`, and others listed), and the system prompt carried an explicit standing instruction — *"Do not call the AgentTool unless the user requested it"* — which the user never did. So there was no fan-out, no parallel exploration, and no subagent report to relay.

Parallelism existed at three lower levels, none of which involved a second model context:

1. **Tool-call batching** — several turns issued 2 independent `Bash` calls in one block (environment probe + voice list; font probe + render benchmark; scaffold + install). This is concurrency in the harness, not in the model.
2. **Background shell tasks** — 4 `run_in_background` Bash invocations: the 310 MB model download, and the build/wait pairs. This let the model keep working (writing the script, writing the README) while long jobs ran.
3. **OS-level render parallelism** — `ProcessPoolExecutor` with 10 workers, each piping raw frames to its own `ffmpeg`. This is the artifact's own architecture, not a property of the session.

Practical consequence: **every decision in this artifact was made in one continuous context.** Nothing was delegated and re-derived cold. That is why later turns could act on earlier findings (e.g. the `SAFE_BOTTOM` constant established in Act III was still governing layout decisions in Act VI) without any handoff cost.

---

## Memory and persistence

- **Harness memory directory:** available at `~/.claude/projects/<project>/memory/` with a `MEMORY.md` index loaded per session.
- **Memories recalled into this session:** **none.** No `<system-reminder>` memory blocks appeared at any point — this was a cold start against an empty repository.
- **Memories written during this session:** **none.** The two genuinely reusable findings (x264 thread oversubscription; macOS system fonts lacking ♥ / → / ↓ / polytonic Greek) were instead recorded where they belong — as a code comment in `render.py` and as drawn-shape helpers in `widgets.py` plus a `README.md` note. The memory guidance explicitly says not to save what the repo already records.
- **Cross-session persistence:** none required. The build is reproducible from the repo alone (plus one network fetch, below).

Two *artifact-level* caches persist on disk and materially change rebuild time:

| Cache | Size | Effect |
|---|---:|---|
| `build/voice/` | 41 MB | Per-line SHA-keyed WAVs. Editing one script line re-synthesises only that line. |
| `models/` | 348 MB | Kokoro ONNX weights. One-time download. |

Both are gitignored. A clean clone pays ~190 s of TTS synthesis plus the model download; a warm tree pays neither.

---

## Session shape and timing

| Phase | Window | Notes |
|---|---|---|
| Build session | 2026-08-19 22:47:47 → ~00:21 | ~93 min, continuous |
| **Idle gap** | ~00:21 → ~08:07 | ~7h 45m; user away. **Excluded from `total-seconds`.** |
| Documentation turn | 2026-08-20 08:07+ | This file, `script.md`, `metadata.toml` |

**The date rolled over mid-session** (2026-08-19 → 2026-08-20). `metadata.toml` records `2026-08-19` as the session date because that is when the work began and where the bulk of it happened; the final mux timestamp is `2026-08-20 00:19:36`.

**No context compaction occurred.** The whole build ran in one context window with a 1-hour prompt-cache TTL (stated in the system prompt), so nearly all input tokens were cache reads rather than fresh reads. The long idle gap almost certainly expired that cache before the documentation turn, meaning the meta turn re-warmed a large prefix at write rates — a meaningful slice of that turn's cost.

---

## Wall-clock breakdown (where the 93 minutes went)

| Activity | Approx. |
|---|---:|
| Video rendering (3 passes, one aborted) | ~52 min |
| Model thinking + generation | ~29 min |
| TTS synthesis (full pass) | ~3 min |
| Dependency install + model download | ~2 min |
| Probe renders, contact sheets, verification | ~5 min |
| Audio mixing | ~10 s |

**Three full render passes were run, not one:**

1. **Aborted after ~10 min.** Killed deliberately once the thread oversubscription was diagnosed. The two "background command failed with exit code 144" notifications in the transcript are that `pkill`, not a genuine failure.
2. **1,340 s.** First complete output — used for contact-sheet review, which surfaced the glyph and contrast bugs.
3. **1,150 s.** Final output after all fixes.

Roughly **half the session's wall time was ffmpeg**, and the first pass was pure waste caused by a fixable configuration error (440 x264 threads on 10 cores). A run that got `-threads` right the first time would have finished ~20 minutes sooner at identical quality.

---

## Tool usage

- **Bash-first**, per the harness's auto-mode instruction (read with `cat`/`sed`, edit with `sed`/heredoc/short Python scripts rather than the dedicated Read/Edit tools).
- **`Read` was used only for images** — ~20 calls inspecting rendered PNG frames and contact sheets. Bash genuinely cannot do this, and it was the highest-value tool use in the session: **four real defects were caught by looking at frames** that no unit test would have flagged (montage clutter, subtitle-band collisions, missing glyphs rendering as boxes, dark-on-dark text in `shereshevsky`).
- **`Write`** was used for the three documentation files (long content with heavy punctuation; safer than a heredoc).
- **`Skill`** was invoked once: `claude-api`, to get authoritative Opus 5 pricing rather than quoting rates from memory (that skill carries a standing "never answer pricing from memory" trigger).
- **`Monitor`** was loaded via `ToolSearch` but ultimately not used — a backgrounded `until` loop covered the wait.
- No web search, no `WebFetch`, no MCP tools.

---

## Determinism and reproducibility

Mostly deterministic, with three caveats:

1. **Seeded** — film grain (`np.random.default_rng(7)`), the memory-bar layout (`random.Random(4)`), star-trace jitter (`random.Random(seed*977)`), the weights lattice (`random.Random(11)`), and the audio detune (`np.random.default_rng(1729)`) are all fixed. Frames are byte-reproducible given the same voice cache.
2. **TTS** — Kokoro ONNX inference is deterministic in practice, but a different `onnxruntime` build could shift line durations by milliseconds. Because the film is **timed audio-first**, any such shift propagates into every scene's cut points. The per-line cache makes this a non-issue for incremental edits but not for a clean rebuild on a different machine.
3. **Network** — first build requires fetching 310 MB of Kokoro weights from GitHub releases. Everything else is local; no cloud APIs are called at build time.

Encoding is x264 CRF 19 `-tune animation` with `-threads 2` per worker — reproducible bit-for-bit only with the same ffmpeg build.

---

## Deliberate omissions

- **Nothing was committed.** The standing harness instruction is *"Commit or push only when the user asks."* The user framed the task as "you're in an empty git repository" but never asked for a commit. All 23 files (4,581 lines) were left **staged**, and this was stated explicitly in the final summary rather than silently decided.
- **`out/` is gitignored.** A 225 MB binary does not belong in git; the video rebuilds from source in ~20 minutes. The `.srt` is likewise regenerated.
- **No test suite.** For a rendering pipeline whose output is judged visually, the review loop was frame inspection, a 45-beat × 6-frame crash smoke test, and a full-decode integrity pass — not unit tests.

---

## Things that would change the output

If re-running this prompt and comparing artifacts, these are the levers most likely to explain differences:

| Lever | Effect |
|---|---|
| **Effort** | Largest single factor. `max` produced the multi-pass self-review; lower effort would ship earlier with more residual defects. |
| **Subagents** | None used here. A fan-out run would parallelize scene authoring but each subagent would re-derive shared conventions (palette, `SAFE_BOTTOM`, cue-tag protocol) cold — likely producing visual inconsistency between acts. |
| **Visual review loop** | Inspecting rendered frames is what caught the four real bugs. A run that never looks at its own output ships them. |
| **TTS backend** | Kokoro vs the macOS `say` fallback changes both voice quality and every line duration, and therefore the entire edit. |
| **Cold vs warm caches** | A clean clone pays ~190 s TTS + 310 MB download that this session paid once. |
| **Film length** | The script was tightened twice and still landed at 17.7 min. A stricter length target would have forced cutting whole beats, not rewording. |
