Update the Microsoft Store listing
The Store is driven by pcenter, which replaced the
PowerShell scripts that used to live in .github/scripts/. Install locally with
brew install prof18/tap/pcenter.
Before anything else
- Reading is free and changes nothing. Start here:
pcenter listing show --locale en-us,pcenter locales list,pcenter submission status,pcenter rollout status,pcenter app info,pcenter reviews list --from 2026-01-01 --all. - Credentials live in
~/.config/pcenter/credentials.envlocally — set up withpcenter auth login, checked withpcenter auth doctor. Never commit them. CI passesMS_STORE_*from repository secrets instead. - Only one pending submission can exist per app. If a command reports one, inspect it
with
pcenter submission statusbefore doing anything.--replace-pendingdeletes whatever draft is there, including one a human left for inspection.
Where the copy lives
assets/storecopy/<locale>/ — microsoft_store_description.md plus the microsoft_store_*
keys of store_listing.json — is the source of truth, maintained through the translation
pipeline. Nothing pcenter writes is authoritative.
.pcenter/ is a gitignored scratch directory, the same idea as .asc. It is a snapshot
of what the Store currently holds: regenerate it with pcenter listing pull --dir .pcenter,
never commit it, and never hand-edit it expecting the edit to survive.
Release notes are separate and committed:
assets/storecopy/microsoft-store-release-notes.json, owned by the
update-store-release-notes skill. A Store locale missing from that file fails the publish
rather than shipping an empty changelog.
Changing listing text
Generate the listing files rather than editing them:
pcenter listing pull --dir .pcenter
.scripts/generate-microsoft-store-listing.py --dir .pcenter --dry-run
.scripts/generate-microsoft-store-listing.py --dir .pcenter
pcenter listing push --dir .pcenter --dry-run
The generator skips locales the Store does not serve, and locales whose source is still the
English base text — it measures word overlap against base, because several
microsoft_store_description.md files are English with only a heading or two translated and
an equality check misses them. It never writes title for an existing locale.
listing push requires exactly one mode:
| Mode | Effect |
|---|---|
--dry-run |
Prints the diff, creates nothing. Safe even with a draft pending. |
--skip-commit |
Creates an inspectable draft in Partner Center. |
--yes |
Creates and commits — goes live after certification. |
Always run --dry-run first, and never --yes unless the user explicitly asks.
Screenshots
One English set is shared by every language; the Windows Store does not need localized ones.
.scripts/generate-microsoft-store-listing.py --dir .pcenter --screenshots ~/path/to/shots
It deletes each locale's existing screenshots and uploads the new set, preserving the
StoreLogoSquare and the en-US captions. Image binaries cannot be downloaded from the
API, so the source PNGs must come from outside the repo — keep them somewhere findable.
Requirements: PNG, ≥1366×768, ≤50 MB, ≤10 per locale.
Adding or removing a listing language
--add <locale> creates a listing file for a language the Store does not serve yet. It
copies the reserved product name into title, which is required: a listing language the
packages do not include has no package to draw its name from, and omitting it fails the whole
submission with MissingTitle.
A language is "additional" in Partner Center when the MSIX does not declare it — the package
languages come from .github/msix-resources-template.xml. Add a language there to make it
package-supported on the next Windows release.
Removing a locale needs --allow-locale-removal on top of the mode flag, so a deleted file
cannot silently drop a Store language.
Store limits pcenter checks before creating a submission
shortDescription≤ 500 characters — Microsoft's published docs wrongly say 1,000.- At most 21 locales may carry keywords at all. Not per locale, not a keyword total: a 22nd locale with keywords is rejected outright, so one must be cleared to make room.
description≤ 10,000;features≤ 20 items.
When something is stuck
- Rollout (the 2026-07-08 class of failure):
pcenter rollout status, thenpcenter rollout finalize. Every mutation verifies the resulting state, because the Store API returns 504 for operations that in fact succeeded. - A failed submission (
CommitFailed,CertificationFailed) still occupies the single pending slot.pcenter submission delete-draft --yesclears it. - Watching:
pcenter submission watch. Running out of poll attempts is not a failure — certification takes hours — it reportsin-progressand exits 0.
Exit codes
2 fix the invocation or config · 3 credentials rejected · 4 invalid for the current
state, never retry unchanged · 5 throttled. Output is JSON when piped.
Full reference: pcenter docs.
Guardrails
- Do not push listing changes or commit a submission unless the user explicitly asks.
- Never
--yeswithout an explicit request;--skip-commitleaves a draft they can review. - Do not translate store copy. If a locale's source is still English, report it — the generator will refuse it anyway.