Wayfinder: Shared integration test framework for garages #126

Closed
opened 2026-08-30 10:21:57 +02:00 by SirStone · 3 comments
Owner

Problem Statement

Each bot garage in the monorepo develops and trains bots independently, but there's no way to run automated integration tests — actual battles against controlled opponents with assertions on outcomes. The only testing today is unit-level (assert-based test_*.nim on pure functions). Validating that a bot behaves correctly in a battle requires manually running the Java battle runner, eyeballing stdout, and mentally checking results. Every garage that wants integration tests would have to reinvent this orchestration from scratch.

Solution

A shared integration test framework in common_libs/test_framework/ that any garage can import to run battle simulation tests via nimble test. The framework wraps the existing Java battle runner, compiles bots on the fly, manages the server lifecycle, and returns structured BattleResult objects that testers assert on however they see fit.

A tester writes a .nim file that calls runBattle(bots=@["path/to/my_bot.nim", "path/to/adversary.nim"], rounds=3), gets back a BattleResult, and uses standard assertions. The framework handles everything else: compilation, server startup, bot launching, output parsing, cleanup.

User Stories

  1. As a bot developer, I want to run nimble test in my garage and have both unit tests and integration battle tests execute, so that I get full coverage in one command.
  2. As a bot developer, I want to write a custom adversary bot for a specific test scenario (e.g. a bot that stands still and fires), so that I can test my bot's behavior against controlled opponents.
  3. As a bot developer, I want to specify initial positions for all bots in a test, so that I can create deterministic, reproducible test scenarios.
  4. As a bot developer, I want runBattle() to compile all bot sources for me, so that I don't need manual pre-compilation steps.
  5. As a bot developer, I want a structured BattleResult object returned from runBattle(), so that I can assert on winners, scores, round counts, and per-bot results without parsing raw text.
  6. As a bot developer, I want to test a single module (e.g. my avoidance logic) by wiring it into a minimal test-specific bot, so that I can isolate what I'm testing without running my full bot with training loops and weight loading.
  7. As a bot developer, I want the server to start automatically on first use and stay up for the test session, so that I don't pay server startup cost per test.
  8. As a bot developer, I want to put all test-related files (adversary bots, test bot variants, the test itself) in a subfolder under tests/, so that test scenarios stay organized.
  9. As a bot developer, I want to pass a flat list of bot paths to runBattle() without distinguishing "main" from "adversary", so that the API stays simple and flexible.
  10. As a bot developer, I want to run multi-round battles and assert on aggregate results, so that I can validate consistency, not just single-round flukes.
  11. As a bot developer, I want clear error messages when bot compilation fails or the server can't start, so that I can debug test setup issues quickly.
  12. As a bot developer, I want to add the framework to my garage with just a config.nims path addition and an import, so that adoption is trivial.
  13. As a new bot developer, I want a usage guide with a working example, so that I can write my first integration test without reading framework internals.

