Skip to content

feat: add authoritative Microsoft Store PWA IAP - #134

Merged
imjlk merged 51 commits into
mainfrom
codex/microsoft-store-iap
Aug 11, 2026
Merged

imjlk merged 51 commits into
mainfrom
codex/microsoft-store-iap

Conversation

@imjlk

@imjlk imjlk commented Aug 11, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add guarded Microsoft Store PWA Digital Goods commerce as the published @mpgd/adapter-browser/microsoft-store subpath for Store-installed Edge PWAs, including localized catalog data, paid-purchase recovery, and game-owned authority hooks
  • add Microsoft Collections v9 verification plus idempotent Game Services ledger grant and Collections v8 developer-managed consume finalization
  • make microsoft-store a first-class platform, Game Services commerce runtime, and target-config runtime; add CLI submission product mapping validation, legacy starter migration, and starter wiring
  • document the PWA account-linking trust boundary and required paired Cloudflare verifier/finalizer bindings without introducing a new npm package registration boundary

Security model

The browser never grants inventory or consumes a purchase. Digital Goods purchaseToken is treated as a non-unique add-on product ID, not a transaction ID or User Store ID. The server resolves Entra credentials, a renewable player-bound User Store ID, and a separate stable opaque accountBindingId. That stable ID survives User Store ID renewal and changes only on a real Store-account relink. Verification records its domain-separated hash in ledger evidence, then finalization re-resolves and compares it before consume.

Only the dedicated Microsoft Store verifier/finalizer pair may verify Store consumables. Aggregate and insecure development verifier fallbacks fail closed for this target. The client calls PaymentResponse completion only after Store ownership reconciliation and authoritative fulfillment: completed maps to success, explicit failure to fail, and pending/transport uncertainty to unknown.

Checkout remains configuration-required until the game supplies authoritative services and one trusted account-linking strategy: a Windows host bridge, or service-side Microsoft/Xbox OAuth with delegated X-tokens. Non-RETAIL developer-managed consume fails closed until delegated XSTS support exists.

Validation

  • pnpm check
  • focused browser adapter tests: 31 passed
  • Microsoft Store Game Services verification, renewal/relink, ledger, consume, and recovery tests
  • Worker HTTP/oRPC/D1 tests, including aggregate and development fallback rejection
  • CLI submission preflight with effective-target SHA-256 evidence
  • CLI starter onboarding through real source-checkout game creation and legacy ^0.5.1 to ^0.6.0 migration
  • repeated OCR workspace and commit review loops; final commit review has 0 findings
  • latest-head GitHub CI passed across validate/test, Microsoft Store, AIT, Devvit, iOS, and Android builds and smokes, including native storage conformance

Release and merge notes

  • Rebase this PR onto latest main before merge if other pending kit PRs land first, then rerun the full suite.
  • Microsoft Store commerce ships through the already-published @mpgd/adapter-browser package, so no initial npm package registration or separate OIDC setup is required.
  • The Sampo changeset includes @mpgd/adapter-browser and @mpgd/cli alongside the catalog, Game Services, platform, and target-config contracts.
  • Sampo release dry-run is branch-restricted to main, so the actual release calculation must run on the release branch.

Downstream blocker

TtokDoku can enable checkout after Partner Center creates two developer-managed consumable add-ons with distinct Collections Store IDs, and the game service has secure Entra plus stable player-to-Store-account linking. A Store-installed PWA cannot derive that identity from Digital Goods alone, so the game repository remains fail-closed until those values and the account-linking strategy exist.

Summary by CodeRabbit

  • New Features
    • Added Microsoft Store Digital Goods discovery, consumable purchases, payment flows, entitlement restoration, and authoritative Collections-based fulfillment.
    • Added Microsoft Store targets, catalog mappings, starter templates, CLI onboarding, submission product mappings, and leaderboard support.
    • Added localized pricing, pending purchases, purchase recovery, and duplicate-safe processing.
  • Bug Fixes
    • Checkout now fails safely when configuration or backend verification is unavailable.
    • Improved recovery for interrupted or previously processed purchases.
  • Documentation
    • Added Microsoft Store setup, authentication, account-binding, submission, and deployment guidance.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@imjlk, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ce133c0a-4d68-4e4d-baf0-5533a7c6fcd1

📥 Commits

Reviewing files that changed from the base of the PR and between 4ee8d73 and b94bd04.

📒 Files selected for processing (36)
  • .sampo/changesets/microsoft-store-iap.md
  • README.md
  • adapters/ait/src/host.test.ts
  • adapters/ait/src/host.ts
  • adapters/ait/src/index.ts
  • adapters/browser/src/microsoft-store.test.ts
  • adapters/browser/src/microsoft-store.ts
  • adapters/devvit/src/index.test.ts
  • adapters/devvit/src/index.ts
  • adapters/devvit/src/payments.test.ts
  • adapters/verse8/src/index.test.ts
  • apps/target-devvit/src/server/index.ts
  • docs/MICROSOFT_STORE_COMMERCE.md
  • docs/VERIFIED_LEADERBOARD_SERVICE.md
  • examples/phaser-starter/src/platform/buildGateways/microsoftStore.ts
  • native-plugins/capacitor-game-services/android/src/main/java/dev/mpgd/capacitor/CapacitorGameServicesPlugin.java
  • native-plugins/capacitor-game-services/ios/Sources/CapacitorGameServices/CapacitorGameServicesPlugin.swift
  • packages/bridge/src/cloudflare-pages.ts
  • packages/cli/src/microsoft-store-package-generation-input.ts
  • packages/cli/src/microsoft-store-submission.ts
  • packages/cli/templates/phaser-game/apps/target-devvit/README.md
  • packages/cli/templates/phaser-game/apps/target-devvit/src/server/index.ts
  • packages/cli/templates/phaser-game/src/platform/buildGateways/microsoftStore.ts
  • packages/game-services/src/client.test.ts
  • packages/game-services/src/microsoft-store-purchase.test.ts
  • packages/game-services/src/microsoft-store-purchase.ts
  • packages/game-services/src/runtime.test.ts
  • packages/platform/src/capability-conformance.ts
  • packages/platform/src/index.ts
  • packages/platform/test/capability-conformance-dist-import.mjs
  • packages/target-config/src/runtime.ts
  • packages/target-config/test/target-config-smoke.ts
  • tools/smoke/adapter-effective-config.ts
  • tools/smoke/cli-microsoft-store-package-generation.ts
  • tools/smoke/platform-capability-conformance.ts
  • tools/smoke/target-config-runtime.ts
📝 Walkthrough

Walkthrough

Microsoft Store support now includes a dedicated Digital Goods and Payment Request adapter, authoritative Collections verification and consumption, target configuration, CLI submission checks, starter migration, worker bindings, and purchase recovery.

Changes

Microsoft Store IAP

