Skip to content

Emit canonical empty save for dynamic nav blocks (fix editor_block_invalid)#340

Merged
chubes4 merged 1 commit into
trunkfrom
cook/editor-block-invalid-residual
Jun 29, 2026
Merged

Emit canonical empty save for dynamic nav blocks (fix editor_block_invalid)#340
chubes4 merged 1 commit into
trunkfrom
cook/editor-block-invalid-residual

Conversation

@chubes4

@chubes4 chubes4 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What

Fixes the editor block-validity residual the offloaded gate surfaced: both content fixtures failed on editor-invalid blocks (15-saas 14, 38-medical 12) — 100% the navigation family. core/navigation/-link/-submenu are DYNAMIC blocks (supports.html:false, save() returns null), but BlockFactory was storing static <nav><ul><li><a><span> chrome → wp.blocks.validateBlock re-runs save (empty), sees leftover tags → every nav block invalid (Expectedendofcontent,insteadsawStartTagnav).

Fix

  • BlockFactory.php: nav family emits canonical empty save markup (inner navigation-link blocks / self-closing comment only); label/url/className ride in the block-comment attrs.
  • BlockValidityValidator.php: added DYNAMIC_EMPTY_SAVE_BLOCKS + dynamic_block_static_markup finding so the fast pure-PHP loop catches static-markup-on-dynamic-block regressions off the editor gate.
  • RuntimeDependencyParityReport.php: recognizes block-comment anchor/className as preserved DOM targets (rendered at runtime), keeping script-target parity honest now that nav stores no static id/class.
  • MaterializationPlanBuilder.php: reads nav links from the canonical navigation-link comments (static <nav><a> scan kept as fallback).

Verification

  • Editor-invalid nav blocks: 15-saas 14→0, 38-medical 12→0 (deterministic transform probe + save()=null reasoning + populated gate artifact).
  • composer test + composer parity: 173 fixtures green; fixtures updated to canonical shape with not_contains guards (static chrome gone) + a fast-loop case proving static <li> is now flagged. Assertions strengthened.
  • Honest limit: SSI consumes php-transformer as a tagged release (^0.2.1), so this reaches the live SSI gate only after a php-transformer release + SSI bump.

AI assistance

  • AI assistance: Yes
  • Tool(s): Claude Code (Claude Opus 4.8, 1M context)
  • Used for: Root-cause diagnosis, fix, and offloaded verification under human review.

…e/navigation family

The navigation family (core/navigation, core/navigation-link,
core/navigation-submenu) are dynamic, server-rendered blocks: supports.html
is false and each registers a render_callback, so their save() returns null.
WordPress stores only the block comment delimiters plus serialized inner
blocks; the <nav>/<ul>/<li> chrome is produced at render time.

BlockFactory was emitting that static <nav><ul> / <li><a><span> chrome into
the stored block. Because save() returns null, wp.blocks.validateBlock re-runs
save() (empty), sees the leftover tags, and flags every navigation block
invalid in the editor ("Expected end of content, instead saw StartTag nav/li").
This was the dominant editor_block_invalid residual surfaced by the offloaded
SSI fixture-matrix gate (navigation/navigation-link were the entire invalid set
in the populated gate artifact; 14 of the cited 21 on 15-saas, 12 of 14 on
38-medical-clinic by direct transform). The label/url/className ride in the
block comment attributes, so the canonical save()-matching shape carries no
inner HTML at all.

- BlockFactory: navigation and navigation-submenu serialize as empty
  opening/closing (inner blocks only); navigation-link serializes as the
  self-closing block comment. Drop the now-dead anchor/submenu markup helpers.
- BlockValidityValidator: add DYNAMIC_EMPTY_SAVE_BLOCKS + the
  dynamic_block_static_markup finding so the fast pure-PHP loop catches any
  future regression that emits static markup for these blocks, off the editor
  gate. Remove the obsolete static-markup link-text check for the nav family.
- RuntimeDependencyParityReport: recognize block-comment anchor/className as
  preserved DOM targets. Anchor and className supports render those onto the
  wrapper at runtime, so a first-party script targeting #id/.class keeps working
  even though the dynamic block now stores no static id=/class= attributes.
- MaterializationPlanBuilder: read navigation links from the canonical
  navigation-link/navigation-submenu block comments (label/url), keeping the
  static <nav><a> scan as a fallback for non-converting navs.
- Update parity + contract fixtures to the canonical block-comment shape,
  adding not_contains guards proving the editor-invalid static chrome is gone
  and a fast-loop case proving clean static <li> markup is now flagged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chubes4 chubes4 merged commit b924704 into trunk Jun 29, 2026
1 check passed
@chubes4 chubes4 deleted the cook/editor-block-invalid-residual branch June 29, 2026 01:43
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