Skip to content

The per-incident table's header and its body were two separate lists - #42

Merged
ChelseaKR merged 1 commit into
mainfrom
fix/the-incident-table-header-and-body-can-disagree
Sep 2, 2026
Merged

The per-incident table's header and its body were two separate lists#42
ChelseaKR merged 1 commit into
mainfrom
fix/the-incident-table-header-and-body-can-disagree

Conversation

@ChelseaKR

Copy link
Copy Markdown
Owner

The defect

dins_page built the per-incident coverage table from two lists that had to agree and
nothing made them:

cells = "".join(
    f'<td class="num">{pct(by_name[name].present_tenths_pct)}</td>'
    for name in incident_fields          # five field names
    if name in by_name                   # ...silently filtered
)
incident_headers = "".join(
    f'<th scope="col" class="num">{esc(label)}</th>'
    for label in ("Structure type", "Roof", "Eaves", "Vent screen", "APN")  # five labels
)

A field leaving DINS_FIELDS drops its cell from every row and leaves all five headers
standing. That does not blank a column. Every cell after the missing one moves left, so
the vent-screen share is published under Eaves: correctly counted, wrongly labelled, on a
page whose entire subject is not misreading a number.

Nothing is wrong on the page today. All five fields are in DINS_FIELDS, so this is a
defect the code could produce rather than one it had produced, and every cell of the
rebuilt table is byte-identical. The two-list shape is one edit away from the bad state
and nothing between here and the deploy would report it.

The per-decade acreage table on the other page already reads its header off its body,
with a comment saying why. This applies the same discipline to the one table that had not
had it.

The fix

  • render.INCIDENT_FIELD_COLUMNS pairs each field name with its column head, and the
    header and the body both read that one list.
  • A column the report carries no coverage for is written as not counted rather than
    dropped, so the row keeps its width whatever happens to the registry.
  • The paragraph under the table says so, and no longer says "five".

The gate

html-validate does not compare row widths and neither does axe. A short row renders as
an ordinary table, so nothing in this repository could have reported it.

tests/test_pages_html.py::row_width_problems counts cells per row per table, colspan
included, across all three pages, and reports a table with no rows rather than passing
over one.

Per ADR-0004 it is run against input that must not pass: a row missing a cell, markup with
no table at all, a colspan that covers the gap, and
test_the_per_incident_table_writes_a_cell_for_a_field_it_cannot_count, which takes a
real DinsReport and removes one field from every incident. Against the previous renderer
that last one reports table 3: row 1 is 9 cells wide, the header is 10 for every
incident row; against this one it reports nothing.

site/

make site-check, added yesterday in #41, caught site/ going stale on the changed
paragraph before the commit rather than after the deploy. site/dins.html is rebuilt and
every cell in the table is unchanged, which is the evidence that this moves no published
number.

Gate

make verify green locally end to end, and make site-check green against the acquired
FRAP and DINS files.

🤖 Generated with Claude Code

The header of the per-incident coverage table was five hard-coded labels. The
body was a separate tuple of five field names, filtered by `if name in
by_name`. A field leaving DINS_FIELDS would have dropped its cell from every
row and left all five headers standing, which does not blank a column: every
cell after the missing one moves left, and the vent-screen share is published
under Eaves, correctly counted and wrongly labelled.

Nothing is wrong on the page today. All five fields are in DINS_FIELDS, so this
is a defect the code could produce rather than one it had produced, and every
cell of the rebuilt table is byte-identical.

Header and body now read one paired list, INCIDENT_FIELD_COLUMNS, and a column
the report carries no coverage for is written as "not counted" rather than
dropped, so the row keeps its width either way. The per-decade acreage table on
the other page already reads its header off its body for this reason.

html-validate does not compare row widths and neither does axe, so a short row
renders as an ordinary table and nothing would have reported it.
`row_width_problems` counts cells per row per table, colspan included, over all
three pages, and reports a table with no rows rather than passing over one. Per
ADR-0004 it is run against a row missing a cell, a page with no table, and a
real DinsReport with a field removed from every incident: against the previous
renderer that last one reports rows nine cells wide under a ten-cell header.

`make site-check` caught site/ going stale on the paragraph under the table,
one day after it was added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChelseaKR
ChelseaKR merged commit 1374e7f into main Sep 2, 2026
6 checks passed
@ChelseaKR
ChelseaKR deleted the fix/the-incident-table-header-and-body-can-disagree branch September 2, 2026 03:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant