XP implementation
Scope
Grid Studio (/app/ and /grid/) has nine cosmetic, one-time milestones totaling 600 XP. Profile and settings are in-app views at #profile and #settings, preserving market query parameters. The landing page and preserved research terminals do not award XP.
Modules
web/journey/model.js: quest catalog, level thresholds, normalized profile data, positive balance eligibility, and storage operations. Total XP is derived from recognized completed quest IDs; there is no mutable total or arbitrary award amount.web/journey/app.js: header account menu, profile, quest actions, four-step resumable tour, preferences, and milestone notifications.web/journey/styles.css: responsive surfaces using the existing four theme palettes, native dialogs and progress elements.web/grid/app.js: emits milestones after valid customization, successful watchlist persistence, saved strategy persistence, opening coverage, and completed replays with at least two observations.frontend/wallet.jsx: emitscanopy:wallet-balanceonly after a current, successful RPC read that checks the endpoint chain ID. The event carries address, chain ID, and decimal ETH/USDG balances. Stale refresh results are discarded using the existing request version.
Wallet identity comes from canopy:wallet, and the balance milestone checks that the event matches the current wallet and a supported chain. Positive existing balances and test ETH qualify; transaction provenance is not inferred. Live wallet testing is separate from fixture-based lifecycle checks.
Persistence contract
canopy-journey-v1 stores version 1, an accounts map keyed by lowercase wallet address or guest, and guestClaimed. Each profile contains recognized completion timestamps, a display name (32 characters maximum), celebration preference, and tour step (0–3). The first connected wallet merges guest completions without duplicating milestones; the guest entry is then cleared and marked claimed. Later wallets do not inherit guest activity. Sign-out loads guest progress. Switching wallets closes the tour to avoid resuming another identity's open flow.
Every mutation reads the latest stored document first. Storage events refresh visible progress in other tabs. This is localStorage, without transactional concurrency guarantees, signed proofs, cross-device sync, or abuse resistance. Do not use this ledger for token distributions, financial rewards, or access control. A production rewards service must authenticate users, verify qualifying events, enforce server-side idempotency and atomic ledger writes, and define a migration policy.
Storage failures do not report successful rewards. The UI shows a storage error. Names are escaped before insertion into markup. XP cannot be edited through profile preferences. Appearance remains device-wide; names and celebration preferences are profile-specific. Existing watchlists and strategies are not imported as achievements.
Validation
Run npm test for milestone eligibility, deduplication, levels, guest transfer, wallet isolation, malformed storage, storage failures, and funding checks alongside the existing suite. Run npm run build after wallet changes. tests/browser_journey.py exercises the actual UI with a test-only wallet SDK and intercepted balance responses, including tour, settings, profile reload, actual feature actions, and responsive layout.
When Playwright-managed Chromium is unavailable, set CHROME_EXECUTABLE to your installed Chrome executable when running the browser test.
Staging integration
The XP account control sits beside the wallet in the Grid Pod builder toolbar beneath the shared navigation. Saving a grid still opens the existing Pods workspace; the XP milestone is persisted before that navigation. Replays opened from Pods also qualify after loading at least two observations. Explorer, Pods, and shared site navigation keep their existing routes.