Commit graph

64 commits

Author SHA1 Message Date
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
7b9ac5dd18 Revert "fix(index-builder): promote recent replays from B2 to R2 warm cache"
This reverts commit 3bd6ed45f9.
2026-05-26 16:33:19 -04:00
jedarden
0b83116689 style(index-builder): normalize EvolutionMeta struct field alignment
Align struct tags for consistency with other structs in the file.
No functional change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 14:41:19 -04:00
jedarden
04b860a8be feat(index-builder, evolver): improve evolution system initialization and logging
Index builder:
- Add slog import for structured logging
- Improve fetchEvolutionMeta to return empty meta instead of error when programs table is empty
- Add logging to show evolution system status (running vs not initialized)
- Add logging in generateEvolutionMeta to show when evolution data is written

Evolver:
- Add automatic schema initialization and population seeding in RunEvolutionLoop
- Programs table is now automatically seeded with 6 initial strategy bots on startup
- Log seeding status to indicate whether programs table was already initialized

These changes ensure the evolution system properly initializes when deployed
and provides better visibility into its status via structured logging.

Closes: bf-4zde
2026-05-26 13:28:44 -04:00
jedarden
6fbe221fe7 feat(index-builder): enhance evolution/meta.json with live observatory data
- Add island_populations: program count per island
- Add best_ratings: top 10 evolved bots with bot_id, name, rating, island, language
- Add total_promoted and promotion_rate: all-time promotion statistics
- Queries programs table and joins with bots table for current ratings

Closes: bf-1cxv
2026-05-26 13:15:39 -04:00
jedarden
c37f68e08b feat(index-builder): generate data/meta/index.json
Adds MetaIndex struct and generateMetaIndex function to create
data/meta/index.json listing all available meta data files
(archetypes.json, rivalries.json) with descriptions.

Also adds the new file to the R2 warm cache upload list.

Closes: bf-66rk

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 13:08:17 -04:00
jedarden
d0df7f2fab docs(plan): update ZoneShrinkStep from 2 to 1 to match implementation
The plan previously specified ZoneShrinkStep=2, but the engine uses
ZoneShrinkStep=1 (per commit 0577fcd). The value of 1 was found to
improve combat density because it matches bot movement speed (1 tile/turn).
A value of 2 caused the zone to shrink faster than bots could move,
killing them before combat could occur.

Updated zone parameters table and rationale in §3.7.1.

Closes: bf-3mrj

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 08:22:27 -04:00
jedarden
3bd6ed45f9 fix(index-builder): promote recent replays from B2 to R2 warm cache
Modified bundleWarmAssetsForCycle to return the deduplicated match IDs
of warm-set replays, and added a call to promoteRecentReplays in the
build cycle. This implements plan §8.2.5: index builder promotes recent
replays from B2 cold archive to R2 warm cache.

Acceptance criteria for plan-gap bead bf-jfmo:
- /r2/replays/{id}.json.gz will return valid gzipped replay JSON once
  the Cloudflare Pages R2 bucket binding (ACB_BUCKET) is configured
- R2 bucket exists with replay objects after each build cycle
- Index builder has R2 S3 client configured (credentials via env vars)

Closes: bf-jfmo
2026-05-26 08:15:02 -04:00
jedarden
4b7d81db45 feat(index-builder): bundle warm-set replays as static Pages assets
Per bead bf-3e60: instead of copying B2->R2 (warm cache), bundle warm-set
replays, thumbnails, cards, and evolution live.json directly into the Pages
deploy directory as static assets (dist/data/). This serves replays same-origin,
eliminating R2 dependency and 404 errors.

