Commit graph

125 commits

Author SHA1 Message Date
jedarden
88d2fa161f feat(web): implement haptic feedback at critical moments in replay viewer
Adds mobile haptic feedback (50ms vibration pulse) at critical moments
during replay playback per plan §16.18:

- Added opt-in toggle in Accessibility panel (checked by default)
- Triggers vibration on:
  - Combat deaths (bot_died events)
  - Core captures (core_captured events)
  - Win probability shifts >15%
- Imports hapticPulse, isHapticEnabled, setHapticEnabled from ambient.ts
- Integrated into onTurnChange callback for real-time feedback

Closes: bf-2m3wm

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 21:45:48 -04:00
jedarden
13d5a9f17d feat(web): add win probability graph to mobile playlist carousel §16.16
- Integrated WinProbSparkline component into playlist-carousel metadata panel
- Compute win probabilities on card load using WinProbabilityEngine
- Tap score bar now toggles metadata panel with win probability graph
- Display critical moments and turn-by-turn probabilities
- Sparkline is clickable to scrub to specific turns

Closes: bf-12nc7

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 21:36:22 -04:00
jedarden
f35477dd96 feat(evolution, web): add live match counter per plan §16.18
- Add matches_today and active_bots fields to LiveData Totals (evolver)
- Query matches table for COUNT(*) WHERE completed_at >= today
- Query bots table for COUNT(*) WHERE status = 'active'
- Add fields to index builder EvolutionMeta struct
- Update homepage to render "X matches today · Y bots active · Gen #Z evolving"
- Add CSS styling for .home-live-stats section

Closes: bf-4m8mo
2026-05-26 19:57:57 -04:00
jedarden
64aa6aef40 test(web): add tests for combat_death attack arrow rendering
Plan §5055: Attack event arrows from killers to victim position.

Added 3 tests to verify:
1. Arrows are drawn from each killer to victim in combat_death events
2. Multiple arrows are drawn for multiple killers (2v1 situations)
3. Arrow colors are set based on attacker player color

The tests mock the canvas context and verify that the appropriate
drawing methods (moveTo, lineTo, stroke, fill) are called when
rendering turns with combat_death events.

Closes: bf-4o9fp
2026-05-26 18:39:12 -04:00
jedarden
79f3bee8a9 fix(web): serve replays from same-origin /data/ static assets, not R2
Replays are bundled into the Pages deploy as gzipped static assets (B2 stays
the private cold archive). Repoint all replay/card/thumbnail/live.json fetches
off the empty R2 cache and the non-resolving b2.aicodebattle.com onto
same-origin /data/, via a shared fetchReplayFromUrl helper that gunzips
.json.gz with DecompressionStream.

- new web/src/lib/replay-data.ts (REPLAY_BASE, replayUrl, fetchReplayFromUrl)
- replay.ts / embed.ts / pages/embed.ts / playlist-carousel.ts use the helper
- og-tags, bot-card, home, matches, bot-profile, playlists, feedback, ambient,
  api-types: /r2/ -> /data/
- pages.json data_paths updated; friendlier 404 message preserved
- 21 web tests pass; npm run build OK

Closes: bf-5cwi

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 16:41:30 -04:00
jedarden
46b9a90f35 feat(web): add maps browsing page per plan §14.6
Implements plan §14.6 Map Evolution with a dedicated /maps page for browsing all competitive maps. Users can now:
- Browse maps grouped by player count (2P, 3P, 4P, 6P)
- View map stats (engagement score, wall density, energy count, dimensions)
- Upvote/downvote maps directly from the browsing interface
- See net vote counts and their own vote state

The page integrates with existing map voting API and data structure from the index builder.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 16:23:59 -04:00
jedarden
639ae65ace feat(web): enhance demo blog post with LLM-generated sections
Update web/public/data/blog/posts/meta-week-13-season-1.json to show
what an LLM-enhanced meta report looks like. Adds:

- Counter-Strategy Spotlight section with narrative analysis
- Evolution Deep Dive subsection with generational insights
- Enhanced Looking Ahead section with specific predictions
- Structured tables for strategies, rising stars, rivalries, evolution
- Prediction standings leaderboard
- More detailed summary with key insights

