Files
SirRoboGarage/DevControlBot_garage/AGENTS.md
T
SirStone 305c3977a6 Add PLAN.md rule, garage .gitignore, remove hardcoded server port hint
- AGENTS.md: mark PLAN.md as user-owned (never modify; it stays tracked)
- move the .env ignore rule to the garage root so it covers any depth
- drop the hardcoded "port 7654" hint from the no-server message; the target
  is now reported from the resolved SERVER_URL. The 7654 fallback remains
  only as the upstream TR default.
2026-10-03 16:39:02 +02:00

240 lines
12 KiB
Markdown

# DevControlBot — Garage Agent Guide
> ## ⚠️ READ THIS FIRST — THIS GARAGE IS *INTENTIONALLY* NOT THE ROOT TEMPLATE
>
> **The layout and format described in the repo-root [`../../AGENTS.md`](../../AGENTS.md)
> template do NOT apply to `DevControlBot_garage/`.** The root template documents a
> `src/<bot>.nim` / `tests/` / `out/` convention for garages. **This garage deviates
> from it on purpose and must stay that way.**
>
> **Do NOT, even to "fix" a perceived inconsistency:**
> - create `src/`, `tests/`, or `out/`
> - add a `config.nims`, `nimble.paths`, `--path` flags, or a vendor/ directory
> - restructure `DevControlBot/` to match the root template
>
> Builds go to a `mktemp -d` throwaway dir that is removed on exit — nothing is
> ever written here. The root `AGENTS.md` and its folder conventions are for the
> **other** garages; here it is context only (issue tracker, Nim conventions), and
> is **NOT a layout contract for this folder**. If the root template and this file
> disagree about layout, **this file wins.**
> ## ⚠️ DO NOT TOUCH `PLAN.md`
>
> **[`PLAN.md`](PLAN.md) is the user's personal working notes file.** The user
> maintains it and edits it exclusively.
>
> - Agents must **NEVER** modify, rewrite, reformat, move, delete or commit
> changes to `PLAN.md` — unless the user explicitly asks in that conversation.
> - Reading it is **not** assumed or expected; do not open it by default.
> - It **IS** tracked in git and is intended to be committed (deliberately added
> in commit `59fad00b`). Do **NOT** gitignore it and do **NOT**
> `git rm --cached` it.
## Start here
This file is the working rules for the bot. The human-facing description lives in
[README.md](README.md) — read it first.
Garage-specific rules for `DevControlBot_garage/`. Repo-wide rules live in
[`../../AGENTS.md`](../../AGENTS.md) (issue tracker, Nim conventions, test
framework) — read it for that context, but note again: **its folder-layout
template does not govern this garage.**
## Conventions
- This bot's code lives **only** inside `DevControlBot_garage/`. Do not edit other
garages to make this bot work.
- Do not reuse or copy another bot's game logic (movement/scanning/firing, radar
tables, targeting heuristics) without explicit permission from the user. Shared,
bot-agnostic code belongs in `common_libs/`, and only with permission.
- Body, gun and radar are `WHITE` (team convention: that part is not programmed
yet). Colors are set once at initialization, never in the tick loop. Keep it
that way: the tick loop must do nothing but `go()` until real logic is written.
## Build artifacts / binaries
- **Rule:** never write a binary, log or build output into this garage. The
`runBot` task builds into a `mktemp -d` throwaway dir and removes it with an
`EXIT`/`INT`/`TERM` trap, so **no `out/` directory is ever created** here.
- **Rule:** never commit binaries or nimble cache dirs (`~/.nimble`,
`nimbledeps/`). `nimbledeps/` only appears with `nimble develop`; ignore it.
- **Rule:** dependencies come from the global nimble store. Do **not** vendor a
copy or add `nimble.paths`/`nimble develop` — `nimble install
robocode_tankroyale_botapi` is the only setup step. (The package was renamed
from `tankroyale_botapi`; the old name is gone.)
Exception: the `runBot` task *derives* `--path:` flags at run time from
`nimble path <pkg>`, because the compiler's bundled
`nimblepath="$home/.nimble/pkgs2/"` only resolves when `$HOME` is set. That is
resolution, not vendoring; keep it.
- Build/run with the folder task, never raw `nim c`:
```sh
cd DevControlBot_garage/DevControlBot
nimble runBot
```
- Note: the root `.gitignore` claims "all builds go to `*_garage/out/`". That is
no longer true for this garage — do not rely on that rule, and do not add an
`out/` directory just to satisfy it. (Root `.gitignore` is not this garage's
file; leave it alone unless the user asks.)
## Running the bot
`DevControlBot/DevControlBot.sh` is the canonical entry point and works from any
cwd — it resolves `SCRIPT_DIR` from `BASH_SOURCE`, `cd`s to the `.nimble` dir,
forwards args and signals to `nimble runBot`, and propagates the exit code.
```sh
./DevControlBot/DevControlBot.sh # from the garage root: bundled metadata
./DevControlBot/DevControlBot.sh /path.json # alternate metadata (first arg, must end in .json)
./DevControlBot/DevControlBot.sh --debug # classic debug build
./DevControlBot/DevControlBot.sh --help # usage, exits 0 without building
```
- **Rule: no arguments means RUN, not usage.** A bare `./DevControlBot.sh`
performs a quiet RELEASE build and runs the bot with the bundled
`DevControlBot.json`, exactly as if that path had been passed. Usage is
printed **only** for an explicit `-h` / `--help`, which exits 0 without
building. (An earlier version printed usage and exited 0 with no arguments —
that was the bug, and it is fixed.)
- There is **no `--json` flag**: the metadata path is the first positional
argument and is used when it ends in `.json`. Anything else is forwarded to
the bot verbatim.
- **Build mode:** default is **release** (`nim c -d:release`, quiet: no compiler
hints, no dot-progress line, just `[devcontrolbot] build ok (release)`).
`--debug` selects the classic debug build (`nim c`, full hints/diagnostics).
The flag is stripped by the wrapper in any position and reaches the `runBot`
task through the exported env var `$DEVCONTROLBOT_DEBUG` (`debug`/`release`),
never as a bot argument. Compiler errors are shown in **both** modes; a failed
release build replays the captured compiler log, so it is never silent.
- With no arguments (or `--help`) the wrapper prints usage and exits 0 without
building.
- **Gotcha:** `nimble runBot` must be invoked from `DevControlBot/`, the
directory containing the `.nimble`. From `DevControlBot_garage/` it fails with
`Could not find a file with a .nimble extension inside the specified directory`.
- `compile`/`build` are reserved Nimble builtin names — hence `runBot`.
- Without a Tank Royale server at `SERVER_URL` the run fails with
`[start] Cannot connect ... Connection refused` and exit code 1. That is
expected during local verification, not a bug.
## Configuration = environment variables (the API's own mechanism)
- **Rule: never add env-reading code to `DevControlBot.nim`.** The bot API
already reads its configuration from the process environment inside
`start()`: `SERVER_URL` and `SERVER_SECRET`
(`robocode_tankroyale_botapi.nim:424-425`, documented at `:396-397`) and
`BOT_NAME` / `BOT_VERSION` / `BOT_AUTHORS` / … in `bot_info.nim:117-130`
(used when no JSON metadata is found). That is the supported mechanism the
official BotLauncher uses; `DevControlBot.nim` calls plain `start(bot,
jsonPath)` and needs no change.
- The `runBot` task makes those variables **present** for local runs (see the
`.env` section). That is all it does; it does not interpret them.
## Optional `.env` (local runs only)
`DevControlBot/.env` holds the local configuration (typically `SERVER_URL` and
`SERVER_SECRET`). Rules:
- **It is optional.** If it is missing the run is unchanged: no error, no
non-zero exit, just one informational line and the API defaults.
- **Loaded in the `runBot` task's shell**, not in `DevControlBot.sh`, so it works
for both `./DevControlBot.sh` and a direct `nimble runBot` and the wrapper
stays a thin wrapper. `$DEVCONTROLBOT_ENV_FILE` overrides the path.
- **Precedence — CRITICAL: the environment always wins.** A variable already
present in the environment is **never** overridden by `.env`, so the official
BotLauncher's values keep winning. It is achieved by *filtering*: a
`NAME=value` line is dropped when `NAME` is already set (`[ -n "${NAME+x}" ]`,
set-but-maybe-empty), and only the still-unset names are `set -a`-exported and
sourced. Do **not** "simplify" this into a plain `set -a; . .env` — sourcing
does the opposite and would let the file beat the launcher.
- Accepts `FOO=bar` and `export FOO=bar`, strips `CR` (CRLF), skips blanks,
`#` comments and any line that is not a plain `NAME=value` identifier.
- **Never print values** and never use `set -x`: report variable *names* only.
- **Never commit it** — it holds secrets. The root `.gitignore` covers
`ModularBot_garage/.env` only and does **not** ignore this one; the rule to
add (ask the user before touching the root file) is
`DevControlBot_garage/DevControlBot/.env`.
## Failure reporting / exit codes
- **Rule:** an expected failure must never surface as a Nimble/NimScript
exception. `exec` raises on any non-zero exit, printing a stack trace and an
escaped copy of `runScript`. So the shell always exits 0 and writes the real
code to `$DEVCONTROLBOT_STATUS_FILE`; `DevControlBot.sh` exports that path and
re-exits with the code. Never mask it to 0, never let the shell's code be
swallowed.
- Exit codes (keep them distinct, they are what BotLauncher branches on):
`0` clean, `1` no game server at `SERVER_URL` (expected), `2` dependency not
installed, `3` compile failure, anything else = the bot's own code.
Each prints its own short, actionable line; compile failures keep the raw
compiler errors.
- Running `nimble runBot` directly (no status file) intentionally falls back to
the legacy behaviour: the shell exits with the real code and Nimble raises.
## Test organization
- **There are currently no tests in this garage.** No `tests/` directory, no
`tests/config.nims`, and the `.nimble` has exactly one task (`runBot`) — the old
`test`, `compileBot` and `setupVendor` tasks are gone. `nimble test` does not
exist; do not invoke it and do not document it.
- Convention **when** tests are added (repo-wide, see
[`../../AGENTS.md`](../../AGENTS.md) and
`common_libs/test_framework/README.md`):
- create `DevControlBot_garage/tests/` with `tests/config.nims` containing
`--path:"../../common_libs"` (depth must match the garage location), one
`test<concern>.nim` per concern (e.g. `test_basic_battle.nim`);
- add a `test` task to `DevControlBot.nimble` running
`nim c -r --path:../common_libs tests/test_basic_battle.nim`;
- guard first, import second — tests must pass with no Java present:
```nim
import std/os
if not existsEnv("TR_SERVER_JAR") or not existsEnv("TR_BATTLE_RUNNER"):
echo "Skipping: TR_SERVER_JAR / TR_BATTLE_RUNNER not set"
quit(0)
import test_framework/test_framework
```
- shared adversaries: `common_libs/test_framework/adversaries/SittingDuck`
(passive) and `OscillatorBot` (fights back); standard call is
`runBattle(@[myBotDir, adversaryDir], rounds = 10)`.
- env vars: `TR_SERVER_JAR`, `TR_BATTLE_RUNNER`. Failures surface as `OSError`
(bot compile failed), `IOError` (runner non-zero), `TimeoutError`
(server 15s / compile 30s per bot / battle runner).
## Onboarding — full sequence
```sh
# 1. dependency (once; already global if nimble install says "already installed")
nimble install robocode_tankroyale_botapi
# 2. run the bot (compiles to a temp dir, runs, cleans up; nothing left behind)
./DevControlBot/DevControlBot.sh
# 2b. same thing via the task — must be run from DevControlBot/
cd DevControlBot && nimble runBot
# 3. tests — none exist yet; see "Test organization" above
```
Checklist after touching build config:
- `cd DevControlBot && nimble runBot` compiles (fails only on "Cannot connect"
without a server) and leaves the garage tree unchanged (`ls DevControlBot_garage`
still shows only `AGENTS.md`, `README.md`, `DevControlBot/`).
## Layout of this folder
```
DevControlBot_garage/
├── AGENTS.md # this file
├── README.md # human-facing description
└── DevControlBot/
├── DevControlBot.nim # bot type + entry point (isMainModule)
├── DevControlBot.json # bot metadata
├── DevControlBot.nimble # single task: runBot
├── DevControlBot.sh # BotLauncher entry point (thin wrapper)
└── .env # OPTIONAL local config; secrets, never commit
```