Summary
The "Engine delivery matrix" is a genuine 2-D cross-tab (message artifacts × engines). Its engine column headers are <th> with no scope="col", and each row's message header is a plain <td>, not a <th scope="row">. A screen reader therefore cannot associate a given delivery-status cell with either its engine column or its message row — the whole matrix is unnavigable non-visually.
Code
forensics/templates/forensics/partials/group_delivery.html:9-40:
<tr>
<th>Message</th> <!-- line 11: no scope="col" -->
{% for engine in delivery_engines %}
<th title="{{ engine.engine_id }}"> <!-- line 13: no scope="col" -->
...
</th>
{% endfor %}
</tr>
...
{% for artifact in artifacts %}
<tr>
<td class="is-wrap"> <!-- line 25: row header emitted as <td>, not <th scope="row"> -->
<div class="is-mono">{{ artifact.artifact_id|truncatechars:33 }}</div>
...
</td>
{% for cell in artifact.delivery_engine_cells %}
<td class="is-wrap">...</td> <!-- data cells -->
{% endfor %}
</tr>
{% endfor %}
Concrete failure scenario
An analyst using a screen reader opens the Delivery tab. Navigating the matrix by cell, assistive tech announces each status badge with no column association (which engine?) and no row association (which message artifact?), because the column <th>s carry no scope and the row identifier is a <td>. The delivery matrix — a core forensic surface — is effectively opaque non-visually.
Why not a duplicate
#246 is about key/value summary tables using row-header <th> without scope="row". This is a distinct template and a distinct structural defect: a true data matrix where the column headers lack scope="col" and the row axis is not a header element at all. #245 is about tab panels being unreachable without JavaScript (a different surface).
Suggested fix
Add scope="col" to the header row <th>s (line 11 and 13), and change the per-row message cell (line 25) to <th scope="row">. This is the standard fix for a two-axis data table.
Severity: LOW — accessibility (consistent with #246).
Summary
The "Engine delivery matrix" is a genuine 2-D cross-tab (message artifacts × engines). Its engine column headers are
<th>with noscope="col", and each row's message header is a plain<td>, not a<th scope="row">. A screen reader therefore cannot associate a given delivery-status cell with either its engine column or its message row — the whole matrix is unnavigable non-visually.Code
forensics/templates/forensics/partials/group_delivery.html:9-40:Concrete failure scenario
An analyst using a screen reader opens the Delivery tab. Navigating the matrix by cell, assistive tech announces each status badge with no column association (which engine?) and no row association (which message artifact?), because the column
<th>s carry noscopeand the row identifier is a<td>. The delivery matrix — a core forensic surface — is effectively opaque non-visually.Why not a duplicate
#246 is about key/value summary tables using row-header
<th>withoutscope="row". This is a distinct template and a distinct structural defect: a true data matrix where the column headers lackscope="col"and the row axis is not a header element at all. #245 is about tab panels being unreachable without JavaScript (a different surface).Suggested fix
Add
scope="col"to the header row<th>s (line 11 and 13), and change the per-row message cell (line 25) to<th scope="row">. This is the standard fix for a two-axis data table.Severity: LOW — accessibility (consistent with #246).