BOGO Select for WooCommerce 2.3.5
Install: download bogo-select-2.3.5.zip below, then WordPress → Plugins → Add New → Upload Plugin.
[2.3.5] — 2026-08-02
A test written to close a gap, which then found something in it.
Most of this release is CI and tooling, and on its own none of it would have
earned a version — BRIEF.md §8.1 gives the version to the plugin code. The
phone-viewport test changed that by failing: the chooser's buttons were 21 CSS
pixels tall on a phone, which is a real defect a real customer has been tapping
at since 2.2.0. The fix for it is what makes this a PATCH rather than another
entry under Unreleased.
Fixed
-
The build no longer packages
node_modules, and the parity check no longer
says it did not. Committing a lockfile in this release means a developer who
has runnpm cihas anode_modules/directory, andbuild-zip.shhad no
reason to exclude one before — so cutting this release produced an archive of
4.1MB and 180 files instead of 136KB and 23, carrying 197 files of Playwright
into stores.The worse half is that
verify-zip.shpassed it. That script exists to
catch exactly this (CODEX-REVIEW.mdM-01, the v1.2.0 archive that shipped a
superseded class). It compares runtime files on both sides while pruning the
directories the build excludes, andnode_moduleswas in neither list — so it
found the same stray.jsfiles in the worktree and in the archive, called
them matching, and reported "87 runtime files verified": six times the real
number, in the message whose whole job is to be trusted. Both scripts now
prune it, and the archive was checked by reading its contents rather than by
trusting the exclude list.CI could not have caught this, because the package job never installs npm
dependencies and its archive was always clean. Only a developer's own build
was affected, and only since the lockfile landed — introduced and found inside
the same unreleased window.
Changed
-
Everything CI reaches for is pinned, and Dependabot watches the pins
(CODEX-REVIEW.mdL-03). Actions are pinned to full commit SHAs with the
version in a comment beside each — a tag can be moved, a SHA cannot. The
integration job's browser now comes fromnpm ciagainst a committed
package-lock.json, so it drives the Playwright that file names by integrity
hash instead of whatevernpm installresolved that morning..github/dependabot.ymlis the half that makes the rest safe, and is why
this was not done earlier. Pinning on its own trades a supply-chain risk for
a staleness risk: pinned actions stop receiving security fixes and nothing
says so. Weekly updates now cover the actions, the Composer dev tools,
Playwright, and the integration containers.Container images stay pinned by tag rather than digest. They exist for the
length of one CI job and are never published or deployed, so what matters is
being told when a newer WordPress or MariaDB appears — the same signal that
keeps the compatibility matrix honest.Nothing here can reach a store: the shipped plugin has no Composer or npm
runtime dependency, and neither manifest is in the archive. -
The first five updates Dependabot proposed, all merged.
actions/checkout5.1.0 → 7.0.1,actions/setup-node5.0.0 → 7.0.0,
actions/upload-artifact4.6.2 → 7.0.1, Playwright 1.56.0 → 1.62.0, and
MariaDB 10.11 → 12.3 in the integration containers. The pinning did not cause
that drift, it revealed it — three of the actions were majors behind before
anything was pinned.The three action majors share one cause: each moved its own runtime to Node 24
at v6, which needs Actions Runner 2.327.1 or newer, andubuntu-latest
satisfies that.actions/checkoutv7 also refuses to check out a fork's PR
head underpull_request_targetandworkflow_run; this workflow triggers on
pushandpull_requestonly.MariaDB was the jump with the most behind it:
order.test.mjsplaces a real
order and reads back its line metadata and stock decrements, and all eleven of
its checks passed on 12.3. The plugin issues no SQL of its own — every query in
the repository is in a test fixture — so the database is infrastructure for the
job rather than something the job certifies. The one cost is representativeness,
since CI now runs a newer database than most stores do. That is acceptable while
the plugin writes no SQL; if it ever does, the answer is a deliberate pin to an
LTS or a second lane rather than letting this drift back by accident. -
The compact chooser is checked at a phone width (
CODEX-REVIEW.mdL-02).
The layout added in v2.2.0 applies below 600px and the integration browser
runs at 1280px, so every browser assertion ever made about the chooser was
made at a width where the rule does not apply.mobile.test.mjsrenders both
carts at 390×844 and measures the boxes: the thumbnail capped and beside the
text rather than above it, the button under the name, no card running off the
side, and buttons meeting the WCAG 2.2 minimum target size.Geometry rather than screenshots, because a screenshot proves a layout
changed and says nothing about whether it changed correctly. It also taps a
gift, since a card that measures perfectly and cannot be tapped — something
invisible over it — is the failure worth catching, and Playwright's
actionability check is exactly that assertion.It found a defect on its first run. Every geometry assertion passed — the
layout is the row it was meant to be — but the buttons measured 21×53 CSS
pixels, under the 24px WCAG 2.2 asks for and well under what a thumb wants.
On a phone these are tapped rather than pointed at, so the compact layout now
gives its controls a 44px minimum target, the size the platform guidelines
settled on. "Remove gift" is styled as text rather than as a button and is
tapped just the same, so it gets the same target without gaining a border:
its background stays transparent, and the extra height is reach rather than
anything the customer sees. Cards grew from 134px to still well inside the
bound the test holds them to.Overflow is asserted against the chooser and its cards rather than the
document. A page-level check would fail on the theme's layout or
WooCommerce's own blocks, neither of which this plugin can fix, and a check
that fails for someone else's reasons is one that gets switched off. A real
page-level overflow is still printed to the log.