Skip to content

Bring the framework's CI, tooling and repository files to configs' current baseline - #70

Merged
ahegyes merged 19 commits into
trunkfrom
fix/ci-and-repository-files
Nov 9, 2026
Merged

ahegyes merged 19 commits into
trunkfrom
fix/ci-and-repository-files

Conversation

@ahegyes

@ahegyes ahegyes commented Nov 9, 2026 •

Copy link
Copy Markdown
Owner

Summary

The framework's CI called configs' reusables at an older commit than its own lock installed, so none of configs' scoping, workflow, baseline or documentation fixes reached it. Its inline scripts were tested only by their source text, no check read its code with Plugin Check's own sniffs, and its nightly never ran a WooCommerce release before it shipped. This moves every configs pin to one commit, runs the three inline scripts in a browser, runs Plugin Check's sniffs over the packages and clears their findings. The nightly gains a leg on WooCommerce's trunk build. Workflows, manifests, the fixture build and the repository files are tidied as the audit ruled.

Affected packages

  • wp-framework-bootstrap
  • wp-framework-shared
  • wp-framework-core
  • wp-framework-settings
  • wp-framework-woocommerce

Checklist

  • Tests added or updated (Unit and/or Integration as appropriate)
  • composer quality-check passes locally (PHPCS, PHPStan and the tests)
  • Public API changes are documented in the affected package's README.md
  • Breaking changes are explicitly justified below

Breaking changes / migration notes

No public API, hook, option or file contract changes. Every package's manifest re-indents with tabs, and each package's behavior is unchanged: the inline scripts print the same bytes, and OptionsPage registers each tab's option with 'type' => 'object', which WordPress reads only for REST. Contributor- and CI-visible:

  • Check names: configs' reusable jobs read as imperatives, so checks such as Lint PHP / lint:php:phpcs read Lint PHP / Run lint:php:phpcs; Quality's bootstrap syntax job reads PHP 7.4 syntax (bootstrap); new checks are Lint scripts and E2E per WordPress branch. No ruleset requires a check by name.
  • Nightly: a new Integration (WooCommerce nightly) job runs the Integration suite against WooCommerce's daily trunk build.
  • Local: npm ci now installs Playwright, WordPress's end-to-end utilities and configs' Node baseline; npm run test:e2e runs the spec on the tests environment; npm run audit runs CI's audit flags; JSON and NEON files indent with tabs.

The workflows called configs' reusables at 3cbd985 and the fixture
locks installed it there, while the root lock held 8d53efd, so the
framework's CI ran neither commit's fixes and configs' scoping,
workflow, baseline and documentation branches never reached it. Every
lock also held symfony/filesystem 7.4.21, which an advisory reported
on 2026-11-04 fails composer audit, and the wp-env core pins trailed
WordPress's newest 7.1 and 6.3 patches.

The 14 workflow refs, the root lock and the seven fixture locks move
to configs c944e73 together, and each lock takes its dependencies'
newest releases (symfony 7.4.23, PHPStan 2.3.5, PHPUnit 13.4.6,
WooCommerce 11.2.2 and its stubs). The npm lock takes the newest
releases npm's seven-day cooldown shows; its three top-level version
changes are hoisting, with the newer copies kept beneath. The wp-env
configs pin WordPress 7.1.4 and 6.3.14, and their $schema names
wp-env.json, since wp-env.schema.json redirects to a 404.

PHPStan 2.3.5 with strict-rules 2.1.1 reads the WP_CLI constant, which
nothing declares, as mixed, so consumer-a's uninstall compares it with
true, the value WP-CLI defines; nothing else needed a change. The
reusables' jobs read as imperatives, so the checks they report take
new names, such as "Validate composer.json".

Assisted-by: Claude Code:claude-opus-5-5
The .editorconfig indented JSON with two spaces and kept WordPress
core's CRLF rule for text files, while configs' baseline, which every
repository copies, is core's file with indent_size = 4, both YAML
extensions at two spaces and no CRLF section, so JSON and NEON take
the tab default that PHPCS and Prettier measure at four columns.

