claude-dev-env 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/_shared/advisor/advisor-protocol.md +2 -2
  2. package/_shared/pr-loop/scripts/CLAUDE.md +1 -0
  3. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -284
  4. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -0
  5. package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +3 -3
  6. package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -0
  7. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +5 -0
  8. package/_shared/pr-loop/scripts/stale_worktree_rule_sweep.py +107 -0
  9. package/_shared/pr-loop/scripts/tests/CLAUDE.md +2 -0
  10. package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +55 -73
  11. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +9 -15
  12. package/_shared/pr-loop/scripts/tests/test_grant_project_claude_permissions.py +91 -1
  13. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +88 -1
  14. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -0
  15. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -0
  16. package/_shared/pr-loop/worker-spawn.md +1 -1
  17. package/agents/code-verifier.md +1 -1
  18. package/hooks/blocking/config/verified_commit_constants.py +11 -0
  19. package/hooks/blocking/test_code_verifier_tools_contract.py +28 -0
  20. package/hooks/blocking/test_verification_verdict_store.py +93 -0
  21. package/hooks/blocking/verification_verdict_store.py +91 -0
  22. package/package.json +1 -1
  23. package/scripts/CLAUDE.md +2 -1
  24. package/scripts/claude-chain.example.json +15 -3
  25. package/scripts/claude_chain_runner.py +130 -27
  26. package/scripts/claude_chain_usage.py +346 -0
  27. package/scripts/dev_env_scripts_constants/CLAUDE.md +4 -3
  28. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +13 -1
  29. package/scripts/dev_env_scripts_constants/claude_chain_usage_constants.py +58 -0
  30. package/scripts/dev_env_scripts_constants/code_review_constants.py +1 -1
  31. package/scripts/dev_env_scripts_constants/timing.py +1 -1
  32. package/scripts/test_claude_chain_runner.py +412 -6
  33. package/scripts/test_claude_chain_usage.py +534 -0
  34. package/skills/orchestrator/SKILL.md +1 -20
  35. package/skills/orchestrator-refresh/SKILL.md +1 -1
  36. package/skills/pr-converge/SKILL.md +3 -3
  37. package/skills/pr-converge/reference/examples.md +3 -3
  38. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  39. package/skills/pr-converge/reference/ground-rules.md +3 -3
  40. package/skills/pr-converge/reference/per-tick.md +5 -5
  41. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  42. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  43. package/skills/team-advisor/SKILL.md +3 -2
