Skip to content

Render prepared widget HTML inside the layout and prepare it before the public guard - #863

Merged
howdu merged 6 commits into
mainfrom
fix/render-partially-hydrated-layouts
Oct 16, 2026
Merged

howdu merged 6 commits into
mainfrom
fix/render-partially-hydrated-layouts

Conversation

@howdu

@howdu howdu commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

Problem

BladeFrontendResponseRenderer used the hydrated layout-graph shortcut as soon as any widget carried prepared section HTML, so <x-capell::layout/> never ran. On layouts without an authored master file, public pages lost their container presentation (e.g. meta.html_class) and every widget that was not pre-rendered. Reproduced on a fresh App install: the free Agency theme rendered without its ppc-shell container and 12 of 13 widgets; Core's default layout lost its sidebar container and widgets. Any content-sections or contributor that hydrates one widget triggers it.

Change

The shortcut is taken only when every graph widget has non-empty section HTML and no container carries presentation metadata; otherwise the normal layout renderer runs. Fully hydrated graphs (e.g. the demo kit) keep identical output. The authored-master guard (#847) and master-file path are unchanged.

Tests

  • Nine new cases in BladeFrontendResponseRendererTest: mixed hydrated/unhydrated layout keeps container class and all widgets; container metadata forces the layout path; fully hydrated graph keeps the shortcut and section order; neutral demo-kit metadata stays shortcut-compatible; unsafe fallback hook output rejected. Six fail on main, all 24 pass here.
  • PHPStan (diff) and Pint clean.

Not verified

  • Local broad packages/frontend run: 2 RequiredChildCommandsTest errors reproduce on the original renderer (SQLite settings table re-create); 2 stylesheet assertions failed only in the broad local run and pass in isolation. Hosted matrix is the gate.
  • Installed App re-run of the Agency journey scripts (follows with the App scoping fix).

@howdu
howdu marked this pull request as draft October 15, 2026 17:28
@howdu

howdu commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

Parked (draft). Review found two P1 issues with falling back to the layout path: hydrated widgets' prepared HTML is not carried into the layout render (content loss on no-master layouts using prepared sections), and the fallback can query inside the public view guard (lazy fragment version lookups). A correct fix renders prepared HTML per widget inside the layout path and prepares fragment references before the guard; that is cross-repository design work, deferred past this release. The Agency trigger is fixed in the App by scoping the marketing payload contributor and resource policy to the marketing master.

howdu added 2 commits October 16, 2026 09:08
… the guard

Partly hydrated graphs now render through the layout with each widget's
prepared section HTML, looked up by container, widget and occurrence, and
fall back to the normal widget renderer only where none was prepared. An
optional PublicLayoutRenderPreparer runs before the public query guard so
layout renderers can load what they need up front. Fully hydrated and fully
unhydrated output is unchanged.
@howdu howdu changed the title Render the layout when a prepared page graph is only partly hydrated Render prepared widget HTML inside the layout and prepare it before the public guard Oct 16, 2026
@howdu
howdu marked this pull request as ready for review October 16, 2026 09:45
howdu added 3 commits October 16, 2026 11:38
Passing the render data to the layout component as an attribute handed the
object to existing anonymous theme layouts that do not declare it, which
broke $attributes->merge(). The renderer now sets a PublicRenderDataScope
around the guarded render, restoring the previous value on exit, and layout
views read it from there.
A container binding would add a surface to every package's installed-runtime
baseline. The scope is now static with the same set-and-restore around each
render, so no state outlives a render.
@howdu

howdu commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator Author

Merge evidence. Hosted CI is green on 7bbbb69: all 28 jobs passed, including the full Test All matrix on MySQL, MariaDB, PostgreSQL and SQLite, Sentinel, PHP Quality and the three Fast PR gate shards.

  • Joint review round 1 (with Packages #1571): the original two P1s (lost prepared HTML; queries inside the public guard) are fixed on standard paths, including real render-cache hits, and byte parity with main holds. It found P1: render data passed as a component attribute broke anonymous theme layouts. Fixed: the data travels in a static PublicRenderDataScope, set and restored around the guarded render. It has no container binding, so no package baselines change, and it is declared in the Octane static-state inventory.
  • Round 2 confirmed P1 and P2 fixed; its remaining finding was on the Packages side (render lifetime) and is fixed in #1571.
  • Ships together with capell-app/capell-packages#1571; Packages floors already require ^1.0.72.

@howdu
howdu merged commit 087a3c8 into main Oct 16, 2026
29 checks passed
@howdu
howdu deleted the fix/render-partially-hydrated-layouts branch October 16, 2026 13:03
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