test: ACF parity with @contentrain/wp-import (shared fixture) - #34
Merged
Merged
Conversation
A page_link stores the linked post's ID, and Bridge modelled it as a
URL, so the validator rejected the entry and the whole export stopped
("Invalid value or missing relation: wp-project.acf_landing_page").
The ID now casts to the post's permalink. A draft, private,
password-protected or missing target has no public address: its value
is left out with an acf-page-link-target-not-public warning, and the
field is never required, so the field keeps one type across entries
(a fallback would make the model inconsistent and fail the export
again). A multiple page_link takes the structured fallback.
SCALARS mapped link to url, so a link sub-field in a group, repeater or
flexible_content row kept only its URL and silently dropped the label
and target ("Contact"). A link is no longer a scalar: a row holding one
falls back to structured values whole, with the existing
acf-shape-not-modelled warning, so nothing is lost. A top-level link is
still modelled with its label and target; an empty one exports nothing.
Modelling a link as an object is B3.
Policy::sensitive() is built for unknown post meta and matches any name containing email, ip or user_agent, so a public ACF field such as contact_email was dropped as sensitive-field. ACF fields and sub-fields now use Policy::secret_name(): the password type and credential names (password, secret, token, credential, api key, private key, authorization, cookie, session) never leave; an email field the owner built is exported. Unknown meta keeps the broad rule, and values are still scanned for credential patterns. Planted ACF secrets are added to the secret-scan canaries.
…port Review N1 from t6 on #32: the unbounded pattern dropped content fields such as passage, session_title and cookie_recipe. secret_name() now splits camelCase and hyphens and matches whole words — the rule @contentrain/wp-import's ACF reader uses (ai #289) — so user_pass, apiKey and access_token stay secret and passage stays content.
Review N from t6 on #30: an attachment's status is inherit, and its permalink carries the parent's slug, so a file attached to a draft would leak the draft's address. The parent must be published and unprotected too.
… as object/array fields
In a collection or singleton entry an ACF group is an object, a
repeater an array of objects, flexible content an array of objects
with a required layout select (every layout in the schema, sorted)
over the union of the layouts' fields, a link {url,title,target}, a
google_map {address,lat,lng,zoom}, a checkbox or multi-select an array
of select. Definitions come from the field schema alone, so every
entry agrees; containers nest at most two deep, and anything with no
value type (a reference, deeper nesting) takes the structured fallback
whole. A document's frontmatter has no nested values, so posts keep
the collection models. A stored choice the field no longer offers is
left out with acf-choice-not-in-options instead of failing the export.
The validator checks object, array and decimal values recursively.
Same table as @contentrain/wp-import's ACF reader (ai #289).
…ir values The schema of a page's and an Options Page's ACF fields was still cleaned with the broad meta rule, so contact_email lost its schema and fell back to structured values (the acceptance check caught it).
# Conflicts: # includes/class-contentrain-bridge-acf.php # tests/integration.php
…ed fixture through Acf::field tests/fixtures/acf-parity.json is copied unchanged from Contentrain/ai (the commit in acf-parity.pin); tests/acf-parity.php runs each case as a collection entry writes it and compares the definition (type, options, fields order-free, items, required), the stored value (canonical JSON, strictly) and how many values were dropped outside the choices.
# Conflicts: # tests/integration.php
…d a link with an empty target The fixture now states the site's time zone (top level, per case); the test sets timezone_string to it for the case, so a date-time is written with that zone's offset at that moment, summer time included, and puts the site's own zone back afterwards.
Parity with wp-import (shared fixture): the field is the schema's, so it is declared even when this entry's value is left out as outside the choices; the entry just has no value for it. Callers write no key for a field with no content.
ABB65
marked this pull request as ready for review
September 25, 2026 16:55
# Conflicts: # tests/integration.php
Member
Author
|
t6 QA — Bridge #34 (40a7060): ONAY, re-pin koşuluyla
|
Merged
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.
The Bridge side of the ACF parity fixture from
@contentrain/wp-import(ai #295, t7).tests/fixtures/acf-parity.jsonis copied unchanged.tests/fixtures/acf-parity.pinrecords the source commit. It is pinned to the PR head for now and will be re-pinned to the merged main commit.tests/acf-parity.phpruns fromtests/integration.php. It passes every case throughAcf::field(..., inline: true), as a collection entry writes it. The fixture'slayoutsobject is converted to ACF's list form. It compares:true == 1is true in PHPdropped: the count ofacf-choice-not-in-optionswarningsDraft.
Status: CI is green. All 38 cases of ai #295 @7f33809 pass, including date-times in the case's site zone (Istanbul, and Berlin in summer and winter) and a link with an empty target.
Bridge change found by the fixture: a select whose stored value is outside its choices now still declares its field, with no value. The callers (
Models::post, the Options Page) write no key for a field with no content. Before, the field was not declared at all, which differed from wp-import.acf-parity.pinwill be re-pinned to the merged main commit of ai #295.Reviewer: t6 · coordination: t7