# OT Agent Arena

A reproducible benchmark for agents that play Tibia (Canary 3.6.1, client 15.11) on a local OT server. The arena is the result we
publish; topping it is meaningful only because the baselines are tuned by their own authors, not by us.

## Three layers

| Layer | Measured against | Accuracy |
| --- | --- | --- |
| 1. Fidelity | Canary 3.6.1 as the oracle | Deterministic mechanics bit-exact; RNG mechanics through a seeded local Canary build, or distribution tests against Canary's damage and loot formulas |
| 2. Agent strength | Open-source bots (below), a scripted cavebot, a random policy | Frozen scenario pack, same server build, character, hunt and seeds; confidence intervals |
| 3. Public anchor (optional) | Craftax-Classic with the same agent architecture | Academic leaderboard; shows the agent is not overfit to our scenarios |

## Baselines

| Baseline | Source | Configuration |
| --- | --- | --- |
| nExBot | `github.com/mCodex/nExBot` (pushed 2026-09-01): Lua bot for OTClientV8 and opentibiabr OTClient; GitHub detects no license file (a mirror's README says MIT) | Author or community config per scenario |
| vBot | Ships inside `OTCv8/otclientv8` as the bot module's default config (`modules/game_bot/default_configs/vBot_4.8`); repository license `NOASSERTION` (custom LICENSE file); OTClientV8 stops at protocol 11.x, but OTClient Redemption 4.1 ships the same bot as `mods/game_bot` with `default_configs/vBot_4.8` and `cavebot_1.3`, so vBot runs on Canary's protocol | Its default config, tuned per scenario |
| Scripted cavebot | Ours: heal below X% HP, attack nearest, walk waypoints | Fixed thresholds, published |
| Random policy | Ours: uniform over legal actions | The control; anything that "learns" must leave it behind |

P4 checks before any baseline is used: the canonical repository, its author and license, that it runs against
Canary's protocol on the client we pair it with, and that its terms allow publishing results.

Bot authors may submit configs; the best submitted config per scenario becomes that scenario's baseline.

## Parity

Every competitor, ours included, plays under the same conditions, so a win cannot come from speed or extra sight:

- Same information: the client screen (15x11 tiles), even though the protocol sends 18x14; no server-side state.
- Same client: OTClient Redemption 4.1 for every competitor. The bots only run inside it, so our agent runs inside it too,
  as Lua compiled from the same Spiral source as the simulator.
- Same action-rate cap and the same 15x11 view, enforced by one arena layer inside OTClient that wraps every `g_game`
  action and observation, for bots and agents alike. First version (2026-10-13, `live/parity.lua`):
  one action per 250 ms shared by every `g_game` action and `LocalPlayer.autoWalk`, and `g_map.getSpectators` returns
  only creatures on the 15x11 screen. It reports allowed and dropped actions per run. The gate answers only for itself:
  an admitted call returns true whatever the wrapped `g_game` call returns, so a probe can tell a dropped action from an
  admitted one.
- Same measurement rate: every probe watches health, experience and the clear every 100 ms. The vBot probe sampled every 10 s
  until 2026-10-14, which rounded vBot's clear times up by about 5 s and turned its lowest-health figures into snapshots.
- Same decision rate: a cap on actions does not equalize reaction time. vBot's HealBot macro runs every 100 ms; our
  probe decided every 250 ms in v1 and v2 and reacted later to damage bursts, so it now ticks every 100 ms too. The
  simulator's knight decides on its 100 ms tick (`hunting_pack.client_decision_ms`) behind the same 250 ms action
  witness (`readiness.action_gate`).
- Same login state: Canary keeps monsters off a freshly logged-in player for 10 s unless the player moves, auto-walks,
  turns, talks, uses an item or cancels an attack (starting an attack does not end it). Until 2026-10-15 14:17 our probe
  kept that protection and vBot and nExBot ended it, which decided PR-9 and half of PR-3a. Since then `parity.lua` makes
  one native turn 300 ms after login, outside the gate, for every competitor. Known remaining asymmetry: the policy
  probe acts from about 2.5 s after login, vBot at once; a test for a win should start every competitor's logic at the
  same moment.
- Same character, equipment, supplies, scenario and seeds.
- Same server build and tick timing; no client-side speed hacks.

## Scenarios

A scenario is: server build hash, map region, spawn set, character (vocation, level, skills, equipment, supplies),
start position, seed, time budget. The pack is frozen and versioned; a change is a new pack version.

Scenario design must avoid saturation. On many hunts XP/h is capped by respawn rate, so a script and a perfect
player tie. Keep scenarios where decisions matter:

- mixed spawns (target choice, pulling, separating),
- danger (death is likely with poor healing or positioning),
- resource limits (mana, potions, capacity, a supply budget),
- route choice (several areas with different yield and risk).

A scenario where the scripted cavebot reaches the respawn cap is removed.

Pack v1, scenario "cyclops clear" (live through `live/run-arena.ps1`, simulated through
`hunting_pack.clear_run`):

| Part | Value |
| --- | --- |
| Server | Canary 3.6.1, data-canary, forge off (`forgeInfluencedLimit = 0`, `forgeFiendishLimit = 0`), rates 1.0 |
| Character | Knight Sample, level 30 (515 hp, 200 mana), axe 60, shield 11, steel axe, brass set, dwarven shield |
| Supplies | 100 health potions (266, heal 125-175); the whole inventory is deleted and re-inserted before every run |
| Spawn | 3 cyclops summoned by a GM standing at 1932,1352 with `/m cyclops,3,,2` (radius 2, so 1-5 tiles south of the knight at 1932,1349); no respawn. Corrected 2026-10-14 14:54: this row said "within 1 tile", which the arena never did |
| Parity | 250 ms action gap, 15x11 view, shared by every competitor |
| Healing | Potion below the simulator's profit-tuned threshold, the same for every competitor (100 from the corrected simulator); runs before 2026-10-13 15:00 are void: deaths dropped gear the reset did not restore |
| End | 675 experience (three kills at 225) or 200 s |
| Recorded | Clear time, potions, gold, deaths, lowest health, damage taken (from the kept logs) |
Paths under `lab/` cited in the rows below name exploratory scripts and probes that were removed on 2026-10-21 when the lab folder was emptied; the methods are described in each row. Model predictions for the current bundle are reproduced by `scripts/predict.ps1` (entry `apps/predict/predict.spi`), and candidates are scored under rule D16 by `scripts/score.ps1`.

### Pre-registration PR-1 (written 2026-10-14 07:30, before any run)

| Item | Value |
| --- | --- |
| Question | Does the retreat policy use fewer potions than vBot in the cyclops clear? |
| Competitors | `spiral-retreat` (rules: heal below 160, step back from the nearest attacker at 2 attackers, ReachFirst targeting) vs `vbot` (vBot 4.8, TargetBot chase, HealBot potion below 160) |
| Scenario | Pack v1 cyclops clear, exactly as in the table above, both probes on the 100 ms watch that stops at the clear |
| Runs | 60 per competitor, alternating, one block (`run-arena.ps1 -Runs 60 -Competitors spiral-retreat,vbot`, results in `results/rows/arena_results_cyclops_pr1.spi`); a run that crashes is rerun once, then counted as missing |
| Primary metric | Potions per run, all runs (a death counts its potions too) |
| Test | Welch two-sided t-test, alpha 0.05; report the difference with its 95% interval |
| Secondary (descriptive only) | Damage taken, clear time, deaths, lowest health |
| Decision | Significant and negative: retreat uses fewer potions. Otherwise: no difference shown. No further runs are added to PR-1. |
| Analysis | Welch t on potions by `eoie experiment analyze target/ledger PR-1 results/rows/arena_results_cyclops_pr1.spi`; damage taken from the block's logs by `eoie table logs <logs> --sum taken='You lose (\d+) hitpoints? due to an attack'`. Until 2026-10-21 this was `live/pr1-analysis.ps1` (git history), written and validated on round v10 before any PR-1 result existed |
| Interruption (2026-10-14 09:36) | The host's memory reaper stopped the run after 22 complete pairs; the pair in progress wrote no row. It resumed with the same command and `-Runs 38` into the same file, so the count stays 60 each. Only row counts were read; nothing was computed. The resumed run copied a newer `apps/tibia` snapshot (simulator modules had changed), but `live.spi` compiles to byte-identical Lua from both snapshots (SHA-256 7FD3D538...93E5, the same `policy.lua` the probes load), and the vBot side predates PR-1's start (`probe-vbot-body.lua` last written 03:32, the vBot 4.8 sources under `otclient-src` 2026-10-12). |
| VOID (2026-10-14 10:29) | Stopped at 61 rows (31 retreat, 30 vBot). Mechanism: the vBot probe enables OTClient's bot for the character (`bot: Knight Sample_1511: enabled, config vBot_4.8` in the client's `config.otml`); nothing reset that setting, the vBot folder stayed installed, and the policy probe never disabled it, so every `spiral-*` run after a vBot run had vBot's HealBot and TargetBot running alongside our policy. Signatures: the server reports potions our policy never drank (13 potions against 6 heal notes in one run, the first potion before our first heal), and the parity gate counts more actions than our policy takes. A scan of every spiral log (`live/arena-check.ps1`) shows such foreign potions in almost every spiral run from 2026-10-13 11:48 on, and none in the three runs before. While diagnosing, the potion counts of four retreat-arm logs were read; the void rests on the mechanism, not on any comparison, and PR-1's rows are not pooled into anything. Replaced by PR-4. |
### Pre-registration PR-2 (written 2026-10-14 07:55, before any live mixed-camp run)

| Item | Value |
| --- | --- |
| Question | Does the simulator predict a live clear of a mixed camp (one cyclops, two fleeing trolls)? |
| Prediction | `hunting_pack.cyclops_and_two_trolls_on_the_arena_spot`, `cavebot_with_heal 160`, 200 simulated clears (seeds 5001-5200): 1.23 potions per clear (sd 0.93), 36.2 s per clear (sd 6.6 s), 0 deaths. Addendum 2026-10-14 08:31, still before any live run: the arena map grew from the client's +-7 window to the server map's radius 30, and the prediction is unchanged (the cavebot never leaves the window). Addendum 2026-10-14 10:42, still before any live run: recomputed after monster sight and the hunter record, 36.21 s (sd 6.59), 1.23 potions (sd 0.93), no deaths, and no troll escapes in 200 clears, so the flee-respawn suspect cannot explain a gap in this scenario |
| Live competitor | `spiral-policy` (rules, heal below 160, CurrentFirst targeting), the same knight and arena as pack v1 |
| Scenario | Arena spot, one cyclops and two trolls summoned like pack v1 (GM at 1932,1352, radius 2; corrected 2026-10-14 14:54 from "within 1 tile"); end at 285 experience (225 + 2 x 30) or 200 s |
| Runs | 20, one block, results in `results/rows/arena_results_mixed_pr2.spi` |
| Test | One-sample t of the live mean against the predicted mean, separately for potions and clear time, two-sided, alpha 0.05 (critical 2.09 at df 19) |
| Decision | Both within: the simulator predicts the mixed clear. Either outside: a fidelity gap, reported with its size; the known suspect is that the simulator respawns a troll that flees beyond 8 tiles, while live trolls flee and stay. |
| Arena (2026-10-14 14:54, before any PR-2 run) | Runs on the arena fixed for PR-4: rows with foreign potions or actions are excluded and listed (`-ExcludeForeign`), the policy Lua is the frozen PR-4 file. The 20 runs go in foreground chunks of 3 into the same file (the memory reaper only stops background shells), with nothing else running on this lane (2026-10-14 15:06) |
| RESULT (2026-10-14 15:46) | 20 runs, 0 arena failures, 0 deaths, every run cleared. Clear time holds: live 37.3 s (sd 7.8) vs predicted 36.2, t 0.62. Potions do not: live 0.55 per clear (sd 0.89) vs predicted 1.23, t -3.43. The same direction as PR-4's rules policy (the simulator lands about 10% too many hits on a standing knight, TIBIA-POTION-FIDELITY); the troll-escape suspect is out (no escapes predicted) |
| Analysis | One-sample t per metric by `eoie experiment analyze target/ledger PR-2-POTIONS` and `PR-2-TIME` on `results/rows/arena_results_mixed_pr2.spi`: clear time over cleared runs only, deaths reported. Until 2026-10-21 this was `live/pr2-analysis.ps1` (git history), written 2026-10-14 10:05 before any PR-2 run |

### Pre-registration PR-3 (written 2026-10-14 09:59, before any nExBot run)

| Item | Value |
| --- | --- |
| Question | Does the retreat policy use fewer potions than nExBot in the cyclops clear? |
| Competitors | `spiral-retreat` (as in PR-1) vs `nexbot` (nExBot 4.0.4 at 19632d4, network calls stubbed, TargetBot chase with the same arena targeting file as vBot, HealBot potion at health percent <= 30, which fires for health <= 159 of 515, the same as below 160) |
| Scenario | Pack v1 cyclops clear, as in PR-1 |
| Arena check | Before the block, at most 3 `nexbot` runs into `results/rows/arena_results_cyclops_nexbot_check.spi`, read only for: it attacks, it drinks, it clears or times out. Seeding may be fixed after them; these runs are never analyzed. |
| Runs | 30 per competitor, alternating, one block (`run-arena.ps1 -Runs 30 -Competitors spiral-retreat,nexbot`, results in `results/rows/arena_results_cyclops_pr3.spi`); a crash is rerun once, then counted as missing. 30, not 60, for the day's time budget: with the potions sd of earlier valid rounds (about 3), it detects about 2.2 potions (17%) at 80% power |
| Primary metric, test, secondary, decision | As in PR-1 |
| Analysis | As in PR-1 with nexbot as the baseline: `eoie experiment analyze` on `results/rows/arena_results_cyclops_pr3.spi`, after registering PR-3 from this section |
| STATUS (2026-10-14 16:33) | Not run: nExBot crashed the client in all 3 check runs (0xC0000374 about 10 s after login; TIBIA-NEXBOT-CRASH). PR-3 stays registered as written for when nExBot runs |
| STATUS (2026-10-15 04:02) | Superseded by PR-3a: the check runs are spent, and nExBot runs only with the arena shims below |
### Pre-registration PR-3a, retreat vs nExBot (written 2026-10-15 04:02, before any PR-3a run; amends PR-3)

| Item | Value |
| --- | --- |
| Question | As PR-3: does the retreat policy use fewer potions than nExBot in the cyclops clear? |
| Arena shims (nExBot only, `run-policy-scenario.ps1 -Bot nExBot` and `probe-nexbot-body.lua`) | 1. Nested `dofile` inside the bot runs as pure Lua (`apps/arena/bot_dofile.spi`, built to `target/arena/bot_dofile.lua`, which replaced the hand-written `live/bot-lua-dofile.lua` on 2026-10-25 with identical behaviour under the fake-OTClient stub: read, `loadstring`, `setfenv` to the bot's module environment); a missing file returns nothing instead of raising. The client's native dofile, and any error raised under nExBot's `pcall` around it, crash OTClient Redemption with 0xC0000374; nExBot's own `dofile("nExBot/targetbot/chase_controller.lua")` names a path that does not exist from the bot root. 2. The bot sandbox (`mods/game_bot/executor.lua`) also exposes getmetatable, rawset, rawget, rawequal, next, select, unpack and xpcall, which nExBot's event bus calls; vBot calls none of them. 3. `g_game.cancelFollow` does nothing while nothing is followed: Canary 3.6.1 clears the attack target on any follow packet (`Game::playerFollowCreature`) without telling the client, so nExBot's one cancel after its first attack left it standing next to a cyclops it never hit. 4. The probe counts nExBot's attack, cancel and movement calls (pass-through wrappers) |
| Seen before this registration | 16 diagnostic runs in `results/rows/arena_results_cyclops_nexbot_diagnostic.spi` while the shims were built (crashes, then no damage); one run with all shims: cleared, 86.1 s, 17 potions, no death. Not analyzed with PR-3a |
| Competitors | `spiral-retreat` (frozen PR-4 Lua) vs `nexbot` (as in PR-3, with the shims) |
| Scenario | Pack v1 cyclops clear, as in PR-4 |
| Runs | 20 per competitor, alternating, foreground chunks into `results/rows/arena_results_cyclops_pr3a.spi`; foreign-activity `spiral-*` rows excluded; a crash is rerun once, then missing. 20, not 30, for the night's time budget: with sd about 3 it detects about 2.7 potions at 80% power |
| Test | Potions per run, Welch two-sided, alpha 0.05, 95% interval (`pr1-analysis.ps1 -Treatment spiral-retreat -Baseline nexbot -Planned 20 -Label PR-3a -ExcludeForeign`) |
| Secondary (descriptive only) | Clear time, deaths, damage taken, nExBot's attack and cancel counts |
| Decision | Significant and negative: retreat uses fewer potions than nExBot. Otherwise: no difference shown. No runs are added |
| RESULT (2026-10-15 05:50) | 40 runs, 0 arena failures, 0 deaths, every run cleared. Retreat 12.20 potions (sd 3.94) vs nExBot 14.55 (sd 2.95), difference -2.35 (95% CI -4.58 .. -0.12), Welch t -2.14 (critical 2.03): significant, retreat uses fewer potions than nExBot. Narrow and single: retreat vs vBot was not significant in PR-4 (-1.10), and nExBot's mean is close to vBot's (14.00 in PR-4, 14.17 in PR-8). Descriptive: clear time 90.9 vs 88.4 s, damage taken 2030 vs 2421 (CI of the difference -748 .. -35; from the 16 and 18 of 20 logs the analysis matched to rows; all 20 retreat logs give 2110), lowest health 106.5 vs 95.4. The policy Lua compiled from live.spi on pack-1h after the block is byte-identical to the frozen file (7FD3D538...93E5). nExBot issued 3.2 attacks per run, no attack cancels, and the cancelFollow guard fired once in every run (without it, each run would have stood still next to its first target) |
| Addendum (2026-10-15 18:45, post-hoc, explanatory) | About half of the difference is the login-protection artifact found after PR-9: nExBot ends the knight's protection early (its probe saw one `talk` per run), taking 8.85 hits in the first 10 s against 3.45 for retreat; after 10 s nExBot still takes 4.7 more hits per clear (60.3 vs 55.6), about one potion, which no test has established. A fair re-test needs the PR-11 arena (protection ended alike) |
### Pre-registration PC-1, path check (written 2026-10-14 10:11, before any live run)

| Item | Value |
| --- | --- |
| Question | Does a cyclops walk around a wall the way the hunting-ground router predicts, where greedy one-tile steps get stuck? |
| Setup | Knight standing at server 1943,1338 (simulator 110,88), not attacking, bot disabled; one cyclops summoned next to 1948,1343 (simulator 115,93), on the other side of the room wall at x 1947 with the gap at 1948-1951 in row 1339 |
| Prediction | `terrain` routing on ArenaHunt from the logged start tile: 8-11 straight steps (9 from 1948,1343), arrival after steps x 702 ms (cyclops 650 ms on the 8%-slower arena ground). Greedy steps (the ArenaSpot model) never arrive from any of the 9 possible start tiles |
| Runs | 5 (`run-policy-scenario.ps1 -ProbeBody probe-path-body.lua -Monster cyclops -MonsterCount 1 -SpawnRadius 0 -SummonAt 1948,1343 -KnightAt 1943,1338`) |
| Decision | Arrives in every run with the predicted step count +-1: the router's path holds. Diagonal steps or a longer detour are reported with their count. Not arriving in 30 s: the router is wrong for this map |
| RESULT (2026-10-14 16:00) | 5 valid runs (3 earlier runs were arena failures: the probe read a nil position at login; fixed before the valid runs; `results/path-check-pc1.md`). Every run arrives (14.9-17.0 s). In every run the cyclops first idles near its spawn for 10-12 s (0-7 wandering steps), then chases along exactly the router's path, north through the gap at x 1948 and west along y 1338: 9, 9, 9 and 8 steps from where the chase starts (predicted 9, 9, 9, 8; one run's start tile was not sampled). Greedy steps would never arrive: the router holds. Two new gaps: chase steps on row 1338 take about 455 ms against about 705 ms elsewhere (per-tile ground speed; the model uses one ground for the area), and monsters do not chase in the first ~10 s after the knight logs in |
### Pre-registration PR-4 (written 2026-10-14 10:39, before any PR-4 run; replaces the void PR-1)

