Shell extraction ignores a third capture device document (tablet), so no shared header/footer parts are extracted
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 74/100
Research direction
Start with ShellExtraction::isResponsiveVariantClass() and CaptureEvidence::DEVICE_DOCUMENT; the issue identifies both the recognition rule and the capture contract to follow. Add a contract test based on tests/contract/capture-variant-plain-page-shell.php that includes desktop, mobile, and tablet documents across three routes. Done means shared header and footer parts include the tablet variant, pages have no inline header or footer, and no shell_retained_* diagnostic appears; check the three existing contracts named in the issue too.
Written by the indexing model from the issue text.
Description
A third capture device document disables shared header/footer extraction
Data Liberation now emits an additional device document for dual routes (data-blocks-engine-capture-tablet-document, alongside the desktop and mobile documents) through the existing capture contract: CaptureEvidence::DEVICE_DOCUMENT allows any profile id ([a-z][a-z0-9_-]{0,63}). Shell extraction recognizes only the two hardcoded document classes:
// ShellExtraction::isResponsiveVariantClass()
return in_array($class, array(CaptureEvidence::DESKTOP_DOCUMENT_CLASS, CaptureEvidence::MOBILE_DOCUMENT_CLASS), true)
|| 1 === preg_match('/^site-document-variant-[a-z][a-z0-9_-]{0,31}$/', $class);
So the tablet copy of each landmark has no responsive variant class, scopedVariantLandmarks() returns null for every route, absorbResponsiveVariantLandmarks() returns false, and both areas end in wordpress_site_plan_shell_retained_incomplete ("shell candidates do not establish a dominant semantic cluster"). Every page keeps its own inline header and footer instead of binding shared template parts.
Observed
A 4fires.org (Wix) import with SSI 1.33.366 / DLA 0.33.136: 41 pages, all with desktop + mobile + tablet documents, produced no parts/ and the two shell_retained_incomplete diagnostics. Probing the materialized pages: on every route the desktop and mobile SITE_HEADER / SITE_FOOTER landmarks resolve to their variant classes and the SITE_HEADER--data-blocks-engine-capture-tablet copy resolves to null.
Reduced reproduction (trunk 15317e2)
Three routes, identical chrome, block markup injected per route the way tests/contract/capture-variant-plain-page-shell.php does. The only difference between the two runs is the third document class:
desktop+mobile parts=["footer","header"] pages-with-inline-header=0 retained=[]
desktop+mobile+tablet parts=["footer"] pages-with-inline-header=3 retained=["wordpress_site_plan_shell_retained_incomplete footer","wordpress_site_plan_shell_retained_incomplete header"]
Repro script
declare(strict_types=1);
require getenv('BE') . '/php-transformer/vendor/autoload.php';
use Automattic\BlocksEngine\PhpTransformer\ArtifactCompiler\ArtifactCompiler;
use Automattic\BlocksEngine\PhpTransformer\Contract\CaptureEvidence;
use Automattic\BlocksEngine\PhpTransformer\WordPressSitePlan\WordPressSitePlan;
// Three routes, each captured as three device documents: desktop, mobile, and
// an additional profile (tablet) declared through the same capture contract.
$tablet = 'data-blocks-engine-capture-tablet-document';
$navigation = '<!-- wp:navigation {"overlayMenu":"never"} --><!-- wp:navigation-link {"label":"Home","url":"/","kind":"custom"} /--><!-- wp:navigation-link {"label":"About","url":"/about/","kind":"custom"} /--><!-- /wp:navigation -->';
$header = static fn(string $class): string => '<!-- wp:group {"className":"' . $class . '","tagName":"header"} --><header class="wp-block-group ' . $class . '"><!-- wp:paragraph --><p><a href="/">Four Fires</a></p><!-- /wp:paragraph -->' . $navigation . '</header><!-- /wp:group -->';
$footer = static fn(string $class): string => '<!-- wp:group {"className":"' . $class . '","tagName":"footer"} --><footer class="wp-block-group ' . $class . '"><!-- wp:paragraph --><p>Four Fires Ministries</p><!-- /wp:paragraph --></footer><!-- /wp:group -->';
$main = static fn(string $title): string => '<!-- wp:group {"tagName":"main"} --><main class="wp-block-group"><!-- wp:heading --><h2 class="wp-block-heading">' . $title . '</h2><!-- /wp:heading --></main><!-- /wp:group -->';
$document = static fn(string $class, string $suffix, string $title): string => '<!-- wp:group {"className":"' . $class . '"} --><div class="wp-block-group ' . $class . '">' . $header('site-header' . $suffix) . $main($title) . $footer('site-footer' . $suffix) . '</div><!-- /wp:group -->';
$capture = static fn(string $title, array $classes): string => implode('', array_map(static fn(string $class, int $i): string => $document($class, 0 === $i ? '' : ' compact', $title), $classes, array_keys($classes)));
foreach (array('desktop+mobile' => array(CaptureEvidence::DESKTOP_DOCUMENT_CLASS, CaptureEvidence::MOBILE_DOCUMENT_CLASS), 'desktop+mobile+tablet' => array(CaptureEvidence::DESKTOP_DOCUMENT_CLASS, CaptureEvidence::MOBILE_DOCUMENT_CLASS, $tablet)) as $label => $classes) {
$result = (new ArtifactCompiler())->compile(array('entrypoint' => 'index.html', 'files' => array(
'index.html' => '<main><h1>Home</h1></main>',
'about/index.html' => '<main><h1>About</h1></main>',
'contact/index.html' => '<main><h1>Contact</h1></main>',
)))->toArray();
foreach ($result['source_reports']['compiled_site']['pages'] as &$page) $page['block_markup'] = $capture(ucfirst(basename(dirname('/' . $page['source_path'])) ?: 'Home'), $classes);
unset($page);
$plan = (new WordPressSitePlan())->fromResult($result);
$areas = array_column($plan['template_parts'], 'area');
$retained = array_values(array_filter(array_map(static fn(array $d): string => ($d['code'] ?? '') . ' ' . ($d['area'] ?? ''), $plan['diagnostics']), static fn(string $c): bool => str_contains($c, 'shell_retained')));
$inline = count(array_filter($plan['pages'], static fn(array $p): bool => str_contains((string) $p['canonical_block_markup'], 'site-header')));
echo str_pad($label, 24), 'parts=', json_encode($areas), ' pages-with-inline-header=', $inline, ' retained=', json_encode($retained), "\n";
if (str_contains($label, 'tablet')) {
$header = array_values(array_filter($plan['template_parts'], static fn(array $p): bool => 'header' === $p['area']))[0]['canonical_block_markup'] ?? '';
echo ' tablet document in shared header part: ', var_export(str_contains($header, $tablet), true), "\n";
}
}
Expected
Any capture device document class (data-blocks-engine-capture-<profile>-document, profile per the DEVICE_DOCUMENT grammar) is a responsive document variant for shell extraction, the same as desktop and mobile. With three documents the routes bind one shared header part and one shared footer part that render all three variants, and no page keeps an inline copy, matching the two-document result.
Acceptance
- A contract test (three routes × desktop/mobile/tablet documents) fails on trunk and passes on the fix: shared
headerandfooterparts exist, each holds the tablet variant, no page retainssite-header/site-footer, and noshell_retained_*diagnostic is emitted. - The existing two-document contracts (
capture-variant-plain-page-shell.php,unlabeled-capture-device-shell.php,viewport-hidden-document-variants.php) still pass. - Recognition derives from the capture contract (one helper on
CaptureEvidencefor "is a device document class") rather than adding a third hardcoded constant.
AI disclosure: filed by Franklin (Claude Opus via Pi/Roadie). Root cause found by probing the 4fires import's materialized pages with ShellExtraction on trunk; the reduced repro was run on trunk.
- Dominant language
- PHP
- Stars
- 25
- Forks
- 3
- Avg merge
- 3h 25m
- Merged PRs (30d)
- 935
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Automattic/blocks-engine
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/blocks-engine#4621 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Automattic/blocks-engine#775 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
Automattic/blocks-engine#4637 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Automattic/blocks-engine#4630 ·
Maintainers usually reply within 1 day
-
Navigation overflow trigger chevron renders below the label instead of inlinePossibly taken @chubes4 claimed this 1 day ago. Open
Difficulty 3/5 1-2 days Newbie friendliness 35/100
Automattic/blocks-engine#4629 ·
Maintainers usually reply within 1 day
All issues in Automattic/blocks-engine
Similar issues
-
Visual COBOL: missing 11.0 and 12.0, and 9.0 date differs from Rocket Software's lifecycle pageOpen
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
endoflife-date/endoflife.date#11561 ·
Maintainers usually reply within 1 day
-
good first issue priority:low type:bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
crazy-goat/fdb-php#184 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
nextcloud/groupfolders#5218 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
nextcloud/sharelisting#624 ·
-
`wp theme mod set` prints its failure message as `Success` and exits 0Possibly taken @dhrupo claimed this today. Openbug command:theme-mod-set status:unconfirmed
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
wp-cli/extension-command#570 ·