Automattic / Automattic/blocks-engine
Resolve generated companion blocks from canonical content attributes
- Dominant language
- PHP
- Stars
- 14
- Forks
- 2
- Avg merge
- 2h 10m
- Merged PRs (30d)
- 561
Description
## Problem
Generated companion blocks carry two copies of the same captured markup:
- the editable `content` block attribute, which the WordPress site-plan path canonicalizes and resolves
- `companion_plugin_payload.blocks[].render`, which remains the original static HTML
Static Site Importer scaffolds the second copy into `render.php`. The frontend therefore renders the uncanonicalized copy and bypasses the resolved editable attribute.
For local browser assets inside a generated custom block, this produces broken requests even though the assets were copied and the canonical page content is valid. For example, a captured gallery containing:
```html
```
is copied into the theme and represented by canonical site-plan asset references, but the generated companion plugin still emits `/assets/css/gallery.css` and `/media/work.jpg` from its static `render.php`.
## Production evidence
Source: https://www.junedigann.com/
- 19/19 routes compile and materialize.
- The site plan has zero unresolved-local-browser-reference diagnostics after #1210.
- All 19 pages open in Gutenberg with zero invalid blocks or block-validation notices.
- The imported homepage requests nonexistent root-relative `/assets/css/capture-*.css` paths from a generated gallery renderer.
- The same CSS files exist under the generated theme and are also enqueued through `get_theme_file_uri()`.
- Playwright full-page comparison at 1440px reports 8.17% mismatched pixels on the homepage; gallery images render as empty boxes.
- The generated plugin's `blocks/gallery-*/render.php` contains the original root-relative `` and media markup.
## Root cause
`AssetReferenceCanonicalizer` processes canonical page block markup, but `CompanionPluginPayload::normalizeGeneratedBlock()` packages the independent raw `render` string. The consumer then treats that string as the authoritative frontend renderer instead of rendering the resolved `content` attribute.
This is a producer/consumer contract gap rather than another URL-parser case. Replacing URL strings in the static renderer would still leave two divergent content authorities and would not make frontend rendering reflect editor changes.
## Proposed contract
1. Blocks Engine emits an audited generic renderer identifier for generated sanitized-markup blocks instead of a static duplicate `render` payload.
2. The companion block declares a string `content` attribute as its single content authority.
3. Static Site Importer implements that renderer using the resolved `content` attribute with its existing executable-markup and URL safety policy.
4. Contract coverage proves local stylesheet, image, `srcset`, poster, and inline-style URL references render through destination theme URLs after materialization.
5. End-to-end coverage verifies a generated custom gallery has no root-relative artifact requests and that an editor content change affects frontend output.
Related parser fix: #1209 / #1210.
## AI assistance
OpenAI GPT-5.6-Sol via OpenCode helped inspect the materialized WordPress output, run Playwright visual and Gutenberg validation, trace the divergent site-plan and companion-render paths, and draft this issue. Chris Huber remains responsible for the report.
Contributor guide
Research direction
Start by tracing AssetReferenceCanonicalizer and CompanionPluginPayload::normalizeGeneratedBlock(), then inspect the generated blocks/gallery-*/render.php output and the Static Site Importer consumer. Use the proposed renderer and content-attribute contract as the scope. Done means local stylesheet, image, srcset, poster, and inline-style references use destination theme URLs, editor content changes affect frontend output, and end-to-end checks show no root-relative artifact requests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- php
- Domain
- backend-api-design, frontend, testing
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100