Skip to content

feat: add Verse8 Agent8 commerce boundary - #85

Merged
imjlk merged 3 commits into
mainfrom
codex/verse8-commerce-boundary
Jul 16, 2026
Merged

imjlk merged 3 commits into
mainfrom
codex/verse8-commerce-boundary

Conversation

@imjlk

@imjlk imjlk commented Jul 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • add an opt-in VXShop client boundary that opens the shop only in an iframe and returns pending without transaction or entitlement grants
  • add an Agent8 server helper that maps Verse8 product IDs to catalog grants, locks per account, and writes grants with permanent consume-once purchase markers
  • allow Verse8 products in effective target configuration while keeping runtime IAP capability unavailable until explicit Agent8-backed wiring exists
  • keep Verse8 purchases out of the external Game Services purchase verifier and document the separate Agent8 authority path

Security boundary

  • client callbacks, VXShop close events, client metadata, and client-created transaction IDs are never grant authority
  • only the reserved Agent8 $onItemPurchased event should call the server grant helper
  • duplicate purchase IDs return their stored result; conflicting or malformed state fails closed
  • the default Phaser starter does not add the React-based Agent8 browser SDK

Validation

  • pnpm check
  • pnpm build:packages
  • pnpm test:prepared
  • pnpm validate:catalog
  • pnpm validate:target-config
  • pnpm validate:effective-config
  • pnpm smoke:target-config
  • pnpm smoke:effective-config
  • pnpm smoke:adapter-effective-config
  • pnpm smoke:game-config
  • adapter Verse8 tests and dist subpath imports
  • GameServices client and vertical-slice smoke tests
  • ttsc graph authority-flow review

Release

Includes a Sampo changeset for @mpgd/adapter-verse8, @mpgd/catalog, @mpgd/game-services, @mpgd/target-config, and @mpgd/cli. The Sampo release dry-run is restricted to main by repository policy, so the feature-branch invocation was expectedly rejected before mutation.

Summary by CodeRabbit

  • New Features

    • Added Verse8 VXShop support, including product discovery and native in-app purchase capabilities.
    • Purchases now remain pending until securely confirmed by the Agent8 server.
    • Added one-time catalog grants for currency and entitlements, with account-level protection against duplicate purchases.
    • Enabled Verse8 product mappings and in-app purchases in catalog configuration.
  • Bug Fixes

    • Unavailable shops, invalid products, malformed purchase data, and untrusted completion callbacks now fail safely without granting entitlements.
  • Documentation

    • Added setup and security guidance for the Verse8 VXShop and Agent8 commerce flow.

@imjlk

imjlk commented Jul 16, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown

@imjlk I'll review the changes in this PR now.

✅ Action performed

Review finished.

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.

@coderabbitai

coderabbitai Bot commented Jul 16, 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: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

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

Run ID: 08b60fb8-4faf-4eb5-ad66-082902d91c44

📥 Commits

Reviewing files that changed from the base of the PR and between 23a460b and 0358b6e.

📒 Files selected for processing (6)
  • adapters/verse8/src/agent8.test.ts
  • adapters/verse8/src/agent8.ts
  • adapters/verse8/src/index.test.ts
  • adapters/verse8/src/index.ts
  • docs/VERSE8_COMMERCE.md
  • packages/game-services/src/client.ts
📝 Walkthrough

Walkthrough

Adds Verse8 VXShop commerce integration, an Agent8 server-side consume-once grant service, Verse8 catalog enablement, pending purchase handling, runtime validation, package exports, tests, smoke-test updates, and commerce documentation.

Changes

Verse8 commerce flow

Layer / File(s) Summary
Target enablement and client purchase flow
packages/catalog/catalog.json, packages/target-config/*, packages/game-services/src/*, tools/smoke/*
Verse8 IAP products become available through catalog mappings and target configuration. Client purchases remain pending, while untrusted completed purchases are rejected without invoking purchase verification.
VXShop gateway adapter
adapters/verse8/src/index.ts, adapters/verse8/src/index.test.ts
Adds VXShop product mapping, shop initialization and refresh behavior, native IAP capabilities, guarded purchases, entitlement loading, and fail-closed unavailable behavior.
Agent8 consume-once grant service
adapters/verse8/src/agent8.ts, adapters/verse8/src/agent8.test.ts
Adds locked per-account purchase processing, catalog currency and entitlement grants, purchase idempotency, stored-state validation, and snapshot reads.
Public packaging and commerce guidance
adapters/verse8/package.json, adapters/verse8/test/*, docs/*, README.md, packages/cli/templates/*, .sampo/changesets/*, tsconfig.base.json
Publishes the Agent8 subpath, verifies its distribution export, adds module resolution, records release metadata, and documents the VXShop-to-Agent8 purchase boundary.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant VXShop
  participant Agent8
  participant UserState
  Client->>VXShop: Open shop and buy product
  VXShop-->>Client: Pending purchase status
  VXShop->>Agent8: $onItemPurchased event
  Agent8->>UserState: Lock account and write grant marker
  UserState-->>Agent8: Updated commerce state
  Agent8-->>Client: Authenticated entitlement snapshot
Loading

Possibly related PRs

  • imjlk/mpgd-kit#82: Introduced the Verse8 gateway foundation that this change extends with VXShop commerce integration.

Poem

I hopped through VXShop, purchase pending in flight,
While Agent8 guards each grant just right.
One little lock, one marker to keep,
Coins and entitlements safely sleep.
No client tale can fool the gate—
The server makes the final state.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding the Verse8 Agent8 commerce boundary.
Description check ✅ Passed It includes the required Summary and Validation sections and is otherwise complete, though the Notes section is omitted.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/verse8-commerce-boundary

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.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 23a460bf1f

ℹ️ 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".

@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: 3

🧹 Nitpick comments (2)
adapters/verse8/src/agent8.test.ts (1)

175-177: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise real concurrent lock behavior in this suite.

This stub ignores the lock key and runs immediately, so tests cover only sequential retries. Add a keyed mutex fixture and a concurrent Promise.all test asserting one state update and one grant.

🤖 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/verse8/src/agent8.test.ts` around lines 175 - 177, Replace the
immediate callback stub in the lock fixture with a keyed mutex that serializes
callbacks sharing the same key. Extend the relevant agent8 tests with a
concurrent Promise.all scenario, asserting that concurrent retries produce
exactly one state update and one grant.
adapters/verse8/src/agent8.ts (1)

97-99: 🚀 Performance & Scalability | 🔵 Trivial

Plan for an indexed, scalable consume-once ledger.

Each purchase scans, validates, appends, and rewrites the account’s complete permanent history. Repeat consumables make processing and state payloads grow linearly for the account.

Verify Agent8 state limits and transactional storage options, then use an indexed ledger while preserving atomic grant-and-marker writes.

Also applies to: 124-131

🤖 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/verse8/src/agent8.ts` around lines 97 - 99, Replace the linear
current.purchases.find lookup and full-history rewrites in the Agent8
purchase-consumption flow with an indexed consume-once ledger suitable for
repeat consumables. Verify Agent8 state-size limits and available transactional
storage, and persist consumption markers through the indexed store while
preserving atomicity between granting the purchase and recording its marker.
🤖 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/verse8/src/agent8.ts`:
- Around line 247-265: Update the entitlement-grant flow around the existing
entitlement lookup to reject grants whose quantity is greater than one before
recording or consuming the purchase ID. Ensure only single-quantity purchases
proceed to add the entitlement and return entitlementIds, while multi-quantity
purchases are rejected without creating a permanent marker.

In `@adapters/verse8/src/index.ts`:
- Around line 373-400: Move the canOpenShop evaluation in purchase inside the
existing try/catch boundary so exceptions from the caller-provided callback are
converted to failedPurchase(). Preserve the current product lookup and
fail-closed behavior for false or unavailable shop access, while keeping
initialization, refresh, item validation, and purchase handling unchanged.

In `@packages/game-services/src/client.ts`:
- Around line 216-218: Update the reason selection in the purchase analytics
tracking flow to return 'agent8_purchase_event_pending' only when
purchase.status is strictly 'pending'; preserve
'verse8_grants_require_agent8_purchase_event' for completed purchases and assign
the appropriate non-pending handling for canceled or failed statuses.

---

Nitpick comments:
In `@adapters/verse8/src/agent8.test.ts`:
- Around line 175-177: Replace the immediate callback stub in the lock fixture
with a keyed mutex that serializes callbacks sharing the same key. Extend the
relevant agent8 tests with a concurrent Promise.all scenario, asserting that
concurrent retries produce exactly one state update and one grant.

In `@adapters/verse8/src/agent8.ts`:
- Around line 97-99: Replace the linear current.purchases.find lookup and
full-history rewrites in the Agent8 purchase-consumption flow with an indexed
consume-once ledger suitable for repeat consumables. Verify Agent8 state-size
limits and available transactional storage, and persist consumption markers
through the indexed store while preserving atomicity between granting the
purchase and recording its marker.
🪄 Autofix (Beta)

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

Run ID: c91011d2-a847-4b08-8970-ce777545ff78

📥 Commits

Reviewing files that changed from the base of the PR and between eab89e5 and 23a460b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • .sampo/changesets/verse8-agent8-commerce.md
  • README.md
  • adapters/verse8/package.json
  • adapters/verse8/src/agent8.test.ts
  • adapters/verse8/src/agent8.ts
  • adapters/verse8/src/index.test.ts
  • adapters/verse8/src/index.ts
  • adapters/verse8/test/agent8-dist-import.mjs
  • docs/GAME_SERVICES_BACKEND.md
  • docs/VERSE8_COMMERCE.md
  • packages/catalog/catalog.json
  • packages/cli/templates/phaser-game/README.md
  • packages/game-services/src/client.test.ts
  • packages/game-services/src/client.ts
  • packages/target-config/src/effective.ts
  • packages/target-config/targets.json
  • tools/smoke/adapter-effective-config.ts
  • tools/smoke/effective-target-config.ts
  • tools/smoke/game-owned-catalog.ts
  • tools/smoke/target-config-runtime.ts
  • tsconfig.base.json

Comment thread adapters/verse8/src/agent8.ts Resolved
Comment thread adapters/verse8/src/index.ts Resolved
Comment thread packages/game-services/src/client.ts Resolved
@imjlk

imjlk commented Jul 16, 2026

Copy link
Copy Markdown
Owner Author

Addressed the review findings in e33dff6:

  • reject multi-quantity entitlement purchases before writing a consume marker
  • keep injected shop-availability failures inside the fail-closed adapter boundary
  • report the Agent8 pending analytics reason only for pending purchases
  • replace linear purchase-history lookup with a purchase-ID-indexed marker record
  • exercise concurrent duplicate events through a keyed mutex and verify one grant/update

The permanent marker remains in the same Agent8 user-state update as the grant. Moving it to a separate collection would lose that atomic grant-and-marker property because the public Agent8 contract does not expose a cross-store transaction.

Validation completed: pnpm check, package build, 28 Verse8 adapter tests, both Verse8 dist subpath imports, GameServices client test, and diff checks.

@codex review
@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown

@imjlk Thanks for the detailed update. I'll review the changes addressing these findings now:

  • Rejecting multi-quantity entitlement purchases before writing a consume marker
  • Keeping injected shop-availability failures inside the fail-closed adapter boundary
  • Reporting the Agent8 pending analytics reason only for pending purchases
  • Purchase-ID-indexed marker lookup instead of linear scan
  • Concurrent duplicate event handling via keyed mutex

Your rationale on keeping the permanent marker in the same Agent8 user-state update (to preserve atomicity, since the public Agent8 contract doesn't expose a cross-store transaction) makes sense and I'll keep that in mind for this review.


✏️ Learnings added
Learnt from: imjlk
URL: https://github.com/imjlk/mpgd-kit/pull/85

Timestamp: 2026-07-16T02:31:30.427Z
Learning: In adapters/verse8/src/agent8.ts, the Verse8 Agent8 commerce service (createVerse8Agent8CommerceService) intentionally writes the permanent consume-once purchase marker in the same Agent8 user-state update as the catalog grant, rather than in a separate collection/store. This is because the public Agent8 contract does not expose a cross-store transaction, so separating the marker from the grant would lose the atomic grant-and-marker property.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

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: e33dff6d71

ℹ️ 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/verse8/src/agent8.ts Resolved
@imjlk

imjlk commented Jul 16, 2026

Copy link
Copy Markdown
Owner Author

Addressed the Codex P2 finding in 0358b6e.

The Agent8 service now checks the permanent consume marker under the account lock before requiring a current catalog mapping. An identical retry still returns its stored logical product and alreadyProcessed result after the product is retired, while a genuinely new unknown product remains fail-closed. The regression test shares persisted state between the active and retired catalog services.

Validation completed: pnpm check, 28 Verse8 adapter tests, catalog validation, package type check, and diff checks.

@codex review
@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cd /tmp && rm -rf repo && git clone --depth 50 https://github.com/imjlk/mpgd-kit.git repo 2>&1 | tail -5
cd repo && git fetch origin pull/85/head:pr85 2>&1 | tail -5
git checkout pr85 2>&1 | tail -5
git log --oneline -10

Length of output: 1099


🏁 Script executed:

#!/bin/bash
cd /tmp/repo
git show 0358b6e --stat
echo "---diff---"
git show 0358b6e -- adapters/verse8/src/agent8.ts

Length of output: 2868


🏁 Script executed:

#!/bin/bash
cd /tmp/repo
git show 0358b6e -- adapters/verse8/src/agent8.test.ts

Length of output: 1323


This looks solid. The fix correctly moves the "unknown product" check after the idempotency lookup, so a retry of an already-processed purchase now returns its stored logicalProductId and alreadyProcessed: true even if the product was later retired from the catalog — while a genuinely new/unknown product still fails closed (the product === undefined check still throws before any grant is applied). The relaxed assertSamePurchase correctly skips the logicalProductId comparison only when the current catalog lookup is undefined (retired), while still enforcing platformProductId and quantity match against the stored record, so replay/tampering protection is preserved. The new test shares the same fixture.context state between the original and retired-catalog service instances and asserts exactly one state update occurred, which validates the intended behavior.

Nice, targeted fix — thanks for the update, @imjlk. This addresses the P2 finding correctly.

✅ Action performed

Review finished.

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

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 0358b6e028

ℹ️ 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 760cdec into main Jul 16, 2026
6 checks passed
@imjlk
imjlk deleted the codex/verse8-commerce-boundary branch July 16, 2026 03:18
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