The executeZone function was killing bots outside the zone radius
before the zone became active (ZoneStartTurn). This caused bots to
die at turn 49 when the zone shouldn't start until turn 50.
Added early return when ZoneActive is false, so zone killing only
occurs after the zone has activated.
Set ZoneEnabled: true in DefaultConfig() to activate the forcing
function that compresses bots into contact. Test matches now show
combat_death events (focus-fire kills) where previously all matches
had 0 combat deaths.
- Zone starts at turn 50, shrinks every 5 turns by 2 tiles
- Bots outside the safe zone die each turn
- Combined with 40x40 maps and center-weighted energy, forces
bots to fight over central territory
Test results:
- Before: 0 combat_death events across all replays
- After: 2-8 combat_death events per match (swarm/hunter matchup)
Closes: bf-2up4
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Implement center-weighted energy distribution as a forcing function
to pull players into contested midfield, increasing combat density.
Changes:
- engine/match.go: Update placeEnergyNodes to use tiered radius
distribution (30% central 0.05-0.20, 40% mid 0.20-0.40, 30% outer
0.40-0.60) instead of uniform 0.3-0.7
- engine/integration_test.go: Add TestIntegration_CenterWeightedEnergy
to verify ~25% of energy nodes spawn in central zone
- cmd/acb-mapgen: Already had tiered distribution (unchanged, just
comments updated)
- cmd/acb-mapgen/mapgen_test.go: Add TestGenerateMap_CenterWeightedEnergy
This uses the existing economic incentive (energy collection) as a
forcing function without changing combat resolution or scoring.
Closes: bf-648i
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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
- Fix computeCombatTurns to count EventCombatDeath events instead of
EventBotDied with reason="combat" (which was never emitted, causing
CombatTurns to always be 0)
- Add CombatDeaths field to MapEngagementScore to track focus-fire kills
- Update engagement formula to weight combat deaths at 3.0 (same as
win_prob_crossings) to bias map evolution toward combat-dense maps
- Add countCombatDeaths helper function to count EventCombatDeath events
- Update log output to include combat_deaths metric
This implements bf-4nxs: the combat-density metric is now measured and
weighted in map engagement, which gates map curation/selection. Maps
with zero combat will have low engagement scores and be filtered out.
Closes: bf-4nxs
Reduce default 2-player map size from 60x60 to 40x40 (from 3600 to 1600
tiles) to increase encounter frequency and combat density. Add -skirmish
flag to acb-mapgen for generating even smaller dense maps (32x32, 0.20
wall density, 15 energy nodes) with "skirmish_" ID prefix.
Changes:
- engine/types.go: DefaultConfig() returns 40x40, ConfigForPlayers()
uses 800 tiles/player for 2-player (40x40) and 2000 tiles/player for
3+ players
- cmd/acb-matchmaker/tickers.go: gridForPlayers() returns 40x40 for 2
players
- cmd/acb-map-evolver/main.go: gridForPlayers() returns 40x40 for 2
players
- cmd/acb-mapgen/main.go: defaults to 40x40, adds -skirmish flag for
32x32 high-density maps
- cmd/acb-matchmaker/tickers_test.go: update test expectations for new
40x40 default
Closes: bf-39wt
Add configurable active zone that contracts toward map center on an interval,
forcing bots together to trigger focus-fire engagements. Bots outside the zone
die with reason "zone", and zone bounds are recorded in replay turns.
Config fields:
- ZoneEnabled: enable/disable the zone
- ZoneStartTurn: turn when zone starts shrinking (default 50)
- ZoneShrinkInterval: turns between shrink steps (default 5)
- ZoneShrinkStep: tiles to shrink each step (default 2)
- ZoneMinRadius: minimum zone radius (default 10)
Turn sequence update: MOVE → COMBAT → ZONE → CAPTURE → COLLECT → SPAWN → ENERGY_TICK
Zone phase inserted after COMBAT, uses toroidal distance calculation,
and emits bot_died events with reason "zone" for killed bots.
Replay update: ZoneBounds struct added to ReplayTurn to record center,
radius, and active state per turn for visualization.
Determinism verified: all tests pass, engine remains deterministic.
Closes: bf-2g96
The migration file migrations/0001_initial.sql was created in commit 31678be
and contains the complete schema matching cmd/acb-api/db.go.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Bead-Id: bf-67io
The migration file migrations/0001_initial.sql was created in commit 31678be
and contains the complete schema matching cmd/acb-api/db.go.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Verified that the mobile bottom tab bar specified in §16.4 is implemented
in web/index.html as inline styles, not in external CSS files.
The implementation matches specification:
- Four bottom tabs: Home, Watch, Compete, Board
- Persistent bottom tab bar on mobile (<640px)
- Proper z-index layering and responsive behavior
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The migration file migrations/0001_initial.sql was added in commit 31678be.
This bead was filed based on an outdated plan inventory.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add comprehensive file header to bot_strategies_phase13.go explaining:
- These are hand-coded strategy implementations, NOT LLM-evolved
- How they relate to the autonomous evolution system in plan §10
- Key distinction: Phase 13 = human-designed fixed archetypes,
Evolver = LLM-generated continuously evolving candidates
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Verified that migrations/0001_initial.sql contains all 20 tables from
cmd/acb-api/db.go schema. Work was completed prior to bead assignment.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Verified all required components are in place:
- CI workflow at starters/csharp/.github/workflows/build.yml (added in 7bf6566)
- Plan §5.8 coverage at line 986 (added in 55c594c)
- Directory listing at line 2565 (added in 9d4a311)
- Plan §11.2 template repos at line 2629 (added in 55c594c)
C# starter kit fully registered alongside 8 other language starter kits.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Re-verified that all required components are in place:
- CI workflow exists at starters/csharp/.github/workflows/build.yml
- Plan §5.8 includes acb-starter-csharp at line 986
- Plan §11.2 includes acb-starter-csharp at line 2629
- Directory listing includes csharp/ at line 2565
The task was originally completed in commits 55c594c and 9d4a311.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The POST /api/register endpoint sets status='active' for newly registered
bots, and the matchmaker queries for all active bots. Integration is complete;
no code changes required.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Verified that commit 7e8c9d7 completed all registration requirements:
- Added to §5.8 Starter Kit table
- Added to file structure section
- Added to published template repos table
- CI workflow already exists
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Complete the file structure section by including csharp/ alongside the other 7 starter kits. The C# starter kit is already fully documented in §5.8 and §11.2 tables.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The bead task indicated missing CI workflow and plan §5.8 coverage,
but verification found all components already in place from commit 55c594c.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add acb-starter-csharp to §5.8 starter kit table
- Add acb-starter-csharp to §11.2 template repos table
- Update evolution validation test suite language list
The C# starter kit exists at starters/csharp/ with full implementation
including ASP.NET Core minimal API, HMAC auth, grid utilities, tests,
Dockerfile, and CI workflow. It was simply missing from plan documentation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The JavaScript starter kit was officially registered via commit 7e8c9d7,
which added it to plan §5.8, the file structure section, and the published
template repos table. This notes file documents the completion status.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add `acb-starter-javascript` to §5.8 Starter Kit table
- Add javascript/ to file structure section
- Add `acb-starter-javascript` to published template repos table
The JavaScript starter already exists in starters/javascript/ with:
- Dockerfile, CI workflow, README, package.json
- Node.js built-in http module, zero dependencies
This registers it as an official starter kit alongside the other 6 languages.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add /opt to nsjail bindmounts so Rust toolchain (/opt/rust) is accessible
during sandboxed validation of Rust bots
- Explicitly enable Alpine community repository in Dockerfile to ensure
nsjail package can be installed (nsjail lives in community, not main)
- nsjail integration was already optional (falls back to plain exec if
unavailable), but these changes ensure it actually works when enabled
This addresses bead bf-3f29: nsjail was listed in apk add but /opt wasn't
bindmounted, causing Rust validation to fail when UseNsjail=true.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The evolver's validation pipeline supports Rust and Java bots, but the
container image was missing rustc and javac runtimes. Additionally, nsjail
was documented as part of the sandbox stage but not installed.
Changes:
- Add nsjail package (from Alpine community repo) for sandbox isolation
- Add openjdk-17-jdk for Java bot validation
- Install Rust toolchain (rustc) via rustup to /opt/rust for shared access
- Set PATH to include Rust binaries for the acb user
The validator already had graceful fallback when nsjail wasn't found in PATH,
but with nsjail installed, the sandbox stage now provides proper CPU/memory
resource limits during smoke testing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Verified complete implementation of §16.9 directed attack arrows feature:
- Engine emits combat_death events with killers array (bot_id, owner, position)
- TypeScript types properly define CombatDeathKiller and CombatDeathDetails
- Viewer draws solid arrows from each killer to victim with attacker player color
- Arrowheads point to victim tile center
- Backward compatibility maintained for old replays (proximity-inference fallback)
- Red explosion flash and X marker for death visualization
Related commits: 8e0aa5e (engine), 323c1e8 (viewer)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Verified that the feature implemented in commits 8e0aa5e (engine) and 323c1e8 (viewer) is complete and working:
Engine (engine/turn.go):
- executeCombat emits EventCombatDeath with killers[] array
- Each killer entry includes bot_id, owner, and position
Viewer (web/src/replay-viewer.ts):
- drawCombatEffects() handles combat_death events with killers[]
- Draws solid colored arrows from each killer to the victim
- Includes red explosion flash and X marker for death
- Maintains backward compatibility with old replays (proximity-inference fallback)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The feature was already fully implemented in commits 8e0aa5e and 323c1e8.
This commit documents the verification of that implementation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- 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>
Modified executeCombat to emit EventCombatDeath events with a killers
array containing all enemy bots within attack radius of the killed bot.
Each killer entry includes bot_id, owner, and position, matching the
replay schema specification.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Each player now starts with 2 cores and a bot at each, placing forces
mid-map from turn 1 and creating earlier conflict.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
- 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>
Investigated why all matches have enriched: false. Root cause is
corrupted R2 credentials in OpenBao that prevent the acb-enrichment
service from uploading AI commentary.
Key findings:
- R2 credentials at secret/rs-manager/ai-code-battle/r2 are corrupted
- endpoint/secret-key values are swapped
- Enrichment service cannot upload to R2
- Fix script exists but requires cluster access
Blocker: Expired kubeconfig (bf-5nap) prevents cluster access
and execution of the fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
The worker was only configured with B2 (ARMOR) credentials, so replays were
being uploaded to B2 but not to R2. The index builder's B2→R2 promotion
cycle only covers the last 24 hours, so older replays were not available
in R2, causing 404 errors when clicking "Watch Replay".
This fix adds R2 credentials to the worker deployment, allowing it to
upload replays directly to R2 (hot cache) in addition to B2 (cold archive).
This makes replays immediately available for viewing without waiting for
the promotion cycle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The bug fix was already applied in commit 9bcbd56. Updated notes to reflect:
- Fix is already in the codebase (using p.Won instead of p.BotID == m.WinnerID)
- Index builder will automatically regenerate static JSON on next 15m cycle
- No manual intervention required - production deployment will pick up the fix automatically
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The code fix for the winner badge bug has been applied. The index
builder needs to be run in an environment with Go installed to
regenerate static JSON files with correct winner badges.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The code fix was already applied in commit 6fe778b. This commit
adds documentation noting that the index builder needs to be run
to regenerate static JSON with correct winner badges.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
Matches stopped being created after 2026-05-09. The iad-acb kubeconfig on
ex44 has expired credentials, preventing cluster access for diagnosis.
Created comprehensive diagnostic documentation covering:
- Cluster architecture and components (matchmaker, workers)
- Step-by-step diagnostic procedures for kubectl access
- Pod status checks and log analysis commands
- Database verification queries
- Service restart procedures
- Known issues from prior incidents (R2 credentials corruption)
Next steps:
1. Renew iad-acb token from Rackspace Spot UI
2. Update kubeconfig on ex44
3. Execute diagnostic commands to identify root cause
4. Restart services as needed
5. Verify match creation resumes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>