Adds lightweight bot status endpoint that returns status and last_active.
Public endpoint for checking bot health without full profile fetch.
Closes: bf-5p43
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per plan §13.3, implements user-requested AI replay commentary with:
- HMAC bot authentication via shared_secret
- Rate limiting: 5 requests/day per bot
- Match validation (exists and completed)
- Idempotency via enrichment_requested_at column
- Enqueues to Valkey for acb-enrichment service
- Returns 202 Accepted with estimated wait time
Also adds:
- AllowN() method to ratelimit package for multi-token checks
- enrichment_requested_at column to matches table (idempotency)
- enrichLtr rate limiter (5/day per bot)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add 'featured' boolean column to series table for weekly featured series
- Add tickFeaturedSeries ticker that runs Friday 20:00 UTC to create bo5 featured series
- Featured series: query top 20 bots by rating, select 4 rivalry pairs by ELO proximity
- Best-of-7 championship bracket already implemented via createChampionshipBracket
- Add FeaturedSchedSecs config (default: 3600s check interval)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
The encryption key stored in OpenBao/K8s secrets is base64-encoded but
the API and worker crypto functions expected hex. Add parseAESKey() that
accepts both formats (tries hex first, falls back to base64).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The replay_feedback table was missing its foreign key constraint to
matches(match_id). This happened because CREATE TABLE IF NOT EXISTS
doesn't add FKs to existing tables.
Added an idempotent migration that checks for the constraint's existence
before adding it, ensuring it's safe to run on both fresh installs and
existing databases.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The metrics package is a local module dependency imported by all services
but was missing from every Dockerfile's build context.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add NashMixture and MetaWeaknesses fields to meta.Description and
compute them from island population proportions (§10.2 PSRO)
- Update behaviorDistance to support N-D vectors for 4-D MAP-Elites
grid (aggression, economy, exploration, formation)
- Wire NashMixture/MetaWeaknesses through FromMetaDescription converter
so they actually reach the LLM prompt (was dead code before)
- Align LLM prompt with plan §15.1/§15.5: correct combat rules
(focus-fire), fog of war, HTTP protocol section, Nash mixture target
- Fix diversity normalization from sqrt(2) (2-D) to 2.0 (4-D max)
- Rename handleUIFeedback to handleCreateFeedback (§13.6 naming)
- Update tests for new fields and corrected prompt text
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 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
- 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>
Add dedicated 10/hour-per-IP rate limiter for POST /api/feedback/{id}/upvote,
separate from the 20/hour feedback submission limiter. Wired in main.go init,
server_test.go helper, and RegisterRoutes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Adds ratelimit package with per-IP and per-key HTTP middleware.
Applied to register (5/hr), feedback (20/hr), predict (60/hr),
and job submission (5/day) endpoints. Includes metrics counter
for rejected requests and periodic bucket cleanup goroutine.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
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>
The getEnv() function in server.go always returned the default value,
preventing ACB_R2_ENDPOINT/ACB_B2_ENDPOINT from being read at runtime.
Also updated Dockerfile from golang:1.24 to golang:1.25 to match go.mod.
K8s manifests for acb-evolver and acb-api already exist in
declarative-config/k8s/iad-acb/ai-code-battle/ (added Apr 21).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The community feedback endpoint was registered as /api/ui-feedback in
the Go API but the plan and annotation.ts client both use /api/feedback.
Rename the route and update agentation-overlay.ts to match. Add a
route-level test asserting the canonical path and that the old path
returns 404.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
- Worker resolves open predictions after writing match results (resolvePredictions + upsertPredictorStats)
- API endpoints: POST /api/predict, GET /api/predictions/open, GET /api/predictions/history
- Frontend /watch/predictions page with polling, prediction submission, and history display
- predictor_stats table tracks streaks and accuracy per predictor
- Series format selection: fix threshold from >200 to >=200 for bo3 eligibility
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Evolution page: live polling (10s), activity feed, candidate tracking,
statistics section, island overview with live.json schema
- Series page: detailed series view with game-by-game results
- Seasons page: season list with status and champion display
- Predictions page: enhanced prediction UI with open matches
- API types: add CycleInfo, Candidate, ActivityEntry, Totals for live.json
- Embed: improved embeddable replay widget
- Mobile CSS: responsive breakpoints and bottom tab bar
- Exporter: enhanced live.json generation with full cycle/candidate data
- Matchmaker: series scheduling support with config
- Worker: additional database queries for series/season data
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- POST /api/register: bot registration with URL + shared secret validation
- GET /api/job: worker polls for next pending match job (authenticated)
- POST /api/job/:id/result: worker submits match result (winner, replay JSON)
- GET /api/replay/🆔 serve replay JSON from R2 warm cache (falls back to B2)
- GET /api/bot/🆔 bot profile JSON (rating, elo, record, metadata)
- GET /api/bots: leaderboard snapshot with pagination
- POST /api/ui-feedback: accept Agentation UI feedback
Authentication via Bearer token (worker API key). Shared secrets encrypted
with AES-256-GCM using ACB_ENCRYPTION_KEY.
Cleanup of superseded code that no longer matches the architecture:
Removed:
- worker-api/ - Cloudflare Worker with D1, superseded by K8s-based matchmaker + direct PostgreSQL
- cmd/acb-indexer/ - TypeScript index builder, superseded by Go cmd/acb-index-builder/
- cluster-configuration/ - K8s manifests belong in ardenone-cluster repo
Gutted cmd/acb-api/:
- Removed registration, job claim/result endpoints (deferred for v1)
- Removed dead code: predictions.go, seasons.go, series.go, register.go, jobs.go, glicko2.go
- API is now a stub with only health/ready endpoints
- Matchmaker and workers handle the core loop without it
Updated PROGRESS.md to reflect current architecture.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Architecture conformance fix per plan §12 Phase 4:
- Plan specifies Matchmaker Deployment as internal service with no external exposure
- Extracted tickers.go from acb-api to new cmd/acb-matchmaker/
- Tickers: bot pairing (1 min), health checking (15 min), stale job reaping (5 min)
- Alerting webhooks moved from acb-api to acb-matchmaker
- Created Dockerfile for acb-matchmaker container
- Created K8s deployment manifest (no service needed - internal only)
- Fixed syntax error in cmd/acb-api/db.go (prematurely closed schemaSQL string)
This separates concerns per the plan:
- acb-api: HTTP endpoints for bot registration, job coordination, bot status
- acb-matchmaker: Internal tickers for matchmaking, health checks, reaping
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the monitoring alerting deliverable from Phase 6. The Alerter
module sends color-coded notifications to Discord and/or Slack webhooks
for operational events: bot health transitions, stale job re-enqueues,
and match errors. Includes per-key rate limiting to prevent alert storms.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the K8s-native Go API service per the plan architecture:
- HTTP server with graceful shutdown and env-var configuration
- PostgreSQL schema (bots, matches, match_participants, jobs, rating_history)
- Health/ready endpoints checking PostgreSQL and Valkey connectivity
- Bot registration with health check, HMAC secret gen, AES-256-GCM encryption
- Key rotation and bot status endpoints
- Job claim via Valkey BRPOP, result submission with Glicko-2 rating update
- Glicko-2 rating system: multi-player pairwise, Illinois volatility algorithm
- Background tickers: matchmaker (1m), health checker (15m), stale job reaper (5m)
- Worker API key authentication (Bearer/X-API-Key)
- Dockerfile, K8s Deployment (2 replicas), ClusterIP Service
- 30 unit tests covering Glicko-2, crypto, config, and handlers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>