# asmdiff Visual Demo Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Add two captured terminal GIFs to the asmdiff README (a reproducible host quickstart and a real-world ESP32 cross-compile matrix) plus a prose augmentation naming the real AMY find and fixing a mis-attribution.

**Architecture:** Two independent VHS recording pipelines produce `demo/quickstart.gif` (from the repo root, bundled harness, host gcc/clang) and `demo/esp32-matrix.gif` (from `~/esp-idf/S3-Amysynth`, xtensa toolchains on PATH). Only the GIFs and README edits are committed; the `.tape` scripts stay in the scratchpad. Spec: `docs/superpowers/specs/2026-07-08-asmdiff-demo-gifs-design.md`.

**Tech Stack:** VHS 0.11.0 (`charmbracelet/vhs`) + ttyd 1.7.7 + ffmpeg; asmdiff (installed on PATH); host gcc/clang; xtensa-esp-elf gcc toolchains.

## Global Constraints

- Public PyPI repo: **no machine-specific absolute paths or usernames** in any committed artifact (GIF frames included). GIF 2 config MUST use bare-PATH compiler names so `resolve_cc` (asmdiff.py:850-868) prints no `/home/...` and no glob banner.
- **Tapes are not committed.** Author them under the session scratchpad; set their `Output` to the repo's `demo/` path. Only `demo/*.gif` and `README.md` are committed.
- **Do not commit `docs/superpowers/`** (process docs) to this public repo - leave untracked. This plan file included.
- Currently on `main`. Branch before committing deliverables.
- Amy prose numbers come from `amy-hotspot-analysis.md` (already verified); no `~/esp-idf/Amy` checkout or rebuild.
- Scratchpad dir: `/tmp/claude-1000/-home-fatta-esp-idf-tools-asmdiff/bbf06738-8306-4d22-8d83-e94ad70c3edc/scratchpad`

---

### Task 0: Branch and demo dir

**Files:**
- Create: `demo/` (directory)

- [ ] **Step 1: Create a working branch**

Run:
```bash
cd /home/fatta/esp-idf/tools/asmdiff
git checkout -b demo-gifs
```
Expected: `Switched to a new branch 'demo-gifs'`

- [ ] **Step 2: Create the demo directory**

Run: `mkdir -p /home/fatta/esp-idf/tools/asmdiff/demo`
Expected: no output; `demo/` exists.

---

### Task 1: GIF 1 - host quickstart

**Files:**
- Create (scratchpad, uncommitted): `<scratchpad>/quickstart.tape`
- Produce (committed): `demo/quickstart.gif`

**Interfaces:**
- Command recorded: `asmdiff asmdiff_example.c --cc 'gcc -O3' --cc 'clang -O3'`
- Output is 49 lines, max 91 cols (measured 2026-07-08). Frame must fit this without scrolling.

- [ ] **Step 1: Re-measure output to confirm geometry**

Run:
```bash
cd /home/fatta/esp-idf/tools/asmdiff
python3 asmdiff.py asmdiff_example.c --cc 'gcc -O3' --cc 'clang -O3' | awk '{if(length>w)w=length} END{print NR" lines, "w" cols"}'
```
Expected: `49 lines, 91 cols` (if materially different, adjust Height/Width in Step 2).

- [ ] **Step 2: Write the tape**

Write `<scratchpad>/quickstart.tape` (Height sized to fit ~52 lines at FontSize 14):
```tape
Output /home/fatta/esp-idf/tools/asmdiff/demo/quickstart.gif
Require asmdiff

Set Shell    bash
Set FontSize 14
Set Width    1040
Set Height   1000
Set Padding  24
Set Theme    "Builtin Solarized Dark"
Set TypingSpeed 45ms

Type "asmdiff asmdiff_example.c --cc 'gcc -O3' --cc 'clang -O3'"
Sleep 500ms
Enter
Sleep 5s
```

- [ ] **Step 3: Record**

Run:
```bash
cd /home/fatta/esp-idf/tools/asmdiff
vhs <scratchpad>/quickstart.tape
```
Expected: VHS runs, `demo/quickstart.gif` is written (no `Require` failure).

- [ ] **Step 4: Verify the artifact**

Run:
```bash
ls -la demo/quickstart.gif
```
Expected: file exists, size < ~1.5 MB.
Then open the GIF and confirm: the full 49-line output is visible (no scroll cut-off), the `old_*|new_*` side-by-side columns are not width-trimmed, and the summary table's `calls` column shows `exp2f`/`ldexpf`. If truncated: lower FontSize / raise Width or Height in the tape and re-run Step 3. If file too large: shorten the trailing `Sleep`.