@@ -0,0 +1,346 @@
1
+ #!/usr/bin/env python3
2
+ """Report remaining weekly usage for every account in the Claude chain.
3
+
4
+ Loads ``~/.claude/claude-chain.json`` (or a path the caller names), probes each
5
+ entry's weekly utilization through the usage-pause OAuth endpoint, and prints a
6
+ JSON report. Ranking is available as an importable helper; ``run_claude``
7
+ consumes that ranking to choose try order.
8
+
9
+ ::
10
+
11
+ python claude_chain_usage.py
12
+ {"accounts": [
13
+ {"command": "claude", "weekly_remaining_percent": 58.0},
14
+ {"command": "claude-ev", "weekly_remaining_percent": null, "error": "..."}
15
+ ]}
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import http.client
22
+ import importlib.util
23
+ import json
24
+ import sys
25
+ import urllib.error
26
+ from collections.abc import Callable
27
+ from dataclasses import dataclass
28
+ from datetime import datetime
29
+ from pathlib import Path
30
+ from types import ModuleType
31
+
32
+ from dev_env_scripts_constants.claude_chain_constants import CHAIN_CONFIG_ERROR_EXIT_CODE
33
+ from dev_env_scripts_constants.claude_chain_usage_constants import (
34
+ CLI_CONFIG_PATH_FLAG,
35
+ FULL_WEEKLY_PERCENT,
36
+ JSON_ACCOUNTS_KEY,
37
+ JSON_COMMAND_KEY,
38
+ JSON_ERROR_KEY,
39
+ JSON_WEEKLY_REMAINING_PERCENT_KEY,
40
+ NO_ACCESS_TOKEN_ERROR_TEMPLATE,
41
+ RESOLVE_USAGE_WINDOW_FILENAME,
42
+ RESOLVE_USAGE_WINDOW_MISSING_ERROR_TEMPLATE,
43
+ RESOLVE_USAGE_WINDOW_MODULE_NAME,
44
+ USAGE_PAUSE_SCRIPTS_DIRECTORY_NAME,
45
+ USAGE_PAUSE_SKILL_DIRECTORY_NAME,
46
+ USAGE_PAUSE_SKILL_NAME,
47
+ USAGE_PROBE_FAILED_ERROR_TEMPLATE,
48
+ WEEKLY_UTILIZATION_MISSING_ERROR,
49
+ )
50
+
51
+ import claude_chain_runner as chain_runner
52
+
53
+ WeeklyUtilizationProbe = Callable[[Path], float]
54
+
55
+
56
+ class WeeklyUtilizationProbeError(Exception):
57
+ """Raised when the weekly_utilization_probe cannot measure one account."""
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class AccountUsageReport:
62
+ """One chain account's remaining weekly usage, or the reason it is unknown."""
63
+
64
+ command: str
65
+ weekly_remaining_percent: float | None
66
+ error: str | None = None
67
+
68
+
69
+ def _default_credentials_path() -> Path:
70
+ usage_window_resolver = _load_resolve_usage_window_module()
71
+ return Path.home().joinpath(
72
+ *usage_window_resolver.ALL_CREDENTIALS_RELATIVE_PATH_PARTS
73
+ )
74
+
75
+
76
+ def _credentials_path_for_entry(entry: chain_runner.ChainEntry) -> Path:
77
+ if entry.credentials_path is None:
78
+ return _default_credentials_path()
79
+ return Path(entry.credentials_path).expanduser()
80
+
81
+
82
+ def _usage_pause_scripts_directory() -> Path:
83
+ package_root = Path(__file__).resolve().parent.parent
84
+ return (
85
+ package_root
86
+ / USAGE_PAUSE_SKILL_DIRECTORY_NAME
87
+ / USAGE_PAUSE_SKILL_NAME
88
+ / USAGE_PAUSE_SCRIPTS_DIRECTORY_NAME
89
+ )
90
+
91
+
92
+ def _discard_sys_path_entry(path_text: str) -> None:
93
+ if path_text in sys.path:
94
+ sys.path.remove(path_text)
95
+
96
+
97
+ def _load_resolve_usage_window_module() -> ModuleType:
98
+ already_loaded = sys.modules.get(RESOLVE_USAGE_WINDOW_MODULE_NAME)
99
+ if already_loaded is not None:
100
+ return already_loaded
101
+ scripts_directory = _usage_pause_scripts_directory()
102
+ module_path = scripts_directory / RESOLVE_USAGE_WINDOW_FILENAME
103
+ if not module_path.is_file():
104
+ raise WeeklyUtilizationProbeError(
105
+ RESOLVE_USAGE_WINDOW_MISSING_ERROR_TEMPLATE.format(
106
+ module_path=module_path
107
+ )
108
+ )
109
+ scripts_directory_text = str(scripts_directory)
110
+ was_path_inserted = False
111
+ if scripts_directory_text not in sys.path:
112
+ sys.path.insert(0, scripts_directory_text)
113
+ was_path_inserted = True
114
+ is_module_ready = False
115
+ try:
116
+ module_specification = importlib.util.spec_from_file_location(
117
+ RESOLVE_USAGE_WINDOW_MODULE_NAME, module_path
118
+ )
119
+ if module_specification is None or module_specification.loader is None:
120
+ raise WeeklyUtilizationProbeError(
121
+ RESOLVE_USAGE_WINDOW_MISSING_ERROR_TEMPLATE.format(
122
+ module_path=module_path
123
+ )
124
+ )
125
+ loaded_module = importlib.util.module_from_spec(module_specification)
126
+ sys.modules[RESOLVE_USAGE_WINDOW_MODULE_NAME] = loaded_module
127
+ module_specification.loader.exec_module(loaded_module)
128
+ is_module_ready = True
129
+ finally:
130
+ if not is_module_ready:
131
+ sys.modules.pop(RESOLVE_USAGE_WINDOW_MODULE_NAME, None)
132
+ if was_path_inserted:
133
+ _discard_sys_path_entry(scripts_directory_text)
134
+ return loaded_module
135
+
136
+
137
+ def _probe_weekly_utilization(credentials_path: Path) -> float:
138
+ usage_window_resolver = _load_resolve_usage_window_module()
139
+ now = datetime.now().astimezone()
140
+ try:
141
+ access_token = usage_window_resolver.read_oauth_access_token(
142
+ credentials_path, now
143
+ )
144
+ if access_token is None:
145
+ raise WeeklyUtilizationProbeError(
146
+ NO_ACCESS_TOKEN_ERROR_TEMPLATE.format(credentials_path=credentials_path)
147
+ )
148
+ usage_payload = usage_window_resolver._fetch_usage_payload(access_token)
149
+ except WeeklyUtilizationProbeError:
150
+ raise
151
+ except (
152
+ urllib.error.URLError,
153
+ http.client.HTTPException,
154
+ TimeoutError,
155
+ OSError,
156
+ ValueError,
157
+ ) as probe_error:
158
+ raise WeeklyUtilizationProbeError(
159
+ USAGE_PROBE_FAILED_ERROR_TEMPLATE.format(error=probe_error)
160
+ ) from probe_error
161
+ usage_windows = usage_window_resolver.extract_usage_windows(usage_payload)
162
+ if usage_windows.weekly_utilization is None:
163
+ raise WeeklyUtilizationProbeError(WEEKLY_UTILIZATION_MISSING_ERROR)
164
+ return float(usage_windows.weekly_utilization)
165
+
166
+
167
+ weekly_utilization_probe: WeeklyUtilizationProbe = _probe_weekly_utilization
168
+
169
+
170
+ def weekly_remaining_from_utilization(weekly_utilization: float) -> float:
171
+ """Convert weekly utilization percent into remaining weekly percent.
172
+
173
+ ::
174
+
175
+ weekly_remaining_from_utilization(42.0) -> 58.0
176
+ weekly_remaining_from_utilization(9.0) -> 91.0
177
+
178
+ Args:
179
+ weekly_utilization: Used weekly capacity on the 0–100 scale.
180
+
181
+ Returns:
182
+ Remaining weekly capacity on the same 0–100 scale.
183
+ """
184
+ return FULL_WEEKLY_PERCENT - weekly_utilization
185
+
186
+
187
+ def _report_for_entry(
188
+ entry: chain_runner.ChainEntry,
189
+ active_probe: WeeklyUtilizationProbe,
190
+ ) -> AccountUsageReport:
191
+ try:
192
+ credentials_path = _credentials_path_for_entry(entry)
193
+ weekly_utilization = active_probe(credentials_path)
194
+ except (WeeklyUtilizationProbeError, ImportError, AttributeError) as probe_error:
195
+ return AccountUsageReport(
196
+ command=entry.command,
197
+ weekly_remaining_percent=None,
198
+ error=str(probe_error),
199
+ )
200
+ return AccountUsageReport(
201
+ command=entry.command,
202
+ weekly_remaining_percent=weekly_remaining_from_utilization(
203
+ weekly_utilization
204
+ ),
205
+ )
206
+
207
+
208
+ def report_chain_weekly_usage(*, config_path: Path) -> list[AccountUsageReport]:
209
+ """Report remaining weekly usage for every account in the chain config.
210
+
211
+ ::
212
+
213
+ report_chain_weekly_usage(config_path=Path("chain.json"))
214
+ -> [AccountUsageReport("claude", 58.0), ...]
215
+
216
+ Uses the module-level ``weekly_utilization_probe`` (tests rebind it).
217
+
218
+ Args:
219
+ config_path: Chain configuration file to load.
220
+
221
+ Returns:
222
+ One report per chain entry, in config order.
223
+
224
+ Raises:
225
+ ChainConfigurationError: When the chain configuration cannot be loaded.
226
+ """
227
+ all_entries = chain_runner.load_chain(config_path)
228
+ return [
229
+ _report_for_entry(each_entry, weekly_utilization_probe)
230
+ for each_entry in all_entries
231
+ ]
232
+
233
+
234
+ def _nulls_last_remaining_rank(report: AccountUsageReport) -> tuple[int, float]:
235
+ if report.weekly_remaining_percent is None:
236
+ return (1, 0.0)
237
+ return (0, -report.weekly_remaining_percent)
238
+
239
+
240
+ def rank_accounts_by_weekly_remaining(
241
+ all_reports: list[AccountUsageReport],
242
+ ) -> list[AccountUsageReport]:
243
+ """Order reports by remaining weekly usage, highest first.
244
+
245
+ ::
246
+
247
+ [40%, 70%, null, 70%] -> [70% (earlier), 70% (later), 40%, null]
248
+
249
+ Ties keep config order. Unmeasurable accounts (null remaining) sort last.
250
+
251
+ Args:
252
+ all_reports: Reports in chain-config order.
253
+
254
+ Returns:
255
+ A new list ordered by remaining weekly percent descending.
256
+ """
257
+ return sorted(all_reports, key=_nulls_last_remaining_rank)
258
+
259
+
260
+ def _account_report_to_json_mapping(
261
+ report: AccountUsageReport,
262
+ ) -> dict[str, str | float | None]:
263
+ if report.weekly_remaining_percent is None:
264
+ return {
265
+ JSON_COMMAND_KEY: report.command,
266
+ JSON_WEEKLY_REMAINING_PERCENT_KEY: None,
267
+ JSON_ERROR_KEY: report.error,
268
+ }
269
+ return {
270
+ JSON_COMMAND_KEY: report.command,
271
+ JSON_WEEKLY_REMAINING_PERCENT_KEY: report.weekly_remaining_percent,
272
+ }
273
+
274
+
275
+ def reports_to_json_payload(
276
+ all_reports: list[AccountUsageReport],
277
+ ) -> dict[str, list[dict[str, str | float | None]]]:
278
+ """Build the CLI JSON payload for a list of account reports.
279
+
280
+ ::
281
+
282
+ reports_to_json_payload([AccountUsageReport("claude", 58.0)])
283
+ -> {"accounts": [{"command": "claude", "weekly_remaining_percent": 58.0}]}
284
+
285
+ Args:
286
+ all_reports: Reports in the order they should appear in the payload.
287
+
288
+ Returns:
289
+ The ``{"accounts": [...]}`` envelope.
290
+ """
291
+ return {
292
+ JSON_ACCOUNTS_KEY: [
293
+ _account_report_to_json_mapping(each_report)
294
+ for each_report in all_reports
295
+ ]
296
+ }
297
+
298
+
299
+ def _build_argument_parser() -> argparse.ArgumentParser:
300
+ parser = argparse.ArgumentParser(
301
+ description=(
302
+ "Report remaining weekly usage for every account in the Claude chain."
303
+ )
304
+ )
305
+ parser.add_argument(
306
+ CLI_CONFIG_PATH_FLAG,
307
+ dest="config_path",
308
+ default=None,
309
+ help="Path to claude-chain.json; defaults to the per-user chain config.",
310
+ )
311
+ return parser
312
+
313
+
314
+ def main(all_command_arguments: list[str]) -> int:
315
+ """Probe the chain and print the weekly-usage JSON report on stdout.
316
+
317
+ ::
318
+
319
+ main(["--config-path", "chain.json"]) -> 0 # JSON on stdout
320
+ main(["--config-path", "missing.json"]) -> 3 # error on stderr
321
+
322
+ Args:
323
+ all_command_arguments: The argument vector after the program name.
324
+
325
+ Returns:
326
+ Zero on success, or the chain-config error exit code when the config
327
+ cannot be loaded.
328
+ """
329
+ parser = _build_argument_parser()
330
+ parsed_arguments = parser.parse_args(all_command_arguments)
331
+ resolved_config_path = (
332
+ Path(parsed_arguments.config_path)
333
+ if parsed_arguments.config_path is not None
334
+ else chain_runner.chain_config_path()
335
+ )
336
+ try:
337
+ all_reports = report_chain_weekly_usage(config_path=resolved_config_path)
338
+ except chain_runner.ChainConfigurationError as configuration_error:
339
+ print(str(configuration_error), file=sys.stderr)
340
+ return CHAIN_CONFIG_ERROR_EXIT_CODE
341
+ print(json.dumps(reports_to_json_payload(all_reports), ensure_ascii=True))
342
+ return 0
343
+
344
+
345
+ if __name__ == "__main__":
346
+ sys.exit(main(sys.argv[1:]))
@@ -8,11 +8,12 @@ Named constants for scripts in `scripts/`. Follows the project convention that t
8
8
  |---|---|