The file is configs' copy, and every tracked JSON and NEON file, the
npm lock included since npm writes it with package.json's indentation,
is re-indented with tabs. Composer writes its locks with four spaces
whatever the manifest uses, so those stay. Nothing but leading
whitespace changes; each JSON file decodes to the same value.

Assisted-by: Claude Code:claude-opus-5-5
The notice delivery, the notice dismissal and the meta box nonce
refresh are inline scripts that the Integration tests pinned only by
matching their source text, so a script that parsed but no longer
worked, or a WordPress or Gutenberg change it relies on, such as the
core/notices store's __unstableHTML option, passed the suite.

One Playwright spec over configs' base config now runs each of them in
Chromium on the tests environment: it dismisses a site notice and finds
it gone after a reload, and in the block editor it refreshes the object
form's nonce through a heartbeat with a stale nonce, saves the post,
finds the delivered notice's link rendered as a link and the form's
value kept. A small unscoped fixture plugin on the root autoloader,
mapped into the tests environment's plugins directory and active only
while the spec runs, provides the notice and the form. Each row fails
when its script's key line is broken. The source-text assertions go.

The spec is the framework's first JavaScript, so it takes the
template's lint: an ESLint config re-exporting configs' baseline and a
lint:scripts script, which Quality runs. Tests runs the spec beside
Integration for every WordPress branch from the floor up.

Assisted-by: Claude Code:claude-opus-5-5
The framework says its code is Plugin-Check-clean, but no check read it
with Plugin Check's own sniffs: Plugin Check skips a plugin's
vendor-prefixed/, where every consumer ships the framework, and the
framework's PHPCS ran only the WordPress standards, so constructs that
wordpress.org's reviewers flag went unnoticed.

wp-plugin/plugin-check joins the dev dependencies, and the production
profile references its bundled PluginCheck ruleset by path. PHPCS's
installed_paths cannot carry it, because Composer's PHPCS installer
rewrites that list from Composer-installed standards on every install,
so autoload-dev maps the standard's namespace for its helper classes.
The profile reports five findings, which the next commit clears.

Assisted-by: Claude Code:claude-opus-5-5
Plugin Check's sniffs reported five findings in the packages. The two
inline notice scripts were heredocs, which its Heredoc sniff refuses;
OptionsPage registered each tab's option without the arguments its
SettingSanitization sniff requires; and the updater's fetch of a
release's entry file and OptionRows' caller-prepared write read as
offloaded content and an unescaped query.

Each inline script is a nowdoc that ends before the call, followed by
the settings argument, and prints byte for byte what the heredoc
printed. register_setting() takes array( 'type' => 'object' ), the
JSON Schema type of a map keyed by field id, which a later REST schema
builds on, and no default, so get_option() callers keep their own
defaults and sanitization stays in pre_update_option. The fetch and
the write carry trailing ignores that name the sniff and the reason.
Convention 10 says local PHPCS runs Plugin Check's own sniffs over the
packages.

Assisted-by: Claude Code:claude-opus-5-5
Tests, Quality and the supply-chain audit had no concurrency group, so
each push to a pull request queued a full matrix beside the run it
superseded, and Workflow Checks ran only on workflow files, so a change
to dependabot.yml or another .github file reached trunk unchecked by
actionlint and zizmor.

The three workflows take the template's group, which cancels only on
pull requests, so a trunk run always finishes, and Workflow Checks
runs on every change under .github/.

Assisted-by: Claude Code:claude-opus-5-5
One failed request to WordPress's version API failed every Integration
leg, since curl did not retry, and the matrix compared each offer's
major and minor against the whole floor, so a floor such as 7.1.2
ruled out the 7.1 branch it names.

