From 379339b888122eac432c9ffa83aae1c72320bfb8 Mon Sep 17 00:00:00 2001 From: ProtocolWarden Date: Wed, 15 Jul 2026 07:19:20 -0400 Subject: [PATCH] fix(OperationsCenter): parse claude weekly reset without minutes for cooldown gate --- .../backends/worker_backend_selector.py | 8 ++++++-- tests/unit/backends/test_worker_backend_selector_cov.py | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/operations_center/backends/worker_backend_selector.py b/src/operations_center/backends/worker_backend_selector.py index a4ea69ef..8a3ab5c2 100644 --- a/src/operations_center/backends/worker_backend_selector.py +++ b/src/operations_center/backends/worker_backend_selector.py @@ -43,7 +43,10 @@ "codex": "codex_cli", "openai": "codex_cli", } -_TIMEZONE_RESET_RE = re.compile(r"resets\s+(\d{1,2}:\d{2}(?:am|pm))\s+\(([^)]+)\)", re.IGNORECASE) +_TIMEZONE_RESET_RE = re.compile( + r"resets\s+(\d{1,2}(?::\d{2})?(?:am|pm))\s+\(([^)]+)\)", + re.IGNORECASE, +) _ISO_RESET_RE = re.compile( r"resets?(?:\s+at)?\s+(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?Z)", re.IGNORECASE, @@ -219,7 +222,8 @@ def parse_worker_backend_reset( except ZoneInfoNotFoundError: return None now_local = current.astimezone(tz) - parsed = datetime.strptime(time_str, "%I:%M%p") # noqa: DTZ007 + time_fmt = "%I:%M%p" if ":" in time_str else "%I%p" + parsed = datetime.strptime(time_str, time_fmt) # noqa: DTZ007 reset_local = now_local.replace( hour=parsed.hour, minute=parsed.minute, diff --git a/tests/unit/backends/test_worker_backend_selector_cov.py b/tests/unit/backends/test_worker_backend_selector_cov.py index 68d25213..759b66f2 100644 --- a/tests/unit/backends/test_worker_backend_selector_cov.py +++ b/tests/unit/backends/test_worker_backend_selector_cov.py @@ -193,6 +193,15 @@ def test_parse_timezone_reset_later_today() -> None: assert (local.hour, local.minute) == (14, 30) +def test_parse_timezone_reset_without_minutes_later_today() -> None: + out = "You've hit your weekly limit ยท resets 9am (America/New_York)" + result = parse_worker_backend_reset(out, "claude_code", now=NOW) + assert result is not None + assert result.tzinfo == UTC + local = result.astimezone(__import__("zoneinfo").ZoneInfo("America/New_York")) + assert (local.hour, local.minute) == (9, 0) + + def test_parse_timezone_reset_rolls_to_next_day() -> None: # 8:00am EDT when it is already ~08:00 EDT -> should roll forward a day. out = "resets 8:00am (America/New_York)"