Summary
stx a11y reports "Table missing header cells (th)" for a <table role="presentation">. That role is precisely the declaration that the table is layout, not data — it removes the table semantics from the accessibility tree, so <th> would be wrong to add.
This is the last finding in an app that is otherwise at zero, and it is unfixable by the author: the correct markup is what triggers it.
Reproduction
mail.stx:
<table role="presentation" cellpadding="0" cellspacing="0" style="width: 100%;">
<tr><td>Layout cell</td></tr>
</table>$ stx a11y .
• SERIOUS: Table missing header cells (th)
Use th elements for table headers
Element: <table role="presentation" cellpadding="0" cellspacing="0" style="width: 100%;">Adding <th> to satisfy the rule would reintroduce the semantics the role exists to remove.
Why it shows up in a real app
Email templates. Every HTML email is nested layout tables — mail clients strip <style> and have no reliable box model, so role="presentation" on layout tables is the standard, correct pattern. Our one email template accounts for 3 of 3 remaining findings.
Worth noting the rule is right to exist: a data table with no <th> is a real problem. It just needs to skip tables that have declared themselves presentational.
Suggested fix
Skip the check when the table carries role="presentation" or role="none" (the two are synonyms in ARIA). Both should be honoured.
A test asserting that a role="presentation" table produces no finding, and that an otherwise identical table without the role still does, would pin it in both directions.
Context
stx a11y is advisory — it exits 0 regardless — so this is not blocking a gate. It matters because an advisory checker with a permanent, uncloseable finding is one an app learns to ignore, which is the same failure the handoff describes for a gate that invents errors.