# Session Notes

Operational notes on how this session ran, with emphasis on anything that could
affect the output or its reproducibility.

---

## Model and effort

- **Model:** Claude Opus 5 (`claude-opus-5`) for the entire session. The user ran
  `/model` before the first prompt, which reported *"Kept model as Opus 5"* — a
  confirmation, not a change.
- **Effort:** `max`, set via `/effort` before the first prompt
  (*"Set effort level to max (this session only)"*).
- **No downgrade occurred.** There was no safeguard trip, no refusal, no fallback to
  a smaller model, and no mid-session model or effort change. The model and effort at
  the last turn are the same as at the first.
- **Fast mode:** not enabled.

## Agent topology

- **Single agent, serial.** No subagents were spawned at any point. The system prompt
  carried an explicit instruction — *"Do not call the AgentTool unless the user
  requested it"* — and the user never requested one.
- Concurrency in this session was **process-level, not agent-level**: background
  `Bash` tasks (two retrains, the preview render, the aborted final render, the final
  render) and a 10-worker `multiprocessing` pool inside the renderer. Those are
  ordinary subprocesses, not additional model contexts.
- Practical consequence: while a background task ran, the single agent continued
  writing scene code in the foreground. Two of the three training runs were fully
  overlapped with productive work; the 29-minute final render was mostly idle waiting.

## Harness and tooling

- **Harness:** Claude Code, CLI.
- **Task tracking:** the built-in task list was used throughout (6 tasks, all
  completed). This is harness state, not repository state — it does not persist.
- **Skills:** exactly one was loaded, `claude-api`, and only in the **final turn**, to
  get current Opus pricing rather than answering from memory. It is a large document
  (roughly 60–80k tokens) and lands entirely in the final turn's context, which
  noticeably skews that turn's input-token count and cache-write volume relative to
  every other turn. No skill influenced the film itself.
- **Memory:** a persistent memory directory was available at
  `~/.claude/projects/…/memory/`. **Nothing was written to it and nothing was recalled
  from it.** The session started cold and left no cross-session state.
- **Scratchpad:** the session scratchpad was used for QA artifacts (contact sheets,
  extracted frames, filter sweeps) so they stayed out of the user's repository.
- **Deferred tools:** `TaskCreate`/`TaskUpdate`/`TaskList` and `Monitor` were fetched
  mid-session via `ToolSearch`. `Monitor` was fetched but ultimately not used — a
  `Bash` `until` loop with `run_in_background` was the better fit for a single
  completion signal.

## Timeline quirks

- **The session crossed midnight.** It began 2026-08-01 22:56 and ended
  2026-08-02 ~00:5x local. `metadata.toml` records the **start** date, 2026-08-01.
  A mid-session system notice announced the rollover.
- Two `until`-loop waits hit the 600 s foreground timeout and were moved to the
  background by the harness. This is normal behaviour, not a failure, but it means
  wall-clock time includes several stretches of near-pure waiting.

## Things that affect the artifact

- **Determinism of the networks.** All seeds are fixed (`seed=7` for the classifier,
  the flow selects the best of seeds 1–5, datasets use `seed=3`). Re-running
  `mise run data` on the same numpy version reproduces the same archive, so the film
  is reproducible. A different numpy version could perturb RNG streams.
- **The archive was regenerated three times**, and each regeneration changes the film:
  1. Initial run — 4,200 steps, `l2=1.4e-4`, 97.91% held out.
  2. After the regularisation sweep — 8,000 steps, `l2=1.2e-3`, 97.77%. Higher decay
     traded 0.14 pp of accuracy for legible first-layer filters, which Movement IV
     depends on.
  3. To capture `cls_snap_steps` and `cls_batch` so Movement IV's on-screen
     correction counter reports the true training step rather than an interpolation.
  The delivered film was rendered against archive 3.
- **A dead unit was rendered for most of the session.** Movement I hardcoded hidden
  unit 3, which the stronger weight decay had zeroed completely (peak |w| = 0.0000).
  Every early preview of Movement I therefore showed twelve zero-magnitude weights.
  Fixed near the end by selecting the strongest sign-balanced unit at setup time.
  **All QA stills of Movement I taken before that fix are not representative.**
