fix(adversaries): the launchers ran STALE binaries - this is why the earlier fix never took effect

P0. Three of the five launchers ran ./<Bot> (a tracked binary at the bot root)
while config.nims sets outdir=out and both the test framework's compileBots and
a manual 'nim c src/<Bot>.nim' write to out/. SittingDuck and OscillatorBot
correctly ran ./out/<Bot>; RandomMover, PatternMover and WaveSurfer did not.
cmp confirms the root and out binaries differed for all three.

Consequence: the previous session's adversary fixes were compiled into out/ and
never executed. Every gauntlet and every capture ran the OLD code. This is
almost certainly why the user's instinct that these bots were still bugged was
correct while the code claimed otherwise.

Fixed by pointing all five launchers at ./out/<Bot>, and by deleting the three
stale root binaries so the trap cannot recur. Verified end to end through the
booter: WaveSurfer went from standing still 96.2% of ticks with a 1398-tick
longest standstill, to rest 12.3% / mean speed 6.69 / longest zero run 18 /
perpendicular 0.845.

Also honours GUN_STATS_PATH in test_gauntlet_5bots.nim (same knob ModularBot
reads) so pooled gauntlet runs append to one file instead of clobbering the
default.

NOTE for a follow-up: the out/ binaries are still TRACKED build artifacts, which
is the same class of hazard that caused this. Untracking them (as was done for
ModularBot_garage/ModularBot) would remove the failure mode entirely.
This commit is contained in:
2026-09-21 08:22:04 +02:00
parent c214abcfa8
commit 1e8f0d342a
7 changed files with 9 additions and 6 deletions
@@ -36,9 +36,12 @@ proc runBattle(label, enemyName, enemyDir: string) =
echo sep
let bots = @[modularBotDir, enemyDir]
discard compileBots(@[enemyDir]) # ensure adversary is compiled
# Write session marker so analysis can map rounds to enemy name
# Write session marker so analysis can map rounds to enemy name.
# Honor GUN_STATS_PATH (same knob ModularBot reads) so repeated pooled runs
# can append to one dedicated file instead of clobbering the default.
let statsPath = getEnv("GUN_STATS_PATH", "/tmp/gun_stats.jsonl")
block:
let lf = open("/tmp/gun_stats.jsonl", fmAppend)
let lf = open(statsPath, fmAppend)
lf.writeLine("{\"session_start\":\"" & enemyName & "\"}")
lf.close()
let raw = runBattleRunner(getServerUrl(), bots, 10, 300_000, true)
@@ -63,4 +66,4 @@ runBattle("Battle 2/5: vs OscillatorBot","OscillatorBot", adversariesDir / "Osci
runBattle("Battle 3/5: vs RandomMover", "RandomMover", adversariesDir / "RandomMover")
runBattle("Battle 4/5: vs PatternMover", "PatternMover", adversariesDir / "PatternMover")
runBattle("Battle 5/5: vs WaveSurfer", "WaveSurfer", adversariesDir / "WaveSurfer")
echo "\nGauntlet complete. Stats in /tmp/gun_stats.jsonl and /tmp/gun_gauntlet_results.log"
echo "\nGauntlet complete. Stats in " & getEnv("GUN_STATS_PATH", "/tmp/gun_stats.jsonl") & " and /tmp/gun_gauntlet_results.log"