Skip to content

Layout follow-ups: clamp the report header, align the budget percentage - #146

Merged
marcoarnulfo merged 10 commits into
mainfrom
fix/layout-followups
Jul 31, 2026
Merged

marcoarnulfo merged 10 commits into
mainfrom
fix/layout-followups

Conversation

@marcoarnulfo

Copy link
Copy Markdown
Owner

🇬🇧 English

Summary

The two follow-ups the tranche D final review filed. One is a fix, one is a fix plus a decision not to change anything.

Issue What the user saw
#144 The budget burn-down measured the widest percentage label to size its name column, then never padded to it, so a row at 62% started its figures one column left of a row at 104%.
#143 The report's header line was the last line above the table never made width-aware: measured, it rendered 68 columns on both a 40- and a 60-column terminal and wrapped.

The half of #143 that closes without code. The issue also asked to stop the overlay compositor from letting the background show beside the palette box. Measuring says there is nothing to fix:

  • The exposure beside the box is one column at width 40 and zero at every other width — it is the report table's own right border, legitimately inside the terminal. The "16 columns" in the issue was the header's overflow (row 0, above the box at paletteTopY = 2) attributed to the compositor by a formula that measured the widest body row instead of the widest covered row.
  • Clipping it would break the contract at overlay.go:11-13 — "cells of body outside the box's rectangle survive verbatim — that layering is the whole point of Keymap registry + navigation stack + overlay compositor (foundations) #59" — and the three tests that pin it. On a wide terminal the body legitimately extends between the box's edge and the screen's, so clipping would erase exactly the depth that contract defends.

So instead of a crop there is a test, TestCompositeDoesNotClipTheReportTableBesideANarrowPalette, that pins that column as intended and names the three tests that would fall if someone tried again. The reasoning is recorded on the issue, including a correction of an earlier comment of mine that carried the wrong figure.

Linked issue

Closes #144
Closes #143

Type of change

  • Bug fix
  • New feature
  • Docs
  • Refactor / chore

Checklist

  • go test ./... -race passes
  • gofmt, go vet, staticcheck are clean
  • Docs updated (CHANGELOG)
  • Commit messages follow Conventional Commits
  • No Co-Authored-By in commit messages

Notes for the reviewer

  • internal/tui/overlay.go is untouched. The only production changes are budget.go (the percentage width, measured once and shared with the layout arithmetic) and report.go (the header clamp).
  • One golden moves: palette_narrow.golden, whose header and its margin line drop from 68 columns to 40. No golden rendered at 80 moves, because 68 fits there.
  • Two tests exist because the reviews found the invariant unprotected rather than broken: the header is clamped before th.Title.Render (the style carries MarginBottom(1), so truncating the rendered string would leave the padding line long), and the budget label stays natural at width <= 0. Inverting either passed the whole suite before those tests existed.

🇮🇹 Italiano

Riepilogo

I due follow-up aperti dalla review finale della tranche D. Uno è una correzione, l'altro è una correzione più la decisione di non cambiare niente.

Issue Cosa vedeva l'utente
#144 Il burn-down budget misurava l'etichetta percentuale più larga per dimensionare la colonna del nome, e poi non ci allineava niente: una riga al 62% iniziava le cifre una colonna più a sinistra di una riga al 104%.
#143 La riga di intestazione del report era l'ultima sopra la tabella mai resa width-aware: misurata, rendeva 68 colonne sia su un terminale da 40 sia su uno da 60, e andava a capo.

La metà della #143 che si chiude senza codice. La issue chiedeva anche di impedire al compositore dell'overlay di lasciar vedere il fondo accanto al box del palette. La misura dice che non c'è niente da correggere:

  • L'esposizione accanto al box è una colonna a larghezza 40 e zero a ogni altra larghezza — è il bordo destro della tabella del report, legittimamente dentro il terminale. Le «16 colonne» della issue erano lo sforamento dell'intestazione (riga 0, che sta sopra il box a paletteTopY = 2) attribuito al compositore da una formula che misurava la riga più larga del body invece della riga coperta più larga.
  • Ritagliarla romperebbe il contratto a overlay.go:11-13 — «cells of body outside the box's rectangle survive verbatim — that layering is the whole point of Keymap registry + navigation stack + overlay compositor (foundations) #59» — e i tre test che lo inchiodano. Su un terminale largo il body sta legittimamente fra il bordo del box e quello dello schermo, quindi il ritaglio cancellerebbe proprio la profondità che quel contratto difende.