This placeholder now accurately reflects the output of generateMetaReportWithLLM()
in cmd/acb-index-builder/blog.go, which adds these LLM-generated sections
to the template-based report.

Closes: bf-39q2
2026-05-26 13:38:22 -04:00
jedarden
7311952416 feat(web): add placeholder prediction data files
Add web/public/data/predictions/ directory with demo JSON files:
- leaderboard.json: demo predictor leaderboard entries
- open.json: demo open matches for predictions

These placeholder files follow the same pattern as other demo data
(e.g., bots/index.json, leaderboard.json) for local development
and consistency. The index builder generates the real versions at
runtime via generatePredictionsIndex() and generatePredictionsOpen().

Closes: bf-4w95
2026-05-26 13:36:32 -04:00
jedarden
c9503d2588 feat(web): add archetypes.json placeholder in data/meta directory
- Add static archetypes.json file alongside rivalries.json
- Placeholder follows the structure generated by index builder
- Closes: bf-5qmu
2026-05-26 13:05:07 -04:00
jedarden
d27aafc532 feat(web): add /docs/replay-format and /docs/data documentation pages
Implements plan §15.2 public match data documentation:

- /docs/replay-format: Complete replay schema v1 specification
  with field-by-field documentation, event types, win probability
  and critical moments format, example replays, and changelog

- /docs/data: Comprehensive guide to all public data endpoints
  including leaderboard, bots, matches, series, seasons, playlists,
  meta, evolution, blog posts, and maps with update frequencies
  and curl examples

- Added lazy loaders and routes for both pages in app.ts

Closes: bf-ckcj

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 13:01:57 -04:00
jedarden
d21c621485 test(web): add Director Mode unit tests per §16.10
Add comprehensive test coverage for the Director Mode (Adaptive Auto-Speed
Playback) implementation, verifying the action density formula and speed
mapping match the plan specification exactly.

Tests cover:
- Action density calculation (deaths×3.0 + captures×5.0 + energy×0.5 + spawns×1.0 + delta_win_prob×10.0)
- Speed mapping (0→16x, 0.1-1.0→8x, 1.0-3.0→4x, 3.0-5.0→2x, 5.0+→1x)
- Speed schedule computation with target duration scaling
- Win probability delta calculation

All 16 tests pass, confirming the Director Mode implementation in
director.ts correctly implements §16.10 of the plan.

Closes: bf-1p5y

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 23:16:42 -04:00
jedarden
82bcb17ab8 feat(web): update demo replays to reflect current combat mechanics
Regenerated demo-replay-v2.json (2-player) and demo-replay-v2-6p.json (6-player)
using acb-local with current engine settings. Both replays now show combat_death
events, demonstrating the focus-fire combat system is working.

- 2-player demo: 6 turns, 2 combat_deaths (seed 789)
- 6-player demo: 6 turns, 6 combat_deaths (seed 456)

Previous replays were generated before zone/spawn radius fixes and showed minimal
combat. Combat density tests confirm targets met: 86% for 2-player, 100% for
6-player (TestCombatDensityMetrics).

Closes: bf-3er8
2026-05-25 21:10:55 -04:00
jedarden
846a976f1c fix(web): increase replay.test.ts timeout for first module load
The first test in replay.test.ts was failing due to a race condition:
the initial module import took longer than the 100ms timeout, causing
urlInput to be null. Subsequent tests passed because the module was
cached. Increased timeout to 500ms to ensure the first module load
completes before checking for DOM elements.

All tests now pass (5/5).
2026-05-25 20:08:45 -04:00
jedarden
b6000fd889 feat(index-builder): add map library deployment per plan §3.8
- Add generate-maps-index.go script to generate maps/index.json and maps/{map_id}.json from maps/ directory
- Update docs-api.ts with /data/maps/index.json and /data/maps/{map_id}.json endpoints
- Generate 200 maps (50 per player count: 2, 3, 4, 6) in web/dist/data/maps/
- Maps include full geometry: walls, cores, energy nodes
- Index builder's generateMapsIndex() function already integrated (line 169 of generator.go)

