Skip to content

feat: Buddy World — shared plaza where buddies teleport, wander, and celebrate#143

Open
terpjwu1 wants to merge 6 commits into
masterfrom
feat/buddy-world
Open

feat: Buddy World — shared plaza where buddies teleport, wander, and celebrate#143
terpjwu1 wants to merge 6 commits into
masterfrom
feat/buddy-world

Conversation

@terpjwu1

@terpjwu1 terpjwu1 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

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

  • Snapshot validation against the real leveling.ts XP curve; hourly XP clamp that re-derives level from clamped XP (flag, never ban)
  • SqliteWorldStore + D1WorldStore behind 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 locally
  • Token auth (sha256 at rest), anon masking ("a wild Void Cat"), per-token rate limiting, 80-citizen district sharding
  • worker-core fetch router (CORS/400/401/404/429) + nightly rollup cron; D1 migration with a drift-guard test against the schema source

Client:

  • buddy-world CLI: teleport [--avatar chibi-1..8] (privacy note + confirm), status, anon on|off, recall [--purge]
  • autoSyncWorld glue in awardXpAndRefresh: debounced, fire-and-forget, silent no-op unless opted in — plaza problems can never reach the buddy
  • Installers ship a /buddy-world command (sh + ps1); buddy-world added to bins

Plaza (world/public/, static for CF Pages):

  • Vanilla Canvas 2D isometric world: seeded deterministic wandering, stat-driven behaviors (CHAOS chases, WISDOM reads, PATIENCE fishes), species-palette colors, RO-style outlined name tags, golden-wing level-ups, deploy fireworks, live ticker, day/night, adopt CTA
  • sprites.json generated from species.ts with a drift-guard test

Test plan

  • 71 new tests, all green (validation, anti-abuse, identity, districts, both stores, handlers, HTTP router, CLI, client sync E2E, 2 drift guards)
  • Puppeteer smoke test: real HTTP server, headless render, canvas pixel assertions, zero page errors
  • tsc --noEmit and npm run build clean; bash -n install.sh clean
  • TDD throughout — every module watched RED before GREEN
  • Deploy: wrangler d1 create buddy-world → id into world/wrangler.tomlmigrations applydeploy; Pages for world/public/
  • Soft-launch to the Slack community per rollout plan

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

terpjwu1 and others added 5 commits July 4, 2026 00:40
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>
- 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>
terpjwu1 added a commit that referenced this pull request Jul 5, 2026
# Conflicts:
#	.gitignore
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant