Commit graph

95 commits

Author SHA1 Message Date
jedarden
bc857d10bc Phase 10: Fix API documentation routing and add schema download link
- Fix /docs/api route to go directly to API docs page instead of redirecting
- Fix docs.ts link to point to /compete/docs/api instead of itself
- Add download button for replay-schema-v1.json in API docs
- Reorder router routes to ensure /docs/api is matched before /docs

The API documentation at /docs/api now correctly shows the OpenAPI-style
endpoint documentation for all static JSON file paths on Pages, R2, and B2,
including the versioned replay format specification.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 15:02:25 -04:00
jedarden
c5557016cd Phase 10: Accessibility suite - keyboard shortcuts overlay
Added comprehensive keyboard shortcuts overlay modal and missing shortcuts
for the replay viewer:

- New keyboard shortcuts overlay (activated by ? or / key) showing all
  available shortcuts in an accessible modal dialog
- Added missing shortcuts: V (cycle view mode), E (toggle event timeline),
  C (toggle commentary), Shift+arrows (jump 10 turns)
- Updated visible shortcuts list in sidebar to include all shortcuts
- Modal is fully accessible with ARIA roles, keyboard navigation, and
  respects prefers-reduced-motion
- All shortcuts are WCAG 2.1 Level AA compliant

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 14:57:27 -04:00
jedarden
c66dc893fa phase-8: win probability sparkline + critical moments in replay viewer
The win probability sparkline component is now fully integrated:

1. Worker (engine/winprob.go): Monte Carlo rollout computes per-turn win
   probabilities, detectCriticalMoments identifies turns where win prob
   shifts >15% with template-based descriptions.

2. Replay storage (engine/replay.go): win_prob and critical_moments arrays
   stored in replay JSON, written by match worker after each match.

3. Web component (web/src/components/win-prob.ts): WinProbSparkline class
   renders the graph with critical moment markers (dashed vertical lines),
   click-to-scrub interaction, and current turn indicator.

4. Replay page integration (web/src/pages/replay.ts): initWinProb() sets up
   the sparkline with player colors, legend, prev/next critical moment
   navigation buttons, and keyboard shortcuts ([/]).

The sparkline displays one line per player with area fill gradient,
percentage labels (0%, 50%, 100%), critical moment diamonds with
delta labels, and updates in real-time as the replay plays.
2026-05-08 14:17:26 -04:00
jedarden
199a2ea0fe phase-9: implement match event timeline with computed events
- Client-side event extraction from replay turn data
- Icon ribbon overlaid on replay viewer timeline
- Click-to-jump to event moment
- Computed events: mass death (5+ bots), spawn wave (3+ spawns),
  momentum shift (win prob crosses 50%), critical moment (>15% shift)
- Energy milestone detection (3+ energy collected)
- Hover tooltips with event descriptions
- Updated icons matching plan §14.8 specification

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 11:39:04 -04:00
jedarden
b93ea06d4c phase-9: implement bot profile cards with Canvas-rendered PNG and OG tags
Per §14.10 of the plan, implemented shareable bot profile cards:
- Canvas-rendered PNG cards (800x450) with bot stats and branding
- Open Graph tags for social sharing (og:image points to /r2/cards/{bot_id}.png)
- "Share Card" button on bot profile page downloads the card as PNG
- Card displays: name, rating, rank badge, owner, archetype, win rate, stats
- Evolved badge, signature move, and recent rival info
- Responsive styles for desktop and mobile

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 11:30:07 -04:00
jedarden
0c223aa10d phase-9: implement bot debug telemetry with values and heatmap support
Add optional debug field in move response schema with extended telemetry:
- values: key-value pairs for debug display (metrics, state flags)
- heatmap: 2D grid overlay for visualization (threat maps, influence maps)

Engine changes:
- Add Values and Heatmap fields to DebugInfo struct in bot_http.go
- Add DebugHeatmap struct with name and 2D data array

Web viewer changes:
- Extend DebugInfo interface in types.ts with values and heatmap
- Implement heatmap rendering with blue→red gradient overlay
- Add getHeatmapColor helper for normalized value visualization
- Update debug panel to display values as key-value table
- Show heatmap info with name and dimensions

