Repository navigation
refactor: neutral capture vocabulary for Blocks Engine capture evidence - #2725
Merged
Merged
Conversation
…neutral vocabulary Publish the capture-evidence input contract as Contract\CaptureEvidence and docs/contracts/capture-evidence.md. Every attribute, class, id suffix, probe token, sidecar schema and recognized runtime identity the transformer reads or emits now comes from that vocabulary under data-blocks-engine-capture-* and blocks-engine-capture/*; the producer-specific names are no longer read. Add the equivalence gate (344 harvested pre-move entry-point calls replayed byte-identically), the vocabulary completeness contract and the producer-neutral source guard.
…y block neutrally Rename the reference-implementation parity goldens, corpora, tests and golden capture scripts away from producer-specific names, emit the scroll-state block as blocks-engine/sticky, and guard the package source and scripts against producer names.
…me script contract Publish the device-selection, device-body, selected-document, selected-viewport, document-unavailable, device-style and member-login names that importers read, flagged as downstream-only so the vocabulary contract keeps every consumed name in use. Document how producers must emit the disclosure and gallery runtime scripts and how Blocks Engine verifies them.
chubes4
added a commit
to Automattic/data-liberation-agent
that referenced
this pull request
Oct 13, 2026
…-findings.json (#787) Capture now emits the producer-neutral capture-evidence vocabulary Blocks Engine owns, and nothing else: attributes, classes, ids and probe tokens use data-blocks-engine-capture-<name>, id suffixes --data-blocks-engine-capture-<variant>, and sidecar schemas blocks-engine-capture/<name>/v<n>. Emitters, runtime scripts, stylesheets, live-page dataset keys and interaction-state selectors are renamed at the source; the dual-emission pass is removed. src/lib/capture-vocabulary.ts is the one table of names, schemas and recognized runtime digests, kept identical to the source by test. The disclosure interpreter body hashes to Blocks Engine's published digest. Capture writes website/source-findings.json (static-site-importer/source-findings/v1) and no longer writes website/capture-findings.json. Lockstep with Automattic/blocks-engine#2725 and Automattic/static-site-importer#2062 (#787 option A).
chubes4
added a commit
to Automattic/data-liberation-agent
that referenced
this pull request
Oct 13, 2026
…-findings.json (#787) (#788) * feat(export): write producer-neutral capture names and source-findings.json (#787 step 1) Export mirrors every data-dla-*, data-liberation-* and dla-* capture name that reaches website/ HTML or interaction-states.json under the Blocks Engine capture namespace (data-blocks-engine-capture-*, blocks-engine-capture-*) with identical values, in one source-located pass that leaves every other byte unchanged. Capture also writes website/source-findings.json in Static Site Importer's neutral schema (static-site-importer/source-findings/v1) beside capture-findings.json, from the same rows. DLA now owns its code -> loss_class mapping (SOURCE_FINDING_LOSS_CLASSES). Refs #787, part of #761. * refactor!: write only the Blocks Engine capture vocabulary and source-findings.json (#787) Capture now emits the producer-neutral capture-evidence vocabulary Blocks Engine owns, and nothing else: attributes, classes, ids and probe tokens use data-blocks-engine-capture-<name>, id suffixes --data-blocks-engine-capture-<variant>, and sidecar schemas blocks-engine-capture/<name>/v<n>. Emitters, runtime scripts, stylesheets, live-page dataset keys and interaction-state selectors are renamed at the source; the dual-emission pass is removed. src/lib/capture-vocabulary.ts is the one table of names, schemas and recognized runtime digests, kept identical to the source by test. The disclosure interpreter body hashes to Blocks Engine's published digest. Capture writes website/source-findings.json (static-site-importer/source-findings/v1) and no longer writes website/capture-findings.json. Lockstep with Automattic/blocks-engine#2725 and Automattic/static-site-importer#2062 (#787 option A). * fix(capture): rename the dialog changes from main and re-pin the disclosure runtime digest
This was referenced Oct 13, 2026
This was referenced Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs Automattic/data-liberation-agent#787 (part of Automattic/data-liberation-agent#761).
Important
LOCKSTEP: merge together with Automattic/data-liberation-agent#788 and Automattic/static-site-importer#2062 (data-liberation-agent#787 option A).
All three switch to the new names only. No repository has compatibility code. Blocks Engine reads only the names below.
What changes
Blocks Engine no longer knows any specific capture producer. It owns a producer-neutral capture-evidence input contract that any capture tool can emit:
php-transformer/src/Contract/CaptureEvidence.php: one constants class with all 63 names plus the 2 recognized runtime digests. Every reader and emitter insrc/uses it. Literals inside embedded JS modules are filled from the constants through the existing__PLACEHOLDER__substitution, and regexes and XPath queries are built from them.php-transformer/docs/contracts/capture-evidence.md: every name with its kind, value grammar and meaning.NativeControlState::ATTRIBUTE,CapturedGlyphColor::ATTRIBUTEandDocumentVariantIds::{MOBILE,DESKTOP}_DOCUMENT_CLASSare deleted. Callers useCaptureEvidencedirectly. No downstream code references the aliases.packages/blocks-engine: the parity goldens, corpora, tests and golden-capture scripts get neutral names (*-dla-goldens.json→*-reference-goldens.json,DLA_*→REFERENCE_*). The scripts now requireREFERENCE_ROOTand no longer fall back to a hardcoded path. The emitted scroll-state blockdla/stickybecomesblocks-engine/sticky; no repository registers either name.Mapping rule, and one deliberate deviation
data-dla-<name>data-blocks-engine-capture-<name>data-liberation-<name>data-blocks-engine-capture-<name>data-liberation(sidecar schemas)blocks-engine-capture(for exampleblocks-engine-capture/capture-receipt/v1)dla-<name>, including the--dla-<variant>id suffixdata-blocks-engine-capture-<name>Deviation from #787's class rule (
dla-<name>→blocks-engine-capture-<name>). I ran the semantic comparison below against unmodified trunk, and it showed that the literal rule loses behavior. About 20 places in conversion treat anyblocks-engine-*class as an engine-generated class: they strip it from source identity, skip it for naming, and don't project its styles. Classes such asblocks-engine-capture-dropdownwere therefore no longer handled as source classes. In the comparison,dropdown/dialog/disclosurewrappers lost a block level and component names changed. With every capture class under the attribute prefix, conversion treats them as source classes and that loss goes away. This puts the whole vocabulary under one prefix,data-blocks-engine-capture-, with schemas underblocks-engine-capture/. The producer side of #787 should use this rule.Values and semantics are unchanged. The two runtime-identity digests do change, because they hash script bodies that contain the names.
CaptureEvidence::*_RUNTIME_SHA256is the digest of the renamed body, and both bodies are published astests/fixtures/capture-{disclosure,gallery}-runtime.js.Published for downstream importers. Blocks Engine does not read these names, but it publishes them in
CaptureEvidenceso Static Site Importer and producers share one spelling. They are flagged inCaptureEvidence::PUBLISHED_FOR_DOWNSTREAM, and the doc gives them their own section. The vocabulary contract allows flagged names to go unused. It fails if a flagged name is ever read bysrc/, or if an unflagged name is unused.data-dla-device-selectiondata-blocks-engine-capture-device-selection<script>)data-dla-device-bodydata-blocks-engine-capture-device-body<script>)data-dla-selected-documentdata-blocks-engine-capture-selected-document<html>data-dla-selected-viewportdata-blocks-engine-capture-selected-viewportdata-dla-document-unavailabledata-blocks-engine-capture-document-unavailable<html>)data-dla-device-styledata-blocks-engine-capture-device-style<style>/<link>data-dla-member-logindata-blocks-engine-capture-member-logindla-member-login-<provider>data-blocks-engine-capture-member-login-<provider>Runtime scripts. The doc's "Runtime scripts" section says producers must emit the disclosure and gallery helpers exactly as published:
tests/fixtures/capture-disclosure-runtime.js, sha2563d350f55…;tests/fixtures/capture-gallery-runtime.js, sha2560a6bf62a….It also explains how
CapturedDialogProjectorchecks them: it comparessha256(trim(textContent))for each marked script, and declines native replacement on any mismatch or any other executable script.Full mapping
data-dla-data-blocks-engine-capture---dla---data-blocks-engine-capture-data-dla-document-scopedata-blocks-engine-capture-document-scopedata-dla-device-documentdata-blocks-engine-capture-device-documentdata-liberation-mobile-documentdata-blocks-engine-capture-mobile-documentdata-liberation-desktop-documentdata-blocks-engine-capture-desktop-documentdata-liberation-responsive-counterpart-data-blocks-engine-capture-responsive-counterpart-data-dla-responsive-sourcedata-blocks-engine-capture-responsive-sourcedata-dla-source-mediadata-blocks-engine-capture-source-mediadata-dla-dialog-triggerdata-blocks-engine-capture-dialog-triggerdata-dla-dialog-paneldata-blocks-engine-capture-dialog-paneldata-dla-dialog-closedata-blocks-engine-capture-dialog-closedata-dla-dialog-panel-statedata-blocks-engine-capture-dialog-panel-statedata-dla-dialog-ancestor-statedata-blocks-engine-capture-dialog-ancestor-statedata-dla-dialog-ancestor-unverifieddata-blocks-engine-capture-dialog-ancestor-unverifieddata-dla-observed-placementdata-blocks-engine-capture-observed-placementdla-dropdowndata-blocks-engine-capture-dropdowndla-dialogdata-blocks-engine-capture-dialogdla-disclosuredata-blocks-engine-capture-disclosuredata-dla-disclosure-runtimedata-blocks-engine-capture-disclosure-runtimedata-dla-disclosure-labeldata-blocks-engine-capture-disclosure-labeldata-dla-disclosure-open-data-blocks-engine-capture-disclosure-open-data-dla-disclosure-closed-data-blocks-engine-capture-disclosure-closed-data-dla-local-disclosuredata-blocks-engine-capture-local-disclosuredata-dla-local-disclosure-runtimedata-blocks-engine-capture-local-disclosure-runtimedata-dla-exclusive-disclosuresdata-blocks-engine-capture-exclusive-disclosuresdata-dla-listbox-triggerdata-blocks-engine-capture-listbox-triggerdata-dla-listbox-paneldata-blocks-engine-capture-listbox-paneldata-dla-gallerydata-blocks-engine-capture-gallerydata-dla-gallery-sequencedata-blocks-engine-capture-gallery-sequencedata-dla-gallery-slotdata-blocks-engine-capture-gallery-slotdata-dla-gallery-stagedata-blocks-engine-capture-gallery-stagedata-dla-gallery-initialdata-blocks-engine-capture-gallery-initialdata-dla-gallery-displaydata-blocks-engine-capture-gallery-displaydata-dla-gallery-directiondata-blocks-engine-capture-gallery-directiondata-dla-gallery-indexdata-blocks-engine-capture-gallery-indexdata-dla-gallery-sourcedata-blocks-engine-capture-gallery-sourcedata-dla-gallery-capture-widthdata-blocks-engine-capture-gallery-capture-widthdata-dla-gallery-runtimedata-blocks-engine-capture-gallery-runtimedata-dla-collectiondata-blocks-engine-capture-collectiondata-dla-collection-fielddata-blocks-engine-capture-collection-fielddata-dla-collection-category-controldata-blocks-engine-capture-collection-category-controldata-dla-collection-indexdata-blocks-engine-capture-collection-indexdata-dla-collection-emptydata-blocks-engine-capture-collection-emptydata-dla-collection-itemdata-blocks-engine-capture-collection-itemdata-dla-collection-membersdata-blocks-engine-capture-collection-membersdata-dla-collection-statusdata-blocks-engine-capture-collection-statusdata-dla-collection-runtimedata-blocks-engine-capture-collection-runtimedata-dla-collection-visibilitydata-blocks-engine-capture-collection-visibilitydata-dla-status-templatedata-blocks-engine-capture-status-templatedata-dla-status-hide-zerodata-blocks-engine-capture-status-hide-zerodla-no-matchdata-blocks-engine-capture-no-matchdla-finite-probedata-blocks-engine-capture-finite-probedata-dla-choice-indexdata-blocks-engine-capture-choice-indexdata-dla-native-control-statedata-blocks-engine-capture-native-control-statedata-dla-native-control-runtimedata-blocks-engine-capture-native-control-runtimedata-dla-glyph-colordata-blocks-engine-capture-glyph-colordata-dla-anchor-unresolveddata-blocks-engine-capture-anchor-unresolveddata-liberation/captured-interactions/v1blocks-engine-capture/captured-interactions/v1data-liberation/capture-receipt/v1blocks-engine-capture/capture-receipt/v1data-liberation/source-interactivity/v1blocks-engine-capture/source-interactivity/v1data-liberation/finite-bootstrap/v1blocks-engine-capture/finite-bootstrap/v1data-liberation/collection-status/v1blocks-engine-capture/collection-status/v1db750332…8624614(disclosure interpreter sha256)3d350f55…b70ce75dc1321b380…5a649c5a7(gallery helper sha256)0a6bf62a…bf29f723aawp:dla/sticky,wp-block-dla-stickywp:blocks-engine/sticky,wp-block-blocks-engine-sticky*-dla-goldens.json,page-reconstruct-dla-baseline.*,theme-reconstruct-dla-baseline-parity.test.ts*-reference-goldens.json,page-reconstruct-reference-baseline.*,theme-reconstruct-reference-baseline-parity.test.tsDLA_*constants,DLA_ROOTenv,dlaSection,dlaSerializeBlockAttrs, "DLA parity" test namesREFERENCE_*,REFERENCE_ROOT(required),referenceSection,referenceSerializeBlockAttrs, "reference parity"tests/fixtures/dla-disclosure-runtime.jstests/fixtures/capture-disclosure-runtime.js(+ newcapture-gallery-runtime.js)"input + internal" means a projector writes the name from sidecar evidence and a converter reads it later in the same compile. Every PHP test fixture's captured HTML, sidecar JSON and assertions were converted with the rule.
Equivalence gate: no loss of power
tests/contract/capture-evidence-equivalence.phpruns incomposer test.origin/trunk(44a1eb5ed, after merging trunk) checkout and ran its full suite (composer testplus every other PHP test script). That recorded 349 distinct calls whose input carried the old names:ArtifactCompiler::compile93,HtmlTransformer::transform85,ArtifactNormalizer::normalize29,CapturedDialogProjector45,CapturedCollectionProjector48,CapturedSelectableSetProjector32,CapturedChoiceGroupProjector17. Inputs are stored verbatim, with old names. Both harvests used the committedharvest-hook.php; the first, on84d0853a6, was reproduced byte-identically. The expected outputs were stable across two baseline runs.htmlBytes. It is then replayed through this branch. The expected output comes from a second disposableorigin/trunkcheckout with the same rule applied to itssrc/. The result must be byte-identical: compile result + WordPress site plan, projected files, or exception. Only the checkout path and*_duration_mstimings are neutralized.Cross-name semantic report (report only:
tools/capture-evidence-equivalence/semantic-report.php). This compares unmodified trunk on the old names with this branch on the new names. It masks what a longer name changes by construction: byte counts, offsets, content digests, and excerpt cut points.source_fragment/snippetexcerpts are cut.reusable_components.componentsdiagnostic list.Guards
tests/contract/capture-evidence-vocabulary.php:src/outsideCaptureEvidencespells a capture name (checked by tokenizer);tests/contract/producer-neutral-source.php(composer test) andpackages/blocks-engine/src/__tests__/producer-neutral-source.test.ts(vitest): fail ondla/data-dla/data-liberation/liberation-agent, case-insensitive and word-ish (dlaSectionandDLA_ROOTfail,childLayoutpasses). They scanphp-transformer/src,packages/blocks-engine/src, and thescriptsofcomposer.jsonand thepackage.jsonfiles.Verification (local, PHP 8.5.5 / Node 24, on the merged head)
CI on the earlier head
19fc3b9f3was all green: PHP Transformer, JS Transformer, JS Fidelity Ratchet and Solved Site Promotion Gate.php-transformer:composer install,composer validate --strict,composer lint(phpcs + phpstan),composer test:lintandcomposer testall pass. The repo-roottests/contract/production-acceptance-matrix.phppasses.Every PHP test script outside
composer testexits the same as on trunk.Visual-parity job:
npm ci,npm run lintandnpm test(with pre/post tests) pass. Both variants oflayout-media-tracks.mjspass. I ran them against a local WordPress 7.1 rather than downloading 7.0.4. The details, collection filter, runtime dialog trigger, dropdown panel, native button background, orphan list item and accordion browser and PHP steps all pass.packages/blocks-engine:pnpm typecheckandpnpm buildpass;pnpm testpasses 106 files / 508 tests;pnpm test:bundlepasses (508);pnpm test:svg-decodepasses;wp-runtime.cjsis 21 MB.Left to CI: the WordPress PHPUnit site-plan integration, the stylesheet-activation Docker job, the PHP 8.2–8.4 matrix, and the solved-site promotion workflow.
AI disclosure: written by Claude in OpenCode with Chris Huber (direct implementation authorized by the operator); Claude implemented, tested and opened this PR.