9
9
  | `timing.py` | `sweep_empty_dirs.py` - `DEFAULT_AGE_SECONDS` (smallest age before an empty directory is eligible for removal) and `DEFAULT_POLL_INTERVAL` (seconds between sweep passes in continuous-watch mode); `spawn_grok_batch.py` - `WORKER_STAGGER_SECONDS` (seconds between staggered headless grok worker starts); `invoke_code_review.py` - `DEFAULT_CODE_REVIEW_TIMEOUT_SECONDS` (timeout for one headless `/code-review` chain run) |
10
10
  | `gh_artifact_upload_constants.py` | `gh_artifact_upload.py` - the `artifacts` release tag, title, and notes body, the GitHub CLI binary name, the asset-name timestamp format and template, the asset download URL template, the notes-file suffix, and the text encoding |
11
- | `claude_chain_constants.py` | `claude_chain_runner.py` - the chain config filename and home subdirectory, the usage-limit signature text, the per-binary status labels, the default timeout, CLI flag and separator tokens, config JSON keys, invalid-shape reason text, config-error and exhausted-chain message templates, and CLI exit codes |
11
+ | `claude_chain_constants.py` | `claude_chain_runner.py` - the chain config filename and home subdirectory, the usage-limit signature text, the per-binary status labels, the default timeout, CLI flag and separator tokens, config JSON keys (including optional `credentials_path`), invalid-shape reason text, config-error and exhausted-chain message templates, and CLI exit codes |
12
+ | `claude_chain_usage_constants.py` | `claude_chain_usage.py` - full weekly percent scale, usage-pause skill path segments, CLI config-path flag, JSON report keys, and probe error message templates |
12
13
  | `grok_worker_constants.py` | `grok_worker_preflight.py`, `grok_headless_runner.py`, and `spawn_grok_batch.py` - the `grok` binary name and CLI flags, model and subcommand tokens, leader-socket and scratch-file name parts, auth and usage-limit signature lists, outcome classifications, fallthrough reasons, tool-profile names and prompt headers, timeouts and turn caps (including launch-failure return code and post-kill grace), ping-cache keys and TTL, batch-spec and summary JSON keys, the prompt-part and report-stream join separators, and the CLI launch-error stderr prefix |