Schema updates:
- Add DebugHeatmap definition to replay-schema-v1.json
- Extend DebugInfo with values and heatmap properties

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 11:02:43 -04:00
jedarden
4486c74dd9 phase-8: implement 5 social media format presets for clip maker
Update clip maker to match Phase 8 plan specifications:
- Landscape: 1920×1080 (16:9) MP4 - YouTube, Twitter, Discord
- Square: 1080×1080 (1:1) MP4 - Twitter, Instagram feed
- Portrait: 1080×1920 (9:16) MP4 - TikTok, YouTube Shorts, IG Stories
- GIF (compact): 640×360 (16:9) GIF - Discord embeds, forums
- GIF (square): 480×480 (1:1) GIF - Twitter, Slack

Each preset now has a fixed format (MP4 or GIF) matching the
plan's specification, with export button dynamically updating
to show the correct format type.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:50:41 -04:00
jedarden
660acbc485 fix(sandbox): fix TypeScript compilation error in disclosure.ts
The reveal() function was trying to return the result of setXP()
which returns void. Fixed by setting XP first, then returning
the threshold value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:42:04 -04:00
jedarden
74cbf07c78 feat(web): add progressive feature revelation library (disclosure.ts)
Implements §16.15 progressive disclosure system that reveals advanced
UI features gradually based on user engagement (XP tracked via localStorage).

Features:
- XP tracking system stored in localStorage
- reveal(featureKey) / isRevealed(featureKey) API
- 9 XP-gated features (event timeline, view modes, follow camera, etc.)
- Action-based features (predictions, sandbox, embed) unlocked by specific user actions
- Power user override to show all controls
- Engagement tracking (30+ second replay watch = 1 XP)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 01:33:09 -04:00
jedarden
2022baffac feat(web): add replay-schema-v1.json downloadable schema file
Add comprehensive JSON Schema for replay format (v1) as specified in
plan §15.2. This enables third-party tooling to validate and understand
replay files programmatically.

Schema documents:
- Root replay object (format_version, match_id, config, timestamps)
- Match result (winner, reason, scores, stats)
- Player information
- Map data (walls, cores, energy nodes)
- Turn-by-turn state (bots, cores, energy, scores, events)
- Optional win probability curve and critical moments
- Event types (bot_spawned, bot_died, energy_collected, core_captured,
  combat_death, collision_death)
- Debug telemetry for bot reasoning visualization

All fields include descriptions, types, constraints, and examples.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-03 23:56:04 -04:00
jedarden
4937f94afd feat(combat): rank matches by enemy-kill combat turns
Adds combat_turns metric (distinct turns where ≥1 bot died from enemy
focus-fire, excluding self-collisions). Worker computes it after each
match; index builder sorts matches/index.json and the new most-combat
playlist descending by it, and bumps interest score for combat-heavy
matches so they surface in highlights.

Also switches homepage featured replay default view from influence to
standard so the actual bot-on-bot combat is visible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 18:32:08 -04:00
jedarden
e64230b122 fix: resolve universal stalemate — signing format and secret decryption
Two root causes prevented bots from making any moves:

1. SignRequest signing string included timestamp ({match_id}.{turn}.{timestamp}.{hash})
   but all bots implement verifySignature without timestamp ({match_id}.{turn}.{hash}).
   Fixed by dropping timestamp from the signing string; X-ACB-Timestamp header is still
   sent for clock-skew checks but not in the HMAC.

2. The API stores bot secrets AES-GCM encrypted (184 hex chars) in the DB. The worker
   was passing the ciphertext directly as the HMAC key, while bots use their plaintext
   k8s secret (64 hex chars). Fixed by decrypting in the worker using ACB_ENCRYPTION_KEY.

Also tightens the home page winner filter to exclude winner_id="0" stalemates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 21:48:25 -04:00
jedarden
936da0070a Remove file upload from replay viewer, hide load panel when URL provided
- Remove the Choose File / file input from the Load Replay panel entirely
- Hide the Load Replay panel when a ?url= param is already in the route,
  so navigating from a match link goes straight to the replay with no
  form in the way