Changes:
- Add B2Client interface for testable B2 operations
- Add bundleWarmReplays(): copies replays/*.json.gz from B2 to dist/data/replays/
- Add bundleWarmThumbnails(): copies thumbnails/*.png from B2 to dist/data/thumbnails/
- Add bundleWarmCards(): copies cards/*.png from B2 to dist/data/cards/
- Add bundleEvolutionLive(): copies evolution/live.json from B2 to dist/data/evolution/
- Replace promoteRecentReplaysForCycle() with bundleWarmAssetsForCycle()
- Remove R2 pruning logic from main loop (no longer needed)
- Add unit tests for all bundling functions with mock B2 client

Replays are served gzipped (as-is from B2) to keep deploy size under Pages'
25MB file limit. Frontend will gunzip client-side (separate bead bf-5cwi).

All tests pass (go test ./...).

Closes: bf-3e60
2026-05-26 07:35:48 -04:00
jedarden
b31ebee32f feat(index-builder): generate evolution/meta.json and lineage.json
Plan §15.4 Live Evolution Observatory requires data/evolution/meta.json
and data/evolution/lineage.json files, which the web platform expects
but the index-builder was not generating.

- Add EvolutionMeta type (generation, promoted_today, top_10_count, updated_at)
- Add LineageNode type (full program lineage with parent relationships)
- Add fetchEvolutionMeta() to query evolver database for stats
- Add fetchLineage() to query evolver programs table for lineage tree
- Add generateEvolutionMeta() to write data/evolution/meta.json
- Add generateLineage() to write data/evolution/lineage.json
- Wire generation into generateAllIndexes()
- Add files to R2 upload list

The implementation gracefully handles missing evolver database by
returning empty/placeholder data, allowing the index-builder to run
without evolution data while still producing valid JSON files.

Closes: bf-6cp0
2026-05-25 16:01:27 -04:00
jedarden
ea04f4debb style: apply gofmt alignment fixes across codebase
Tab/space alignment consistency from running gofmt on all packages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:40:33 -04:00
jedarden
9bcbd566b6 Bug fix bf-3mx7: Fix match index winner badge - use p.Won instead of comparing BotID with WinnerID
The WinnerID field is a player-slot integer as string (e.g. "2"), not a bot_id.
The SQL query already computes the correct winner status in p.Won field.

Fixed in 3 functions:
- matchToSummary: Changed Won: p.BotID == m.WinnerID to Won: p.Won
- buildPlaylistMatch: Changed Won: p.BotID == m.WinnerID to Won: p.Won
- ratingUpsetMagnitude: Use p.Won to identify winner instead of comparing with m.WinnerID
- maxScoreDiff: Use p.Won to identify winner instead of comparing with m.WinnerID
- isEvolutionBreakthrough: Find winner using p.Won before checking if evolved

This fixes the issue where 984/1000 prod matches had winner_id set but all participants showed won: false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:59:02 -04:00
jedarden
b27272de5a Phase 10: Fix narrative engine tests to match exact specs
- Fix TestBuildNarrativePrompt_Comeback to check for current ELO
  instead of old rating (comeback arc shows bottom 25%→top 25%)
- Fix TestDetectRivalryArcs to use 10+ matches (grudge match spec)
  instead of only 5 matches

Story arc detection (per §3.7 chronicles):
✓ Comeback bots: recovered from bottom 25% to top 25%
✓ Grudge matches: same pair meets 10+ times
✓ Underdog victories: bottom-10 beats top-10

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 15:10:42 -04:00
jedarden
0e6a4977b6 Phase 10: §3.7 chronicles - Update story arc detection to exact specs
- detectComebackArcs: Now detects bottom 25% → top 25% climb (was peak→trough→recovery)
- detectRivalryArcs: Now detects 10+ total meetings grudge matches (was 5+ recent matches)
- detectUpsetArcs: Now detects bottom-10 beats top-10 (was biggest ELO gap)
- Updated narrative prompts to reflect new comeback arc specification
- Updated weekly chronicles prompt for comeback arcs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 15:07:23 -04:00
jedarden
0028100c64 Phase 9: Map voting + positional fairness monitoring - verify existing implementation
Verified all Phase 9 deliverables already in place:
- PostgreSQL map_votes table (UNIQUE constraint on map_id, voter_id)
- POST /api/vote-map endpoint (+1/-1 votes, rate-limited)
- GET /api/vote/map/{map_id} endpoint for vote counts
- Positional fairness monitoring: tickFairnessAudit with 5-step lifecycle
  * updateMapFairnessStats: recompute per-slot win rates
  * flagUnfairMaps: probation for >10pp deviation
  * retireDislikedMaps: force-retire at < -20 net votes
  * pruneLowEngagementMaps: monthly bottom 10% pruning
  * promoteClassicMaps: top-5 sustained (3+ months) to classic
- maps/index.json includes NetVotes from aggregation

All tests pass (mapvote, map_fairness, index-builder).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 14:30:46 -04:00
jedarden
8e33ee1f27 fix(index-builder): correct function name typo in weekly chronicles generation 2026-05-04 03:10:46 -04:00
jedarden
c88474ad6b feat(index-builder): update community hints filter for insight/idea types
- Change filter from 'idea'/'mistake' to 'insight'/'idea' (mapping to 'hint'/'strategy' from plan §13.6)
- Increase upvote threshold from 3 to 10 for higher quality signals
- The evolver consumes community_hints.json for LLM prompt context
2026-05-04 01:38:53 -04:00
jedarden
aeef954590 feat(index-builder): add sitemap.xml generation
Add sitemap.xml generation as a final pass in the index builder. The
sitemap covers all public pages: home, leaderboard, bots list, bot
profiles, matches list, featured replays, seasons, rivalries,
predictions, and docs.

- Add SiteURL config field (ACB_SITE_URL env var, defaults to
  https://aicodebattle.com)
- Add generateSitemap() function with proper XML encoding
- Add SitemapURL and Sitemap types for XML marshaling
- Call generateSitemap() at the end of generateAllIndexes()
- Write sitemap.xml to output directory alongside leaderboard.json

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 00:09:53 -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
c397e66239 fix(index-builder): run wrangler from /app/web to pick up functions/ bundle
_worker.js static file approach fails — Cloudflare rejects it when uploaded
as a static asset. Instead, copy web/functions/ into the image and set
wrangler CWD to /app/web/ so it discovers functions/ and uploads the Pages
Functions bundle correctly on every deploy cycle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 12:39:57 -04:00
jedarden
037a04e8b5 fix(index-builder): bundle Pages Functions into _worker.js at build time
Every index-builder deploy was overwriting the production Pages deployment
without functions (wrangler ran from /tmp, no functions/ dir visible).
Compiling functions/ to dist/_worker.js during the Docker web-builder stage
means the worker is always included in every Pages deploy, regardless of CWD.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 12:35:54 -04:00
jedarden
626a173e17 test(index-builder): fix buildRivalryNarrative call signature
Added missing aID and bID arguments to match the function
signature in generator.go.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 23:40:57 -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
09fced7dfe fix(worker,index-builder): use us-east-1 region for S3-compatible endpoints
The AWS SDK requires a valid AWS region name even when using custom
S3-compatible endpoints (ARMOR/B2). Using "auto" as the region causes
an error: "Invalid region: region was not a valid DNS name."

This fixes the replay upload pipeline which was failing with the
invalid region error. Replays should now upload successfully to B2
via the ARMOR proxy.

Related to ai-code-battle-o43: Replay viewer verification task.
2026-04-25 11:07:08 -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
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
5d89fbd885 fix(index-builder): add _redirects to route / → app.html
The replay viewer was baked into index.html (served at /) while the
leaderboard app was at /app.html. Add a _redirects file so visitors
landing on / get redirected to the main leaderboard app.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 07:22:06 -04:00
jedarden
be66af1ad5 fix(narrative): add season championship context and §13.2 critical moments to spotlight prompt
Add formatSeasonChampionshipContext helper to inject season progress,
championship bracket positioning, and seed lines into LLM prompts.
Add §13.2 critical moment / turning point summary to the match-of-the-week
section of buildSpotlightPrompt. These complete the §15.1/§15.5 alignment
for structured contextual match data in sports-journalism-style prompts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 20:08:49 -04:00
jedarden
922ef5cd5a fix(narrative): align LLM prompts with plan §15.1/§15.5 sports-journalism spec
Restore detailed system prompt constant framing the LLM as a sports
journalist covering an emergent bot league, with specific guidance on
ELO deltas, rivalry context, head-to-head records, and scouting-style
lineage framing. Enrich per-arc prompts with critical moment summaries
(§13.2), community tactical hints, ELO before/after deltas, and
head-to-head records. Fix rivalry arc to include ELO context for both
bots. Ensure fall arc shows both wins and losses in key match listings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 19:40:49 -04:00
jedarden
ae8eb0465e feat(narrative): add critical moment summaries to key match extraction
Generates contextual turning-point descriptions for matches used in blog
narratives and rivalry chronicles (§13.2). Summarizes close scores, ELO
upsets, non-standard end conditions, and marathon matches.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 19:08:50 -04:00
jedarden
fddcc0ba34 fix(index-builder): add missing getCurrentSeasonTheme and buildHeadToHeadFromArc
Two functions referenced in generateLLMChronicle were undefined:
- getCurrentSeasonTheme: returns the active season's theme string
- buildHeadToHeadFromArc: computes W/L head-to-head records for a bot
  against all opponents from match data, enriching LLM narrative prompts

Also improves the sports journalist system prompt with more detailed
coverage style guidance for better narrative quality.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 19:01:03 -04:00
jedarden
7978ebbab3 feat(§15.2): generate and stream static meta JSON files to R2
- Add data/meta/rivalries.json to R2 upload list in uploadMetaJSONToR2
- Add attachCommunityHints() to narrative.go to enrich story arcs with
  highest-upvote community tactical hints (upvotes >= 3, idea/mistake types)
- Fix detectRivalryArcs() key separator from "-" to "|" to avoid UUID
  hyphen collisions when parsing bot ID pairs
- Fix partitionBots() call sites in bot_strategies_phase13.go to use
  struct field access (.friendly, .enemy) matching updated return type

generator.go already contains generateArchetypes, generateCommunityHints,
and generateMatchFeedback (all called from generateAllIndexes). main.go
uploads all four outputs to R2 on every build cycle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 18:46:27 -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
4d1f5f976d feat(index): implement §14.5 predictions/open.json static file generation
Adds generatePredictionsOpen to acb-index-builder, writing:
- data/predictions/open.json — upcoming predictable matches

Queries pending matches that are "predictable":
- Both bots in top 20
- Rivalry matches (3+ previous h2h matches)
- Series matches
- Evolved bot vs top-10 human-written bot

Output schema:
- updated_at — timestamp of generation
- matches — array of {match_id, bot_a, bot_b, a_rating, b_rating, open_until, head_to_head_record}

Limits to next 10 matches to keep file small. Refreshes every ~15 min by the index builder.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 17:53:48 -04:00
jedarden
32d7dd07e7 feat(index-builder): merge latest site build artifact before Pages deploy (§8.4, §11.3)
Add crane CLI to the runtime Dockerfile so the index builder can pull the
latest SPA shell from the Forgejo container registry on each cycle. The
existing syncSiteBuild logic checks for a newer image digest, extracts
the dist/ assets via crane export, and overlays generated JSON data files
on top before deploying to Cloudflare Pages.

- Dockerfile: install go-containerregistry crane binary (v0.20.2)
- sitebuild.go: new file with syncSiteBuild, craneDigest, craneExport,
  digest caching, fallback to baked-in /app/web/dist
- main.go: wire initCraneAuth at startup, replace hardcoded webDistDir
  with syncSiteBuild call in runBuildCycle
- sitebuild_test.go: 18 tests for extractRegistry, digest caching,
  fallback logic, crane auth config, and copyWebAssets overlay behavior

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 17:52:03 -04:00
jedarden
b4a975f5bf feat(index): implement §7.2/§15.2 maps/index.json and maps/{map_id}.json generation
Adds generateMapsIndex to acb-index-builder, writing:
- maps/index.json — active/probation/classic maps grouped by player count
- maps/{map_id}.json — full map definition with walls, cores, energy_nodes

Queries maps.map_json column for geometry; adds RawJSON field to MapData
and updates fetchMaps to select it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 17:46:27 -04:00
jedarden
89560e5ec4 feat(index): implement match thumbnail PNG generation (§7.2, §14.3)
Implementation complete:

1. engine/thumbnail.go - New thumbnail rendering package
   - GenerateMatchThumbnail() creates 640x360 PNG thumbnails
   - Renders grid, bots, cores, walls, energy with player colors
   - SelectThumbnailTurn() chooses most interesting turn
   - Pure Go stdlib image rendering (no canvas required)

2. cmd/acb-worker - Upload thumbnails to B2 alongside replays
   - uploadThumbnail() generates PNG and uploads to B2
   - Key: thumbnails/{match_id}.png, content-type: image/png
   - Called after match completion, non-blocking on failure

3. cmd/acb-index-builder/deploy.go - Promote thumbnails to R2
   - promoteRecentReplays() copies both replays AND thumbnails from B2 to R2
   - Thumbnails promoted to warm cache alongside replay promotion

4. cmd/acb-index-builder/generator.go - Populate thumbnail URLs
   - buildPlaylistMatch() now includes thumbnail_url field
   - URL pattern: https://r2.aicodebattle.com/thumbnails/{match_id}.png
   - Enables playlist cards and embed OG tags to show preview images

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 17:42:36 -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
7f2407ed00 feat: add Prometheus metrics instrumentation across services
Add metrics server startup and HTTP middleware to acb-api, generation
counter metric to evolver, and R2 cache size metric to index builder.

Also remove dead measureR2CacheSize reference from index builder main.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 16:16:03 -04:00
jedarden
fff795ba6c fix(index-builder): improve rivalry recency test isolation + add metrics
The RecencyBoost test now uses balanced 5-5 splits for both pairs so
recency is the sole differentiating factor (previously one pair was 10-0
which conflated balance and recency). Also wires Prometheus build
duration metric in main loop.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 16:07:18 -04:00
jedarden
80334c6e34 feat(evolver): expand MAP-Elites from 2-D to 4-D grid per §10.2
- Add Exploration and Formation axis definitions with feature extraction
  from source code pattern matching (exploration/formation indicators)
- Extend Grid key from (x,y) to (x,y,z,w) with 3⁴=81-cell behavior grid
- Update bin assignment, promotion gate, and persistence (JSON snapshot)
- Add Slice() for 2-D dashboard visualization across any axis pair
- Migration: old 2-D archives project at z=middle, w=middle
- Update cross-pollination to pad 2-element behavior vectors to 4
- Add Prometheus metrics to matchmaker (bot crashes, stale job count)
- Add rivalry detection to index builder (data/meta/rivalries.json)
- Web: batched bot list loading, leaderboard keyboard accessibility,
  improved ARIA attributes on match/playlist cards

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 15:44:39 -04:00
jedarden
80af92b022 feat(replay): add debug telemetry panel and fix test build
Add debug telemetry UI to replay viewer with player toggles,
priority-based target markers, and stacked reasoning boxes.
Fix undefined generateTestImage in main_test.go.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 16:57:15 -04:00
jedarden
80040fa501 test(index-builder): add tests for fast playlist lookup helpers
Covers buildFirstMatchPerBot, isNewBotDebutFast, buildPairFrequency,
isRivalryMatchFast, and integration test for playlist generation with
the optimized lookups.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 16:48:20 -04:00
jedarden
be3843d9ac fix(index-builder): correct series/season exempt queries, optimize playlist curation
- Fix deploy.go to query actual table names (series_games not series_matches,
  join through series_games for seasons instead of non-existent season_matches)
- Add playlist_matches table to exempt match IDs from R2 pruning
- Pre-build lookup maps for O(1) playlist match filtering instead of O(n²)
- Enhance home page featured replay to prefer AI-enriched matches
- Add enrichment test coverage (shouldEnrich criteria validation)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 16:47:13 -04:00
jedarden
b1121ed6f8 feat(playlists): add playlist curation and rebuild logic per §10, with series/seasons/enrichment
Implement auto-curated playlists in the index builder: 12 playlist types
(closest finishes, upsets, comebacks, marathons, rivalry classics, etc.)
with weekly highlight curation. Add DB persistence, R2 pruning exemptions,
frontend pages, and AI commentary enrichment pipeline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 16:11:27 -04:00
jedarden
70b7337867 feat(blog): add rivalry context to spotlight prompt and fix narrative tests
The buildSpotlightPrompt function accepted a rivalries parameter but never
used it. This adds top rivalry data to the LLM prompt so the generated
Counter-Strategy Spotlight can reference active rivalries. Test updated to
verify rivalry data appears in prompt output.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 15:45:33 -04:00