Skip to content

fix(php-transformer): replace listing cards with a native Query Loop - #2247

Merged
chubes4 merged 4 commits into
trunkfrom
fix/1245-live-listing
Sep 27, 2026
Merged

chubes4 merged 4 commits into
trunkfrom
fix/1245-live-listing

Conversation

@chubes4

@chubes4 chubes4 commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Problem

#2221 materialized listed dated articles as Posts with day-level dates and /blog/<slug>/ permalinks, but kept the listing page as captured static markup. A newly published post never appeared on /blog/.

The first pass of this PR replaced listing cards with a Query Loop, but each Post's post_content was still the full captured article page (title, date/comments, body, share, sidebar). The loop then re-rendered that chrome once per card: sidebar 9×, 17 titles, document height 41067 vs baseline 33931 / source 37609.

Design

Post-shaped routes split into template-owned chrome and body.

  1. Shared article chrome — title, date/comments meta, share row, and the sidebar column — moves into the single template:
    • title → core/post-title
    • date → core/post-date in the source format
    • sidebar stays in the single template; a month list that matches the posts becomes core/archives
      Identity uses ShellExtraction::identityMarkup() (the same nested-chrome identity as header/footer shells). Extraction runs only when two or more posts share a title plus a date or identity-shared subtree, and leaves a non-empty body. Comment/share chrome is detected on the current block, not descendant text.
  2. post_content is the article body only, so the listing Query Loop's core/post-content renders that body once per post. The listing page keeps its own sidebar once.
  3. Unmatched listing cards (no captured post route, e.g. "It has been a while..." 4/8/2019) stay as static markup before the loop. They are not materialized from the card; there is no article page to own as a Post.

Query / post-template / post wrappers still use display: contents so captured card geometry participates in the parent layout.

No importer change.

Fix

After shared shells and the navigation entity:

  • Extract repeated post chrome into the generated single template and rewrite each post to body-only markup.
  • Then replace sibling listing cards with a core/query (postType: post, inherit: false, order: date desc, perPage = captured card count) whose post-template reuses one card (title, date, comments, post-content).
  • Convert a matching month list to core/archives.

Tests

Contract fixture in php-transformer/tests/contract/wordpress-site-plan.php (nested header chrome, comments form, share, sidebar, archives, orphan card):

  • Fails on trunk: listing stays frozen cards (<!-- wp:query absent).
  • Fails with listing-only HEAD: Query Loop present, but post content still includes title/date/sidebar (Post content is the article body only assertion).
  • Passes on this branch: listing stays a page; cards become one Query Loop; post content is body only; single template owns post-title, post-date, sidebar/core/archives; unmatched card stays static before the loop; "Leave a Reply" stays in the template, not in post_content.

composer test and php tests/contract/production-acceptance-matrix.php passed locally. Visual parity tool tests passed.

Fresh import evidence