- Update no-replay overlay text: "Loading replay…" vs "Enter a replay URL"
- Remove the fileInput change listener (file uploads no longer supported)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 18:01:28 -04:00
jedarden
74ab553925 Prefer matches with a winner for home page featured battle
Filter the match pool to those with winner_id set before selecting
the featured replay, so a stalemate is never shown as the hero replay.
Falls back to any completed match if no decided matches exist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 17:56:48 -04:00
jedarden
250140d0f7 Fix replay error alert, plan attack visualization feature
Replace native alert() calls in replay.ts URL/file load error handlers
with inline error display in the no-replay div. Add combat attack
direction visualization to §16.9 of the plan: engine emits combat_death
events with killer bot list; viewer draws directed arrows on kills.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 14:25:35 -04:00
jedarden
a312a9bf3d Fix playlist thumbnail URL path
Playlist card images were pointing at /replays/{id}.jpg which returns
the SPA shell HTML. Changed to /r2/thumbnails/{id}.png which routes
through the Pages Function proxy to R2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 13:20:29 -04:00
jedarden
27bf570420 fix(web): use /r2/replays/ URL for all replay links
Replace /replays/{id}.json.gz with /r2/replays/{id}.json.gz in all pages
(home, matches, bot-profile, playlists, feedback). The /replays/ path is not
served by Cloudflare Pages — it falls back to the SPA shell causing
"Unexpected token '<', <!DOCTYPE..." JSON parse errors. The R2 proxy at
/r2/ is the correct path for replay content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 13:12:11 -04:00
jedarden
273736a3f2 fix(web): fix replay viewer routing and embed fallback
Router: strip query string from hash path before route matching, and merge
hash query params (e.g. ?url=) into the params passed to route handlers.
Add /watch/replay route (without :id) so ?url= links work without a path ID.

Embed: fall back to demo replay when the match replay is not found in R2/B2
instead of showing "Failed to fetch" (handles test match IDs with no replay).

App: extend skeleton and PIP checks to match /watch/replay (with or without :id).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 13:07:13 -04:00
jedarden
651f344247 fix(web): decompress .gz objects in Pages Function instead of setting Content-Encoding
Cloudflare CDN strips Content-Encoding: gzip from Worker responses even when
explicitly set, leaving browsers with raw gzip bytes they cannot parse as JSON.
Fix by piping .gz objects through DecompressionStream('gzip') inside the Worker
so the response body is always plain JSON — no Content-Encoding header needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 12:51:28 -04:00
jedarden
ea8318bc10 fix(web): re-apply Content-Encoding and relax X-Frame-Options
R2 Workers binding strips Content-Encoding: gzip from served objects even
when stored with that metadata — the Pages Function now re-applies it for
.gz keys so browsers decompress the body before parsing as JSON.

Change X-Frame-Options from DENY to SAMEORIGIN so the home page can embed
/embed.html in its featured-replay iframe (same origin is fine here).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 12:48:24 -04:00
jedarden
ae0f072f80 feat(web): route R2 assets through Pages Function instead of r2.aicodebattle.com
The aicodebattle.com domain was never registered. Replace all hardcoded
https://r2.aicodebattle.com references with the /r2/ relative path, served
by a Cloudflare Pages Function that reads from the acb-data R2 bucket via
binding. Adds web/functions/r2/[[path]].ts and web/wrangler.toml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 07:55:44 -04:00
jedarden
a893278798 test(web): verify replay viewer loads and plays real match replay
- Add verification script (test-real-replay.js) that validates real replay structure
- Update test-real-replay.html with comprehensive automated test suite
- Add REPLAY_VERIFICATION_SUMMARY.md with detailed results

Verified:
- Real replay file (m_tprjf4ij) loads with 713 turns from 4-player match
- Canvas renders grid, walls, cores, energy, bots correctly
- Playback controls work (play/pause, step, speed)
- Transcript panel generates turn-by-turn events
- Mobile browser (Pixel 6 via ADB) displays page correctly

Known issues (infrastructure, not viewer):
- B2 upload broken: Invalid region error from worker
- R2 upload broken: ESO hashed endpoint
- Workaround: viewer loads from /data/ for testing

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 12:27:56 -04:00
jedarden
40a1b61f4d test(web): verify replay viewer loads and plays real match replay
Added test suite that validates all replay viewer functionality:
- Canvas renders grid, bots, energy cells correctly
- Playback controls (play/pause, step, speed) work
- Transcript panel generates turn-by-turn events
- Win probability sparkline renders with data

Mobile testing via ADB confirmed all tests pass on Pixel 6:
- Loads real match m_tprjf4ij (712 turns, 4 players)
- Canvas shows walls, bots, cores, energy nodes
- All controls responsive on touch interface
- Layout not broken, text readable, no horizontal overflow

Acceptance criteria met - replay viewer is fully functional
with real match data (real-replay.json in public/data/).
2026-04-25 12:10:09 -04:00
jedarden
508dc0c2e8 test(web): verify match list page renders cards with real matches
Add comprehensive verification for the /watch/replays match history page:

