Require complete router configuration before veto

This commit is contained in:
acamilo 2026-09-26 01:23:31 +00:00
parent dccc52ae1c
commit 1bd2223431
3 changed files with 11 additions and 3 deletions

View file

@ -17,8 +17,10 @@ COOLDOWN = 3600
def verdict(report): def verdict(report):
url = os.environ.get("FLY_LOOP_ROUTER_URL") url = os.environ.get("FLY_LOOP_ROUTER_URL")
model = os.environ.get("FLY_LOOP_MODEL") model = os.environ.get("FLY_LOOP_MODEL")
if not url or not model: if not url and not model:
return True return True
if not url or not model:
return False
payload = {"model": model, "temperature": 0, "messages": [ payload = {"model": model, "temperature": 0, "messages": [
{"role": "system", "content": "Classify whether a suspected repeating game macro loop is truly stuck. Return only JSON {\"stuck\":true|false}. No instructions or commands."}, {"role": "system", "content": "Classify whether a suspected repeating game macro loop is truly stuck. Return only JSON {\"stuck\":true|false}. No instructions or commands."},
{"role": "user", "content": json.dumps({key: report.get(key) for key in ("reason", "sequence", "window", "places", "milestone")})}, {"role": "user", "content": json.dumps({key: report.get(key) for key in ("reason", "sequence", "window", "places", "milestone")})},

View file

@ -18,8 +18,8 @@ containing `FLY_LOOP_ROUTER_URL` (base URL ending in `/v1`) and
`FLY_LOOP_MODEL` (an available free-tier model). A private router can additionally `FLY_LOOP_MODEL` (an available free-tier model). A private router can additionally
use `FLY_LOOP_ROUTER_KEY`; provision it outside this public checkout and limit use `FLY_LOOP_ROUTER_KEY`; provision it outside this public checkout and limit
file permissions to `0640 root:fly`. Never put credentials in the unit or the file permissions to `0640 root:fly`. Never put credentials in the unit or the
repository. When a router is configured, malformed or unavailable responses repository. When either router setting is present, both must be set; malformed or
prevent recovery; the model may only return `{"stuck": true|false}` and cannot unavailable responses prevent recovery. The model may only return `{"stuck": true|false}` and cannot
choose commands, buttons, or checkpoint paths. Confirm the model actually choose commands, buttons, or checkpoint paths. Confirm the model actually
exists and is reachable from the release container before configuring it. exists and is reachable from the release container before configuring it.

View file

@ -76,6 +76,12 @@ class RecoveryTests(unittest.TestCase):
self.assertIn("did not confirm", recover.run(1300)) self.assertIn("did not confirm", recover.run(1300))
restart.assert_not_called() restart.assert_not_called()
def test_partial_router_configuration_cannot_bypass_veto(self):
with patch.dict("os.environ", {"FLY_LOOP_ROUTER_URL": "http://router/v1"}, clear=True):
self.assertFalse(recover.verdict(self.report))
with patch.dict("os.environ", {"FLY_LOOP_MODEL": "free"}, clear=True):
self.assertFalse(recover.verdict(self.report))
def test_unconfigured_router_uses_deterministic_confirmation(self): def test_unconfigured_router_uses_deterministic_confirmation(self):
with patch.dict("os.environ", {}, clear=True): with patch.dict("os.environ", {}, clear=True):
self.assertTrue(recover.verdict(self.report)) self.assertTrue(recover.verdict(self.report))