curl retries three times on any error within 20 seconds per attempt,
and both the floor and each offer compare as three numbers, padded
with zeros, so 7.1.4 stays in the matrix for a 7.1.2 floor and a 7.1
floor matches 7.1.0.

Assisted-by: Claude Code:claude-opus-5-5
Quality granted contents: read to the whole workflow and repeated it
on some jobs only, unlike CodeQL and Workflow Checks, so a job's
permissions depended on which pattern its author followed, and its two
PHP 7.4 syntax jobs were named differently.

The workflow grants nothing, every job grants contents: read itself,
and the bootstrap package's syntax job reads "PHP 7.4 syntax
(bootstrap)", beside "PHP 7.4 syntax (consumer-a)".

Assisted-by: Claude Code:claude-opus-5-5
Each split checked out its run's own commit and force-pushed the
result to the mirrors' trunk, and a GitHub re-run keeps the original
run's commit for 30 days, so re-running an older failed split after a
newer one succeeded rewound the mirrors until the next push under
packages/. The comment said an older run never force-pushes over a
newer split, which the per-ref queue does not guarantee. The job also
ran without the split environment the repository now defines and
without a time limit.

A branch run checks out trunk, so a re-run splits the current trunk; a
tag run keeps its tagged commit and its push without --force. The
queue's comment says what it guarantees: no two splits of one ref run
at once. The job runs in the split environment, which admits only
trunk and v* tags, and stops after 10 minutes.

Assisted-by: Claude Code:claude-opus-5-5
npm run audit audited the npm development tree and Composer's current
resolution rather than the locks, so it failed where CI passed, and its
Composer half failed on an abandoned package, which Composer 2.11
fails by default, while the gate only reports one. The workflow also
passed npm-audit-flags with the value configs' audit now defaults to,
and its comment said every development advisory skips the gate,
though Composer's are audited.

The audit script runs composer audit --locked --abandoned=report and
npm audit --omit=dev --audit-level=high, as AGENTS.md's command line
and CONTRIBUTING now do through it. The workflow takes configs'
default, and its comment names npm's development advisories.

Assisted-by: Claude Code:claude-opus-5-5
Dependabot's Composer entry covered only the root, so the seven
committed fixture locks, whose tools and runtime packages the
Integration and PHP 7.4 lanes build with, moved only when someone moved
them by hand.

The entry lists the root, consumer-a, consumer-b and every persona.
Dependabot's Composer fetcher resolves the fixtures' path repository
against the repository root, and a named-dependency update in a copy
without git keeps the framework packages at their locked dev-trunk, so
its updates resolve; only a full update would guess dev-main, and
Dependabot runs none. The entry ignores wordpress-configs, whose
dev-trunk Dependabot would move in the locks alone, while its 14
workflow refs and npm pin, which it cannot move, stay behind.

Assisted-by: Claude Code:claude-opus-5-5
The nightly ran only WordPress trunk, so nothing warned before a PHP
8.6 or WooCommerce release broke the framework. SettingsSection
follows WooCommerce's experimental settings API, whose changes reach
merchants with WooCommerce's next release, and configs' own CI already
checks PHP 8.6.

Two wp-env configs repeat the tests environment with one change each:
.wp-env.php86.json pins PHP 8.6 on port 8805, and
.wp-env.woocommerce-nightly.json maps WooCommerce's daily trunk build
on port 8804. Each has a Composer lane pair whose run script calls the
Integration run script with WP_ENV_CONFIG_FILE naming its environment,
which that script reads in place of .wp-env.tests.json, so the three
lanes share one PHPUnit command. The nightly runs Integration on the
WooCommerce build and, with an npm script that starts the PHP 8.6
environment and points Playwright at port 8805, Integration and the
end-to-end spec on PHP 8.6.

wp-env builds a site from WordPress's Docker images, which have no PHP
8.6 tag yet, so a job looks both tags up on Docker Hub and the PHP 8.6
legs run only once they exist; until then the run skips them and
stays green.