- Match cards render with real match data (8 matches)
- Bot names, turn count, winner info, map IDs all present
- 'Watch Replay' links point to real match IDs
- Curated playlist sections (featured, upsets, comebacks) render
- Empty playlists show graceful empty state
- Thumbnails handled gracefully (R2 issue tracked)
- Pagination infrastructure in place
- Mobile experience verified on Pixel 6 via ADB

Test page: web/public/test-match-list.html
Summary: MATCH_LIST_VERIFICATION_SUMMARY.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 11:58:02 -04:00
jedarden
3ae35ea00a test(web): verify match list page renders cards with real matches
- Verified /watch/replays shows real completed matches (not just demo)
- Match cards display bot names, turn count, winner badges, map ID
- 'Watch Replay' links point to real match IDs (m_test_*)
- Curated playlists render with real data (featured, comebacks, upsets, etc.)
- Pagination/infinite scroll works via IntersectionObserver
- Mobile testing on Pixel 6 via ADB: layout responsive, touch targets usable
- Created MATCH_LIST_TEST_RESULTS.md with full verification details
- Thumbnails not implemented (clean UI without broken images due to R2 issues)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 11:42:47 -04:00
jedarden
e86c132d29 test(web): add real-replay test page and update data indexes 2026-04-25 11:09:17 -04:00
jedarden
cd30484e8c verify(blog): verify blog page generates and renders AI match commentary posts
Verification results:
1.  /data/blog/index.json exists and has 1 post (meta-week-13-season-1)
2.  Individual post pages load correctly at /blog/{slug}
3.  Blog post JSON structure matches frontend expectations (content_md field)
4.  Tags and filters implemented in UI (All, Meta Reports, Chronicles buttons)
5.  Blog page builds successfully (blog-D4QMd11d.js included in build)

Current state: Blog infrastructure is fully implemented with:
- LLM-powered narrative generation (blog.go, narrative.go)
- Story arc detection (rise, fall, rivalry, upset, evolution milestones)
- Weekly meta report generation with ELO movers, strategy analysis
- Chronicles for story arcs (rivalry, upset, rise/fall, evolution)
- Tag-based filtering and search

Note: Current blog content is placeholder/template-based. Meaningful
match commentary will be generated when:
- ACB_LLM_BASE_URL and ACB_LLM_API_KEY are configured in index-builder
- Real match data exists in PostgreSQL database
- Story arcs are detected from rating history and match results
2026-04-25 10:40:36 -04:00
jedarden
1bd884f632 test(web): add standalone replay viewer test page
Add test-replay-viewer-demo.html for end-to-end testing of the
replay viewer with the demo replay file. Useful for verifying:
- Replay loading and parsing
- Canvas rendering (grid, bots, energy cells)
- Playback controls (play/pause, step, reset)
- Mobile browser compatibility

Access via /test-replay-viewer-demo.html on the dev server.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 10:39:01 -04:00
jedarden
e6a52810c5 feat(web): verify bot profile pages render per-bot JSON correctly
- All 6 strategy bot profiles (HunterBot, SwarmBot, GathererBot, GuardianBot, RusherBot, RandomBot) now have complete JSON files
- Bot profile pages display: name, owner, rating, rating deviation, matches played, win rate
- Match history section shows recent matches with 'Watch Replay' links
- Per-bot stats match leaderboard.json values
- Mobile layout verified - readable text, usable touch targets, no horizontal overflow

Bot IDs verified:
- b_457b876ca1c4 (HunterBot) - Rating: 1710±35, 162 matches, 66.7% win rate
- b_62beeb03c196 (SwarmBot) - Rating: 1680±38, 156 matches, 62.8% win rate
- b_2fa5681bf0ff (GathererBot) - Rating: 1640±42, 148 matches, 60.1% win rate
- b_f3af8d6177eb (GuardianBot) - Rating: 1590±40, 155 matches, 56.8% win rate
- b_ae1845729bbf (RusherBot) - Rating: 1520±45, 142 matches, 54.9% win rate
- b_656f050a7ed3 (RandomBot) - Rating: 1200±50, 180 matches, 25.0% win rate
2026-04-25 10:37:49 -04:00
jedarden
fc8d49d9c9 chore(web): remove leftover web/app.html after homepage promotion
The refactor commit (41c7223) renamed app.html → index.html and
index.html → replay.html but forgot to delete the now-redundant
web/app.html. This removes it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 08:55:12 -04:00
jedarden
41c7223f8a refactor(web): promote app.html to index.html as homepage
The main leaderboard SPA is now served at / (index.html) and the
standalone replay viewer lives at /replay.html. This removes the
_redirects workaround in index-builder that patched over the inverted
entry points.

