Skip to content

Commit 51386ba

Browse files
committed
fix(quota): expose typed CLI error codes
1 parent a29b23a commit 51386ba

3 files changed

Lines changed: 52 additions & 1 deletion

File tree

loopx/cli_commands/quota.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
compact_quota_monitor_poll_cli_payload,
1010
compact_quota_should_run_cli_payload,
1111
)
12+
from ..control_plane.quota.error_codes import quota_error_code
1213
from ..control_plane.quota.heartbeat_receipt import (
1314
fail_heartbeat_receipt,
1415
find_heartbeat_receipt,
@@ -493,6 +494,7 @@ def _quota_failure_payload(
493494
"mode": command,
494495
"registry": str(registry_path),
495496
"runtime_root": runtime_root_arg,
497+
"error_code": quota_error_code(error),
496498
"error": str(error),
497499
"summary": {
498500
"registered_goals": 0,
@@ -520,6 +522,7 @@ def _quota_failure_payload(
520522
"goal_id": args.goal_id,
521523
"decision": "skip",
522524
"should_run": False,
525+
"error_code": quota_error_code(error),
523526
"reason": str(error),
524527
"state": "blocked_health",
525528
"waiting_on": "codex",
@@ -813,7 +816,7 @@ def handle_quota_command(
813816
payload = build_quota_plan(status_payload, mode=args.quota_command)
814817
if cache_metadata:
815818
payload["status_projection_cache"] = cache_metadata
816-
except Exception as exc:
819+
except Exception as exc: # noqa: BLE001 - CLI fail-safe boundary; error_code is typed below.
817820
payload = _quota_failure_payload(
818821
args,
819822
registry_path=registry_path,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from __future__ import annotations
2+
3+
import json
4+
5+
6+
def quota_error_code(exc: BaseException) -> str:
7+
if isinstance(exc, json.JSONDecodeError):
8+
return "quota_state_invalid_json"
9+
if isinstance(exc, ValueError):
10+
return "quota_invalid_arguments"
11+
if isinstance(exc, PermissionError):
12+
return "quota_state_permission_denied"
13+
if isinstance(exc, OSError):
14+
return "quota_state_io_failed"
15+
if isinstance(exc, KeyError):
16+
return "quota_state_missing_field"
17+
if isinstance(exc, TypeError):
18+
return "quota_state_shape_error"
19+
return "quota_unexpected_collection_error"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from __future__ import annotations
2+
3+
import json
4+
5+
import pytest
6+
7+
from loopx.control_plane.quota.error_codes import quota_error_code
8+
9+
10+
@pytest.mark.parametrize(
11+
("exc", "expected"),
12+
[
13+
(ValueError("bad argument"), "quota_invalid_arguments"),
14+
(OSError("state unavailable"), "quota_state_io_failed"),
15+
(PermissionError("state denied"), "quota_state_permission_denied"),
16+
(KeyError("missing"), "quota_state_missing_field"),
17+
(TypeError("bad shape"), "quota_state_shape_error"),
18+
(
19+
json.JSONDecodeError("invalid json", "doc", 0),
20+
"quota_state_invalid_json",
21+
),
22+
(RuntimeError("unexpected"), "quota_unexpected_collection_error"),
23+
],
24+
)
25+
def test_quota_error_code_maps_typed_exceptions(
26+
exc: BaseException,
27+
expected: str,
28+
) -> None:
29+
assert quota_error_code(exc) == expected

0 commit comments

Comments
 (0)