- [ ] **Step 5: Embed in README and commit**

Edit `README.md`: after the tagline block, immediately before line 6 (`### Try it yourself:`), insert:
```markdown
![asmdiff quickstart - side-by-side assembly and a fold-vs-libcall summary](demo/quickstart.gif)

```
Then:
```bash
git add demo/quickstart.gif README.md
git commit -m "docs: add host quickstart demo GIF to README"
```

---

### Task 2: GIF 2 - ESP32 cross-compile matrix

**Files:**
- Create (scratchpad, uncommitted): `<scratchpad>/esp32-matrix.tape`
- Create (in S3-Amysynth, uncommitted there): a demo `asmdiff.toml` with bare-PATH targets, OR reuse an existing config edited to bare-PATH names
- Produce (committed to asmdiff repo): `demo/esp32-matrix.gif`

**Interfaces:**
- Command recorded: `asmdiff main/main.c -db` (run in `~/esp-idf/S3-Amysynth`)
- `-db` = `--compile-commands` (asmdiff.py:1479); auto-discovers `build/compile_commands.json`.
- Config default target must resolve to esp32 / esp32s2 / esp32s3 via **bare-PATH** cc values, e.g. `cc = "xtensa-esp32-elf-gcc -O2 -mlongcalls"`.

- [ ] **Step 1: Put the xtensa toolchains on PATH**

Run (adjust to the actual IDF export path used in this workspace):
```bash
cd /home/fatta/esp-idf/S3-Amysynth
. $HOME/esp-idf/export.sh    # or the project's documented export
which xtensa-esp32-elf-gcc xtensa-esp32s2-elf-gcc xtensa-esp32s3-elf-gcc
```
Expected: all three resolve to a path on PATH (the bare name will now run).

- [ ] **Step 2: Ensure a bare-PATH default matrix config**

Confirm the config `asmdiff main/main.c` will use names the esp32/s2/s3 targets with **bare-PATH** cc values and a `default` listing all three. If the active config uses version-glob paths, create a demo config next to the sources (or in CWD) whose targets read like:
```toml
default = ["esp32", "esp32s2", "esp32s3"]

[esp32]
cc = "xtensa-esp32-elf-gcc -O2 -mlongcalls"
[esp32s2]
cc = "xtensa-esp32s2-elf-gcc -O2 -mlongcalls"
[esp32s3]
cc = "xtensa-esp32s3-elf-gcc -O2 -mlongcalls"
```

- [ ] **Step 3: Dry-run to confirm clean output and measure size**

Run:
```bash
cd /home/fatta/esp-idf/S3-Amysynth
asmdiff main/main.c -db 2>&1 | awk '{if(length>w)w=length} END{print NR" lines, "w" cols"}'
asmdiff main/main.c -db 2>&1 | grep -c '/home/'
```
Expected: three `== xtensa-esp32*-elf-gcc -O2 -mlongcalls ==` headers, **zero** `/home/` matches (second command prints `0`), no `cc pattern matched N toolchains` banner. Note the line count to size the frame in Step 4. If `/home/` count > 0, the config still uses glob paths - fix Step 2 before recording.

- [ ] **Step 4: Write the tape**

Write `<scratchpad>/esp32-matrix.tape` (set Height from the Step 3 line count; the three stacked tables are long - use FontSize 13 and, if it scrolls unreadably, add `--filter` to a representative function subset in the recorded command and note that in the README caption):
```tape
Output /home/fatta/esp-idf/tools/asmdiff/demo/esp32-matrix.gif
Require asmdiff

Set Shell    bash
Set FontSize 13
Set Width    1100
Set Height   1200
Set Padding  24
Set Theme    "Builtin Solarized Dark"
Set TypingSpeed 45ms

Type "asmdiff main/main.c -db"
Sleep 500ms
Enter
Sleep 6s
```
Note: VHS starts a fresh shell, so the tape must run in a shell that already has the toolchains on PATH. Launch `vhs` from the same terminal where Step 1 was sourced, or prepend a `Source`/`Type ". export.sh"` beat and trim it - simplest is to source in the launching shell.

- [ ] **Step 5: Record**

Run (from `~/esp-idf/S3-Amysynth`, PATH sourced):
```bash
cd /home/fatta/esp-idf/S3-Amysynth
vhs <scratchpad>/esp32-matrix.tape
```
Expected: `demo/esp32-matrix.gif` written.

- [ ] **Step 6: Verify the artifact**