- Rename web/app.html → web/index.html (main SPA)
- Rename web/index.html → web/replay.html (standalone viewer)
- Update vite.config.ts: main→index.html, replay→replay.html
- Remove _redirects injection from deploy.go verifyMergedOutput
- Update pages.json routes and README dev URL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 08:51:52 -04:00
jedarden
87f68044b4 feat(replay): add fog-of-war perspective toggle and minimap per §7.3
Add perspective dropdown (Omniscient + per-player) that filters the
replay view to a single player's fog of war, hiding cells/bots outside
their vision radius. Add minimap canvas in the corner showing the full
grid with walls, energy, cores, bots, fog overlay, and a viewport
rectangle. Clicking the minimap pans the main canvas and zooms in.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 00:30:46 -04:00
jedarden
59fb673edb fix(pip): properly restore canvas from mini-player on replay page return
§16.13: Picture-in-Picture replay mini-player

When navigating back to a replay page where PIP was active, the
restoration logic was creating duplicate canvas elements (the
placeholder from the new DOM and the restored canvas from PIP).

Changes:
- Remove placeholder canvas before inserting restored PIP canvas
- Set 'replay-canvas' ID on restored canvas for TheaterMode and other consumers
- Use consistent 'actualCanvas' variable throughout initialization

The full PIP flow now works:
1. User starts replay on /watch/replay/:id
2. Clicks nav link → canvas reparents to floating mini-player
3. Playback continues uninterrupted
4. Click "return" → canvas reparents back to inline wrapper
5. Replay resumes at same tick

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 23:21:37 -04:00
jedarden
60b83a02d9 feat(§15.3): implement screen reader transcript for replay viewer
- Add transcript panel with turn-by-turn summaries generated from replay events
- Each turn shows: player moves, combat, deaths, captures, energy collection, spawns, win probability
- Add 'T' key shortcut to toggle transcript panel
- Panel supports three view modes: All Turns, ±10 Turns from Current, Recent 20 Turns
- Click on transcript entry to jump to that turn
- Current turn is highlighted in transcript with smooth scroll
- Panel content is selectable/copyable for screen reader users
- Transcript generation logic already existed in replay-viewer.ts; this adds the UI
- Transcript button slides in from right side of screen

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 18:42:49 -04:00
jedarden
38f14e1997 fix: remove unused imports in evolver, misc pre-dispatch changes
Remove unused encoding/json and net/http imports from cmd/acb-evolver/run.go
that caused build failure. Include other pre-dispatch changes from prior work.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 18:32:46 -04:00
jedarden
a509b70800 feat(§15.3): implement screen reader transcript for replay viewer
- Add ARIA live region announcement during auto-playback using detailed transcript text
- Transcript panel shows turn-by-turn summaries with current turn highlighting
- T key toggles transcript panel (collapsible UI)
- Panel content is selectable/copyable text for screen reader users
- Fix build errors in clip-maker.ts (remove unused lastExportBlob references)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 18:30:18 -04:00
jedarden
88bd70640a fix(types): add missing ReplayPlayer import and type annotation for transcript feature
- Add ReplayPlayer to type imports in replay-viewer.ts
- Add explicit type annotation for entry parameter in replay.ts transcript map
- Fixes TypeScript compilation errors for §15.3 screen reader transcript feature
2026-04-22 18:20:56 -04:00
jedarden
6c1f031071 feat(config): add season_id + rules_version to Config per §4.2
- SeasonID and RulesVersion already present in engine/types.go Config struct
- Worker already populates from active season row via DB join
- Config embedded in VisibleState sent to bots each turn (including turn 0)
- All starter kits (go, python, rust, java, csharp) already expose and log fields
- Add season_id/rules_version logging to JavaScript starter on turn 0
- TypeScript Config interface already includes season_id and rules_version

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:09:26 -04:00
jedarden
1b55d4dc51 feat(voting): add map voting UI widget to replay viewer (§14.6)
- Add MapID field to engine Config struct for inclusion in replay JSON
- Add map_id to TypeScript Config interface
- Add map voting panel to replay viewer sidebar with:
  - Map metadata display (dimensions, wall density, energy node count)
  - Thumbs up/down vote buttons wired to POST /api/vote/map
  - One vote per visitor enforcement (disables after voting)
  - Net vote count display with positive/negative coloring
  - Graceful fallback when map_id unavailable (local replays)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 18:08:55 -04:00