Acceptance criteria met:
1. Maps directory exists with 200 maps (50 per player count)
2. generateMapsIndex() generates maps/index.json and maps/{map_id}.json in outputDir
3. web/dist/data/maps/ appears after npm run build (201 files: 1 index + 200 map details)
4. Maps endpoints documented at /docs/api

Closes: bf-2xjg

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 18:57:43 -04:00
jedarden
1839f5e7d1 style: format Go files with gofmt
- cmd/acb-maps-loader/main.go
- wasm/bots/guardian/main.go
- wasm/bots/hunter/main.go
- wasm/bots/random/main.go
- web/package-lock.json (npm ci update)
2026-05-25 15:33:38 -04:00
jedarden
223bfa3d86 feat(web): handle zone_death events in replay viewer
Per plan §3.7.1, the shrinking zone kills bots outside the safe radius.
The engine emits zone_death events (commit f0a0673), but the web viewer
only handled bot_died events, so zone kills weren't visualized correctly.

Changes:
- Add zone_death event collection in drawCombatEffects()
- Visual distinction: yellow-amber lightning bolt marker vs red X for combat
- Zone death animation: fast yellow particles + shockwave
- Screen reader transcript: "Bot X killed by zone"
- Separate summarizeZoneDeaths() for detailed transcripts

Closes: bf-4i44
2026-05-25 15:28:39 -04:00
jedarden
736b0f1bd1 feat(web): add individual rivalry page route (plan §13.5)
Adds /rivalry/:bot_a/:bot_b route showing detailed head-to-head history:
- Win rates, draws, match count
- Recent matches list
- Longest streak highlight
- Narrative description
- Links to bot profiles and replays

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 08:24:33 -04:00
jedarden
55b259c918 feat(web): add embeddable replay widget route (plan §13.4)
Adds /embed/:id route for iframe-embeddable replay viewer with:
- Minimal chrome (controls visible on hover)
- Auto-play on load
- Query params: start, speed, mode
- ~2.7KB gzipped embed chunk

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 08:21:44 -04:00
jedarden
18c8400a93 feat(web): implement accessibility.ts per plan §15.3
Implements the accessibility suite with:
- Color-blind safe palettes (default + Tol)
- Shape-per-player for redundant encoding
- Keyboard shortcuts system
- High contrast mode toggle
- Reduced motion detection
- Screen reader transcript generation
- ARIA live region helpers
- Focus management utilities
- Skip-to-content link

Closes: bf-38zz
2026-05-25 07:37:31 -04:00
jedarden
2480104214 feat(web): add rivalry platform integration (plan §13.5)
Adds Top Rivalries widget to landing page and Rivals section to bot
profiles, completing the platform integration for the automatic rivalry
detection system.

## Changes
- web/src/pages/home.ts: fetch rivalries data and render Top Rivalries card
- web/src/pages/bot-profile.ts: add Rivals section filtered to this bot
- web/src/styles/components.css: add rivalry list/item styles

## Plan §13.5 Platform Integration
 Rivalry widget on landing page with head-to-head records
 Bot profile pages show Rivals section with filtered rivalries
 Narratives already implemented via buildRivalryNarrative()

Closes: bf-2quf
2026-05-25 07:21:27 -04:00
jedarden
44f6e5c1ec fix(web): add matchMedia mock to prevent unhandled test errors
The window.matchMedia API (used for accessibility features) was not
mocked in tests, causing unhandled rejections when replay.ts tried to
check prefers-reduced-motion. Added the mock to both test-setup.ts
and the beforeEach hook in replay.test.ts to ensure it's available
before modules load.

Closes: bf-5cwi

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 00:18:32 -04:00
jedarden
41193b25a4 fix(web): distinguish 404 from generic errors in replay viewer
Updates showLoadError to display a friendly "not available yet" message
for HTTP 404 errors (common for unuploaded replays) vs generic "could
not load" for other HTTP errors. Adds the URL to error output and
maintains HTML escaping.

Also adds vitest testing infrastructure with 5 tests covering:
- 404 not-found message
- Generic HTTP error message
- Parse error handling
- HTML escaping (XSS protection)
- 404 vs error distinction

