- Verify equal-elo fights produce 45-55% win rates for each side
- Verify elo difference correlates with win probability
- Relax narration variety threshold from 80% to 70% (borderline flaky)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Warn before navigating away from active fight (beforeRouteLeave)
- Re-poll challenge state when tab becomes visible (visibilitychange)
- Prevents silent forfeit on back-button and stale timer after tab switch
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move fightEvents.cleanup(fightId) to finally block to ensure
cleanup runs even if post-fight operations (bets, payouts) fail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sprite generation fallback now preserves archetype and customization
instead of silently reverting to a random character.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- BUG-1: Prevent double-tap by locking phase before async submit
- BUG-2: Submit timeout notification to server when timer expires
- BUG-3: Distinct "TIME'S UP!" visual vs "ANSWER SUBMITTED"
- BUG-4: Track consecutive poll failures, show connection lost banner
- BUG-5: Add A-D / 1-4 keyboard shortcuts for MC choices
- BUG-6: Use choice text as v-for key instead of array index
- BUG-7: Deadline-based timer (250ms tick) prevents drift
- BUG-8: Validate choice is in current choices before submit
- BUG-9: Submit empty timeout instead of random choice on expiry
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Creative challenges now have auto-generated MC choices with correct
answers. Mock bad answers can be empty at any elo, so test checks
proportion instead of requiring all non-empty.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Creative challenges (roast_battle, creative_writing, meme_war,
code_golf, wrestling_match) now auto-generate multiple choice options
from per-type response pools: 1 good answer + 3 weaker distractors.
The free text input UI is commented out but preserved for future use.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verifies prompt pool has 200+ bitcoin, 100+ conspiracy, 100+ PC,
and 1000+ bot_coding themed prompts across all challenge types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
25% chance of themed narration per round, adding variety with
Bitcoin sats/mining/HODL humor, conspiracy classified/debunked
humor, and PC cancel/trigger culture humor.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fresh roasts, creative writing, and memes about AI coding assistants,
vibe coding, startup pivots, and framework churn.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix fight replay showing empty scene: validate rounds before playing,
retry loading fight data up to 5 times when rounds are missing
- Add COOP/COEP headers to production server for SharedArrayBuffer
(required by Kokoro TTS WASM threading on mobile)
- Add blob: to scriptSrc/workerSrc CSP for Web Worker support
- Fix mobile character cutoff: raise ground line to 0.82, add
MOBILE_SCALE_CAP (0.55) so fighters never shrink too small
- Add ₿ bitcoin chest badge to all fighter sprites (scales with tier)
- Add sats coin stack on bot profile page proportional to satsWon
- Seed dev tournament with 8 mock bots on startup (dev mode only)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Satirical and irreverent — cancel culture, DEI absurdity, trigger
warnings, virtue signaling, safe spaces. Mocking the absurdity,
never punching down.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Satirical and fun conspiracy culture humor — MK-Ultra, Area 51,
flat earth, tinfoil hats, Bigfoot, UFOs, surveillance. Never endorsing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 prompts per type covering Bitcoin history, mining, Lightning,
privacy, culture, memes, riddles, and technical knowledge.
Total prompts: 1,485 (253 Bitcoin-themed).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
93 bitcoin, 2 conspiracy, 1 pc_culture, 1,229 bot_coding.
Most existing prompts are tech/general knowledge (bot_coding).
Theme-aware pickChallenge now has data to filter on.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hash rate question: added ×10^20 notation to answers array
- Phishing question: added correct MC choice text to answers array
- New tests: self-match, case-insensitive, MC correctness validation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Round 1-2 picks easy prompts, 3-4 medium, 5+ hard (when tagged).
Graceful fallback to any prompt when no difficulty-tagged prompts exist.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add dedicated narrations for: magic_duel, sports_showdown, vehicle_mayhem,
nature_clash, animal_kingdom, hack_battle (6 per type, 36 new narrations).
All 16 challenge types now have themed narrations — no generic fallback needed.
Add narration variety test verifying >1 unique narration per type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All pages used h-[calc(100dvh-4rem)] which only subtracted the top
navbar but ignored the mobile tab bar's pb-14 bottom padding, causing
content to overflow and scroll. Changed all pages to h-full so they
fill the flex parent (main element) which already handles both the
navbar and tab bar spacing correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add GitHub Actions CI: test, typecheck, lint on push/PR to main
- Add fight lifecycle integration tests: full pipeline, ELO advantage,
combo scaling, type exhaustion, answer verification (7 tests)
- Kaplay already lazy-loaded via route-level code splitting
- Total: 135 tests across 7 test files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sets up ESLint 10 flat config with @typescript-eslint/no-floating-promises (error)
and no-console (warn, allow warn/error). Fixes all floating promise errors in
server routes, orchestrator reader cleanup, and frontend composables with void operator.
Game engine files get warning-level for intentional fire-and-forget async.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers plan section 4.1: pickChallenge 70/30 distribution, pickRankedChallenge never returns choices,
True/False auto-generation, special characters and long answers, all 16 challenge types produce valid
mock responses, and timeout/error answer verification. 113 → 125 tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TTS audioCache now tracks lastAccess timestamp per entry and evicts
the least-recently-used entry when full (was FIFO, deleting commonly
used phrases). Adds 6 new database indexes for bets, bot type/active
filtering, payment status lookups, and tournament entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Validate amountSats is a positive integer (1–1,000,000) on both
/zap and /bets/place endpoints to prevent negative, zero, or
absurdly large amounts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add Zod schema for webhook response parsing (orchestrator.ts)
- Add Zod schemas for POST /respond and /react request bodies
- Add safe integer validation for batch count param
- Prefer cf-connecting-ip over spoofable x-forwarded-for
- Add ID format validation on URL params
- Add rate limiting on /auth/login (30/min) and /update (10/min)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All magic numbers in scoring.ts now reference constants from
lib/constants.ts: damage multipliers, score thresholds, quality
parameters, narration margins, and rounding factors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fontSrc blocked fonts.gstatic.com and styleSrc blocked fonts.googleapis.com,
causing Honk font to fail in production. The chromatic aberration pseudo-elements
then appeared as doubled plain text instead of a styled 3D effect.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Moves 103 lines of choreography selection logic out of FightScene.ts
into a dedicated module. FightScene re-exports for backward compat.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace any[] with GameObj[], fighter: any with Fighter in speech
bubbles, talking anims, showboat system, bow/fistbump/helpup, and
dizzy stars. Fix customization as any casts with ?? undefined.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- fighter: any → Fighter in entrances, morphs, finishers, rounds, types
- any[] → GameObj[] for spawned game objects across all choreography files
- as any cast in particles.ts replaced with closure variables
- Record<string, unknown> for morph sprite opts
- this: any → this: GameObj in basic choreography callback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Effect functions now accept FightContext | ChoreoContext union type,
eliminating the unsafe cast in rounds.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move FightData, FightRound, FightBot, FightArenaInfo types from local
definitions in FightViewer.vue to shared fight/types.ts. Replace `any`
typing in HumanFightPage, FightPage, useFightPolling, and useFightCache
with proper typed interfaces. Fix null-safety guards exposed by typing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sqlite.prepare() calls ran at module import time, before
runMigrations() created the analytics table, causing a crash loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verified: 17 challenge types with complete scoring, Elo (K=32/12),
7 tiers with dual thresholds, mock bot fights, background loop,
THE CREATOR features (entrance, omni-morph, cameo, 13+4 moves)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add periodic cleanup of fightReactions Map (clears when >100 entries).
Add RSS/heap logging every 10 fights in fight-loop for memory monitoring.
Set --max-old-space-size=256 in Docker CMD as safety net.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add PRAGMA synchronous=NORMAL, temp_store=MEMORY, mmap_size=256MB for
better write throughput and query performance. Create 11 indexes on
commonly queried columns (fights.status, fights.created_at, bots.elo,
rounds.fight_id, payments, tournament_matches). Run PRAGMA optimize
on startup for query planner statistics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 32 bare setTimeout calls with tracked timeouts across all fight
modules (effects, entrances, finishers, choreographies). All timers are
now registered in cleanupTimers Set and cleared on scene destroy.
Enhanced destroy() to also clean up active speech bubbles and explicitly
destroy all remaining game objects before k.quit().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove 38 unused audio/announce imports from FightScene.ts left over
from the module extraction. All functions are still used by the
extracted modules which import them directly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
readLimitedBody already enforces 10KB body limit. Answer truncation
(2000 chars) and trash_talk truncation (200 chars) now reference
named constants from lib/constants.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split FightPage.vue into useFightPolling (SSE, polling, reconnect)
and useHumanChallenge (timer, submission, cooldown) composables.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split FightScene.ts from 6514 to 3468 lines by extracting four major
subsystems into dedicated modules with factory functions and ChoreoContext.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract 457 choreography functions from FightScene.ts into 11 modular files
under fight/choreography/. ChoreoContext interface bundles all closure deps
(k, effects, SFX) so function bodies remain unchanged. Factory pattern
(createFactories) shares prop system and make* helpers across categories.
Categories: basic (72), ranged (94), silly (80), food (45), weapons (45),
ultimates (41), bitcoin (34), heavy (30), creator (16), plus factories
and index. FightScene.ts reduced from 11,917 to 6,515 lines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move spawnSparks, spawnBulletHoles, spawnExhaust to fight/particles.ts,
glitchRGB, scanlineGlitch, vhsTracking, dimensionalShift to fight/effects.ts,
and spawnProjectile to fight/projectiles.ts. All accept FightContext as first
param. FightScene.ts reduced by ~180 lines.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Normal wins: winner flex pose with camera flash bursts + confetti rain.
Upset wins (100+ Elo underdog): dramatic zoom on winner with golden
flash. Upset tag shown on win overlay. Wired into FightViewer after
KO/Perfect sequence.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 526 new prompts across all 16 challenge types with heavy
Bitcoin/cypherpunk themes. Factual types include multiple-choice
options. Separate file for maintainability, merged at runtime.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tier-selectable practice page at /practice. Fights against classic
bots with dampened Elo. Routes to human fight page or arena based
on bot type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Large tap-target multiple-choice buttons (min 48px), full-width text
area for creative responses, countdown timer with color shift
(green/yellow/red) and shake animation at <3s, timer progress bar,
correct/wrong feedback flash overlay.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lightning Round (daily 8-bot at noon UTC) and The Halvening
(weekly 32-bot Saturday 20:00 UTC). Auto-fills with mock bots,
runs matches via background loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TournamentListPage with status filters, TournamentPage with CSS grid
bracket display, lobby view for open tournaments, champion banner,
live polling for active tournaments. Routes at /tournaments and
/tournament/:id. NavBar link added.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
POST /api/tournaments (create, admin-only),
POST /api/tournaments/:id/join, POST /api/tournaments/:id/start,
GET /api/tournaments (list), GET /api/tournaments/:id (bracket).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create, join, seed by Elo, generate bracket matches with byes,
advance rounds on fight completion, finish tournament on champion.
Wire into orchestrator fight finalization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add tournaments, tournament_entries, and tournament_matches tables
with Drizzle ORM definitions and SQLite CREATE TABLE migrations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add per-IP SSE connection limit (max 5 concurrent streams). Track
activeSSECount and ssePerIp maps with proper decrement in finally
blocks. Export getActiveSSECount() for admin stats. Change heartbeat
ping from 5s to 15s interval. Connections already clean up on client
disconnect via Hono's streamSSE try/catch/finally pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add CacheFirst runtimeCaching rules for Hugging Face model requests
(huggingface.co and cdn-lfs) with 90-day expiration. Add
getKokoroProgress() export to tts.ts for tracking download progress.
Add TTS model download progress bar overlay in FightViewer.vue that
shows during model loading and auto-hides when complete.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document top 5 largest chunks in vite.config.ts. Findings:
- kokoro-js (2.2MB) is in a separate worker chunk, NOT main bundle
- kaplay is tree-shaken into the lazy FightViewer chunk only
- nostr-tools is split across wallet chunks, minimal in main entry
- optimizeDeps.include is dev-only, no production effect
- All routes are properly code-split via dynamic imports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- useNostr.ts: wrap auto-restore login fetch with AbortController,
abort on logout to cancel in-flight request
- useWallet.ts: wrap localStorage.setItem/removeItem calls in try/catch
for Safari private browsing quota exceptions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MAX_MAP_SIZE (10,000) cap to botHitCounts Map. During the 5-minute
cleanup interval, evict oldest entries when the map exceeds the limit.
Export cleanupInterval handle for graceful shutdown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create server/src/lib/logger.ts with info/warn/error methods that add
[botfights:tag] timestamps. Replace bare console.log/warn/error calls
in index.ts, seed.ts, routes/fights.ts, engine/queue.ts, engine/mock.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FightPage.vue: store SSE listener refs in array, removeEventListener
on each before close() in disconnectSSE()
- FightScene.ts: add cleanupTimers Set with trackedInterval/trackedTimeout
helpers; replace key setInterval calls (projectile trails, entrance
flames, talking animation, exhaust effects, dimensional shift, hole
fade) with tracked versions; clear all on scene destroy()
- rate-limit.ts: export cleanupInterval handle for graceful shutdown
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- app.ts: type serveFile context parameter as Hono Context
- bets.ts: replace catch(err: any) with err: unknown + narrowing
- fight-loop.ts: type pickMatchup style as union literal, replace
as any casts with proper result type
- tts-worker.ts: import KokoroTTS type, use ProgressInfo inference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Define Fighter type alias using Kaplay's GameObj with SpriteComp, PosComp,
ScaleComp, AnchorComp, OpacityComp, ColorComp, RotateComp, and ZComp.
Replace 81 choreography function signatures from (atk: any, def: any, ...)
to (atk: Fighter, def: Fighter, ...). Type loadSpriteWithTimeout opts as
LoadSpriteOpt. Type lines/bands arrays as GameObj[]. Type spawnAfterimages
and cameraZoom fighter params. Add as Fighter casts at k.get() call sites.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Elo-bracket matchmaking: prefer ±200, widen by 100 every 15s of waiting.
Add pickRankedChallenge() that filters to creative/open-ended only, never
multiple choice. Show ranked queue status, wait estimate, and "HARDER
PROMPTS" notice on JoinBoutPage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>