feat: Buddy World — shared plaza where buddies teleport, wander, and celebrate#143
Open
terpjwu1 wants to merge 6 commits into
Open
feat: Buddy World — shared plaza where buddies teleport, wander, and celebrate#143terpjwu1 wants to merge 6 commits into
terpjwu1 wants to merge 6 commits into
Conversation
RO-style isometric shared plaza: async state sync via CF Workers + D1, client-side simulation, ASCII citizens with unlockable pixel skins later, /buddy-world one-command onboarding, opt-in game-state-only privacy model, and the retention-analytics pipeline as a first-class goal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…a client
Implements docs/superpowers/specs/2026-07-04-buddy-playground-design.md end to end:
Server (Cloudflare Workers + D1, all logic host-agnostic and tested):
- validate/antiabuse/identity/districts: snapshot validation against the
real XP curve, hourly XP clamping with level re-derivation, slug + name
hygiene, 80-cap district sharding
- SqliteWorldStore + D1WorldStore behind one async interface; the same
test suite runs against both (better-sqlite3 shim presents the D1 API)
- handlers: teleport/events/recall/anon/world with sha256 token auth,
anon masking ("a wild Void Cat"), per-token rate limiting
- worker-core fetch router (CORS, 400/401/404/429) + thin CF entry with
nightly rollup cron; wrangler.toml + D1 migration with drift-guard test
Client:
- buddy-world CLI: teleport (privacy note + confirm), status, anon on|off,
recall [--purge]; world.json token config under ~/.buddy
- autoSyncWorld glue in awardXpAndRefresh: debounced fire-and-forget event
sync, silent no-op unless opted in, network failures never reach buddy
- installers ship the /buddy-world command; buddy-world added to bins
Plaza (world/public, static for CF Pages):
- vanilla Canvas 2D isometric plaza: seeded deterministic wandering,
stat-driven behaviors, species-palette colors, RO-style name tags,
owner chibi avatars, golden-wing level-ups, deploy fireworks, ticker,
day/night, adopt-your-own CTA
- sprites.json generated from species.ts with drift-guard test
- Puppeteer smoke test: real HTTP server, real render, pixel assertions
72 new tests, all green (repo has 9 pre-existing failures on master in
penguin/doctor/reasoning suites, untouched by this change).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Stable per-species sprite metrics (max box across ALL frames) so uneven frame widths (Penguin) can no longer shift the centering anchor and shake the sprite - Sprite font 13px with measured char width; ground shadows and dark glyph halos separate ASCII citizens from the checkered tiles - WCAG AA: species colors lifted to >=4.5:1 contrast against the brightest tile (ratios exposed and asserted in the smoke test), canvas role=img with live aria-label, visually-hidden citizen list for screen readers, aria-live=polite ticker, prefers-reduced-motion freezes wandering/frame cycling/celebration bobbing - Smoke suite grows to 4 tests: render+pixels, a11y contract, reduced-motion, per-sprite contrast Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…al waddle Frame advance was incrementing every render tick (~60fps) during even 450ms windows, strobing all sprites; now time-based with per-actor phase, exactly one advance per 450ms. The AA pass had also switched sprites from bottom- to top-anchoring, so variable-line-count frames bobbed vertically; lines are bottom-anchored again while keeping the stable horizontal box. Both regressions now pinned by smoke tests (cadence delta, bottom drift). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Stored XSS: names with HTML metacharacters rejected server-side; ticker and SR list build DOM via textContent only (both sinks — the review caught the SR list, the ticker had the same hole) - XP economy: per-request grace replaced with a persisted token bucket (xp_bucket column, 500/hr refill, 200 burst) — any request pattern is bounded by cap*time + burst; level derived from granted XP only - Teleport bypass closed: store.teleport no longer writes snapshots for existing citizens; all updates flow through the clamped handler path - Rate limiting: IP bucket checked before attacker-chosen token keys; limiter map bounded at 10k keys with expiry sweep - CLI recall keeps the local token when the server call fails (no more false 'purged' with lost credentials) - world.json written 0600; MCP-path config reads cached 30s; event batches capped at 50; plaza refresh failures no longer break boot; flagged/hidden/xp_bucket stripped from public world responses 12 new tests pin every finding (hostile-name browser test, grace- amplification property test, re-teleport clamp, rotating-token IP limit, offline recall). 85/85 world tests green. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4 tasks
- validateSnapshot accepts unknown and 400s null/primitive payloads instead of throwing into the Worker - world.json permissions repaired on every save (chmod, not just create mode); config cache invalidated on save/delete so opt-in/out and the XP blessing take effect immediately instead of up to 30s later - Fresh citizens start with xp_bucket=60 (near-empty) so a fabricated high-XP entry cannot also grow fast post-entry; initial claimed XP is documented as an accepted design risk (server never saw local history) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the approved spec (
docs/superpowers/specs/2026-07-04-buddy-playground-design.md): a Ragnarok-Online-inspired shared plaza. Owners opt in with one command, their buddy + chibi avatar walk around a hosted isometric world, and real coding activity celebrates in public — level-ups get golden wings, deploys get fireworks. Every opt-in sync event doubles as the retention-analytics pipeline.What's in the box
Server (Cloudflare Workers + D1; every line of logic host-agnostic and tested):
leveling.tsXP curve; hourly XP clamp that re-derives level from clamped XP (flag, never ban)SqliteWorldStore+D1WorldStorebehind one async interface — the same test suite runs against both (better-sqlite3 shim presents the D1 API), so the SQL that ships to Cloudflare is exercised locallyworker-corefetch router (CORS/400/401/404/429) + nightly rollup cron; D1 migration with a drift-guard test against the schema sourceClient:
buddy-worldCLI:teleport [--avatar chibi-1..8](privacy note + confirm),status,anon on|off,recall [--purge]autoSyncWorldglue inawardXpAndRefresh: debounced, fire-and-forget, silent no-op unless opted in — plaza problems can never reach the buddy/buddy-worldcommand (sh + ps1);buddy-worldadded to binsPlaza (
world/public/, static for CF Pages):sprites.jsongenerated fromspecies.tswith a drift-guard testTest plan
tsc --noEmitandnpm run buildclean;bash -n install.shcleanwrangler d1 create buddy-world→ id intoworld/wrangler.toml→migrations apply→deploy; Pages forworld/public/Note: master has 9 pre-existing test failures (penguin animation, doctor, reasoning detectors) — verified identical on a clean master worktree; untouched here.
🤖 Generated with Claude Code