Files
my-botty/test/test_act_detection.py
alexpolo1andClaude Opus 5 31dacab9e3 test: pin the act-margin invariant for BOTH act-deciding functions
The same defect has now appeared twice: detect_current_act was fixed in Bug 28,
wait_for_town_spawn kept it, and nothing linked the two. These tests assert the
invariant for both, so a future fix to one cannot silently leave the other
behind:

- both must require _ACT_DETECT_MARGIN over the best rival-act marker
- both must score own-act and rival markers on ONE image (scores from two
  different grabs are not comparable — the character can move between them)
- ambiguous detection must return None rather than guess

Audited every other TOWN_MARKERS consumer: bot.py:561 and main_menu.py:40/61
use best_match only as a boolean "are we in town?" check and never derive an
act, so they need no margin.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-28 01:03:01 +02:00

69 lines
2.6 KiB
Python

"""Act identity must never be decided on a thin margin.
Bug 28: detect_current_act committed to whatever town marker won, and in Act 4
the A5_TOWN_1 template scored 0.636 on corner scenery while the genuine
A4_TOWN_5 scored 0.619. A 1.7pp gap decided the act, and the bot ran A5 pathing
inside Act 4 after every Halbu repair trip.
That was fixed in detect_current_act and NOT in wait_for_town_spawn, which runs
first every game and sets the act for everything after it. On 2026-08-28 the
character spawned in Harrogath and it reported a4_town_start every game.
These tests pin the invariant for BOTH, because the bug has now been the same
twice: a fix applied to one call site while its twin kept the defect.
"""
import inspect
import pytest
ACT_DECIDING_FUNCS = ["detect_current_act", "wait_for_town_spawn"]
@pytest.mark.parametrize("func_name", ACT_DECIDING_FUNCS)
def test_act_decision_requires_a_margin(func_name):
from town.town_manager import TownManager
src = inspect.getsource(getattr(TownManager, func_name))
assert "_ACT_DETECT_MARGIN" in src, (
f"{func_name} decides an act without requiring a margin over the best "
"marker from another act — a marginal winner is not evidence"
)
assert "TOWN_MARKERS_ACT" in src, f"{func_name} does not separate own-act from rival markers"
@pytest.mark.parametrize("func_name", ACT_DECIDING_FUNCS)
def test_act_decision_scores_both_sides_on_one_image(func_name):
"""Comparing scores from two different grabs is not a comparison.
The character can move between them, so the margin becomes meaningless.
"""
from town.town_manager import TownManager
src = inspect.getsource(getattr(TownManager, func_name))
grabs = src.count("grab(force_new=True)") + src.count("grab()")
assert grabs <= 1, (
f"{func_name} grabs {grabs} images; own-act and rival scores must come "
"from the same frame to be comparable"
)
def test_margin_is_meaningfully_large():
from town.town_manager import TownManager
assert TownManager._ACT_DETECT_MARGIN >= 0.03, "margin too small to separate a phantom from a real marker"
def test_ambiguous_detection_returns_none_not_a_guess():
"""Refusing to answer is the safe outcome.
Callers treat None as "keep the assumed act", which makes go_to_act actually
travel rather than wrongly early-returning. A confident wrong answer skips
the recovery path entirely.
"""
from town.town_manager import TownManager
src = inspect.getsource(TownManager.detect_current_act)
assert "refusing to guess" in src
assert src.rstrip().endswith("return None")