- **One render was aborted and restarted** because of that fix. Roughly 22 minutes of
  the first final render were discarded.
- **A `ruff --fix` pass ran while the final render was in flight.** It removed four
  unused imports and sorted a `__slots__` tuple. These are provably non-behavioural,
  and spawned workers had already imported their modules, so the render remained
  valid. Had the fixes been semantic, the render would have needed restarting.
- **Render time is machine-specific.** 29 minutes on an M5 with 10 cores. The
  single-core benchmark projected ~4 minutes across 9 workers; the gap is per-worker
  setup cost in the first batch plus memory-bandwidth contention in the splat kernel,
  which is the dominant cost. Expect sublinear scaling with core count.
- **Grain makes the master large.** 565 MB at crf 16 for four minutes (~19 Mbps). The
  per-pixel film grain is near-incompressible. The 142 MB crf-21 copy was produced by
  re-encoding the master, not by re-rendering — so it is lossy-on-lossy, though
  visually near-identical. A from-source render at crf 21 via the new `--crf` flag
  would be marginally cleaner.
- **MNIST is downloaded at first run** from the Google CVDF mirror (with an
  ossci-datasets S3 fallback). An offline machine cannot reproduce the archive.

## Verification performed

- Every movement inspected as rendered stills at 960×540 during development, and the
  final master spot-checked at full 1920×1080.
- Whole-film contact sheet (30 frames at 8 s intervals) built from the *encoded*
  preview, not from the renderer — end-to-end verification.
- `ffmpeg -f null -` full decode of the final master and the viewing copy: clean, no
  errors.
- Score verified by spectral analysis (per-movement RMS, peak, spectral centroid),
  **not by listening** — see limitations.
- `ruff check src/` clean at exit.
- Accuracy figures quoted on screen and in the README were read back out of the
  archive and confirmed, and one incorrect README claim (weight sign split) was
  corrected against the real weights.

## Limitations and open items

- **The score was never heard.** It was tuned by measurement — levels, per-movement
  RMS, spectral centroid — which caught one clearly audible defect (hissy ticks at a
  5.5 kHz centroid) but cannot substitute for listening. It may want a pass by ear.
- **Nothing is committed.** Six untracked paths in a repository with no commits. The
  user framed the repo as context, not as a request to commit, and the harness
  instruction is to commit only when asked.
- **`out/`, `cache/`, and `data/mnist/` are gitignored**, so a clone reproduces the
  film only by re-running `mise run data && mise run render`.
- The directory name contains a typo (`deep-nerual-networks`). It has been carried
  through to `metadata.toml`'s `title` field verbatim, since the field is specified as
  deriving from the directory name.

## Usage accounting

The figures in `metadata.toml` are **estimates**, derived as follows — the harness
does not expose per-request token counters to the model, so these are reconstructed
rather than measured.

- **Duration** comes from filesystem timestamps: `.git` at 22:55:58 (session start),
  `descent-1080p.mp4` at 00:30:54, plus the final documentation turn.
- **`model-seconds`** is total wall clock minus measured tool/subprocess time.
  Foreground tool time tallies to roughly 2,800 s (spiral experiments 274 s, training
  145 s foreground, regularisation sweep 20 s, benchmarks ~230 s, preview render
  220 s, final render 1,740 s, viewing-copy encode 117 s, plus many short renders).
  The residual is attributed to model generation, which at `max` effort with heavy
  thinking is consistent with ~35 s per turn across ~115 turns.
- **Token counts** are estimated from work product: ~3,900 lines of Python plus a
  README and these documents (~75k output tokens of file content and edits), thinking
  at max effort (~130k), and prose (~20k). Input is the cumulative context across
  ~115 requests growing from ~15k to ~200k tokens, of which ~22 images
  (~28k tokens) and the final-turn skill load (~70k) are notable contributors.
- **Cache split** assumes the 1-hour prompt-cache TTL this session ran under, with
  continuous work keeping the prefix warm — hence a high cache-read fraction. Cache
  writes are billed at 2× base input for the 1-hour TTL (not 1.25×, which is the
  5-minute rate); cache reads at 0.1×.
- Treat the cost figure as accurate to roughly ±30%.