13
- | `code_review_constants.py` | `invoke_code_review.py` - the `/code-review high --fix` prompt, opus model alias, permission-mode flag and value, result mode and JSON keys, session-model CLI flag, git dirty-check tokens, and in-session return markers |
14
+ | `code_review_constants.py` | `invoke_code_review.py` - the `/code-review xhigh --fix` prompt, opus model alias, permission-mode flag and value, result mode and JSON keys, session-model CLI flag, git dirty-check tokens, and in-session return markers |
14
15
  | `__init__.py` | Empty package marker |
15
16
 
16
17
  ## Convention
17
18
 
18
- Every constant is `UPPER_SNAKE_CASE` with an explicit type annotation and a docstring. Scripts import from here rather than embedding literal values in their bodies.
19
+ Every constant is `UPPER_SNAKE_CASE` with an explicit type annotation and a docstring. Scripts import from here rather than embedding literal values in their bodies.
@@ -18,6 +18,9 @@ CLAUDE_HOME_SUBDIRECTORY: str = ".claude"
18
18
  CONFIG_FILENAME: str = "claude-chain.json"
19
19
  """Real chain-configuration filename read from the user's home directory."""
20
20
 
21
+ CHAIN_USAGE_MODULE_NAME: str = "claude_chain_usage"
22
+ """Import name of the weekly-usage report module loaded lazily by the runner."""
23
+
21
24
  EXAMPLE_CONFIG_FILENAME: str = "claude-chain.example.json"
