# Field feedback: S3-Amysynth reverb codegen session — handoff 2026-07-10

Feedback from a real investigation, not a scripted test matrix. Workload:
`stereo_reverb` in AMY's `delay.c` (ESP32-S3 project at `~/esp-idf/S3-Amysynth`,
xtensa-esp-elf-gcc 15.2.0, ship `-O2` flags via `-db`), comparing four source
variants: pre-optimization original (extracted from git history), the shipped
token-paste locals-cache, a struct/`static inline` respelling, and a
`restrict`-annotated original. Modes exercised: two-file `-a FUNC` compare,
`--target` matrix, `-db` + `--flags-like`, host-compiler contrast runs.

## State

The tool answered every question the session asked, correctly. All findings
below are UX/ergonomics, not correctness: in each case asmdiff computed the
right thing and then made the caller dig for it. Ranked by time actually lost.

## Findings

### 1. Summary tables print last; no summaries-only mode
Two-file compare with the default target set produced a 102 KB dump (three full
side-by-side listings, esp32/s2/s3) with the `function/role/insns/loop
spans/calls` table at the *end* of each target section. Every subsequent
invocation was pre-piped through `grep -A4 "role.*insns"` to skip the listings.

Repro: `asmdiff delay_macro.c delay_struct.c -a stereo_reverb -db --flags-like
components/amy/src/delay.c` (no `--target`).

Proposal: `--summary-only` (or print summaries before listings). For
agent/scripted callers the table is the decision input; the listing is only
pulled when a delta needs explaining. A `--json` summary emitter would serve
the same callers.

### 2. Doc drift: `host` target promised, absent from shipped config
The skill/README text says ESP profiles "+ host" are pre-configured;
`~/.config/asmdiff.toml` actually ships `gcc` and `clang`. `--target host`
errors out. The error's target list is what rescued the session — good — but
the drift costs one failed call per session that trusts the docs.

Fix: add a `[host]` alias target to the example/default toml, or correct the
README and the `~/.claude/skills/asmdiff` skill text (both say "host").

### 3. `-db` on foreign-arch targets drops project include paths with the flags
For host targets, `-db` correctly refuses to borrow xtensa flags ("compiling
without its include/define flags") — but that also drops `-I`, so the compile
dies on `fatal error: amy.h: No such file or directory`. Workaround was manual:
`-- -I~/esp-idf/S3-Amysynth/components/amy/src`.

Proposal: the project's own `-I` paths are target-portable even when its `-D`s
are not. For foreign-arch targets, borrow `-I` only (skip `-D`), or offer
`--db-includes`. Keep the current loud note either way — the refusal itself is
right (see FIELD-TEST-REPORT Trap 1 for why silently borrowing would be worse).

### 4. No per-span instruction mix (`--span-stats`)
The decisive number of the session — loop body 96 loads / 31 stores vs 85 / 26,
multiplies identical at 14 — required two rounds of hand-written awk over the
listing, and the first attempt silently counted past the loop end into the
epilogue. The tool's own doctrine is "weigh the span, not the function"; it
should be able to weigh it. The 2026-07-07 ELF round hit the same wall (ad-hoc
awk over objdump for loop tabulation), so this is a recurring gap.

Proposal: `--span-stats` — per-span opcode histogram (loads / stores / mul /
branch / other), per function, alongside the existing `span:count` column.

### 5. Side-by-side pair listings don't collapse identical runs
Two ~500-insn functions differing by ~5 instructions print ~1000 listing lines
of which ~990 are identical pairs. Context-diff-style collapsing (keep N lines
around changed hunks, elide the rest with a count) would turn the 102 KB dump
from finding 1 into a few readable hunks. Interacts with finding 1: summary
first, collapsed hunks second, full listing on request.

### 6. Minor: missing-toolchain error could name the compiler
`--target rp2350` with no ARM toolchain installed → "error: no usable compiler
in the matrix". Accurate, but naming the miss ("arm-none-eabi-gcc: not found
on PATH") makes the fix obvious.

## Verified working this session (regression notes)

- `--flags-like <db-file>` on out-of-tree copies: exact borrowed-flag parity
  both sides — this closes FIELD-TEST-REPORT Trap 2 ("before/after on a project
  source has no supported path") and its Trap 1 hazard for the copy workflow.
- Two-file `-a FUNC` compare against a `git show <rev>:src/file.c` extraction:
  frictionless; this is the natural "what did that upstream commit buy"
  workflow.
- Default target resolution + multi-target matrix from config: zero toolchain
  path digging; glob `cc` patterns resolved and announced.
- `span:count` + `calls` columns answered ZOL and libcall questions at a
  glance.

Session results usable as a future test corpus (esp32s3, ship -O2, AMY at
v1.2.31): `stereo_reverb` original 437 insns / span 327; +`restrict` best
effort 473 / 306; shipped locals-cache 509 / 290; struct respelling 504 / 289.
Host: gcc -O3 467 = 467, clang -O3 443 = 443 (macro vs struct byte-count
identical). Variant sources preserved in the session job dir
(`~/.claude/jobs/b3de0cac/tmp/delay_{orig,restrict,macro,struct}.c`) — copy
them into `demo/` or the test suite if wanted before that dir is cleaned.

## Next steps

1. Finding 1 (`--summary-only`) — smallest change, largest per-call saving.
2. Finding 4 (`--span-stats`) — recurring need across two sessions.
3. Finding 5 (collapse identical hunks) — pairs with 1.
4. Findings 2, 3, 6 — config/doc/message fixes, each ~minutes.
