fix(ext): store-required icons + manifest description limit, with a CI gate - #12
Merged
Conversation
… guard both
Two Chrome Web Store submission blockers found by the listing-package prep, plus
a gate so they cannot silently come back.
ICONS. The built manifest had no `icons` key at all; CWS requires 128x128.
Generated 16/32/48/128 from store-listing/app-icon-512.png (the teal two-bar mark
the NudgeMark SVG already renders) with Pillow/Lanczos, ImageMagick is not
installed here, and Lanczos matters because a hard-edged glyph goes visibly
ragged at 16px under a cheaper downscale. Placed at extension/public/icon/{size}.png,
which is WXT's documented auto-discovery convention, so the `icons` key is
synthesized at build time rather than hand-maintained. action.default_icon is set
explicitly too: Chrome would fall back to `icons`, but naming the small sizes
keeps the toolbar button crisp instead of downscaling the 128 itself.
DESCRIPTION. Was 160 chars against a 132 hard limit. Replaced verbatim with the
verified 130-char line from
ops/routes/nudge/research/ext-09-listing-package/listing-copy.md.
THE GATE. Neither defect was visible to lint, tsc, 598 unit tests, the build or
37 e2e specs, all green the whole time a submission would have been rejected. So
scripts/store-validity.mjs holds the CWS rules once, and:
- scripts/verify-manifest.mjs runs them against the BUILT .output manifest
(deliberately not the source: WXT synthesizes the icons key, so it appears in
no source file and a source-side check would have passed while the shipped
artifact was still broken), wired into CI right after the build step
- tests/build/storeValidity.test.ts unit-tests the rules themselves
Plain ESM so both share ONE definition of the limits; a duplicated limit drifts.
Proven to bite: against a manifest with the icons key deleted and the old 160-char
description restored it reports both problems and exits 1.
Verified in the BUILT manifest: icons {16,32,48,128}, action.default_icon, and a
130-character description.
Gate: eslint clean, tsc clean, 608 unit tests (22 files, +10), 37/37 e2e,
verify:manifest passing.
astraedus
marked this pull request as ready for review
July 26, 2026 23:55
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.
Two Chrome Web Store submission blockers found by the listing-package prep, plus a gate so they can't silently come back.
1. No
iconskey at allCWS requires 128×128; the built manifest had none. Generated 16/32/48/128 from
store-listing/app-icon-512.png(the teal two-bar mark theNudgeMarkSVG already renders) using Pillow with Lanczos, ImageMagick isn't installed on this box, and the resampling choice matters: a hard-edged glyph goes visibly ragged at 16px under a cheaper downscale.Placed at
extension/public/icon/{size}.png, which is WXT's documented auto-discovery convention, so theiconskey is synthesized at build time rather than hand-maintained.action.default_iconis also set explicitly, Chrome would fall back toicons, but naming the small sizes keeps the toolbar button crisp instead of letting it downscale the 128 itself.2. Description was 160 chars against a 132 limit
Replaced verbatim with the verified 130-char line from
ops/routes/nudge/research/ext-09-listing-package/listing-copy.md:3. The gate
Neither defect was visible to lint, tsc, 598 unit tests, the build, or 37 e2e specs, all green the entire time a submission would have been rejected. Store validity is invisible to every gate we had.
scripts/store-validity.mjsnow holds the CWS rules once, and:scripts/verify-manifest.mjsruns them against the BUILT.outputmanifest, wired into CI right after the build step. Deliberately not the source: WXT synthesizes theiconskey, so it appears in no source file, a source-side check would have passed while the shipped artifact was still broken.tests/build/storeValidity.test.tsunit-tests the rules themselves (10 cases, including the exactly-at-limit boundary and reporting both problems at once).Plain ESM so both share one definition of the limits, a duplicated limit is a limit that drifts. Typed for the TS side via a
.d.mtsrather than a suppression comment.Proven to bite: against a manifest with the
iconskey deleted and the old 160-char description restored, it reports both problems and exits 1.Verification
Built manifest now contains:
npm run lintnpm run typechecknpm testnpm run buildnpm run verify:manifestnpm run e2eScope kept tight: no Lights Off files touched.
Draft on purpose, the orchestrator QA-gates the merge.