Assisted-by: Claude Code:claude-opus-5-5
A local .wp-env.override.json, or one beside any of the wp-env
configs, was not ignored, so it could be committed and reshape every
CI site. The toptal block had lost the two carriage returns that end
its Icon rule, so it ignored any file named Icon rather than only
macOS's Icon\r folder file.

One .wp-env*.override.json rule above the toptal block covers every
config's override, and the block is toptal's current output for the
same templates, byte for byte, its Node patterns included.

Assisted-by: Claude Code:claude-opus-5-5
The woocommerce package listed its scoping keys before its floors,
while core and settings put the floors first, so the three manifests
read differently for the same keys.

Its extra block reads branch-alias, requires-wp, requires-plugins,
scoping-stubs and scoping-text-domain, the floors first, as core's and
settings' do. No value changes.

Assisted-by: Claude Code:claude-opus-5-5
The bootstrap PHPStan profile's comment joined two reasons with a
semicolon above the includes, one of them about the analysis floor
set further down, and said "supports" where AGENTS.md says "runs on";
the bootstrap persona's repeated its floor numbers, which its entry
file's headers own, and the split's checksum comment broke the
one-sentence form with a dash and a parenthesis.

Each reason sits above the lines it explains, the profile says "runs
on", the persona names its floors by their headers, and the checksum
comment is one sentence.

Assisted-by: Claude Code:claude-opus-5-5
The root manifest and the seven fixture manifests listed their
repositories after their requirements, while the template lists them
first, and the root allowed every composer/* plugin to run where only
composer/installers is installed.

Each manifest takes the template's order: identity, repositories,
requirements, stability, autoload, scripts, extra and config, and the
root allows composer/installers by name. Every manifest decodes to the
same value and validates strictly.

Assisted-by: Claude Code:claude-opus-5-5
composer.json repeated the same six-step fixture build seven times,
changing only the directory, and listed the fixture helpers between
generate-autoloader and the packages-install and packages-update
wrappers, inside the setup group.

tests/bin/build-fixture.sh <dir> runs the steps once, beside
prefer-lowest.sh, and each fixtures:build:<name> calls it on one line
with Composer's process timeout off. The setup wrappers come first,
then the fixture helpers as their own group. Every fixture builds as
before.

Assisted-by: Claude Code:claude-opus-5-5
Each Integration admin request runs WordPress's plugin, theme and core
update checks whenever their transient is stale or the plugin list
changed, which the uninstall and install rows cause, and WordPress
gives WordPress.org a few seconds before it warns that it could not
connect. The request harness fails a test on any warning, so a slow
answer from WordPress.org failed two or three unrelated rows in most
full runs.

The request process answers those three endpoints itself with a reply
that lists no updates, as consumer-request.php keeps its update check
fresh, so the rows depend on WordPress.org no longer.

Assisted-by: Claude Code:claude-opus-5-5
@ahegyes ahegyes changed the title Fix/ci and repository files Bring the framework's CI, tooling and repository files to configs' current baseline Nov 9, 2026
The nightly's PHP 8.6 legs were meant to warn before PHP 8.6's release,
but PHP 8.6 ships within days and the legs wait for WordPress's PHP 8.6
Docker images, which follow a release, so they would first run on the
released version. Testing the newest PHP then belongs in pull-request
CI, not in a nightly environment named after one version.

The two legs, their Docker Hub gate job, .wp-env.php86.json, its npm
and Composer scripts and their documentation go. The WooCommerce
nightly leg stays on its own environment, since WooCommerce ships every
month and the settings API SettingsSection follows can change in any
release.

Assisted-by: Claude Code:claude-opus-5-5
@ahegyes
ahegyes merged commit b165094 into trunk Nov 9, 2026
29 of 32 checks passed
@ahegyes
ahegyes deleted the fix/ci-and-repository-files branch November 9, 2026 15:14
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