Al posto del ritaglio c'è un test, TestCompositeDoesNotClipTheReportTableBesideANarrowPalette, che inchioda quella colonna come voluta e nomina i tre test che cadrebbero se qualcuno ci riprovasse. Il ragionamento è registrato sulla issue, inclusa la correzione di un mio commento precedente che portava il numero sbagliato.

Issue collegata

Closes #144
Closes #143

Tipo di modifica

  • Bug fix
  • Nuova funzionalità
  • Documentazione
  • Refactor / manutenzione

Checklist

  • go test ./... -race passa
  • gofmt, go vet, staticcheck puliti
  • Documentazione aggiornata (CHANGELOG)
  • I messaggi di commit seguono Conventional Commits
  • Nessun Co-Authored-By nei messaggi di commit

Note per chi rivede

  • internal/tui/overlay.go è intatto. Le uniche modifiche di produzione sono budget.go (la larghezza della percentuale, misurata una volta e condivisa con l'aritmetica del layout) e report.go (il clamp dell'intestazione).
  • Si muove un golden: palette_narrow.golden, la cui intestazione e la sua riga di margine scendono da 68 colonne a 40. Nessun golden reso a 80 si muove, perché lì 68 ci sta.
  • Due test esistono perché le review hanno trovato l'invariante non protetto, non rotto: l'intestazione viene troncata prima di th.Title.Render (lo stile porta MarginBottom(1), quindi troncare la stringa già resa lascerebbe lunga la riga di padding), e l'etichetta del budget resta naturale a width <= 0. Invertire l'uno o l'altro passava tutta la suite prima che quei test esistessero.

Measured before writing, and the measurements reordered the work: the report
header overflows at 60 columns too, not just at 40 (68 columns once the member
note is there), and the 16 columns of background exposed beside the palette box
at width 40 come from that header rather than from the table. So the header
clamp goes first, and the compositor decision is made afterwards against the
new numbers instead of against these.

Also recorded: dimming the exposed background is byte-identical to today under
the termenv.Ascii that TestMain pins, so no golden could verify it — the same
trap as #141.
The review found three critical defects, all mine:

- The exposure formula measured the wrong rows. The widest body line is the
  title at row 0, which sits ABOVE the box (paletteTopY = 2, covering rows
  2-13), so "16 columns exposed beside the box" was the title's own overflow
  attributed to the compositor. Measured properly, iterating only over covered
  rows: the exposure is 1 column at width 40 and 0 everywhere else, it is the
  report table's right border, and clamping the header does not change it. The
  Task 3 dependency on Task 2 was an artifact of the same formula.
- Clipping that column would break composite's documented layering contract
  (overlay.go:11-13) and three tests that pin it, and the safety argument for
  it was false: after the header clamp nothing overflows the terminal, and on a
  wide terminal the body legitimately extends between the box and the screen
  edge. So #143's compositor half closes without code, with a test that pins
  the column as intended.
- Both prescribed budget tests could never pass. One anchored the figure column
  on "/" with fixture rows whose Billed values differ in width, asserting an
  alignment the spec deliberately does not provide; the other asserted the
  absence of a separator space that the prescribed implementation always emits.

Also corrected: "dimming is unverifiable by this suite" is false —
TestCompositeDoesNotLeakStyleIntoTheBox builds its own colored renderer for
exactly the failure mode goldens cannot see. It stays cut, for the right reason.
…ngelog

- overlay_test.go: correct the comment on the narrow-palette clipping test —
  row 7 is not "the widest row, against 38 for every other covered row"; the
  six table rows below it are 39 cells too and each exposes a cell, so what
  survives beside the box is the table's whole right-border strip, not one
  stray cell. Remeasured from goldenPaletteModel at width 40 to confirm.
- budget_test.go: add TestBudgetViewLeavesPercentLabelsNaturalAtZeroWidth,
  the untested half of the #144 invariant documented on budget.go's view —
  that width <= 0 never pads the percentage label. Verified it fails without
  the guard in view (pctW measured unconditionally) and passes with it.
- CHANGELOG.md: finish the dangling "instead of ragged" into "instead of a
  ragged column".
@marcoarnulfo
marcoarnulfo merged commit 207e338 into main Jul 31, 2026
4 checks passed
@marcoarnulfo
marcoarnulfo deleted the fix/layout-followups branch July 31, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant