Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions dev/test/fixtures/visual-review/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Visual finishing exercise

Two synthetic regions exercise [the rendered-delivery workflow](../../../../skills/block-runner/references/VISUAL.md): a long-copy page and a shared template part. The reference uses different HTML elements from the native block tree. Its copy must survive unchanged.

`styles/native.css` deliberately narrows the article and closing note and forces the highlight wider than a phone. `styles/native.corrected.css` contains the source correction, including the WordPress first-child spacing rule found during review. Keep the original fixture intact; copy it to a separate working directory before substituting corrected CSS.

## Replay

Build the checkout with `npm run build`. Serve this directory with a local static server and open `reference.html`. Compile the native source:

```sh
node dist/cli.js site compile dev/test/fixtures/visual-review/site.intent.json --asset-root dev/test/fixtures/visual-review --out /tmp/visual-review-before --strict --json
```

Use a new output path if that directory already exists. Install the emitted `theme/` as `visual-review-demo` on an explicitly selected disposable WordPress 7.1 site and activate it. Use the existing `site plan` / hash-confirmed `site apply` route from [SITE.md](../../../../skills/block-runner/references/SITE.md); retain the plan, journal and readback. No production target is needed.

Compare the rendered reference and native homepage at 1280×900 and 390×844, with the same scale and scrollbar state. Check content, then the article, highlight and closing note. Retain paired screenshots and targeted bounding boxes; inspect the images.

Copy this fixture to a working directory, replace that copy's `styles/native.css` with `styles/native.corrected.css`, then compile to a new output directory. Deploy the changed theme bytes to the same disposable target, plan/apply again and recapture the same regions. Do not patch serialized block delimiters or discard the initial evidence. Finish with a representative native editor edit, save and reopen.

## Observed comparison

On 2026-10-24, local WordPress 7.1 / PHP 8.4.27 and the in-app Chromium browser showed:

| Region / view | Reference | Before | After |
| --- | --- | --- | --- |
| Article / desktop | 720px wide | 480px; extra wrapping | 720px |
| Closing part / desktop | 960px, centred text | 440px, left-aligned | 960px, centred |
| Highlight / mobile | 342px | 520px; document overflow to 544px | 342px; document width 390px |
| Article top / desktop, mobile | 48px, 24px | 0px, 0px | 48px, 24px |

Matched mobile viewports used 390 CSS pixels with scrollbars hidden on both sides; desktop used 1280. The corrected region text, widths, heights, positions and alignment matched the reference. The screenshots showed the same wrapping and spacing. Both strict compilations had no diagnostics; the site applies completed with readback. These are separate validity, operational and visual observations, not a universal fidelity score.

No new helper, renderer, command or dependency was needed. A clean comparison ends after the paired inspection; it does not require a correction loop. Missing-reference, unavailable-browser and source-only cases remain explicitly unverified under the workflow, and do not block ordinary source delivery.

## Native editing and retained evidence

In Chrome 154, the page highlight paragraph was edited, saved and reopened in the native editor; the saved WordPress content and frontend both contained the test sentence. The closing-note template part passed the same edit/save/reopen and frontend check. Both original texts were then restored through the editor. The restored page also passed the existing validation gate.

- Desktop: [reference](evidence/reference-desktop-matched.png), [before](evidence/before-desktop-matched.png), [after](evidence/after-desktop-matched.png).
- Mobile: [reference](evidence/reference-mobile-matched.png), [before](evidence/before-mobile-matched.png), [after](evidence/after-mobile-matched.png).
- Native persistence: [page reopened](evidence/editor-reopened.png), [template part reopened](evidence/part-editor-reopened.png).

The paired after comparison was taken before the deliberate editor test edits. These captures show synthetic content only. The exercise was performed by one operator; independent reviewer replay remains separate.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions dev/test/fixtures/visual-review/reference.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Visual review reference</title><link rel="stylesheet" href="styles/base.css">
<article class="reference-article">
<h1>A quiet place to read</h1>
<p>A good reading page gives every sentence enough room. The words should feel unhurried, with a clear measure, familiar type and a useful rhythm between paragraphs. The design is modest on purpose: changes to its width are easy to see, but the content must remain exactly the same.</p>
<p>The article can be read on a large screen or on a phone. Neither reader should need to scroll sideways to finish a sentence. When a theme adds a narrower container, the extra wrapping can look like a content problem even though every word is still present.</p>
<aside class="reference-highlight"><p>Keep the words. Correct the layout. Check the result at both sizes.</p></aside>
<p>The highlighted note is part of the article, not a picture of some text. It should fit within the available width, stay in the reading order and remain editable through the normal paragraph control. A successful correction fixes the layout without rewriting the message.</p>
<p>After checking the page, inspect the shared closing note as a separate region. It has a different purpose and a wider measure. Matching these two regions by meaning makes the comparison useful even when the reference and WordPress use different element trees.</p>
</article>
<footer class="reference-note"><p>This is a shared closing note. Its text remains a native paragraph.</p></footer>
</html>
136 changes: 136 additions & 0 deletions dev/test/fixtures/visual-review/site.intent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
{
"version": 1,
"siteId": "visual-review-demo",
"homePageId": "home",
"theme": {
"slug": "visual-review-demo",
"title": "Visual Review Demo",
"themeJson": {
"version": 3,
"settings": {
"appearanceTools": true
},
"styles": {
"spacing": {
"blockGap": "0"
},
"typography": {
"fontFamily": "Georgia, serif",
"fontSize": "18px",
"lineHeight": "1.6"
},
"color": {
"background": "#f6f4ee",
"text": "#252c28"
}
}
}
},
"templates": {
"index": {
"body": {
"blocks": [
{
"block": "core/post-content"
},
{
"block": "core/template-part",
"attrs": {
"slug": "closing-note"
}
}
]
}
}
},
"parts": {
"closing-note": {
"body": {
"blocks": [
{
"block": "core/group",
"attrs": {
"className": "review-note"
},
"children": [
{
"block": "core/paragraph",
"text": "This is a shared closing note. Its text remains a native paragraph."
}
]
}
]
}
}
},
"patterns": {},
"pages": [
{
"id": "home",
"route": "/",
"title": "Reading page",
"template": "index",
"status": "publish",
"body": {
"blocks": [
{
"block": "core/group",
"attrs": {
"className": "review-article"
},
"children": [
{
"block": "core/heading",
"level": 1,
"text": "A quiet place to read"
},
{
"block": "core/paragraph",
"text": "A good reading page gives every sentence enough room. The words should feel unhurried, with a clear measure, familiar type and a useful rhythm between paragraphs. The design is modest on purpose: changes to its width are easy to see, but the content must remain exactly the same."
},
{
"block": "core/paragraph",
"text": "The article can be read on a large screen or on a phone. Neither reader should need to scroll sideways to finish a sentence. When a theme adds a narrower container, the extra wrapping can look like a content problem even though every word is still present."
},
{
"block": "core/group",
"attrs": {
"className": "review-highlight"
},
"children": [
{
"block": "core/paragraph",
"text": "Keep the words. Correct the layout. Check the result at both sizes."
}
]
},
{
"block": "core/paragraph",
"text": "The highlighted note is part of the article, not a picture of some text. It should fit within the available width, stay in the reading order and remain editable through the normal paragraph control. A successful correction fixes the layout without rewriting the message."
},
{
"block": "core/paragraph",
"text": "After checking the page, inspect the shared closing note as a separate region. It has a different purpose and a wider measure. Matching these two regions by meaning makes the comparison useful even when the reference and WordPress use different element trees."
}
]
}
]
}
}
],
"assets": [
{
"id": "base",
"path": "styles/base.css",
"kind": "stylesheet",
"role": "theme-file"
},
{
"id": "native",
"path": "styles/native.css",
"kind": "stylesheet",
"role": "theme-file"
}
],
"assetUses": []
}
14 changes: 14 additions & 0 deletions dev/test/fixtures/visual-review/styles/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* Shared design vocabulary; reference and native markup deliberately differ. */
body { margin: 0; background: #f6f4ee; color: #252c28; font: 18px/1.6 Georgia, serif; }
.wp-site-blocks { padding: 0; }
.reference-article, .review-article { box-sizing: border-box; width: calc(100% - 48px); max-width: 720px; margin: 48px auto; }
.reference-article h1, .review-article h1 { font: bold 44px/1.12 Georgia, serif; margin: 0 0 24px; }
.reference-article p, .review-article p { margin: 0 0 24px; }
.reference-highlight, .review-highlight { box-sizing: border-box; background: #e2e8dd; padding: 24px; border-left: 4px solid #596d4e; margin: 32px 0; }
.reference-highlight p, .review-highlight p { margin: 0; }
.reference-note, .review-note { box-sizing: border-box; width: calc(100% - 48px); max-width: 960px; margin: 48px auto; padding: 24px; background: #252c28; color: #f6f4ee; text-align: center; }
.reference-note p, .review-note p { margin: 0; }
@media (max-width: 600px) {
.reference-article, .review-article { margin-top: 24px; }
.reference-article h1, .review-article h1 { font-size: 34px; }
}
8 changes: 8 additions & 0 deletions dev/test/fixtures/visual-review/styles/native.corrected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* Scoped source correction, after the initial comparison identifies the defects. */
.review-article { max-width: 720px; }
.review-highlight { min-width: 0; }
.review-note { max-width: 960px; text-align: center; }

/* Keep the authored top spacing against WordPress first-child layout defaults. */
.wp-block-post-content > .review-article { margin-block-start: 48px; }
@media (max-width: 600px) { .wp-block-post-content > .review-article { margin-block-start: 24px; } }
4 changes: 4 additions & 0 deletions dev/test/fixtures/visual-review/styles/native.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* Three intentional presentation defects; native content stays identical. */
.review-article { max-width: 480px; }
.review-highlight { min-width: 520px; }
.review-note { max-width: 440px; text-align: left; }
11 changes: 10 additions & 1 deletion skills/block-runner/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ compatibility: Requires Node.js ^22.13.0 || >=24.0.0 and shell access. An uncach
## Pick the artifact first

Choose the visible result before discovering a project. Read the named first reference, use the
condition, and stop at its stated boundary. Do not load a construction taxonomy for a
condition, and respect its stated boundary. Do not load a construction taxonomy for a
self-contained page-content or markup-repair task.

| Requested artifact | First reference | Use when | Completion boundary |
Expand All @@ -36,6 +36,15 @@ If “reusable section” does not say whether it is page `post_content` or a na
block, ask that one artifact question before writing. Do not use this skill for unrelated WordPress
administration, non-WordPress HTML, or frontend-scraped HTML presented as source.

## Finish rendered delivery

When the task creates or changes visible WordPress output, follow
[VISUAL.md](references/VISUAL.md) by default before calling the delivery complete.
Check the affected region on desktop/mobile, correct meaningful differences and verify
the applicable native edit/save behaviour. Do not ask whether the user wants this check.
Respect source-only delivery and explicit skips; disclose unavailable rendering or
unverified checks. Existing CLI/API behaviour and write permissions remain unchanged.

## Discover only missing project facts

For a component that belongs in an existing project, use [GUIDE.md §0](references/GUIDE.md) after
Expand Down
4 changes: 4 additions & 0 deletions skills/block-runner/references/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ tool is broken.
**Always say what you did** — where it went, how many blocks, and anything that fell back to
Custom HTML. A silent success is indistinguishable from a silent failure.

For a rendered delivery, follow [VISUAL.md](VISUAL.md) by default: compare desktop/mobile,
correct meaningful differences and check native editing/save. Source-only delivery still
finishes normally; say when rendering or persistence has not been verified.

---

## 7. Matching the user's site
Expand Down
4 changes: 4 additions & 0 deletions skills/block-runner/references/SITE.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,7 @@ theme proof checks the theme's declared PHP requirement. Both routes require the
optional pinned WordPress/browser tooling. They pin the captured theme snapshot
automatically, so a separate plugin-style `--input` is unnecessary. Save proof and
visual/editor acceptance are separate claims.

When delivering a rendered site or theme change, use [VISUAL.md](VISUAL.md) by default
to compare the affected views, correct source-level differences and retain the observed
limits. The proof commands above do not replace that comparison or authorise production writes.
84 changes: 84 additions & 0 deletions skills/block-runner/references/VISUAL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Finish rendered output

Use this after creating or changing a rendered WordPress page, section, template/part,
site or integrated block. It is the default delivery check: do not wait for a separate
QA request. Existing CLI/library calls remain headless and unchanged.

Check the changed region and surrounding layout on desktop and mobile. Reuse the
current browser session and evidence that still matches the delivered revision.
For a site, cover its different templates and responsive behaviour; do not revisit
every unrelated page for a small edit. A clean first comparison needs no correction pass.

## Establish the comparison

Record the authored input/revision, native destination, intended regions and any
approved design differences. Use the authored design's render or supplied screenshots
as the reference. Rendered HTML is visual evidence, never a substitute for authored
converter input.

Match viewport, scale and UI state between reference and target. Choose desktop/mobile
widths appropriate to the design; add a breakpoint-adjacent view when a defect needs it.
Wait for fonts and images with a bounded timeout, and disclose missing resources.
Freeze animation consistently for static comparison; check its behaviour separately
when it is part of the requested result. Retain the actual browser/version and views.

Without a visual reference, inspect the render against the stated content/layout
requirements and check overflow, missing assets and obvious presentation failures.
Say that source fidelity was not compared. An inaccessible target/browser, missing
required reference or explicit user skip is an unverified check, never a visual pass.
Source-only or batch work without a rendered destination still delivers normally.
This check grants no deployment, credential or production-write authority.

## Compare, explain, correct

1. **Check content first:** section order, headings, body text, media, links and required
interactions, including fragment-link destinations and drawn/animated graphics.
A similar silhouette cannot compensate for lost content. A present canvas element
does not prove that its artwork rendered.
2. **Compare corresponding regions:** widths/alignment, spacing, typography/wrapping,
image crop and mobile overflow or clipping. `overflow: hidden` can conceal broken
layouts while document width still looks correct. Confirm breakpoint rules still
affect the native regions. Match regions by meaning; source and native DOM
trees need not share selectors. Inspect the screenshots, not just capture success.
3. **Measure a discrepancy:** use the browser's bounding boxes/computed styles for
the affected region. Record both selectors or region labels. Missing/ambiguous
regions block that comparison; screenshot-only references have no DOM measurements.
Do not collect every CSS property or require DOM/style equality. A pixel diff can
locate differences; it does not establish fidelity or editorial quality.
4. **Fix the responsible input:** identify the authored selector, intent node path or
theme rule. Keep uncertain attribution explicit. Prioritise missing content,
broken interaction and overflow before decorative differences. Preserve unrelated
copy and native editability; never hand-patch generated block delimiters.
5. **Regenerate and recapture:** use the existing conversion/assembly and delivery
route, including its gate, confirmation and drift checks. Preserve human edits.
Compare the same views again, keeping the original capture. Stop when the result
meets the brief or a real blocker/task budget is reached, not after an arbitrary
number of passes. Keep unresolved differences and editorial TODOs visible.

A short working list is enough; it is not a required formal handoff:

| Region / viewport | Expected → observed, evidence | Impact / source to inspect | Action / result |
| --- | --- | --- | --- |
| Article / desktop | Reference 720px → native 480px; paired capture and width | Extra wrapping; theme content width | Correct width, regenerate; resolved in recapture |

Finish with the applicable native editor check: make a representative edit through
its actual control, save and reopen, and inspect the rendered result. Keep permission
to mutate the target and preservation of existing content explicit. If this cannot
be exercised, record persistence as unverified rather than deriving it from a screenshot.

## Reuse the existing facilities

Use the harness's available browser controls for paired inspection and targeted
measurements. The project's existing proof runners retain screenshots, errors and
receipts and exercise native editing. `proof --theme` / `proof --site` use controlled
sandboxes and may write fixture state: see [SITE.md](SITE.md#prove-the-right-result).
They are not read-only capture commands for arbitrary sites.

Registered-block `ProofFixture.visual` compares a render to a reviewed golden. That
regression gate retains its own thresholds/masks; do not replace the golden to hide
a source-to-native mismatch. Reuse captured evidence, without treating a successful
gate as proof of a comparison it did not perform.

Tell the user what was checked, what was corrected, and what remains. Distinguish
block validity, visual observations, interaction and save persistence. No universal
fidelity score, new CLI mode or separate report package is required.
Loading