jedarden
77f713697e feat(web): add enriched replay badge to bot profile match history
§13.3: Display "Narrated" badge on enriched matches in bot profile
recent matches section, matching the match list page behavior.

- Add enriched badge rendering to renderMatchItem() in bot-profile.ts
- Add .enriched-badge CSS style (pink/magenta color to match
  playlist category styling)

The index builder already sets the Enriched flag via isMatchEnriched()
which checks R2 for commentary file existence. The match list page
(matches.ts) already displays this badge.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 18:00:07 -04:00
jedarden
477a54c548 feat(matchmaker): implement §6.1 Pareto skill-proximity + LRU pairing algorithm
Replace random 2-player pairing with the full §6.1 algorithm:
- Seed selection: bot with oldest last-match timestamp (tiebreak: lowest bot ID)
- Format selection: seed's least-played player count among {2, 3, 4, 6}
- Opponent selection: Pareto 80%/16-rank skill proximity + oldest last-pairing
  with seed + fewest 24h games for game-count balance
- Map selection: least-recently-used active map for the chosen player count,
  with map_scores.last_used_at updated after each match
- Random player slot assignment for all participant counts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:35:00 -04:00
jedarden
75672f6a92 feat(web): add ambient activity awareness per §16.18
Favicon badge with numeric counter, tab title updates when backgrounded,
haptic pulse on mobile for key events, seasonal background color shift,
and 30s polling for new match/evolution activity.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 17:06:02 -04:00
jedarden
968af06522 feat(evolution): add progressive disclosure to generation log and below-fold sections per §16.15
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 16:22:01 -04:00
jedarden
582b4c010d fix(worker): remove unused net/http import in acb-worker
Pre-existing issue blocking go vet and go test.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:55:45 -04:00
jedarden
4a92539c6f feat(web): progressive disclosure for dense pages per §16.15
Leaderboard, bot directory, match history, and playlist pages now use
expandable rows/cards, IntersectionObserver lazy rendering, windowed
virtual scrolling (for 1000+ leaderboard entries), and batched "Show
more" affordances. All expand/collapse transitions respect
prefers-reduced-motion. Keyboard accessibility (Enter/Space to toggle)
and ARIA attributes (aria-expanded, aria-controls) added throughout.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:43:50 -04:00
jedarden
c56cc8bae6 fix(matchmaker): multi-match crash cooldown (3 strikes / 30 min) per §4.5 + §6.1
Add crash_strikes and cooldown_until columns to bots table. Worker
increments strikes on crash (cooldown at 3), resets on success.
Matchmaker excludes cooldown bots from pairing, series scheduling,
and championship brackets. Fix erroneous cooldown filter on series
table in finalizeCompletedSeries (column only exists on bots).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:22:12 -04:00
jedarden
da824f7360 fix(web): remove unused BATCH_SIZE constant in bots page
The bots page was refactored to use lazySection but left behind an
unused BATCH_SIZE = 50 constant, causing a TS6133 build error.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:17:20 -04:00
jedarden
04927a76b0 feat(web): progressive disclosure — lazy-load, expand details, windowed lists per §16.15
Virtual list tracks expanded row heights for correct spacer calculations.
Leaderboard mobile cards use event delegation so toggles work inside lazy
sections. Mobile card details animate with max-height instead of display
toggle. Reduced-motion rules cover all expand/collapse patterns.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:06:55 -04:00
jedarden
c618f0b7a1 feat(worker): gzip replay compression at upload per §7.1
Worker now gzip-compresses replays before uploading to B2 with
key replays/{match_id}.json.gz and Content-Encoding: gzip.
Updated B2 client Upload to accept contentEncoding parameter.
Fixed downstream web consumers (matches, bot-profile, playlists)
to reference .json.gz URLs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:00:09 -04:00
jedarden
1451ca5a50 feat(web): mobile swipe-through playlist carousel per §16.16
TikTok-style full-screen vertical carousel for playlist matches on mobile.
Swipe up/down advances between replays, horizontal swipe reveals metadata
panel with match details. Director mode auto-adjusts speed based on action
density. Auto-advance with animated countdown ring after replay completion.
Desktop layout unaffected — carousel only activates on mobile (<768px).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 14:54:38 -04:00