22
25
  """Committed template filename referenced in the config-error guidance."""
23
26
 
@@ -30,6 +33,9 @@ CONFIG_COMMAND_KEY: str = "command"
30
33
  CONFIG_EXTRA_ARGS_KEY: str = "extra_args"
31
34
  """Chain-entry key holding per-account arguments appended to each invocation."""
32
35
 
36
+ CONFIG_CREDENTIALS_PATH_KEY: str = "credentials_path"
37
+ """Optional chain-entry key naming that account's OAuth credentials file path."""
38
+
33
39
  ALL_USAGE_LIMIT_SIGNATURES: tuple[str, ...] = (
34
40
  "hit your session limit",
35
41
  "usage limit reached",
@@ -91,9 +97,15 @@ CONFIG_ENTRY_EXTRA_ARGS_INVALID_REASON: str = (
91
97
  )
92
98
  """Reason detail when a chain entry's extra_args value is the wrong shape."""
93
99
 
100
+ CONFIG_ENTRY_CREDENTIALS_PATH_INVALID_REASON: str = (
101
+ "a chain entry's 'credentials_path' is not a non-empty string"
102
+ )
103
+ """Reason detail when a chain entry's credentials_path value is the wrong shape."""
104
+
94
105
  CONFIG_MISSING_MESSAGE_TEMPLATE: str = (
95
106
  "Claude chain config not found at {config_path}. Copy {example_filename} to "
96
- "{config_path} and list your logged-in claude binaries in fallback order."
107
+ "{config_path} and list your account binaries. Try order comes from weekly "
108
+ "remaining; config order is the tiebreak."
97
109
  )
98
110
  """Guidance shown when the config file is absent."""
99
111
 
@@ -0,0 +1,58 @@
1
+ """Named constants for the claude chain weekly-usage report tool.
2
+
3
+ Per the project's configuration conventions, every scalar and structural
4
+ constant the usage reporter needs lives here rather than inline in the module.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ FULL_WEEKLY_PERCENT: float = 100.0
10
+ """Percent scale ceiling for weekly utilization (remaining = this minus used)."""
11
+
12
+ USAGE_PAUSE_SKILL_DIRECTORY_NAME: str = "skills"
13
+ """Package-root directory name that holds skill trees (sibling of ``scripts/``)."""
14
+
15
+ USAGE_PAUSE_SKILL_NAME: str = "usage-pause"
16
+ """Skill directory name under ``skills/`` that owns the OAuth usage probe."""
17
+
18
+ USAGE_PAUSE_SCRIPTS_DIRECTORY_NAME: str = "scripts"
19
+ """Directory under the usage-pause skill that holds the OAuth probe module."""
20
+
21
+ RESOLVE_USAGE_WINDOW_MODULE_NAME: str = "resolve_usage_window"
22
+ """Module name of the usage-pause OAuth probe loader."""
23
+
24
+ RESOLVE_USAGE_WINDOW_FILENAME: str = "resolve_usage_window.py"
25
+ """Filename of the usage-pause OAuth probe module on disk."""
26
+
27
+ JSON_ACCOUNTS_KEY: str = "accounts"
28
+ """Top-level key in the CLI JSON report holding the per-account list."""
29
+
30
+ JSON_COMMAND_KEY: str = "command"
31
+ """Per-account JSON key naming the chain binary."""
32
+
33
+ JSON_WEEKLY_REMAINING_PERCENT_KEY: str = "weekly_remaining_percent"
34
+ """Per-account JSON key for remaining weekly usage percent (or null)."""
35
+
36
+ JSON_ERROR_KEY: str = "error"
37
+ """Per-account JSON key carrying the unmeasurable-reason string."""
38
+
39
+ CLI_CONFIG_PATH_FLAG: str = "--config-path"
40
+ """CLI flag that overrides the chain configuration file path."""
41
+
42
+ NO_ACCESS_TOKEN_ERROR_TEMPLATE: str = (
43
+ "no usable bearer token from the OAuth credential file at {credentials_path}"
44
+ )
45
+ """Error when the entry credential file yields no usable OAuth bearer."""
46
+
47
+ WEEKLY_UTILIZATION_MISSING_ERROR: str = (
48
+ "usage response carried no weekly utilization"
49
+ )
50
+ """Error when the OAuth usage payload has no readable seven_day utilization."""
51
+
52
+ USAGE_PROBE_FAILED_ERROR_TEMPLATE: str = "usage probe failed: {error}"
53
+ """Error when the OAuth usage HTTP probe raises."""
54
+
55
+ RESOLVE_USAGE_WINDOW_MISSING_ERROR_TEMPLATE: str = (
56
+ "usage-pause probe module not found at {module_path}"
57
+ )
58
+ """Error when the resolve_usage_window script cannot be located on disk."""
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
  CODE_REVIEW_SLASH_COMMAND: str = "/code-review"
12
12
  """Built-in Claude Code slash command that runs the repository review."""
13
13
 
14
- CODE_REVIEW_EFFORT: str = "high"
14
+ CODE_REVIEW_EFFORT: str = "xhigh"
15
15
  """Effort level passed to the built-in review slash command."""
16
16
 
17
17
  CODE_REVIEW_FIX_FLAG: str = "--fix"
@@ -13,5 +13,5 @@ DEFAULT_POLL_INTERVAL: int = 30
13
13
  WORKER_STAGGER_SECONDS: int = 15
14
14
  """Seconds between staggered headless grok worker process starts in a batch."""
15
15
 
16
- DEFAULT_CODE_REVIEW_TIMEOUT_SECONDS: int = 600
16
+ DEFAULT_CODE_REVIEW_TIMEOUT_SECONDS: int = 2400
17
17
  """Default timeout applied to one headless `/code-review` chain invocation, in seconds."""