Implementation Decisions

  • Framework location: common_libs/test_framework/, wired into garages via config.nims --path — same pattern as radar_lock.
  • Server wrapper: Wraps the existing Java battle runner at tools/battle_runner/. Server launched with --enable-initial-position (-I) flag. No mock server — the real Java server is the only backend.
  • Server lifecycle: Lazy singleton. First runBattle() call starts the server process; it stays alive for the process lifetime and is killed on exit. No per-test server restarts.
  • Bot compilation: runBattle() compiles all provided .nim bot sources before launching them. Compilation uses the Nim compiler directly. Errors surface as test failures with compiler output.
  • Bot JSON configs: The framework generates temporary bot JSON config files for each bot, including initialPosition when specified by the tester.
  • runBattle() API: Takes a flat list of bot source paths, optional round count, optional per-bot initial positions. Returns a BattleResult. No main/adversary distinction in the API signature.
  • BattleResult type: Structured object parsed from the Java battle runner's stdout. Contains: round results (winner, turn count), final rankings, per-bot scores. Exact fields derived from the server's output format (see RunBattle.java).
  • Output parsing: Pure function parseServerOutput(stdout: string): BattleResult. The Java runner prints a known format: === GAME STARTED ===, per-tick lines, === ROUND N ENDED ===, === RESULTS === with ranked scores.
  • No assertion helpers: The framework returns data; what to assert and how is entirely the tester's decision. Standard Nim assert or unittest check both work.
  • Test file structure: Each test scenario lives in a subfolder under the garage's tests/ dir (e.g. tests/avoidance/). Contains: the test .nim file, adversary .nim files, any test-specific bot variants. The garage's main bot source stays in src/.
  • Module isolation: To test a specific module, the tester writes a minimal bot in the test folder that imports only that module. The framework treats it as any other bot — no special instrumentation or hooks.
  • Invocation: nimble test runs all tests/test_*.nim and tests/*/test_*.nim files. Integration tests are just test files that happen to import the framework and call runBattle().

Testing Decisions

  • Two seams, no more:
    1. Output parser (parseServerOutput): Pure function, unit-testable with captured server output strings. This is where parsing bugs concentrate. Tested with a test_parser.nim in the framework's own tests/ dir.
    2. runBattle() end-to-end: The example integration test (Ticket #131) validates the full lifecycle — compile, server start, battle, parse, result. If runBattle() works, the internal plumbing (server manager, bot launcher) works.
  • No separate tests for internal modules (server manager, bot compiler). Testing implementation details couples tests to refactoring. The runBattle() seam covers them.
  • Prior art: common_libs/radar_lock/tests/test_radar_lock.nim uses unittest with suite/test/check. PPO_Bot tests use bare assert-style check template. Either pattern works for framework consumers.
  • Good test = external behavior only: A test calls runBattle(), gets a BattleResult, asserts on fields. Never assert on internal framework state (server PID, temp file paths, compilation commands).

Out of Scope

  • Mock server or in-process game simulation
  • Shared pre-built adversary bot library (testers write their own)
  • Parallel test execution
  • Statistical assertion helpers (win rate confidence intervals, etc.)
  • Tick-by-tick state replay or recording
  • Training loop integration (tests run bots in eval-only mode)
  • GUI or visual debugging of test battles

Grilling Session — Revised Decisions

The following decisions were made during a design review (grilling session) that uncovered gaps between the original spec and the actual codebase/APIs. These supersede any conflicting statements above.

Architecture Changes

External server, not embedded. The original spec assumed BattleRunner.create { embeddedServer() } could support initial positions. Investigation revealed that initial positions require the server to be started with the -I (--enable-initial-position) CLI flag, which is incompatible with embedded server mode. The framework starts an external server process with -I --tps -1 (unlimited speed, no GUI rendering). The BattleRunner connects to it in external server mode. Battles run at full computational speed — no rendering overhead.

RunBattle.java must be generalized. The existing tools/battle_runner/RunBattle.java is a custom diagnostic harness hardcoded for PPO_Bot vs Target (1 round, no CLI flags). It must be generalized into a CLI tool accepting: --server ws://host:port --bots dir1 dir2 ... --rounds N [output flags]. It stays in tools/battle_runner/.

Bot launch model: server-launched. BotEntry.of(directory) works in both embedded and external server modes. The Booter launches bot processes locally regardless of server mode. The framework produces bot directories (compiled binary + JSON config + boot script); the runner discovers them via BotEntry.

Server Lifecycle

  • Lazy singleton: first runBattle() call starts the server, stays alive for process lifetime.
  • Port: random free port to avoid conflicts.
  • Readiness: trust BattleRunner's internal wait logic (it was designed for external server mode). Add manual wait only if proven flaky.
  • Cleanup: addQuitProc kills server process. Bot processes die with the runner/booter.
  • JAR location: TANK_ROYALE_JAR env var (existing convention).
  • Build: runner is pre-compiled. Framework fails fast with clear message if JAR is missing.

Runner Output Design

Stdout/stderr split: results go to stdout (parsed by framework), diagnostics go to stderr (passthrough to terminal).

Always-on output (stdout):

  • Lifecycle markers: === GAME STARTED ===, === ROUND N ENDED (turn T) ===, === RESULTS (N rounds) ===
  • Full results with all 8 score categories per bot + placement counts

Fine-grained diagnostic flags (stderr):

Flag Event
--positions Per-tick bot x/y/direction/speed/energy
--radar Per-tick radar direction/sweep/turn rate
--scanned ScannedBotEvent
--bullet-fired BulletFiredEvent
--bullet-hit BulletHitBotEvent
--hit-by-bullet HitByBulletEvent
--hit-bot HitBotEvent (ram)
--bot-death BotDeathEvent
--all Everything above

No group flags in v1. Add when someone asks.

BattleResult Type

Full score breakdown parsed from results:

  • Per bot: rank, name, totalScore, survival, lastSurvivorBonus, bulletDamage, bulletKillBonus, ramDamage, ramKillBonus, firstPlaces, secondPlaces, thirdPlaces
  • Per round: seq[RoundResult] with roundNumber, turnCount, winner
  • Runner must be updated to print per-round winner (not currently printed).

Bot Compilation

  • nim c invoked from garage root — config.nims (with --path:"../common_libs") applies automatically.
  • No explicit --path flags needed in runBattle() for test bots under the garage tree.
  • Framework auto-generates bot directories: name derived from source filename, gameTypes: ["classic"], version "1.0.0", one-liner boot script. Temp dir, cleaned up after test.
  • initialPosition written into generated JSON config when specified by tester.

runBattle() API

  • outputFlags: seq[string] — raw passthrough of diagnostic flags to the Java runner.
  • Default timeout parameter — kills battle and fails clearly on timeout.
  • initialPosition parameter kept (functional via external server with -I).

Test Discovery

  • Nimble does NOT recurse into subdirectories (only finds tests/t*.nim at top level).
  • Solution: single tests/tall.nim entry point that imports all test modules including subdirectory ones.
  • No custom nimble task needed — default nimble test discovers tall.nim.

Framework Module Structure

common_libs/test_framework/
├── test_framework.nim      # Public API: runBattle(), types, server manager, bot compiler
├── test_framework.nimble   # Package metadata
├── parser.nim              # parseServerOutput() → BattleResult (pure function)
└── tests/
    └── test_parser.nim     # Unit tests for the parser

Two source files, not four. Parser is isolated (pure, unit-testable). Server manager and bot compiler are glue code inlined in test_framework.nim.

Error Handling

  • Compilation failure: raises exception with compiler output. Test clearly fails.
  • Runtime battle error: surfaced clearly, no silent failures.
  • Timeout: default in runBattle(), kills battle, test fails with timeout message.

Risks

  1. Initial position via embedded server is impossible — external server required, changes #128 entirely.
  2. RunBattle.java needs significant generalization — more work than originally scoped.
  3. BotEntry.of() in external mode — confirmed via docs but untested in this repo. #131 is the real proof.
  4. Nimble doesn't recurse — solved by tall.nim entry point.

Further Notes

⚠️ See 'Grilling Session — Revised Decisions' above for architectural changes that supersede parts of this spec.

  • The InitialPosition feature is a native Tank Royale debug capability. Bots declare it in their JSON config; the server respects it when launched with -I. All three coordinates (x, y, direction) are optional — omitted values randomize. This is the key enabler for deterministic test scenarios.
  • The Java battle runner's output format in RunBattle.java is the contract the parser depends on. If the runner's output format changes, only the parser needs updating.
  • Implementation is tracked in child tickets #127–#132 on this issue, with blocking dependencies wired.
## Problem Statement Each bot garage in the monorepo develops and trains bots independently, but there's no way to run automated integration tests — actual battles against controlled opponents with assertions on outcomes. The only testing today is unit-level (assert-based `test_*.nim` on pure functions). Validating that a bot *behaves* correctly in a battle requires manually running the Java battle runner, eyeballing stdout, and mentally checking results. Every garage that wants integration tests would have to reinvent this orchestration from scratch. ## Solution A shared integration test framework in `common_libs/test_framework/` that any garage can import to run battle simulation tests via `nimble test`. The framework wraps the existing Java battle runner, compiles bots on the fly, manages the server lifecycle, and returns structured `BattleResult` objects that testers assert on however they see fit. A tester writes a `.nim` file that calls `runBattle(bots=@["path/to/my_bot.nim", "path/to/adversary.nim"], rounds=3)`, gets back a `BattleResult`, and uses standard assertions. The framework handles everything else: compilation, server startup, bot launching, output parsing, cleanup. ## User Stories 1. As a bot developer, I want to run `nimble test` in my garage and have both unit tests and integration battle tests execute, so that I get full coverage in one command. 2. As a bot developer, I want to write a custom adversary bot for a specific test scenario (e.g. a bot that stands still and fires), so that I can test my bot's behavior against controlled opponents. 3. As a bot developer, I want to specify initial positions for all bots in a test, so that I can create deterministic, reproducible test scenarios. 4. As a bot developer, I want `runBattle()` to compile all bot sources for me, so that I don't need manual pre-compilation steps. 5. As a bot developer, I want a structured `BattleResult` object returned from `runBattle()`, so that I can assert on winners, scores, round counts, and per-bot results without parsing raw text. 6. As a bot developer, I want to test a single module (e.g. my avoidance logic) by wiring it into a minimal test-specific bot, so that I can isolate what I'm testing without running my full bot with training loops and weight loading. 7. As a bot developer, I want the server to start automatically on first use and stay up for the test session, so that I don't pay server startup cost per test. 8. As a bot developer, I want to put all test-related files (adversary bots, test bot variants, the test itself) in a subfolder under `tests/`, so that test scenarios stay organized. 9. As a bot developer, I want to pass a flat list of bot paths to `runBattle()` without distinguishing "main" from "adversary", so that the API stays simple and flexible. 10. As a bot developer, I want to run multi-round battles and assert on aggregate results, so that I can validate consistency, not just single-round flukes. 11. As a bot developer, I want clear error messages when bot compilation fails or the server can't start, so that I can debug test setup issues quickly. 12. As a bot developer, I want to add the framework to my garage with just a `config.nims` path addition and an import, so that adoption is trivial. 13. As a new bot developer, I want a usage guide with a working example, so that I can write my first integration test without reading framework internals. ## Implementation Decisions - **Framework location**: `common_libs/test_framework/`, wired into garages via `config.nims --path` — same pattern as `radar_lock`. - **Server wrapper**: Wraps the existing Java battle runner at `tools/battle_runner/`. Server launched with `--enable-initial-position` (`-I`) flag. No mock server — the real Java server is the only backend. - **Server lifecycle**: Lazy singleton. First `runBattle()` call starts the server process; it stays alive for the process lifetime and is killed on exit. No per-test server restarts. - **Bot compilation**: `runBattle()` compiles all provided `.nim` bot sources before launching them. Compilation uses the Nim compiler directly. Errors surface as test failures with compiler output. - **Bot JSON configs**: The framework generates temporary bot JSON config files for each bot, including `initialPosition` when specified by the tester. - **`runBattle()` API**: Takes a flat list of bot source paths, optional round count, optional per-bot initial positions. Returns a `BattleResult`. No main/adversary distinction in the API signature. - **`BattleResult` type**: Structured object parsed from the Java battle runner's stdout. Contains: round results (winner, turn count), final rankings, per-bot scores. Exact fields derived from the server's output format (see `RunBattle.java`). - **Output parsing**: Pure function `parseServerOutput(stdout: string): BattleResult`. The Java runner prints a known format: `=== GAME STARTED ===`, per-tick lines, `=== ROUND N ENDED ===`, `=== RESULTS ===` with ranked scores. - **No assertion helpers**: The framework returns data; what to assert and how is entirely the tester's decision. Standard Nim `assert` or `unittest` `check` both work. - **Test file structure**: Each test scenario lives in a subfolder under the garage's `tests/` dir (e.g. `tests/avoidance/`). Contains: the test `.nim` file, adversary `.nim` files, any test-specific bot variants. The garage's main bot source stays in `src/`. - **Module isolation**: To test a specific module, the tester writes a minimal bot in the test folder that imports only that module. The framework treats it as any other bot — no special instrumentation or hooks. - **Invocation**: `nimble test` runs all `tests/test_*.nim` and `tests/*/test_*.nim` files. Integration tests are just test files that happen to import the framework and call `runBattle()`. ## Testing Decisions - **Two seams, no more**: 1. **Output parser** (`parseServerOutput`): Pure function, unit-testable with captured server output strings. This is where parsing bugs concentrate. Tested with a `test_parser.nim` in the framework's own `tests/` dir. 2. **`runBattle()` end-to-end**: The example integration test (Ticket #131) validates the full lifecycle — compile, server start, battle, parse, result. If `runBattle()` works, the internal plumbing (server manager, bot launcher) works. - **No separate tests for internal modules** (server manager, bot compiler). Testing implementation details couples tests to refactoring. The `runBattle()` seam covers them. - **Prior art**: `common_libs/radar_lock/tests/test_radar_lock.nim` uses `unittest` with `suite`/`test`/`check`. PPO_Bot tests use bare `assert`-style `check` template. Either pattern works for framework consumers. - **Good test = external behavior only**: A test calls `runBattle()`, gets a `BattleResult`, asserts on fields. Never assert on internal framework state (server PID, temp file paths, compilation commands). ## Out of Scope - Mock server or in-process game simulation - Shared pre-built adversary bot library (testers write their own) - Parallel test execution - Statistical assertion helpers (win rate confidence intervals, etc.) - Tick-by-tick state replay or recording - Training loop integration (tests run bots in eval-only mode) - GUI or visual debugging of test battles ## Grilling Session — Revised Decisions The following decisions were made during a design review (grilling session) that uncovered gaps between the original spec and the actual codebase/APIs. These supersede any conflicting statements above. ### Architecture Changes **External server, not embedded.** The original spec assumed `BattleRunner.create { embeddedServer() }` could support initial positions. Investigation revealed that initial positions require the server to be started with the `-I` (`--enable-initial-position`) CLI flag, which is incompatible with embedded server mode. The framework starts an external server process with `-I --tps -1` (unlimited speed, no GUI rendering). The `BattleRunner` connects to it in external server mode. Battles run at full computational speed — no rendering overhead. **RunBattle.java must be generalized.** The existing `tools/battle_runner/RunBattle.java` is a custom diagnostic harness hardcoded for PPO_Bot vs Target (1 round, no CLI flags). It must be generalized into a CLI tool accepting: `--server ws://host:port --bots dir1 dir2 ... --rounds N [output flags]`. It stays in `tools/battle_runner/`. **Bot launch model: server-launched.** `BotEntry.of(directory)` works in both embedded and external server modes. The Booter launches bot processes locally regardless of server mode. The framework produces bot directories (compiled binary + JSON config + boot script); the runner discovers them via `BotEntry`. ### Server Lifecycle - **Lazy singleton**: first `runBattle()` call starts the server, stays alive for process lifetime. - **Port**: random free port to avoid conflicts. - **Readiness**: trust BattleRunner's internal wait logic (it was designed for external server mode). Add manual wait only if proven flaky. - **Cleanup**: `addQuitProc` kills server process. Bot processes die with the runner/booter. - **JAR location**: `TANK_ROYALE_JAR` env var (existing convention). - **Build**: runner is pre-compiled. Framework fails fast with clear message if JAR is missing. ### Runner Output Design **Stdout/stderr split:** results go to stdout (parsed by framework), diagnostics go to stderr (passthrough to terminal). **Always-on output (stdout):** - Lifecycle markers: `=== GAME STARTED ===`, `=== ROUND N ENDED (turn T) ===`, `=== RESULTS (N rounds) ===` - Full results with all 8 score categories per bot + placement counts **Fine-grained diagnostic flags (stderr):** | Flag | Event | |------|-------| | `--positions` | Per-tick bot x/y/direction/speed/energy | | `--radar` | Per-tick radar direction/sweep/turn rate | | `--scanned` | ScannedBotEvent | | `--bullet-fired` | BulletFiredEvent | | `--bullet-hit` | BulletHitBotEvent | | `--hit-by-bullet` | HitByBulletEvent | | `--hit-bot` | HitBotEvent (ram) | | `--bot-death` | BotDeathEvent | | `--all` | Everything above | No group flags in v1. Add when someone asks. ### BattleResult Type Full score breakdown parsed from results: - Per bot: `rank`, `name`, `totalScore`, `survival`, `lastSurvivorBonus`, `bulletDamage`, `bulletKillBonus`, `ramDamage`, `ramKillBonus`, `firstPlaces`, `secondPlaces`, `thirdPlaces` - Per round: `seq[RoundResult]` with `roundNumber`, `turnCount`, `winner` - Runner must be updated to print per-round winner (not currently printed). ### Bot Compilation - `nim c` invoked from garage root — `config.nims` (with `--path:"../common_libs"`) applies automatically. - No explicit `--path` flags needed in `runBattle()` for test bots under the garage tree. - Framework auto-generates bot directories: name derived from source filename, `gameTypes: ["classic"]`, version `"1.0.0"`, one-liner boot script. Temp dir, cleaned up after test. - `initialPosition` written into generated JSON config when specified by tester. ### runBattle() API - `outputFlags: seq[string]` — raw passthrough of diagnostic flags to the Java runner. - Default timeout parameter — kills battle and fails clearly on timeout. - `initialPosition` parameter kept (functional via external server with `-I`). ### Test Discovery - Nimble does NOT recurse into subdirectories (only finds `tests/t*.nim` at top level). - Solution: single `tests/tall.nim` entry point that imports all test modules including subdirectory ones. - No custom nimble task needed — default `nimble test` discovers `tall.nim`. ### Framework Module Structure ``` common_libs/test_framework/ ├── test_framework.nim # Public API: runBattle(), types, server manager, bot compiler ├── test_framework.nimble # Package metadata ├── parser.nim # parseServerOutput() → BattleResult (pure function) └── tests/ └── test_parser.nim # Unit tests for the parser ``` Two source files, not four. Parser is isolated (pure, unit-testable). Server manager and bot compiler are glue code inlined in `test_framework.nim`. ### Error Handling - Compilation failure: raises exception with compiler output. Test clearly fails. - Runtime battle error: surfaced clearly, no silent failures. - Timeout: default in `runBattle()`, kills battle, test fails with timeout message. ### Risks 1. **Initial position via embedded server is impossible** — external server required, changes #128 entirely. 2. **RunBattle.java needs significant generalization** — more work than originally scoped. 3. **`BotEntry.of()` in external mode** — confirmed via docs but untested in this repo. #131 is the real proof. 4. **Nimble doesn't recurse** — solved by `tall.nim` entry point. ## Further Notes ⚠️ See 'Grilling Session — Revised Decisions' above for architectural changes that supersede parts of this spec. - The `InitialPosition` feature is a native Tank Royale debug capability. Bots declare it in their JSON config; the server respects it when launched with `-I`. All three coordinates (x, y, direction) are optional — omitted values randomize. This is the key enabler for deterministic test scenarios. - The Java battle runner's output format in `RunBattle.java` is the contract the parser depends on. If the runner's output format changes, only the parser needs updating. - Implementation is tracked in child tickets #127–#132 on this issue, with blocking dependencies wired.
SirStone added the wayfinder:map label 2026-08-30 10:21:57 +02:00
SirStone added the ready-for-agent label 2026-08-30 10:27:58 +02:00
Author
Owner