Layer / File(s) Summary
Commerce contracts and target configuration
packages/platform/*, packages/game-services/src/types.ts, packages/target-config/*, packages/catalog/catalog.json, tools/target/*
The Microsoft Store target, consumable mappings, commerce settings, and adapter validation are defined.
Browser checkout and recovery
adapters/browser/src/microsoft-store.*, adapters/browser/package.json
The adapter queries Digital Goods, runs Payment Request checkout, preserves evidence, restores purchases, and reports pending or failed outcomes.
Collections fulfillment and worker integration
packages/game-services/src/microsoft-store-purchase.*, packages/game-services/src/server.ts, apps/game-services-worker/src/*
The backend queries and consumes Collections items, performs retry-safe finalization, supports recovery, and requires paired Microsoft Store worker bindings.
CLI submission and starter migration
packages/cli/src/*, packages/cli/templates/phaser-game/*, tools/smoke/cli-microsoft-store-*
CLI preflight validates commerce products and effective-target hashes. Starter initialization migrates targets, dependencies, runtime detection, and gateway files.
Gateway, runtime, and conformance integration
examples/phaser-starter/*, tools/smoke/*, .github/workflows/ci.yml, tsconfig.base.json
Examples, templates, runtime mapping, smoke checks, CI, and compiler paths use the dedicated Microsoft Store adapter.
Documentation and release metadata
README.md, docs/*, adapters/browser/CHANGELOG.md, .sampo/changesets/*
Microsoft Store setup, production requirements, API references, and release metadata are documented.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Poem

A rabbit checks the Store with care,
Digital goods hop through the air.
Collections grant, then consume,
Safe retries leave no duplicate bloom.
Targets and gateways line up just right—
The burrow ships commerce tonight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the main change: authoritative Microsoft Store PWA IAP support.
Description check ✅ Passed The description covers the change, security model, validation, release notes, and downstream requirements in sufficient detail.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/microsoft-store-iap

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (5)
README.md (1)

257-264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the Store-installed runtime requirement.

The Microsoft billing method is available only in a PWA installed from the Microsoft Store on Windows. A normal browser can make getDigitalGoodsService() unavailable. Add this constraint to the README or make it explicit at the linked commerce guide. (learn.microsoft.com)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 257 - 264, Update the Microsoft Store support
documentation around the build and commerce adapter description to state that
Microsoft billing requires the PWA to be installed from the Microsoft Store on
Windows. Clarify that normal browser execution may not provide
getDigitalGoodsService(), and place the constraint in README.md or the linked
Microsoft Store commerce guide.
tools/smoke/effective-target-config.ts (1)

374-386: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Guard against a vacuous consumable assertion.

filter(...).every(...) returns true when the filtered list is empty. If the Microsoft Store target later maps no consumable products, this check still passes. Assert that at least one consumable exists.

♻️ Proposed guard
   if (target === 'microsoft-store') {
+    const consumables = config.monetization.products.filter(
+      (product) => product.type === 'consumable',
+    );
     assertEqual(
-      config.monetization.products.filter((product) => product.type === 'consumable')
-        .every((product) => product.enabled),
+      consumables.length > 0 && consumables.every((product) => product.enabled),
       true,
       'microsoft-store consumables should be enabled',
     );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/smoke/effective-target-config.ts` around lines 374 - 386, Update the
microsoft-store validation block to assert that the filtered consumable products
collection is non-empty before checking that every consumable is enabled.
Preserve the existing all-enabled assertion and its message, using the
collection derived from config.monetization.products.
packages/game-services/src/microsoft-store-purchase.test.ts (1)

186-234: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the pending verification paths.

The suite covers MICROSOFT_STORE_PRODUCT_KIND_MISMATCH, the sandbox block, and consume recovery. Three reachable outcomes stay uncovered:

  • MICROSOFT_STORE_PRODUCT_MAPPING_REQUIRED, when storeIds has no entry for the catalog product.
  • MICROSOFT_STORE_PURCHASE_NOT_PROPAGATED, when the query returns items: [].
  • MICROSOFT_STORE_COLLECTIONS_UNAVAILABLE, when queryProduct rejects.

The third case matters most. It asserts that a transport failure produces pending and records no ledger entry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/game-services/src/microsoft-store-purchase.test.ts` around lines 186
- 234, Extend the Microsoft Store purchase verification tests with cases for
missing product mapping, an empty query result, and a rejected queryProduct
call. Assert the corresponding reasons MICROSOFT_STORE_PRODUCT_MAPPING_REQUIRED,
MICROSOFT_STORE_PURCHASE_NOT_PROPAGATED, and
MICROSOFT_STORE_COLLECTIONS_UNAVAILABLE; for the transport failure, also assert
pending finalization and no ledger event.
tools/smoke/cli-microsoft-store-submission.ts (1)

204-232: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for an empty commerce.products array.

The parser rejects an empty product list at packages/cli/src/microsoft-store-submission.ts lines 369-371. No smoke case exercises that branch. Add one expectConfigError case with products: [] and the expected message at least one product.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/smoke/cli-microsoft-store-submission.ts` around lines 204 - 232, Add an
expectConfigError smoke case near the existing commerceConfig coverage,
configuring Microsoft Store commerce with products: [] and asserting the
expected validation message contains “at least one product”.
adapters/microsoft-store/src/index.ts (1)

226-253: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unreachable fail completion branch.

Line 226 sets paymentCompletionAttempted = true immediately after response is assigned, with no await between the two statements. Therefore response !== undefined && !paymentCompletionAttempted at Line 247 can never be true. The branch is dead code and hides the real invariant: the response is always completed with 'success' at Line 227.

If you intend to keep a fail path for a future reordering, set the flag only after completePayment resolves.

♻️ Proposed simplification
         paymentCompletionAttempted = true;
         await completePayment(response, 'success');
         input.onError?.(error);
-        if (response !== undefined && !paymentCompletionAttempted) {
-          await completePayment(response, 'fail');
-        }
         if (purchaseToken !== undefined) {

Then drop the now-unused paymentCompletionAttempted declaration at Line 211.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/microsoft-store/src/index.ts` around lines 226 - 253, Remove the
unreachable failure-completion branch from the catch block and delete the
now-unused paymentCompletionAttempted declaration. Keep
completePayment(response, 'success') as the sole completion path, preserving the
existing error and pending-purchase returns.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adapters/microsoft-store/src/index.ts`:
- Around line 370-379: Update the price validation near currencyCode and
numericValue so empty or whitespace-only price.value strings are rejected before
Number coercion; validate the raw value first, then convert and retain the
existing finite and non-negative checks for valid numeric prices.

In `@docs/MICROSOFT_STORE_COMMERCE.md`:
- Around line 47-50: Update the sandbox sentence in the documentation to replace
“needs delegated XSTS authentication” with “requires delegated XSTS
authentication,” leaving the surrounding behavior and error description
unchanged.

In `@packages/cli/templates/phaser-game/mpgd.targets.json`:
- Around line 13-14: Update the microsoft-store target configuration in
mpgd.targets.json by setting authoritativeGameServices to false, while leaving
the adapter and other target settings unchanged.

In `@packages/cli/templates/phaser-game/package.json`:
- Line 63: Publish `@mpgd/adapter-microsoft-store` version 0.1.0 before relying on
the dependency declaration in the Phaser game template, ensuring the CLI’s
replacement of __MPGD_DEPENDENCY_VERSION_ADAPTER_MICROSOFT_STORE__ with ^0.1.0
resolves successfully during installation.

In `@packages/game-services/src/microsoft-store-purchase.ts`:
- Around line 204-221: Ensure credential validation failures are not converted
into transient pending results. In the verification flow around
assertCredentials, move validation outside the catch that handles queryProduct
transport errors, or explicitly propagate TypeError; apply the same correction
in consumeMicrosoftStorePurchase so malformed accessToken, userStoreId, or
sandbox values produce a permanent configuration error rather than
MICROSOFT_STORE_COLLECTIONS_UNAVAILABLE.

---

Nitpick comments:
In `@adapters/microsoft-store/src/index.ts`:
- Around line 226-253: Remove the unreachable failure-completion branch from the
catch block and delete the now-unused paymentCompletionAttempted declaration.
Keep completePayment(response, 'success') as the sole completion path,
preserving the existing error and pending-purchase returns.

In `@packages/game-services/src/microsoft-store-purchase.test.ts`:
- Around line 186-234: Extend the Microsoft Store purchase verification tests
with cases for missing product mapping, an empty query result, and a rejected
queryProduct call. Assert the corresponding reasons
MICROSOFT_STORE_PRODUCT_MAPPING_REQUIRED,
MICROSOFT_STORE_PURCHASE_NOT_PROPAGATED, and
MICROSOFT_STORE_COLLECTIONS_UNAVAILABLE; for the transport failure, also assert
pending finalization and no ledger event.

In `@README.md`:
- Around line 257-264: Update the Microsoft Store support documentation around
the build and commerce adapter description to state that Microsoft billing
requires the PWA to be installed from the Microsoft Store on Windows. Clarify
that normal browser execution may not provide getDigitalGoodsService(), and
place the constraint in README.md or the linked Microsoft Store commerce guide.

In `@tools/smoke/cli-microsoft-store-submission.ts`:
- Around line 204-232: Add an expectConfigError smoke case near the existing
commerceConfig coverage, configuring Microsoft Store commerce with products: []
and asserting the expected validation message contains “at least one product”.

In `@tools/smoke/effective-target-config.ts`:
- Around line 374-386: Update the microsoft-store validation block to assert
that the filtered consumable products collection is non-empty before checking
that every consumable is enabled. Preserve the existing all-enabled assertion
and its message, using the collection derived from config.monetization.products.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 53e1b9ce-e65b-40a8-bea6-61c62a2d483b

📥 Commits

Reviewing files that changed from the base of the PR and between 48187e4 and a8ba2bb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (45)
  • .sampo/changesets/microsoft-store-iap.md
  • README.md
  • adapters/microsoft-store/CHANGELOG.md
  • adapters/microsoft-store/package.json
  • adapters/microsoft-store/src/index.test.ts
  • adapters/microsoft-store/src/index.ts
  • adapters/microsoft-store/tsconfig.json
  • apps/game-services-worker/src/handler.ts
  • apps/game-services-worker/src/index.test.ts
  • docs/MICROSOFT_STORE_COMMERCE.md
  • docs/PUBLIC_RELEASE_CHECKLIST.md
  • examples/phaser-starter/mpgd.targets.json
  • examples/phaser-starter/package.json
  • examples/phaser-starter/src/platform/buildGateways/microsoftStore.ts
  • examples/phaser-starter/vite.shared.ts
  • packages/catalog/catalog.json
  • packages/cli/package.json
  • packages/cli/src/index.ts
  • packages/cli/src/microsoft-store-starter.ts
  • packages/cli/src/microsoft-store-submission.ts
  • packages/cli/src/production-target-readiness.ts
  • packages/cli/templates/phaser-game/README.md
  • packages/cli/templates/phaser-game/mpgd.targets.json
  • packages/cli/templates/phaser-game/package.json
  • packages/cli/templates/phaser-game/src/platform/buildGateways/microsoftStore.ts
  • packages/cli/templates/phaser-game/vite.shared.ts
  • packages/game-services/package.json
  • packages/game-services/src/client.test.ts
  • packages/game-services/src/client.ts
  • packages/game-services/src/index.ts
  • packages/game-services/src/microsoft-store-purchase.test.ts
  • packages/game-services/src/microsoft-store-purchase.ts
  • packages/game-services/src/server.ts
  • packages/game-services/src/types.ts
  • packages/platform/src/index.ts
  • packages/target-config/src/effective.ts
  • packages/target-config/targets.json
  • tools/smoke/adapter-effective-config.ts
  • tools/smoke/cli-microsoft-store-starter-onboarding.ts
  • tools/smoke/cli-microsoft-store-submission.ts
  • tools/smoke/effective-target-config.ts
  • tools/smoke/game-owned-catalog.ts
  • tools/smoke/platform-capability-conformance.ts
  • tools/smoke/target-config-runtime.ts
  • tools/target/effective-config.ts

Comment thread adapters/browser/src/microsoft-store.ts Resolved
Comment thread docs/MICROSOFT_STORE_COMMERCE.md Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a8ba2bb59c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread adapters/microsoft-store/package.json Resolved
@imjlk

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Please review exact head 3d49723ebe9857ba9a5e1bd863fad50e7a757843, including the Microsoft Store commerce boundary and the clean-CI source-resolution fix.

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

Fixed the clean-CI pnpm check failure in 3d49723ebe9857ba9a5e1bd863fad50e7a757843. The new @mpgd/adapter-browser/microsoft-store export now has an explicit source path mapping, so target workspaces can type-check before package dist exists. Local pnpm check passes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d49723ebe

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/game-services/src/client.ts Resolved
Comment thread packages/platform/src/index.ts Resolved
@imjlk

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Please re-review exact head b3ab0175db66345bb166b81a5e4eaee64fb8eaa5 after the five prior review fixes, OCR fix loops, and green full CI. Focus on the authoritative Store boundary, account-bound consume, starter migrations, and submission artifact binding.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b3ab0175db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/index.ts Resolved
Comment thread adapters/browser/src/microsoft-store.ts Resolved
@imjlk

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@imjlk

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Please re-review exact head 4cd2c59d272a22e2ce4e940622f0878a017c8482 after resolving all five latest findings. Focus on the stable Store account-link binding, paired verifier/finalizer routing (including aggregate/development fail-closed behavior), post-authority PaymentResponse completion, effective-target evidence digest, and source-checkout adapter release floor.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4cd2c59d27

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@codex review

Please review the exact current head b33fd0d8 after the final Microsoft Store dependency-floor, package-evidence integrity, and single-unit entitlement fixes.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

return input.configTarget === 'microsoft-store' && !input.debug;

P2 Badge Recognize custom targets that use the Store adapter

For a configured target such as microsoft-store-staging with kind: 'web' and adapter: 'microsoft-store', appTargetForPlatformTarget() sets APP_TARGET to microsoft-store while MPGD_CONFIG_TARGET remains the custom target name. This exact-name check therefore skips service-worker registration even though Vite selected the Store gateway; tools/target/build-target.ts:213-223 likewise omits the Store PWA artifacts for that target. Classify Store PWA behavior from the adapter/runtime, or reject custom target names using this adapter, rather than producing a Store gateway artifact without its offline/update boundary.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/platform/src/index.ts Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
adapters/browser/src/microsoft-store.test.ts (1)

274-285: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Reuse memoryRecoveryStorage for the inline storage fixtures.

These three tests re-implement the same getItem/setItem/removeItem map adapter. memoryRecoveryStorage already returns both the backing values map and the storage object, so the assertions on storedRecoveryIds/storedRecoveries still work through recovery.values.

♻️ Proposed change for lines 274-285
-    const storedRecoveryIds = new Map<string, string>();
-    const recoveryIdStorage = {
-      getItem(key: string) {
-        return storedRecoveryIds.get(key) ?? null;
-      },
-      setItem(key: string, value: string) {
-        storedRecoveryIds.set(key, value);
-      },
-      removeItem(key: string) {
-        storedRecoveryIds.delete(key);
-      },
-    };
+    const { values: storedRecoveryIds, storage: recoveryIdStorage } = memoryRecoveryStorage();

Also applies to: 410-421, 1058-1068

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/browser/src/microsoft-store.test.ts` around lines 274 - 285, Replace
the duplicated inline recovery storage adapters in the affected tests with the
existing memoryRecoveryStorage helper, destructuring its storage object and
values map as needed. Update assertions and references from
storedRecoveryIds/storedRecoveries to the corresponding recovery.values map
while preserving each test’s behavior.
packages/game-services/src/microsoft-store-purchase.ts (1)

531-540: 🗄️ Data Integrity & Integration | 🔵 Trivial

Plan an expiry for ownership records that survive a failed release.

The rationale in the comment is correct: a stale same-player claim lets that player repurchase, and it stays closed for every other player. Consider the follow-on state. The record key is [accountBindingHash, storeId] and the value is the owning playerId. After a failed release, a second game player who links the same Store account cannot claim that product, because claim compares the stored owner against the new playerId.

Add a TTL or a reconciliation job to the durable MicrosoftStoreRecoveryOwnershipStore implementation, and record a metric for release failures. Document that requirement next to the durability requirement in docs/MICROSOFT_STORE_COMMERCE.md Lines 86-88.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/game-services/src/microsoft-store-purchase.ts` around lines 531 -
540, Extend the durable MicrosoftStoreRecoveryOwnershipStore implementation with
expiry or reconciliation for ownership records left behind after release
failures, and emit a metric whenever release fails in the finalization flow
around recoveryOwnershipStore.release. Document both the cleanup requirement and
release-failure metric beside the existing durability requirement in the
Microsoft Store commerce documentation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/game-services/src/microsoft-store-purchase.ts`:
- Around line 309-331: The recovery ownership flow conflates denial with service
unavailability. In
packages/game-services/src/microsoft-store-purchase.ts:309-331, widen
claimRecoveryOwnership and hasRecoveryOwnership to return granted, denied, or
unavailable, and have resolveRecoveryOwnership return unavailable when
resolveCredentials, assertCredentials, or createAccountBindingHash fails. In
adapters/browser/src/microsoft-store.ts:241-264, have authorizeRecovery return
that state instead of throwing; update fulfill so recovery records are released
on denied and retained only on unavailable.

In `@packages/target-config/targets.json`:
- Around line 50-53: Update the reddit target configuration’s
features.leaderboard setting to true so it matches reddit’s supported Game
Services leaderboard status; alternatively, remove reddit from the supported
leaderboard target set, but keep the configuration and supported-target
declarations consistent.

---

Nitpick comments:
In `@adapters/browser/src/microsoft-store.test.ts`:
- Around line 274-285: Replace the duplicated inline recovery storage adapters
in the affected tests with the existing memoryRecoveryStorage helper,
destructuring its storage object and values map as needed. Update assertions and
references from storedRecoveryIds/storedRecoveries to the corresponding
recovery.values map while preserving each test’s behavior.

In `@packages/game-services/src/microsoft-store-purchase.ts`:
- Around line 531-540: Extend the durable MicrosoftStoreRecoveryOwnershipStore
implementation with expiry or reconciliation for ownership records left behind
after release failures, and emit a metric whenever release fails in the
finalization flow around recoveryOwnershipStore.release. Document both the
cleanup requirement and release-failure metric beside the existing durability
requirement in the Microsoft Store commerce documentation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bccca8dc-57fb-4512-845b-906d8b628d44

📥 Commits

Reviewing files that changed from the base of the PR and between 0c17efb and 4ee8d73.

📒 Files selected for processing (18)
  • .sampo/changesets/microsoft-store-iap.md
  • adapters/browser/src/microsoft-store.test.ts
  • adapters/browser/src/microsoft-store.ts
  • docs/MICROSOFT_STORE_COMMERCE.md
  • examples/phaser-starter/src/platform/buildGateways/microsoftStore.ts
  • packages/catalog/catalog.json
  • packages/cli/templates/phaser-game/src/platform/buildGateways/microsoftStore.ts
  • packages/game-services/src/client.test.ts
  • packages/game-services/src/client.ts
  • packages/game-services/src/microsoft-store-purchase.test.ts
  • packages/game-services/src/microsoft-store-purchase.ts
  • packages/game-services/src/server.ts
  • packages/game-services/src/types.ts
  • packages/target-config/targets.json
  • tools/smoke/adapter-effective-config.ts
  • tools/smoke/platform-capability-conformance.ts
  • tools/smoke/target-config-runtime.ts
  • tools/validate-target-config.ts
💤 Files with no reviewable changes (1)
  • packages/game-services/src/server.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/game-services/src/client.ts
  • packages/catalog/catalog.json
  • packages/cli/templates/phaser-game/src/platform/buildGateways/microsoftStore.ts
  • tools/smoke/adapter-effective-config.ts
  • tools/smoke/target-config-runtime.ts
  • tools/smoke/platform-capability-conformance.ts
  • .sampo/changesets/microsoft-store-iap.md

Comment thread packages/target-config/targets.json Resolved

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4ee8d73fae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread adapters/browser/src/microsoft-store.ts Resolved

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@codex review exact head 789e0bc51a33d0f9e540ebc782d4a698cf56566f. Please re-review the complete PR after both category-level fixes: authenticated durable Store recovery ownership and the separate native/remote leaderboard capability model. Focus on trusted token mappings, cross-player claim atomicity, dynamic target availability, real Microsoft Store capability discovery, and complete TS/native bridge snapshots.

@coderabbitai review exact head 789e0bc51a33d0f9e540ebc782d4a698cf56566f. Please re-review the same full-PR boundaries.

Exact-head verification: pnpm check; lint; browser/platform 26/26 targeted tests; full Game Services suite; target-config, adapter-effective-config, and platform-capability conformance smokes; clean Node 24 Microsoft Store production build and artifact smoke. OCR delegate accounted for all changed source files and excluded tests/docs were reviewed manually. All review threads are resolved.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='789e0bc51a33d0f9e540ebc782d4a698cf56566f'
previous='4ee8d73fae1faddbd3e27d1381e2d62b4d4d3fef'
actual="$(git rev-parse HEAD)"
printf 'HEAD=%s\nEXPECTED=%s\nPREVIOUS=%s\n' "$actual" "$expected" "$previous"
test "$actual" = "$expected"

printf '\n=== Complete PR path list and whitespace validation ===\n'
git diff --name-status main...HEAD
git diff --check main...HEAD

printf '\n=== Incremental path list and patch ===\n'
git diff --name-status "$previous...HEAD"
git diff --unified=90 "$previous...HEAD" -- \
  adapters/browser/src/microsoft-store.ts \
  adapters/browser/src/microsoft-store.test.ts \
  adapters/browser/src/index.ts \
  packages/platform/src/index.ts \
  packages/target-config/src/runtime.ts \
  packages/target-config/src/effective.ts \
  packages/target-config/targets.json \
  packages/game-services/src/microsoft-store-purchase.ts \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/game-services/src/server.ts \
  tools/smoke/target-config-runtime.ts \
  tools/smoke/platform-capability-conformance.ts

printf '\n=== Focused outlines ===\n'
ast-grep outline adapters/browser/src/microsoft-store.ts --items all
ast-grep outline adapters/browser/src/index.ts --items all
ast-grep outline packages/target-config/src/runtime.ts --items all --match 'capability|Capability|leaderboard|Leaderboard|availability|Availability'
ast-grep outline packages/game-services/src/microsoft-store-purchase.ts --items all --match 'recovery|Recovery|owner|Owner|claim|Claim|mapping|Mapping|final'
ast-grep outline packages/game-services/src/server.ts --items all --match 'purchase|Purchase|recovery|Recovery|owner|Owner|claim|Claim|ledger|Ledger'

printf '\n=== Requested-boundary references ===\n'
rg -n -C6 \
  'remoteLeaderboard|nativeLeaderboard|leaderboard|capabilit|availability|microsoft-store|DigitalGoods|digitalGoods|recoveryOwnership|claimRecoveryOwnership|hasRecoveryOwnership|releaseRecoveryOwnership|historicalProductMappings|historical.*token|inAppOfferToken|storeId|bridge|snapshot' \
  adapters/browser/src \
  packages/platform/src \
  packages/target-config/src \
  packages/game-services/src \
  tools/smoke \
  -g '*.ts' -g '*.json' \
  -g '!**/dist/**' -g '!**/node_modules/**'

Length of output: 202802


🏁 Script executed:

#!/bin/bash
set -euo pipefail

previous='4ee8d73fae1faddbd3e27d1381e2d62b4d4d3fef'

printf '%s\n' '=== Latest incremental patch paths ==='
git diff --name-status "$previous...HEAD"
git diff --unified=55 "$previous...HEAD" -- \
  packages/platform/src/index.ts \
  adapters/browser/src/index.ts \
  adapters/browser/src/microsoft-store.ts \
  packages/target-config/src/runtime.ts \
  packages/target-config/src/effective.ts \
  packages/target-config/targets.json \
  tools/smoke/target-config-runtime.ts \
  tools/smoke/platform-capability-conformance.ts

printf '%s\n' '=== Platform capability contract ==='
rg -n -C5 \
  'interface PlatformCapabilities|remoteLeaderboard|nativeLeaderboard|createBrowserPlatformGateway|capabilities:' \
  packages/platform/src/index.ts \
  adapters/browser/src/index.ts \
  adapters/browser/src/microsoft-store.ts

printf '%s\n' '=== Dynamic target availability calculation ==='
rg -n -C10 \
  'remoteLeaderboard|nativeLeaderboard|features\.leaderboard|capabilitySupported|withTargetAvailability|leaderboard:' \
  packages/target-config/src/runtime.ts \
  packages/target-config/src/effective.ts \
  packages/target-config/targets.json

printf '%s\n' '=== Real Microsoft Store gateway construction sites ==='
rg -n -C8 \
  'createMicrosoftStoreCommerceAdapter|withMicrosoftStoreCommerceAdapter|remoteLeaderboard|nativeLeaderboard|createBuildGateway' \
  examples/phaser-starter/src \
  packages/cli/templates/phaser-game/src \
  tools/smoke/adapter-effective-config.ts \
  tools/smoke/platform-capability-conformance.ts \
  tools/smoke/target-config-runtime.ts \
  -g '*.ts'

printf '%s\n' '=== Capability and configured-target regression tests ==='
rg -n -C10 \
  "reason.*available|capability-unsupported|remoteLeaderboard|nativeLeaderboard|microsoft-store|submitScore" \
  tools/smoke/target-config-runtime.ts \
  tools/smoke/platform-capability-conformance.ts \
  packages/target-config/src \
  -g '*.ts'

printf '%s\n' '=== TypeScript/native bridge and snapshot references ==='
rg -n -C6 \
  'bridge|Bridge|native.*snapshot|snapshot.*native|capability.*snapshot|PlatformCapabilities' \
  adapters \
  packages \
  tools \
  -g '*.ts' \
  -g '!**/dist/**' -g '!**/node_modules/**'

Length of output: 180849


🏁 Script executed:

#!/bin/bash
set -euo pipefail

previous='4ee8d73fae1faddbd3e27d1381e2d62b4d4d3fef'

printf '%s\n' '=== Exact latest patch ==='
git diff --unified=45 "$previous...HEAD" -- \
  packages/platform/src/index.ts \
  packages/platform/src/capability-conformance.ts \
  adapters/browser/src/index.ts \
  adapters/browser/src/microsoft-store.ts \
  packages/target-config/src/runtime.ts \
  packages/target-config/src/effective.ts \
  packages/target-config/targets.json \
  tools/smoke/target-config-runtime.ts \
  tools/smoke/platform-capability-conformance.ts

printf '%s\n' '=== Capability declarations and browser gateway ==='
sed -n '140,175p' packages/platform/src/index.ts
sed -n '313,338p' packages/platform/src/index.ts
rg -n 'function createBrowserPlatformGateway|export function createBrowserPlatformGateway' adapters/browser/src/index.ts
sed -n '1,180p' adapters/browser/src/index.ts

printf '%s\n' '=== Microsoft Store adapter capability wrapper ==='
rg -n 'getCapabilities|remoteLeaderboard|nativeLeaderboard|withMicrosoftStoreCommerceAdapter' \
  adapters/browser/src/microsoft-store.ts
sed -n '540,660p' adapters/browser/src/microsoft-store.ts

printf '%s\n' '=== Target availability and Microsoft Store target policy ==='
sed -n '640,720p' packages/target-config/src/runtime.ts
jq '.targets["microsoft-store"]' packages/target-config/targets.json
sed -n '205,285p' tools/smoke/target-config-runtime.ts
sed -n '450,535p' tools/smoke/target-config-runtime.ts

printf '%s\n' '=== Microsoft Store capability conformance fixture ==='
rg -n -C5 'microsoft-store|remoteLeaderboard|nativeLeaderboard' \
  tools/smoke/platform-capability-conformance.ts
sed -n '1,180p' packages/platform/src/capability-conformance.ts

printf '%s\n' '=== Ownership store atomic key behavior ==='
sed -n '127,180p' packages/game-services/src/microsoft-store-purchase.ts
sed -n '269,345p' packages/game-services/src/microsoft-store-purchase.ts
sed -n '690,715p' packages/game-services/src/microsoft-store-purchase.ts

Length of output: 69411


@imjlk I reviewed exact head 789e0bc51a33d0f9e540ebc782d4a698cf56566f.

I found one actionable issue.

withMicrosoftStoreCommerceAdapter() sets remoteLeaderboard from commerce.getAvailability() in adapters/browser/src/microsoft-store.ts at Line 560. This couples remote Game Services leaderboard support to Microsoft Store IAP authority availability.

If Store commerce is disabled or configuration-required, remoteLeaderboard becomes false. withTargetAvailability() then blocks submitScore(), even if the game has a configured Game Services leaderboard. This conflicts with the separate native/remote leaderboard model. It also prevents a Store deployment from using leaderboards without IAP.

Set remoteLeaderboard from the configured Game Services leaderboard route or a dedicated remote-leaderboard capability. Do not derive it from commerce.getAvailability().

Add a regression that uses the real Microsoft Store gateway with commerce unavailable and remote leaderboard support enabled. Assert all of these results:

  • nativeLeaderboard === false
  • remoteLeaderboard === true
  • runtime.features.leaderboard.reason === 'available'
  • submitScore() delegates to Game Services

The current configured-target smoke does not detect this defect. It uses a synthetic gateway that sets remoteLeaderboard: true directly.

The trusted token mapping and ownership paths are fail-closed in the reviewed code. The ownership key binds the server-derived account-binding hash and resolved Collections Store ID. The ownership store contract requires atomic claims. The capability conformance code also includes remoteLeaderboard in complete snapshot checks.

git diff --check main...HEAD reported no whitespace errors. I did not run repository tests in this sandbox.

You are interacting with an AI system.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 789e0bc51a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

Re-review requested for exact head 565340f6326eb2793f4657fd3a14ca77a30dcde1.

@codex review
@coderabbitai review

Please review the complete recovery-ownership protocol and capability/release snapshot changes at this exact SHA.

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

Correction: the exact pushed head is 565340f6490afcebe64bed30560ab7f628d015e5 (the abbreviated 565340f6 cited in the thread replies is correct). Please anchor re-review to this full SHA.

@codex review

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='565340f6326eb2793f4657fd3a14ca77a30dcde1'
previous='789e0bc51a33d0f9e540ebc782d4a698cf56566f'
actual="$(git rev-parse HEAD)"

printf 'HEAD=%s\nEXPECTED=%s\nPREVIOUS=%s\n' "$actual" "$expected" "$previous"
test "$actual" = "$expected"

printf '\n=== Full PR paths and whitespace validation ===\n'
git diff --name-status main...HEAD
git diff --check main...HEAD

printf '\n=== Changes since the previous review ===\n'
git diff --name-status "$previous...HEAD"
git diff --unified=80 "$previous...HEAD" -- \
  adapters/browser/src/microsoft-store.ts \
  adapters/browser/src/microsoft-store.test.ts \
  adapters/browser/src/index.ts \
  packages/platform/src/index.ts \
  packages/platform/src/capability-conformance.ts \
  packages/target-config/src/runtime.ts \
  packages/target-config/src/effective.ts \
  packages/game-services/src/microsoft-store-purchase.ts \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/game-services/src/server.ts \
  packages/game-services/src/server.test.ts \
  tools/smoke/target-config-runtime.ts \
  tools/smoke/platform-capability-conformance.ts \
  .sampo/changesets/microsoft-store-iap.md \
  adapters/browser/CHANGELOG.md

printf '\n=== Focused source outlines ===\n'
ast-grep outline adapters/browser/src/microsoft-store.ts --items all
ast-grep outline packages/game-services/src/microsoft-store-purchase.ts --items all
ast-grep outline packages/target-config/src/runtime.ts --items all --match 'capability|Capability|leaderboard|Leaderboard|availability|Availability'
ast-grep outline packages/platform/src/capability-conformance.ts --items all

printf '\n=== Recovery ownership, remote leaderboard, and snapshot references ===\n'
rg -n -C6 \
  'recoveryOwnership|RecoveryOwnership|claimRecoveryOwnership|hasRecoveryOwnership|releaseRecoveryOwnership|owner|reservation|historicalProductMappings|remoteLeaderboard|nativeLeaderboard|commerce\.getAvailability|capabilitySnapshot|snapshot|release|CHANGELOG|changeset' \
  adapters/browser/src \
  packages/game-services/src \
  packages/platform/src \
  packages/target-config/src \
  tools/smoke \
  .sampo/changesets \
  -g '*.ts' -g '*.md' \
  -g '!**/dist/**' -g '!**/node_modules/**'

printf '\n=== Microsoft Store gateway construction sites ===\n'
rg -n -C5 \
  'createMicrosoftStoreCommerceAdapter\s*\(|withMicrosoftStoreCommerceAdapter\s*\(|remoteLeaderboard|createBuildGateway' \
  examples \
  packages/cli/templates \
  tools/smoke \
  adapters/browser/src \
  -g '*.ts' \
  -g '!**/dist/**' -g '!**/node_modules/**'

Length of output: 3031


🏁 Script executed:

#!/bin/bash
set -euo pipefail

requested='565340f6326eb2793f4657fd3a14ca77a30dcde1'
checked_out='565340f6490afcebe64bed30560ab7f628d015e5'

printf '=== Requested commit availability ===\n'
git cat-file -e "${requested}^{commit}" && printf 'requested commit is available locally\n' || printf 'requested commit is not available locally\n'

printf '\n=== Checked-out commit ===\n'
git show -s --format='commit=%H%nparents=%P%nsubject=%s%ncommitter=%cn <%ce>%ncommitter_date=%cI' "$checked_out"

if git cat-file -e "${requested}^{commit}" 2>/dev/null; then
  printf '\n=== Requested commit ===\n'
  git show -s --format='commit=%H%nparents=%P%nsubject=%s%ncommitter=%cn <%ce>%ncommitter_date=%cI' "$requested"

  printf '\n=== Tree comparison ===\n'
  printf 'requested_tree=%s\n' "$(git show -s --format=%T "$requested")"
  printf 'checked_out_tree=%s\n' "$(git show -s --format=%T "$checked_out")"

  printf '\n=== Commit relationship ===\n'
  git merge-base --is-ancestor "$requested" "$checked_out" && printf 'requested is an ancestor of checked-out\n' || true
  git merge-base --is-ancestor "$checked_out" "$requested" && printf 'checked-out is an ancestor of requested\n' || true
fi

Length of output: 1918


@imjlk I could not verify the requested exact head.

The sandbox checked out 565340f6490afcebe64bed30560ab7f628d015e5.

The requested commit 565340f6326eb2793f4657fd3a14ca77a30dcde1 is not available from the repository. Git returned upload-pack: not our ref.

Please retry the review with the reachable exact SHA. I did not review the different checked-out commit.

You are interacting with an AI system.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 565340f649

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@codex review
@coderabbitai review

Please re-review exact head 53e93edf170c2756414a15ff8958338b79b05499. The three newly raised findings were addressed together with provider-purchase-bound recovery ownership, effective-target commerce revalidation, and the documented Collections consume response shape.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='53e93edf170c2756414a15ff8958338b79b05499'
previous='565340f6490afcebe64bed30560ab7f628d015e5'
actual="$(git rev-parse HEAD)"

printf 'HEAD=%s\nEXPECTED=%s\nPREVIOUS=%s\n' "$actual" "$expected" "$previous"
test "$actual" = "$expected"

printf '\n=== Commit relationship ===\n'
git merge-base --is-ancestor "$previous" "$actual" && printf 'previous is an ancestor of HEAD\n'

printf '\n=== Full PR and incremental changed paths ===\n'
git diff --name-status main...HEAD
git diff --name-status "$previous...HEAD"
git diff --check main...HEAD

printf '\n=== Incremental focused patch ===\n'
git diff --unified=100 "$previous...HEAD" -- \
  adapters/browser/src/microsoft-store.ts \
  adapters/browser/src/microsoft-store.test.ts \
  packages/game-services/src/microsoft-store-purchase.ts \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/game-services/src/server.ts \
  packages/game-services/src/server.test.ts \
  packages/cli/src/microsoft-store-submission.ts \
  packages/cli/src/microsoft-store-package-generation-input.ts \
  packages/cli/src/microsoft-store-package-generation-contract.ts \
  tools/smoke/cli-microsoft-store-submission.ts \
  tools/smoke/cli-microsoft-store-package-generation.ts \
  docs/MICROSOFT_STORE_COMMERCE.md

printf '\n=== Focused source outlines ===\n'
ast-grep outline adapters/browser/src/microsoft-store.ts --items all
ast-grep outline packages/game-services/src/microsoft-store-purchase.ts --items all
ast-grep outline packages/cli/src/microsoft-store-submission.ts --items all
ast-grep outline packages/cli/src/microsoft-store-package-generation-input.ts --items all

printf '\n=== Ownership, commerce-evidence, and consume-response references ===\n'
rg -n -C6 \
  'recoveryOwnership|RecoveryOwnership|claimRecoveryOwnership|hasRecoveryOwnership|releaseRecoveryOwnership|purchaseToken|platformTransactionId|itemId|effectiveTarget|commerce|sha256|consume|Collections|response|status|trackingId' \
  adapters/browser/src/microsoft-store.ts \
  adapters/browser/src/microsoft-store.test.ts \
  packages/game-services/src/microsoft-store-purchase.ts \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/game-services/src/server.ts \
  packages/game-services/src/server.test.ts \
  packages/cli/src/microsoft-store-submission.ts \
  packages/cli/src/microsoft-store-package-generation-input.ts \
  packages/cli/src/microsoft-store-package-generation-contract.ts \
  tools/smoke/cli-microsoft-store-submission.ts \
  tools/smoke/cli-microsoft-store-package-generation.ts \
  docs/MICROSOFT_STORE_COMMERCE.md \
  -g '*.ts' -g '*.md'

printf '\n=== Construction sites and public contracts ===\n'
rg -n -C5 \
  'createMicrosoftStorePurchaseBoundary\s*\(|createMicrosoftStoreCommerceAdapter\s*\(|claimRecoveryOwnership\s*\(|MicrosoftStoreRecoveryOwnership' \
  . -g '*.ts' -g '*.md' -g '!**/dist/**' -g '!**/node_modules/**'

Length of output: 203475


🏁 Script executed:

#!/bin/bash
set -euo pipefail

previous='565340f6490afcebe64bed30560ab7f628d015e5'

printf '%s\n' '=== Exact incremental file list ==='
git diff --name-status "$previous...HEAD"

printf '%s\n' '=== Exact incremental patches ==='
git diff --unified=50 "$previous...HEAD" -- \
  packages/game-services/src/microsoft-store-purchase.ts \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/cli/src/microsoft-store-submission.ts \
  packages/cli/src/microsoft-store-package-generation-input.ts \
  packages/cli/src/microsoft-store-package-generation-contract.ts \
  tools/smoke/cli-microsoft-store-submission.ts \
  tools/smoke/cli-microsoft-store-package-generation.ts \
  docs/MICROSOFT_STORE_COMMERCE.md

printf '%s\n' '=== Recovery ownership implementation ==='
nl -ba packages/game-services/src/microsoft-store-purchase.ts | sed -n '300,465p'
nl -ba packages/game-services/src/microsoft-store-purchase.ts | sed -n '480,605p'
nl -ba packages/game-services/src/microsoft-store-purchase.ts | sed -n '625,705p'
nl -ba packages/game-services/src/microsoft-store-purchase.ts | sed -n '787,920p'

printf '%s\n' '=== Ownership and consume regression tests ==='
nl -ba packages/game-services/src/microsoft-store-purchase.test.ts | sed -n '240,370p'
nl -ba packages/game-services/src/microsoft-store-purchase.test.ts | sed -n '450,535p'
rg -n -C8 \
  'newQuantity|CONSUME_RESPONSE|providerPurchaseId|historical.*ownership|legacy.*ownership|claimRecoveryOwnership|hasRecoveryOwnership' \
  packages/game-services/src/microsoft-store-purchase.test.ts

printf '%s\n' '=== Submission effective-target validation ==='
ast-grep outline packages/cli/src/microsoft-store-submission.ts --items all
ast-grep outline packages/cli/src/microsoft-store-package-generation-input.ts --items all
rg -n -C10 \
  'effectiveTarget|commerce|revalid|sha256|snapshot|monetization|inAppOfferToken|storeId' \
  packages/cli/src/microsoft-store-submission.ts \
  packages/cli/src/microsoft-store-package-generation-input.ts \
  packages/cli/src/microsoft-store-package-generation-contract.ts \
  tools/smoke/cli-microsoft-store-submission.ts \
  tools/smoke/cli-microsoft-store-package-generation.ts

printf '%s\n' '=== Documentation: v8 consume response ==='
rg -n -C8 \
  'consume|newQuantity|trackingId|itemId|Collections v8|response' \
  docs/MICROSOFT_STORE_COMMERCE.md \
  README.md

Length of output: 90667


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== Browser authority input and recovery flow ==='
sed -n '70,310p' adapters/browser/src/microsoft-store.ts
sed -n '310,590p' adapters/browser/src/microsoft-store.ts
sed -n '250,390p' adapters/browser/src/microsoft-store.test.ts
sed -n '820,1025p' adapters/browser/src/microsoft-store.test.ts

printf '%s\n' '=== Recovery ownership and consume implementation ==='
sed -n '300,465p' packages/game-services/src/microsoft-store-purchase.ts
sed -n '480,605p' packages/game-services/src/microsoft-store-purchase.ts
sed -n '625,705p' packages/game-services/src/microsoft-store-purchase.ts
sed -n '787,920p' packages/game-services/src/microsoft-store-purchase.ts

printf '%s\n' '=== Tests for provider purchase replacement and consume parsing ==='
sed -n '240,535p' packages/game-services/src/microsoft-store-purchase.test.ts
rg -n -C10 \
  'nextConsumeResponse|CONSUME_RESPONSE|newQuantity|itemId|transactionId|trackingId|providerPurchaseId|fresh-generation|stale-generation' \
  packages/game-services/src/microsoft-store-purchase.test.ts

printf '%s\n' '=== Complete effective-target validator and generation revalidation ==='
sed -n '250,390p' packages/cli/src/microsoft-store-submission.ts
sed -n '180,330p' packages/cli/src/microsoft-store-package-generation-input.ts
rg -n -C8 \
  'contradictory-disabled|effective target|enableCommerceEvidence|changed while it was parsed|microsoft-store' \
  tools/smoke/cli-microsoft-store-package-generation.ts

Length of output: 104349


🤖 web_search executed:

site:learn.microsoft.com Microsoft Store Collections v8 consume API response itemId transactionId trackingId newQuantity developer-managed consumable

Length of output: 20750


🤖 web_search executed:

site:learn.microsoft.com/en-us/gaming/gdk/docs/store/commerce/service-to-service/microsoft-store-apis/xstore-v8-consume "Response body" "newQuantity" "trackingId"

Length of output: 4451


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '=== Remaining effective-target validator and parsing helpers ==='
sed -n '330,430p' packages/cli/src/microsoft-store-submission.ts
sed -n '300,410p' packages/cli/src/microsoft-store-package-generation-input.ts

printf '%s\n' '=== Documentation consume contract ==='
sed -n '35,115p' docs/MICROSOFT_STORE_COMMERCE.md

printf '%s\n' '=== All current tests that force consume response validation ==='
rg -n -C12 \
  'malformed|MISMATCH|CONSUME_RESPONSE|newQuantity|trackingId|itemId|transactionId' \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  apps/game-services-worker/src/index.test.ts

Length of output: 47561


@imjlk I reviewed exact head 53e93edf170c2756414a15ff8958338b79b05499.

I found one actionable issue.

consumeMicrosoftStorePurchase() no longer verifies raw.productId against context.storeId. The check was removed at Line 674 in packages/game-services/src/microsoft-store-purchase.ts.

Collections v8 specifies productId as a required consume-response field. The code must validate it with itemId, trackingId, and newQuantity. The local documentation also says to check the returned product.

Without this check, a malformed or mismatched successful response can release the ownership claim and mark finalization complete for a different product.

Restore this condition:

raw.productId !== context.storeId

Update the valid fixture to include productId. Add a regression that returns a different productId and expects MICROSOFT_STORE_CONSUME_RESPONSE_MISMATCH.

The provider-purchase ownership binding and effective-target commerce revalidation are otherwise correctly fail-closed. git diff --check main...HEAD reported no whitespace errors. I did not run repository tests in this sandbox.

You are interacting with an AI system.

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@codex review
@coderabbitai review

Please re-review final exact head f44b70ba1d0c6da0e10f39e6340bf2d98ae86d88. Follow-up hardening preserves exact-generation finalizer retries after Collections stops listing a consumed item, while still denying stale generations once a different provider purchase is visible.

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

Correction: the final exact head is f44b70baf56de424e876ccff8d027f93fc34dc1c (f44b70ba). Please use this SHA for the requested Codex and CodeRabbit re-review.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='f44b70ba1d0c6da0e10f39e6340bf2d98ae86d88'
previous='53e93edf170c2756414a15ff8958338b79b05499'
actual="$(git rev-parse HEAD)"

printf 'HEAD=%s\nEXPECTED=%s\nPREVIOUS=%s\n' "$actual" "$expected" "$previous"
test "$actual" = "$expected"

printf '\n=== Incremental changed paths and whitespace validation ===\n'
git diff --name-status "$previous...HEAD"
git diff --check "$previous...HEAD"

printf '\n=== Focused incremental patch ===\n'
git diff --unified=80 "$previous...HEAD" -- \
  packages/game-services/src/microsoft-store-purchase.ts \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/game-services/src/server.ts \
  packages/game-services/src/server.test.ts \
  apps/game-services-worker/src/handler.ts \
  apps/game-services-worker/src/index.test.ts \
  docs/MICROSOFT_STORE_COMMERCE.md

printf '\n=== Focused source outline ===\n'
ast-grep outline packages/game-services/src/microsoft-store-purchase.ts --items all

printf '\n=== Finalization, provider generation, listing, and retry references ===\n'
rg -n -C8 \
  'finaliz|consume|providerPurchaseId|platformTransactionId|purchaseToken|listPurchases|Collections|stale|generation|alreadyProcessed|productId|trackingId|newQuantity' \
  packages/game-services/src/microsoft-store-purchase.ts \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/game-services/src/server.ts \
  packages/game-services/src/server.test.ts \
  apps/game-services-worker/src/handler.ts \
  apps/game-services-worker/src/index.test.ts

printf '\n=== Implementation and targeted regression ranges ===\n'
nl -ba packages/game-services/src/microsoft-store-purchase.ts | sed -n '520,960p'
rg -n -C12 \
  'consumed item|no longer|not listed|stale generation|different provider|provider purchase|retry|finaliz' \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/game-services/src/server.test.ts

printf '\n=== Full-PR whitespace validation ===\n'
git diff --check main...HEAD

Length of output: 2472


@imjlk I could not verify the requested exact head.

The requested SHA is f44b70ba1d0c6da0e10f39e6340bf2d98ae86d88.

The sandbox checked out f44b70baf56de424e876ccff8d027f93fc34dc1c.

These commits share the f44b70ba prefix, but they are different commits. Please provide the reachable exact SHA, or confirm that f44b70baf56de424e876ccff8d027f93fc34dc1c is the intended head.

You are interacting with an AI system.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: f44b70baf5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

imjlk commented Aug 11, 2026

Copy link
Copy Markdown
Owner Author

@codex review
@coderabbitai review

Please re-review current exact head b94bd045fdbaf800fe8393b7bed9c078f6d2fcc7. This only removes a redundant player comparison after the same condition has already returned denied; focused game-services typecheck and Microsoft Store purchase tests pass.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='b94bd045fdbaf800fe8393b7bed9c078f6d2fcc7'
previous='f44b70baf56de424e876ccff8d027f93fc34dc1c'
actual="$(git rev-parse HEAD)"

printf 'HEAD=%s\nEXPECTED=%s\nPREVIOUS=%s\n' "$actual" "$expected" "$previous"
test "$actual" = "$expected"
git merge-base --is-ancestor "$previous" "$actual"

printf '\n=== Incremental changed paths and whitespace validation ===\n'
git diff --name-status "$previous...HEAD"
git diff --check "$previous...HEAD"

printf '\n=== Incremental patch ===\n'
git diff --unified=100 "$previous...HEAD"

printf '\n=== Microsoft Store purchase boundary outline ===\n'
ast-grep outline packages/game-services/src/microsoft-store-purchase.ts --items all

printf '\n=== Player comparison and recovery ownership control flow ===\n'
rg -n -C12 \
  'player|playerId|owner|ownership|claimRecoveryOwnership|hasRecoveryOwnership|releaseRecoveryOwnership|denied|conflict|providerPurchaseId' \
  packages/game-services/src/microsoft-store-purchase.ts \
  packages/game-services/src/microsoft-store-purchase.test.ts \
  packages/game-services/src/server.ts \
  packages/game-services/src/server.test.ts \
  -g '*.ts'

printf '\n=== Exact modified-file sections ===\n'
git diff --name-only "$previous...HEAD" | while IFS= read -r file; do
  case "$file" in
    *.ts|*.tsx|*.js|*.jsx)
      printf '\n--- %s ---\n' "$file"
      nl -ba "$file" | sed -n '1,1000p'
      ;;
  esac
done

printf '\n=== Full PR whitespace validation ===\n'
git diff --check main...HEAD

Length of output: 231977


@imjlk I reviewed exact head b94bd045fdbaf800fe8393b7bed9c078f6d2fcc7.

The player comparison removal is safe. hasRecoveryOwnership() returns { status: 'denied' } before this branch when existing.playerId !== ownership.record.playerId. The removed condition cannot change the result.

I found one remaining actionable issue.

consumeMicrosoftStorePurchase() does not validate the Collections consume response productId against context.storeId. The response validation at Line 694 checks itemId, trackingId, and newQuantity, but not productId.

A mismatched successful response can therefore release the ownership record and complete finalization for a different Store product.

Add this check to the mismatch condition:

raw.productId !== context.storeId

Update the valid consume fixture to include productId. Add a regression that supplies a different productId and expects MICROSOFT_STORE_CONSUME_RESPONSE_MISMATCH.

git diff --check reported no whitespace errors.

You are interacting with an AI system.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: b94bd045fd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@imjlk
imjlk merged commit e7029d7 into main Aug 11, 2026
6 checks passed
@imjlk
imjlk deleted the codex/microsoft-store-iap branch October 27, 2026 11:26
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