diff --git a/bot_bottle/orchestrator/service.py b/bot_bottle/orchestrator/service.py index 5c34815..924fa7b 100644 --- a/bot_bottle/orchestrator/service.py +++ b/bot_bottle/orchestrator/service.py @@ -157,8 +157,17 @@ class Orchestrator: return [p.to_dict() for p in list_all_pending_proposals()] def _record_for_slug(self, slug: str) -> BottleRecord | None: - """The live registry record whose metadata carries `slug`, or None - (e.g. the bottle was torn down before the operator responded).""" + """The live registry record for a proposal's bottle, or None (e.g. the + bottle was torn down before the operator responded). + + In consolidated mode the supervise server attributes each proposal to + the orchestrator-assigned bottle_id and stores that as the proposal's + `bottle_slug` (see supervise_server `_attributed_config`), so the fast + path is a direct bottle_id lookup. The metadata-slug scan is the + fallback for legacy single-tenant proposals keyed by the human slug.""" + rec = self.registry.get(slug) + if rec is not None: + return rec for rec in self.registry.all(): try: meta = json.loads(rec.metadata) if rec.metadata else {} diff --git a/tests/unit/test_orchestrator_service.py b/tests/unit/test_orchestrator_service.py index 8957ba0..f724d66 100644 --- a/tests/unit/test_orchestrator_service.py +++ b/tests/unit/test_orchestrator_service.py @@ -226,6 +226,21 @@ class TestOrchestratorSupervise(unittest.TestCase): self.assertEqual(STATUS_APPROVED, read_response("demo", pid).status) self.assertEqual([], self.orch.supervise_pending()) + def test_approve_by_bottle_id_applies_policy(self) -> None: + # Consolidated reality: the supervise server keys each proposal by the + # orchestrator-assigned bottle_id, not the human slug. Approval must + # resolve the record by that id and apply the policy (regression for + # the "bottle is no longer registered" 409). + bottle_id = self._register("codex-dev-a1b2c", "routes: []\n") + new_routes = "routes:\n - host: google.com\n" + pid = self._queue(bottle_id, new_routes) + ok, err = self.orch.supervise_respond( + pid, bottle_slug=bottle_id, decision="approve") + self.assertTrue(ok, err) + rec = self.store.get(bottle_id) + assert rec is not None + self.assertEqual(new_routes, rec.policy) + def test_modify_applies_final_file_not_proposed(self) -> None: bottle_id = self._register("demo", "routes: []\n") pid = self._queue("demo", "routes:\n - host: google.com\n")