Grilling resolutions (from spec review)

Resolved

  • Port wiring: BattleRunner handles bot-to-server connection internally. Nim framework only needs to pass server URL to the modified BattleRunner process. No explicit port wiring needed.
  • Bot compilation: Use nim c (not nimble build) — nimble swallows output. Framework calls nim c src/bot.nim with appropriate flags.
  • InitialPosition: Supported natively by TR server (--enable-initial-position / -I flag) + initialPosition field in bot JSON metadata. Server-side feature, no custom work needed.
  • CI: Out of scope. This is a local development tool for LLM-assisted development. Document "local only" in the usage guide (#132).
  • DangerZones migration: Out of scope.
  • Process cleanup: Use defer blocks in runBattle() and addExitProc for the server singleton to kill child processes on exception/exit/signal. Prevents zombie Java/bot processes.

New tickets created

  • #133: Modify BattleRunner.java to connect to external server and emit structured output
  • #134: Implement BattleRunner Java process lifecycle in Nim framework

Spec additions: Timeouts

All subprocess interactions must have timeouts:

  • Server startup ready-wait: 15s (server JAR is local, should be fast)
  • Bot compilation: 30s per bot
  • Battle execution: 60s per round (safety net for hung bots)
  • BattleRunner process completion: 30s after last battle event
  • Overall runBattle() call: 120s hard ceiling

Timeout values should be configurable via runBattle() optional params with these as defaults.

## Grilling resolutions (from spec review) ### Resolved - **Port wiring**: BattleRunner handles bot-to-server connection internally. Nim framework only needs to pass server URL to the modified BattleRunner process. No explicit port wiring needed. - **Bot compilation**: Use `nim c` (not `nimble build`) — nimble swallows output. Framework calls `nim c src/bot.nim` with appropriate flags. - **InitialPosition**: Supported natively by TR server (`--enable-initial-position` / `-I` flag) + `initialPosition` field in bot JSON metadata. Server-side feature, no custom work needed. - **CI**: Out of scope. This is a local development tool for LLM-assisted development. Document "local only" in the usage guide (#132). - **DangerZones migration**: Out of scope. - **Process cleanup**: Use `defer` blocks in `runBattle()` and `addExitProc` for the server singleton to kill child processes on exception/exit/signal. Prevents zombie Java/bot processes. ### New tickets created - **#133**: Modify BattleRunner.java to connect to external server and emit structured output - **#134**: Implement BattleRunner Java process lifecycle in Nim framework ### Spec additions: Timeouts All subprocess interactions must have timeouts: - Server startup ready-wait: **15s** (server JAR is local, should be fast) - Bot compilation: **30s** per bot - Battle execution: **60s** per round (safety net for hung bots) - BattleRunner process completion: **30s** after last battle event - Overall `runBattle()` call: **120s** hard ceiling Timeout values should be configurable via `runBattle()` optional params with these as defaults.
Author
Owner

Dependency update

Implementation is tracked in child tickets #127–#134 (not #127–#132 as originally stated). #133 and #134 were added during spec grilling to cover Java-side modifications and BattleRunner process lifecycle.

## Dependency update Implementation is tracked in child tickets #127–#134 (not #127–#132 as originally stated). #133 and #134 were added during spec grilling to cover Java-side modifications and BattleRunner process lifecycle.
Author
Owner

All child tickets #127–#134 implemented in commit 57a1915. Framework is ready for use.

All child tickets #127–#134 implemented in commit 57a1915. Framework is ready for use.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: SirStone/SirRoboGarage#126