SSI dev package from origin/main (c90b293a3e1e) with --blocks-engine-path this branch.

  • Provenance SHA: 41fd0307cc34
  • Capture: adventuringankara.weebly.com website artifact
  • quality_pass: true, fallback_blocks: 0, invalid_blocks: 0
  • Editor round-trip: serialize_blocks(parse_blocks(content)) matches on /blog/ and a post
  • Header navigation uses the shared core/navigation ref (wp_navigation entity from fix(php-transformer): emit a shared navigation entity referenced by token #2248)
  • New post "Loop test" appears first in the Query Loop (ahead of "Once in a month..."); its single view has post-title, post-date (n/j/Y), Author/Archives sidebar, and body

Playwright at 390/768/1440 (visible titles / .blog-sidebar count / document height):

page width imported baseline source
/blog/ 390 9 / 0* / 53919 9 / 1 / 29740 9 / 1 / 38726
/blog/ 768 9 / 0* / 68046 9 / 1 / 34946 9 / 1 / 28706
/blog/ 1440 9 / 1† / 75821 9 / 1 / 33931 9 / 1 / 32349
merhaba 390 1 / 1 / 2344 1 / 1 / 2129 1 / 1 / 2112
merhaba 768 1 / 1 / 2087 1 / 1 / 1872 1 / 1 / 1716
merhaba 1440 1 / 1 / 1749 1 / 1 / 1474 1 / 1 / 1618

* Listing markup contains one Author/Archives sidebar; the article column fills the row so the column box itself can report height 0 while the inner sidebar still renders once (below the cards at 1440). Title count is 9 (8 loop posts + the unmatched static card), down from 17.

† Inner .blog-sidebar-separator is visible once.

Single-post title, meta, body, and sidebar placement match the baseline. Listing is live; card height still exceeds the frozen-card baseline because loop bodies include full article media.

Remaining on #1245

  • Listing visual parity: article-column width so the listing sidebar sits beside the cards like the source.
  • core/categories when the captured list matches WordPress terms (this source is only "All").
  • Comments as core/post-comments-link once comments are imported.

Refs #1245

AI assistance

Written by Claude (Anthropic) via Claude Code, orchestrated by chubes4. The model split post-shaped routes into template-owned chrome and body-only post_content using ShellExtraction identity, kept unmatched listing cards static, added regression coverage (fails on trunk / listing-only HEAD, passes on this branch), rebuilt an SSI dev package, and verified a fresh import plus a newly published post. Chris Huber directed the work and remains responsible for the submitted change.

@chubes4

chubes4 commented Sep 26, 2026

Copy link
Copy Markdown
Contributor Author

Verified on a fresh import (SSI dev package, BE 1aecc40ac). The Query Loop works (a new post appears first), but the listing regresses at 1440:

  • The sidebar renders 9 times (source: 1): Author / Archives / Categories / RSS repeat inside every post card.
  • 17 post titles visible instead of 9: each card shows the template's post-title and then the post's own captured title again.
  • Document height 41,067 vs 33,931 (baseline) / 37,609 (source).

Root cause: each Post's post_content is still the full captured article page (title block, meta row, body, share row, and the blog sidebar), so core/post-content inside the loop re-renders all of it. The listing needs the Posts themselves to own only the article body; title/date/sidebar belong in the single template (and the sidebar in a shared part, like the header). Suggest splitting that out first: extract the repeated article chrome (title, meta, sidebar) from post-shaped routes into the single-post template/parts, so post_content is the body only, then the loop in this PR renders like the source.

Review by Claude (Anthropic) via Claude Code, orchestrated by @chubes4: AI imported the branch, measured the listing with Playwright, and drafted these notes.

Listed dated articles already materialize as posts. The listing page
kept frozen cards, so a newly published post never appeared.

When repeated listing cards share one structure, replace that run with
a core/query whose post-template reuses one captured card (title, date,
comments, content) and keep non-card chrome. Convert a month list that
matches the posts to core/archives.

Refs #1245
…rome

Weebly-style cards wrap title, date, and comments in a header group, so
a direct-child rewrite never found a heading. Recurse into wrappers,
replace leaf title/date/content, and drop share chrome.

Refs #1245
Post-shaped routes kept title, date, share, and sidebar in post_content,
so a listing Query Loop re-rendered that chrome once per card.

Reuse ShellExtraction identity to find chrome shared across posts, put
title/date in the single template as post-title/post-date, keep sidebar
(and matching archives) on the template, and leave only the article body
in post_content. Unmatched listing cards stay static before the loop.

Refs #1245
A document that contains a comments form matched Leave a Reply at the
root, so shared article chrome never split. Detect comment and share
chrome from the current block, not descendant text.

Refs #1245
@chubes4
chubes4 force-pushed the fix/1245-live-listing branch from 1aecc40 to 41fd030 Compare September 26, 2026 19:56
@chubes4

chubes4 commented Sep 26, 2026

Copy link
Copy Markdown
Contributor Author

Re-verified the rework on a fresh import (BE 41fd0307c). Titles are now correct (9), and single posts match, but the listing is still a regression vs the captured listing at 1440:

  • Sidebar gone visually: .blog-sidebar renders at left 0, height 0; the loop column stretches to 954px (source/baseline: 735px article column + 217px sidebar at x=981).
  • Height 75,560 vs 33,931 baseline / 37,609 source: post bodies render full content with galleries/images stacked full-width (52 images >600px wide vs 23).
  • Text 13,274 vs 14,550.

The card template needs to reproduce the listing's two-column row (article column + sidebar, the layout-table track widths from #2211/#2213) around the loop, and the source listing's per-card body (the source shows each post's intro + lead media, not the full article with every gallery). Converting this to draft until the listing reaches parity; the post/single split in this branch looks worth keeping as its own PR.

Review by Claude (Anthropic) via Claude Code, orchestrated by @chubes4: AI imported the branch, measured the listing with Playwright, and drafted these notes.

@chubes4
chubes4 marked this pull request as draft September 26, 2026 20:06
@chubes4
chubes4 marked this pull request as ready for review September 27, 2026 14:07
@chubes4
chubes4 merged commit 9416547 into trunk Sep 27, 2026
10 checks passed
@chubes4
chubes4 deleted the fix/1245-live-listing branch September 27, 2026 14:07
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