Closes: bf-5cwi
2026-05-24 23:04:58 -04:00
jedarden
46915d1437 feat(web): add combat_deaths to MatchResult and display in sandbox
- Add combat_deaths field to MatchResult interface (types.ts, engine.ts)
- Display combat kills count in sandbox match result panel
- Combat deaths were already tracked in engine (CombatDeaths []int)
  but not exposed in the web UI types or displayed to users

Closes: bf-2wjo
2026-05-24 18:42:38 -04:00
jedarden
da5e3f1479 feat(web): regenerate demo replays with combat_death events
Regenerated demo replays using the latest combat density and zone mechanics.
Previous replays (May 10) had 0 combat_death events and didn't reflect the
current state where zone starts early (turn 15-20) and forces combat.

New replays:
- demo-replay-v2.json: 2-player (random vs idle), 15 turns, 2 combat_death events
- demo-replay-v2-6p.json: 6-player (swarm,hunter,gatherer,rusher,guardian,random), 41 turns, 8 combat_death events

Verification:
- grep for combat_death events: > 0 in both replays
- Replays show focus-fire combat working as intended

Closes: bf-a918

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 17:45:26 -04:00
jedarden
b7817ea073 fix(wasm): rebuild engine and bots with latest combat density zone changes
- WASM engine now includes zone mechanics (ZoneStartTurn, ZoneMinRadius, etc.)
- Bot WASM modules rebuilt with latest strategy code
- Browser sandbox now matches production Go engine behavior
- Engine size increased 5.3MB→5.6MB due to zone code

Closes: bf-5uyv
2026-05-24 16:50:35 -04:00
jedarden
16fce127ff feat(viewer): render shrinking play-zone in replay viewer
Draws the active zone boundary and out-of-zone danger area using
per-turn zone_bounds data from the replay. The zone renders as:
- Red semi-transparent overlay outside the safe zone
- Solid red boundary circle with dashed inner ring
- Center cross marker
- Inactive zones show as subtle dashed outline

Changes:
- Add ZoneBounds type to types.ts
- Add zone_bounds field to ReplayTurn
- Implement drawZone() method in replay-viewer.ts
- Call drawZone() in renderStandardView()
- Update replay-schema-v1.json with ZoneBounds definition

Accepts: bf-k1oy

Closes: bf-k1oy
2026-05-24 10:24:44 -04:00
jedarden
323c1e8241 Viewer: directed attack arrows from killers[] in combat_death events (§16.9)
- Updated CombatDeathDetails type to include killers[] array with each killer's bot_id, owner, and position
- Modified drawCombatEffects() to handle combat_death events by drawing solid arrows from each killer to the victim
- Added drawArrow() helper method to draw arrows with arrowheads
- Maintained backward compatibility: old replays without combat_death events use proximity-inference lines

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 14:51:32 -04:00
jedarden
676cd8bc64 Bug fix bf-1unj: Fix match card participant names cut off on mobile
- Add display: flex and align-items: center to .participant on mobile
- Change .participant-name from flex-shrink: 1 to flex: 1 for proper space allocation
- Add margin-left spacing to .participant-score and .winner-badge
- Ensures all participant info (name, score, winner badge) is visible on mobile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:34:19 -04:00
jedarden
3a5244fbae Bug fix bf-1unj: Fix match card participant names cut off on mobile
- Change .match-participants to column layout on mobile for better width distribution
- Allow .participant elements to shrink (flex-shrink: 1)
- Remove max-width constraint from .participant-name
- Ensures participant names, scores, and Winner badge are all visible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:33:08 -04:00
jedarden
60531c66f2 Bug fix bf-2vkw: Fix replay viewer blank when navigating to /watch/replay/:id directly
When navigating to /watch/replay/:id (e.g., from the "Closest Match" link in rivalries), params.url was undefined, causing the viewer to show "Enter a replay URL to load" message. Fixed by constructing the URL from params.id when params.url is not set, using the pattern /r2/replays/MATCHID.json.gz.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:26:33 -04:00
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