# asmdiff summary-table legibility - checkpoint 2026-07-07

Handoff for a fresh session in THIS repo (`~/esp-idf/tools/asmdiff`).
Feature is COMPLETE and verified, NOTHING COMMITTED. Origin: ELF-mode
output (`asmdiff build/S3-Amysynth.elf --filter 'core_'`) was unreadable -
every row hard-wrapped in the terminal (screenshot in repo root,
`Screenshot 2026-07-07 225145.jpg`).

## Root cause

`render_table` pads every column to its widest cell. The `calls` column
sat in the middle; with real firmware call lists (~150 chars at the
8-callee cap) plus 50-char LTO symbol names, `loop spans` started past
column ~210 and every row wrapped: dangling `-` lines, split headers,
call lists colliding with the next function name.

## What changed (asmdiff.py)

1. **Column order**: all three builders now emit
   `function / [role] / insns / loop spans / calls` - calls (the one
   unbounded column) moved last, ragged right. Builders:
   `summary_table`, `file_summary_table` (TOTAL row spans cell stays
   `-`), `inspect_table`. Each gained a `max_width=None` passthrough
   param.
2. **Width-aware trim**: `render_table(rows, max_width=None)` - when
   set, fixed columns keep full width, the last column's over-budget
   cells are trimmed by `_fit_calls`: whole callees dropped from the
   end, closed with `Total Calls:N` (N = full callee count, preserved
   from an existing summary). The FIRST callee is never dropped - a
   single huge symbol overflows/wraps instead of vanishing
   (`sequencer_core_set_melodic_envelope2` is the real-world case).
   Cells that fit, `-`, and headers are untouched. `TOTAL_CALLS_RE`
   guards summary parsing.
3. **`format_calls`** cap suffix changed from `+N more` to
   `Total Calls:N` (user-requested format; one consistent suffix for
   both the 8-callee cap and the width trim).
4. **`table_width()`**: `shutil.get_terminal_size().columns` when
   `sys.stdout.isatty()`, else `None` - piped/redirected output skips
   the trim so it stays deterministic and greppable. Passed at all six
   print sites (both summary_table sites, both file_summary_table
   sites, both inspect_table sites).

All new code sits together around `render_table` (~line 1053 region).

## Tests (test_asmdiff.py)

- New `TestTableMaxWidth` (9 tests): trim-to-fit, fitting cell left
  alone, merges existing `Total Calls:N` (does not recount), single
  callee never dropped, dash untouched, `max_width=None` unchanged,
  builder passthrough, `table_width()` tty/piped (via `mock.patch` on
  `asmdiff.sys.stdout.isatty` / `asmdiff.shutil.get_terminal_size`).
- Updated for new column order / suffix: `TestFormatCalls`
  `test_long_list_capped`, `test_summary_table`,
  `test_summary_table_loop_spans_column`,
  `test_file_summary_totals_and_call_union`, both
  `TestInspectRendering` inspect_table tests.
- Suite: 191 tests, sole failure the KNOWN env-dependent
  `TestShortAliases.test_p_is_pair` (global ~/.config/asmdiff.toml with
  target esp32s3 short-circuits it; fails at HEAD too).

## Docs

README: all 8 example tables reordered; the truncation paragraph (ELF
section, "Only labels the assembler types...") now documents
`Total Calls:N`, the tty-only width trim, and the piped-output
exemption; intro prose order updated. Module docstring summary line
reordered to match. No flags or config keys changed, so
`asmdiff.example.toml` / `EXAMPLE_CONFIG` are untouched (the
byte-match unit test still applies to any future config edit).

## Verification done

- Full suite as above.
- Real ELF (`~/esp-idf/S3-Amysynth/build/S3-Amysynth.elf`, config-derived
  xtensa objdump), `--filter 'core_'`:
  - piped: full capped lists, new column order, aligned.
  - fake tty via `script -qec "stty cols 190 ..."`: all rows one line
    except the designed first-callee-floor overflow (4 chars over).
  - 110-col tty: fixed columns alone can exceed width when a loopy
    function widens `loop spans`; calls floor at one callee - accepted
    graceful degradation, only `calls` ever trims (scope decision).

## Next steps

1. Commit (user has not asked yet; suggested scope: asmdiff.py,
   test_asmdiff.py, README.md in one commit - pure output-format
   change, no behavior change to analysis).
2. Optional follow-up the user explicitly deferred: making the
   `loop spans` cells themselves friendlier (e.g. "2 ZOL + 3 br"
   summary) - scoped OUT of this change ("layout only").
