PlayMusic Backlog Snapshot

Weekly product update · last updated 2026-10-15 · initiatives tracked against PRs across platform · engine · integrations
–
Shipped / always on
–
In flight
–
Dated ETA
–
Test / trial
–
Not done
–
Unfunded
Counted from the Initiatives tab (28 items) at page load · status as of the latest weekly update · "Not done" = the measurement items that gate everything else.
Week of 2026-10-15 · covers Oct 8 → 15. The six-day promotion block ended on 9 Oct; production is on today's build (#1802, go-live 10:56Z).
  • 14 merges to main — 4 staging→prod releases (#1727 #1747 #1759 #1792) and 10 hotfixes; 5 engine releases; 6 Telegram prod releases in one week.
  • 14 Go Live presses, 8 succeeded — four failures and two cancels, every one of them a rollout-tooling or prod-health fault, each restored cleanly. Root causes below.
  • One incident: portal-cms connection-pool starvation (5, 6, 8, 9, 12 Oct) — every public read answered 500 for hours at a time; fixed 12 Oct, follow-ups shipped 14 Oct.
  • Creator Round 2 (six Google Search campaigns) live since 12 Oct — $1,776 → 177 Game Created ($10.03) by 15 Oct; health check 17 Oct, spend stops 23 Oct, read 26 Oct.
  • Customer feedback is live on prod and answering: ~33–36% of shown prompts get a response; top complaint "Controls felt awkward" — the same thing 14 of 20 mobile playtesters said.
Chips: live promoted to prod traffic · canary on main, 0% traffic · staging merged to staging · open PR open · branch feature branch

⭐ Highlights

Nine themes · every bullet names its PR and where it is

1 · Production promotions unblocked — and what the rollout tooling cost us

An IAM grant on 9 Oct ended the block; the week then exposed four more faults in the 4 Oct rollout rewrite, each fixed the day it bit.

  • 9 Oct 08:4xZ — roles/logging.viewer granted to the Actions service account (the "Observe 10%" step could never read Cloud Logging). live#1727 promoted 10 → 50 → 100 with the check passing three times; 11/11 services at 100%; migrations 113–115; the first prod wizard-config sync from code returned 200.
  • 9 Oct 18:21 / 21:37 — two presses cancelled at the 45-min ceiling: check_errors vetoes on the first 5xx anywhere at 100%, and one drop-out beacon for a deleted project 500'd (an FK violation latent since May, ~2/day). live#1753 returns the 204 sendBeacon expects; #1755 keeps browser services on the stable revision until the final 100% (users were getting HTML from one build and chunks from another at 50/50).
  • 12 Oct 18:14 — the gateway was blamed for 5xx it forwarded from the wedged old portal-cms; live#1773 lets the rollout promote a fix during an outage.
  • 14 Oct 11:33 / 11:59 — a full-list migration run re-executed 111's DROP/CREATE TRIGGER on analytics_events, stalled 16 min behind the hourly rollup and wedged live player-api; the retry died on a second copy of the abandoned-normalization guard. live#1793 fixes the guard; rule from now: one migration file per dispatch.
  • live#1767 (in #1792): slowapi failed closed — a rate limiter that couldn't reach Redis 500'd every request including /health, on cold canaries and once on live engine-api; all nine FastAPI services inherited it.
  • The ledger: 12/12 green promotions before the 4 Oct rewrite; 18 runs after it, 8 green. Fix list written (docs/ + memory): rehearse the script on staging canary tags, one guard in one place, gate on error rate vs baseline, rollback as its own job, IAM preflight as the CI principal.

2 · Incident: portal-cms pool starvation (5 → 12 Oct)

/api/contests/*, /api/access/settings, promo campaigns and ads config answered 500 "connection pool exhausted" for hours on five days; the contest page rendered that error to the public.

  • Cause: prod portal-cms had a 2-slot pool per instance and one instance; each LiveOps Product Journeys open (fired in pairs) held a slot for minutes, the hourly rollup held one for 5–10 min at the 300 s timeout, and the 25 s statement timeout never applied — PgBouncer's DISCARD ALL wiped the session-level SET before the first real query.
  • live#1771 (12 Oct 19:36) separate heavy pool for analytics and cron, public pool 5, per-transaction SET LOCAL statement_timeout · #1773 single-flight cache on Product Journeys, rollup one day not two, 900 s request window.
  • live#1781 (13 Oct) the real cap then broke the hourly rollup five runs straight: retention_d1 was a correlated NOT EXISTS over all of analytics_events — rewritten 219.6 s → 2.1 s on a prod-sized copy; 12 Oct's frozen metrics backfilled.
  • live follow-ups in #1792: #1786 dashboard requests the report once · #1787 write routes release their connection before purges, syncs and LLM calls · #1788 hourly precompute into product_report_snapshots (CMS 0014).
  • Open: #1788's precompute cannot finish on prod data — the 7-day FUNNELS query runs past the 600 s budget (quadratic correlated subqueries over a materialised CTE); scheduler job paused 14 Oct. The on-demand Product Journeys route has never completed on prod; it needs a hash-join rewrite timed on a prod-sized snapshot. Post-mortem: doc.

3 · Performance improvement for webviews

Telegram shipped six times to prod; the shared scene-loading menu is on by default for playmusic.gg.

  • live Telegram #62 (R1, 9 Oct): named-game links open and auto-start; CDN thumbnail renditions took Discover from 62 MB to 0.7 MB; shared engine cache path so the second game re-downloads 0 KB; no game-HTML redirect hop; one XP read instead of three.
  • live #79 (R3) multiplayer as a lazy chunk, minified critical path · #87 (R4) boot deferrals (gtag + MP past the first screen), lean feed rows, SDK ETag, 60 s /api/games cache · #88 (R5) Appearance, the engine loading menu, For You and Create on prod — first load 50 KB gz.
  • live#1741 scene loading on by default for playmusic.gg (engine #573 carries the presentation); #1699/#1724 modal hands loading to the engine menu, engine preloaded on arrival.
  • live engine #576 deduplicated foliage images + bundled standalone deps · #575 elapsed time preserved under frame caps, render work trimmed · #577 a dead model URL can no longer hold the boot.
  • staging Telegram round 5 (#92–#94: first screen, feed latency, Browse 120 rows first) and round 6 (#95–#97: engine warm manifest, shipped disabled).

4 · Performance stability for webviews

Silent render freezes on in-app browsers can now be detected and recovered — gated off until physical acceptance.

  • live#1758 + engine #579/#580 (11 Oct): silent render-stall detection for recovery-capable hosts, independent default-off gates for Facebook/iPhone and Android WebViews, multiplayer excluded, diagnostics + retry.
  • live#1802 + engine #584 (today): opaque cross-origin Script error. no longer reported as a fatal boot failure; load-timeout reports carry the engine build, the pending readiness blocker (world / config_audio / orchestrator) and stage timings; the engine retries transient config and audio downloads.
  • live engine #582 the Play card shows the right controls on first paint · #570 pause button scoped to Drops · #569 flight preview no longer seats the boot runner.
  • live Telegram #73 (R2): engine flat crash recorded as engine_error not timeout; 7-day last-good feed served when the catalog is down; pm_track survives the engine's config-fetch abort (missing track title + per-song leaderboard on Chrome).

5 · Website and game loads

  • live#1737 small versioned preview clips fill the edge cache after a Range miss (MISS → HIT verified live); song audio kept 72 h in the browser, 1 h at the edge.
  • live#1745 asset URLs resolved at serve time instead of backfilling stored copies — all 4,812 public catalog rows verified free of raw storage:// / /api/minio/ URLs · #1750 /api/songs limit raised to 500 so curated carousels render.
  • live#1755 No Brakes campaign list rendered empty — the frontend resolved its 12 games against the first 500 catalogue rows; now pages to completion. #1740 drafts survive sign-in; song promotion hardened; repair script for the metadata it left missing.
  • open#1803 CMS catalog waits for asset-service to start instead of failing.

6 · Alchemy

  • live the first-time walkthrough shipped (9 Oct, #1721) and grew: #1777 "Create a new game" as its closing step · #1778 it counts the step it is on · #1749 a prompt when returning from a published game · #1748 Exit follows the visitor's trail back, not always Home.
  • live#1779 song ships load in the preview · song-generated vehicles offered in Flight and Drift, one "Made for This Song" card · a game cannot be published twice · #1762 the intro gate reaches pre-consent paid arrivals and runs in Alchemy (paid-only, no holdout, creation_gate_evaluated logs every decision) — the gate Round 2 needed.
  • What the numbers say: Round 2 wizard converts 25% of starts to a made game (≈$9/maker) vs Alchemy 13% (≈$17); Alchemy's loss is desktop — $39/conversion on 169 desktop clicks vs $11 mobile, finish-step 35%. Desktop playtesters: "configuring, not creating" (11 of 26).

7 · Telegram

  • live six prod releases: #62 R1 fast-launch (9 Oct) · #73 R2 post-release fixes · #79 R3 size · #87 R4 size + load · #88 R5 Appearance, loading menu, For You + Create on prod · #91 header/Settings polish — all 13 Oct; last prod dispatch 13 Oct 20:15Z. #46 was closed and replaced by #62 pinned to a frozen sha.
  • live creator handoff (#1732/#1733 in #1759): the token travels in the wizard URL fragment, cleared after a successful create, callback gated and scrubbed from Sentry — callbacks stay disabled until verified with a real issued handoff.
  • staging the account-link workstream, inert behind flags: #98 bounded shell XP writes · #99 start + status routes · #100 PMH callback routes · #101 LinkOutbox binding; PMH side open #1798 XP writer lock · #1799 Sentry scrub · #1800 link tables. Telegram staging auto-deploys on push (52 commits ahead of main); prod is a manual dispatch.

8 · User feedback — live, and already telling us something

  • live customer feedback shipped 9 Oct (#1723/#1725, migrations 113–115) and was extended every release: #1746 impressions as the response-rate denominator (116) · #1751 campaign + variant on each respondent · #1774 a creation survey chain (preview → created → own-game play) · #1775 why a survey was never shown (117) · #1776 six random survey groups incl. a control · #1783/#1784 the wizard asks on the preview screen.
  • Prod, since impressions went live 12 Oct: game-ended 36 of 37 issued were shown, preview 87 of 91 — the earlier "shown 15 of 136" was missing data, not unseen surveys. Response rate ~33–36% of shown. 13 Oct snapshot: preview 30 responses (15 good / 11 okay / 4 poor), game-ended 25 (9 / 11 / 5), top reason "Controls felt awkward".
  • Playtests (PlaytestCloud 20 mobile · Antidote 26 desktop): use-again mobile Wizard 8 / Alchemy 4 / either 4 / neither 4; desktop W10 / A8 / either 1 / neither 7. Themes (mobile/desktop testers): didn't know what to do 18/9 · lag 15/9 · controls awkward 14/5 (Drift Drive named by 11) · thin catalogue 11/8 · "configuring not creating" 7/11. Expectation predicts the verdict: came for a rhythm game → match 3.75 (14/18 would use); came for a builder → 2.72.
  • By design, game_created gets no survey while preview_ready is sampled (one prompt per visitor per cooldown) — a CMS decision if creation feedback is wanted.

9 · Song pipeline

  • staging the pipeline runs on Inngest (8 Oct, with local dev + docs), the old pipeline's D1 sync fixes carried over, one reusable sized-small deploy for the services, finished runs reported to Slack with a section per failed step (#1795/#1804), a step yields to an earlier run on the same song, character and slot meshes on a song without a source image (#1805), theme from a closed profile (#1796). Hosting / vendor-limit / account-split decisions recorded as ADRs.
  • live#1740 song promotion hardened (partial promotions rejected, pooled connections released) · #1745 promotion copies preview assets before publishing new version URLs · CMS files every character variant under character and shows the rigged one.
  • open #1801 Asset Studio phases 1–3 — song-independent asset creation (upload GLB → validate → review → publish; generated assets; rig + clips), migration 120, CMS pages, ADR 0008 · #1797 pick the character pose and prompt per regenerate · #1782 keep the prod -next services warm (the 61 s cold start that 504'd Drops uploads) · #1769 compressed model variants.

🚀 Release log

Serving: 9e7c956b (15 Oct 10:56Z) · engine prod = the main Pages alias · Telegram prod = manual dispatch, last 13 Oct 20:15Z

Platform

DateReleaseWhatTraffic
15 Oct#1802filter opaque boot errors; load-timeout diagnostics (direct to main; engine #584 companion)live 10:56
14 Oct#1792 + #179316 PRs — feedback chain/unseen/groups (#1774 #1775 #1776 #1783 #1784), CMS pool follow-ups (#1786 #1787 #1788 #1780), limiter fail-open (#1767), takedown guard by product (#1766), Alchemy (#1777 #1778 #1779), Horror Survival label (#1790), back-merge (#1785); migrations 117 + CMS 0014; #1793 normalize-retry fixlive 12:53 after two failed presses
13 Oct#1781hotfix: retention_d1 rollup in one pass (219.6 s → 2.1 s)live 08:09
12 Oct#1771 #1773hotfixes: heavy pool + per-transaction timeout; analytics runaway guards, rollup one day, 900 slive 19:36 (18:14 press failed)
12 Oct#1762hotfix: intro gate for paid first visits, wizard + Alchemy, ?msg= — unblocked Round 2live 11:12
11 Oct#1759player render-stall recovery gates (#1758), asset delivery (#1737 #1745), Telegram handoff (#1732 #1733), No Brakes landing coverage (#1757), feedback attribution (#1751), create-success cleanup (#1742); carries #1753 + #1755live 19:39
9 Oct#1753 #1755hotfixes: drop-out beacon 204 for an unknown project; campaign lists page the catalogue; browser services switch only at 100%live 11 Oct
9 Oct#17477 PRs — drafts through sign-in + song promotion (#1740), Sky Rider character in preview (#1744), Alchemy exit/return (#1748 #1749), /api/songs 500 (#1750), survey impressions (#1746, migration 116)live 11 Oct (two 9 Oct presses cancelled)
9 Oct#1741hotfix: scene loading on by default for playmusic.gglive 11:13
9 Oct#172719 PRs held since 2 Oct — customer feedback (#1723 #1725), Alchemy FTUE (#1721), Drops Play v2 follow-ups (#1714), modal loading handoff (#1699), crash-report retention (#1700), feed preload (#1724), prod wizard-config sync (#1701), rollout fixes (#1702 #1707); migrations 113–115live 08:30 — first promotion since 2 Oct
8 Oct#1728hotfix: the Observe step retries its Logging query and prints gcloud's reason — which surfaced the IAM gaplive 9 Oct

Engine (live on merge)

DateReleaseWhat
15 Oct#584retry transient config and audio downloads (companion to #1802)
14 Oct#583Play card shows the right controls on first paint (#582)
11 Oct#580gated silent render-stall detection, multiplayer excluded (#579)
9 Oct#578frame-cap timing (#575), foliage dedup + standalone bundle (#576), boot no longer held by a dead model URL (#577); smoke budget 30 min (#574)
9 Oct#57321 PRs — scene-loading presentation + per-mode loading (#554–#567), iOS/Android WebView safeguards (#555), Drops preset Play v2 (#551 #562), HUD reliability (#552 #553), Vector Squadron (#560), flight shadow (#561), Drops pause scope (#570), flight preview runner (#569), shadow disc (#571)

Telegram (integrations → main; prod by dispatch)

DateReleaseWhat
13 Oct#91hotfix: header polish, gear → Settings, Appearance switches
13 Oct#88R5: Appearance on, engine loading menu (VITE_LOADING_UI=scene), For You landing, Create opens the playmusic.gg wizard; sound on by default
13 Oct#87R4: boot deferrals (#80), size round 3 + SDK ETag (#81), Safari 14 CSS target (#82), build nits (#84), lean feeds + 60 s cache (#85), Browse pages (#86)
13 Oct#79R3: minified critical path (#75), dead CSS (#76), multiplayer lazy chunk (#77 #78), shell_boot_failed (#71), handoff token in the fragment (#74, inert)
9 Oct#73R2: post-release fixes (#63), last-good feed (#64), login widget + cache/security headers (#65), engine crash classed correctly (#66), pm_track survives config abort (#69), scene loader fatal-only (#70); 404s no-store (#72)
9 Oct#62R1: fast-launch (#43–#45 #47–#53), UX quick wins (#39), contract v1 (#40), theme switch (#41, hidden), loading handoff (#42)

📥 Inbound

Platform staging 14 commits · integrations staging 52 commits · engine staging 0 · no release PR open anywhere
  • Platform staging → main is entirely the Inngest song-pipeline work above plus devcontainer wrangler and an asset-catalog MCP fix; main is back-merged (#1794). Nothing user-facing is waiting.
  • open against staging, worth a look this week: #1801 Asset Studio · #1791 forged-guest XP, unauthenticated /end payouts, guest convert/claim — this is the unowned guest-claim takeover from the 29 Sep hold assessment · #1806 Drops records the share code a sign-up arrived with · #1803 catalog cold start · #1798–#1800 Telegram link (PMH side) · #1797 character pose per regenerate · #1782 warm -next services · #1769 webp/compressed models · #1768 (conflicting, superseded in part by #1771).
  • Engine open: #572 Beat Snake mode · #581 single landing page for creator user testing · #543 Forge visual editing workspace · #525/#531 Block Jam + Beat Swing · #538 street racer · #542 LOOP territory playtest.
  • Integrations: Telegram staging carries rounds 5–6 and link L0–L2b1; the next prod dispatch is a decision, not a merge.

Next 7 days

  • Round 2 health check 17 Oct (day 5): run the first-party cohort pull (r2_cohort_d4.sh — prod reads need the user), fold into the Day-4 doc; candidate actions from the read: drop the +15% desktop bid, add "how to" bare-term negatives, decide GB. Spend stops 23 Oct — a manual pause; read on 26 Oct.
  • Product Journeys on prod: rewrite the FUNNELS SQL hash-join style, time it on a prod-sized local snapshot, then un-pause portal-cms-precompute-product-journeys-production. Until then the LiveOps dashboard has no prod data.
  • Rollout tooling: start the fix list — contract tests on recorded gcloud output, one drift guard in plan, error-rate gate, rollback as its own job, IAM preflight as the CI SA. Migrations: one file per dispatch; add an applied-files ledger + lock_timeout. Tidy the six stale 0% rollout-* tags on the gateway.
  • Security: review and ship #1791 (guest-claim takeover); the admin-key delete route from the same assessment still has no PR.
  • Render-recovery gates (#1758 / engine #580): physical Facebook-iPhone and Android WebView acceptance before enabling; Telegram callback acceptance before enabling "Play it" DMs.
  • Feedback: decide whether game_created gets its own sampling; read the first week of survey groups vs control.
  • Still owed: the 23 SuperTokens accounts orphaned Jul 27 → Sep 15; source maps for Sentry.

🔍 Monitoring

Creator Round 2 · WZ TikTok UA is automation · feedback + playtests · deploy reliability · daily metrics

Creator Round 2 — six Google Search campaigns, live since 12 Oct ~11:30Z

$1,776 → 177 Game Created at $10.03 (Ads, to 15 Oct 12:50Z) · first-party (14 Oct pull, 65% click-id coverage): 615 landed → 271 started (44%) → 123 makers (166 games) → 92 played own → 30 claimed ($29.57/creator) → 14 shared → 3 finished a song.

  • Tool: wizard 26 makers / 105 starts (25%, ≈$9) vs Alchemy 17 / 135 (13%, ≈$17); Alchemy loses at the finish step (35%), the wizard at start. Alchemy's loss is desktop: $39/conv on 169 desktop clicks vs $11 mobile.
  • Market: Tier A (India-led) $2.43/maker, 22 of 30 sign-ups, 11 of 14 shares; Nigeria 14 makers of 22 arrivals. GB weak — 228 clicks → 16 conv, $30 vs US $13.65, ZA $14.23; A-Song GB 40 clicks, 0 conv. Mobile cheaper per game overall ($11.85 vs $21.72) — the +15% desktop bid adjustment is buying the worse half.
  • Terms: day 1 had ~16% of spend on Roblox/Minecraft/coding searches → 24 phrase negatives (now ~2%). Bare "how to" terms convert ~3% vs "…for free" 15–44%. Known-age clicks convert 18% vs unknown 8%; 18–24 cheapest ($8.57).
  • Docs: Creator Round 2, Day 4 (framed by funnel stage, playtest overlaid) · plan + stop rules. Read with care: 11 of 11 days are not in; surveys stand at 13 Oct; the intro gate reopens after a reload for visitors who haven't answered consent (pre-existing).

WZ No Brakes TikTok UA — the traffic is Pangle automation, not people

83,111 TikTok-arm sessions 1 → 14 Oct · 82% referred by pangleglobal.com (ByteDance's in-app ad network) · CA 96.8% · median 2 s on page · 0.25% played.

  • Game-tap funnel: 1,771 taps → 988 loaded → 143 played (8.1%); one iPhone in a news-app WebView = 1,357 visits / 568 taps = 32% of all taps; top 10 devices 44%; 56% of taps from devices with ≥10 visits.
  • The Instagram arm is real: 3,576 sessions, 68% of taps play, 18% of sessions play, median 6 s.
  • Any site-wide visit/session figure since 1 Oct is dominated by this — split wz_ua_tiktok out first. The fix is a placement exclusion (Pangle / audience network) in TikTok Ads — a campaign decision; spend is not in our data.

Deploy reliability, by the numbers

  • 28 Sep → 4 Oct (inline workflow): 12 presses, 12 green, 6–13 min each. 4 → 14 Oct (rewritten script): 18 presses, 8 green, 7 failed, 3 cancelled at the 45-min ceiling. This week: 14 presses, 8 green — every failure traced and fixed the same day (IAM, FK 500, forwarded 5xx, migration lock, duplicated guard). Builds: 34 runs, 1 failure.
  • A successful graduated promotion now takes ~24 min (freeze → smoke → normalize → 10% → 50% → 100% → verify → tags → wizard-config sync); the 100% step alone is ~15 min because each update-traffic blocks until Cloud Run reports Ready.

Daily metrics — repaired, watch the 00:00 UTC run

  • The hourly rollup 504'd at 300 s every hour from 7 Oct; after #1773 it failed five runs on retention_d1; after #1781 it completes in seconds. 12 Oct's frozen metrics and the 11 Oct cohort's retention row were backfilled by hand. Any daily_metrics-sourced play or retention figure for 7–13 Oct should be re-read before quoting.
  • Song engagement across all 174 songs now has a report (30 d to 12 Oct; picker exposure = distinct picker sessions, not events — events over-count by a quarter from remounts). song_sections v16 went live 8 Oct, so "since re-layout" is the comparable window.
Week of 2026-10-08 · covers Sep 24 → Oct 8 (two weeks). 12 platform releases, 7 hotfixes, 5 engine promotes, 20 Telegram PRs.
  • Production is serving the 2 Oct build (#1673). 17 promotions 23 Sep → 2 Oct; none since.
  • On canary at 0%: #1685 #1687 #1697 (29 PRs) #1708 — three go-lives failed on 7 Oct at the 10% shift (fixed by #1708), a fourth and a fifth (7 Oct 16:02, 8 Oct 14:58 on #1728) failed at "Observe 10%": the Actions service account has no Cloud Logging role, so the candidate-error check is refused with PERMISSION_DENIED. Not a code fault — an IAM grant unblocks it. Every failure restored prod cleanly (~30 min).
  • Inbound: integrations #46 open · platform staging 41 commits, no PR yet · engine staging 17 PRs, no PR yet.
  • Paid tests concluded 5 Oct; every Google campaign paused.
Chips: live promoted to prod traffic · canary on main, 0% traffic · staging merged to staging · open PR open · branch feature branch

⭐ Highlights

Seven themes · every bullet names its PR and where it is

1 · Performance improvement for webviews

The second game in a Telegram session no longer re-downloads the engine; skies, textures and stems got lighter.

  • staging#49 shared engine asset path (/game-proxy/_e/): the second game re-downloads 0 KB of engine instead of ~1.74 MB. Also fixed games after the first never leaving the loading screen (a chained postMessage wrapper).
  • staging#48 CDN thumbnail renditions · #47 direct links, hero-first /start, menu button · #51–#53 per-frame message realms, early chrome, one XP read, loader stages.
  • staging#42 opt-in engine loading handoff (?loadingUi=scene) for the Discord, TikTok and Telegram players.
  • live engine #549 shared GLB textures before GPU upload · #540 skies via Cloudflare resizing, stems skipped for non-stem OSU games, traffic cars preloaded only for drift modes.
  • live#1676 stems at 192 kbps — a 3.5-minute game's 34 MB download cut ~40%.
  • staging#1699 GameModal hands its overlay to the engine menu · #1724 engine preloaded on arrival, feed artwork and position kept through a load · engine #554–#567 per-mode scene loading.

2 · Performance stability for webviews

Graphics caps and context recovery for iOS and Android WebViews, and a load that always ends in Play, Retry or an error — never a spinner.

  • live engine #548 iOS WebView graphics bounded, lost context recovered.
  • staging engine #555 Android WebView graphics cap, startup deadlines, constrained-startup recovery guards.
  • canary#1685 (= #1683 + #1684) a failed app asset leaves bounded reload evidence on the next boot; branded iOS in-app browsers get a browser-choice sheet behind inapp_interstitial — off by default, 48 h of diagnostics first.
  • canary#1671 load progress stays until the engine says PLAYABLE (WORLD_READY could precede decoded music); homepage single-click launch gated · #1674 retry panel when a load budget expires, audio diagnostics kept · #1677 the panel shows before a game URL exists.
  • live engine #522 waits for song + config readiness · #544/#546 playback readiness and load-phase timings · #545 per-asset audio failure diagnostics.
  • staging#1700 fatal crash reports kept after timeouts, click ids scrubbed from Sentry · open#1712 web-storage polyfill · #1711 a slug embed's load counted once.

3 · Performance improvement — website and game loads

/foryou starts fetching at first byte, the edge serves byte ranges and cached shells, and Drops stops pulling 28–40 MB of clips below the fold.

  • canary#1694 feed data and route chunk start at first byte on /foryou · #1696 paid links (ttclid, list, tour, lpt) keep the shell cache; the games-list cache no longer advertises 4 h of browser freshness.
  • live#1555 slow-connection loading — modulepreload with retries, next-song preload waits for playback, no speculation on Save-Data / 2G / 3G.
  • live#1568 edge: byte ranges on /playmusic-assets/ (416, If-Range, HEAD); anonymous /foryou, /create, /alchemy HTML reused at the POP for 30 s.
  • live#1627 Drops: clips stream only while at least half on screen; signed-in routes lazy; /me probe in parallel; self-hosted fonts; CDN-resized tiles; a stale tab reloads once.
  • live#1621 prosumer lookups compare ids as uuid so the indexes serve them.
  • open#1709 the CDN answers If-None-Match with 304 instead of resending the file.

4 · Alchemy

Fixes on every mode, a measurable Wizard-vs-Alchemy split, and — on staging — a guided first build.

  • live#1549 Obby builds a real course; no human in the car modes; a picked ship stays · #1550 Merge has no character and its song reaches the engine · #1551 Publish once per press · #1591 Keep pills removed · #1616 Shuffle's pick is the preview's model.
  • live#1605/#1607 the create split (create_split): /create never asks, the split routes · #1669 the draw stamped on every event (it had been landing on one racing feature_flags row) · #1617 slot counts mapped onto the wizard funnel.
  • live#1666 the preview play the publish gate waits for is logged — 47 paid arrivals filled all four slots and 6 published (28 Sep – 1 Oct); the 41 who stopped can now be split into never-played vs played-but-didn't-publish.
  • staging#1721 directed walkthrough for a first-time creator: Song → Game → Character → World/Sky → Preview → Publish, one lit box at a time, the cue on the whole picker; owns no state, publish gate untouched.
  • staging#1691 Mode first, and a default that is really selected · #1713 Zombie Drive previews as itself, not Drift · #1715 a Music Aimer backdrop stays out of worlds; Obby's sky lives in the World slot · #1722 song picks and builds on LiveOps' per-song tables.

5 · Telegram

Twenty PRs on staging, one open release, and the first Telegram Ads funnel read (see Monitoring).

  • open#46 staging → main, 55 commits: #39 UX quick wins · #40 contract v1 · #41 light/dark theme · #42 loading handoff · #43–#45 fast-launch row 1 + auto-start. Its head is the staging branch, so #47–#58 ride along.
  • staging#54 staging multiplayer through a same-origin relay passthrough (engine endpoints only, shell credentials stripped) · #55 the host's real name on the guest's lobby · #56 the proxy patch's pm_* messages no longer count as the engine speaking · #57 join a Browse game outside the portal catalog; a withdrawn room cancels a pending join; a stale lookup can't override a newer choice · #58 group /start opens the app, inline results recorded.
  • On merge, Discord prod auto-deploys; Telegram and TikTok prod are manual dispatches. Before merging: the WIP Appearance panel (c1f79fe), a red contract-scan test, a staging Discord secret reset, an MP-retry smoke.
  • live#1568 the Telegram preview-clip engine ported to the portal shell (per-portal previews, migration 110) · canary#1679 Card Clips settings in the CMS.

6 · User feedback

An in-product sentiment prompt with an Ops inbox — on staging, off by default.

  • staging#1723 prompts after a preview, after creating, after two consecutive creation failures, and after completing a song or game; ratings reveal reasons; hidden during gameplay, creation and auth; partner embeds and Drops excluded · #1725 after game over.
  • staging CMS → Growth → Customer Feedback (/admin/feedback): per-event summaries, filters, review status, issue tags, notes, CSV, editable questions, sampling and frequency. ADMIN-only; the respondent snapshot comes from the submitting session, never the request body.
  • staging migrations 113 / 114 / 115; disabled by default; pilot 25% sampling, 7 days between invitations; 3,149 unit + 21 integration + 6 Chromium tests.
  • Feedback-driven fix batches: Drops Play v2 QA PV2-3 → 15 (staging#1714) · Drops UI F-01 → 08 (live#1639) · SB-21 → 24 (#1621) · GL-7 / GP-2 / GP-9 (#1643) · Telegram S42 → 44 (#55, #57).

7 · Song pipeline

Lyrics, uploads and stem size fixed on the legacy pipeline; the Inngest pipeline sits behind a switch that is inert on prod.

  • live#1567 Synced Lyrics: exact lookup → free-text search → duration ranking · #1572 undecodable uploads skip ffmpeg · #1645 bulk upload no longer dies on a 33 MiB batch (an HTML 413 hidden by a JSON parse) · #1647 artist and title from Artist - Title.mp3 and tags.
  • live#1676 Replicate stems at 192 kbps · #1654 runtime switch legacy ↔ Inngest (song_pipeline_next_enabled) — no -next service on prod, so inert there · #1661 migration guard, after the partition repair nearly discarded 13.1k events.
  • branch feat/inngest-song-pipeline: #1710 an isolated chart-analysis worker reused across songs · #1717 CMS character buttons generate a rigged character · #1719 thumbnail re-rendered after a merge · open#1692 cold start — a staging Drops upload 504'd on 5 Oct when the -next service took 61 s against the proxy's 60 s.
  • staging#1718 an existing song can re-enable 3D assets and skyboxes · #1706 Drops upload acceptance verified without consumer catalogue publication.

🚀 Release log

Serving: the 2 Oct build · engine prod = the main Pages alias, so engine releases are live on merge

Why nothing has reached traffic since 2 Oct

  • 4 Oct #1687 rewrote promotion (frozen revisions, the same ones 10 → 50 → 100%, real rollback) after finding the old 10% step used --to-latest — 100% immediately. Its own deploy run failed on merge.
  • 7 Oct #1697 merged 08:36; go-live failed 11:33, 12:18, 12:38 — all at "Shift selected revisions to 10%", each rolled back.
  • 7 Oct #1708 hotfix, tooling only: update-traffic --format=json returns a list and the script required a dict, so the split was applied and then discarded as a failure; an aborted run's rollout-* revision tripped the drift check on every retry. Three new tests fail on the old script.
  • 8 Oct #1728 (cherry-pick of #1726) retried the Logging query ×3 and finally printed gcloud's answer: PERMISSION_DENIED: Permission denied for all log views … authenticated as github-actions@playmusic-staging.iam.gserviceaccount.com. The SA holds run.admin, cloudsql.client, artifactregistry.writer, cloudscheduler.admin, iam.serviceAccountUser — and no logging role. Tuesday's "blip" was the same refusal with its stderr dropped.
  • Owed: grant roles/logging.viewer on playmusic-staging to that service account, then Deploy + Go Live on main (promotes the 3–7 Oct canaries), then back-merge main → staging or the next release reverts #1708/#1728. Before promoting: the portal-cms canary 00316-pet carries a stray third container (playmusic-portal-cms-1, old image, no port, inert) — redeploy portal-cms or remove it. Worker and Drops changes from those releases are already live — they deploy at merge.

Platform releases

DateReleaseWhatState
7 Oct#1708rollout tooling hotfix (above)canary
7 Oct#169729 PRs — Drops Play v2 (#1686 #1658 #1664 #1670 #1656 #1667 #1662 #1688 #1680) · load reliability (#1671 #1674 #1677 #1659 #1683 #1684) · feed (#1693 contest inline on paid landings, #1694, #1675) · analytics (#1660 paid cohorts + identity reconciled, #1672 msclkid, #1668, #1665, #1695) · CMS (#1679 Card Clips, #1690) · tooling (#1661 #1657 #1663). Nothing to migrate.canary Drops + worker live
5 Oct#1687rollout safety rewrite, 242 testscanary
3 Oct#1685reload diagnostics + default-off iOS browser choice (#1683 #1684)canary
1–2 Oct#1666 #1669 #1673 #1676hotfixes: Alchemy preview play · create-editor draw · Microsoft msclkid as a click id (88 of 224 Alchemy paid landings had none) · stems −40%live
1 Oct#1650Drops player accounts, scores, multiplayer invites (#1646) · October partition repair (#1652, migration 112 — prod had no analytics_events_2026_10) · Contest Rules in Promote-to-Prod (#1648, 0013) · promo same-tab + an open redirect closed (#1655) · TikTok/Meta pixels in CSP (#1644) · uploads (#1645 #1647) · pipeline switch (#1654)live
30 Sep#1640Drops UI F-01 → 08 (#1639) · play accounting (#1638) · performance (#1627) · two Google Ads conversions for the paid player KPIs (#1642) · legal slugs at the edge (#1625) · CMS manual listslive
30 Sep#1635play/create product measurement + migration 111 (#1612) · sign_up/login, run_index, play_context (#1614) · OAuth sign-in merges the guest's games (#1619) · four ordered dance routines (#1611, engine #530) · worker purge auth (#1613) + per-env KV keys (#1618) · radio scoped to /foryou + creation (#1633) · mute pauses the run (#1629) · shelf cap 12 (#1634)live
30 Sep#1631?tour=offlive
28–29 Sep#1605 #1607 #1610listen test ready: ?song= starts the station · the create split · weighted split links 75/25 (#1609)live
28 Sep#1590 #1592 #1597 #1599remix on older flight games (#1589) · Keep pills removed (#1591) · two autoplay hotfixes (#1596, #1598 + #1600)live
27 Sep#1581song feed as /foryou's default side with autoplay + first-tap fallback (#1577) · ?radio=quiet (#1579) · ?radio_card (#1580) · first-run tutorial (#1570 #1578 #1584) · arm pins for 30 days + test plan + Sky Rider D1 drift fixed (#1569) · #1567 #1571 #1572 #1576live
25 Sep#156851 commits — For You desktop layout as the / landing-split variant (#1554) · slow-connection loading (#1555) · radio + remix behind the features section, off on prod (#1561) · remix (#1559 #1560) · Alchemy #1549–#1551 · edge byte ranges + shell cache · AGENTS.md (#1553) · README (#1556)live

Engine releases

DateReleaseWhatState
3 Oct#550iOS graphics cap + bounded context reload (#548) · shared GLB textures (#549)live
3 Oct#547playable readiness + audio-load diagnostics (#544 #545 #546)live
30 Sep#541dance routine slots (#530; #534 rolled back the pose alignment) · play-path performance (#540) · a vehicle picked mid-build kept (#537) · TIME_UPDATE survives a backwards clock (#536) · debug seek off on prod (#539)live
28 Sep#528coaster ends on the shared results screen and reports the score (#527) · True Platformer self-contained, no road (#529)live
25 Sep#524merge board + run-end (#521) · startup readiness gate (#522) · Floor is Lava restart (#523) · Music Runner Play Again (#526)live
—#551–#567Drops preset Play v2 (#551 #562) · HUD dirty-check + reliability (#552 #553) · scene-loading handoff + per-mode loading (#554 #556 #558 #559 #563–#567) · Android WebView guards (#555) · Vector Squadron mode (#560) · flight shadow (#561) · canary timing (#557) — no release PRstaging

📥 Inbound — the staging → prod PRs

integrations #46 open · platform 41 commits, no PR · engine 17 PRs, no PR

Integrations #46 — open since 7 Oct 17:39

  • Everything on integrations staging: #39–#45 and the ride-along #47–#58 (Telegram, above). 55 commits, 148 files, +15,400.
  • Before merging (its own checklist): confirm the WIP Appearance panel (c1f79fe) is meant to ship · fix or accept the red contract-scan › setSettingsHandler test (3 vs 2, predates #42) · reset the staging Discord client secret (briefly exposed in a staging bundle; staging only) · smoke Discord retry with the MP gateway on.
  • On merge: Discord prod auto-deploys (packages/discord/**, packages/shared/**); Telegram prod is a telegram-deploy.yml dispatch with environment=production; TikTok manual. Scene loading stays opt-in.

Platform — the next release, not yet opened

  • Staging is 41 commits past main (#1691 → #1725): Alchemy walkthrough + fixes (#1691 #1713 #1715 #1721 #1722) · customer feedback (#1723 #1725 — migrations 113/114/115 before portal-cms) · Drops Play v2 follow-ups (#1714) · modal loading handoff (#1699), scene-loading review on for staging (#1705) · crash-report retention (#1700) · feed preload (#1724) · CMS art gate (#1718) · LiveOps Creation tab (#1720) · stray node_modules symlink (#1716) · Drops upload acceptance (#1706) · a localization and market-expansion plan.
  • #1701 prod never synced wizard-config from code, so new flags never reached it — this is why the iOS browser-choice toggle was missing from the prod CMS. Plus its Doppler-action pin fixes.
  • Already on main via #1708: the rollout fixes #1702 #1707. main is 1 commit ahead — back-merge first, then a merge commit.
  • open against staging: #1703 / #1704 visit-lifecycle events so a page killed by its host still leaves a row (TikTok preloads) · #1709 · #1711 · #1712.

Next 7 days

Promote · open the two releases · enable feedback · the pipeline cut-over
  • Unblock and promote the 3–7 Oct canaries. Grant Logs Viewer to the Actions service account (one IAM command), clean the stray container off the portal-cms canary, then Deploy + Go Live on main; keep inapp_interstitial off and read 48 h of reload diagnostics first; back-merge main → staging afterwards.
  • Open the platform release as a merge commit after the back-merge; run 113/114/115 in the canary window before portal-cms takes traffic; run the #1701 wizard-config sync so prod sees new flags.
  • Ship integrations #46 once its four items are cleared; dispatch Telegram prod; verify the second-game engine download is 0 KB on a phone. Open an engine staging → main PR for #551–#567 so #1699/#1705 have their other half.
  • Turn on customer feedback in Ops at the 25% / 7-day pilot; confirm embeds and Drops stay excluded; invitation counts are issued prompts, not impressions.
  • Decide the Inngest cut-over per environment. #1692 (cold start) blocks Drops uploads on the -next service; the switch is per-env and prod has no -next service, so staging can go first.
  • Still owed: the 23 SuperTokens accounts orphaned Jul 27 → Sep 15 · the two auth-route findings from the 29 Sep hold assessment (confirm closed or schedule) · source maps for Sentry.

🔍 Monitoring

Paid tests concluded 5 Oct · intro gate read clean · the creation loop · surfaces · Telegram Ads · measurement repairs

Paid tests concluded — every Google campaign paused 5 Oct 12:50 UTC

$9,987 Google since 1 Aug · 20,323 clicks · 274 creators · ~$18 per creator on Create campaigns (creators = the "Game Created" action only; metrics.conversions also counts Games Played).

  • Wizard Top 11 $22 / creator (impression share 12.5%, 60% lost to rank) · Wizard ZA $6 · Wizard 18–34 $10 · Alchemy ×3 $30 · Listen radio $1,018 → 0 creators (~$8 per listener).
  • Last week's $3,120 split 30 / 38 / 32 wizard / Alchemy / Listen → 63 / 36 / 1 creators.
  • Microsoft (account export, no API): Search $2,907 · 3,071 clicks · CPC $0.95 · wizard 1,120 people → 152 created (13.6%, matching Google's 14.0%) · ~$18 per creator · Alchemy 117 → 8.
  • Verdict: lean in on wizard search and Microsoft wizard; lean out of paid Alchemy and Listen.
  • Caveats: Microsoft clicks were invisible to every paid read until #1673 (2 Oct); Google served zero impressions from 12:00 PT on 1 Oct until the next morning with no change-history cause — a one-day gap, not a tracking fault.

Create expectation gate — clean read, no effect

14.9% vs 14.5% made a game, shown vs not shown (±3.6 pts).

  • First-time campaign visitors who entered the wizard, first touch 22–28 Sep PT, outcomes to 5 Oct: shown 677 vs 792 → chose a type 31% / 32% → configured 16% / 15% → made a game 14.9% / 14.5% → claimed an account 1.0% / 2.4%.
  • 26% left the gate without an exit event. The holdout is still never logged; consent gating broke eligibility from 29 Sep, so the series ends there.
  • Correction to the Sep 24 tab: intro_experiment.py reads the Ads [intro=1] trial (12 clicks, ended), not this gate.

The creation loop closed once

568 → 53 → 62 → 1 created → shared → strangers played → a stranger then created (24 Aug – 5 Oct).

  • 53 creators shared 62 games; 62 strangers played a user-made game (15 games); one of them created (28 Sep, arrived via a shared link).
  • Paid Google wizard: 348 created → 221 played their own (64%) → 96 finished (28%) → 30 shared (9%) → 8 games got a stranger play.
  • The share event keys the game as content_id, not project_id — a join on the latter returns zero silently.

Surfaces and channels

  • Plays / people / runs finished: playmusic.gg 2,003 / 714 / 80% · CrazyGames 1,321 / 964 / 70% (weeks of 14 + 21 Sep only, nothing since 24 Sep) · Playgama 358 / 160 · PlayHop 227 / 129 (197 in the last week) · FRVR 144 / 70 · Telegram 69 / 11 · Discord 65 / 14 · Y8 stopped 27 Sep.
  • Unpaid traffic is 14% of people, 47% of plays, and returns 3× as often.
  • The owned site's portal_id is the literal 'playmusic', not NULL.

Telegram Ads, 28 Sep – 1 Oct

318 → 22 → 1 opened → started a play → completed a song.

  • 318 opened → 281 saw games → 123 clicked a game (237 clicks) → 22 started a play (6.9%) → 17 ended one → 1 completed a song. 77% of game clicks never became a play.
  • Identity in the mini-app is telegram_user_id (client_fp sees ~18 of 338 openers).
  • The portal logs no visit_start, heartbeats, load stages or run events — no engaged time, load diagnosis or completion rate. The fast-launch loader stages (#53) and loading handoff (#42) start to close that.

Measurement repairs this fortnight

  • October's analytics partition was missing on prod (found 1 Oct): a future-dated row in the default partition blocked its creation and the cron reported success. Migration 112 repaired it; the cron now ensures current + next month and answers 500 on partial failure. The repair's first run nearly lost 13.1k events to the dedupe trigger — hence #1661.
  • Microsoft click ids captured since 2 Oct (#1673). user_identify carries the SuperTokens id, not users.id.
  • WebView stability from Sentry: No Brakes dominates iOS load timeouts; one Instagram iPad white-screen near-miss — the #1684 sheet is built for exactly that, still off.
  • Seen during the 8 Oct go-live window, on the live build: POST /api/cron/rollup-daily-metrics hit the 300 s request timeout (504) at 15:00 — the daily-metrics rollup that feeds this report's play counts is timing out; check daily_metrics coverage for October before quoting it.
Week of 2026-09-24. Eight staging→main merges and six gateway promotions in seven days — the most prod has moved in a week. #1495 (Sep 19) carried 254 commits: the Drops SICK redesign, Alchemy, /foryou chrome, Game Lists, click-id analytics, bulk song upload. Three same-day follow-ups made Alchemy reachable at /alchemy, split direct+organic traffic at / between home and /foryou, and stamped the arm on every event. #1527 (Sep 22) turned on crash and load-attempt telemetry for 100% of prod and shipped the create expectation gate. #1542 / #1545 / #1548 (Sep 23) fixed survival asset picks, rebuilt /games from the Front Page carousels, wired Drops consent, and took three attempts to stop the CookieYes banner painting over partner embeds. Engine gained real error capture; Telegram got its own GA4 property and a For You feed. Staging is nearly empty: two Alchemy commits.

⭐ Highlights

Sep 17 → Sep 24 · prod releases #1495 · #1503 · #1508 · #1510 · #1527 · #1542 · #1545 · #1548 · engine #509 / #517 · Telegram #32–#37

Sep 19 — release #1495 and the landing split at /

The big one landed at 04:34 (merge commit — a squash would have inherited [skip ci] from the wizard_config snapshots and skipped every Action): Drops SICK redesign P-0→P-8, Alchemy character/song/tile fixes and XP parity, /foryou chrome + Game Lists behind ?list=, Sky Rider options + collectibles, ad click-id capture (migration 109), PlayMusic Approved + bulk upload (108), ffmpeg normalisation, meshy-6-lite, real ESLint on both frontends. Migrations 107/108/109 ran 04:36; gateway 100% at 05:24 after a failed first promote. #1503 (05:36) fixed /alchemy serving the retired engine studio — AlchemyRoute fell back to the engine view whenever creation_flows.alchemy was false, which prod has; the route now ignores the flag and only the /create picker consults it. #1508 (19:03) shipped the landing split: direct and organic visitors to / are assigned at the edge to home or /foryou via a 30-day pm_lx cookie with a hop crumb; Googlebot, paid clicks (gclid) and Playgama referrals are exempt, so / stays the indexed SSR page. It also versioned the dead-R2-host repair scripts that had already fixed 2,421 catalog rows on prod. #1510 (19:27) stamped landing_variant on every event to both sinks — the split had been assigning prod visitors for 24 minutes with nothing joining an arm to an outcome.

Sep 22 — release #1527: crash telemetry on for 100% of prod, create expectation gate

Every game load now has a load_attempt_id and emits a stage event per step (open → engine_url_ready → iframe_loaded → engine_first_message → world_building → world_ready → playing), so a failed load is attributable to a stage instead of inferred from an absence (#1519). Crash reporting is narrow and opt-in — four kinds, no tracing or replay, SDK out of the bundle unless VITE_SENTRY_DSN is set — and that variable was set on Sep 21, so it went live with this deploy; #1524 opened connect-src for the ingest host. #1525 records auth failures that block generation; #1529 keeps the E2E/smoke suites out of Sentry (navigator.webdriver) and tags their analytics rows automation. Engine-side, #513/#514 add the hooks the engine never had: window error / unhandledrejection, a guarded Babylon Engine constructor, WebGL context loss and restore, and a render-stall escalation sampled from engine.frameId — fatal only for boot failures and a confirmed stall, so a game that keeps running never lands behind the host's error overlay. Promoted as engine #517. The create expectation gate (#1521–#1526): one screen between a paid click and the wizard, first-visit campaign arrivals only, 50/50 against a holdout, once-only, touches no wizard state. It also fixed two things in the shared Modal: focus containment (Tab walked out of every dialog in the product) and an elevation prop. Plus album-art upload from the CMS song page (#1520, with a worker carve-out), a failed CSS chunk preload now treated as a stale-deploy reload (#1528), and ffmpeg failures logging the tail of stderr — Sentry issue PYTHON-B8 had 4,000+ events that all read "ffmpeg version 5.1.9 … built with gcc" (#1530).

Sep 23 — releases #1542, #1545, #1548: survival picks, /games, Drops consent, and the embed banner

#1532: the config-builder worker's survival preset was applied with a blanket Object.assign, overwriting the collectible and chaser the creator picked in Step 3 — every published Horror Survival game shipped the default heart and Zed_1. The preset now fills only missing fields; games published before the worker shipped need a republish. #1534: /games grouped by the legacy projects.category string that nothing on the Front Page reads; the SPA and the crawler render now both read wizard_config.home_sections, one chip per carousel. Five slugs (drift, shooter, obby, flight, platformer) exist as both a carousel and a legacy category and the carousel now wins, so those indexed hubs shrink to the carousel's picks (staging: drift 45→6, shooter 44→3). #1533/#1535: Alchemy's Share button was createGame() with none of Publish's gating and a second project row per press — removed; pickers stop closing themselves; obby's Iso camera survives the apply. #1540/#1543: Drops gets a CookieYes banner wired to Consent Mode v2 — until now nothing ever granted consent, so every Drops GA4 hit was a cookieless ping and GA4 showed 0 events over 7 days; the script loads async after a stalled cdn-cookieyes.com was holding first paint. #1538: pm_arm and intro captured on first touch and stamped on every event — on Sep 23, 0 of 26,792 prod events carried intro. The embed banner took three releases. Yandex reported the CookieYes banner painting over the game in the PlayHop shell. #1541 (in #1542) pushed gtm.blocklist: ['cvt_KDQSW'] above the snippet on embedded surfaces — works on staging, not on the prod container. #1544 (release #1545, 22:01) gated the CMP per platform profile (features.consent true for web only; playhop gets a profile) with the sandboxedScripts token, verified against the live prod container — and the banner still showed. #1547 (release #1548, 23:05) found the real cause: prod builds carry a Google Ads snippet injected by vite.config.js that pushes three gtag entries before main.jsx runs, and GTM only honours a blocklist that precedes them; staging has no Ads id, so it could neither reproduce nor prove. The blocklist is now unshifted to index 0. Canary verified 23:15 (dataLayer[0] = blocklist, no CookieYes request, GTM + GA4 loading); promoted 23:12–23:20. #1548 also carried a nine-commit Drops batch (SB-13…SB-20 + a prosumer-api song_id::uuid compare so the 4-second status poll uses its index) that reached staging by a direct push — a mis-set upstream, since fixed.

Telegram: its own GA4 property, a For You feed, and per-launch attribution

Telegram analytics moved off the shared main-site GA4 property (where prod co-mingled with playmusic.gg on one stream) onto a dedicated property with four web streams — player/creator × prod/staging, each Measurement ID baked per build mode; the creator stream had been dead (#37). A traffic_type=internal QA flag keeps prod-stream testing filterable. #36: a For You feed screen — CSS scroll-snap with one IntersectionObserver, no gesture code, like the site's — with rankFeed as a fixed pipeline (pinned → curated-first → stable category-affinity sort → a diversity swap so no two neighbours share a song; the catalog has three "Tinfoil Hats"), a GET /api/foryou reader, every event attributed to the launch that started it, and the SDK + fonts served first-party so a filtered network can still boot. Earlier: /play opens the Mini App on the browse grid, and the prod webhook secret is set so the bot answers /start (#33–#35).

Also shipped

Sep 19 hotfixes · engine #509–#518 · Drops SB batch · dev tooling

Five overnight hotfixes straight to main (Sep 18–19)

PG-85: context menu, selection and drag suppressed on the portal chrome, the consumer embed (the frame the shell cannot reach) and, engine-side, the whole game field with a text-field exemption (#1493, engine #510). Strict-lifecycle portals: the modal sends HOST_CAPS so the engine stops granting free revives there, and the HUD lives display is driven off the revive grant rather than FREE_REVIVES (#1496, engine #511). Back-merged before #1495 so the release didn't conflict in GameModalV2.jsx.

Engine

#509 (Sep 19, pairs with #1495): Drops UI theme preset + data-ui-theme stamp with a Drops-scoped flatten, self-hosted Anton/Spectral; Sky Rider creator-enabled collectibles with picked models, belly-to-earth rest pose, lean HUD, configurable off-beat wording, negativeFeedback config; Audio Flyer values chords like Music Runner, draws the whole chart, a colour collect is a collect; shooter boss gets real HP and opening spawn HP is capped to what the player can fire; Vector Beat Arena's loudest section stopped spawning (#500); coaster honours resultsMode 'continue'; a dead rig is caught instead of freezing the character, with a script that detects skin weights that tear the mesh; GLB sub-mesh transforms applied to vegetation instance matrices. #515–#518 (Sep 22): the aimer renders the backdrop image in Mania and Taiko; the Catch backdrop scrim is configurable.

Drops batch (SB-13 → SB-20)

Back from Style keeps the character pick; the preview names every game type, not 9 of 16; the success screen and the song page share the built game, not the song; the song-level Result card is gone; songs open in the basic view and return to Studio; GTM events no longer inherit the previous event's params; success-screen GA events name their song; creator's name casing kept in the greeting (SB-12); Rename-song allows editing the artist (SB-9); iOS fixes for monochrome glyphs, fixed-sheet shift and the marquee loop (SB-5/6/7).

Smaller

Alchemy on Sep 19: Play Preview shows the scene before the overlay covers it; Play swallowed the first press then went black then trapped; Tile Mode is Drop-only and Publish keeps it; Platformer stops spawning obstacles it doesn't have; the preview camera resets; the four slots stay on screen on mobile. Thumbnail backfills authenticate their show_all read; 122 catalog has_thumbnail rows repaired on prod Sep 22. Modal reads elevation consistently. Dev container for F5-debugging one API (#1419) and a write-jira-task skill (#1537/#1539) — no runtime effect.

On staging

Two commits · three small PRs open

Nearly drained

#1549: Alchemy's Obby builds a real course rather than a level strip; no human in the car modes; a picked ship stays picked. Open against staging: #1546 (the one lint error the SB-20 change left), #1536 (homepage promo popups on the /foryou landing), #1518 (song-pipeline revision), #1490 (tracks upload as file + JSON metadata).

Next 7 days

Config promotes that gate the release · experiment plumbing · follow-ups the PRs named

Promote the config the code is waiting on

Much of #1495 is config-driven and dark on prod until wizard_config staging → prod is promoted through the CMS: Sky Rider options and entity slots, Alchemy definitions, feed lists, promo popup, contest CTA. Prod has no wingsuit/coaster rows at all. Then D1 0025_add_song_genre by hand + Sync to Edge in Game Library, or the music-genre list filters match nothing. Then republish the Horror Survival games published before the #1532 worker shipped. The coasterSkyRiderDefinitions test has been red since Sep 19 because #1483's staging D1 edit was never made — a CMS edit of the staging wingsuit row, not a code change.

Make the intro-gate experiment readable, then fix what the phone screenshot showed

In this order: (1) emit the assignment event for the control arm in creationIntroGate.js so a holdout leaves a record — a handful of lines, and until it lands the experiment returns nothing; (2) collapse the song banner on the game-mode step (h-[40vh] in StepSongPreview.jsx) — CSS only, four or five modes above the fold instead of one and a half; (3) geo-target the cookie banner in GTM; (4) re-measure the mode step alone. Keep the ads-side intro override out so the product's 50/50 stays clean.

Experiment plumbing before spend

The CMS split links (split_destinations) have never carried prod traffic; Experiment 1's Wizard-vs-Alchemy comparison needs one live. Confirm client_fp is populated on prod rows — still no fix in any release this window. Source maps for Sentry: sourcemap is off and nothing runs sentry-cli, so every crash report points at minified offsets.

Consent follow-ups the PRs named

A durable GTM trigger exception on pm_surface equals embed, then drop the blocklist line. CookieYes dashboard: mark the SuperTokens cookies Necessary, keep Support GCM on. Ask the Yandex reporter to re-check the draft. A playhop partner-registry key.

Still owed from Sep 15

Reconcile the 23 SuperTokens accounts orphaned by the Jul 27 → Sep 15 /me regression — no commit or script for it in this window.

🔍 Monitoring

Three live experiments · the intro-gate read-out · a mobile-viewport finding · one correction to the Sep 10 read-out · source for the first two: landing readout v2 (home vs feed, Sep 2026)

Landing split at / — live and readable since Sep 19 ~19:56

Direct + organic arrivals at / are split between home and /foryou; landing_variant on every event in GA4 and analytics_events.event_params. Visitors assigned between 19:03 and the #1510 promote have a cookie but no stamped events — unreadable, not backfillable. Paid, Googlebot and Playgama traffic is exempt by design, so this reads organic behaviour only.

Create expectation gate — live since Sep 22, and not yet readable as an experiment

The banner is the treatment, not an obstacle: the question is conversion with it against without, with CTA click-through as the intent signal. Early paid-cohort numbers at the game-mode step (Sep 23 pulls, DB access has since been blocked): never saw it 79 → 43 chose a mode (54%), 23 made a game; saw it 29 → 12 (41%), 3 made a game. Dismissed 17 → 8 (47%); did not dismiss 12 → 4 (33%). Of 16 shown the banner, 4 clicked the CTA (25%), 8 dismissed it, 4 did neither and left. Directionally lower with the banner; not significant (p ≈ 0.23) and not to be acted on. Three things stop this being a real read, two fixable this week: (1) the control arm does not record itself — describeIntroAssignment is only called from CreationExpectationGate.jsx, which only renders when the banner shows, so a holdout visitor emits nothing; the only holdout rows are four the ads override forced, and the 79 "never saw" are holdouts, returning visitors and segment exclusions mixed together. (2) the ads-side intro parameter overrode the product's assignment for two days, forcing the banner onto people assigned to control — removed Sep 23, so the 50/50 runs clean from then. (3) 29 vs 79 is too small either way; ~400 per arm to detect a 10-point gap. The fix for (1) is a handful of lines in creationIntroGate.js — emit the assignment event for the control arm at the moment it is assigned. Until it lands the experiment consumes traffic and returns nothing. #1538's intro stamp and scripts/analytics/intro_experiment.py still stand for the treatment side.

Mobile wizard: the song banner owns 40% of the screen at the moment we ask for a choice

Screenshotted on a 664-px phone viewport: song banner 266 px (40%), "Choose Game Mode" heading ~80 px (12%), yellow tutorial callout ~150 px (23%), game modes ~170 px (25%) — one and a half of twelve modes visible. The banner is h-[40vh] sm:h-[45vh] in StepSongPreview.jsx, so it claims the same share of every phone regardless of size, and it was still an empty black box eight seconds after load. The cookie banner then lands at 3.2 s and covers the bottom 57%, starting 20 px below the song banner; for anyone who hasn't dismissed it the picker is effectively gone — which is what the strong geography correlation was measuring. That is the second problem, not the first, and the earlier read-out had them the wrong way round. Collapsing the banner on the mode step is a CSS change and would put four or five modes above the fold; geo-targeting the cookie banner is a tag-manager setting; then re-measure that single step — what exists is a strong correlation across three markets, not proof. The rest of the readout is unchanged: the cost movement, the loop journey, and the stable bad news that about half of games are never played.

Crash + load-attempt telemetry — first week of data

Sentry receives from 100% of prod since Sep 22; automation excluded and tagged. Read with docs/load-attempt-telemetry.md's SQL. Caveat: a missing terminal stage is "unknown", not a crash, and stacks are minified until source maps ship.

Consent defaults differ between prod and staging

The prod GTM container's CookieYes template has all six consent signals defaulted to granted for all regions; staging's table is empty so the template denies by default. On playmusic.gg, GA4 gets storage before any banner interaction; staging is opt-in. Flagged, unchanged — an owner decision, not a code fix. Partner embeds run GA4 with cookies and no consent mode; ruled as designed (the partner owns the consent relationship).

Correction to the Sep 10 read-out

The hp-vs-lp label was six campaigns, not one A/B — three search, three YouTube Demand Gen — and YouTube DG was 91% of tagged sessions with almost no human signal. Any homepage-vs-feed comparison has to drop it first; pooled, it made the homepage look ahead. Search alone is too small to call in either direction. Re-read is in progress; the Sep 10 tab's per-channel conversion rates stand, its landing-page rows do not.

Week of 2026-09-17. The headline is a bug, not a feature: no real (non-guest) account has been created since 27 Jul — a typed-parameter regression from #924 made /me 500 on every genuinely new sign-in for seven weeks. Hotfixed on prod Sep 15. Every metric on users.is_guest since then has been wrong, and last week's "0 sign-ups" from the Ads test was this, not the ads. Alongside it, Experiment 1 is fully instrumented on prod (song deep-links into the Wizard, 50/50 split short-links, one creation funnel for Wizard and Alchemy, Ads conversions), /foryou finally has a Create entry, and Telegram had three production releases. Staging is loaded for the next push: Game Lists on /foryou, Alchemy reaching XP parity with the Wizard, worlds cleanup, bulk song upload, share attribution.

⭐ Highlights

Sep 10 → Sep 17 · prod hotfixes Sep 11–15 · Telegram prod releases Sep 15–16 · staging build-up for the next release

Hotfix: new-account creation had been broken since 27 Jul

jsonb_build_array is variadic, so under server-side binding Postgres could not infer parameter $6 and rejected the whole INSERT on /me. OAuth succeeded, SuperTokens created the account, /me 500'd, no users row was written, the client fell back to guest and people retried with another provider. Introduced by #924 on Jul 26; the last non-guest row was 2026-07-27 11:43. 23 real accounts exist in the auth core and are invisible to the app. Fixed in prod (23eb6b02, same as #1407 on staging); verified the INSERT prepares and completes for email and null-email providers. Follow-ups: reconcile the 23 orphaned ids; re-read every sign-up number since 27 Jul.

Experiment 1 ("Create a game with [song]") is instrumented end-to-end on prod

?song=<id> opens Wizard Step 1 with the advertised song selected (b3b6a398); &mode=<id>[&submode=] advances past the game-type step, and a hand pick disarms the auto-advance for good (#1383 + Aikido follow-ups). 50/50 split short-links draw an arm server-side, pin it in a 30-day per-code cookie, append pm_arm, forward the ad's own query, and emit campaign_split_assigned for human deduped clicks only; the CMS Campaign Builder gets an A/B section with per-arm arrivals (6c6f9e46, migration 106). Plain /c/ links now forward gclid and utm_* too (9ec8fc90, 8ebd7aba). One creation-funnel vocabulary — creation_deeplink_arrive → start → step → preview_ready → publish / error / abandon, tagged with tool and arm, arm persisted across the sign-in wall — is fired by both the Wizard and Alchemy, which previously had no tracking at all (e5312ebb). creation_created is the terminal event; creation_publish means listed (6a2612d4). Google Ads receives Game Created and Mode Selected as conversions with the real labels (b48a0f13, 0a05de94). Staging E2E covers deep-link + funnel (7e0e2de1).

/foryou gets a way to Create without leaving the feed

The Ads landing surface is a fixed full-screen pager with no BottomNav, so the only way off it was the back chevron. A slim TikTok-style tab bar now sits over the pager with the four BottomNav destinations and Create promoted to a centre + (cf4ea30d); a new feed_create_click event carries card index and visit depth so we can tell whether the feed produced the creator. The registered XP pill is a real tap target and opens the missions drawer instead of /profile (57185907, a4c0a68c); bottom-nav labels are readable over light content (3fa62bb2).

Telegram mini-app: three production releases

A 32-commit staging→main promote (28ecc73) fixed the "stuck on Finding the Match" chain (leave stranding the next match, engine-spoke gating, deferred picker, engine addressed at contentWindow) and the room-config gameMode bug that made guests refuse the host's room; the same gameMode fix was ported to the TikTok activity. GA4 via gtag.js + LiveOps portal funnel landed, then four analytics defects were fixed in the production funnel (c6d55cd): every prod event was labelled environment=staging (the *.workers.dev hostname matched an includes("dev") test — LiveOps filtered to production showed no Telegram traffic at all); the #tgWebAppData launch fragment with user ids and the signed auth hash was reaching Google Analytics on every hit; scoreless runs dropped game_play_end/song_completed. Two more releases followed: a KV-backed leaderboard fallback with the global board derived from raw scores, and brand-2026 alignment (slate palette, site mark, card design, preview clips). CI: Telegram auto-deploys staging on push, production stays a deliberate dispatch.

Also shipped to prod

Alchemy hardening · run-end & retention · four pieces of prod plumbing that were silently broken · engine

Alchemy hardening (#1351–#1364)

White screen from a TDZ in the mode-options callback (#1364); mode cards get thumbnails and only the options that belong to the mode (#1362); Obby was unselectable until a song was picked (#1358); the slot panel confirms a selection, honours skip flags and gets the brand pass (#1359); characters come from the catalog the Wizard reads and FX is hidden on course modes (#1356); the platformer course no longer rebuilds on every parent render (#1351).

Run-end and retention on mobile

Play again is a reachable target on mobile and stands the run-end UI down when pressed (1d48e720, c68164d8); the progression flag resolves when the guest read times out, and the test checks the flag field rather than the config object (92e1fe5d, f8f96209); modal analytics lifecycle and teardown delivery hotfixed (18976f16).

Prod plumbing that was quietly failing

CSP now allows CookieYes hosts alongside Cookiebot — the prod cookie banner had been blocked since the migration (f0221895). The CDN answered 206 to plain GETs on /playmusic-pipeline/*, so card-image generation rejected its own reference frames (aafb02cb). Prod portal-cms was missing MEDIA_SERVICE_URL and GAME_PIPELINE_URL, so screenshot capture was unavailable (14c99e92). The Drops Doppler overlay was repointing consumer storage — prod NoSuchBucket (3e3ff5a7).

Smaller

Asset Library gains display-name editing (090e3db4), rename returns 404 for an unknown id (#1368). Dead Discord invite in header and footer and the "Music Music Games" typo hotfixed (fff5f8cd). Survival's env picker ignored its own exclusions (b668fe31). Engine main: the clicker/merge Play card vanished one frame after the mode showed it (2d222c65); no lane-switch line on a side-camera platformer (engine #487).

On staging — queued for the next release

Game Lists · Alchemy #1399–#1428 · worlds cleanup · bulk song upload · share attribution · engine

Game Lists — CMS-managed discovery feeds, now on /foryou

Editors build named lists (manual picks or rules over game genre / music genre / artist / song / game id) with a default sort and the filters players may change; resolved client-side with zero per-filter requests (60170e67). Built on the existing generator as a new wizard_config type game_feeds (D1 edge, Promote flow) rather than a new R2 pipeline; D1 0025 adds song_genre to projects. Hard rule: a game with no preview clip is never a list member. Product moved the player surface from /browse to /foryou and restored /browse byte-for-byte (05a4eaff): with no visible list the feed is exactly the previous one; with ≥1 list a second chrome row (list ▾ · Filters · sort ▾ · count) drives the pager and the view lives in the URL.

Alchemy reaches parity with the Wizard (#1399–#1428)

A game made in Alchemy now earns what one made in the Wizard — 150 XP, missions ticked, celebration toast, first-creation mark for guests (#1426). Publish is gated, the game screen's chrome is restored and Shuffle can no longer pick art that does not exist (#1422); the preview stops before Publish creates the game (#1423) and the published game has a way out (#1424). Mobile picker takeover, song-art parity, slot labels and publish identity (#1411); the world picker offers what the Wizard offers and a world you're given is one you can pick (#1427); the preview names the mode it is previewing (#1428); the engine's menu is hidden while building, play is full-screen, mobile gets an exit (#1406); tutorial held, auto-picks shown, finished game marked (b50421d9).

Worlds cleanup (#1429–#1433)

Skyfall is Sky Rider's world only; Boujee Drift is not a world and leaves the pickers; Boujee Night is withheld from modes that render a character; Skyfall, Backrooms and Boujee Night tiles resolve to new art. The Wizard decides the world step from the mode definition rather than mode-id lists (7dfc932c) and shows a World confirmation tile only where a world was applied silently.

Songs: PlayMusic Approved flag + drag-and-drop bulk upload

Informational "PlayMusic Approved" flag (migration 108) on single and bulk forms; new POST /api/tracks/batch-upload takes multiple MP3s with per-file error isolation, then a per-file table → shared fields → cost estimate → generation → live batch status (291e1dc4). Fixed along the way: the "artist required" check ran before ID3 extraction, so a blank artist could never be backfilled from the file. Per-song metadata is editable after a bulk upload.

Share attribution — shares were invisible, not absent

Every share copied a raw /play/<id> URL and never called the share API: share_links last gained a row on 15 Jan, and all 22 September share events carried short_code = null, so a recipient looked exactly like direct traffic. The resolver was fine (verified on prod: 307 with ?ref=, bad codes handled, click counted); only the caller was missing. Tracked short links are now minted on menu-open (a1747d16).

Also on staging

Portal config serialises each game's multiplayer flag (ccb373e2); GTM event-trigger regex widened to feed/identity/retention/session events (ea0a5dd5); migration 107 wired into both runners; wizard-config sync accepts game_type_order so it writes anything at all (b3e95314). Engine: Vector Beat Arena's loudest section stopped spawning, plus a song indicator (engine #500); wingsuit lean HUD by default with configurable off-beat wording; coaster honours resultsMode 'continue'; shooter boss gets real HP and opening spawn HP is capped to what the player can fire; a dead rig is caught instead of rendering a frozen character, with a script to detect skin weights that tear the mesh; audio-flyer values chords like Music Runner, draws the whole chart, and a colour collect is a collect not a detonation; MODE_CAPS coverage pin 24 (engine #481). Integrations: TikTok deploy falls back to staging not production; the MP lobby reads the engine's lobbyState; guest toast wording matches the host side.

Next 7 days

Week 1 of the eight-week experiment plan · next prod release · account reconciliation

Launch Experiments 1 and 2

Per the eight-week plan: "Create a game with [song]" → Wizard Step 1 vs Alchemy through one split link (~$500 / $500), and "Play rhythm games" → one curated page (~$1,000). Verify ad → /c/ → correct song → creation → publish, and ad → page → play, with attribution intact, before releasing full spend. The curated rhythm page is what Game Lists is for — it needs the next release, or a feed_order-pinned interim.

Staging → prod release

Game Lists on /foryou, Alchemy #1399–#1428, worlds cleanup, bulk song upload, share attribution, GTM triggers; migrations 107 and 108 plus D1 0025. Then an engine promote for wingsuit / coaster / shooter / audio-flyer / VBA and a wizard_config promote behind it.

Reconcile and re-baseline accounts

Fold the 23 orphaned SuperTokens ids into users; re-read every sign-up and is_guest figure since 27 Jul; restart the sign-up series from Sep 15.

Telegram

Deliberate Deploy Telegram production dispatch for anything still on staging; confirm prod events now carry environment=production and that no tgWebAppData reaches GA.

🔍 Monitoring

Corrections to prior read-outs · readiness for Experiment 1 · still-open tracking gaps

Sign-ups: series invalid 27 Jul → 15 Sep

0 non-guest users were created in that window because creation was broken, not because nobody tried — 23 accounts made it into the auth core. Last week's Ads read-out ("0 sign-ups, 5 guests") cannot be read as an ads result. Re-baseline from Sep 15; treat any account-based KPI in the Aug 6 → Sep 10 tabs as unmeasured.

Shares: 22 events in September, 0 attributable

Instrumentation gap (no short code minted), fixed on staging only. Keep "share → play" at n/a until the release ships; do not read the September zero as "sharing doesn't work".

Telegram traffic was hidden from the production LiveOps view

Until c6d55cd every prod Telegram event was labelled staging. Any Telegram number filtered to production before Sep 16 is zero by mislabelling; re-pull after the fix.

Experiment 1 readiness

Arm assignment is server-side per short code with a 30-day cookie and a forged-pm_arm guard, so the pill-vs-missions failure mode (no config key, split drifting 24–97% hourly) does not apply. Both tools fire the same funnel. Still to confirm before spend: client_fp populated on prod rows (the Sep 3–7 blind spot — no fix for it appears in this week's history), and that the split link survives the sign-in wall end-to-end on prod, not just staging E2E.

27-item initiative inventory · status as of 2026-09-10. Two goals change K — D1 retention (more sessions) and player→creator conversion (higher p per session); they multiply. Everything else feeds the loop, proves a constraint, or gates measurement. Status is backed by PRs where work is in progress; "No PRs" means nothing has landed in the platform repo yet.
Shipped · 3 Always on / ongoing · 4 In flight · 4 Dated ETA · 5 Test / trial · 5 Not done · 4 Unfunded · 3

🎯 Inside the loop — the work that raises K

Goal 1 · D1 retention up (#1–#4) · Goal 2 · player → creator conversion (#5–#7) · Catalogue (#27)
#InitiativeBand StatusETADeliverable Supporting PRs / evidence
1 Progression + live systems — player level, economy, onboarding, mission system, creator conversion, tournaments (incl. further iteration) Loop · Retention Shipped; iterating — Shipped surface #981–#1006 (D0–D1 retention, 11 PRs) · #1227 retention on landing feeds · #1350 guest identity self-heal
2 Game templates — Dance, Zombie Drive, Merge, Platformer (8 PoCs held as option value) Loop · Retention September Sep Shipped surface #1276 Beat Dance → main (#1208–#1220) · #1335 Rollercoaster & Sky Rider creatable · #1346 True Platformer course · Merge: no engine template yet
3 playmusic.gg UX — visitor → player conversion Loop · Retention Always on — Shipped surface #1260 mobile LP release · #1282 / #1284 /foryou observer fixes · #1350 guest half-state
4 Front page curation, chart testing Loop · Retention End September End Sep — #1229 CMS feed order (default sort + pinned) · chart testing: no PR yet
5 Wizard improvement — multi-character & object expansion Loop · Creator Dev Complete — Shipped surface #934 / #942 CMS asset slots · #926 extra slots · #1248 entity-slot seed · #1352 override whitelist
6 Creation flow UX improvements Loop · Creator Always on — Shipped surface #1054 wizard port · create-success screen · #1332 Beat Dance preview fix
7a Creator flow alternatives — Alchemy Loop · Creator Early October Early Oct — #1325 live (gated) · #1337–#1349 editor (13 PRs: CMS controls, slots, themes, publish payload, camera/FX, song sections, True Platformer) · #1351 course rebuild fix
7b Creator flow alternatives — Director Loop · Creator Not started — — No PRs
27 Top 100 + Top 50 global trending track support Loop · Catalogue Shipped; ongoing ingestion — Shipped surface — ongoing ingestion #648 catalog CDN URLs · #894 song-asset thumbnails · asset-ingest job (backfill_has_thumbnail)

📣 Visitor acquisition — feeds the loop, doesn't raise K

6 of 8 are tests: the deliverable is a cost-per-user verdict per channel, not volume. Distribution-led (#15) is durable placement, survives a paid-media freeze.
#InitiativeBand StatusETADeliverable Supporting PRs / evidence
8 Google paid search tests Acquisition In flight — — #1074 Google Ads id · hotfix ads-tag-in-html · #1286 CSP GA4/Ads pixels · gtm-ads-conversion-tags · Sep 3–7 HP vs LP test (see Week of Sep 10)
9 Telegram mini-app + TG Ads test Acquisition Early September Early Sep — No PRs in platform repo
10 Performance ads — TikTok, Meta, Google Acquisition End August End Aug — fix/csp-ads-conversions · #1286 CSP audience pixels · campaign-session-attribution
11 Artist contests (≥2) — Warren Zeiders 8/28, 9/16 Acquisition Dated 8/28 · 9/16 — #923 contests promote-to-prod · promote-contests-promos
12 Playgama paid UA test Acquisition Test — — #1206 portal config self-resolve · campaign paused 19 Jul
13 Influencer alternatives — wavelink.co Acquisition Test — — No PRs
14 Astrocade paid-per-play tests Acquisition Test — — No PRs
15 Distribution-led growth — PlayHop, CrazyGames, Yandex (incl. Artist Games) Acquisition Ongoing — Durable placement PlayHop SDK live (migration 087) · portal-discord-toggle · FRVR embeds live
16 LG TV app Acquisition Shipped — Shipped surface —
17 Poki Acquisition Trial — — No PRs

🎼 Prosumer / supply — proves or kills “content supply is the constraint”

Demand test from DIY / indie artists; initial UA via clipping campaign
#InitiativeBand StatusETADeliverable Supporting PRs / evidence
18 Prosumer offering launch — DIY / indie demand test Supply End August End Aug — #955 base tier · #963 isolation · #1183 drops.game play modal · #1199–#1205 CMS/roles · #1309 / #1312 / #1328 / #1329 fixes
19 Clipping campaign UA (ugc.ninja) Supply September Sep — No PRs
20 Warren Zeiders games Supply Content — Content —

📐 Measurement — hidden but essential

Without these nothing above is measurable per step. Every K figure is a model number until #21–#23 ship. The Sep 3–7 Ads test hit exactly these gaps (client_fp empty, no landing-page tag, no A/B config key).
#InitiativeBand StatusETADeliverable Supporting PRs / evidence
21 QA for analytics events Measurement Not done — Gating dependency #1299 tracking backlog (14 items, staging) · 124d4822 feed-depth tracking · client_fp still empty in prod
22 Funnel measurement Measurement Not done — Gating dependency #794 / #810 / #822 campaign tracking · campaign-session-attribution · portal vs game identity not joinable
23 CMS — publishing-rights support, reporting Measurement Not done — Gating dependency — also gates #27 No PRs

💡 Proposed — unfunded

Shipped surfaces but no budget; #24 is the only organic-acquisition item in the list
#InitiativeBand StatusETADeliverable Supporting PRs / evidence
24 Auto-clip → 9:16 → "Enter this world" end card Proposed Unfunded — Shipped surface — only organic acquisition in the list —
25 Varied worlds bound to play behaviour (combinatorial) Proposed Unfunded — Shipped surface —
26 Remix via parent id + attribution Proposed Unfunded — Shipped surface —
Week of 2026-09-10. Two production releases: #1325 (Sep 9) scaled the pipeline, hardened catalog visibility, shipped Alchemy (gated), and guest-identity self-heal. #1332 (Sep 10) unlocked Rollercoaster & Sky Rider as creatable types with dark-mode variants, completed Alchemy restoration (CMS controls, asset slots, themes, publish payload, camera/FX, song sections, True Platformer course), and fixed Beat Dance preview. Google Ads landing-page test report: search 8.3%, YouTube 0.14%, data quality caveats documented.

⭐ Highlights

Sep 9–10 · Two releases (#1325 + #1332) · 13 Alchemy PRs · two new game types · retention fix · analytics polish

Production release #1325 (Sep 9) — pipeline scale, catalog security, guest retention

Staged 7 days of work to prod: pipeline asset-ingest scaling, catalog visibility hardening (auth + search perms), Alchemy brought into the live creator flow (feature-gated, safe to iterate), guest identity self-healing (#1350) so half-states don't mint duplicate guests, and analytics event QA (tracking backlog cleared).

Rollercoaster & Sky Rider become first-class game types

Both modes unlocked in the creator wizard as standalone game types, not variants. Engine now supplies correct defaults per mode; wizard overrides that forced wrong models got reverted. Rollercoaster got dark-mode support (#1335). Sky Rider dark-mode variant now available too.

Alchemy editor completed across 13 PRs

Shipped in pieces Sep 3–10: CMS controls for Alchemy modes (#1337), asset slots UI (#1342), fill-the-blank mode messages (#1344), True Platformer course (#1346), theme toggle (#1348), publish payload (#1347), song sections UI (#1343), camera & FX controls (#1339), world pins & player slot (#1338), and environment names (#1349). Platformer reference-error fixed (#1341). Isometric view dropped from wizard config (#1340).

Guest identity self-healing (retention win)

Laggy publishes + dead previews + creator flips left guests with session cookies but missing localStorage tokens, triggering the create gate to mint a second guest identity on next visit. Now detects and merges half-states into one identity (#1350). Tests locked in.

Beat Dance preview two-character body fixed

Preview showed engine's runner character + selected dancer simultaneously. Scoped runner suppression to real games only; song preview now correct from start. Dance mode-init now seeds the selected character before apply, so preview and game show the same model (#1332).

🔍 Monitoring

Google Ads Sep 3–7 · Homepage vs /foryou feed test · rebuilt 8 Sep

Search converts 59× better than YouTube (but tracking gaps inflated both numbers)

Search: 64 of 770 sessions started a game (8.31%, 95% CI 6.6%–10.5%). YouTube: 11 of 7,756 (0.14%, 95% CI 0.08%–0.25%). The gap is real (p < 0.0001). But YouTube's tagged-session count is ~11× too high because the app's browser wipes its memory each session, so every load looks like a new person; search inflated less. YouTube's 0.58% "human activity rate" (tap, scroll, or play) vs search's 14% is the finding: YouTube delivered almost no people, not YouTube people didn't play.

Landing-page question (homepage vs /foryou feed) unanswered

Search showed feed → 9.4% conversion vs homepage → 6.9%, but needed ~900 tagged sessions per page to tell if real; test had 469 feed, 291 homepage. YouTube showed 0.16% feed vs 0.08% homepage, also too small. Statistically, both differences are noise (p > 0.23). Takeaway: need ~1,800 total search sessions per test repeat to answer this.

Pill vs missions A/B test didn't run

Configuration key to randomise pill vs missions display never shipped to production. Split swung 24%–97% hour-by-hour (a real coin flip would stay ~50%), so no experiment took place. Pill rates: search 10.5%, YouTube 0.22%. Missions: search 8.3%, YouTube 0.08%. Differences are not trustworthy.

Sign-ups and game creation stayed flat

0 sign-ups (5 accounts created were all guest). 7 games created (0 published). 19 of 361 games were played at least once. Campaign generated 7–10× traffic increase but no creator lift.

Key unknowns: device fingerprinting, landing-page tagging

Device fingerprint (client_fp) was empty on every production record during the test—the one field that would answer "is YouTube real people or app-auto-fetches" outright. Tagging was inferred after the fact by landing-page assignment; a proper tag in the ad link would settle homepage vs feed. Both fixes are done on staging and ready to ship. Next test should run with both live.

=============================================== -->
Week of 2026-07-30. The headline is a production release on Jul 29 (staging→main) that carried three weeks of work to playmusic.gg — and required a careful auth reconciliation to merge duplicate accounts that had formed across environments. In parallel, the creator wizard turned into a real game-mode toolbox: Horror Survival, Spy Hunter, UA/Zen shooter styles and a Merge mode all became first-class options, backed by CMS-defined asset slots and a new snapshot/restore safety net for wizard config.

⭐ Highlights

Jul 24 → Jul 30 · prod release (#947) + creator-wizard modes across engine & platform

Production release — three weeks live on playmusic.gg

Everything from Jul 12→21 — SEO/LLM discovery, the performance pass, portal LiveOps & retention, catalog thumbnails, PlayHop — reached production in one staging→main push (#947). It bundled an auth reconciliation: a migration (094) that backfills linked account IDs and merges the duplicate accounts that formed when the same person signed in across staging and prod.

The creator wizard becomes a game-mode toolbox

Horror Survival is now its own mode (with a Horror Runner submode), Spy Hunter joined as a vehicular-combat play style, the shooter gained UA Mode and Zen Flow styles, and Merge became a selectable type. New environments (boujee night/drift, backrooms) and skydomes surfaced too. Creators now pick from a genuine menu of genres instead of a single runner.

CMS-defined asset slots per game type

The CMS now decides which asset slots — traffic, enemies, obstacles, collectibles, characters, environments — a given game type exposes (#934, #942), and players can opt into extra slots (#926). Content, not code, shapes what each mode lets you customize.

Wizard-config snapshot / restore + auto-sync

Wizard configuration now syncs additively from code on every deploy (#931) and can be snapshotted and restored from D1 (#932), so a bad edit or a missed seed can no longer silently wipe modes or environments.

Also shipped

Home create surface · contests promote · funnel analytics · play counts

Home create surface polish

featuredCreate modes moved onto the Home page (out of the /create picker) with a readable "New" badge, and the create banner now sits under the Featured Drop hero.

Contests & Promotions promote-to-prod

Contests and promo campaigns can now be promoted staging→prod through the same lineage-tracked flow as games (#923); contest migrations wired into both runners.

Creation funnel visibility

New LiveOps view into the create→publish funnel plus per-song completion attribution (#937).

Play counts stop jumping

The homepage/modal play count now reads a live D1 edge counter (#941), so it no longer flips between a stale replica and the live number.

Next 7 days

What's queued
→ Prod smoke + monitoring on the Jul 29 release (auth reconciliation, play counts)
→ Creator-wizard game-mode buildout toward a stable, curated mode menu

R&D / PoC

→ Engine "recipe" system — data-driven modes (camera / spawner / scoring) toward mode mashups.
→ New modes maturing — Musical Chairs, Zombie Drive, physics arena, 2D platformer.
Week of 2026-07-23. A discovery-and-performance week. PlayHop — Yandex's games portal — went live as a new distribution channel. The SEO/LLM push matured into real discovery pages (directory pages, an entity graph, hub enrichment), while a performance pass cut the app's main bundle and image weight dramatically. Underneath, cross-environment auth got hardened after duplicate-account and cookie-bleed issues, and a Contests feature landed. The engine, meanwhile, spun up a wave of new play styles — a full shooter, Horror Survival, a Spy Hunter mode, and a brand-new 2D platformer.

⭐ Highlights

Jul 17 → Jul 23 · PlayHop live · SEO/LLM + perf on platform · new engine modes

PlayHop distribution live

PlayMusic games now run inside Yandex's PlayHop portal via its SDK (#825) — a third external surface alongside Discord and TikTok — including the Yandex SDK integration and its auth/player quirks.

SEO/LLM discovery pages

The discovery layer went from hubs to a real graph: directory pages (#849), entity-graph IDs linking songs/artists/creators/games (#848), LLM-oriented hub enrichment (#850), an is-indexable gate wired via migration (#839), and a sitemap/robots audit (#834) that cleaned up what search engines and AI crawlers see.

Performance pass — bundle + images

The main JS bundle dropped from ~809 kB to ~625 kB (SuperTokens UI out of boot, path-based chunking) (#871), the mobile LCP chain was broken up with hero preload (#870), and wizard/catalog images now route through CDN resize — roughly 11–20× smaller (#917). Noticeably faster first load, especially on mobile.

Cross-environment auth hardening

Signing in with a second provider now merges into the existing account instead of creating a duplicate (#886); the OAuth connect flow was locked to same-origin with CSRF nonces (#885); stale cross-env session cookies get cleared (#879); and cached API responses vary on Origin to stop cross-origin CORS bleed (#877).

Also shipped

Contests · draft-first games · portal retention · engine play styles

Contests feature

A Contest Hub with a homepage entry point, landing pages, and CMS authoring (#914), a brand-styled leaderboard with podium + player position (#915), and contest-rules links in promo popups (#913).

Draft-first player games

Player-created games now start as drafts; publishing is a PlayMusic-gated step (#916), plus a post-run "Create your own game" prompt on playmusic.gg.

Portal LiveOps analytics

Retention split by acquisition source × destination — playmusic.gg vs portals side-by-side (#861, #863) — and play-count consistency fixes (#857).

Engine — new play styles

A full shooter pass (walls, power-ups, boss, HP display, weapons), a config-bootable Horror Survival, a Spy Hunter vehicular-combat mode, and a new 2D platformer package with double-jump, gravity-flip, and on-beat scoring.

Next 7 days

What's queued
→ Ship Jul 12→21 to production (lands Jul 29)
→ Wire the new engine modes into the creator wizard

R&D / PoC

→ ModeRecipe engine — a data contract to compose modes without bespoke code.
→ Contest operations and the first live contest.
Week of 2026-07-16. A production push on Jul 12 (#732) carried the two-step draft→publish flow, catalog thumbnails, the Search & Social report, promo-campaign promotion, and the Playgama ads rework to playmusic.gg. The rest of the week was polish-to-prod: a long run of Playgama ad-integration fixes (pause-on-ad, eligibility, legal), embed load-time improvements, and thumbnail correctness across the asset catalog. The engine shipped a tutorial-copy overhaul and its first UA-Mode shooter and weapon system.

⭐ Highlights

Jul 10 → Jul 16 · Jul 12 prod push (#732) · Playgama + embeds + thumbnails

Create → Publish shipped to production

The new two-step draft→publish flow went live (#732, #738): players create a draft, then publish as a distinct, gated step. This was the release's highest-risk item and cleared prod after a staging publish-route fix.

Catalog thumbnails end-to-end

Song/character and asset thumbnails now render across the CMS asset library and game cards, backed by a has_thumbnail flag (#718), ingest-time rendering (#796), and cache-busting on rotation — no more initials-only placeholders.

Playgama ads hardened

Ads now pause the whole game (not just audio) on overlay (PG-35/37), respect multiplayer eligibility (PG-39), carry legal/noindex correctness (PG-44), and route share correctly on TikTok/Discord (PG-40) — the fixes that make monetized play feel intentional rather than jarring.

Faster embeds

Embedded games got edge-cached config (#788), engine preloading (#789), and deep-link slug resolution (#790), cutting the time from click to playable when a game is embedded elsewhere.

Also shipped

Search & Social report · engine tutorials + shooter · flight fixes · SEO plumbing

Search & Social report live

The Soundcharts + Google Trends CMS report (admin-only) went to production with its migration and scheduler.

Engine — tutorials & first shooter

A full audit/rewrite of every game and sub-mode's instruction copy (#310), plus the first UA-Mode shooter with a generic projectile/weapon pipeline (Pistol).

Engine — flight & platformer fixes

Per-mode flyer orientation, dynamic floor, collision anchoring, and a single config-driven platformer health/lives model.

SEO & social plumbing

Organization sameAs socials (#807), a Discord social footer (#805), and robots rules that disallow internal routes (#809).

Next 7 days

What's queued
→ PlayHop portal go-live
→ SEO/LLM discovery pages and a performance pass

R&D / PoC

→ Shooter gameplay depth — bosses, power-ups, HP.
→ Horror Survival and a 2D platformer prototype.
Week of 2026-07-09. A dense delivery week that ended in a production release (#699). The headline is a gameplay-video sharing suite — record, share, and rich-link your runs — landing next to a personalized home (My Games, Followed Creators shelves) and a content-takedown system with real enforcement. The creator wizard got a large batch of fixes, and the engine grew a Match3 family, an Alchemy "fill-the-blank" asset picker, and a SIDEKICK avatar builder.

⭐ Highlights

Jul 3 → Jul 9 · prod release (#699) · video sharing · personalized home · takedowns

Gameplay-video sharing suite

Players can record a run and share it with rich social links (#696), a video library (#697), and an in-player share (#698) — turning a play session into shareable content across social surfaces.

Personalized home

My Games and Followed Creators shelves now inject over the CMS home layout (#687), plus a "create your own" card (#684) and the brand-2026 polish — Featured Drop, posters, badges (#702).

Content takedowns + enforcement

A global takedown system that hides taken-down content everywhere and blocks republish, with coverage logging (#659, #661, #675) — the compliance backbone for a UGC catalog.

Engine — Alchemy asset picker & Match3

The Alchemy slot panel became a "fill-the-blank" UX driven by live catalog data (#280), a Match3 mode family shipped with four forks, and a SIDEKICK avatar builder PoC (customize + animate) opened up.

Also shipped

Search & Social report · Director NLE · wizard batch · share hub · ingest

Search & Social report + Director NLE

First cut of the Soundcharts / Google-Trends CMS report (#703) and a Director prompt/NLE mode with fuller mode coverage (#704).

Creator wizard batch (#706–#717)

Multiplayer defaults on in-card, unpublished-song characters surfaced, flight preview live-swap, model-less song handling, and a raft of scroll/preview fixes.

Share Hub / Invite Players

A refreshed share hub with an Invite Players component and QR codes (#719).

Ingest & catalog correctness

Vehicle category (#713), idempotent incremental sync (#721), and is_public preserved so CMS "hide" toggles survive re-ingests (#725).

Next 7 days

What's queued
→ The Jul 12 production push — draft→publish, catalog thumbnails, Search & Social, Playgama
→ Playgama ad-integration hardening

R&D / PoC

→ SIDEKICK avatar builder — clothing, accessories, dance.
→ Director-authored multi-level games.
Week of 2026-07-02. Two strategic streams moved from plan to running code this week. Prosumer — the independent-artist self-serve track — shipped as a real end-to-end vertical slice (API, frontend, migration, staging deploy) and got hardened through its first live sign-in and song-upload bugs. And the SEO/LLM workstream went from foundation to a full indexing + landing-page builder (P1–P5). Alongside, the game-preview capture pipeline finally lands on real gameplay.

⭐ Highlights

Jun 25 → Jul 2 · PMH + engine merged; TikTok (integrations) + Prosumer in flight on staging · relay quiet

Prosumer — first end-to-end vertical slice is live

The independent-artist self-serve track went from architecture doc to running code: a full slice spanning the API, a dedicated frontend, database migration, and a staging deploy (#455). We then worked through its first real-world bugs — sign-in was 500-ing and song upload was 502-ing, both traced to the frontend's own nginx proxy (SuperTokens headers not forwarded; API resolver + Host header; a reserved PORT env blocking the gateway route). Artists can now stand up on the prosumer frontend on staging.

SEO/LLM — full indexing + landing-page builder (P1–P5)

Last week's AEO foundation grew into the real thing: SEO/LLM indexing with a block-based CMS landing-page builder, image/video content blocks with upload, and the ability to link songs/artists on a game page and manage Song/Artist hubs in the CMS. Migrations wired, human-preview sign-in gate fixed. This is the machinery that turns curated song × genre combos into publishable, discoverable pages.

TikTok Mini Game — Discord parity + multiplayer (on staging)

A third distribution surface came together this week: the TikTok Mini Game landed on staging with full feature parity to the Discord Activity — games browser, scores, XP, song progress, likes/achievements, game-proxy, and multiplayer signaling — plus a two-tab dev harness so multiplayer can be exercised without TikTok partner access (one browser acts as streamer + viewer against the real engine). A cross-distribution platform-profiles framework was also drafted, with TikTok as profile #1. Open PRs to staging (integrations #16, PMH #585), not yet merged to production.

Preview capture — real gameplay, finally

The preview-video pipeline landed on the approach that actually works: capture real gameplay via page screenshots at speed, starting games with a DOM click (Playwright's normal click was hanging on Cloud Run) instead of frozen frames or engine-side exports. Preview videos now show the game genuinely being played — the missing piece for rich, trustworthy game cards.

Also shipped

Home deep-link · genre hubs · asset rotation · wizard camera · flight/obby

Home deep-link control

A global home-deep-link default plus a per-game tri-state override in the CMS — fine-grained control over whether a home-screen click routes straight into a game or to its detail page.

Genre & Song/Artist hubs

Game pages now link their song and artist; hubs show all categories with a published-fallback; game-page hub links wired end-to-end — part of the same discovery push as SEO.

Asset rotation & CDN correctness

Model load + rotation fixes across multi-storage backends, credential handling, and a CDN purge on rotation so the immutable-cached viewer reloads the new model instead of a stale one.

Wizard camera & game-mode fixes

Camera reset-to-standard and the top/flight/obby view fixes (partial APPLY_CONFIG no longer stomps a picked Top variant); flight generated-ship-on-floor and drop/obby fixes in the engine; a 4-player game-over guard and leaver-forfeit result for multiplayer.

Next 7 days

What's queued
→ Bug fixing / performance improvements
→ CMS review session

R&D / PoC

→ Prosumer — continue infra / DevOps / user-flow hardening; move the vertical slice toward a usable self-serve loop.
→ Avatar generation
Week of 2026-06-25. Discord multiplayer was the big push — the two failures that made it feel broken (rooms dying after an idle drop, and not being able to start a fresh lobby after a match) are fixed with a new single-socket gateway. Two new bets opened up: rewarded ads running live through a Playgama vendor, and the first SEO/LLM discovery pages so curated games can be found by search engines and AI.

⭐ Highlights

Jun 18 → Jun 25 · across consumer, engine & relay

Discord multiplayer — reconnection fixed + single-socket gateway

Discord Activity multiplayer had two structural problems, both now fixed. Rooms died on an idle drop — redundant reconnect attempts were tearing down healthy connections and never recovering, so you had to relaunch the activity. And you couldn't start a fresh lobby after a match — Discord only allows one live connection per activity, so opening a second one just hung.

The fix is a new single-socket gateway: one persistent connection for the whole session, owned by the outer activity frame (which survives Discord's menu navigation), with rooms switched over control messages instead of new connections. It's behind a flag with a one-line rollback. We also tidied the lobby/spectator experience: watch-instead choice on invites, switching to spectator mid-match, spectators see the results screen, and a host "force-start" to unblock stuck lobbies.

Playgama PoC — rewarded ads through the vendor layer

We wired Playgama in as a first-class ad vendor with a new ad manager that routes rewarded-ad requests with CMS-driven vendor selection — proving the end-to-end monetisation path on a partner channel. Sits on top of last fortnight's session-tracking fixes, so Playgama sessions now report clean length data alongside the ad path. The vendor abstraction means the next partner plugs in without a rewrite.

SEO / LLM discovery pages — AEO foundation (in flight)

A new workstream to make curated games discoverable by search engines and LLMs: llms.txt, server-rendered game-detail pages with structured data (JSON-LD), AI-bot crawl rules, and per-game SEO overrides in the CMS. The model is deliberately curated, not automatic — we hand-pick the song × genre combinations worth owning, gated by a single editorial toggle that defaults to no-index. Architecture is merged; the first feature slice (P1a) is on a branch, not yet to production.

Also shipped

Previews · wizard UX · multiplayer correctness · skydomes

Game previews — big push

Manual preview-video upload and preview controls in the CMS Game Library, hover-to-play preview videos on the consumer feed, and a "has preview video" filter. Capture got much more robust across every mode — dismissing the start screen reliably, capturing actual gameplay rather than the menu, and not mistaking rhythm-mode audio for "song complete." Plus an in-engine 3D model preview in the CMS that shows real in-game orientation.

Wizard & vehicle-picker fixes

Camera perspective only resets on an actual game-type change; the mode accordion centers when it opens; the character/vehicle picker got a right-edge fade so hidden cars are discoverable; going Back from Step 3 keeps your picked vehicle; song-generated vehicles now reuse the catalog thumbnail.

Multiplayer correctness — drift & flight

Resolved the phantom 3rd car in drift and the oversized opponent ghost; fixed flight-ghost orientation, grid offset, flicker and glide smoothness — all derived locally with zero extra bandwidth. Drift traffic is now deterministic across screens (host-seeded, fixed-step). Results screens support 3–4 players and abandoned matches resolve to the survivor.

Skydomes & camera (preview parity)

Picked skydomes now render correctly in the wizard preview across aimer and obby — resilient texture loading with retry and CDN fallback, fixed an upside-down skydome bug, and the camera re-applies on perspective changes. Also: the aimer wizard preview now autoplays a muted demo so you can see real gameplay before generating.

Relay repo split complete

The multiplayer relay is now its own repository with CI/CD at parity with the engine — branch-driven deploys, secrets via Doppler, version drift-checking, and contributor docs. The relay side of the single-socket gateway shipped here too.

Next 7 days

What's queued
→ TikTok MP — promote the single-socket gateway from flag-gated to on, verify back-to-back matches survive in the live activity.
→ SEO/LLM — land the P1a feature slice to main and production behind the curated-index toggle.
→ Playgama — verify the rewarded-ad revenue loop on staging, then production.
→ Jira hygiene — close out the ~8 BUIL items now shipped (drift phantom car, vehicle thumbnails, aimer scoring, Play-Again lobby).
→ Prosumer — further infrastructure, DevOps, and user-flow work toward the independent-artist self-serve track.
→ Shared code ownership — expand shared ownership of the codebase across the team.

Why it matters

Discord MP feels reliable

The failure modes that made multiplayer feel broken — dead rooms after a drop, can't-start-a-new-lobby — are gone. That's the difference between a demo and something players will use.

Revenue loop proven

Rewarded ads running through a real vendor on Playgama is the first end-to-end proof of monetisation on a partner channel — and the vendor layer makes the next partner cheap.

New acquisition channel

SEO/LLM pages open organic search and AI-answer discovery. Curated-only keeps quality high — every indexed page is an intentional choice, not thin auto-generated content.

Previews drive clicks

Hover-play previews and robust capture mean every game card can show real gameplay — the single biggest lever on whether a browsing player presses play.

Current Release — live on playmusic.gg. Shipped to production 2026-07-29 via the staging→main promotion (#947). The headline is a careful auth account-linking reconciliation (migrations 091–094) that merges duplicate accounts formed across environments. It also lands the biggest creator-wizard expansion yet — new game modes and CMS-defined asset slots — plus Contests, draft-first player games, and a front-end performance pass.

⭐ Highlights

Prod · 2026-07-29 · staging→main (#947) · migrations 091–094 + contests 0011/0012/0024

Auth account-linking — true merge + reconciliation

Signing in with a second provider now resolves to your existing account instead of creating a duplicate (#886), backed by a new user_identities model and a backfill (migrations 091–094) that merges accounts already duplicated across staging and prod. The OAuth connect flow was locked down — same-origin, CSRF nonce, no unauth fallback (#885). This was the release's highest-risk item: it was reverted once on Jul 21 (#896) and re-landed after hardening.

Creator wizard — new game modes & CMS asset slots

Horror Survival (now its own mode) plus a Horror Runner submode, UA Mode & Zen Flow shooter styles, Spy Hunter on Drift Drive, and new environments (boujee night/drift, backrooms) + YSTO skydomes + a Merge type. The CMS now defines which asset slots each game type exposes (#934, #942), players can add extra slots (#926), and settings show/hide via CMS-authored rules (#944).

Contests

A Contest Hub with a homepage entry point, landing pages, and CMS authoring (#914), a brand-styled leaderboard with podium + player position (#915), contest-rules links in promo popups (#913), and Contests + Promotions added to Promote-to-Prod (#923). Migrations 0011/0012 (Postgres) + 0024 (D1).

Wizard-config safety net

Wizard configuration now syncs additively from code on every deploy (#931), with no-store reads and D1 snapshot/restore workflows (#932) — a bad edit or missed seed can no longer silently wipe modes or environments in production.

Also shipped

Draft-first games · performance · LiveOps counts · home/worker polish

Draft-first player games

Player-created games now start as drafts; publishing is a PlayMusic-gated step (#916), plus a post-run "Create your own game" prompt on playmusic.gg (#908).

Front-end performance pass

Main JS bundle cut from ~809 kB to ~625 kB (#882), the mobile LCP chain broken up, wizard/catalog images routed through CDN resize (~11–20× smaller) (#917), and a boot-smoke gate to catch dead bundles.

LiveOps & play counts

Creation-funnel visibility + per-song completion attribution (#937), and the D1 edge play_count kept live so homepage/modal counts stop jumping (#941, #907).

Home / modal polish & worker CSP

Featured Drop banner placement with readable "New" badges, aspect-safe hero media, share unblocked on desktop/iframe surfaces (#905), and engine-iframe frame-ancestors + CORS fixes (#900–#903, #877).

Post-release watch

What to monitor now it's live
→ Auth reconciliation — duplicate-account merges and linked-provider correctness after the 094 backfill.
→ Play counts — D1 edge counter vs Postgres, now that the modal count reads live.
→ New wizard game modes — behavior against real prod song/character content.
Next Release — Requested & Planned. The stakeholder-requested scope for the upcoming release, and the planned subset committed for it. Two requested items — Customer Avatar (Beta) and the Single-Screen Creator (Alchemy) — are not in this release. See the Release Cadence tab for how scope maps to release tiers.

Requested

Stakeholder asks, by stream
PlayMusicMain consumer product
Requested
  • New Game Modes — Platformer, Dance Game
  • Customer Avatar — Beta
  • Single-Screen Creator — Alchemy
  • Mission / XP System
  • Telegram — Multiplayer
  • TikTok — Multiplayer
ProsumerIndependent artist
Requested
  • First deployment on infra
  • First UI/UX pass

Planned

Committed scope for the next release, by stream
PlayMusicMain consumer product
Planned
  • New Game Modes — Platformer, Dance Game
  • Mission / XP System
  • Telegram — Multiplayer
  • TikTok — Multiplayer
ProsumerIndependent artist
Planned
  • First deployment on infra
  • First UI/UX pass
Requested, not in this release
  • Customer Avatar — Beta
  • Single-Screen Creator — Alchemy
Release Strategy & Cadence. How PlayMusic ships — four release tiers, each with its own cadence, risk profile, approval gate, and deploy path. Use it to classify any change, plan a release train, and know what lead time a stakeholder request needs. Source: docs/RELEASE_SCHEDULE.md (PR #951) · last reviewed 2026-07-16.

Tier summary

When in doubt, size up — a Standard-looking change that needs a migration is Major
TierCadenceLead timeDeploy pathApprovalRollback target
MinorAd-hoc (continuous)NoneAuto CI/CD → staging → prodAutomated gatesRevert PR / prev revision
StandardEvery 2 weeksCut at trainScheduled train1 reviewer + preflightPrevious train tag
SpecialAd-hocMin. 2 weeksScheduled trainStakeholder + eng leadFlag off / previous train
MajorQuarterly (or as scoped)4+ weeksDedicated cutoverEng lead + product sign-offBlue/green traffic shift

The four tiers

Each tier owns its cadence, gate, and deploy path
MinorAd-hoc · continuous CI/CD

Low-risk, reversible changes that ship the moment they're merged. Push to staging → auto-deploy; merge to main → blue/green auto-deploy.

Includes
  • Bugs — defect / regression / crash fixes
  • Experiments — flagged A/B tests, toggled trials
  • Data — catalog backfills, thumbnail regens, D1 edge re-syncs
  • Copy & config — text, CMS content, SEO settings
  • Hotfixes — urgent prod-down fixes
Rules
  • Behind a flag if user-visible and not fully verified
  • Ships continuously — no batching (that's Standard)
  • D1 migrations ship with the merge — never defer
StandardEvery 2 weeks · scheduled train

Accumulated non-urgent work, batched, soaked on staging, and promoted on the train date. Predictable, reviewable, communicable.

Includes
  • Content — new songs / characters, curated catalog changes
  • Templates — via engine + playmusic.gg updates
  • Minor features — small, self-contained additions
  • UI polish + routine, non-breaking dependency bumps
Rules
  • Train cut every other week; code freeze the day before
  • 1 reviewer + green preflight (unit + cross-browser E2E)
  • Not merged by freeze → rides the next train
SpecialAd-hoc · min. 2-week lead

Time-sensitive, externally-coordinated releases tied to a date or partner. Ad-hoc timing, but never a surprise — they need lead time for assets, legal, and comms.

Includes
  • Artist promos — sponsored drops, timed features, pop-ups
  • Marketing / campaign launches — landing pages, seasonal events
  • Partner integrations — TikTok, LG webOS, etc. with a launch date
  • Licensed content windows — contractual go-live / takedown
Rules
  • Minimum 2 weeks' notice — inside 2 weeks is best-effort
  • Coordination checklist: assets, rights/legal, comms, sign-off
  • Prefer a scheduled reveal (flag / publish gate) over a timed deploy
MajorQuarterly / as scoped

Large, higher-risk changes: new capabilities, architecture shifts, or anything needing a migration or coordinated multi-service cutover. Dedicated planning and a cutover runbook.

Includes
  • New features — net-new surfaces (new game modes, engine features)
  • Platform — infra, performance, scalability, new deploy targets
  • Schema / migrations — Postgres + D1, data-model changes
  • Breaking changes + new third-party / SDK integrations
Rules
  • 4+ weeks lead; scoped spec + cutover runbook
  • Eng-lead + product sign-off; blue/green + canary + manual gate
  • Migrations in order: PG → merge → D1 + worker → Sync to Edge
  • Feature-flagged where possible so launch ≠ deploy

Classifying a change

When in doubt, size up a tier
→ Urgent / low-risk / reversible? → Minor (ship now)
→ Routine content / template / small feature? → Standard (next train)
→ Tied to an external date or partner? → Special (min. 2-week lead)
→ New capability, migration, or cutover? → Major (scoped, quarterly)
Update · 2026-06-18 — Strategy shift + three delivery streams. The pitch sharpens: "Come play games with music you love — can't find the right game? Build it in seconds." We are closing the gap between customisation and creation. Delivery is now split into three parallel streams (below); the locked End-of-Q2 launch scope is on the Launch Scope tab, and exploratory tracks on R&D.

Three delivery streams

Replaces the prior single-track framing — each stream owns its surface, audience, and pace
PlayMusicMain consumer product
Scope
  • Main application
  • Native embeds — Discord, TikTok, (Reddit or Telegram)
  • Portals
ProsumerIndependent artist
Scope
  • Infrastructure
  • New UI
  • CDN
Decomposing TemplatesReusable kit
Scope
  • Mix & match templates
  • Mix & match game mechanics
PlayMusic Launch Scope — locked for End-of-Q2 Go Live. The committed surface area across gameplay, content/creation, and distribution. Items marked * are in progress / not yet fully shipped.

PlayMusic Launch Scope

End-of-Q2 Go Live · * = in progress / not yet fully shipped
GameplayModes · sub-modes · env
Modes × 8
  • Music Runner
  • Drift
  • Music Aimer
  • Shooter*
  • Obby
  • Clicker
  • Flight
  • Platform Runner
Depth
  • Sub-modes × 20+
  • Environments × 13*
  • SFX — Beat Reactivity* (charts & visuals, filters)
Multiplayer
  • 1v1
  • 2v2*
  • Watcher*
Content & creationAssets · wizard · cards
Assets — 1.4k
  • Generated*
  • Library assets*
Game creation
  • Game Wizard + Advanced
Game cards
  • File upload
  • In-game capture
  • AI generation
  • Preview video*
Distribution & platformChannels · auth · growth
Embedded channels
  • Discord
  • TikTok
  • Reddit?
  • Telegram?
Partner portals
  • Y8
  • PlayGama
  • FRVR
  • PlayHop
  • CrazyGames (disable leaderboards)
Auth
  • Discord
  • Google
  • TikTok*
  • Email (?)
Social
  • Share game
  • Share video
SEO
  • Game indexing for SEO / LLM (HTML indexing)
Analytics & growth
  • Validate onsite happy-conversion analytics
  • Promo — competition cards
  • Misc — unknown-user high-score name capture
PlayMusic R&D. Exploratory tracks beyond the committed launch scope — where we're probing the next wave of formats.

PlayMusic R&D

Exploratory tracks beyond launch scope
Music Mixing

Deeper interactive control over the stems / mix as a play mechanic.

Open World

Exploration beyond the fixed-track loop.

Platformers

Mario / Crash Bandicoot-style platforming.

2D Games

Find, match, and other casual 2D formats.

PlayMusic — Product Strategy

Come play games with music you love.
Can't find the right game? Build it in seconds.
We are closing the gap between customisation and creation.

Strategy shift. Work is now organised into three parallel streams rather than the prior single-track distribution/content/technology framing. Each stream owns its own surface, audience, and pace.

PlayMusic — main app

The core consumer application plus native embeds (Discord, TikTok, Reddit/Telegram) and partner Portals. The flagship "play & create" loop.

Prosumer — independent artist

A self-serve track for independent artists: dedicated infrastructure, a new UI, and CDN. Lets artists stand up their own music-game experiences.

Decomposing templates

Mix & match templates and mix & match game mechanics — breaking modes into reusable, recombinable parts so new games assemble from a shared kit.

This baselines and iterates on: engagement habits (music & game affinity, sessions, session length), channel effectiveness (impressions, sessions), sentiment (D1/D7/D14 retention around creation & gameplay), and ops model at scale (deployments, games, portals, uptime).

Iteration Matrix

How the three focus areas unlock Go Live
Learning goal Distribution Content Technology
Engagement habits Multiple channels = real behavioural patterns ~10 modes × ~20 environments = enough surface to see what sticks Live analytics captures it cleanly
Channel effectiveness Y8 + Playgama + Discord gives genuine comparison Same content across channels = clean A/B Graduated rollout lets us test without risk
Sentiment (creation & gameplay) Channel mix surfaces different audience reactions Diverse modes isolate creation vs. play-loop signal Song-to-game pipeline is the thing being judged
Ops model More partners = more ops surface to stress-test More content = more throughput demands on pipeline CMS + LiveOps + deploy panel — we'll see where they break

Production Status

Live now → In flight → End of Q2 (Go Live) · updated 2026-06-18
Live NowAs of 2026-06-18
Technology
  • Song-to-game pipeline live
  • Engine in production, embedded across internal and external channels
  • CMS with full pipeline control, asset management, and deploy panel
  • Graduated deployments for maximum uptime
  • Google, Discord, and TikTok sign-in
  • Director Studio live — NLE timeline + AI cue generation, mid-song mode switching
  • Full-funnel analytics + UTM/geo attribution; LiveOps model in place
  • Quick user generation
Game Content
  • 8 game modes: Music Runner, Drift, Aimer (OSU/Taiko/Mania/Catch), Shooter, Obby, Clicker, Flight, Platform Runner
  • 20+ sub-modes; Drift Runner + Crash Course + Horror Survival presets
  • 13+ environments live
  • 4-player multiplayer (mid-join + spectators) merged to staging
Distribution
  • Fully remote control of content globally
  • Discord Activity live; partner portals (Y8, PlayGama, FRVR, PlayHop, CrazyGames)
  • Campaign builder + UTM short-link shortener
In FlightNow → Q2
Technology
  • Three delivery streams — PlayMusic (main app), Prosumer (independent artist: infra + new UI + CDN), Decomposing Templates (mix-&-match templates + mechanics, Lego-core)
  • Multiplayer hardening across all modes (2v2, Watcher)
  • Beat-reactivity SFX (charts & visuals, filters)
Game Content
  • Shooter mode finalisation
  • 1.4k asset library (generated + library assets)
  • Preview videos for game cards
Distribution
  • Native embeds: TikTok, plus Reddit / Telegram under evaluation
  • Game indexing for SEO / LLM (HTML indexing)
  • Improved global load times
End of Q2Go Live launch
Technology — platform maturity
  • Multiplayer hardening — scale to 2–4 person rooms across all modes (foundation already on feature/multiplayer; Mid-May ships Music Runner + Duet, Q2 expands the rest)
  • Tournaments & competitions engine
  • CRM in place — closes the loop between analytics and lifecycle communication
  • Advanced game-generation flow
Game Content — 10 modes, double today's depth
  • 3 new modes shipped (10 in total)
  • Multiplayer enabled across all modes (H2H + co-op)
  • Tournament / competition formats
Distribution — 4 partner channels live
  • Partner-curated content
  • Channel-specific competitions and content

Value Proposition & Consumer Messaging

Play games to the music you love. Can't find your combo? Make it in under a minute.

Play-first, create-on-the-gap. The voice makes playing feel effortless and creating feel like the natural next click — never a chore. Source: PlayMusicHack/docs/TONE_OF_VOICE.md + playmusic-value-prop.md.

The voice, in one line

Plain-spoken and confident, action-led, a little playful — we let the music and the game be the exciting part, and never try too hard.

The two-verb spine

Press play. Can't find it? Make it. — Press vs make: playing is the default, making is the fallback. Keep that contrast intact wherever both actions appear.

Reference lines (calibration set — pass all eight personas)

• Play games to the music you love.
• Press play. Can't find it? Make it in under a minute.
• Your music. Any game.
The test: if a new line would make the Explorer, the Curator, or the Philosopher wince, rewrite it.

Five rules

The register both ends of the 10–20 audience trust
1 · Plain over hype

"Play games to the music you love" — not "Unleash the ULTIMATE music gaming experience." Plainness reads as confidence to older personas, clarity to younger.

2 · Show, don't sell

Let the song titles, genres, and the actual game be exciting. The copy's job is to get out of the way.

3 · You-and-yours, never "users"

"the music you love," "your game," "the ones you can't find."

4 · Verbs, not adjectives

Play. Make. Press play. Action language is age-neutral; adjective-piles read as try-hard to anyone 15+.

5 · One light human beat, max

"Can't find it? Make it." is playful without cringe. Never two jokes in a row, never stacked exclamation marks.

Who we're talking to

Eight personas, ages 10–20 — write a voice none of them rejects
PersonaAgeAlienated by…
The Explorer / The Storyteller10–12Cynicism, complexity, anything like homework
The Identity Seeker13–15Being talked down to, fake hype, "fellow kids" voice
The Squad13–15Boring, corporate, anything not fun or shareable
The Curator15–17Anything algorithmic, mass-market, or try-hard
The Main Character15–17Flatness — they want stakes, drama, a moment
The Creator17–20Being treated as a passive consumer; over-polish
The Philosopher17–20Hype, exclamation marks, shallowness, marketing-speak

The two poles: young end (10–14) needs clear, warm, energetic, zero-cynicism; older end (15–20) needs understated, authentic, never try-hard — hype is the #1 alienator. What they share: don't be fake. That's the seam the voice threads.

Do / Don't

DoDon't
Play games to the music you loveUnleash the ULTIMATE music-gaming experience!!
Press play.Drop in. (ambiguous — say the real action)
Can't find it? Make it in under a minute.Become a CREATOR and build your dream game today!!!
Your music. Any game.A platform for music-driven gaming experiences
Make your own in under a minute.Our revolutionary next-gen pipeline lets you…
Pick a song, press play.Get ready to embark on an epic musical journey

Hard no's (the alienators): stacked !! → loses 15–20 · slang / "fellow kids" → loses everyone · "revolutionary / ultimate / next-gen / unleash / epic" → loses Curator + Philosopher · corporate flatness ("a platform for…") → loses 10–15 · talking down → loses 13+.

Homepage & SEO templates

One hand-written homepage + two data-driven templates (CMS gameTypeDefinitions)

Tier 1 — Homepage (broad, hand-written)

H1: Play games to the music you love
Subhead: Any song, any genre — from flappy flight to endless runners. Can't find your combo? Make it in under a minute.
CTA: Press play · Can't find it? Make it →

LLM-quotable definition (the line an LLM quotes back)

PlayMusic lets you play games set to any song or artist — across genres like flappy, runner, drift, obby, and rhythm — and create your own in under a minute when the combo you want doesn't exist.

Tier 2 — Genre /play/{genre} · Tier 3 — Song /play/song/{artist}-{title}

Both fill {variables} from the same wizardMeta.gameTypeDefinitions the wizard renders from, plus the song catalog. Add a genre in the CMS → its landing page exists with correct copy, no second edit. SEO surfaces lean plainer still — shaped like the query, genre/artist leads the line.

Positioning — the two-tier business value prop

From playmusic-value-prop.md · the why behind the consumer voice
DistroKid democratized music distribution. PlayMusic democratizes game-based fan engagement. Travis Scott / Ariana Grande / Lil Nas X proved games drive fan engagement — but those activations cost $500K–$5M, locking out 99.9% of artists.
Self-ServeIndie artists · $30/yr
Value
  • Turn your music into playable, shareable games — not another link fans scroll past
  • "Play my song" vs "Listen to my song"
  • 5 general OR 2 in-depth generations/yr · AI beat/energy/mood sync · web-ready export
  • Minutes to a game vs $50K–$500K / months for custom builds
Market
  • 50M+ music creators (SoundCloud 40M+, Spotify indie 11–12M, bedroom producers 20–50M)
EnterpriseLabels · agencies · brands
Value
  • Fan Portal — white-label destination where fans build games with the artist's music
  • 10–20× cheaper than custom Fortnite/Roblox builds; scalable across a roster; measurable
  • UGC flywheel: activate → fans create → compete → winning game ported to a major platform
Pricing
  • $15K/track activation · +$25K custom template · $25K/platform port
  • Blended margin 65–80% · Enterprise TAM $500M–$1B

One-liner: PlayMusic — turn music into games. $30/year for indie artists to create; $15K+ per track for labels to activate their fanbase.

Week of 2026-05-13. We can finally see how long our games take to load — by partner, by country, by device. The Portals dashboard goes live for everyone in CMS tomorrow. This was the missing piece blocking every other speed improvement on the roadmap.

What shipped this week

Cold-start visibility · adaptive loading · partner-portal polish

We can finally see load times

Until this week, partner complaints about "45-second load times" were a guessing game — we had no data. The game now reports back exactly how long each step took (initial download, configuration, game assets, audio) for every play session, on every partner.

New "Portals" dashboard in CMS

Anyone in the business can now open the CMS, go to LiveOps → Portals, and see per-partner load times, trends over time, and how many players are coming back to replay. Sparklines on each partner card give an at-a-glance health check.

Drop-out tracking

We also capture when a user bails before the game finishes loading — historically these players were invisible. Now we know how many we're losing and where.

Smart asset loading

The game now adapts how aggressively it downloads assets based on the player's connection and device. Fast connections on good devices get more in parallel; slow phones get protected from getting overwhelmed. This was originally scheduled later in the roadmap but came along for free.

Hover-to-prefetch on portals

When a player hovers over a game card for 200ms, we start loading the game in the background. By the time they click, it's already partway loaded — feels instant.

Discord embeds report in

Discord-embedded games now route through the same analytics pipeline as everywhere else, so we'll see Discord performance alongside Playgama, Y8, etc.

What we still need to fix on this

Partner-embed coverage gap — follow-up next week

The new load-time tracking works inside our own site (playmusic.gg). It doesn't yet report from games embedded directly on partner sites like Playgama or Y8, or from our portal shells. So tomorrow's prod dashboard will show consumer-site data first; partner-embed coverage is the next follow-up.

Next 7 days

What's queued · who benefits
→ Tomorrow — Push load-time tracking + the new Portals dashboard to production.
→ Tomorrow — Y8 build refresh: artist promo + region-specific music.
→ This week — Turn on interstitial ads for Playgama and resubmit.
→ Friday — Multiplayer to production; possibly to portal shells too.
→ Friday — Production-ready game templates integrated.
→ Friday — Portal shell rebrand.

Why it matters

Data-driven optimisation

Once prod has 7 days of real traffic, we'll have concrete numbers per partner, country, and device tier — and that tells us which speed investment to make next instead of guessing.

Partner conversations

Playgama, Y8, and Discord ask "how fast does it load in our region?" — we can now answer with numbers, not promises.

Drop-off recovery

Drop-out tracking turns invisible churn into a measurable metric. We can fix what we can see.

Free wins along the way

Smart asset loading + hover-prefetch came along with the instrumentation work — already shaving seconds off perceived load.

Production releases · May 1 → May 13. Two weeks of prod pushes. Over a dozen production releases landed. The themes: partner channels got faster and more reliable, internal teams got better tooling, and the platform got more stable under load. Everything below is live in production.

For our partners (Playgama, Y8, Discord)

Faster, more reliable, better measured

Y8 partner attribution fixed

Y8 traffic wasn't being properly credited in our funnel — every session looked like it came from "unknown". Fixed: Y8 sessions now show up correctly in partner reporting, so we can have a real conversation with them about volume and payouts.

Discord Activity foundation

The "game-inside-Discord" launch surface is now mostly in place — Discord Activity UI redesigned, share + mobile UX polished, analytics flowing through the same pipeline as our other partners. Featured Artist Experience landed with STYNGR admin controls for curating which artist gets the prime slot.

Playgama feedback addressed

Items Playgama flagged during partner review have been closed out and shipped to prod.

One-click "promote to prod" for partner content

Previously, getting a new song or partner-portal update from staging to production required manual coordination across systems. Now it's one click from cms-staging — for the team running partner relationships, this is daily friction gone.

For the creator + content teams

More modes, smoother creation flow

New game mode: OSU (rhythm tap)

Full rhythm-tap mode landed in production, with a polished wizard flow for picking it during game creation. Expands the catalogue beyond runner/drift/flight into the rhythm-game category.

Wizard rebuilt around real data

The "create a game" wizard used to have a lot of hardcoded options. It's now driven by actual config — so when content/engineering add a mode, it shows up in the wizard automatically. Less drift, less "why isn't X showing up?".

CMS song delete + maintenance polish

CMS got a clean song-delete flow (previously a support ticket) and maintenance-page polish so partner-visible downtime looks professional.

For the ops / LiveOps team

Better visibility, easier to act on

LiveOps got drill-down views, music data quality, and trend visibility

Five separate LiveOps improvements shipped in two weeks. The internal ops tool keeps getting tighter — drill down from a top-line metric into the underlying sessions, see music data quality at a glance, catch errors that used to go silent. Less time spent asking engineers "can you look this up for me?".

Better partner reporting infrastructure

D1 + Worker Redis runbooks finalised; observability fixes shipped. When something looks weird in the LiveOps dashboard, we now have actual playbooks for triaging it instead of inventing one each time.

For end-users (players)

Bugs fixed before most people noticed

Two same-day production hotfixes

A bug made collectibles invisible in some game modes (May 4); fixed same day. A separate bug caused phantom "MISS" popups in Cyber Run / Cosmo Run (May 5); also fixed same day. Both were caught before they hit large player numbers — operational maturity getting better.

Flight-mode ship revive UX

Smoother revive flow when players crash in flight mode — small UX nicety, but the kind of polish that compounds.

Mobile UI polish on Discord/partner shells

Mobile drawers, auto-hiding bars, share-link flow refined — the embedded-game experience feels closer to a native app.

Behind the scenes — platform reliability

Quieter wins that protect everyone

Production load tolerance improved

Database connection pooling (PgBouncer) extended to production, memory increased on the busiest services, CPU throttling tuned. Net result: we can take more concurrent players without falling over, and OOM crashes that used to happen during traffic spikes are gone.

Engine release process formalised

The game engine now has a proper "canary cutover" promotion system — new versions go to a small slice of traffic first, with one-click flip to full traffic once we're confident. Riskiest part of any release (game engine itself) is now the safest.

By the numbers

15+

Production releases in 13 days. Daily-ish cadence — partners and internal teams get value continuously, not in big quarterly drops.

Zero major outages

Two hotfixes were caught and fixed same-day. No prolonged production downtime in the window.

3 partner channels improved

Playgama, Y8, and Discord all got dedicated work this fortnight — diversifying our distribution risk.

1 new game mode live

OSU rhythm-tap shipped end-to-end — pipeline, wizard, and gameplay. Net new genre in the catalogue.

Production releases · May 14 → Jun 18. Five weeks pulled from all three repos. The engine stream drove features & templates (new modes, sub-modes, Director Studio, multiplayer); the integrations stream drove features & integration channels (Discord Activity, partner portals, embed UX); the platform stream drove CMS, analytics, and the creation flow. Everything below is live or merged to staging.

Engine — features & templates

playmusic-game-engine · new modes, Director Studio, multiplayer, template kit

Director Studio — full NLE timeline + AI cue generation

The engine-side of Director Studio matured into a real non-linear editor: multi-lane timeline, drag-edit cues, scrubbable ruler (SEEK_TO_TIME to scrub the running game), and seamless live mode-switching (APPLY_MODE) with stem crossfade between levels. Switch game mode / map / VFX / song / camera mid-song without a restart.

New & expanded game modes

Drift Runner (music runner with a steerable car), Crash Course (grouped as a Drift sub-mode), the Aimer family (OSU / Taiko / Mania / Catch) with configurable Builder/Breaker stem game-over and Drop integration + fever danger, Obby multiplayer, and a Horror Survival preset (backrooms popout zombies). FlyTap collectibles made reachable; Shooter HUD + enemy preload fixed.

Multiplayer — 4-player, mid-join, spectators

Engine threaded explicit 4-player + spectator caps, a mid-game join flow, and ghost fixes across Drift/Flight/Obby (orientation, phantom-vehicle dedup, ghost height/animation, input gating). Mid-join spectators now start rendering, not just players.

Template kit — Lego-core + env capability bounds

Phase 1–2 of Lego-core: standalone registries with parity-tested canCombine() and a harness-v2 lego route — the seam for mix-&-match templates and mechanics. Plus an env capability-bounds system, Synty Apocalypse traffic pool, and drift/crash-course driving extended to ground+road environments.

SIDEKICK avatar builder (PoC)

Proved live face/head + hair swap on a shared body via slot-agnostic equipPartGLB, natural arms via A-pose re-bind, and head-only hair binding. Foundation for a customizable character pack.

Alchemy mixer redesign

Slot-panel "fill-the-blank" UX wired to live catalog data, More Songs/Games dropdowns, and stable character alternates.

Integration — features & channels

playmusic-integrations + PMH channel plumbing · Discord, TikTok, partner portals

Discord Activity — live channel

The Discord Activity surface went from foundation to a working channel: redesigned activity UI with portal data + brand styling, mobile drawers / auto-hiding bars / share-link flow, Featured Artist Experience with STYNGR admin controls, and the game iframe tagged ?portal=discord. Activity analytics now route through the /game-api proxy and carry portal slug + user-agent in the envelope.

TikTok auth + new embedded channels

TikTok added as an auth provider (SuperTokens authorize-URL fix). Embedded-channel scope now spans Discord, TikTok, and partner portals — with Reddit / Telegram flagged as next.

Partner portals — faster, cache-correct, measured

Portal cold-start fix Phase 2 (edge-cache /api/portal/config/* in KV); scoped edge-cache purge on portal create / delete / config-save; FRVR dev harness + reliable FRVR ad triggering with correct revives; CMS "Test Portal" button + automated suite; per-portal Clear Cache. Partner portals: Y8, PlayGama, FRVR, PlayHop, CrazyGames (leaderboards disabled).

Campaign builder + UTM shortener

CMS Campaign Builder with a general /c/ short-link shortener and full UTM ingestion; environment-specific short/destination URLs; gateway routes /api/c/ to player-api for campaign links. Closes the loop from promo card → tracked session.

Attribution survives the whole funnel

First-touch UTM/referrer persisted across OAuth; GA4 ↔ Playgama join keys upgraded; shared pm_session_id/pm_user_id stamped into the GA4 dataLayer; CF geo (CF-IPCountry → X-Country) forwarded end-to-end so analytics country isn't NULL. Abandoned sessions closed via pagehide beacon + hourly reaper.

Platform — CMS, creation & analytics

PlayMusicHack · wizard, LiveOps, deploy, security

Game wizard — accordion picker, persistence, funnel tracking

Accordion-style game-mode picker; persistent wizard state so reload/back recall the engine preview in sync; loading screen on re-sync; full end-to-end funnel instrumentation (wizard + Director Studio). Real game-mode labels on Profile + CMS cards (was always "Runner").

Preview videos (P1)

Capture pipeline for hover-preview videos landed — the missing piece for richer game cards (alongside file upload, in-game capture, and AI generation).

CMS — themes, songs, portals

Operator-editable display names for consumer themes; ISRC/UPC/label/parent-label persisted on song upload; per-tab delete for custom portals; cron layer for portal-cms operationalized (never ran in prod before).

Security & reliability hardening

ADMIN auth enforced on CMS portal/game management; SSRF guard on catalog fetch; staging/canary hostnames blocked from indexing; warm instances kept on user-facing services; remaining console.logs DEV-gated. Multiple Aikido review passes closed out.

By the numbers

3 repos, 5 weeks

Engine, integrations, and platform all shipped continuously — features/templates, channels, and CMS/analytics in parallel.

Director Studio live

NLE timeline + AI cue generation — game authoring without code, mid-song mode switching with no restart.

4-player multiplayer

Caps, mid-join, and spectators threaded end-to-end through engine + relay + CMS seed.

New channels

Discord Activity live, TikTok auth in, campaign shortener + full-funnel attribution.

Two-day release window: Apr 22-23 + Apr 29. Babylon engine consolidation, AI chart review, and a major production push covering portal attribution, share metrics, security hardening, and partner experience.

Apr 22-23 · Babylon engine consolidation + AI chart review

Engine + chart pipeline

Live Babylon engine + per-game config.json everywhere

Finished migrating every game-load path to use the live Babylon engine + per-game config.json. Removed the last legacy "baked HTML export" code paths from the consumer frontend, CMS, and the play API.

Portal import populates engine config

Portal imports now correctly populate game titles and song metadata in the engine config.

AI chart review hardening

Added robustness to the AI-driven chart review pipeline: handles truncated Gemini responses, configurable model, 16K token limit, robust JSON parsing for double-encoded responses.

Apr 29 · Major production push

Portal attribution · share metrics · security hardening

Portal attribution end-to-end

Every game session played from a partner portal (Playgama, Discord, Y8, etc.) now records which portal it came from. Spans the engine, consumer frontend, and backend; backed by a new game_sessions.portal_id column. Critical for partner reporting and revenue attribution.

Wizard song analytics

When a user picks a song, navigates the wizard, and creates a game, we now track impressions, clicks, and "song builds" — feeding the LiveOps Songs leaderboard for editorial decisions ("which songs should we refresh?").

Share metrics fix

Share funnel had been silently broken since March — frontend was firing one event name and backend was looking for another. Fixed; share metrics will populate from now on.

Tween-friendly share menu

Dropped X/Twitter, added Messages (iMessage/SMS), reordered for tween-relevance. Native Discord share works for connected users.

Rich link previews

Share links pasted into Discord/Slack/iMessage/Twitter now unfurl as rich cards with the game thumbnail, title, and description. Previously rendered as a generic placeholder.

Music integration semantics fix

A pipeline-vs-engine vocabulary mismatch ("drop" / "break" / "breaker" weren't valid engine values) was causing audio to drop out and never recover on some songs. Both the publish path and the engine's runtime are now defensive.

Admin security hardening

All /api/admin/* endpoints now require ADMIN-role authentication at the router level. Removed two shared-secret backdoor endpoints that were guarded only by a hardcoded string in the codebase.

Playgama partner experience

New CDN-cached path for engine assets (immutable headers); portal D1 sync respects curated CMS data instead of overwriting; tab-pause/resume on the game modal; vehicle-URL safety fix to prevent cross-mode contamination.

Quick Builder cross-link

CMS sidebar's Quick Builder now opens the consumer-app /create flow on the matching environment.

Two artist games shipped

First content showcase pieces — proof points for the song-to-game pipeline

Boujee Baby (Kendrick) — Drift mode

New environment (NYC streetscape, taller buildings, highway signs, custom sky), rigged character riding a Taxi Drift vehicle, organic camera pull-back, custom collectibles (flowers + trash), uniform Office wall with 8-color variety, denser lamps, full song-spaced gameplay tuning. Demonstrates the pipeline producing a polished one-of-one experience for a specific artist.

It Takes A Village (Canyon) — Platformer mode

New canyon environment with 3-height collectibles, double-jump + extended hang-time, "no game-over" forgiving mode, rolling wheels, diamond collectibles, song progress bar, always-on tutorial, results-screen polish. Audio unmute fix shipped alongside. Shows platformer mode now ready to host artist content, not just abstract levels.

Why these matter

Both are end-to-end proof that the song → concept art → 3D → game pipeline can deliver branded artist experiences. They also surfaced and forced fixes on real edge cases (vehicle wheel preservation, character swapping, audio fallback, void-damage modes) that now harden the engine for everyone.

Multiplayer — built and gated for testing

Live on feature/multiplayer branch · Mid-May merge target
What we'd previously listed as "Q2 / 3-6 month build" turned out to be substantially shipped. The full real-time multiplayer system is feature-complete on a branch and being load-tested with limited modes before staging merge.

Real-time relay infrastructure

Cloudflare Workers relay with Durable Objects GameRoom per match. WebSocket hibernation recovery so a worker restart doesn't drop in-flight games. 30Hz tick rate with smooth client-side interpolation between state ticks. Production relay URL wired up in the harness.

H2H (head-to-head)

Full ghost renderer with synced jump, slide, and celebration animations; lane sync; vertical-curve following on hills; postMessage integration with parent frame; dedicated MultiplayerHUD; H2H results screen.

Duet co-op mode

Stem-builder co-op where two players cooperatively trigger stems. Synced stem state, host config sharing on room join, dedicated co-op results screen.

Lobby + spectator system

Lobby with countdown, ready button gated on config load, room-state config persistence in Durable Object storage. Spectator camera + spectator lobby — anyone can watch live multiplayer matches without joining.

Customization

Ghost color picker, opacity slider, emote system, room settings panel, host toggles for ghost/score display. Player avatars with speed multiplier. Auto-removal of stale ghosts.

Currently gated

Modes restricted to Music Runner + Duet during testing. Flight and Drift disabled in MP for now — they'll come online as physics-sync mode matures. Mid-May ships the gated set; Q2 expands across all 10 modes.

Discord Activity — foundation laid

Targeting Mid-May ship — embedding + attribution groundwork in place

Frame-ancestors + trusted origins for partner embedding

CSP frame-ancestors updated to allow Y8, Playgama, GamePix, and localhost. Portal-shell can be embedded in y8.com iframes without breaking. Same plumbing extends to Discord Activity iframe at launch.

Portal attribution end-to-end

Game sessions now record portal_id at the engine level — Phase 1 (engine sessions) and Phase 2 (frontend + backend regex matching) both shipped. When Discord Activity goes live, it'll plug into the same attribution pipeline with no additional work.

Embed-mode UX

Login button hidden in /e/ embed routes. Recording.js skipped in portal/embed contexts. Tab-pause/resume on the game modal. Cumulatively this is the embed UX Discord Activity will use.

What's left for Mid-May launch

Discord Activity SDK integration, Discord OAuth scopes, lobby/voice channel awareness, and submission to Discord's developer portal. Underlying iframe + attribution + embed UX is already production-tested via Y8 / Playgama traffic.

What broke (and was fixed) during the push

Three in-flight issues, all resolved within minutes of detection
Production migrations workflow had a hardcoded list that hadn't been updated for migrations 044-048. Caused session-start endpoint to error briefly until we appended the missing migrations.
Admin auth lockdown silently broke 19 CMS pages whose fetch calls didn't carry session cookies. Patched (credentials: 'include').
First Playgama portal promote landed with broken state — project links missing, engine URL pointing at staging. Promotion-service hotfix shipped, idempotent re-run healed all 7 games.

Why it matters

Partner reporting unlocked

We can now answer "how many sessions came from Playgama vs Y8 vs Discord this week?" and start setting payouts/billing on accurate data.

Editorial signal restored

Share metrics + song engagement tracking surface "which songs are popular" — informs content refresh decisions.

First-impression upgrade

Rich link previews when users share games make Tweets, Discord posts, and iMessages noticeably more attractive to click. Compounds organic share growth.

Security posture

Admin endpoints are now properly gated. Two shared-secret backdoors removed.

Outstanding for next week

→ Connect the new asset CDN handler to actual engine traffic (currently dormant infrastructure).
→ Backfill creator attribution on Playgama games.
→ Pull baseline share metrics for analysis before next iteration.
→ Resolve a few latent data anomalies (one song with a bad audio path on staging).
Reconciled against git 2026-07-30 → 2026-10-08 across platform, engine and integrations: 10 items closed (9 shipped — five of those had already landed before the 30 Jul snapshot — and 1 no-op), 10 moved to in progress with the PRs that back them (one of them unverified in scope), 40 unchanged. Chips: #… merged PR · #… on staging, a feature branch or open · eng# engine · int# integrations. Previous reconciliation 2026-07-30.

Ranked by ROI — impact ÷ effort

Re-sorted to surface the highest-leverage work first: small efforts with outsized impact at the top, large bets where the impact justifies the spend further down. Effort and impact are subjective 1–5 scores; ROI = Impact × 2 − Effort (so small / high-impact items lead).

Effort — S = days · M = 1-2 weeks · L = month+
Impact — combined Perf / Creator-User / Ops weight
ROI — high-leverage shipping order, not raw importance

Top 10 by ROI

Highest leverage first · effort ↓ + impact ↑
1
Partner-embed perf coverage
Cold-start load-time tracking shipped this week (consumer site + dashboard live in CMS). Direct partner iframes (Playgama, Y8 standalone) and portal shells aren't yet reporting. Closing this gap completes the partner-perf story before tomorrow's prod push goes wider.
Impact: 4/5Effort: SWhy now: closes the loop on this week's instrumentation work
S Perf Ops
2
Production deploy parity (pool / instances / PgBouncer)
Staging values validated by load test. Production workflow still on old values — risk: prod can't absorb 5K CCU. Just a workflow YAML diff applied to deploy-microservices-production.yml.
Impact: 5/5Effort: SWhy now: protects Go-Live capacity
S Perf Ops
3
Asset catalog production sync
Staging 4,628 → production 1,428 assets. Bulk D1 sync to bring prod creator catalog to parity. Likely a script invocation + verification, not net-new code.
Impact: 4/5Effort: SWhy now: unblocks pipeline at prod scale
S Creator Ops
4
Sentry on consumer-frontend
@sentry/react + error boundaries. We have product analytics (LiveOps) but no error tracking for prod traffic. Blind spots cost minutes during incidents. Grafana stack can come later — Sentry alone delivers 80% of the value.
Impact: 4/5Effort: SWhy now: incident response is currently flying blind
S Ops
5
Merge multiplayer (H2H + Duet) to staging
Branch feature-complete: Cloudflare Workers relay, Durable Objects, ghost renderer, lobby, customization. Largest single user-facing differentiator on the calendar. Effort is the merge + soak test, not the build.
Impact: 5/5Effort: MWhy now: bulk of work already done
M User Ops
6
Concept Art CMS Screen
Backend complete (6 DALL-E images, 8 personas, per-asset upload/regen/3D). Need React screen with gallery + variation cards + selection. Unlocks daily creator workflow for every song. Effort is mostly UI plumbing.
Impact: 5/5Effort: MWhy now: blocked creators on a daily basis
M Creator
7
Per-song asset slots + curated style dropdown
Configurable JSONB slots replace hardcoded 9-slot pipeline. ~12 vetted art styles in dropdown. Internal creator ask — directly enables the next 50+ artist games. Touches both pipeline + CMS.
Impact: 4/5Effort: MWhy now: internal-team unblocked, multiplies output
M Creator
8
Read replica wiring + activation
get_read_connection() already exists in game-pipeline + asset-service. Provision Cloud SQL read replica + flip to use it. Largest remaining single perf lever for backend reads. Falls back to primary so it's safe.
Impact: 4/5Effort: MWhy now: code already laid, infra is the only step
M Perf
9
Discord Activity launch (Mid-May)
Foundation laid (frame-ancestors, attribution, embed UX). Remaining: SDK integration, OAuth scopes, voice/lobby awareness, Developer Portal submission. Unlocks 4th distribution channel — but Discord SDK + review cycle is real work.
Impact: 4/5Effort: MWhy now: on the calendar already
M User
10
Tournaments engine
Docs/briefs landed (engine plan, system design, leaderboard integration). Schema + CMS + consumer UI not yet built. Largest piece of the Q2 Go-Live target — high impact but a multi-week build, so ROI sits below the quick wins.
Impact: 5/5Effort: LWhy now: Q2 gate, but plan first to avoid sprawl
L User Ops

By pillar — open work breakdown

Items unique to each axis (cross-cutting items are in the Top 10 above)

⚡ Performance

Read replica wiringWire get_read_connection() to provisioned Cloud SQL replica. Falls back to primary safely.
Engine bundle size auditTree-shake unused Babylon modules; measure current size. Engine-side.
Asset progressive loadRender scene before all GLBs are ready, swap in as they arrive. Engine-side.
/api/config/modes edge cache~620ms first-load. Cache at edge.
R2 multi-regionAll buckets WEUR today. US/APAC for latency. Research.
Guest sessions → stateless JWTsEliminates last Cloud SQL hot-path write for unauthenticated users.

👤 External user / creator

Concept Art CMS ScreenReact screen with gallery + variation cards (backend ready).
Per-song asset slots + art-style dropdownConfigurable slots; curated style catalog.
Personas viewer pageStandalone /personas with scope toggles for profile + asset use.
Alchemy creator UX polishGLB lazy-load + pagination on character grid (port already shipped).
CMS nav restructurePlan in cms-nav-restructure-plan.md.
Stale chunk error boundaryReact boundary that auto-reloads on lazy-route chunk failure.
Share Experience planScore-share with replay link, highlight clips, challenge invites, OG images.
TikTok video capture flowPlan in video-capture-tiktok-plan.md. Not started.
CrazyGames SDK integrationSubmit at least one game.
Native app (Capacitor) Phase 0-1PWA basics → Capacitor shell + Apple Sign-In + store submission.
Apple Sign-In frontend wiringBackend configured; required for App Store.
AAC fallback for SafariEngine OGG → m4a fallback. ~30 lines.

⚙️ Ops

Production pool / PgBouncer / max-instances parityStaging values validated by load test; prod still on old config.
Auto-sync D1 on publishRemoves manual "Sync to Edge" click.
Asset catalog production sync4,628 staging → 1,428 prod assets. Bulk sync.
Sentry on consumer-frontendError tracking + boundaries.
Prometheus + Grafana CloudUnified dashboards.
PostgresManager dedup~7 services have copies; move to shared.
psycopg2 → psycopg3 conversion~10 files, mechanical translation.
WizardPipelineManagerV2 dedupSingle source of truth across services.
Monolith deploy fallbackHard-fail or remove dead-monolith fallback in staging workflow.
SuperTokens production instanceProd still hits staging SuperTokens; needs dedicated instance.
R2 multi-region(also under Perf — ops cost angle)
Husk package deletionRemove deprecated babylon-game-engine, playmusic-game-sdk, r3f-game-engine dirs.
Filter Active 0 Planned 0 Research 0 Shipped 0 Reset

PlayMusicHack

~/Projects/PlayMusicHack/docs/backlog/

✅ Shipped — Week of Jul 2

Verified against merged PRs across PMH & engine (Jun 25 → Jul 2). See the 📅 Week of Jul 2 tab for the narrative.

TikTok Mini Game (staging / in flight)

  • ✅
    TikTok Mini Game — Discord parity + multiplayer — games browser, scores, XP, likes, game-proxy, MP signaling. Open PR to staging.int#16int#18int@28ecc73int@5b03b9d
  • ✅
    Two-tab dev MP harness — exercise multiplayer without TikTok partner access (streamer + viewer, real engine).ca11f46int#16
  • ✅
    Platform-profiles framework — cross-distribution config; TikTok as profile #1 (docs).#585

Prosumer — independent-artist track

  • ✅
    Prosumer vertical slice — API + frontend + migration + staging deploy (end-to-end).#455
  • ✅
    Prosumer sign-in 500 fixed — forward SuperTokens headers in the frontend /api proxy.#582
  • ✅
    Prosumer song-upload 502 fixed — nginx API proxy resolver + Host header.#581
  • ✅
    Prosumer gateway route unblocked — drop reserved PORT env from the frontend deploy.#580

SEO / LLM discovery

  • ✅
    SEO/LLM indexing + landing builder (P1–P5) — block-based CMS landing pages.#545
  • ✅
    Content blocks with upload — image/video blocks in the landing builder.#567
  • ✅
    Song/Artist hubs — link song/artist on game page + manage hubs in CMS.#571
  • ✅
    SEO migrations wired + human-preview gate — portal-cms SEO migrations; sign-in gate fix.#563#570

Preview capture

  • ✅
    Real-gameplay capture — page.screenshot of live play (not record_video / frozen frame).#554
  • ✅
    DOM-click game start — Playwright click hung on Cloud Run; start via DOM click.#557
  • ✅
    Preview endpoint + service routing — correct service, screenshot speed, warmup gating.#561#556

Home deep-link · hubs · assets · wizard

  • ✅
    Home deep-link — global default + per-game tri-state override in CMS.#572#569
  • ✅
    Genre hubs — show all categories, published fallback, game-page hub links.#565#571
  • ✅
    Asset rotation + CDN purge — multi-storage rotation, credentials, immutable-cache reload.#577#584
  • ✅
    Wizard camera + game modes — reset-to-standard, top/flight/obby views, MP 4-player guard + leaver forfeit.#244#246

✅ Shipped — Week of Jun 25

Verified against git log across PMH, engine & relay (Jun 18 → Jun 25). See the 📅 Week of Jun 25 tab for the narrative.

Discord multiplayer — gateway & reconnection

  • ✅
    Reconnection storm fixed — single-flight connect() stops redundant calls tearing down healthy sockets; idle 1006 drops now recover.5756260b
  • ✅
    Single-socket gateway — one persistent WS to the relay's /gateway/ws, rooms via control frames (fixes Discord's 2nd-WS-per-instance wedge). Flag-gated.d5d5c10da9bbc93
  • ✅
    Activity-owned socket — ParentBridgeSocket moves socket ownership to the outer activity frame so it survives Discord menu nav.4afd521d
  • ✅
    Gateway correctness — bridge is Discord-only (fixed web regression); leave-room scoped to roomId (fast-restart race).a5ffadd79b92110a
  • ✅
    Lobby / spectator UX — watch-instead picker, in-place spectator switch, spectator results screen, host force-start.ddc3ffad

Partner / discovery bets

  • ✅
    Playgama rewarded-ads vendor — AdManager routes rewarded requests with CMS-driven vendor selection (PoC).7f07e780
  • ✅
    SEO/LLM — AEO foundation — llms.txt, edge-render game-detail pages, JSON-LD, per-game CMS SEO (P1a, branch).09bfe9cede84f018

Game previews

  • ✅
    Preview videos end-to-end — manual upload + detail-modal controls, hover-play on consumer feed, "has preview" filter.d259f9bb8f6bd6be
  • ✅
    Capture robustness — adaptive start + deathless capture, gameplay (not menu) on gated modes, audio autoplay fix.bf1e0312b763cbc2
  • ✅
    In-engine 3D model preview in CMS — real in-game orientation.267d4a20

Multiplayer correctness · wizard · skydomes

  • ✅
    Drift phantom 3rd car + oversized ghost — centralized character re-enable; ghost scaled by room mode.fbc3fec946b0b678
  • ✅
    Flight ghost — orientation/pitch/roll, grid offset, flicker + glide; non-default ghost stays upright. Zero extra bandwidth.21d3cd7f8a99a6ed
  • ✅
    Deterministic drift traffic — host-seeded, fixed-step cadence + seeded lane shuffle; matches across screens.0d9aba97
  • ✅
    Wizard UX — picker edge-fade, Back keeps vehicle, accordion centers, song-vehicle thumbnail, camera-reset scoping.13cf2d48e8ec6a88
  • ✅
    Skydome preview parity — resilient load (retry + CDN fallback), un-flipped skydome, aimer/obby preview fixes, aimer autoplay demo.4ed0536b786c1817
  • ✅
    Relay repo split complete — pm-mp-relay standalone with engine-parity CI/CD + Doppler secrets + drift-check.352fe92a

Backend Microservices

Tech debt, latent bugs, performance follow-ups across the 7 microservices.

Tech debt

  • ✅
    psycopg2 → psycopg3 conversion — ~10 files in player-api + media-service still import psycopg2 at module level. Mechanical 1:1 translation.3ad011ac
  • ✅
    PostgresManager duplicated across 7+ services — each has its own infra/postgres_manager.py copy. Move to shared module.c80d147b
  • ✅
    WizardPipelineManagerV2 duplicated — exists in portal-cms, game-pipeline, asset-service, celery-worker. Single source of truth needed.46e842a7
  • ✅
    D1 game sync is manual — CMS "Sync to Edge" must be clicked after publish. Should auto-sync.87ff298f
  • ✅
    Docker layer cache serves stale shared code — backend/shared/ changes may not propagate due to GHA cache.8b62bf27
  • ✅
    Game categories inconsistent — DB has old categories (runner, rhythm) for migrated games; consumer app has new (endless, music, drift).8afbf67d
  • ✅
    Monolith deploy fallback — staging workflow silently falls back to dead playmusic-backend-staging if a service URL fetch fails. Hard-fail or remove.825e2c2b

Latent bugs (April audit)

  • ✅
    TIME_UPDATE log spam in GameModalV2 — handler added.verified
  • ✅
    start_session non-UUID insert — accepts "dev-test" as guest_id; psycopg rejects. Validate or 400.#973#1130
  • ✅
    get_leaderboard route ordering — static /leaderboard/creators matched by parameterized route. Reorder.@fd7c3a6
  • 🟡
    Transient psycopg connection drops — Cloud SQL cuts idle connections; pool doesn't pre-ping.#1024#1127Oct 8: Pool hygiene fixed (idle-in-transaction leak, starvation) and PgBouncer sidecar removed (#1025); the SQLAlchemy engine already had pool_pre_ping=Tr…
  • ✅
    asset-service OOM on 512 MiB — bump to 1Gi or profile leak.cc68a05f

Performance follow-ups (load test 2026-04-21)

  • 🟢
    Read replica wiring — get_read_connection() exists but isn't called yet. Provision Cloud SQL replica + wire up.
  • ✅
    Production deploy parity — pool size, PgBouncer config, max-instances bumps from staging.02204539
  • ✅
    Upstash Redis activation — leaderboard + likes instant reads/writes at scale.6047ef48
  • ✅
    PgBouncer sidecar permanent in staging — load test fixes including DB pool sizing and edge caching.9ceb43b4
  • ✅
    Guest sessions → stateless JWTs — eliminates last Cloud SQL hot-path write for unauthenticated users.a40b98a9

Cold-start load time (2026-05-13)

  • ✅
    Cold-start instrumentation end-to-end — engine reports timing back to host; backend captures per-session step breakdown (download / config / assets / audio); country + device tier dimensions populated.a98263a5af4f019b
  • ✅
    Drop-out tracking — users who bail before session start now counted via beacon ping; race condition vs in-flight session fixed.b6048dae9ac973ad
  • ✅
    Portals dashboard in CMS — LiveOps → Portals tab with per-partner KPI cards, inline sparklines, trends + retention.8a6588403ae7b382
  • ✅
    Hover-to-prefetch on game cards — 200ms intent prefetch on consumer + portal-shell; saved-data / low-tier defended.2e3a8125a8d03cc4
  • ✅
    Per-session perf token — closes Aikido IDOR finding on the new perf endpoint.595b692fba045eff
  • 🟡
    Partner-embed coverage gap — perf reporting wired from consumer site only. Direct Playgama / Y8 iframes and portal shells not yet reporting. Audit each host + decide engine fallback strategy.#1519eng#514#1700#1711Oct 8: Load/crash telemetry now exists for the consumer modal and the engine emits errors to any host, but direct Playgama/Y8 engine iframes and portal sh…

Production cutover

  • ✅
    game-pipeline service extraction — still partial.8b54e940
  • ✅
    engine-api service stabilization — works but flagged unstable.98a2698f
  • ✅
    /api/games/purge-cache route missing on prod gateway — add nginx route.9c97ad4a
  • ✅
    Production SuperTokens instance — production still hits staging SuperTokens. Need dedicated instance + DB.8488be22

CMS & Creator Tools

Concept art, Alchemy port, share/recording, promote-to-prod. CMS lives in frontend/.

Concept art & creative direction

  • ✅
    Concept Art CMS Screen — backend complete (6 DALL-E images, 8 persona variations). Need React screen: gallery, variation cards, selection, reference upload.#1308#1310#1315#1325
  • ✅
    Per-asset upload/replace — replace concept art image in R2 from CMS.0441947a
  • ✅
    Per-asset prompt regen — show full prompt + extra direction field, regenerate single asset via gpt-image-1.193aa754
  • ✅
    Per-asset 3D from concept — Meshy I2D button per concept image.d90cb0f5

Alchemy UI port

  • ✅
    Port Alchemy to React — engine packages/alchemy/ → consumer-frontend/src/pages/Create.jsx. Replace hardcoded defs with catalog API.#1238#1232#1242
  • 🟡
    /create route — split-panel: engine iframe left, song/mode/character pickers right. Replaces multi-step wizard.#1238#1604#1606Oct 8: The split-panel editor shipped as /alchemy; /create routes to it only behind the create_split CMS flag, which is OFF in both envs, so /create still…

Share & recording

  • 🟢
    Share Experience plan — full plan in docs/share-experience-plan.md. Not started.
  • ✅
    Recording engine port — Babylon recording.js sourced from backend/game-server/static/, manually synced. Make engine repo canonical.767690cf
  • ✅
    /play/:gameId GameModalV2 wrap — currently bare EngineFrame. Should wrap in full GameModalV2 (likes, leaderboard, share).2f705103

Promote to Production

  • ✅
    Promote to Production CMS feature — batch select songs/games/portals from CMS.5cfa3b4f
  • ✅
    Promotion service hardening — engine URL rewrite, project_id relink, cascades, schema fixes.7c5b5ee7
  • ✅
    E2E promote flow — first Playgama portal promote landed (with hotfixes); 7 games healed via idempotent re-run.7c5b5ee7

Other CMS gaps

  • 🟡
    Asset Library Tool — standalone /asset-library.html with semantic search across 4,000+ catalog assets.#1361#1325Oct 8: The CMS Asset Library page (frontend/src/pages/AssetLibrary.jsx, pgvector semantic search since March) got display-name editing and vector-search v…
  • ✅
    CMS nav restructure — plan in docs/cms-nav-restructure-plan.md.76185308
  • ✅
    Stale chunk error boundary — lazy-loaded routes fail after deploys. Auto-reload on chunk failure.756308a6
  • ✅
    Wizard preview env reset bug — after handleConfirm, mode-init effect re-fires with default neonArcade. Visual only.fac37fbf

Song-to-Game Pipeline

Internal-user requests for the song → concept art → 3D → game flow.

Per-song asset slots (drives concept art + 3D)

  • ✅
    Configurable slot system — replace hardcoded 9 concept art slots + character/obstacle/collectible workers with per-song JSONB concept_art_slots config. Each slot: {id, type, label, generate_concept, generate_3d, art_style?, rigged?}.6d87aba0
  • ✅
    Slots panel in CMS — UI to add/remove/rename slots before generation. Defaults to current 9.3856028b
  • 🟢
    Engine config compat — slot ids → engine roles. Decision pending: keep canonical filenames or remap in config builder.
  • ✅
    Migration — new column on songs, default = current 9-slot config.6d87aba0

Curated art-style dropdown

  • ✅
    Style catalog — ~12 styles vetted against gpt-image-1 + Meshy lowpoly: anime, western cartoon, comic book, Studio Ghibli, cel-shaded, low-poly stylized, claymation, synthwave, noir, watercolor (2D-only), pixel art (2D-only).0af52a90
  • ✅
    Per-song default + per-slot override — song profile gets art_style field. Regen modal exposes per-asset override.6d87aba0
  • ✅
    Prompt injection — fragments slot into existing prompt builders.46e842a7
  • 🟢
    Style verification batch — generation run across all 12 to confirm Meshy-safe flag is correct.

Personas viewer (standalone CMS page)

  • ✅
    /personas page — list all 8 hardcoded personas with name, age range, description, system prompt. Read-only v1.45ba0509
  • ✅
    Two scope toggles per persona — "Use for song profiles" + "Use for asset generation".658945d9
  • ✅
    Persistence decision pending — move 8 personas into personas table, or keep hardcoded + side table for scope toggles.mig 053/054

Multiplayer

  • ✅
    Real-time multiplayer foundation — Cloudflare Workers relay, Durable Objects GameRoom, WebSocket hibernation recovery, 30Hz tick.c2f3f806d442cc
  • ✅
    H2H (head-to-head) gameplay — ghost renderer, lobby, postMessage integration, MultiplayerHUD, results screen.abb0361661c802
  • ✅
    Duet co-op mode — stem builder co-op with synced state and dedicated results screen.bd94019a7dd79f
  • ✅
    Spectator camera + lobby — watch live multiplayer matches.cfd2d697f2ce35
  • ✅
    Multiplayer customization — ghost color, opacity, emotes, room settings, host toggles.24df3d6
  • ✅
    Merge feature/multiplayer to staging — branch is feature-complete; gated to Music Runner + Duet during testing. Mid-May ship.8d12b671
  • ✅
    Expand multiplayer to all modes — flight + drift currently disabled in MP; Q2 target enables across all 10 modes.2e10fd77
  • 🟢
    Score challenges (async ghost share) — share link "beat my 12,000" using existing ghost replay infra.

Tournaments

  • 🔵
    Tournament definition — schema needed: tournament, entry, leaderboard scope, prize, time window. New CMS page to manage.
  • 🔵
    Time-bounded leaderboard scope — D1 leaderboard infra exists; needs tournament_id filter.
  • 🔵
    Tournament UI on consumer — entry, current standings, countdown, results.
  • 🔵
    Reward / prize plumbing — out of scope until tournament v1 ships.

Deeper sharing

  • 🟢
    Score-share with replay link — share link includes recorded session for ghost playback.
  • 🟢
    Highlight clip share — auto-generate clip of best moment for TikTok/Discord.
  • 🟢
    Challenge invite — share-with-target to specific friend, results aggregated server-side.
  • 🟢
    Embed share image — OG image with score, song, character thumbnail. Pre-rendered server-side.

Integrations

TikTok, Discord, CrazyGames, native (Capacitor), Apple Sign-In.

TikTok

  • 🟡
    Profile "Connect" gating — not gated on VITE_ENABLE_TIKTOK_SIGNIN. Auth sign-in is gated; profile connect button always shows.#1160Oct 8: The guest sign-in surface on /profile is now gated on VITE_ENABLE_TIKTOK_SIGNIN, but the signed-in account 'Connect TikTok' block in Profile.jsx (t…
  • 🟢
    TikTok video capture flow — full plan in docs/video-capture-tiktok-plan.md. Not started.

Discord

  • ✅
    Discord Activity (game-in-Discord) — full plan in docs/discord-activity-integration.md. Not started.3a28557
  • ✅
    Discord activity analytics via gateway proxy — analytics calls now route through /game-api so Discord-embedded games are visible in the same pipeline as Playgama / Y8.d7817b6

CrazyGames

  • 🟢
    CrazyGames SDK integration — slash command exists for export prep. Not yet wired into publish flow. Submit at least one game.

Native app (Capacitor)

  • 🟢
    Phase 0 (1-2 wk): PWA basics — manifest, service worker, installable.
  • 🟢
    Phase 1 (4-6 wk): Capacitor shell, Apple + Google Sign-In, store submission.
  • 🟢
    Phase 2 (4-6 wk): Push notifications, offline caching (~500MB), native share, score challenges.
  • 🟢
    Phase 3 (8-12 wk): IAP, ghost replays, Babylon Native prototype.

Apple Sign-In + Luminate

  • 🟢
    Apple Sign-In frontend wiring — backend already configured. Required for App Store submission.
  • ✅
    Luminate music data — backlog in docs/luminate-backlog.md. Decide before scoping.c2fc013d

Infrastructure

Deploy panel, blue/green, perf, observability, D1, R2.

Deploy

  • ✅
    Deploy panel + Force Deploy + maintenance page — non-dev control panel with branded maintenance/404 toggle.b1fff58dde771703
  • ✅
    Canary preview routing + fallback — auto-set cookie + 404 fallback to stable.98d679c4c04801ad
  • ✅
    Graduated rollout (production) — production canary live; backends graduate to 100% before gateway+frontend.441aaaa0
  • 🔵
    Blue/green deployment — ~$320/mo extra. Revisit if canary doesn't stabilize.

D1 / edge

  • ✅
    Auto-sync D1 on cache purge — published games sync to D1 edge automatically.9a619716
  • 🟢
    Asset catalog prod sync — staging has 4,628 assets, production has 1,428. Bulk D1 sync.
  • ✅
    Thumbnail URL cleanup — production games reference staging API URLs. Migrate to CDN.2953fa38
  • ✅
    /api/config/modes edge cache — ~620ms first load. Cache at edge.2024e482

Performance & storage

  • ✅
    Engine iframe load time — engine-side optimizations + immutable CDN headers + redundant audio load skip.ca7058a
  • ✅
    R2 jurisdiction cleanup — switched to default endpoint for WEUR buckets, removed dual-client routing.5921d3b9a9a6a088
  • 🔵
    R2 multi-region — all WEUR today. US/APAC for latency.
  • ✅
    Thumbnail storage unification — pipeline bucket → assets bucket + CDN URLs in D1.1649bccc
  • ✅
    Read replica provisioning — Cloud SQL replica + wire get_read_connection().7fb7c3ac

Observability

  • ✅
    Sentry on consumer-frontend — error boundaries, error tracking.#1519#1524#1529#1683
  • 🟢
    Prometheus + Grafana Cloud — unified dashboards.
  • ✅
    LiveOps on production — hybrid PostgreSQL + BigQuery analytics live, dynamic source badges, Creation tab.4e6e5d0ea0b9fcf7
  • ✅
    Migration 042 (analytics_events) — ran on prod via 044-048 hotfix workflow.0ba4065d

Other

  • ✅
    Remove Godot prefetch — D1 game feed + project detail return Babylon engine URL; wizard prefetches Babylon engine, not Godot.5b4790442e145c07698c581d
  • 🟡
    Delete husk packages — babylon-game-engine/, playmusic-game-sdk/, r3f-game-engine/ are deprecated.#612Oct 8: Two of three husks were deleted before the window (#612); packages/babylon-game-engine is still on main and CLAUDE.md now calls it active and slate…

Open Bugs (April snapshot)

Production / staging bugs not yet fixed.

Production

  • ✅
    TikTok "Connect" shows on production Profile — gating fixed.verified
  • ✅
    Game likes refreshing in GameModalV2 — Upstash Redis + D1 edge writes resolved sync issues.6047ef4803b22e3b
  • ✅
    /api/games/purge-cache on prod gateway — D1 sync side-effect cleaned up.38672324

Catalog / preview

  • ✅
    Wizard preview env reset to neonArcade — fixed.verified
  • ✅
    Catalog vehicle/ship thumbnails — generation + upload run, R2 backfilled.verified
  • ✅
    Catalog vehicle orientation auto-correct (engine-side) — runtime fix for Y-up oriented vehicle models.f81195c
  • ✅
    Drift vehicle wheel preservation + swap — VehicleController preserves GLB hierarchy and shared modelCache.098ac55de310b7

Mobile / dev experience

  • ✅
    Director mobile touch input — override select-none for inputs to enable touch.6320356
  • ✅
    TIME_UPDATE log spam — handler added (or throttled engine-side).verified
  • ✅
    Stale chunk errors after deploy — lazy-loaded routes fail. Need React error boundary that auto-reloads.756308a6

Babylon Game Engine

~/Projects/playmusic-game-engine/docs/backlog/

Engine Runtime

Performance, audio, retargeting, iframe protocol, catalog assets.

Performance

  • ✅
    Iframe load time — engine-side optimizations + immutable CDN headers + redundant audio load skip.ca7058a
  • ✅
    Engine debug logging — [STATE] / [gameLoop] console logs stripped before production.verified
  • ✅
    Adaptive asset-load concurrency — tier-aware GLB preload batch size (sequential gating preserved for low-end).03939a14
  • ✅
    Cold-start perf instrumentation — engine reports timing marks to host via PERF_TIMING; Server-Timing parser captures gateway overhead.488154ff62e554c9e2cbac02
  • 🟢
    Bundle size audit — measure current Babylon + game code; tree-shake unused modules.
  • 🟡
    Asset progressive load — load scene before all GLBs are ready, swap in as they arrive.eng#554eng#558#1699int#42Oct 8: A scene-loading-handoff workstream (engine #554/#558/#563–#567, platform #1699/#1724, integrations #42) shows the menu while models finish and boun…

Audio

  • 🟢
    AAC (.m4a) fallback for Safari — full plan in AAC_FALLBACK_PLAN.md. ~30 lines new code.

Iframe protocol bugs

  • ✅
    TIME_UPDATE log spam — resolved on PMH side.verified
  • ✅
    SET_VEHICLE_MODEL swap fix — route to DriftMode's VehicleController; reparent meshes correctly.33c980ab617443
  • ✅
    Director mobile touch input — override select-none on inputs.6320356

Retargeting / animations

  • ✅
    Runtime animation retargeting — fallback verified; library skeleton kept alive, minimal retarget options for same-convention skeletons.3a38c5637fbfe5
  • ✅
    Babylon.js v8 upgrade — required for AnimatorAvatar retargeting (CHR-03, ANIM-02, ANIM-03).1db82d75
  • 🔵
    Babylon.js v9 upgrade — supersedes v8 work. WebGPU defaults, IBL improvements, NME updates. Likely batch with v8 (skip straight to v9).
  • 🟢
    Shared animation library — Quaternius UAL packaged into anims-core.glb, etc. on R2. Eliminates per-song animation gen.

Catalog assets

  • ✅
    Vehicle orientation auto-correct — runtime fix in engine for Y-up oriented vehicle models.f81195c
  • 🟢
    Engine player model API unification — 3 URL fields + 3 SET actions. Long-term: unify into one.

Engine Tools

Standalone tools (Alchemy, Director, Harness, Asset Library, Anim Harness).

Alchemy (creator)

  • ✅
    Port to React in PMH — Alchemy creator rewritten as React, connected to catalog API.verified
  • 🟢
    GLB lazy-load + pagination — character grid currently loads all at once.

Director Studio

  • ✅
    Director Studio UI — full plan in DIRECTOR_STUDIO_PLAN.md. Standalone page with timeline + cue editor + VFX/audio/scene panels.5ba325fd
  • ✅
    Director touch handling — mobile touch input enabled for inputs.6320356

Harness — embedded Scene Editor

  • ✅
    Slot editor + asset browser in harness (v0) — first cut shipped 2026-04-20.1daf5c4
  • 🟡
    Embed full Babylon Scene Editor — beyond slot editor: drag-place props, edit lights/cameras, materials. Save scene back to game config. Likely iframe or panel inside packages/harness/.eng#543Oct 8: Direction superseded: instead of embedding Babylon's Scene Editor, Forge is getting a visual editing workspace (gameplay/assets/music/presentation…
  • 🟢
    Scene → config serialization — Scene Editor writes .babylon/glTF; serializer maps editor entities → our slot/spawn/entity model.
  • 🟡
    Asset library bridge — Scene Editor pulls props/characters from catalog instead of file uploads. Custom asset browser panel.eng#543Oct 8: Catalog-fed asset selection exists in the open Forge workspace PR #543 (and 8353e9b5 'live staging song catalog' on feature branches); nothing on m…

QA Harness, Asset Library, Anim Harness

  • ✅
    QA tools panel — full spec in QA-TOOLS-PLAN.md. 11 tool categories.146791f0
  • 🟢
    Standalone /asset-library.html — browse all catalog assets, filter by type/style/pack, semantic search, 3D preview.
  • 🟢
    /anim-harness.html — load any character GLB + animation library, test retargeting. Blocked on Babylon v8/v9.

Husk packages

  • 🟢
    Delete packages/babylon-game-engine/ — deprecated split.
  • ✅
    Delete packages/playmusic-game-sdk/ — split to separate repo.#612
  • ✅
    Delete packages/r3f-game-engine/ — deprecated.#612

Engine Bridges

postMessage protocol, recording, multiplayer hooks, native, CrazyGames.

Iframe / postMessage protocol

  • 🟡
    TIME_UPDATE handler / throttle — see runtime section.eng#536Oct 8: likely shipped via eng#536 — scope unverified
  • 🟢
    Bridge API doc — PlayMusicBridge.ts has 3 player-model URL fields + 3 SET actions. Document and unify.

Recording

  • ✅
    recording.js source-of-truth — currently in PMH, manually synced into engine. Make engine repo canonical.767690cf

Multiplayer & sharing hooks

  • ✅
    Real-time multiplayer surface — Durable Objects WebSocket relay, ghost renderer, lobby, customization.feature/multiplayer
  • 🟢
    Score-challenge async embed — accept ?challenge=<replay_id>, fetch ghost data, render semi-transparent player. Reuses real-time ghost infra.
  • 🟢
    Highlight clip detection — engine emits "best moment" event for share clip extraction.
  • 🟢
    Pre-rendered OG image — server-side OG image; engine just provides screenshot endpoint.

Native & CrazyGames

  • 🔵
    Babylon Native viability — currently blocked by no Web Audio API support.
  • 🟢
    CrazyGames SDK ad hooks — engine emits "level start"/"level end"/"rewarded ad opportunity" events.