Repository navigation
Update the monorepo root and rehearse every plugin's release - #27
Merged
Merged
Conversation
The monorepo pinned configs at 8d53efd, before configs' scoping, workflow, baseline and docs branches, and each plugin locked the framework mirrors from before the framework's CI, runtime and docs branches, so the plugins built and tested against tooling and a framework upstream no longer has. Every lock also held symfony/filesystem 7.4.21, whose advisory GHSA-6prc-h956-hxjq has failed the Supply-Chain Audit on trunk since 2026-11-09. Configs moves to b107001 in the 53 workflow references, the four npm pins and every lock, and the framework to 1dfb070's split in each plugin's lock. Every other package moves to its newest release (symfony 7.4.23, PHPStan 2.3.5, PHPUnit 13.4.6, WooCommerce 11.2.2 and its stubs, Groups 4.9.0, serializable-closure 2.2.0 and the @wordpress/* minors, whose minimums rise to them), with TypeScript held on 6. The wp-env sites move to WordPress 7.1.4, and their $schema to the wp-env URL that resolves. The moves force six changes: - WooCommerceRequirement::check() is static, in LO's and LPM's compositions. - The kernel no longer lists the components it resolved, so the boot rows and LPM's strategy rows read the components it skipped. - PHPStan's strict rules refuse a constant of unknown type as an operand, so WP_CLI, XMLRPC_REQUEST and REST_REQUEST compare with true. - Configs' Playwright base reads tests/EndToEnd, so the root's and each plugin's end-to-end folder takes that name. - Configs' release declares id-token and attestations, which each plugin's release caller grants. - A rejected setting's message also says the other changes were saved. Assisted-by: Claude Code:claude-opus-5-5
The monorepo's .editorconfig files asked for JSON indented with two spaces and plain-text files with CRLF line endings, while the manifests and wp-env configs used four spaces, the Composer manifests and tsconfigs two, and the PHPStan profiles four. Configs' .editorconfig, which every repository copies, indents every file but YAML with tabs four columns wide and keeps LF line endings everywhere (X190, X314). The root and each plugin take configs' .editorconfig, and their tracked JSON and NEON files, npm's locks included, are re-indented with tabs. The change is whitespace only: git diff -w is empty outside the .editorconfig files, and each JSON file parses to the same value. Composer's locks keep four spaces, which Composer always writes. Assisted-by: Claude Code:claude-opus-5-5
Each plugin's entry reads Version: 2.0.0, while its CHANGELOG.md opens at its last 1.x release and its 2.0.0 entries wait as fragments in changelog/. Configs' release refuses a version whose changelog holds no section for it, so every rehearsal of a plugin's first release, and the release itself, would fail. Each CHANGELOG.md gains a "2.0.0 - unreleased" block holding the plugin's v2-framework entry: Linked Orders and Internal Comments move theirs out of its fragment, and Locked Payment Methods, which had none, takes Linked Orders' sentence, which describes its boot as well. The other fragments stay pending, and the first release dates the block with changelogger's --amend, which folds them in (X365). A plain changelog:write would now open a 3.0.0 block above it, so each changelog's header and each AGENTS.md command line send the release to the monorepo's CONTRIBUTING.md, as the template's header sends it to its own. Assisted-by: Claude Code:claude-opus-5-5
Each plugin's release workflow runs on its tag, a pull request or a dispatch, but only on its mirror, which takes no pull requests, and no root workflow called configs' release. So Plugin Check, the archive build, the version, changelog and header checks and the test of the built artifact had never run for any plugin, and would first run on its 2.0.0 tag, which the split forwards and never moves again. A root Release Rehearsal workflow runs configs' release for each plugin on pull requests and pushes to trunk, with wp.org's full checks for a plugin with a readme.txt, the plugins' excluded checks and publish off, granting every scope the release declares (X130). The mirrors keep publishing on forwarded tags. Assisted-by: Claude Code:claude-opus-5-5
The root kept a changelog with a hand-written 1.0.0 block, changelogger and a fragments folder, but the monorepo itself is never released: its only tags are the plugins' <slug>/vX.Y.Z, so root fragments were validated and never written. Quality also validated changelogs in a job of its own, where the framework validates them inside the lint job, which it names "Lint PHP". The root's CHANGELOG.md, changelog/, changelogger, its scripts and config and its quality-check step go, and CONTRIBUTING and the pull request template ask for fragments only in the changed plugin (X158). Each plugin's "Lint PHP" job runs changelog:validate after its PHPCS and PHPStan, and the separate changelog job goes (X270). Assisted-by: Claude Code:claude-opus-5-5
The root required wordpress-configs through its own vcs repository and allowed its PHPCS and PHPStan installers, yet nothing in the root reads it: the plugins lint and scope with their own copies, and the root's workflows call configs by reference. The root also kept a platform-check override the plugins need and it does not, since it ships no code, and its test:integration:ci split the test:integration pair it belongs after. The requirement, its repository and the two installers' allow-plugins entries go, composer/installers staying for WooCommerce's install path, and composer audit --locked keeps the advisories check (X236). The platform-check override goes, and test:integration:ci follows test:integration:run (X271). Assisted-by: Claude Code:claude-opus-5-5
plugins.yml listed WordPress's versions in the job every workflow needs, so one failed call to the version API stopped the split, the tag forwarding and every lane, and it called the API once, without a retry. A tag starting with a plugin's slug but missing the release pattern split nothing and reported success, as did a tag naming no plugin, and a plugin's directory could drift from its text domain, which the template's workflows read as its slug. The versions move into a job of their own, which runs only when a caller asks for it, as Tests does, and calls the API with the framework's retry flags (X235's A453, X370). The plugins job refuses a tag that names no listed plugin and a plugin whose extra.text-domain differs from its directory, and the split refuses a plugin's tag that misses the release pattern (A454, A455). Assisted-by: Claude Code:claude-opus-5-5
The shared environment ran only on its pinned WordPress, never on a newer release or WordPress trunk, so a coexistence break there would surface only once the pin moved. The root's end-to-end spec and its Playwright config were never linted or type-checked. The root's "all" commands left out every plugin's script, style and type checks, and CI installed the plugins with npm install, which rewrites a lock that has drifted instead of refusing it. Tests runs the shared Integration and end-to-end lanes on the newest WordPress the plugins list, and the nightly runs both on WordPress trunk (X234's A449). The root gains lint:scripts and lint:types, with the template's ESLint config, a tsconfig over tests/EndToEnd and the typescript and @types/node they need, and Quality's script lint covers the root; its Playwright config takes the template's shape, which Prettier accepts, and the spec references Node's types (A450). lint:all, test:all and quality-check:all also run each plugin's checks, through a plugins:lint fan-out over the template's lint script, which each plugin gains (A451). CI installs the plugins through plugins:packages-install:ci and setup:ci, which run npm ci (A452). Assisted-by: Claude Code:claude-opus-5-5
Each plugin's workflows become its mirror's own at the split, yet the root's Workflow Checks ran only on changes under .github/workflows and linted only the root's, so a change to a plugin's workflows reached its mirror unchecked; a change to dependabot.yml or another .github file skipped them too. Dependabot's Actions entry listed /plugins/*, where it reads only a directory's own workflow files, so it never updated the plugins' action pins. Workflow Checks run on changes under .github and every plugin's .github/workflows, and pass the plugins' workflows to actionlint through configs' extra-workflows input, zizmor already auditing every workflow in the repository (X154, X310, X275's A705); AGENTS.md drops the limitation that recorded the gap. Dependabot's Actions entry names each plugin's .github/workflows (X131). The schedule's comment takes the framework's one line (X237's A459). Assisted-by: Claude Code:claude-opus-5-5
The root's Quality, Tests and Supply-Chain Audit had no concurrency group, so every push to a pull request queued its full matrix beside the runs it made stale. CodeQL analysed only the workflows, not the TypeScript of the shared spec and the plugins' scripts and specs. The audit passed npm flags configs now defaults to, and its and the split's comments restated a schedule and named a hash in lower case. Quality, Tests and the audit take the template's concurrency group, which cancels only a pull request's earlier runs, so trunk runs always finish (X275's A704). CodeQL also analyses JavaScript and TypeScript (X293). The audit drops its flags for configs' identical default (X274), and the comments take the template's and the framework's wording (X237's A459). Assisted-by: Claude Code:claude-opus-5-5
tests/Fixtures/multi-plugin-smoke/ held only a .gitkeep, reserved for a fixture plugin the shared environment would mount once a second plugin landed. All three plugins have landed, and the shared environment mounts them and WooCommerce with no fixture, so the folder reserved nothing. The folder and its AGENTS.md layout line go; a fixture plugin adds its folder with it when one is needed (X157). Assisted-by: Claude Code:claude-opus-5-5
The shared Integration run left out Locked Payment Methods' groups and wc-memberships groups, and AGENTS.md gave as the reason that the shared environment installs neither plugin. That holds for Groups, mapped only in the plugin's own environments, but the Memberships rows run on the plugin's own stub of WooCommerce Memberships in every environment, so the shared run skipped rows it can run. The shared run excludes only the groups group, and AGENTS.md names Groups alone (X237's A460). Assisted-by: Claude Code:claude-opus-5-5
Both issue forms offered only Linked Orders, so a report about Internal Comments or Locked Payment Methods had to pick "Other". Their runtime area still named the "Plugin singleton" the plugins no longer have, the CI area named three of the workflows, and the version placeholders suggested releases below the plugins' floors. Both forms list the three plugins, the runtime and CI areas take the template's wording, and the version placeholders read x.y.z, which no release outdates (X237's A458, X280's A718). Assisted-by: Claude Code:claude-opus-5-5
The root's .gitignore covered no wp-env override, though wp-env 12 merges a .wp-env.override.json, or a <name>.override.json beside a named config, from a developer's own machine. Its toptal block, and each plugin's, had also lost the two carriage returns of macOS's Icon\r\r rule, so the rule matched any file named Icon and left a stray blank line. The root ignores .wp-env*.override.json above the toptal block (X282's A724), and the root's and every plugin's block is toptal's again, byte for byte, as the template's is (X199's A233). Assisted-by: Claude Code:claude-opus-5-5
AGENTS.md's convention 2 listed what names each plugin as "one line each", which the shared spec's boot row, the README table, the layout tree and the issue forms do not follow and did not name, and the shared environment mapped the plugins alphabetically where every other list reads Linked Orders, Internal Comments, Locked Payment Methods. The layout said the root's lanes are the template's, though they also lint the root and run the shared environment, and convention 1 said a plugin keeps "the template's files", which hides what it drops. Convention 3 allocated ports in pairs, leaving no place for the multisite ports, and convention 4 forbade the option-key migration Locked Payment Methods' 2.0.0 makes. Neither the release step's home nor the root's lint:all and quality-check:all were named. Convention 2 names every list a plugin joins, and the mappings follow the plugins' order (X261's A649). The layout and convention 1 state what the root lanes and plugin directories hold (A650), and convention 4 keeps option and meta keys unless a major version migrates them without losing data, with changelog and Upgrade Notice lines (A651). Convention 3 allocates ports in threes: 8820 shared, 8821 the root's multisite, 8822 to 8827 the three plugins' pairs and 8828 to 8830 their multisite ports, and a new plugin the next free three, from 8831 (X273's A697). Convention 5 points at CONTRIBUTING.md's release step (A694), and Commands lists lint:all and quality-check:all (X234's A451). Assisted-by: Claude Code:claude-opus-5-5
The root's CONTRIBUTING and README sent a release to "the plugin's own CONTRIBUTING.md", which no plugin has, since the plugins drop the template's for the monorepo's. So the release step was written nowhere: not the changelog write and readme sync, not the first release's amend of the 2.0.0 - unreleased block, which a plain write would leave below a new version, not the version headers the release checks, and not that the release waits for the mirror's runs. CONTRIBUTING's Releases section holds the step: the changelog write, the first release's two commands run apart, since Composer hands the arguments after -- to both of changelog:write's, the version headers, the wait for the mirror's Quality and Tests runs, and the signed <slug>/v<version> tag the split forwards (X273's A694, X365). README's Releases section points there. Assisted-by: Claude Code:claude-opus-5-5
The mirrors are read-only and take no issues, yet each plugin's npm bugs field and SECURITY.md advisory link named its mirror, and no plugin's README said where to report, so a report reached a repository that refuses it. Internal Comments' SECURITY.md also kept the template's line about plugins generated from it, and said "the template" where it meant the plugin. Each plugin's bugs field and advisory link name the monorepo, whose private vulnerability reporting is on, Internal Comments' template leftovers go, and each README ends by saying the plugin is developed in the monorepo, where issues and pull requests go (X262's A652 and A653, X273's A694). The READMEs' lint step runs the plugin's lint script. Assisted-by: Claude Code:claude-opus-5-5
The root's audit script ran a bare composer audit and npm audit, so it failed on an advisory among npm's development tools, which the gate reports through Dependabot instead, and on a locked package marked abandoned, which the gate only reports; it also resolved the manifest rather than auditing the lock CI audits. The script runs composer audit --locked --abandoned=report and npm audit --omit=dev --audit-level=high, the gate's defaults (X199's A231, X319). Assisted-by: Claude Code:claude-opus-5-5
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
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.
Summary
The monorepo pinned configs and the framework from before their latest changes, no workflow had ever built or run Plugin Check on a plugin's release, and the root's audit found gaps in its CI, its tag handling and its docs. This moves the root and the three plugins to configs
b107001and the framework at1dfb070with every change they force, starts each plugin's changelog at an unreleased 2.0.0 block, rehearses every plugin's release on pull requests, runs the shared environment on the newest WordPress and nightly, lints the root and the plugins' workflows, isolates the WordPress listing to Tests, refuses misnamed tags, drops the root's changelog and configs requirement, and corrects the root and plugin docs.Type of change
Checklist
composer changelog:addin the changed plugin's directory): no plugin's behavior changes beyond its unreleased 2.0.0 entriescomposer quality-checkpasses locally in every changed pluginreadme.txtupdated if runtime requirements changed: no requirement changedBreaking changes
changelog:*scripts,CHANGELOG.mdandchangelog/are gone;lint:all,test:allandquality-check:allalso run every plugin's checks.changelog:validate, which no longer has a job of its own; "Scripts and styles lint (…)" is "Lint scripts and styles (…)" over the root and each plugin; Release Rehearsal joins.<slug>/tag that missesvMAJOR.MINOR.PATCH, fails.bugs, README and SECURITY.md send issues and vulnerability reports to this repository.