| Item | Value |
| --- | --- |
| Questions | Primary: does the retreat policy use fewer potions than vBot? Secondary, named now: does retreat use fewer potions than our rules policy; does the simulator predict each Spiral policy's live clear? |
| Arena | Fixed 2026-10-14: the scenario writes the client's bot setting per competitor (disabled for `spiral-*`) and the policy probe disables the bot before login; every `spiral-*` row records foreign potions (server potions minus the policy's heal notes) and foreign actions (parity-gated actions minus the policy's own). One check run before PR-4: 14 potions, 14 heal notes, 0 foreign actions |
| Competitors | `spiral-policy` (rules, heal below 160, CurrentFirst), `spiral-retreat` (heal below 160, step back at 2 attackers, ReachFirst), `vbot` (as in PR-1); policy sources byte-identical to PR-1's (live.spi and policy.spi frozen) |
| Scenario | Pack v1 cyclops clear, as in PR-1 |
| Runs | 30 per competitor, alternating in that order, one block (`run-arena.ps1 -Runs 30 -Competitors spiral-policy,spiral-retreat,vbot`, results in `results/rows/arena_results_cyclops_pr4.spi`). A `spiral-*` row with foreign potions or foreign actions above 0 is a arena failure: excluded and listed, not rerun. A crash is rerun once, then missing. nExBot crashed the client in both PR-3 check runs (exit 0xC0000374 after login) and is not in this block |
| Primary test | Potions per run, retreat vs vBot, Welch two-sided, alpha 0.05, with the 95% interval (`pr1-analysis.ps1 -Baseline vbot -Planned 30 -Label PR-4`) |
| Secondary tests | Retreat vs rules potions, Welch (`pr1-analysis.ps1 -Treatment spiral-retreat -Baseline spiral-policy`); each Spiral policy against the simulator's current held-out prediction on the arena spot (100 clears, seeds 1001-1100): rules 13.23 potions (sd 2.52), 88.58 s (sd 12.63); retreat 9.09 potions (sd 1.83), 88.10 s (sd 12.86); no deaths (`pr2-analysis.ps1` one-sample t per metric) |
| Decision | As in PR-1 for each Welch test; a prediction holds when both one-sample tests are within the critical value. No runs are added after the block |
| Interruption (2026-10-14 10:58) | The host's memory reaper stopped the block after 2 complete triples (6 rows; a test suite was running alongside). It resumed with `-Runs 28` into the same file, nothing else running on this lane; only row counts and the arena columns were read |
| Frozen policy (2026-10-14 13:57) | A compiler install into the shared toolchain was announced mid-block, so from row 71 the scenario copies a frozen `policy.lua` instead of compiling `live.spi` per run; the file is byte-identical to the Lua every earlier run compiled (SHA-256 7FD3D538...93E5, printed in each run's log) |
| RESULT (2026-10-14 14:52) | 90 rows, 0 arena failures, 0 deaths, every run cleared. Primary: retreat 12.90 potions (sd 3.24) vs vBot 14.00 (sd 2.61), difference -1.10 (95% CI -2.62 .. 0.42), Welch t -1.45: no difference shown. Retreat vs rules: 12.90 vs 11.53 (sd 2.52), difference +1.37 (CI -0.13 .. 2.87), t 1.82: no difference shown, and the direction is against retreat. Predictions: rules clear time holds (87.6 vs 88.6 s, t -0.43) but potions do not (11.53 vs 13.23, t -3.69); retreat clear time holds (91.3 vs 88.1 s, t 1.33) but potions do not (12.90 vs 9.09, t 6.44). The simulator's retreat advantage does not exist live; its potion model is off in both directions (too many for rules, far too few for retreat). Descriptive: damage taken 2217 retreat, 2029 rules, 2397 vBot; clear time 91.3, 87.6, 92.7 s |
### Pre-registration PR-5, live random control (written 2026-10-14 10:46, before any run)

| Item | Value |
| --- | --- |
| Question | Where does chance play sit live, and does the simulator's random control predict it? |
| Competitor | `random-control` (`probe-random-body.lua`): every 100 ms it drinks a potion, engages the nearest monster (chase), steps in one of 8 directions, or stands, with equal odds; the same parity gate and arena checks as the Spiral competitors |
| Prediction | `hunting_pack.random_control` on the arena spot, 100 held-out clears capped at 200 s like the live timeout: 99 of 100 clear, 117.0 s per clear, 90.5 potions per run (sd 17.8), no deaths. The simulator's random control was corrected at 2026-10-14 10:46 to respect the 1 s potion cooldown before this prediction |
| Runs | 10, one block after PR-2 and PC-1 (`run-arena.ps1 -Runs 10 -Competitors random-control`, results in `results/rows/arena_results_cyclops_pr5.spi`); rows with foreign activity excluded as in PR-4 |
| Test | `pr2-analysis.ps1 -Competitor random-control -PredictedPotions 90.5 -PredictedSeconds 117.0 -Planned 10 -Label PR-5 -ExcludeForeign` (one-sample t, critical 2.26 at df 9) |
| Decision | Descriptive: the control's potions and clear time next to PR-4's competitors. Prediction holds when both one-sample tests are within the critical value |
| RESULT (2026-10-14 16:31) | 10 runs, 0 arena failures, 0 deaths, every run cleared. Potions 94.1 per clear (sd 7.5) against predicted 90.5 (t 1.53, holds); clear time 102.7 s (sd 12.0) against predicted 117.0 (t -3.75, gap). Next to PR-4 (11.5-14.0 potions) the control is far below every competitor. Likely cause of the time gap: live, once the knight attacks, the server keeps auto-attacking whatever the policy picks next, while the simulated control strikes only on the quarter of ticks where it picks engage. Addendum (2026-10-14 19:36, explanatory, after the result): the simulated control now keeps swinging once in reach; with the 10 s wake it predicts 103.4 s (live 102.7) and 81.1 potions (live 94.1), and the remaining potion gap points at the potion gate (TIBIA-POTION-FIDELITY) |
### Pre-registration PR-6, out-of-sample check of the monster wake delay (written 2026-10-14 17:13, before any run)

| Item | Value |
| --- | --- |
| Question | Does the simulator with the 10 s monster wake (`live_wake_ms`, a value taken from the PC-1 path check, not fitted to any clear) predict a clear it has never been compared with? |
| Scenario | Pack v1 with two cyclops instead of three (`/m cyclops,2,,2` by the GM at 1932,1352), end at 450 experience or 200 s |
| Competitor | `spiral-policy` (rules, heal below 160, CurrentFirst), frozen PR-4 Lua |
| Prediction (wake model) | `uniform_camp Cyclops` with size 2 on the arena spot, `waking_at (live_wake_ms ())`, 100 held-out clears: 8.25 potions (sd 2.42), 59.34 s (sd 10.09), no deaths |
| Alternative (no wake) | Same without the delay: 10.14 potions (sd 2.31), 58.80 s |
| Runs | 20, foreground chunks into `results/rows/arena_results_cyclops2_pr6.spi`; foreign-activity rows excluded |
| Test | `pr2-analysis.ps1 -PredictedPotions 8.25 -PredictedSeconds 59.34 -Planned 20 -Label PR-6 -ExcludeForeign`; the same for the no-wake numbers, reported alongside |
| Decision | The wake model holds when both its one-sample tests are within the critical value (2.09); the no-wake model's potion test is reported to show whether the data separate the two |
| RESULT (2026-10-14 18:00) | 20 runs, 0 arena failures, 0 deaths, every run cleared. Clear time holds (62.5 s vs 59.34, t 1.39). Potions fail for both models: live 4.85 (sd 1.27) vs 8.25 with the wake (t -12.0) and 10.14 without (t -18.7). Live the knight takes 30.7 hits per clear (32.9 damage each) against 43.4 simulated: the simulator overestimates how often adjacent cyclops land hits; with three cyclops (PR-4) that error was offset, so the wake delay alone does not explain the potion gap |
### Pre-registration PC-2, road speed check (written 2026-10-14 18:50, before any run)

| Item | Value |
| --- | --- |
| Question | Does the per-tile ground speed added after PC-1 predict a cyclops chase it was not fitted on? |
| Setup | Knight standing at 1943,1338 (simulator 110,88), bot disabled, not attacking; one cyclops summoned next to 1952,1338 (simulator 119,88), 9 tiles east on the same road (every tile of row 1338 from 1943 to 1954 is ground speed 100) |
| Metric | Mean duration of the cyclops's consecutive westward steps along row 1338 during its approach (`path t=` lines of `probe-path-body.lua`), pooled over runs |
| Prediction | `terrain.on_ground_at ArenaHunt` from a road tile: 438 ms per step. Alternative (one ground speed for the map): 702 ms |
| Runs | 5 (`run-policy-scenario.ps1 -ProbeBody probe-path-body.lua -Monster cyclops -MonsterCount 1 -SpawnRadius 0 -SummonAt 1952,1338 -KnightAt 1943,1338`) |
| Decision | The road model holds when the pooled mean is within 10% of 438 ms (394-482 ms) |
| RESULT (2026-10-14 18:58) | Holds. Pre-registered metric (westward steps on row 1338): 6 steps from run 1, mean 456 ms (453-459). In runs 2, 3 and 5 the summon placed the cyclops at 1950,1337 and it approached along row 1337, also road (speed 100); counting those, 24 road steps, mean 452 ms. Run 4 saw no monster in 30 s (summon failure, excluded). The uniform-ground alternative (702 ms) is far off; the model's 438 ms is 3-4% fast (`results/path-check-pc2.md`) |
### Pre-registration PR-7, out-of-sample check of the occupancy-aware router (written 2026-10-14 23:29, before any run)

| Item | Value |
| --- | --- |
| Question | Does the simulator with the occupancy-aware router on the arena spot (`terrain.weighted_step`: Canary-style weights, straight 10, diagonal 25, other creatures as obstacles; adopted after PR-6 because live cyclops surround the knight and simulated ones queued behind each other) predict a clear it has never been compared with? |
| Scenario | Pack v1 with two cyclops (`/m cyclops,2,,2` by the GM at 1932,1352), end at 450 experience or 200 s |
| Competitor | `spiral-retreat` (heal below 160, step back at 2 attackers, ReachFirst), frozen PR-4 Lua (SHA-256 7FD3D538...93E5, copied, no compile) |
| Prediction (adopted model) | camp size 2 on the arena spot, `waking_at (live_wake_ms ())`, weighted router, 100 held-out clears: 4.67 potions per clear (sd 1.73), 59.70 s (sd 9.10), no deaths |
| Alternative (greedy steps on the spot, otherwise identical) | 3.49 potions (sd 1.42), 58.08 s |
| Runs | 20, foreground chunks into `results/rows/arena_results_cyclops2_pr7.spi`; foreign-activity rows excluded |
| Test | `pr2-analysis.ps1 -Competitor spiral-retreat -PredictedPotions 4.67 -PredictedSeconds 59.70 -Planned 20 -Label PR-7 -ExcludeForeign`; the alternative's numbers reported alongside |
| Decision | The adopted model holds when both its one-sample tests are within the critical value (2.09) |
| RESULT (2026-10-15 00:15) | Holds, narrowly. 20 runs, 0 arena failures, 0 deaths, every run cleared. Potions: live 5.45 per clear (sd 1.79) vs predicted 4.67, t 1.95 (critical 2.09); clear time 62.6 s vs 59.7, t 1.23. The greedy alternative fails (3.49 predicted, t 4.89). First out-of-sample potion prediction that holds; the live mean is still 0.8 potions above it |
### Pre-registration PC-3, a knight step delays the next swing (written 2026-10-15 06:24, before any PC-3 run)

| Item | Value |
| --- | --- |
| Question | Does a knight step make its next swing wait for the next 1 s think, as Canary's source says (`classicAttackSpeed = false`: the next swing is the player's next action task, `Player::onWalk` clears it, `Game::checkCreatures` calls `onAttacking` every 1000 ms) and as the opt-in camp rule `swinging_on_think` models? |
| Instrumentation | `probe-policy-body.lua` now stamps server texts and each retreat step with ms since the policy start (`@t+`); one smoke run (06:23, `results/rows/arena_results_pc3_smoke.spi`) checked the format and exercised the analysis; it is not PC-3 data |
| Runs | 5 `spiral-retreat` runs, cyclops clear as in PR-3a, into `results/rows/arena_results_pc3.spi` |
| Analysis | `analysis/pc3-analysis.ps1 -Logs <the 5 logs>`: consecutive landed knight hits (`due to your attack`), pairs with a kill between them excluded; residual = gap mod 2000 ms. Pairs with no step between them are the baseline; pairs with a knight step between them are the test. Missed swings are invisible but stay on the grid |
| Prediction | Baseline pairs on the 2000 ms grid (+-60 ms) at 90% or more; step-spanning pairs late by 60-1060 ms in at least half |
| Alternative (no rule) | Step-spanning pairs on the grid at about the baseline rate |
| Decision | The rule holds when both predictions hold; it fails when 80% or more of step-spanning pairs are on the grid. Known confound, reported not corrected: a target switch without a kill can also move the grid |
| RESULT (2026-10-15 06:37) | Holds. 5 runs (one did not clear: it retreated out of the fight, 23 retreats, 0 kills, the failure mode seen once in v6; its hits count). Baseline: 101 of 106 pairs on the grid (95.3%). Step-spanning: 31 of 36 late by 60-1060 ms (86.1%), 2 on the grid, 3 other. The delays cluster at 150-200 ms (median about 180 ms), not evenly over the think second: the swing mostly resumes sooner, likely through `hasExtraSwing` when a following cyclops moves next to the knight (`Creature::onCreatureMove`). The model's phase-0 think (about 500 ms on average) is the right direction and probably too long |
### Pre-registration PR-12, retreat vs nExBot on the fixed arena (written 2026-10-15 18:52, not yet run)

| Item | Value |
| --- | --- |
| Question | With login protection ended alike for both, does the retreat policy use fewer potions than nExBot in the cyclops clear? |
| Why | PR-3a found retreat 2.35 potions under nExBot (t -2.14), but about half of that is the login-protection artifact (nExBot talks after login); after the first 10 s nExBot still took 4.7 more hits per clear, about one potion, untested |
| Arena | As PR-11 (`parity.lua` ends the protection for every competitor at login) plus the nExBot shims of PR-3a; the policy probe skips floor-change tiles when it retreats (since 2026-10-15 12:45) |
| Competitors | `spiral-retreat` (heal below 160, retreat at 2, ReachFirst; frozen PR-4 Lua or live.spi if its compile is byte-identical, SHA-256 7FD3D538...93E5) vs `nexbot` (as in PR-3a) |
| Scenario | Pack v1 cyclops clear, as in PR-11 |
| Runs | 40 per competitor, alternating, foreground chunks into `results/rows/arena_results_cyclops_pr12.spi`; foreign-activity rows excluded; a crash is rerun once, then missing. With sd about 3 this detects about 1.9 potions at 80% power |
| Test | Potions per run, Welch two-sided, alpha 0.05, 95% interval (`pr1-analysis.ps1 -Treatment spiral-retreat -Baseline nexbot -Planned 40 -Label PR-12 -ExcludeForeign`) |
| Expectation, stated now | A smaller difference than PR-3a, likely not significant at this size |
| Secondary (descriptive only) | Clear time, deaths, damage taken, hits in the first 10 s, floor changes (any `,6` position) |
| Decision | Significant and negative: retreat uses fewer potions than nExBot. Significant and positive: nExBot uses fewer. Otherwise: no difference shown. No runs are added; no interim looks at potions |
| Amendment (2026-10-18 15:29, before any PR-12 run) | Both competitors start at login + 2.5 s (TIBIA-START-SYNC: the client bot is kept disabled at login and enabled by the probe at the same moment the policy starts; the observer's clock starts there too). The runs go in chunks across days into the same TSV; chunk boundaries are logged; still no interim looks at potions |
| Chunk 1 (2026-10-18 15:42-16:39) | 10 `spiral-retreat` + 10 `nexbot`, alternating, TSV rows 1-20; 30 per competitor to go |
| Before chunk 2 (checked 2026-10-20 01:45, no PR-12 outcome looked at) | Since chunk 1 the arena sources changed only by the once-per-second `seen at` trace (commit 24343e9: `client.monsters_described`, `scoreboard.due_for_trace`, one `probe.note` in competitor.spi; no actions), and the compiler moved to core v1 (2026-10-19 11:14). competitor.spi compiled today differs from the 2026-10-19 06:03 Lua only in generated form: `a == b` then `~= true` is now `a ~= b`, locals are renumbered, and the top-level closure captures 21 values instead of 14; after collapsing that pattern and the numbering the two files are otherwise identical. Same decisions, so the chunks continue |
| Chunk 2 (2026-10-20 06:18-07:04) | 10 `spiral-retreat` + 10 `nexbot`, alternating, TSV rows 21-40; 20 per competitor to go. `spiral-retreat` run 10 (07:02) ended without a clear (experience 0, 823 actions dropped); whether it counts as a crash to rerun is decided by the crash rule before chunk 3, without looking at potions |
| Crash rule for that run (decided 2026-10-20 07:10, potions not compared) | Not a crash: the client stayed up and the observer wrote a full row (no death, lowest health 139), so the policy failed to clear in time. `pr1-analysis.ps1` already counts uncleared rows (cleared n/N, clear time over cleared rows only), and a rerun would drop a real policy outcome. The row stays; no rerun |
| Chunk 3 (2026-10-20 07:52-) | Pairs 1-5 written 07:54-08:17 (TSV rows 41-50). At about 08:18 Claude Code's low-memory reaper stopped the block during `spiral-retreat` run 6, which wrote no row (not a crash of the run; the machine ran short while another lane compiled). Resumed 08:19 with `-Runs 5` for pairs 6-10 (their `run` column restarts at 1), then chunk 4 |
| Chunk 3 end and chunk 4 (2026-10-20 08:19-09:25) | Pairs 6-10 of chunk 3 at 08:21-08:41 (TSV rows 51-60), chunk 4 at 08:41-09:25 (10 + 10, rows 61-80); 40 per competitor, complete. No other lane compiled during the resumed block |
| RESULT (2026-10-20 09:27) | No difference shown. 80 runs, 0 foreign-activity rows. Potions per run: retreat 12.75 (sd 2.89) vs nExBot 13.93 (sd 2.70); difference -1.18, 95% interval -2.42 .. 0.07, Welch t -1.88 (df 77.7, critical 1.99). As expected: smaller than PR-3a's 2.35 and short of significance at this size |
| Secondary (descriptive only) | Clear time 90.3 vs 89.2 s; deaths 0 vs 0; cleared 39/40 vs 40/40 (the chunk 2 row); lowest health 101.5 vs 93.8; damage taken 2,230 vs 2,369 (logs matched 37 and 40, difference -139, interval -306 .. 29); hits in the first 10 s after the start 6.18 vs 9.75 and after it 55.15 vs 57.65 per run; floor changes 0 vs 0 (`analysis/pr12-secondary.ps1`, 40 logs each). Actions dropped by the parity gate: retreat 1,144 (823 of them in the uncleared run), nExBot 4 |
| Post-hoc model check (2026-10-20 09:27, explanatory) | The live-mirroring model on this arena (`mirroring_the_live_arena \|> waking_at 0`, `live_retreat_tactic 160 2`, 100 held-out clears, `lab/tibia-potion-fidelity/parity_arena_probe.spi`) predicts retreat 13.97 potions, 65.4 landed hits, 87.5 s; live 12.75 (se 0.46, t about 2.7), 61.3 hits, 90.3 s. The same model gives rules 15.29 against PR-11's 14.42. On the arena without login protection the model runs high for both policies (+0.9 rules, +1.2 retreat), here through hit count, not damage per hit (36.4 both). Without the uncleared row (the knight stood in the protection zone east of the spot for 121 of 148 samples, 18 hits, 2 potions; TIBIA-RETREAT-TARGET-SPREAD) live retreat is 13.03 potions (sd 2.33, n 39), still 0.94 under the model (t -2.5). The opening is not where the hits go: the model takes 5.16 hits in its first 10 s (`parity_opening_probe.spi`) against 6.18 live after the policy starts, and 60.2 after against about 56 live. Crowding accounts for it (2026-10-20 11:25): seconds per clear with 0/1/2/3 cyclopes adjacent are 4.4/42.5/27.9/15.4 live (the once-per-second `seen at` samples of the 29 cleared chunk 2-4 retreat runs, `lab/tibia-potion-fidelity/trace-adjacency.ps1`) against 0.3/40.1/25.5/21.5 in the model (`adjacency_probe.spi` with `live_retreat_tactic 160 2`); at the model's own hit rate per adjacency level, the live time mix gives about 4 fewer hits per clear, the whole 61.3 vs 65.4 gap. The model's retreating knight is surrounded by all three about 6 s longer and almost never stands free |



### Pre-registration PC-4, the level-30 knight's straight step on arena ground (written 2026-10-20 02:10, before any PC-4 run)

| Item | Value |
| --- | --- |
| Question | How long does a level-30 knight's straight step take on arena ground when it steps again the moment the last step ends? The model uses 650 ms on grass (702 on arena ground) for every knight step; Canary's formula for speed 139 (level 30) gives 450 (486); chase-mode traces of 2026-10-19 show 2.03 tiles/s with the target 4 tiles away, which only the faster step allows (TIBIA-COMBAT-FIDELITY) |
| Probe | `apps/arena/steps.spi` with `TIBIA_STEPS_KNIGHT_WALK=1`: from the spot start (1932,1349) the knight walks 5 tiles west and 5 back along y 1349 (x 1927-1932, all arena ground, no road), polling every 50 ms and stepping as soon as `player_walking` is false; every knight step's interval is logged (`knight-straight`) |
| Scenario | `run-policy-scenario.ps1 -ProbeBody steps -Level 30 -AxeSkill 60 -Monster cyclops -MonsterCount 1 -SummonAt 1960,1343` (the cyclops spawns 28 tiles east, beyond monster sight, so nothing chases or blocks the knight), about 45 s of walking |
| Runs | 2, back to back, after PR-12 is complete; a crash is rerun once |
| Amendment (2026-10-20 07:23, before any PC-4 run) | `-SummonAt 1960,1358` instead of 1960,1343: the map tool's new protection-zone listing shows 1960,1343 inside the protection zone east of the spot (x 1954-1973, y 1330-1355), where Canary may refuse to place a monster. 1960,1358 is walkable, outside every protection zone, and still 28 tiles from the knight's walk |
| Amendment (2026-10-20 09:50, no interval measured yet) | Three attempts at 09:39-09:48 (two planned runs and one diagnostic) logged no knight step, so none counts as a run. Cause: OTClient's battle module connects `onPositionChange` on the `LocalPlayer` class (`game_battle/battle.lua:1359`), and that field shadows the probe's `Creature` handler for the knight, so only monster steps ever reached the probe (no `knight-` step appears in any earlier steps log either). `client.on_position_change` now connects the handler on both classes. The probe also notes when the walk starts, every step the client refuses, and every 2 s spent waiting on `player_walking`. Descriptive, seen in all three attempts: the client shows speed 0 at the login note and 139 two seconds later when the walk starts, which is the level-30 formula speed, not the level-8 base of 117. The two runs are taken again in the next live slot with the same scenario and decision |
| RESULT (2026-10-20 11:03, runs 11:00-11:03) | Neither band, as pre-registered: median 558 ms over 122 straight intervals outside turns (32 excluded), range 537-566, 12 ms above the formula band and 144 ms below the level-8 base. Both runs logged 77 knight steps, no refused step, client speed 139 at walk start. Explanatory, post-hoc: the intervals sit in two tight clusters (540-549 ms: 60, 560-569 ms: 64 of 154), 51-80 ms above Canary's 486 ms for speed 139 on arena ground and nowhere near 702, so the server steps the level-30 knight at the formula speed, and a client-issued step adds the probe's 50 ms poll plus the client's own walk lag. The band's 60 ms slack was too tight for that lag; the model's 650 ms knight step (702 on arena ground) is too slow either way (TIBIA-COMBAT-FIDELITY) |
| Measure | Median of the knight-straight intervals that do not span a turn (the first interval of each leg is excluded), by `analysis/pc4-analysis.ps1 -Logs <the 2 *-pc4-pc4-run-<n>.log logs>` |
| Decision | Median within 486 + 60 ms: the server steps a level-30 knight at the formula speed, and client-issued steps cost the probe's decision loop, not the server. Median within 702 +- 30 ms: the knight's server speed is the level-8 base (client speed 117), and the fast chase pace needs another cause. Anything else: neither, report the value. Descriptive: the speed the client shows at login (`in game as ... speed N`) |

### Pre-registration PC-5, the model with protection zones (written 2026-10-20 07:26, before the model change is run)

| Item | Value |
| --- | --- |
| Question | Does the server's protection zone, missing from the model, explain the hunter clear-rate gap (87 of 100 predicted, 16 of 25 live)? |
| Why | Canary keeps monsters off protection-zone tiles and refuses every attack from them. The map's flag bit (now read by `apps/map`) marks 276 walkable tiles within radius 45 in two buildings east of the spot (x 1940-1947 and 1954-1978); the model lets every creature walk through them, and live retreats that ended on them never cleared (TIBIA-RETREAT-TARGET-SPREAD) |
| Change | One server fact, no free parameter: the generated `arena_protection_zone` blocks walking for monsters and the knight at both arena layouts; sight lines are unchanged. The live data was seen before this change, so this is a post-hoc check of a fact, not a blind prediction |
| Measure | The hunter prediction as before (100 clears, the PR-14 setup with radius 45 and road speed) |
| Expectation, stated 2026-10-20 07:49 | Little or no change, so above 80: in the saved first-90-s model traces (5 variants, 20 runs each, 1800 samples each) neither the knight nor any hunter ever stands on a protection-zone tile, and the 25 live hunter logs show none either (`analysis/pz-in-traces.ps1`) |
| Decision | Clears within 45-80 of 100 (the Wilson 95% interval of 16 of 25): the gap is explained within sampling. Above 80: the gap stays open and the protection zone is not its cause. Below 45: the model overshoots; report it. Whatever the result, the change stays, because it is a server fact |
| RESULT (2026-10-20 09:55) | Above 80: the gap stays open, and the protection zone is not its cause. `hunter_pr14_prediction_probe.spi` on the tree with zones (`lab/tibia-fidelity/hunter-prediction-r45-road-pz.txt`): 87 of 100 clear (before: 87), potions 2.10 per clear (2.08), cleared in 60.3 s on average (60.0), hits 14.8 (14.7), no deaths. The small shifts are likely routes that now bend around a zone tile between two once-per-second trace samples (not checked); the clear count matches the trace check |
| Scenario correction (2026-10-20 22:29, post-hoc) | The prediction above, like PR-14's, used `mirroring_the_live_arena`, which freezes monsters for the 10 s login wake; the hunter runs happened on the arena where the parity layer ends the protection at login. With the wake removed (`mirroring_the_current_arena`) the model predicts 45 of 100 clears, inside the Wilson band (45-80), so the hunter gap is a scenario error far more than a missing mechanic; the zone rule itself changes nothing either way (TIBIA-FIDELITY) |

### Pre-registration PR-16, the current-arena model on fresh clears (written 2026-10-20 22:31, before any PR-16 run)

| Item | Value |
| --- | --- |
| Question | Does the model chosen overnight for the arena as it runs (`mirroring_the_current_arena`: protection ended at login, monsters re-path only on their think and swing only on Canary's attack rounds) predict potions per clear for the rules and the retreat policy? |
| Why | Chosen by the pre-written rule D1 (TIBIA note 12) on in-sample data: it cut the score over PR-11, PR-12 and PR-15 from 36.9 to 12.6. Fresh runs test it out of sample. The protected-arena bundle is not under test |
| Setup | Pack v1 cyclops clear as PR-12 (`run-arena.ps1 -Monster cyclops -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -RetreatAt 2`), competitors `spiral-policy` and `spiral-retreat` alternating, 20 runs each, into `results/rows/arena_results_cyclops_pr16.spi`; the knight's shielding is now reset to 11 before every run (it drifted to 12 before) |
| Prediction (100 held-out clears, shield 11; `lab/tibia-potion-fidelity/current_arena_probe.spi`) | Rules 14.63 potions per clear (sd 2.86), 90.1 s, 69.5 hits; retreat 13.74 (sd 3.17), 94.7 s, 65.2 hits |
| Test | One-sample t per arm on potions against its prediction, two-sided, critical 2.09 (19 df), computed by `eoie experiment analyze target/ledger PR-16-RULES` and `PR-16-RETREAT` (registered 2026-10-21T02:30Z with `--time timestamp`, so rows before registration cannot count) |
| Expectation, stated now | Rules holds (its in-sample t was 0.45); retreat is the risk: in sample the model ran 0.8-1.0 high at shield 12 (t 1.65), so a t near 2 is possible |
| Decision | Each arm holds when its t is within 2.09. A crash is rerun once, then missing; no runs are added after a look. The block runs only through an eoie live-gui window and is skipped if it has not started by 03:30 (D3) |
| RESULT (2026-10-21 00:46, runs 23:14-00:46, all 40 counted, no crash, no death) | Rules holds, retreat does not. `eoie experiment analyze`: rules 14.35 potions per clear (sd 2.91) against 14.63, t -0.43, 95% CI 12.99-15.71; retreat 12.45 (sd 2.42) against 13.74, t -2.39 (p 0.028), CI 11.32-13.58. Descriptive: clear time rules 90.7 s (predicted 90.1), retreat 87.4 s (94.7); lowest health rules 100 (min 27), retreat 94 (min 40); dropped actions per run rules 1.9, retreat 9.6. The retreat over-count has the same sign and size as in sample (0.8-1.0 high at shield 12, now 1.3 at shield 11), so it is a retreat-specific gap, not the shield drift; the model's retreat also clears 7 s slower than live, so it spends more time stepping and less time striking than the live policy. The bundle stays for the rules policy; retreat predictions carry this bias until a retreat mechanic explains it, and any fix found now is in-sample against PR-16 (TIBIA-POTION-FIDELITY) |
| Post-hoc diagnosis and fix (2026-10-21 00:50-01:29, rules D8 and D9 written before measuring, TIBIA-POTION-FIDELITY note 80) | D8 compared the 60 live retreat logs (PR-12 and PR-16) with 100 model clears (`lab/tibia-potion-fidelity/d8_retreat_probe.spi`). Leads: hits taken 61.3 live vs 65.2 model (Welch t -2.30) and damage 2176 vs 2377 (t -3.02); retreat steps per clear were not a lead (11.7 vs 14.8, t -1.57). Hits per second matched (0.70 vs 0.69), so the extra hits came from the model's 7 s slower clear. Cause: the model's knight stepped at a fixed 650 ms (702 on arena ground). Canary steps a level-30 knight at speed 110 + 29 = 139 (`Player::updateBaseSpeed`, Knight basespeed 110 in `vocations.xml`) through `Creature::updateCalculatedStepSpeed` and `getStepDuration`, which gives 500 ms on arena ground; PC-4 measured 558 ms including client lag. The model already had that formula (`creatures.player_step_ms`) but the knight never used it. The bundle now adds `paced_like_canary`. D9 score (`lab/tibia-potion-fidelity/d9-score.ps1`): retreat squared t over PR-12 and PR-16 fell from 8.42 to 0.04 (retreat 12.53 vs 12.45 live at shield 11, 12.69 vs 12.75 for the PR-12 shield mix); rules unchanged at 14.63 (t 0.45 and 0.43). Adopted, in sample. Open: the model's retreat now takes 21.0 steps per clear vs 11.7 live and still clears in 93.1 s vs 87.4 live. Side effects outside D9's scope: hunting-ground kills 11.91 -> 12.73 per hunt (PR-15 live 12.80; potions 14.18 vs 12.70, unchanged), hunter clears 45 -> 30 of 100 (below the Wilson band 45-80 of 16 of 25), so the hunter match was partly two errors cancelling. A fresh block (PR-17) is the out-of-sample test |
| Post-hoc, the retreat-step gap (2026-10-21 01:29-02:08, rule D12 written before measuring, TIBIA-POTION-FIDELITY notes 82-83) | Live, the retreating knight gets cornered in the pocket at 1936,1349 in 41 of 60 runs (blocked south row and 1937,1348, a cyclops on 1937,1349), and only 11 of 60 ever pass x 1945; the model escaped east in about half its clears. The model's terrain matches every tile the client reports blocked. Adopted by D12: Canary's dance step (`Monster::doFollowCreature` -> `getDanceStep`; `staticAttackChance` 90 for cyclops, troll and hunter, so 10% per think when the path offers no step; an orthogonal step that keeps the distance, at double cost while the target is near), bundle builder `dancing_like_canary`. Retreat 12.87 at shield 11 (squared t over PR-12 and PR-16 0.80, within the cap 2.04), rules 14.62, steps per clear 20.6 (|t| 4.69 -> 4.27). Side effects: hunter clears 30 -> 51 of 100 (inside 45-80 again), hunting-ground kills 12.70 (live 12.80). Rejected by D12: Canary's step formula on arena ground (cyclops 700 ms instead of 702, which the 100 ms tick turns into 800; hunter 650 instead of 700), because steps rose to 27.4. One traced clear shows the knight winning a same-tick race into 1937,1349 that a cyclops wins live; the step gap stays open |
| Post-hoc, the race into the pocket (2026-10-21 02:08-02:15, D12 and the search cap D14, TIBIA-POTION-FIDELITY notes 84-85) | Rejected by D12: the knight acting before the foes each tick (`seeing_a_tick_late`: the probe polls every 100 ms and can act only on positions the client already has, so a monster step in the same server instant is unseen); steps fell to 16.1, but the retreat squared t reached 2.11, over the cap 2.04. D14 then allowed one last candidate, both cited changes together: the last-tick view plus Canary's step formula on the server map (`stepping_by_canary_formula`: `creatures.step_ms_from_canary_formula` with ground speed 160, 100 on the road). Retreat 12.81 at shield 11 and 12.64 for the PR-12 mix (t -0.23 and 0.67, squared t 0.50), rules 14.55 (t 0.28 and 0.31), steps per clear 12.4 against 11.7 live, hits taken 62.3 against 61.3. Adopted into `mirroring_the_current_arena`, in sample; the D12 search is closed for the night, and PR-17 tests the bundle on fresh runs. Side effects outside D14's scope: hunter clears 51 -> 99 of 100, above the band 45-80 (the last-tick view alone: 59 without it, 99 without the step formula; traced in lab: any knight aim or action that runs before the hunters' move in the same tick pre-empts their flee step (aim alone first: 100; actions first with the old target: 99; both after: 59), so it conflicts with the retreat-step fix, which needs the knight's movement first; both point to sub-tick event order, TIBIA-POTION-FIDELITY notes 100-107), hunting ground 12.94 kills and 14.20 potions per hunt (live 12.80 and 12.70) |

### Pre-registration PC-10, what happens at a free think that does not move (written 2026-10-23 07:4x, before any PC-10 run)

| Item | Value |
| --- | --- |
| Question | In PC-8, 3 of about 21 thinks of the walled-in monster with 1931,1352 free were followed by no move (TIBIA-EVENT-TIME note 123). Are those races for the tile or true misses? Rule L8 (note 125) |
| Scenario | PC-8's 10 hunter runs and 10 cyclops retreat runs, the same arguments plus `-ServerProbe live/server-probes/lead_trace.lua`, which now also logs, at each think of the walled-in monster, who holds 1931,1352, and 750 ms later whether the monster moved and who holds the tile. Results go to `results/rows/arena_results_hunter_pc10.spi` and `results/rows/arena_results_cyclops_pc10.spi`. Runs in a live window after main's Cells pack. A crash is rerun once, then counted missing |
| Prediction | None numeric (diagnostic). The model retries on every think, so it has no misses |
| Reading | Per think with the tile free: moved, race (tile taken at the check, monster unmoved) or true miss (tile free at the check, monster unmoved). (a) True misses in 2 or more runs: the source search resumes in the walk-event path with these timings, and a model candidate needs a named source line. (b) Every miss is a race: the thread closes (the model already handles occupancy). (c) No miss: report and close |
| RESULT, hunter half (2026-10-23 18:27) | (a) by L8. 10 hunter runs 18:04-18:27, all cleared, probe trace `arena-logs/20261023-182655-hunter-lead-trace.log` split into its 10 runs at the start-tile sightings (1932,1349; the knight leaving and re-entering probe range logs 8 extra mid-run sightings). Free thinks 21, races 2, true misses 7 in 6 of 10 runs (2, 4, 5, 7, 9, 10), by the counts of TIBIA-EVENT-TIME note 126. The misses are skipped steps, not late ones: the next free think also gives no move until the knight moves (run 2: knight 3274 ms, hunter 3462 ms). The boxed hunter moved before the policy start in 4 of 10 runs, exactly the 4 without a true miss. Source (EVENT-TIME note 130): getRandomStep tries all four directions, so the misses come from follow mode; hunter.lua targetDistance 4 equals the hunter's distance to the knight's start tile, so it holds until the knight moves. Candidate H1 is pre-registered under rule L10 (note 131); rejected 2026-10-23 23:1x by L10 (c), 0 of 100 boxed movers in the model (note 135). Boosted creature: Poison Spider |
| RESULT, cyclops half (2026-10-23 18:55) | Descriptive under L8, whose verdict the hunter half already decided. 10 cyclops retreat runs 18:29-18:55, all cleared, trace `arena-logs/20261023-185445-cyclops-lead-trace.log` split the same way (11 sightings, 10 runs). Free thinks 2, races 0, true misses 1 (run 8). The boxed cyclops moved before the policy start in 9 of 10 runs (not run 8); last night's PC-8 had it staying in 8 of 10. The contrast with the hunter half fits the source reading: the melee cyclops (targetDistance 1) approaches as soon as its exit frees, while the hunter (targetDistance 4) already holds its distance from the start. Boosted creature: Poison Spider |
| Probe port (2026-10-25) | The hand-written `live/server-probes/lead_trace.lua` this row ran with is retired. Its Spiral port, `apps/canary/lead_trace.spi`, built to `target/canary/lead_trace.lua` by `apps/canary/build.ps1`, writes the same trace lines under the fake-Canary stubs (15 of 15 on the boxed-hunter stub), so later runs pass `-ServerProbe target/canary/lead_trace.lua` (TIBIA-NO-HAND-LUA) |
### Pre-registration PR-28, two knights with the ally's measured entry delay in the model (written 2026-10-25 15:3x, before any PR-28 run)

| Item | Value |
| --- | --- |
| Question | PR-27 failed its harness acceptance (ally login within 1000 ms in 4 of 10; median 1155 ms), and rule P11 (TIBIA-PARTY note 88) moved the remaining gap into the model rather than the harness: the model's ally now enters after a delay drawn per held-out seed from PR-27's 10 measured delays (746 745 618 934 1116 1193 1318 2346 1847 1520 ms; `with_delayed_ally_entry`, src/hunting_pack.spi). PR-27's kill and potion sums were printed by its read script, so they are seen and excluded from this test; only its login delays (harness facts) feed the model. Does the duo model with modelled entry predict fresh duo hunts? |
| Scenario | PR-27's arguments and harness unchanged (rule P10's login gate), through `live/block.ps1 -Runs 11 -Results results/rows/arena_results_hunting_ground_pr28.spi`: run 1 is the smoke hunt (not counted; void if its knight log has no `ally login line` delay), runs 2-11 the 10 counted hunts, in one eoie live-gui window. The parity prelude is now `apps/arena/parity.spi` built to `target/arena/parity.lua` (TIBIA-NO-HAND-LUA M4, identical to the retired hand-written prelude under the stub, 16 of 16); this window is its first live run, and each knight log's `ArenaParity.summary()` line is reported. A crash is rerun once, then counted missing; no runs are added after a look. Valid only without a boosted Troll or Cyclops |
| Acceptance | Descriptive only (P11 (4)): the 10 counted login delays are reported against PR-27's distribution, with no threshold, since the model carries the delay |
| Predictions (adopted bundle with S1, candidate 2 `targeting_like_canary`, `camp.party 2`, `with_delayed_ally_entry`, 100 held-out hunts, job job-1792956566092-tibia-duo-predict-pr28) | 16.73 party kills and 6.68 party potions per hunt (knight 4.88, ally 1.8, ally share 27 percent), 0 deaths. Control, same job: PR-27's arm without the delay reproduces PR-27's registered 16.4 kills and 6.09 potions (reproduced exactly: 16.4 kills and 6.09 potions per hunt, knight 2.83, ally 3.26, ally share 54 percent, 0 deaths) |
| Test | As PR-27: `eoie table logs` over the 20 logs with `--field run` and the kills and potions counts, `table query --group run --sum kills,potions`, then `table test --test one-sample --metric sum_kills --arms all --prediction 16.73` and `--metric sum_potions --prediction 6.68`, critical 2.26 (9 df) |
| Decision (P11 (5)) | Both hold: candidate 2 with modelled entry becomes the duo default, and the 10-14 saturation sweep reruns on it. Either fails: the gap is named from the per-knight split on TIBIA-PARTY, with no adoption. Descriptive: per-knight split against the predicted share, login delays, deaths, each knight's lowest health, late troll kills per hunt, the parity summary lines |
| RESULT (2026-10-25 21:1x, 11 duo hunts 20:05-21:03 in one window, smoke hunt run 1 valid with a 1256 ms gate delay, boosted Destroyer) | Both hold, so candidate 2 with `with_delayed_ally_entry` becomes the duo default and the 10-14 saturation sweep reruns on it. Counted hunt 2 crashed without the harness noticing (it ended 81 s after hunt 1, the ally's log ends `finish: offline`, the knight was never hit and took 4 actions), so by the crash rule it is counted missing: no rerun was possible after the look. Valid 9 hunts: 16.78 party kills per hunt against 16.73 (t 0.17, df 8, critical 2.31) and 6.89 potions against 6.68 (t 0.59). As run, with hunt 2 included: 15.5 kills (t -0.94) and 6.4 potions (t -0.48), critical 2.26, so it holds either way. Descriptive: the per-knight split misses the prediction, with knight 3.33 and ally 3.56 potions per hunt (ally share 52 percent against the predicted 27), and kills 8.2 and 8.6. The totals land, but the model gives the ally too few potions; this goes on TIBIA-PARTY. Login delays in the 9 valid hunts are 803-1119 ms (mean 892), against PR-27's 618-2346. 0 deaths, lowest health 183 (knight) and 159 (ally) on average. Late troll kills per hunt: 1.67 (knight) and 2.33 (ally). Parity prelude (NO-HAND-LUA M4, first live run): a `parity: gap 250 ms, view 15x11` summary line in all 11 knight logs, 4-26 actions allowed and 0-4 dropped per hunt. Read with scratch pr28-read.ps1 (`eoie table logs`, `table query --where 'run>2' --group run --sum kills,potions`, `table test --test one-sample`), tables in `target\pr28-*.tsv` |
| Saturation sweep rerun (rule P12 with amendment A1, run 2026-10-27 19:1x, `apps/predict/sweep.spi`) | On the duo default camp, rules were evolved on training_block (seed 91, 6 generations by 6 offspring from { 160; 0; 4 }, ProfitKeepingKills at the validated arm's training kills) and judged once on held_out_block. Validated arm `cavebot_with_heal 160`: 1673 kills, 0 deaths, profit -17929. Evolved genes { 106; 0; 4 }: 1669 kills, 1 death, profit -29269. The evolved rules fall short of the 5 percent profit bar (63 percent lower), and both kills and deaths are worse, so saturation stands on the live-checked duo model. The validated arm's 16.73 held-out kills per hunt reproduce the PR-28 prediction |

### Pre-registration PR-26, where the late troll clusters come from (written 2026-10-23 04:5x, landed here 07:2x, before any PR-26 run)

| Item | Value |
| --- | --- |
| Question | P5 (TIBIA-PARTY note 67) found the late-troll gap real and in the clusters: 3.85 late troll kills per hunt against 2.94, clusters of 2.74 trolls against 2.06. Respawn timing already follows the source (PARTY note 39: trolls are not blockable, and the death-relative check grid plus 4200 ms matches `canary_respawn_ms`), and the hunting ground's spawn file is the server's only monster file, so no troll walks in from outside its 4 troll spawns. Which trolls make up a live late cluster? |
| Scenario | PR-23's hunting ground, unchanged arguments (`-Runs 10 -Scenario hunting-ground -SpawnFile live/hunting-ground-spawns.xml -KnightAt 1936,1336 -RunMs 300000 -MonsterCount 0 -Monster cyclops -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 1000000 -TimeoutSeconds 420 -Competitors spiral-policy -ServerProbe live/server-probes/ground_trace.lua`), so run-arena.ps1 installs the server probe in `data/scripts` for all 10 hunts and removes it afterwards (appear, move, gone and death per monster within 16 tiles of 1947,1345, plus knight out of view and back, on the server clock from the knight's first sighting). Removed after the block. A crash is rerun once, then counted missing; no hunts are added after a look. Valid only if the logs name neither Troll nor Cyclops as boosted |
| Prediction | None numeric (diagnostic) |
| Classification | Per killed troll in a late cluster (kills after 200 s, a troll kill within 10 s of the previous one, the PARTY note 59 definition): R if it first appeared on its spawn tile after the knight's first sighting and stayed in view until death; F if it went out of view (gone) at least once and came back before death; I if it was present at the knight's first sighting and never left view |
| Decision, rule P6 | (a) F trolls make 30 percent or more of late-cluster kills: the clusters are returners. The model side to read first is how it handles a troll that leaves and comes back (escape reset to a fresh foe at its spawn; F35 kept health only), before any candidate. (b) Else, if R trolls make 70 percent or more, and in at least half the clusters the R trolls' appear times lie within 1500 ms of each other: synchronised respawns from different spawns. The model's per-spawn respawn phases are compared with these appear times before any candidate. (c) Else: report the table, no next step |
| Descriptive | Per cluster: troll ids, spawn tile, appear time, kill time; F trolls' time out of view; the same 4 numbers as PR-23B (late trolls, clusters, potions, kills) to check that the probe does not change the hunt |
| RESULT (2026-10-23 11:2x) | (c), report only, in both readings. All 10 runs: 96 troll deaths, 12 late clusters holding 35 trolls, classes R 30, F 0, I 5 (R 86 percent, F 0 percent), and the respawns appear within 1500 ms in 0 of 12 clusters. The 7 runs with no load beside them (1 and 5-10; eoie's heavy job ran 10:26-10:40 beside runs 2-4, an eoie window-registry bug, TIBIA-PARTY note 71): 10 clusters, 30 trolls, R 25, F 0, I 5 (R 83 percent), synced 0 of 10. Read by scratch read-ground.ps1 on the block's one kept trace (`arena-logs/20261023-111503-hunting-ground-ground-trace.log`), split into its 10 run sections at each run's first pre-sighting line (canary restarts per run, so creature ids repeat across sections). No troll left view and came back. The late clusters are respawns from different spawns that appear seconds to tens of seconds apart (for example 256.4 s, 259.4 s and 288.7 s) and die together, so they gather on the way to the knight; they do not respawn in sync. Cross-check: trace troll deaths per run equal the client's `Loot of a troll` count in 9 of 10 runs (run 9: 12 vs 11). Late troll deaths 3.9 per hunt (P5's live 3.85). Valid: the boosted creature was Poison Spider |
| Probe port (2026-10-25) | The hand-written `live/server-probes/ground_trace.lua` this row ran with is retired. Its Spiral port, `apps/canary/ground_trace.spi`, built to `target/canary/ground_trace.lua` by `apps/canary/build.ps1`, writes the same trace lines under the fake-Canary stub (9 of 9), so later runs pass `-ServerProbe target/canary/ground_trace.lua` (TIBIA-NO-HAND-LUA) |

### Pre-registration PR-27, two knights logging in together (written 2026-10-23 23:2x, before any harness change or PR-27 run)

| Item | Value |
| --- | --- |
| Question | PR-25 failed both tests with a lopsided split (ally potion share about 14 percent). The harness had serialized the ally's whole client boot after the knight's login line, so the ally logged in 11-19 s after the knight (mean 14.5 s; PR-24, booted in parallel, mean 1.8 s). Candidate 2 assumes both knights come into view within one think, knight first. With the harness fixed by rule P10 (TIBIA-PARTY note 84), does the duo model predict fresh duo hunts? |
| Scenario | PR-25's arguments unchanged, through `live/block.ps1 -Results results/rows/arena_results_hunting_ground_pr27.spi`, 10 hunts in one eoie live-gui window. The only change is the harness: both clients boot in parallel, and the ally's probe holds its login until the harness sees `Knight Sample has logged in.` in the world log and writes the ally's gate file. Each run log gets a `== login gate` line with the ally's login-line delay in ms. One smoke hunt runs first in the same window to check that the ally's client sees the gate file; it is not counted. Mechanics (added 2026-10-24 00:57, before any PR-27 run): `block.ps1 -Runs 11`, so run 1 is the smoke hunt and runs 2-11 are the 10 counted hunts; if run 1's knight log has no `ally login line` delay, the window's hunts are void and P10 is reworked. A crash is rerun once, then counted missing; no runs are added after a look. Valid only without a boosted Troll or Cyclops |
| Acceptance of the harness (P10) | The ally's login line within 1000 ms after the knight's in at least 8 of 10 hunts, read as `eoie table logs` over the 10 counted knight logs with `--count gated='ally login line \d+ ms after' --last delay='ally login line (\d+) ms after'`, then `table query --where 'gated>0' --where 'delay<=1000' --count file`; a log without the line counts as over 1000 (the `gated>0` filter is needed because an empty cell passes `delay<=1000`, stub-checked 01:0x). Fewer: (H) failed, the tests below are not read, and modelling the gap gets its own rule |
| Predictions (unchanged from PR-25: adopted bundle with S1, candidate 2, `camp.party 2`, 100 held-out hunts, job tibia-p9-duo) | 16.4 party kills and 6.09 party potions per hunt (knight 2.83, ally 3.26, ally share 54 percent), 0 deaths |
| Test | As PR-25: `eoie table logs` over the 20 logs with `--field run` and the kills and potions counts, `table query --group run --sum kills,potions`, then `table test --test one-sample --metric sum_kills --arms all --prediction 16.4` and `--metric sum_potions --prediction 6.09`, critical 2.26 (9 df) |
| Decision | Both hold: candidate 2 becomes the duo default (TIBIA-PARTY), and the 2026-10-14 duo saturation sweep is rerun on it before any party claim. Either fails: the remaining gap is named on TIBIA-PARTY. Descriptive: the per-knight split against the predicted share, the login delays, deaths, each knight's lowest health, late troll kills per hunt |
| RESULT (2026-10-24 03:4x, 11 duo hunts 02:34-03:39 in one window, smoke hunt run 1 valid with an 812 ms gate delay, no crash, boosted Hunter) | (H) failed: the ally's login line came within 1000 ms of the knight's in 4 of the 10 counted hunts (need 8), so by the pre-registration the kill and potion tests are not read, and modelling the remaining gap gets its own rule on TIBIA-PARTY. Delays per counted hunt in ms: 746 745 618 934 1116 1193 1318 2346 1847 1520 (median 1155, mean 1238), against PR-25's 11-19 s and PR-24's -1 to 5 s. The harness change cut the gap about twelvefold, but the delay grew through the block (about 0.7 s in hunts 2-4, 1.1-2.3 s in hunts 6-11). Read with `eoie table logs` on the knight logs (`--count gated`, `--last delay`) and `table query --where 'gated>0' --where 'delay<=1000'` (`target\pr27-gate.tsv`) |

### Pre-registration PR-25, two knights with Canary's target order and respawn grid in the model (written 2026-10-23 15:1x, before any PR-25 run)

| Item | Value |
| --- | --- |
| Question | PR-24 failed rule P1 on both metrics, and the named gap was who the cyclopses attack (TIBIA-PARTY note 30). Candidate 2 (rule P3, note 35: Canary's targetList order from monster.cpp, mapsector.cpp and spectators.cpp, on top of candidate 1's target persistence) closed the potions and split gap on PR-24 but missed party kills (15.32 against 15.70-17.50, note 36), a shortfall shared with the solo arm. S1 (rule P8, note 76: Canary's spawn check event keeps running after a non-blockable respawn, spawn_monster.cpp 165-170, 304, 308-311) restores the near-spawn respawn cycle and raised solo hunting-ground kills from 13.18 to 13.61. With both, the duo prediction passed rule P9's calibration on PR-24 (party potions 6.09 in 4.01-6.19, party kills 16.4 in 15.70-17.50, ally potion share 54 percent in 25-75). Does it predict fresh duo hunts? |
| Scenario | PR-24's, unchanged: `live/run-arena.ps1 -Scenario hunting-ground -SpawnFile live/hunting-ground-spawns.xml -KnightAt 1936,1336 -RunMs 300000 -MonsterCount 0 -Monster cyclops -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 1000000 -TimeoutSeconds 420 -Competitors spiral-policy -Ally 'Paladin Sample' -AllyAt 1937,1336` through `live/block.ps1 -Results results/rows/arena_results_hunting_ground_pr25.spi`, 10 hunts in one eoie live-gui window. Login order knight then ally, as the harness enforces since P3. A crash is rerun once, then counted missing; no runs are added after a look. Valid only without a boosted Troll or Cyclops |
| Predictions (adopted bundle with S1, candidate 2, `camp.party 2`, 100 held-out hunts, job tibia-p9-duo) | 16.4 party kills and 6.09 party potions per hunt (knight 2.83, ally 3.26, ally share 54 percent), 0 deaths |
| Test | As PR-24: `eoie table logs` over the 20 logs with `--field run` and the kills and potions counts, `table query --group run --sum kills,potions`, then `table test --test one-sample --metric sum_kills --arms all --prediction 16.4` and `--metric sum_potions --prediction 6.09`, critical 2.26 (9 df) |
| Decision | Both hold: candidate 2 becomes the duo default (TIBIA-PARTY), and the 2026-10-14 duo saturation sweep is rerun on it before any party claim. Either fails: the remaining gap is named on TIBIA-PARTY. Descriptive: the per-knight split against the predicted share, deaths, each knight's lowest health, late troll kills per hunt, and PR-24 plus PR-25 pooled |
| RESULT (2026-10-23 20:30, 10 duo hunts 19:28-20:29, all counted, no crash, boosted Poison Spider) | Both fail, so the duo model does not predict fresh duo hunts and the gap is named on TIBIA-PARTY (note 83). Party kills 15.5 per hunt (95% CI 14.66-16.34) against 16.4, t -2.41; party potions 8.0 (CI 6.22-9.78) against 6.09, t 2.42 (df 9, critical 2.26). Per-hunt sums: kills 15 16 16 16 14 16 13 16 17 16, potions 7 6 5 9 9 8 6 6 12 12. The named gap is the split between the knights: the knight used 6.9 potions per hunt and the ally 1.1 (an ally share of about 14%, against the model's 54% and PR-24's about 45%), with damage taken 37.1 against 13.3. Live also moved between the two duo days (PR-24: 16.6 kills, 5.1 potions). Next is a CPU-light read of tonight's login order and first hits per knight before any new rule |

### Pre-registration PC-11, the walled-in hunter under a fixed CPU load (written 2026-10-23 10:2x, before any PC-11 run)

| Item | Value |
| --- | --- |
| Question | Without the probe, the walled-in hunter on 1931,1353 moved before the policy start in 8 of 10 PC-9 runs on a quiet machine, but in only 6 of 20 PR-22 runs beside daytime compiles. Does a known CPU load reproduce PR-22's low count? Rule L9 (TIBIA-EVENT-TIME), the next step of L5 (a) |
| Scenario | PC-9's arguments unchanged, plus a fixed load for the whole block: 4 busy-loop pwsh processes at Normal priority (half of the 8 logical cores, the share a heavy compile job takes), started just before the first run and stopped by their own pids after the last. Nothing else heavy or compiling beside it. Results go to `results/rows/arena_results_hunter_pc11.spi`. A crash is rerun once, then counted missing |
| Prediction | None numeric (diagnostic) |
| Reading | By the note-100 method of record, as PC-9. (a) 3 or fewer of 10 movers: the load reproduces PR-22, so live lead timing depends on host load; model comparisons use the quiet condition, and EVENT-TIME records load as a live-side factor. (b) 6 or more: this load does not explain PR-22; report, and close the question as unexplained. (c) 4-5: report only |
| RESULT (2026-10-23 19:27) | (b) by L9. The walled-in hunter moved before the policy start in 10 of 10 runs beside the fixed load (4 busy loops at about one core each, verified running and stopped afterwards), so this load does not reproduce PR-22's 6 of 20, and the question closes as unexplained. 7 of 10 runs cleared (1, 2 and 6 hit the 200 s cap). Descriptive: some hunters stepped twice in the lead. With the source reading of PC-10 (the hunter holds at its targetDistance 4 until the knight moves), a longer lead under load gives the knight time to step first, so the mover count looks set by the lead's length and the knight's first step rather than by CPU load as such |

### Pre-registration PC-9, the walled-in hunter without the server probe (written 2026-10-23 07:2x, before any PC-9 run)

| Item | Value |
| --- | --- |
| Question | The walled-in hunter on 1931,1353 moved before the policy start in 8 of 10 PC-8 runs (server probe installed, quiet machine) but in only 6 of 20 PR-22 runs (no probe, daytime load). Does the probe change the outcome, or the machine load? Rule L5 (TIBIA-EVENT-TIME note 117) |
| Scenario | PC-8's hunter arguments without `-ServerProbe` (`live/run-arena.ps1 -Runs 10 -Monster hunter -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -TimeoutSeconds 200 -Competitors spiral-policy`), nothing heavy or compiling beside it (window rules as for PC-8). Results go to `results/rows/arena_results_hunter_pc9.spi`. A crash is rerun once, then counted missing |
| Prediction | None numeric (diagnostic). The model moves the walled-in hunter in every run |
| Reading | From client logs by the note-100 method of record (`monster ...82 moved` before the policy line). (a) 6 or more of 10 movers: the probe is not the cause, the session difference points to load, and the next step is 10 probe-free runs beside a fixed scratch CPU load. (b) 3 or fewer: the probe changes the outcome, so PC-8's data stands only for probed runs. (c) 4-5: report only. No W39 candidate before this decides |
| RESULT (2026-10-23 10:16) | (a). The walled-in hunter (id 82) moved in 8 of 10 runs (not in runs 7 and 10), by `eoie table logs <the 10 PC-9 hunter logs, 20261023-095427 .. 101615> --count-by mover='monster 13421772(\d+) moved'`; hunters 81 and 83 moved in 10 of 10. The probe is not the cause: without it the boxed hunter moves as often as in PC-8 (8 of 10), so PR-22's 6 of 20 points to machine load. The machine was quiet: every lane held its compiles, and nothing heavy ran beside the block. 9 of 10 runs cleared; run 9 hit the 200 s cap. Next by the rule: 10 probe-free runs beside a fixed scratch CPU load, pre-registered before they run. Boosted creature: Poison Spider, not a spawned kind here |

### Pre-registration PC-8, the walled-in monster's first step, timed on the server (written 2026-10-23 00:03, before any PC-8 run)

| Item | Value |
| --- | --- |
| Question | Live, the hunter created on 1931,1353 (walled on three sides; only 1931,1352 is free) moves before the policy start in 6 of 20 PR-22 runs, always 153-326 ms after its neighbour leaves 1931,1352 and never on a later think. Canary's source as read, and the model (100 of 100), retry the blocked step on every think (TIBIA-EVENT-TIME notes 100-111). Why does live not retry, and does a walled-in cyclops behave the same way? |
| Scenario | 10 hunter-arena runs (`live/run-arena.ps1 -Runs 10 -Monster hunter -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -TimeoutSeconds 200 -Competitors spiral-policy`) and 10 cyclops retreat runs (the same with `-Monster cyclops -RetreatAt 2 -Competitors spiral-retreat`), with a Canary server-side Lua probe installed in `data/scripts` for these 20 runs only. The probe writes each monster's thinks and moves, the knight's arrival, first move and first target, all on the server clock. Results go to `results/rows/arena_results_hunter_leadtrace.spi` and `results/rows/arena_results_cyclops_leadtrace.spi`. The first part of one live-gui window, before PR-23B |
| Prediction | None numeric (diagnostic). The model retries: the walled-in monster moves before the policy in every run |
| Reading | Rule L2 (TIBIA-EVENT-TIME note 105): it thinks about every 1000 ms while 1931,1352 is free and still doesn't move, so the cause is in the walk-event path; or its thinks stop after the failed step, so the cause is in the idle or check-list path. The server times of the three first moves settle whether thinks share a tick. Rule L3 (note 109) for the cyclopses: stays in 6 or more of 10 makes a no-retry candidate eligible once L2 names the source line; moves in 8 or more drops the hypothesis; 5-7 movers is report only |
| RESULT (2026-10-23 03:4x, corrected 08:3x) | 20 runs, 0 crashes, 0 deaths; the cyclops runs cleared 10 of 10, the hunter runs 7 of 10 (three stopped at the 200 s cap with 225 or 450 experience, consistent with the PR-22 hunter clear rate of about 0.725); the probe was removed afterwards. Hunters, by L2 (TIBIA-EVENT-TIME note 118): live does retry. The walled-in hunter moved before the policy in 8 of 10 runs, at its first walk event after a think with 1931,1352 free. A free think without a move happened once in 10 runs, and thinks never stop. Every first move lands 665-724 ms after the mover's own think, and monsters in one think bucket move in the same millisecond (think period 1079-1086 ms). The client logs of the same runs show the same movers, so PR-22's 6 of 20 differed by session, not by instrument; rule L5 (note 117) tests probe against load next. Cyclopses, by L3 (note 119): outcome (a), the walled-in cyclops stayed until the policy in 8 of 10 runs, against the model's 100 of 100. The stays are timing (its neighbour leaves late, median about 1500 ms, plus one think and about 750 ms of walk lag), not a missing retry, so no candidate is built; rule L6 (note 120) traces the model side |

### Pre-registration PR-23B, late troll kills on 10 more solo hunts (written 2026-10-23 00:00, before any PR-23B run)

| Item | Value |
| --- | --- |
| Question | On PR-23 the knight killed 4.0 trolls per hunt after 200 s against the model's 2.94 (t 2.25, 95% CI 2.93-5.07, so not settled), and live late kills come in larger clusters after a cyclops kill (2.58 trolls per cluster, se 0.26, against 2.06). Is the late-troll gap real or live noise? Rule P5 (TIBIA-PARTY note 58) |
| Scenario | PR-23's, unchanged: `live/run-arena.ps1 -Runs 10 -Scenario hunting-ground -SpawnFile live/hunting-ground-spawns.xml -KnightAt 1936,1336 -RunMs 300000 -MonsterCount 0 -Monster cyclops -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 1000000 -TimeoutSeconds 420 -Competitors spiral-policy -Out results/rows/arena_results_hunting_ground_pr23b.spi`. It is the last part of one live-gui window, after 10 hunter and 10 cyclops arena runs with a Canary server probe that is removed before these hunts start. No smoke hunt (PR-23's ground and harness are unchanged). A crash is rerun once, then counted missing; no runs are added after a look |
| Predictions (adopted bundle with F34, solo, 100 held-out hunts, jobs tibia-troll-gaps and tibia-troll-clusters) | 2.94 troll kills per hunt from 200 s; clusters (a troll kill within 10 s of the previous, counted once) 1.08 per hunt with 2.06 trolls each |
| Test | Pooled over PR-23 and PR-23B (20 hunts): `eoie table logs <20 logs> --count late_trolls='Loot of a troll.*@t\+([2-9]\d{5}\|\d{7,})' --out <tsv>`, then `eoie table test <tsv> --test one-sample --metric late_trolls --arms all --prediction 2.94`. Cluster size by the TIBIA-PARTY note 59 definition, against 2.06 and its standard error |
| Decision | (a) The late-kill interval excludes 2.94 and the pooled cluster size exceeds 2.06 by more than its standard error: the gap is real, and the next model output is a per-troll arrival trace (server-side hunting-ground probe drafted). (b) The interval includes 2.94: the late-troll thread closes as live noise, and PARTY moves to a duo rerun with candidate 2 (pre-registration drafted with predictions 5.90 potions, 15.33 kills). (c) The interval excludes 2.94 but cluster size doesn't differ: per-spawn attribution with server-side spawn logging. Descriptive: potions and kills per hunt against PR-23 |
| Validity (added 00:33, before any run) | Canary halves a spawn's interval for the day's boosted creature (`spawn_monster.cpp:359-367`; also scaled by `rateSpawn`, 1 here, and spawn events). If PR-23B's logs name Troll or Cyclops as the boosted creature, or a spawn event is active, the block isn't comparable with PR-23 or the model: P5 isn't applied, and PR-23B is rerun on an unboosted day. PR-15, PR-23 and PR-24 ran with Monk, Bandit and Wasp boosted |
| RESULT (2026-10-23 04:4x) | Valid: Poison Spider was boosted in all 10 PR-23B logs and Wasp in PR-23's. 10 hunts, 0 crashes, 0 deaths. Pooled over 20 hunts, late troll kills average 3.85 (95% CI 3.22-4.48) against 2.94, t 3.02, p 0.007. 23 clusters average 2.74 trolls (se 0.18) against 2.06. So the outcome is **(a)**: the late-troll gap is real and sits in the clusters, and the next model output is the per-troll arrival trace (TIBIA-PARTY). Descriptive, PR-23B against PR-23: late trolls 3.7 vs 4.0, potions 13.9 vs 14.8, kills 13.2 vs 14.2. eoie's test output carries post_hoc=yes; the rule, test and prediction were written here and in TIBIA-PARTY note 58 before any PR-23B run, but no eoie experiment ledger entry exists for P5 |

### Pre-registration PR-24, two knights on the hunting ground (written 2026-10-22 12:47, before any duo run)

| Item | Value |
| --- | --- |
| Question | Does the model predict a live duo? The model's shared sweep (both knights run the same rule, no coordination) is the party result of 2026-10-14 (TIBIA-PARTY note 5: the best hand rule is within 5% of the evolved duo), but no duo has run live. Rule P1 (TIBIA-PARTY note 13) |
| Scenario | PR-23's hunting ground (`live/run-arena.ps1 -Scenario hunting-ground -SpawnFile live/hunting-ground-spawns.xml -KnightAt 1936,1336 -RunMs 300000 -MonsterCount 0 -Monster cyclops -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 1000000 -TimeoutSeconds 420 -Competitors spiral-policy`) plus `-Ally 'Paladin Sample' -AllyAt 1937,1336`: the second sample character made a knight with Knight Sample's stats, shield 11 and items, its own OTClient profile (`--user-dir`), the same probe, both per-account limits raised to 2 for the run. Through `live/block.ps1 -Results results/rows/arena_results_hunting_ground_pr24.spi`, 10 hunts in one eoie live-gui window, after one duo smoke hunt (not counted: two rows, both in game, loot by either). A crash is rerun once, then missing; no runs are added after a look |
| Predictions (adopted bundle, shield 11, `camp.party 2`, 100 held-out hunts, job tibia-duo-predict) | 15.36 party kills and 7.56 party potions per hunt (knight 7.14, ally 0.42), 0 deaths. The solo arm of the same job gives 13.19 kills and 14.84 potions (the bundle at shield 11; see the PR-23 caveat) |
| Test | Party sums per hunt: `eoie table logs <the 20 logs> --field run='-(\d+)\.log' --count kills='Loot of a ' --count potions='Using one of \d+ health potions' --out target/pr24-logs.tsv`, `eoie table query target/pr24-logs.tsv --group run --sum kills,potions`, then `eoie table test <sums> --test one-sample --metric sum_kills --arms all --prediction 15.36` and `--metric sum_potions --prediction 7.56`, critical 2.26 (9 df). With PR-23's spreads (sd 2.35 potions, 1.69 kills per knight), 10 hunts resolve about 2.4 party potions and 1.7 party kills |
| Decision | Both hold: the duo model matches live, and the 2026-10-14 saturation finding stands on a live-checked model. Either fails: the gap is named on TIBIA-PARTY before any candidate. Descriptive: kills and potions split between the knights (the model's 7.14 vs 0.42 is a sharp claim about who gets attacked), deaths, each knight's lowest health |
| RESULT (2026-10-22 15:00, hunt logs closing 13:59-14:56, block job ended 14:57, all 10 counted, no crash; the smoke hunt, closing 13:53, showed both knights in game and loot by either) | Both fail, so the duo model does not match live, and the gap is named on TIBIA-PARTY before any candidate is judged. Party kills 16.6 per hunt (95% CI 15.70-17.50) against 15.36, t 3.10; party potions 5.1 (CI 4.01-6.19) against 7.56, t -5.10 (sums per hunt: kills 17 16 14 16 16 18 16 18 17 18, potions 6 3 7 3 4 7 4 6 6 5). The split is the model's largest error: knight 9.7 kills, 2.8 potions and 18.8 hits taken per hunt, ally 6.9 kills, 2.3 potions and 20.3 hits, where the model gives knight 7.14 potions and ally 0.42; the ally used more potions than the knight in 4 of 10 hunts. 0 deaths, as predicted; lowest health 132.9 (knight) and 141.6 (ally) on average. Against the solo hunts on the same ground (PR-23, 14.8 potions, 14.2 kills), the second knight cuts potions by 2.9x live, where the model says 2.0x. No compile job ran during the block (tibia stopped its own queue at the go) |

### Pre-registration PR-23, the hunting ground's potion gap on fresh hunts (written 2026-10-22 07:37, before any PR-23 run)

| Item | Value |
| --- | --- |
| Question | PR-15's 10 hunts used 12.70 potions per hunt against the model's 14.34 (adopted bundle, t -3.16). Every mechanism checked since then matches live: the second cyclops's arrival, targeting, kill rate, how two-cyclops fights end, block-charge recharge (TIBIA-POTION-FIDELITY notes 141-142). The only difference left is the share of late joins (4 of 13 episodes live, 16% in the model, P = 0.137). Does a fresh block of 10 hunts replicate the gap? |
| Scenario | PR-15's: `live/run-arena.ps1 -Scenario hunting-ground -SpawnFile live/hunting-ground-spawns.xml -KnightAt 1936,1336 -RunMs 300000 -MonsterCount 0 -Monster cyclops -Runs 10 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 1000000 -TimeoutSeconds 420 -Competitors spiral-policy` through `live/block.ps1 -Results results/rows/arena_results_hunting_ground_pr23.spi -Ledger PR-23-POTIONS`, in an eoie live-gui window with no compiles from any lane, after PR-22. One smoke hunt first (not counted) checks that the six spawns appear and the hunt ends at 300 s. A crash is rerun once, then counted missing; no runs are added after a look |
| Predictions (adopted bundle, 100 held-out hunts, `scripts/score.ps1`) | 14.34 potions per hunt, 13.19 kills |
| Test | Primary: potions per hunt (`potions` in the results table), one-sample t against 14.34, critical 2.26 (9 df), `eoie experiment analyze target/ledger PR-23-POTIONS`. Secondary: kills per hunt from the `Loot of a` lines, `eoie table logs <logs> --count kills='Loot of a ' --count-by kind='Loot of a (\w+)' --out target/pr23.tsv`, then `eoie table test target/pr23.tsv --test one-sample --metric kills --prediction 13.19` |
| Decision | Potions hold (\|t\| <= 2.26): PR-15's gap does not replicate, and TIBIA-POTION-FIDELITY's hunting-ground gap is closed as sampling noise. Potions below (t < -2.26): the gap replicates on 20 hunts, and the late-join share is re-measured on both blocks before any candidate. Potions above (t > 2.26): the model under-predicts on fresh hunts, which reopens the question the other way. Descriptive: the late-join share and two-cyclops episode length on the new hunts (live-join method) |
| RESULT (2026-10-22 11:28, hunts 10:30-11:25, all 10 counted, no crash; smoke hunt 10:25-10:30 showed both spawn kinds and ended at 300 s) | Potions hold, so PR-15's gap is closed as sampling noise. `eoie experiment analyze`: PR-23-POTIONS 14.8 per hunt (95% CI 13.12-16.48) against 14.34, t 0.62 (values 18 17 14 13 17 17 13 13 15 11). Kills 14.2 per hunt (CI 12.99-15.41; 4.8 cyclops, 9.4 trolls) against 13.19, t 1.89, inside 2.26 (eoie prints `post_hoc=yes` because only the primary was in the ledger; this test was written above at 07:37). Descriptive (live-join method on the 10 logs): 11 two-cyclops episodes, 1 late join (fight age 23 s; the others 0-6 s), so 9% against PR-15's 4 of 13 and the model's 16%; episodes last 26.0 s on average (model 23.4, PR-15 17.4); 2+ cyclops adjacent in 9.6% of 1-s samples, exactly one in 37.8% (PR-15 7.6% and 39.3%). Pooled over PR-15 and PR-23, 13.75 potions per hunt. No compile job started during the block. Caveat found 12:47 (verdict unchanged): the registered 14.34 is `scripts/score.ps1`'s hunting-ground table, computed at shield 12 (PR-15's setting), while the scenario has reset shielding to 11 since PR-16. At shield 11 the bundle predicts 14.84 potions and the same 13.19 kills (the solo arm of the PR-24 prediction job), and live 14.8 sits at t -0.05 against it. Of the 2.1-potion difference between PR-15 and PR-23 live, the shield explains about 0.5 in the model |

### Pre-registration PR-22, does the hunter arena still clear 19 of 20? (written 2026-10-22 07:11, before any PR-22 run)

| Item | Value |
| --- | --- |
| Question | PR-21 cleared 19 of 20 hunter runs where PR-18 and PR-19 cleared 14 and 15 (29 of 40). Is the live clear rate higher now, or was PR-21 an outlier? PR-21's knight also logged in later (median 7.3 s after client start against 3.9 and 4.3 s), so the hunters wandered longer before it acted (TIBIA-FIDELITY note 90) |
| Scenario | PR-21's command line (`live/block.ps1 ... -Runs 20 -Monster hunter -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -TimeoutSeconds 200 -Competitors spiral-policy`), `-Results results/rows/arena_results_hunter_pr22.spi`, in an eoie live-gui window with no compiles from any lane; the competitor now also logs PC-7's lines and every monster move between login and the policy start (`monster <id> moved to x,y @login+ms`, for TIBIA-EVENT-TIME's protected-lead question; logging only). A crash is rerun once, then counted missing; no runs are added after a look |
| Test | Clears k of 20 (`cleared_ms`). The central 95% range is 10..18 at PR-18/19's rate (0.725) and 17..20 at PR-21's (0.95) |
| Decision | k <= 16: PR-21 was an outlier, and the model targets PR-18/19's rate. k >= 19: the live rate has moved up, and the next step is finding what changed. k of 17 or 18: inconclusive; both rates are kept in view. Descriptive: each run's login delay against whether it cleared, by `eoie table logs <the 20 logs> --field run='-(\d+)\.log' --last login='Your last visit.*@t\+(\d+)' --count kills='You gained \d+ experience' --out target/pr22-login.tsv`, then `eoie table query target/pr22-login.tsv --group kills --count file --mean login` (kills 3 = cleared); PC-7's measure runs on these logs once eoie's latency verb exists |
| RESULT (2026-10-22 11:28, runs 09:40-10:25, all 20 counted, no crash; a first start at 09:33 aborted at run 1 on a runner bug before any row, ledger addendum) | k = 14 of 20, so PR-21 was an outlier and the model targets PR-18/19's rate (0.725); without PR-21 the live blocks clear 43 of 60. `eoie experiment analyze target/ledger PR-22-CLEARS`: binomial p_at_most 0.48 at 0.725, inside the registered band. Hits taken 20.45 per run (sd 8.57; the bundle's 21.03, descriptive). Login delay against clears: the 14 cleared runs logged in after 5.47 s on average, the 6 that did not after 6.34 s (5 with one kill, 6.18 s; 1 with two, 7.17 s), so a later login did not raise clears here. PC-7 lines present in all 20 logs (per run 28.1 chase changes, 94.2 knight/target moves, 2.2 pre-policy monster moves). The first pre-policy monster move came 578-2445 ms after login (none under 200 ms; TIBIA-EVENT-TIME's D36 gate). The cleared column of runs 1-3 was printed mid-block by a progress check (ledger disclosure); no action followed. No compile job started during the block |

### Pre-registration PC-7, the knight's first step after its target leaves (written 2026-10-22 05:45, before any PC-7 run)

| Item | Value |
| --- | --- |
| Question | Why is the live knight still on its tile a second after its target steps away from it in 98% of cases (PR-18 and PR-19, 141 of 144) while the model's moves in about a third to two fifths of them? Canary's source points at the competitor: `engage` turns chase mode off while the target is adjacent, `Player::setChaseMode(false)` drops the server's follow, and when chase comes back on the new path waits for the player's next one-second think, then a full step (TIBIA-FIDELITY notes 82-87) |
| Probe | The competitor, logging only (no behavior change, landed outside any live block): `chase on` / `chase off` with `@t+ms` when `engage` changes the client's chase mode, and `knight moved to x,y` / `target moved to x,y` with `@t+ms` from `client.on_position_change`, which connects on both `Creature` and `LocalPlayer` (PC-4's amendment) |
| Scenario | PR-21's command line with `-Runs 10`, in an eoie live-gui window; a crash is rerun once |
| Measure | Per move of the attacked hunter from Chebyshev distance 1 to 2 or more: L1 = `chase on` minus that move, L2 = the knight's next move minus that move, by `eoie table logs <the 10 logs> --time t='@t\+(\d+)' --latency L2='target moved to' --to 'knight moved to' --out target/pc7-l2.tsv`, then `eoie table query target/pc7-l2.tsv --median L2 --quantile 0.1 L2 --quantile 0.9 L2 --share-at-least 500 L2` (and the same with `--to 'chase on'` for L1), the EOIE-LOG-SEQUENCES latency measure; the adjacent-to-2+ condition waits for its `--where` over captured columns, and PC-7 waits for both (no per-experiment script) |
| Predictions | From the source: L1 within 0-250 ms in at least 90% of events (probe tick plus client lag); L2 about U(0, 1000) + 558 ms, median 0.95-1.35 s, at least 90% of L2 at 0.5 s or more |
| Decision | Confirmed when L2's median is in 0.85-1.45 s and at least 85% of L2 are 0.5 s or more: rule D32 (TIBIA-FIDELITY note 86) is scored as registered. Refuted when L2's median is below 0.7 s: D32 is withdrawn before scoring. Anything else: D32 is scored as registered and PC-7 is reported as inconclusive |

### Pre-registration PR-21, the knight's chase steps at the live probe's pace (written 2026-10-22 01:33, before any PR-21 run)

| Field | Content |
| --- | --- |
| Question | Does the model predict the hunter arena on fresh runs once the knight's chase steps take at least the live probe's measured 558 ms (PC-4 median), `chasing_at_the_live_probe_pace` (rule D29, TIBIA-FIDELITY notes 70 and 77)? |
| Scenario | As PR-19: `live/block.ps1 -Results results/rows/arena_results_hunter_pr21.spi -Ledger PR-21-CLEARS -SecondaryLedger PR-21-HITS` with `-Runs 20 -Monster hunter -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -TimeoutSeconds 200 -Competitors spiral-policy`, through main's eoie live-gui window (05:30-06:30), skipped if it cannot start by 05:45 |
| Predictions (100 held-out runs, the adopted bundle plus the option) | 74 of 100 clear, 23.66 hunter hits per run, in-reach share 33.7%. The adopted bundle without it: 77 of 100, 21.03 hits. Cyclops arms and hunting-ground kills unchanged (score: arms and kills hold) |
| Test | Clears: binomial on `cleared_ms`, holds when k of 20 is in 11..18 (central 95% range for p 0.74), `eoie experiment analyze target/ledger PR-21-CLEARS`. Hits: one-sample t on `taken` in `results/rows/arena_results_hunter_pr21_secondary.spi` against 23.66, critical 2.09 (19 df), `PR-21-HITS`. Both registered in target/ledger with `--time timestamp` before the window |
| Honest limit | The bundle's own predictions (12..19 clears; 21.03 hits) are close, so 20 runs may not separate the two. A pass shows the option is consistent with live; the case for it is the source fact (PC-4's measured 558 ms) plus the in-sample hunter score |
| Decision | Rule D29 plus N7: the option joins `mirroring_the_current_arena` when both tests hold; otherwise it stays off by default. A crash is rerun once, then counted missing; no runs are added after a look |
| RESULT (2026-10-22 06:14, runs 05:30-06:09, all 20 counted, no crash, no death) | The option stays off by default. Clears 19 of 20, outside 11..18: PR-21-CLEARS fails (eoie's binomial analyze printed `reject_h0=no` from a one-sided `p_at_most` 0.998; the registered test is the two-sided band, read by hand). Hits 23.8 per run (95% CI 20.42-27.18) against 23.66, t 0.09: PR-21-HITS holds. Live clears ran well above both predictions (95% against 74% with the option and 77% without), and above PR-18 and PR-19 (14 and 15 of 20), so the hunter clear rate, not the hits, is now the open gap (TIBIA-FIDELITY note 89). No compile overlapped any run: the next compile windows were granted from 06:09:52.9, after the window's release |

### Pre-registration PR-20, retreat steps at the competitor's own pace (written 2026-10-21 14:51, before any PR-20 run)

| Item | Value |
| --- | --- |
| Question | Does the model predict retreat steps per clear on fresh runs once the knight's retreat steps follow the competitor's own pacing (pace = all4 plus `pacing_retreats_like_the_live_probe`)? Without it (all3 and all4 have the same cyclops arms) the model over-counts them |
| Why | The 80 live retreat logs (PR-12, PR-16, PR-17, seen data, so the change is in sample) average 11.29 retreat steps per clear (sd 11.35) against all3's 15.06. Split by time since start: first 10 s live 4.31 vs model 4.25, after 10 s live 6.98 vs model 10.81, so the excess is in long late retreats. The competitor waits `step_ms` 650 between retreat steps, x3 diagonal (`apps/arena/competitor.spi`), above Canary's step time on every arena tile (arena ground 500, road 300, diagonal 1500), while the model paced retreats by Canary's step time alone. The option copies the competitor's constant; nothing is tuned (rule D19, TIBIA-EVENT-TIME note 36, written before measuring) |
| Scenario | As PR-17's retreat arm: `live/block.ps1 -Results results/rows/arena_results_cyclops_pr20.spi -Ledger PR-20-PACE-POTIONS -SecondaryLedger PR-20-PACE-STEPS,PR-20-ALL3-STEPS` with `-Runs 20 -Monster cyclops -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -RetreatAt 2 -Competitors spiral-retreat`, shield reset to 11, through an eoie live-gui window after PR-19, skipped if it cannot start by 19:00 |
| Predictions (100 held-out clears, shield 11, `scripts/score.ps1` on a patched copy under `target/`) | pace: 11.73 retreat steps per clear (sd 9.73), 13.32 potions per clear (sd 3.22), 92.6 s. all3 and all4: 15.06 steps (sd 13.97), 13.11 potions. Scored by D19: steps Welch t -0.28 against the 80 logs; gated arm t PR-16 rules -0.63, retreat 1.61, PR-17 rules 0.05, retreat 0.12 (all within 2.09); hunting-ground kills 13.19 and the hunter table unchanged (the cavebot never retreats) |
| Test | Primary: retreat steps per clear (`steps` in `results/rows/arena_results_cyclops_pr20_secondary.spi`, the log count of 'step away from'), one-sample t against 11.73 by `eoie experiment analyze target/ledger PR-20-PACE-STEPS`, and potions per clear (`potions` in the results table) against 13.32 by `PR-20-PACE-POTIONS`, both critical 2.09 (19 df), all three registered 2026-10-21T18:52Z. Contrast: `PR-20-ALL3-STEPS`, the same steps test against 15.06 |
| Decision | Rule D19: pace replaces the bundle that D17 and D18 leave on 2026-10-22 or later, only when PR-20-PACE-STEPS and PR-20-PACE-POTIONS both hold; otherwise `pacing_retreats_like_the_live_probe` stays off by default and the retreat-step gap stays open. A crash is rerun once, then missing; no runs are added after a look |
| RESULT (2026-10-21 17:08, runs 16:21-17:07, all 20 counted, no crash, no death, 20 of 20 cleared) | Pace holds. `eoie experiment analyze`: PR-20-PACE-STEPS 10.1 retreat steps per clear (95% CI 3.8-16.4) against 11.73, t -0.54; PR-20-PACE-POTIONS 12.80 potions per clear (CI 11.12-14.48) against 13.32, t -0.65. The contrast PR-20-ALL3-STEPS also holds (t -1.65 against 15.06): with a step sd of 13.4, 20 runs do not separate the two predictions, so the case for pace is the source fact (the competitor's own 650 ms retreat pace) plus a pass, not a discrimination. Descriptive: 3.9 steps in the first 10 s and 6.2 after (the 80 earlier logs: 4.31 and 6.98); 10 of 20 runs step after 10 s, 3 take 16 or more (59 at most); cornered 382 ticks per clear (earlier about 374), so most live runs are pinned in the pocket after 3-4 steps while the model clusters at 9-12; clear time 89.1 s (sd 12.6), hits taken 61.4, lowest health 96.3 (min 62). By rule D19, pace (all4 plus `pacing_retreats_like_the_live_probe`) is adopted into `mirroring_the_current_arena` on 2026-10-22 (TIBIA-EVENT-TIME) |

### Pre-registration PR-19, hunter arrows past low obstacles (written 2026-10-21 12:29, before any PR-19 run)

| Item | Value |
| --- | --- |
| Question | Do the hunters' arrows stop only at Canary's projectile blockers? With that fact (all4 = all3 plus `sighting_like_canary`) the model predicts hunter hits per run on fresh runs; without it (all3), it under-counts them |
| Why | PR-18 (seen data, so the change is in sample): live landed 23.85 hits per run against all3's 14.88. Hunters within range 7 of the knight per 1 s sample: live 1.87, model 1.66, so exposure matches. But only 1.10 of the model's had a clear line, while live lands 0.25 hits per in-range hunter-second, the full datapack rate (one 50% arrow every 2 s). Canary's `Map::checkSightLine` stops only at `CONST_PROP_BLOCKPROJECTILE`, which comes from the appearance flag `unsight` (15) or items.xml `blockProjectile` (365 ids); the model's sight lines stopped at every unpassable tile (flag 13). `apps/map` now emits `arena_sight_bits` from those two sources (rule D18, TIBIA-EVENT-TIME note 33, written before measuring) |
| Scenario | As PR-18: `live/block.ps1 -Results results/rows/arena_results_hunter_pr19.spi -Ledger PR-19-CLEARS -SecondaryLedger PR-19-ALL4-HITS,PR-19-ALL3-HITS` with `-Runs 20 -Monster hunter -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -TimeoutSeconds 200 -Competitors spiral-policy`, through an eoie live-gui window, skipped if it cannot start by 19:00 |
| Predictions (100 held-out runs, `scripts/predict.ps1` table 2) | all4: 77 of 100 clear, 21.03 hunter hits per run (sd 6.24), 3.79 potions per run, 55.9 s per clear. all3: 77 of 100, 14.88 hits (sd 4.64), 2.21 potions, 55.9 s. The cyclops arms and the hunting ground are unchanged by the option (no ranged foes there; `scripts/score.ps1`: identical t values, kills 13.19) |
| Test | Primary: hits per run (`taken` in `results/rows/arena_results_hunter_pr19_secondary.spi`, the log count of 'You lose N hitpoints due to an attack'), one-sample t against each prediction, critical 2.09 (19 df), by `eoie experiment analyze target/ledger PR-19-ALL4-HITS` and `PR-19-ALL3-HITS`; clears by `PR-19-CLEARS` (binomial on `cleared_ms`, holds for 12 to 19 of 20). All three registered 2026-10-21T16:29Z. Potions and clear time are descriptive |
| Decision | Rule D18: all4 replaces all3 in `mirroring_the_current_arena` on 2026-10-22 when PR-19-ALL4-HITS and PR-19-CLEARS both hold; otherwise all3 is adopted alone (rule D17) and `sighting_like_canary` stays off by default. A crash is rerun once, then missing; no runs are added after a look |
| RESULT (2026-10-21 16:38, runs 15:39-16:21, all 20 counted, no crash, no death) | All4 holds, all3 rejects. `eoie experiment analyze`: PR-19-CLEARS 15 of 20 (P(k <= 15) 0.50 under p 0.77, inside 12-19); PR-19-ALL4-HITS 20.45 hits per run (95% CI 17.43-23.47) against 21.03, t -0.40; PR-19-ALL3-HITS the same runs against 14.88, t 3.86, rejected. With PR-18 (23.85) the live hit rate is about 22 per run, and only the arrow sight line from Canary's projectile blockers reaches it. Descriptive: potions 3.25 per run (sd 1.65; 3.93 per clear, 1.20 per uncleared run) against all4 3.79 and all3 2.21; clear time 60.5 s over the 15 clears (sd 11.7) against 55.9; lowest health 120.1 on average (min 88). The five uncleared runs killed one hunter (four) or two (one), as in PR-18. By rule D18, all4 (all3 plus `sighting_like_canary`) replaces all3 in `mirroring_the_current_arena` on 2026-10-22 (TIBIA-EVENT-TIME) |

### Pre-registration PR-18, the hunter arena out of sample (written 2026-10-21 08:39, before any PR-18 run)

| Item | Value |
| --- | --- |
| Question | Which within-tick setting predicts the live hunter clear rate on runs no model saw: the frozen bundle (`mirroring_the_current_arena`) or the candidate all3 (the bundle plus `seeing_at_its_poll`, `acquiring_like_canary`, `following_like_canary`)? The hunter band so far rests on in-sample data (PR-13, PR-14, the diagnostic block: 16 of 25) |
| Scenario | As PR-14 (`-Monster hunter -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -TimeoutSeconds 200`), competitor `spiral-policy` only, 20 runs, through `live/block.ps1` into `results/rows/arena_results_hunter_pr18.spi`; it runs after PR-17 and only if it can start by 16:00 (rule E8) |
| Predictions (100 held-out clears, 200 s cap, `scripts/predict.ps1` table 2) | Bundle: 99 of 100 clear, 2.39 potions per run (sd 1.17), 50.0 s per clear (sd 7.1). All3: 77 of 100 clear, 2.21 potions (sd 1.27), 55.9 s per clear (sd 8.6) |
| Test | Primary: the number of clears k of 20, counted by `eoie experiment analyze target/ledger PR-18-BUNDLE-CLEARS` and `PR-18-ALL3-CLEARS` (binomial on `cleared_ms`, registered 2026-10-21T12:39Z with `--time timestamp`). The bundle holds when k is 19 or 20 (central 95% range for p 0.99); all3 holds when k is 12 to 19 (central 95% range for p 0.77). Potions and clear time are descriptive |
| Decision | The verdicts feed rule D17 (TIBIA-EVENT-TIME note 27) as its hunter criterion, replacing the in-sample band. A crash is rerun once, then missing; no runs are added after a look |
| RESULT (2026-10-21 12:08, runs 10:52-11:34, all 20 counted, no crash, no death) | 14 of 20 clear: the bundle fails, all3 holds. `eoie experiment analyze`: PR-18-BUNDLE-CLEARS rejects (P(k <= 14) about 0 under p 0.99), PR-18-ALL3-CLEARS does not (P(k <= 14) 0.30 under p 0.77). The 16 of 25 in sample (PR-13, PR-14, the diagnostic block) and these 14 of 20 agree, 30 of 45 in all. The six uncleared runs killed one hunter (five runs) or two (one run) and stalled with nothing in sight, the failure mode README describes. Descriptive: cleared in 64.2 s on average (sd 11.7; bundle 50.0, all3 55.9); potions 4.75 per run (sd 2.94; 6.50 per clear, 0.67 per uncleared run) against 2.39 and 2.21 predicted, about double, as in sample (PR-13 3.0, PR-14 4.1), so the model under-counts the hunters' damage whatever the within-tick order; lowest health 133.7 on average (min 88). Retreat steps 0 (`results/rows/arena_results_hunter_pr18_secondary.spi`, the rules policy). By rule D17 all3 is now the only candidate that passes every criterion out of sample, and it is adopted into `mirroring_the_current_arena` on 2026-10-22 (TIBIA-EVENT-TIME) |

### Pre-registration PR-17, the overnight bundle on fresh clears (written 2026-10-21 03:44, before any PR-17 run)

| Item | Value |
| --- | --- |
| Question | Does `mirroring_the_current_arena` as adopted overnight (PR-16 rows: the knight steps at Canary's speed, monsters dance, every step follows Canary's formula on the map's ground speed, and the knight acts on what it saw a tick earlier) predict potions per clear for the rules and the retreat policy on runs it has not seen? |
| Why | Each change was adopted in sample, by rules D9, D12 and D14 written before measuring, and all of them were scored on PR-11, PR-12 and PR-16. PR-17 is the first out-of-sample look at the whole bundle |
| Setup | As PR-16: `live/block.ps1 -Results results/rows/arena_results_cyclops_pr17.spi -Ledger PR-17-RULES,PR-17-RETREAT -StartBy <time>` with the arena arguments below runs `run-arena.ps1 -Monster cyclops -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -RetreatAt 2`, competitors `spiral-policy` and `spiral-retreat` alternating, 20 runs each, shield reset to 11, inside an eoie live-gui window, into `results/rows/arena_results_cyclops_pr17.spi` |
| Prediction (100 held-out clears, shield 11; computed with a lab script, reproduced exactly on 2026-10-21 07:35 by `scripts/predict.ps1`) | Rules 14.55 potions per clear (sd 2.80), 90.1 s, 68.7 hits; retreat 12.81 (sd 3.03), 92.2 s, 62.3 hits |
| Second prediction (written 2026-10-21 08:36, before any PR-17 run; registered 2026-10-21T12:36Z as PR-17-ALL3-RULES and PR-17-ALL3-RETREAT) | The candidate all3, the bundle plus three off-by-default options (`seeing_at_its_poll`, `acquiring_like_canary`, `following_like_canary`), the only setting found that also puts the hunter arena inside its band (77 of 100): rules 13.94 potions per clear (sd 2.63), 87.7 s; retreat 13.11 (sd 2.80), 94.5 s, 15.1 retreat steps. The same runs test it out of sample; rule D17 (TIBIA-EVENT-TIME note 27) adopts it from 2026-10-22 only if both arms hold |
| Test | One-sample t per arm on potions against its prediction, two-sided, critical 2.09 (19 df), by `eoie experiment analyze target/ledger PR-17-RULES` and `PR-17-RETREAT` (registered 2026-10-21T07:47Z with `--time timestamp`, so rows before registration cannot count) |
| Secondary, descriptive | Retreat steps per clear from the logs (`eoie table logs --count steps='step away from'`), predicted 12.4 (live PR-12 and PR-16: 11.7); hits taken per clear from the logs |
| Decision | Each arm holds when its t is within 2.09. A crash is rerun once, then missing; no runs are added after a look. If an arm fails, the bundle keeps its in-sample label, and the next model change needs a new rule written before measuring |
| RESULT (2026-10-21 10:51, runs 09:22-10:51, all 40 counted, no crash, no death) | Both predictions hold out of sample. `eoie experiment analyze`: rules 13.90 potions per clear (95% CI 12.34-15.46) against the bundle's 14.55, t -0.87, and against all3's 13.94, t -0.05; retreat 13.25 (CI 12.01-14.49) against the bundle's 12.81, t 0.74, and against all3's 13.11, t 0.24. Descriptive (`results/rows/arena_results_cyclops_pr17_secondary.spi`): retreat steps 10.1 per clear (bundle 12.4, all3 15.1; PR-12 and PR-16 live 11.7), hits taken 67.5 rules and 64.0 retreat (bundle 68.7 / 62.3, all3 66.5 / 63.1). The overnight bundle now predicts both policies on fresh clears; all3 fits both arms more closely, and rule D17 adopts it only if PR-18 also holds (TIBIA-EVENT-TIME) |

### Pre-registration PC-6, the swing after a retreat step on fresh logs (written 2026-10-20 11:58, before the analysis)

| Item | Value |
| --- | --- |
| Question | Do retreat steps cancel the knight's pending swing, so that it resumes late (Canary `Player::onWalk` cancels the attack task on every walk; the next swing waits for a think, ~1083 ms apart, or `hasExtraSwing`)? |
| Why | PC-3 (5 runs, 2026-10-15) found 31 of 36 step-spanning gaps late. The model reproduces that only with two changes found today: `player_retreats` must apply `swing_after_step` (it never did; only approach steps cancelled the swing) and the knight's think must run at the measured ~1083 ms period instead of exactly 1000 ms. Both were found on model traces against PC-3's numbers, so PC-3 itself is in-sample |
| Data | The 40 PR-12 `spiral-retreat` logs (2026-10-18 15:42 to 2026-10-20 09:26), all with `step away from ... @t+` lines, never analyzed for swing timing; `analysis/pc3-analysis.ps1` as in PC-3 (grid 2000 ms, tolerance 60 ms) |
| Predictions | Share of step-spanning landed pairs on the grid: fixed model 2% (12 of 604 in `lab/tibia-fidelity/swing_delay_probe.spi` on the patched copy), current model 70.5% (445 of 631). Pairs without a step: about 96% on the grid in both |
| Decision | The fixes hold when the live share of step-spanning pairs on the grid is below 20% and the no-step share is above 85%; the current model holds when the step share is above 50%. Anything between is inconclusive. Run once, no further looks |
| RESULT (2026-10-20 11:59) | The fixes hold. 40 logs: step-spanning pairs 232, on the grid 9 (3.9%; fixed model 2%, current model 70.5%), late by 60-1060 ms 210 (90.5%), other 13; pairs without a step 935 of 940 on the grid (99.5%). The late residuals cluster again at 160-200 ms (about a quarter of them), Canary's `hasExtraSwing` when an adjacent creature moves, which the patched model spreads more evenly; that is the next refinement, not part of this test. Both changes are in src since 2026-10-20 14:30 (`hunting_pack.live_think_period_ms`, `swing_after_step` in `player_retreats`); pack_sim 32/32 after re-pinning three tests, gpu_sim 6/6, hunt_sim 11/11 |

### Pre-registration PR-15, the hunting ground with Canary's own spawns (written 2026-10-20 11:35, before any hunting-ground run)

| Item | Value |
| --- | --- |
| Question | Does the model predict a five-minute hunt on the hunting ground (the respawning six-monster camp no live run has walked; TIBIA-HUNTING-GROUND's open 10%)? |
| Server setup | The six model spawns of `arena_hunt` as Canary spawns (`live/hunting-ground-spawns.xml`, swapped in by `run-policy-scenario.ps1 -SpawnFile` and restored after): cyclops at 1958,1336 and 1953,1346, trolls at 1943,1334, 1950,1345, 1953,1355 and 1941,1354, one spawn block each, spawntime 60. Canary places them at server start; no GM summon |
| Respawn, from Canary's source, no free parameter | A death starts the spawn's check event (`Monster::onCreatureDisappear` -> `startSpawnMonsterCheck`, period gcd(30000, interval) = 30 s); the first check stamps `lastSpawn`, the spawn is due one spawntime later, and cyclops and trolls (`isBlockable = false`) appear after 3 x 1400 ms of teleport effect. So a kill returns 94.2 s later (`hunting_pack.canary_respawn_ms 60000`, pinned in pack_sim), not the model's former 60 s. Spawns of non-blockable monsters are not held back by a player in view |
| Competitor | `spiral-policy` rules: heal below 160, strike above 0, CurrentFirst, never retreats or kites (`TIBIA_ARENA_RETREAT_AT=0`, `TIBIA_ARENA_KITE_AT=0`, `TIBIA_ARENA_TARGETING=current`), knight level 30, axe 60, starting at 1936,1336 (the layout start), 300 s from the policy start (`TIBIA_ARENA_RUN_MS=300000`) |
| Prediction (model, 100 held-out hunts of 300 s; `arena_hunt () \|> mirroring_the_live_arena \|> waking_at 0 \|> respawning_like_canary 60000`, `cavebot_with_heal 160`; `lab/tibia-fidelity/live_hunt_prediction_probe.spi`) | 12.38 kills per hunt (sd 1.38; from experience about 4.7 cyclops and 7.7 trolls), 14.95 potions (sd 2.32), 71.9 hits taken, no deaths |
| Runs | One harness smoke run first (not counted; it checks that the six spawns appear and the hunt ends at 300 s; a harness fix found there is logged as an amendment before any counted run), then 10 runs in one block; a crash is rerun once, then missing |
| Test | Primary: kills per hunt, one-sample t against 12.38 (two-sided, critical 2.26 at 9 df). Secondary: potions per hunt, same test. Kills are counted from the `Loot of a ...` lines (cyclops and trolls separately); experience is descriptive only, because the live server pays 1.5 times the model's experience per kill (225 per cyclops) |
| Expectation, stated now | Fewer kills live than predicted: live monsters out of the knight's view stay idle, and the standing policy only fights what comes to it, which the model may reach sooner through its single opening layout (the parity-arena openings replay one geometry, TIBIA-POTION-FIDELITY 2026-10-20) |
| Decision | The model holds when both t are within the critical value. It fails on either t beyond it; the direction and the per-kind kill counts are reported either way. No runs are added after a look |
| Smoke run (2026-10-20 12:29-12:34, not counted) | Harness check passed: the six spawns were in place at login, the hunt ran its 300 s, 14 kills, +1395 experience (5 x 225 + 9 x 30: the live server pays 1.5 times the model's 150 and 20) |
| RESULT (2026-10-20 12:34-13:29, 10 runs, no crash, no death) | The model fails, on potions only. Kills hold: 12.80 per hunt (sd 1.87) against 12.38, t 0.71, with 4.70 cyclops (model about 4.7) and 8.10 trolls (about 7.7). Potions: 12.70 (sd 1.64) against 14.95, t -4.35 (critical 2.26). Run as automated eoie job `tibia-pr15-block` behind a live-gui window; analysis on the 10 `*-hunting-ground-<run>.log` logs (not the smoke log): `eoie table logs <logs> --field arm='-(hunting-ground)-\d+\.log' --count kills='Loot of a ' --count-by kind='Loot of a (\w+)' --count potions='Using one of \d+ health potions' --count deaths='finish: death' --out target/pr15.tsv`, then `eoie table test target/pr15.tsv --test one-sample --metric kills --arms hunting-ground --group arm --prediction 12.38 --where deaths=0` (and `--metric potions --prediction 14.95`), and `eoie table query target/pr15.tsv --where deaths=0 --group arm --mean kills,potions,kind_cyclops,kind_troll --sd kills,potions` for the per-kind means |
| Post-hoc (2026-10-20 13:31, explanatory) | The potion gap splits into fewer and softer hits: live 65.9 hits per hunt (65.4 by cyclops, 0.5 by trolls) against the model's 71.9, and 33.3 damage per landed cyclops hit against about 35.6 implied by the model's potions; 33.3 sits below even the model's fully charged mean (35.16), so the block-charge share cannot explain it here. Checked 14:44: every PR-15 run started at shield skill 12 (the scenario reset never reset shielding, which rose to 12 on 2026-10-19 and stayed; fixed since for future runs), while the model assumes 11; with shield 12 the model predicts 15.08 instead of 15.46 potions (tree with the 14:30 swing fixes), about 0.4 of the gap. Same direction as the parity arena (model +0.9 to +1.2 potions per clear): the model over-counts damage taken on every arena without login protection. Later the same day (post-hoc): the live policy starts 2.5 s after login (TIBIA-START-SYNC) while the prediction let the knight act from t 0; delaying the model knight's first decision by those 2.2 s alone moves the hunting ground to 10.66 kills and 13.23 potions, so with the start delay modeled the potions would hold and the kills fail. The verdict above stays as registered; why a 2.2 s idle moves a 300 s hunt this much is open (TIBIA-POTION-FIDELITY) |
| Post-hoc (2026-10-21 03:55-04:04, diagnosis only; the bundle is frozen until PR-17, rule D15) | With the overnight bundle the model gives 70.8 damaging hits per hunt (live 65.2), damage 2398 (2176), and potions 14.20 (12.70), at an equal 33.9 damage per hit (live 33.4) and the same kill mix (about 4.7 cyclopes and 8.2 trolls). Trolls are not the gap: with troll melee zeroed the model still takes 69.8 hits. In 10-s buckets the extra hits sit at wave onsets: the opening (10-20 s: live 1.5, model 4.5) and the two respawn waves (+1.0 near 130 s, +2.9 near 255 s); between onsets the rates match. First cyclops hit per hunt: live median about 18.5 s (12-23), model 16 (9-20; 18 without the step formula). Live chasing cyclopes (7 step-probe logs) step straight in 700-719 ms, Canary's formula. The 10 of 37 intervals of 1200-2200 ms all fall in close-quarters jostling within 2 tiles of the knight, not on the approach. They fit Canary's fresh-walk delay (`getEventStepTicks` with `RespectDelay` waits a full step before a new walk), but a lab prototype of that delay leaves the opening unchanged and breaks the ranged hunters. Why the model's cyclopes arrive about 2 s early at each wave stays open (candidates: target acquisition and the first walk start at login or spawn); a rule and a score come after PR-17 |

### Pre-registration PR-14, ranged hunters with the calibrated follow pace (written 2026-10-19 06:01, before any PR-14 run)

| Item | Value |
| --- | --- |
| Question | With ranged followers limited to one step per creature think (calibrated on the PR-13 diagnostic block), does the model predict the hunter clear rate, potions and clear time? |
| Scenario | As PR-13 (`-Monster hunter -MonsterCount 3 -Level 30 -AxeSkill 60 -HealBelow 160 -StrikeAbove 0 -FullExperience 675 -TimeoutSeconds 200`), with the once-per-second monster trace on (`competitor.spi` `due_for_trace`) |
| Competitor | `spiral-policy` (rules heal below 160, CurrentFirst, never retreats; live.spi compile of the tree at run time) |
| Prediction (model, 100 held-out clears, 200 s cap, `uniform_camp Hunter` on the arena spot under `mirroring_the_live_arena`; `lab/tibia-fidelity/hunter_pr14_prediction_probe.spi`) | 94 of 100 clear; 2.36 potions per run (sd 1.51) over all runs; 62.0 s per clear (sd 11.8) over the clears; 15.9 hits taken; no deaths |
| Alternative | The model before the follow-pace rule: 100 of 100 clear (`lab/tibia-fidelity/hunter-stall-model.txt`) |
| Context, not part of the test | PR-13 and the diagnostic block cleared 9 of 15 live; the follow-pace rule was fitted to pace measured in that block, not to its clear rate |
| Runs | 10, one block, into `results/rows/arena_results_hunter_pr14.spi`; a crash is rerun once, then missing |
| Test | Clear rate: under the predicted 0.94, P(at most 7 of 10) = 0.019 and P(at most 8) = 0.118. One-sample t on potions over all runs and on clear time over the clears (critical 2.26 at 9 df; the clear-time df is clears minus 1) |
| Decision | The model holds when at least 9 of 10 clear and both t are within the critical value. It fails on 7 or fewer clears or on either t beyond the critical value; 8 clears with both t within is inconclusive. Descriptive: the follow-pace table (`follow-pace.ps1`) next to the model's. No runs are added after a look |
| RESULT (2026-10-19 06:03-06:28, 10 runs, no crashes, main's build paused) | FAILS on the clear rate: 7 of 10 runs clear (predicted 94 of 100; P(at most 7) = 0.019). Potions 4.10 per run (sd 2.56, t 2.15, critical 2.26) and clear time 60.4 s over the 7 clears (sd 13.5, t -0.31, critical 2.45) are within. No deaths. The follow-pace rule itself holds: live 1.00 and 0.91 tiles/s at 5 and 6 tiles from the knight, the model 0.91 and 1.00. What separates the outcomes is how far east the chase goes: over the 15 traced live runs (diagnostic block and PR-14) all 4 uncleared runs take the knight to x 1960-1967, all 11 cleared runs stay at x 1953 or less. The model's arena spot ends at radius 30 (`terrain.arena_spot_radius`, x 1963) and treats everything beyond as wall, so a hunter fleeing east is cornered there and caught; live the road continues. Next: widen the scanned arena spot (apps/map) so the model can follow the long eastward chase, then a new pre-registration |
| Post-hoc diagnosis (2026-10-20 00:04-01:00, not part of the verdict, no new runs) | The widened spot did not explain the gap. With the server map at radius 45 (`apps/map`, three 32-bit words per row) the same probe still predicts 94 of 100 clears (2.38 potions, 62.2 s). The traces show what does: in all 3 uncleared PR-14 runs the knight ends at x 1960-1967 with no hunter in view for the last 103-125 s. The last visible hunter dies there (1-7% health, adjacent), the other two are near x 1930, out of the client's view, and the policy (like the model's cavebot) stands when it sees nothing. The chase runs fast live: with the target 3 or 4 tiles away the knight covers 1.77 and 2.03 tiles/s (the model 1.15 and 1.33), largely along the road on y 1353 (x 1939-1952), which the model's arena spot priced as plain ground. Road speed now applies at the spot as on the hunting ground (`terrain.on_ground_at`, the same server map); with it and radius 45 the probe predicts 87 of 100 clears, 2.08 potions (sd 1.49), 60.0 s (sd 9.3), 14.7 hits. Two knight-speed rules were tried and reverted: the Canary step formula for a level-30 knight (450 ms on grass) on every approach step gives 100 of 100 clears in 45 s, and stepping only on the knight's think after its target moves gives 97 of 100 and still 1.24-1.35 tiles/s at 3-4 tiles. Live, the fast chase strands the knight; in the model a faster knight catches its target sooner instead. Matching both needs several interacting rules fitted to the same 25 hunter runs, so the hunter clear rate stays an open gap and no PR-15 is registered (`lab/tibia-fidelity/approach-pace.ps1`, `road-rows-r45.txt`, `hunter-seen-trace-model-*.txt`, `hunter-prediction-r45-road.txt`) |
| Scenario correction (2026-10-20 22:29, post-hoc) | PR-14's prediction scenario froze monsters for the 10 s login wake (`mirroring_the_live_arena`), but the parity layer had ended the protection at login since 2026-10-15 14:17. Without the wake the model predicts 45 of 100 clears (59.0 s, 2.97 potions per run) against live 7 of 10 here and 16 of 25 over all hunter runs: the clear-rate failure above came from the scenario, not from the follow-pace rule |

### Pre-registration PR-13, ranged hunters on the arena spot (written 2026-10-18 17:04, before any hunter run)

| Item | Value |
| --- | --- |
| Question | Does the model's ranged-creature rule set predict a clear it has never been compared with? New since the last live check: hunters (`creatures.Hunter`: 150 hp, speed 105, keep 4 tiles away, arrow 0-100 at 50% every 2 s within 7 tiles with a clear sight line, melee 0-20), Canary's keep-distance slow-down (a straight step costs x2 while the target was within 1 tile; `Monster::getDistanceStep`, `WALK_TARGET_NEARBY_EXTRA_COST`), the ported `Map::checkSightLine`, and arrows that skip the shield but spend a block charge |
| Scenario | Pack v1 with three hunters instead of three cyclops (`-Monster hunter -MonsterCount 3`, GM at 1932,1352, radius 2), knight level 30 axe 60, end at 675 experience (three kills at 225, hunters give 150 like cyclops) or 200 s |
| Competitor | `spiral-policy` (rules heal below 160, CurrentFirst, never retreats; live.spi compile of the current tree, chase mode on to approach) |
| Prediction (model, 100 held-out clears, `uniform_camp Hunter` on the arena spot under `mirroring_the_live_arena`) | 2.67 potions per clear (sd 1.89), 63.6 s with the 200 s cap (sd about 18; 99 of 100 clear, one hunter escaped the corner), 16.8 hits taken, no deaths |
| Alternatives, same model | Retreat at 2 (heal 160): 2.85 potions (sd 1.73), 64.7 s. Without login protection: 3.87 potions (sd 1.52), 60.4 s. Without the keep-distance slow-down the knight never catches a hunter in open ground and kills one in five arena runs (lab `hunter_chase_probe.spi`), so a live clear rate near 100% already favours the rule |
| Runs | 10, one block, into `results/rows/arena_results_hunter_pr13.spi`; a crash is rerun once, then missing |
| Test | One-sample t on potions per run and on clear time against the prediction (critical 2.26 at 9 df); clear rate reported |
| Decision | The rule set holds when both t are within 2.26 and at least 9 of 10 runs clear. It fails on either t beyond 2.26 or 3 or more uncleared runs; in between it is inconclusive. No runs are added after a look |
| RESULT (2026-10-18 21:25-21:50, 10 runs, no crashes) | FAILS on the clear rate: 5 of 10 runs clear (the model predicted 99 of 100). The other five stopped at the 200 s cap with 225 (four runs) or 450 experience (one run). Potions: 3.00 per run over all 10 runs (sd 2.62, t 0.40). Clear time over the 5 clears: 61.6 s (sd 10.9, t -0.40, 4 df). No deaths. Health conservation: 44 of 44 readings balance. All five uncleared runs make their last kill far east (x 1942-1957, after chasing a fleeing hunter about 10-25 tiles along y 1352-1354) and then log "attacking nothing" until the cap: the remaining hunters never come back into the client's view. The cleared runs kill at x 1920-1937. In the model the remaining hunters shadow the knight at about 4 tiles on the same chase (lab/tibia-fidelity/hunter-companions-model-run6.txt), and Canary's monster view (11 by 11) matches the model's, so the gap is in how live companions follow; a diagnostic block with a per-second monster trace comes next. The analysis script of the time printed "predicts" because it tested only the two t values; the pre-registered decision also counts uncleared runs |
| Diagnostic block (2026-10-19 05:02-05:14, 5 runs, not pre-registered, not part of the verdict) | Same scenario with a once-per-second trace of every monster the client sees (`seen at` lines; `lab/tibia-fidelity/companion-trace.ps1`), into `results/rows/arena_results_hunter_diag_1019.spi`. 4 of 5 clear (9 of 15 with PR-13). The uncleared run repeats the PR-13 pattern: the knight follows a fleeing hunter 30 tiles east along y 1353, the third hunter falls behind at 5 tiles, drops out of view at x 1933 and never returns, and the knight stands at 1963,1352 seeing nothing until the cap. Follow pace by distance to the knight (`lab/tibia-fidelity/follow-pace.ps1`, healthy hunters, knight moving): live 0.93 tiles/s at 5, 0.83 at 6; the model 1.15 and 1.19, almost never standing still. At 4 tiles and closer the model matches live. Canary re-plans a follow path on every knight move (`Creature::onCreatureMove` sets `isUpdatingPath`), so that alone does not explain the slower live pace; the model now lets a ranged hunter beyond its target distance step only on its creature think (`hunting_pack.thinks_now`), a calibration to the measured pace (model 0.91 and 1.00 tiles/s at 5 and 6) whose server-side cause is still open. With it, 94 of 100 held-out model clears finish (the 6 others strand a hunter the same way as live), against 100 before; 9 of 15 live clears is still well below |
### Pre-registration PR-11, rules vs vBot without the login-protection artifact (written 2026-10-15 14:21, before any PR-11 run)

| Item | Value |
| --- | --- |
| Question | With login protection ended the same way for both, does the Spiral rules policy use fewer potions than vBot in the cyclops clear? |
| Arena change | `live/parity.lua` (prepended to every probe) makes one native turn 300 ms after login, outside the parity gate, so Canary ends the 10 s login protection for every competitor alike. Check run (14:17-14:20, `results/rows/arena_results_cyclops_pr11_check.spi`, not analyzed): hits in the first 10 s 9 (rules) and 10 (vBot), against 1.6 and 9.1 per run in PR-9; 0 foreign actions |
| Competitors | `spiral-policy` (as in PR-9, frozen PR-4 Lua) vs `vbot` (as in PR-9) |
| Scenario | Pack v1 cyclops clear, as in PR-9, with the arena change |
| Runs | 50 per competitor, alternating, foreground chunks into `results/rows/arena_results_cyclops_pr11.spi`; foreign-activity rows excluded; a crash is rerun once, then missing |
| Test | Potions per run, Welch two-sided, alpha 0.05, 95% interval (`pr1-analysis.ps1 -Treatment spiral-policy -Baseline vbot -Planned 50 -Label PR-11 -ExcludeForeign`) |
| Expectation, stated now | No difference: after the first 10 s of PR-9 both took the same hits (60.1 vs 60.4) |
| Side prediction (2026-10-15 15:16, during the block, before any look at PR-11 data; not part of the pre-registration) | The live-mirroring model without a wake (`mirroring_the_live_arena |> waking_at 0`, no protection so no idle wandering), rules heal 160, 100 held-out clears: 15.63 potions (sd 2.88), 90.1 s, 72.4 hits, no deaths |
| Secondary (descriptive only) | Clear time, deaths, damage taken, hits in the first 10 s |
| Decision | Significant and negative: the rules policy uses fewer potions than vBot. Significant and positive: vBot uses fewer. Otherwise: no difference shown. No runs are added; no interim looks at potions |
| RESULT (2026-10-15 18:40) | No difference shown. 100 runs, 0 arena failures, 0 deaths, every run cleared. Rules 14.42 potions (sd 3.30) vs vBot 14.78 (sd 2.84), difference -0.36 (95% CI -1.58 .. 0.86), Welch t -0.58. Descriptive: clear time 88.3 vs 87.4 s, damage taken 2421 vs 2505, hits per clear 69.1 vs 69.2, hits in the first 10 s 9.6 vs 9.0. With the login protection ended alike, our rules policy and vBot are level; PR-9's win was the protection artifact. The side prediction (15.63 potions, 72.4 hits) is about 1.2 potions high (one-sample t about -2.6) |
### Pre-registration PR-10, out-of-sample check of the live-mirroring model (written 2026-10-15 12:57, before any run)

| Item | Value |
| --- | --- |
| Question | Does the live-mirroring model (`mirroring_the_live_arena`: wake, idle wandering, 2166 ms cyclops swings, swing after a step) predict a configuration it has never been compared with? |
| Competitor | `spiral-reach` (new in run-arena.ps1): the rules policy, heal below 160, ReachFirst targeting (prefer a monster already in reach), never retreats; frozen PR-4 Lua, whose targeting functions are the simulator's own `policy.spi` |
| Scenario | Pack v1 cyclops clear, as in PR-9 |
| Prediction (adopted model) | `tactic_with_order ReachFirst {160, 0, never_retreat}` on the arena spot with `mirroring_the_live_arena`, 100 held-out clears: 13.51 potions per clear (sd 3.14), 91.08 s, 63.8 hits taken, 1 death in 100 |
| Alternative (targeting makes no difference) | the CurrentFirst rules prediction, 12.74 potions, 90.74 s |
| Runs | 30, foreground chunks into `results/rows/arena_results_cyclops_pr10.spi`; foreign-activity rows excluded; a crash is rerun once, then missing |
| Test | `pr2-analysis.ps1 -Competitor spiral-reach -PredictedPotions 13.51 -PredictedSeconds 91.08 -Planned 30 -Label PR-10 -ExcludeForeign` (one-sample t, critical 2.05 at df 29); the alternative's potion t reported alongside |
| Decision | The model holds when both one-sample tests are within the critical value. With sd about 3.1 and 30 runs the test cannot separate 13.51 from 12.74 (standard error about 0.57); it is a calibration check, not a discrimination test |
| RESULT (2026-10-15 14:11) | Fails on potions. 30 runs, 0 arena failures, 0 deaths, every run cleared. Potions 12.30 (sd 2.73) vs predicted 13.51, t -2.43 (critical 2.05); clear time 89.1 s vs 91.1, t -0.86 (holds). The no-targeting-effect alternative fits (12.74, t -0.88). Live, our three policies drink alike (rules 12.46 in PR-9, ReachFirst 12.30, retreat 12.20 in PR-3a; damage 2110-2149 per clear), while the model spreads them over 11.8-13.5: it reacts too strongly to targeting and retreat |
### Pre-registration PR-9, rules vs vBot with enough runs (written 2026-10-15 08:13, before any PR-9 run)

| Item | Value |
| --- | --- |
| Question | Does the Spiral rules policy use fewer potions than vBot in the cyclops clear? |
| Why again | PR-4 (descriptive) and PR-8 (pre-registered) both point the same way (rules about 1.3 potions under vBot) with t near -1.5; each had 30 runs per side, enough only for about 1.9 potions. Pooling them after the fact gives t about -3.4, which is not a valid test; this block is |
| Competitors | `spiral-policy` (rules, heal below 160, CurrentFirst; frozen PR-4 Lua, SHA-256 7FD3D538...93E5, byte-identical to live.spi compiled on pack-1h) vs `vbot` (as in PR-4 and PR-8) |
| Scenario | Pack v1 cyclops clear, as in PR-4 |
| Runs | 50 per competitor, alternating, foreground chunks into `results/rows/arena_results_cyclops_pr9.spi`; foreign-activity rows excluded; a crash is rerun once, then missing. With sd about 3.3 and 2.7 this detects about 1.7 potions at 80% power |
| Test | Potions per run, Welch two-sided, alpha 0.05, 95% interval (`pr1-analysis.ps1 -Treatment spiral-policy -Baseline vbot -Planned 50 -Label PR-9 -ExcludeForeign`) |
| Secondary (descriptive only) | Clear time, deaths, damage taken; the live-mirroring model's rules prediction (12.74 potions, 90.7 s) next to the live mean, in-sample since PR-4 and PR-8 rules data shaped that model |
| Decision | Significant and negative: the rules policy uses fewer potions than vBot. Otherwise: no difference shown. No runs are added |
| RESULT (2026-10-15 12:42) | 100 runs, 0 arena failures, 0 deaths, every run cleared. Rules 12.46 potions (sd 3.07) vs vBot 14.66 (sd 2.71), difference -2.20 (95% CI -3.35 .. -1.05), Welch t -3.80 (critical 1.98): significant, the rules policy uses fewer potions than vBot. Descriptive: clear time 88.7 vs 87.9 s, lowest health 100.1 vs 100.0, damage taken 2145 vs 2479 (difference -334, CI -528 .. -140; 39 and 50 of 50 logs matched), so the saving is damage avoided, not healing. The live-mirroring model's rules prediction (12.74 potions, 90.7 s) sits within the live noise (in-sample) |
| Addendum (2026-10-15 14:20, post-hoc, explanatory) | The difference comes from the knight's login protection, not from hunting skill. Canary 3.6.1 ends a player's 10 s login protection on any own move, auto-walk, turn, speech, item use or attack cancel (`Game::playerMove`, `playerAutoWalk`, `playerTurn`, `playerSay`, `playerUseItem*`, cancel in `playerSetAttackedCreature`); starting an attack does not. Our probe only attacks in the first seconds and keeps the protection; vBot ends it. Hits taken in the first 10 s: vBot 9.14 per run, rules 1.58; vBot's whole excess is 7.2 hits per clear (69.2 vs 62.0). After the first 10 s the two are level (60.1 vs 60.4 hits). The verdict stands as computed, but it measures a scenario artifact: every run starts from a fresh login with cyclopes summoned next to the knight |
| Interim look (2026-10-15 10:37, disclosed) | At 25 of 50 pairs, a log query meant for a side question (do vBot's drinks get rejected?) pooled all logs since 00:23, so it printed potions per run for PR-8 and the PR-9 runs so far together. The run count stays 50 per side and nothing stops early, so the decision rule is unchanged; no further looks until the block ends |
### Pre-registration PR-8, rules vs vBot replication (written 2026-10-15 00:23, before any run)

| Item | Value |
| --- | --- |
| Question | Does the Spiral rules policy use fewer potions than vBot in the cyclops clear? Motivated by PR-4's descriptive, not pre-registered, numbers (rules 11.53, vBot 14.00 potions per clear): this is a fresh replication |
| Competitors | `spiral-policy` (rules, heal below 160, CurrentFirst; frozen PR-4 Lua) vs `vbot` (as in PR-4) |
| Scenario | Pack v1 cyclops clear, as in PR-4 |
| Runs | 30 per competitor, alternating, foreground chunks into `results/rows/arena_results_cyclops_pr8.spi`; foreign-activity rows excluded; a crash is rerun once, then missing. With PR-4's spreads (sd 2.5-2.6) this detects about 1.9 potions at 80% power |
| Test | Potions per run, Welch two-sided, alpha 0.05, 95% interval (`pr1-analysis.ps1 -Treatment spiral-policy -Baseline vbot -Planned 30 -Label PR-8 -ExcludeForeign`) |
| Secondary (descriptive only) | Clear time, deaths, damage taken |
| Decision | Significant and negative: the rules policy uses fewer potions than vBot. Otherwise: no difference shown. No runs are added |
| RESULT (2026-10-15 03:06) | 60 runs, 0 arena failures, 0 deaths, every run cleared. Rules 12.90 potions (sd 3.88) vs vBot 14.17 (sd 2.67), difference -1.27 (95% CI -2.99 .. 0.46), Welch t -1.47: no difference shown. PR-4's descriptive rules mean (11.53) did not replicate. Descriptive: clear time 92.6 vs 88.1 s, damage taken 2136 vs 2398 |
## Metrics

| Metric | Definition |
| --- | --- |
| XP/h | Experience per in-game hour |
| Profit/h | Loot value minus supplies, at a published price table |
| Deaths/h | Deaths per in-game hour |
| Score | XP/h subject to deaths/h at or below the best baseline's, reported with a confidence interval |

Every comparison is pre-registered: hypothesis, primary metric, test and run count are written down before the first run,
and the data is analysed once, after the last run. Interim looks are limited to counting completed runs. A comparison that
ends inconclusive is reported as such; a new comparison needs a new pre-registration.

## Evaluation cost

The open-source bots run only inside a real OTClient against a real Canary server, so their runs take wall-clock time. Our
simulator runs faster than real time, but arena results always come from the real server so every competitor is
measured the same way.

| Quantity | Starting assumption (to measure in P4) |
| --- | --- |
| Scenario length | 30 in-game minutes |
| Runs per scenario per competitor | 10 seeds to start, more until the interval separates |
| Competitors | 5 (three bots, scripted cavebot, agent; random policy in the simulator only) |
| Parallel client+server pairs on the 16 GB laptop | 2-3 (measure memory per pair first) |

Example: 6 scenarios x 5 competitors x 10 seeds x 0.5 h = 150 hours of play, about 2-3 days at 2-3 pairs in
parallel per full pass. A second pass for tighter intervals doubles it. Rented CPU instances can run more pairs if
the laptop becomes the bottleneck.

## Promotion

From TritCritNet's control panel: an agent is promoted only when it beats the best baseline, the random policy stays at
its level (no luck), and the result reproduces on independent seed windows, all under a declared compute budget.

Tuning rules, from the first simulator runs (2026-10-13, `hunt_sim.spi`):
- The agent and every baseline are tuned on the same training seeds and scored on held-out seeds.
- Use at least 20 training seeds. An evolution search on 6 seeds overfit: it scored 31 on held-out seeds where a
  cavebot scored 34.
- A baseline tuned to the edge of its range is not tuned. In the room-and-corridor scenario the cavebot's heal sweep (40-200)
  picked 200, and evolved rules 'won' by 81600 gp; with the sweep widened to 400 the cavebot tuned to 230 and the win
  vanished (-477181 vs -477431 gp). Sweeps now reach `hunting_pack.highest_tuned_heal`.
- A death rarer than the training set can show is invisible to tuning. On the mixed camp, evolved rules saw 0 deaths in
  100 training clears and pushed the heal threshold down; held out they died twice, against the cavebot's once, and lost on
  cost. When deaths are near 1%, train on fresh seed blocks per generation (`search.evolve_vector_resampled`) or on several
  hundred seeds.
- Fix the number of runs before looking. The retreat policy 'beat' vBot after 20 runs (t -2.14) and 38 (t -2.36); 18 more
  runs brought it to t -1.89, a tie. Stopping when a result looks significant inflates claims.
- Never compare against hand-picked baseline settings. A hand-picked cavebot "lost" 34 to 90; the same cavebot,
  tuned on the training seeds, tied at 90.
- Every design choice is also made on training seeds: kiting thresholds, network wiring, search budgets. In an early
  20-seed run, kiting lost on the training seeds but would have looked like a gain (1113 vs 1099) if picked on the
  held-out seeds.
- Training seeds must contain the rare events that decide the score. On the cyclops pack, 20 training seeds held no
  deaths, so every heal threshold tied and tuning picked one arbitrarily; 100 seeds hold 7 deaths and separate the
  thresholds (`pack_sim.spi`). The protocol constants live in `hunting_pack.spi` (`training_first`, `held_out_first`).
- That gap later closed for the right reason: once creature hits used Canary's blocks, deaths vanished at every
  threshold. A scenario that cannot separate policies is saturated, not solved; the cyclops pack is now judged on kills
  and profit together, and a policy beats the baseline only if it is at least as good on both and better on one
  (Pareto dominance). Training uses profit with the baseline's kills as a floor (`ProfitKeepingKills`).
- The arena world turns off Canary's forge (`forgeInfluencedLimit = 0`, `forgeFiendishLimit = 0`). With the default
  limits, almost every monster in the small world became influenced or fiendish, which raised cyclops hits from a
  34.7 mean to 61.8 and made runs incomparable.

## Publication

Published: arena, scenario specs, seeds, server build recipe, baseline configs, results. Not published: CipSoft
map, sprites or item data. The arena runs only on local OT servers.