Run: `ls -la /home/fatta/esp-idf/tools/asmdiff/demo/esp32-matrix.gif`
Expected: exists, < ~3 MB.
Open the GIF and confirm: all three target tables render, headers read `== xtensa-esp32*-elf-gcc ... ==` with **no** `/home/` path and no glob banner, columns not width-trimmed. Fix geometry/`--filter` and re-record if needed.

- [ ] **Step 7: Embed in README and commit**

Edit `README.md`: in the ELF / real-firmware area (near the "ELF input: what actually shipped after LTO" discussion, around the existing `sequencer_*` block ~line 258-277), insert the embed at a natural spot:
```markdown
![asmdiff running a three-chip xtensa matrix (esp32/s2/s3) from one project via -db](demo/esp32-matrix.gif)

```
Then:
```bash
cd /home/fatta/esp-idf/tools/asmdiff
git add demo/esp32-matrix.gif README.md
git commit -m "docs: add ESP32 cross-compile matrix demo GIF to README"
```

---

### Task 3: Amy prose augmentation + attribution fix

**Files:**
- Modify: `README.md:258-277` (the real-firmware ELF prose)

**Interfaces:**
- Verified facts (from `amy-hotspot-analysis.md`): `sequencer_timer_callback` (77 insns, loop `.L21:41`, calls `__divsf3` + `__udivdi3`) and `sequencer_recompute` live in **vendored AMY** (`components/amy/src/sequencer.c`; divide sourced from `amy_sysclock()` in `components/amy/src/api.c`), NOT synth_core. Only `arp_rebuild` is project code. Confirmed as a codegen cost AND an upstream correctness bug (u32 sample-count wrap at 24.85h + float precision decay); fixed locally, PR pending to shorepine/amy.

- [ ] **Step 1: Fix the attribution sentence**

In `README.md`, the sentence at ~line 258-260 currently reads "the same binary's sequencer core (project code, not a synth library) surfaces three findings". Replace the parenthetical so it correctly attributes `sequencer_timer_callback`/`sequencer_recompute` to vendored AMY and notes only `arp_rebuild` is project code. Keep the surrounding sentence structure and tone.

- [ ] **Step 2: Add the "found and fixed" prose**

After the existing silent-divide callback description (~line 271-274, the "caught in shipped firmware instead of a harness" sentence), add one or two sentences: asmdiff was built to hunt AMY hotpath codegen, and this `__divsf3 + __udivdi3` pairing in AMY's 2 kHz sequencer timer callback was confirmed as both a cost and an upstream correctness bug (the `amy_sysclock()` u32 wrap + float precision decay), since fixed locally with a PR pending to shorepine/amy. Keep it factual and brief; no numbers beyond those already in the section.

- [ ] **Step 3: Verify prose consistency**

Run:
```bash
cd /home/fatta/esp-idf/tools/asmdiff
grep -n 'project code, not a synth library' README.md
grep -n 'AMY\|amy_sysclock\|shorepine' README.md | head
```
Expected: the first grep returns **nothing** (old mis-attribution gone); the second shows the new AMY references. Confirm no em dashes were introduced (use `-`).

- [ ] **Step 4: Commit**

Run:
```bash
git add README.md
git commit -m "docs: attribute the real sequencer divide find to AMY and note the fix"
```

---

### Task 4: End-to-end render check

- [ ] **Step 1: Push the branch**

Run:
```bash
cd /home/fatta/esp-idf/tools/asmdiff
git push -u origin demo-gifs
```
Expected: branch pushed (confirm with the user before pushing if the remote is public).

- [ ] **Step 2: Confirm inline rendering**

On the pushed branch's README (GitHub, or a local Markdown preview), confirm both GIFs render inline at the intended spots and animate, and the Amy prose reads correctly. If a GIF is too large to render smoothly, return to its task's geometry/Sleep tuning.

- [ ] **Step 3: Report**

Summarize: both GIFs' final file sizes, that GIF 2 output carries no `/home/` paths, and that the README attribution is corrected. Offer to open a PR or merge per the user's preference.

---

## Self-Review

- **Spec coverage:** GIF 1 (Task 1), GIF 2 with bare-PATH constraint (Task 2), Amy prose + attribution fix (Task 3), tapes-uncommitted + no-username constraints (Global Constraints + Task 2 Step 3 check), placement (Task 1 Step 5, Task 2 Step 7), verification (Task 4). All spec sections mapped.
- **Placeholders:** `<scratchpad>` is a named path (given in Global Constraints), not a TBD. IDF export path in Task 2 Step 1 is environment-specific by nature and flagged as "adjust to the actual path" - unavoidable for a private workspace.
- **Consistency:** `-db`/`--compile-commands`, `resolve_cc` line refs, and the verified AMY facts match the spec and asmdiff.py.
</content>
