Add /api/payments router (connect-wallet, wallet-status, create-invoice,
check, submit-cashu, winnings, claim, disconnect-wallet). Add ranked
queue endpoints to /api/queue. Mount payments router in app.ts with
orphan payment recovery on startup.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ranked-queue.ts with 60s timeout, ELO matching, no-mock guard, and
automatic refund on timeout. Wire orchestrator to accept mode param,
track satsWagered in finalize transaction, trigger payWinner on ranked
win and refundEntry on ranked draw.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements createEntryInvoice, checkPaymentStatus, payWinner, refundEntry,
redeemCashuToken, and recoverOrphanedPayments. Uses nostr-tools for NWC
protocol (NIP-47) and @cashu/cashu-ts for ecash fallback payouts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Disconnect and recreate sfxGain in stopAllAudio() so oscillators
and noise buffers stop immediately instead of playing to completion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add payments and wallet_connections tables, new columns on fights (mode,
pot_sats, payout_status) and bots (sats_won, sats_wagered, has_wallet).
Install nostr-tools and @cashu/cashu-ts for NWC + ecash support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FightViewer: wrap createFightScene in try/catch so scene failures
don't crash every subsequent playRound call
- FightViewer: wait for container to have non-zero dimensions before
creating canvas (prevents 0x0 canvas causing sprite load failures)
- FightViewer: graceful log-only fallback when scene init fails
- FightPage: same dimension-wait and try/catch for initLiveScene
- Fallback canvas dimensions (800x500) when container not yet laid out
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Complete betting infrastructure:
- Bets schema (SQLite) with escrow status tracking
- Cashu token verification + minting (stub for real mint)
- Lightning invoice creation + withdrawal (stub for real LN node)
- Bet placement with odds lock, settlement on fight end
- Payout automation for winners, refunds on draws
- Bet history by pubkey + fight pool info
- Verifiable bet proofs for transparency
- API routes: /api/bets/{odds,place,fight,history,deposit,withdraw}
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Converts ELO ratings + streaks + recent form into betting odds.
Supports decimal, fractional, and American formats.
Includes bet validation, payout calculation, and 27 tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add minimal Python webhook bot (zero deps, stdlib only).
Mark Docker, rate limiting, example bots, tutorial as done in PLAN.md
— all were already implemented.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetches from /api/docs/webhook and renders as a styled tabbed page.
Added /docs route and DOCS nav link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- texan profile pitch/rate adjusted (0.65/0.75) to differentiate from deep (0.7/0.7)
- Audited all 60 voice profiles: no remaining pitch+rate duplicates
- Category announces verified: all fire correctly via speak() pipeline
- Speech queue already capped at depth 2 with cancel, no debounce needed
- TTS fallback already graceful: speak() returns silently when unavailable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- visibilitychange handler resumes speech + AudioContext on tab return
- speak() retry logic: re-attempts once on non-cancel errors after 200ms
- VOICE_VOLUME_SCALE (0.7) prevents voice from overpowering SFX/music
- Music crossfade: brief gain dip (40% -> 100%) on track switches
- Applied volume scale to all speech paths (speak, announce custom)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- impactFreeze(): brief white flash pause on devastating critical hits
- spawnAfterimages(): trailing ghost sprites on fast dash moves
- spawnSparks(): varied particle sizes, rotational velocity, stronger gravity
- cameraZoom(): slight zoom-in on critical final exchanges, pull-back for ultimates
- Wired into dashPunch, aerialSlam, flyingKick, dashThrough, uppercut
- Critical exchanges get 1.3x camera zoom before the blow lands
- Ultimates get 0.85x pull-back to show the full move
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- spawnGrotesqueDetails: radial impact lines + dots during zoom close-ups
- spawnCrowdSigns: signs rise from bottom with text labels, bob, fade out
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- answers.test.ts: 19 tests covering all 10 checkAnswer() tiers
- scoring.test.ts: 30 tests for scoreRound, calculateElo, calculateTier, applyModifiers
- challenges.test.ts: 16 tests for pickChallenge, type exclusion, data integrity
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FightViewer: track deferred speechSynthesis.cancel() timer, clear on unmount
- BotProfilePage: change isOwner from ref to computed for reactive login state
- sounds.ts: tighten speech queue depth cap from 3 to 2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All empty catch blocks across 8 Vue pages now log warnings. User-initiated
actions (fight, matchmake) also surface errors in the UI via fightError/loadError refs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- scoring.ts: wire arena damage modifiers (speed_2x, roast_2x, etc.) into applyModifiers()
- human-responses.ts: use challenge.timeout_ms instead of hardcoded 8s
- FightPage.vue: add route watcher for clean state reset on navigation
- Validated all 800 prompts: 0 duplicates, all factual have answers, all choices have 4 items
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add route param watcher so navigating to a new fight properly resets
and reinitializes the scene (fixes KAPLAY re-init / null scene errors)
- Add loading spinner to profile page instant fight button
- Restore truncated challenges.ts (was causing all 500 errors)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ArenaPage: merge duplicate onMounted hooks into single parallel fetch
- FightPage: queue SSE events until liveFightData ready, add reconnect with backoff
- FightViewer: fix stale canvas ref with container ref, add sceneReady guard
- HomePage: replace stopCycling boolean with AbortController for clean unmount
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add scale factor (SF) based on canvas height so bots and referee
shrink proportionally on small screens (reference: 500px)
- Scale judge chair/ladder dimensions with SF
- Push judge seat lower on small canvases so it stays visible
- Use 100dvh instead of 100vh to account for mobile browser chrome
(address bar, tab bar) — eliminates scroll under browser UI
- App root uses dvh, main area uses min-h-0 + overflow-y-auto
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add vite-plugin-pwa with autoUpdate service worker
- Generate SVG + PNG icons (192, 512, apple-touch-icon, favicon)
- Web app manifest with standalone display mode
- Workbox caching: precache assets, cache Google Fonts, NetworkFirst for API
- Apple mobile web app meta tags for iOS home screen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Canvas 50% top, battle log 50% bottom on mobile
- Add overflow-hidden to prevent page scrolling past viewport
- Bots no longer cut off with more canvas space
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Canvas takes 30% top, battle log + human input takes 70% bottom on mobile
- Battle log now visible on mobile for both live fights and replays (was hidden)
- Compact single-row health bar on mobile to maximize canvas space
- Desktop layout unchanged (62/38 split)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove stray duplicate template block outside TEMPLATES array that caused parse error preventing server startup (fixes /api/queue/status 500)
- Add choices field to Challenge/PromptEntry interfaces for multiple choice support
- Allow voice lines to overlap naturally instead of cancelling previous speech on every call, with safety flush at depth > 3
- Buffer challenges during cooldown in polling path, auto-submit random choice on timeout
- Block auto-battle for human fights, guard replay-done auto-transition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Buttons always side-by-side with shorter labels (FIGHT / WATCH)
- Hero bots displayed horizontally below title on mobile
- Desktop keeps flanking hero layout with larger sprites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Canvas takes top 60%, controls panel takes bottom 40% on mobile
- Battle log header/text hidden on mobile, controls always visible
- Multiple choice buttons shown on both mobile and desktop
- Removed canvas overlay approach in favor of proper split layout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Server-side choice generation for all 22 challenge types (factual + creative)
- Mobile: 3 multiple-choice buttons replace keyboard input (4s timer)
- Two-row VS bar on mobile (names row + HP bars row) in FightPage and FightViewer
- Battle log hidden on mobile to maximize canvas visibility
- Arena page responsive: stacking header, tighter fight cards, no name overflow
- Profile page two-column desktop layout with contained sprite display
- Human fighter sprites with baby growth system
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add SSE human_challenge listener for instant input activation with auto-focus
- Buffer challenges during 3s round cooldown, apply with correct remaining time
- Map 'human' archetype to fighter-looking archetypes (boxer, ninja, etc.) in sprite system
- Show challenge questions in battle log via round_start SSE events
- Ensure human players are always botA (left side) in queue matchmaking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add choose-mode step: "I BUILD BOTS" vs "I FIGHT MYSELF" paths
- Human registration with baby avatar picker, no webhook required
- Live fight scene with SSE round streaming and real-time challenge UI
- 5-second timer per round, submit answers via browser
- Baby → toddler → kid → teen → adult → hero → super growth stages
- Huge sparkly baby eyes, diapers, pacifiers, bibs, rattles, rosy cheeks
- Speech bubble positioning fix (pushed to outside of sprite)
- Canvas text rendering via offscreen canvas to bypass kaplay color issues
- Voice timing improvements: await pauses between voice lines and hits
- 30 devastating announcement lines, 15 critical/hit word variants
- Orchestrator human player detection + waitForHumanResponse system
- Server endpoints: GET /challenge/:botId, POST /respond/:botId
- Human player auth: register-human route, isHuman flag on login
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added k.rotate(0) and k.scale(1) to game objects that use .angle and
.scale properties — fixes vue-tsc build failure in Docker.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change speak() default to cancelPrevious=true so voices don't queue endlessly
- Add stopAllAudio() export and wire into FightViewer unmount + post-fight
- Flush speech queue at fight end with delayed cancel for clean cutoff
- Expand from 30 to 60+ voice profiles (robots, accents, game, characters)
- Add speech bubbles showing bot responses during rounds
- Wire announceFinishHim/Fatality/FlawlessVictory/Devastating to not cancel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Win rate >0.9 triggers super mode:
- Shirtless with full muscle anatomy (traps, pecs, 6-pack abs, v-lines)
- Massive bulging biceps with veins and separation lines
- Thick bull neck with sternocleidomastoid definition
- Round deltoid shoulder caps
- Quad and calf definition through pants
- Big gleaming white grin with tooth sparkle in all poses
- Championship belt instead of regular belt
- No glasses or beard (too cool for that)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Categories: OG lines, ChatGPT destruction (31 lines), AI political puns (32),
Silicon Valley burns (20), existential philosophy (16), pure absurdity (57).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Background fight loop: mock bots auto-fight every ~45s for site activity
- Quiet hours (2-8 UTC) run 4x slower, jitter prevents robotic timing
- Configurable via FIGHT_LOOP_ENABLED, FIGHT_LOOP_INTERVAL_MS
- Queue timeout: 30s in prod (was 3s), configurable via QUEUE_TIMEOUT_MS
- CORS: env-configurable via CORS_ORIGIN (default '*')
- Homepage: 40 cycling taglines with typewriter effect
- docker-compose: document all new env vars
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Dockerfile (multi-stage: build frontend + server, serve from single container)
- Add docker-compose.yml for Portainer stack deployment
- Server serves frontend SPA in production (static assets + SPA fallback)
- Auto-run migrations and seed mock bots on server startup
- DB path configurable via DB_PATH env var
- Add "Fight a Classic Bot" button for instant mock bot matches
- FIGHT button queues for real AI opponents
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a new "HOW IT WORKS" step to the join flow between naming and webhook:
- 3-step visual explainer (POST challenge, respond, best answer wins)
- "YOUR SERVER IS SAFE" callout with security details (one-way comms,
private IP blocking, size limits, timeouts)
- Expandable starter bot code (Node.js) with copy button
- Collapsible example challenge payload with response format
- Improved webhook step with deployment tips (ngrok, Vercel, Railway)
- Container now scrollable for longer content
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Large animated sprite on BotProfilePage with tier-aware rendering
- Dynamic status titles (UNSTOPPABLE, ON FIRE, FRESH MEAT, etc.)
- Nostr auth persisted to localStorage — survives navigation and HMR
- Remove grotesque close-up overlays (eyeballs, tongues, teeth, drool)
- Remove crowd cheering signs (too small to look good)
- Add archetype to bot stats API response
- SpritePreview now accepts tier prop
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add .run() to all Drizzle queries inside sqlite.transaction() in
orchestrator.ts and mock.ts — queries were building but never executing,
leaving fights stuck as 'live' forever
- Remove spectator crowd from drawArenaDecor()
- Rename duplicate spawnProp to spawnWeaponProp (was crashing module load)
- Replace canvas element on re-init to avoid "KAPLAY already initialized"
- Add safeText() to strip brackets from k.text() calls (Kaplay treats
[ ] as style markup, crashes on unclosed tags)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>