Skip to content

Extract table routing and the unsupported-element fallback (#242) - #1315

Merged
chubes4 merged 1 commit into
trunkfrom
refactor/242-table-and-unsupported
Aug 28, 2026
Merged

Extract table routing and the unsupported-element fallback (#242)#1315
chubes4 merged 1 commit into
trunkfrom
refactor/242-table-and-unsupported

Conversation

@chubes4

@chubes4 chubes4 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Slice 3 for #242, workstream 1. Follows #1310 and #1312.

What moved

TableElementConverter — the table branch. Ordering is the contract here: layout-table shapes (isNestedLayoutTableMember, isMediaLayoutTable, isPercentLayoutTable) are claimed before the element reaches data-table classification, so a table used purely for layout becomes columns rather than a semantic core/table. That precedence is now asserted directly rather than implied by statement order in a 300-line method.

TableClassificationPolicy was already a first-class collaborator, so it is injected as itself instead of wrapped in closures — only the transformer-owned block builders are passed as closures.

UnsupportedElementRecorder — the terminal captureUnsupported block that closes convertElement(). This is the core/html fallback decision from issue #497: generate a static-render custom block when the structural classifier is confident, otherwise record an html_unsupported_element diagnostic and drop the element.

Behavior preservation

Corpus hash diff on clean trunk before any edit, recaptured after — serializedBlocks SHA-256 plus block/diagnostic/fallback counts for every fixture HTML file:

385 fixtures — DIFFERING: 0 — threw: 0

The fallback counts matter for this slice specifically: UnsupportedElementRecorder appends diagnostics, so an identical fallback count across the corpus is what shows the diagnostic path is unchanged, not just the block output.

composer test exit 0 — 289 parity fixtures, contract suite, unit suite, packaging proof.

Coverage

Two new isolation tests, neither constructs a transformer:

  • tests/unit/table-element-converter.php — 13 assertions. Covers that layout projection precedes preservation (a nested table is unrepresentable by classification, but the layout branch claims it first and yields columns), and that a spanning table is preserved rather than lossily flattened.
  • tests/unit/unsupported-element-recorder.php — 15 assertions. Covers that a generated custom block short-circuits the fallback entirely with no diagnostic recorded, the full diagnostic row shape, and that control metadata is attached only when the element actually has it.

Impact

trunk after
HtmlTransformer.php 12,606 12,608
convertElement() 349 301

The class grew by 2 lines — the two context factories cost slightly more than the extracted branches saved. That is the honest number and it is the point: this epic is not about shrinking the file, it is about removing methods from the transformer's $this scope and making them testable.

Cumulative across the three slices: convertElement() 441 → 301, with 9 collaborator files and 100 isolation assertions that construct no transformer.

Scope

convertElement() is still a 301-line ordered chain. What remains inline is mostly guard clauses and pattern-recognizer delegations that are already thin; the next meaningful targets are the media/anchor/inline/list/figure convertXDispatchElement methods, which are separate methods but still trait- or $this-bound.


AI assistance disclosure: implemented and drafted by Claude Sonnet 4.6 running in Claude Code, operated by @chubes4. The AI captured the pre-change corpus baseline, performed both extractions, verified byte-identical serializedBlocks and fallback counts across 385 fixtures, and wrote the isolation tests. Reviewed by a human before opening.

TableElementConverter owns the table branch, where ordering is the
contract: layout-table shapes are claimed before data-table
classification, so a table used for layout becomes columns rather than a
semantic core/table. TableClassificationPolicy was already a
collaborator and is injected directly instead of wrapped in closures.

UnsupportedElementRecorder owns the terminal core/html fallback decision
(issue #497) that closes convertElement.
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