claude-dev-env 2.2.1 → 2.4.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 (170) hide show
  1. package/CLAUDE.md +3 -2
  2. package/_shared/advisor/advisor-protocol.md +2 -2
  3. package/_shared/pr-loop/scripts/CLAUDE.md +1 -0
  4. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -284
  5. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -0
  6. package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +3 -3
  7. package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -0
  8. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +5 -0
  9. package/_shared/pr-loop/scripts/stale_worktree_rule_sweep.py +107 -0
  10. package/_shared/pr-loop/scripts/tests/CLAUDE.md +2 -0
  11. package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +55 -73
  12. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +9 -15
  13. package/_shared/pr-loop/scripts/tests/test_grant_project_claude_permissions.py +91 -1
  14. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +88 -1
  15. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -0
  16. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -0
  17. package/_shared/pr-loop/worker-spawn.md +1 -1
  18. package/agents/CLAUDE.md +1 -0
  19. package/agents/code-verifier.md +4 -4
  20. package/agents/skill-writer-agent.md +84 -0
  21. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +141 -41
  22. package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +29 -13
  23. package/docs/CLAUDE.md +1 -0
  24. package/docs/references/CLAUDE.md +1 -0
  25. package/docs/references/code-review-enforcement.md +97 -0
  26. package/docs/wsl-docker-cowork-starter-matrix.md +89 -0
  27. package/hooks/blocking/CLAUDE.md +8 -1
  28. package/hooks/blocking/code_review_enforcement_config_bootstrap.py +53 -0
  29. package/hooks/blocking/code_review_gate_deny.py +74 -0
  30. package/hooks/blocking/code_review_pr_create_gate.py +194 -0
  31. package/hooks/blocking/code_review_push_gate.py +140 -0
  32. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +340 -0
  33. package/hooks/blocking/code_review_stamp_store.py +233 -0
  34. package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +7 -0
  35. package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +15 -0
  36. package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +212 -0
  37. package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +138 -0
  38. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +49 -0
  39. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +38 -0
  40. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +39 -27
  41. package/hooks/blocking/config/code_review_enforcement_constants.py +110 -0
  42. package/hooks/blocking/config/test_code_review_enforcement_constants.py +108 -0
  43. package/hooks/blocking/config/verified_commit_constants.py +24 -9
  44. package/hooks/blocking/conftest.py +2 -0
  45. package/hooks/blocking/convergence_gate_blocker.py +112 -23
  46. package/hooks/blocking/destructive_command_blocker.py +19 -6
  47. package/hooks/blocking/pr_description_proof_of_work.py +52 -34
  48. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +4 -1
  49. package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +62 -0
  50. package/hooks/blocking/test_code_review_gate_deny.py +54 -0
  51. package/hooks/blocking/test_code_review_pr_create_gate.py +185 -0
  52. package/hooks/blocking/test_code_review_push_gate.py +189 -0
  53. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +180 -0
  54. package/hooks/blocking/test_code_review_stamp_store.py +205 -0
  55. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +124 -2
  56. package/hooks/blocking/test_code_verifier_tools_contract.py +28 -0
  57. package/hooks/blocking/test_convergence_gate_blocker.py +153 -5
  58. package/hooks/blocking/test_destructive_command_blocker.py +1 -1
  59. package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +45 -0
  60. package/hooks/blocking/test_pr_description_proof_of_work.py +151 -0
  61. package/hooks/blocking/test_pre_tool_use_dispatcher.py +8 -8
  62. package/hooks/blocking/test_verification_verdict_store.py +920 -810
  63. package/hooks/blocking/test_volatile_path_in_post_blocker.py +114 -2
  64. package/hooks/blocking/verification_verdict_store.py +118 -5
  65. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +29 -17
  66. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +35 -0
  67. package/hooks/blocking/volatile_path_in_post_blocker.py +69 -8
  68. package/hooks/git-hooks/git_hooks_constants/__init__.py +6 -0
  69. package/hooks/git-hooks/pre_push.py +89 -2
  70. package/hooks/git-hooks/test_pre_push.py +103 -0
  71. package/hooks/hooks.json +16 -1
  72. package/hooks/hooks_constants/CLAUDE.md +1 -0
  73. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +8 -0
  74. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
  75. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +26 -11
  76. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +20 -3
  77. package/hooks/hooks_constants/destructive_command_segment_constants.py +3 -1
  78. package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -4
  79. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
  80. package/hooks/hooks_constants/pyproject_config_discovery_constants.py +16 -0
  81. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +24 -0
  82. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +6 -0
  83. package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +8 -1
  84. package/hooks/validators/CLAUDE.md +1 -0
  85. package/hooks/validators/mypy_integration.py +63 -50
  86. package/hooks/validators/pyproject_config_discovery.py +101 -0
  87. package/hooks/validators/ruff_integration.py +211 -23
  88. package/hooks/validators/run_all_validators.py +21 -14
  89. package/hooks/validators/test_mypy_integration.py +32 -0
  90. package/hooks/validators/test_pyproject_config_discovery.py +94 -0
  91. package/hooks/validators/test_ruff_integration.py +68 -1
  92. package/hooks/validators/test_run_all_validators.py +25 -0
  93. package/hooks/validators/test_run_all_validators_config_discovery.py +123 -0
  94. package/package.json +1 -1
  95. package/rules/docstring-prose-matches-implementation.md +20 -43
  96. package/rules/durable-post-artifacts.md +7 -0
  97. package/scripts/CLAUDE.md +2 -1
  98. package/scripts/claude-chain.example.json +15 -3
  99. package/scripts/claude_chain_runner.py +130 -27
  100. package/scripts/claude_chain_usage.py +346 -0
  101. package/scripts/codec_forwarding_test_support.py +83 -0
  102. package/scripts/conftest.py +8 -0
  103. package/scripts/dev_env_scripts_constants/CLAUDE.md +4 -3
  104. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +57 -2
  105. package/scripts/dev_env_scripts_constants/claude_chain_usage_constants.py +58 -0
  106. package/scripts/dev_env_scripts_constants/code_review_constants.py +129 -12
  107. package/scripts/dev_env_scripts_constants/test_code_review_constants.py +55 -0
  108. package/scripts/dev_env_scripts_constants/timing.py +1 -1
  109. package/scripts/invoke_code_review.py +550 -38
  110. package/scripts/resolve_worker_spawn.py +8 -1
  111. package/scripts/test_claude_chain_runner.py +412 -6
  112. package/scripts/test_claude_chain_usage.py +534 -0
  113. package/scripts/test_invoke_code_review.py +298 -4
  114. package/scripts/test_invoke_code_review_codec.py +77 -0
  115. package/scripts/test_resolve_worker_spawn_codec.py +101 -0
  116. package/skills/autoconverge/SKILL.md +9 -3
  117. package/skills/autoconverge/reference/convergence.md +2 -1
  118. package/skills/autoconverge/reference/multi-pr.md +6 -1
  119. package/skills/autoconverge/reference/stop-conditions.md +16 -10
  120. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +1 -1
  121. package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +175 -3
  122. package/skills/autoconverge/workflow/converge.contract.test.mjs +19 -0
  123. package/skills/autoconverge/workflow/converge.mjs +24 -8
  124. package/skills/autoconverge/workflow/converge_multi.mjs +7 -3
  125. package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +5 -0
  126. package/skills/fresh-branch/CLAUDE.md +2 -0
  127. package/skills/fresh-branch/SKILL.md +2 -0
  128. package/skills/fresh-branch/scripts/create_fresh_branch.py +78 -180
  129. package/skills/fresh-branch/scripts/fresh_branch_git_commands.py +285 -0
  130. package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -0
  131. package/skills/fresh-branch/scripts/pytest.ini +4 -0
  132. package/skills/fresh-branch/scripts/test_create_fresh_branch.py +98 -0
  133. package/skills/fresh-branch/scripts/test_fresh_branch_git_commands.py +310 -0
  134. package/skills/orchestrator/SKILL.md +1 -20
  135. package/skills/orchestrator-refresh/SKILL.md +1 -1
  136. package/skills/pr-converge/SKILL.md +3 -3
  137. package/skills/pr-converge/reference/examples.md +3 -3
  138. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  139. package/skills/pr-converge/reference/ground-rules.md +3 -3
  140. package/skills/pr-converge/reference/per-tick.md +5 -5
  141. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  142. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  143. package/skills/prototype/SKILL.md +86 -0
  144. package/skills/prototype/reference/honest-limitations.md +23 -0
  145. package/skills/prototype/reference/promotion-tasks.md +23 -0
  146. package/skills/prototype/scripts/build_sandbox_settings.py +249 -0
  147. package/skills/prototype/scripts/conftest.py +15 -0
  148. package/skills/prototype/scripts/launch_sandbox.py +205 -0
  149. package/skills/prototype/scripts/probe_sandbox_safety.py +311 -0
  150. package/skills/prototype/scripts/prototype_scripts_constants/__init__.py +1 -0
  151. package/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  152. package/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +41 -0
  153. package/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +23 -0
  154. package/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +45 -0
  155. package/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +10 -0
  156. package/skills/prototype/scripts/test_build_sandbox_settings.py +275 -0
  157. package/skills/prototype/scripts/test_launch_sandbox.py +303 -0
  158. package/skills/prototype/scripts/test_probe_sandbox_safety.py +284 -0
  159. package/skills/prototype/workflows/promotion.md +27 -0
  160. package/skills/prototype/workflows/sandbox.md +35 -0
  161. package/skills/skill-builder/CLAUDE.md +3 -3
  162. package/skills/skill-builder/SKILL.md +5 -5
  163. package/skills/skill-builder/references/CLAUDE.md +1 -1
  164. package/skills/skill-builder/references/delegation-map.md +3 -3
  165. package/skills/skill-builder/references/description-field.md +1 -1
  166. package/skills/skill-builder/references/skill-modularity.md +2 -3
  167. package/skills/skill-builder/workflows/CLAUDE.md +1 -1
  168. package/skills/skill-builder/workflows/improve-skill.md +1 -1
  169. package/skills/skill-builder/workflows/new-skill.md +2 -2
  170. package/skills/team-advisor/SKILL.md +5 -4
@@ -0,0 +1,8 @@
1
+ """Put the scripts directory on sys.path so tests import each script by name."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ _scripts_directory = str(Path(__file__).resolve().parent)
7
+ if _scripts_directory not in sys.path:
8
+ sys.path.insert(0, _scripts_directory)
@@ -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; `invoke_code_review.py` and `resolve_worker_spawn.py` - the text-codec keyword names and the `collect_forwarded_text_codec` helper that forwards them to the subprocess runner for text-mode capture |
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.
@@ -1,4 +1,5 @@
1
- """Named constants for the claude fallback-chain runner.
1
+ """Named constants for the claude fallback-chain runner, and the helper that
2
+ picks which text-codec kwargs a chain subprocess wrapper forwards.
2
3
 
3
4
  Per the project's configuration conventions, every scalar and structural
4
5
  constant the runner needs lives here rather than inline in the module.
@@ -6,18 +7,63 @@ constant the runner needs lives here rather than inline in the module.
6
7
 
7
8
  from __future__ import annotations
8
9
 
10
+ from collections.abc import Mapping
11
+
9
12
  UTF8_ENCODING: str = "utf-8"
10
13
  """Encoding used to read the chain configuration file."""
11
14
 
12
15
  CODEC_ERROR_STRATEGY: str = "replace"
13
16
  """Codec error handler that maps any unencodable or undecodable value to a marker."""
14
17
 
18
+ SUBPROCESS_ENCODING_KEYWORD: str = "encoding"
19
+ """Keyword name for text encoding when forwarding chain subprocess runner kwargs."""
20
+
21
+ SUBPROCESS_ERRORS_KEYWORD: str = "errors"
22
+ """Keyword name for text decode error policy when forwarding chain subprocess runner kwargs."""
23
+
24
+ ALL_SUBPROCESS_TEXT_CODEC_KEYWORDS: tuple[str, ...] = (
25
+ SUBPROCESS_ENCODING_KEYWORD,
26
+ SUBPROCESS_ERRORS_KEYWORD,
27
+ )
28
+ """Keyword names to forward from the chain runner for text-mode subprocess capture."""
29
+
30
+
31
+ def collect_forwarded_text_codec(
32
+ all_keywords: Mapping[str, object],
33
+ ) -> dict[str, object]:
34
+ """Return only the text-codec kwargs present in ``all_keywords``.
35
+
36
+ ::
37
+
38
+ collect_forwarded_text_codec({"encoding": "utf-8", "timeout": 30})
39
+ # -> {"encoding": "utf-8"}
40
+
41
+ collect_forwarded_text_codec({"timeout": 30})
42
+ # -> {}
43
+
44
+ Args:
45
+ all_keywords: Keyword arguments received by a chain subprocess wrapper.
46
+
47
+ Returns:
48
+ Mapping of codec keyword names to their values, limited to keys listed
49
+ in ``ALL_SUBPROCESS_TEXT_CODEC_KEYWORDS`` that are present in the input.
50
+ """
51
+ return {
52
+ each_key: all_keywords[each_key]
53
+ for each_key in ALL_SUBPROCESS_TEXT_CODEC_KEYWORDS
54
+ if each_key in all_keywords
55
+ }
56
+
57
+
15
58
  CLAUDE_HOME_SUBDIRECTORY: str = ".claude"
16
59
  """Per-user directory under the home directory that holds the chain config."""
17
60
 
18
61
  CONFIG_FILENAME: str = "claude-chain.json"
19
62
  """Real chain-configuration filename read from the user's home directory."""
20
63
 
64
+ CHAIN_USAGE_MODULE_NAME: str = "claude_chain_usage"
65
+ """Import name of the weekly-usage report module loaded lazily by the runner."""
66
+
21
67
  EXAMPLE_CONFIG_FILENAME: str = "claude-chain.example.json"
22
68
  """Committed template filename referenced in the config-error guidance."""
23
69
 
@@ -30,6 +76,9 @@ CONFIG_COMMAND_KEY: str = "command"
30
76
  CONFIG_EXTRA_ARGS_KEY: str = "extra_args"
31
77
  """Chain-entry key holding per-account arguments appended to each invocation."""
32
78
 
79
+ CONFIG_CREDENTIALS_PATH_KEY: str = "credentials_path"
80
+ """Optional chain-entry key naming that account's OAuth credentials file path."""
81
+
33
82
  ALL_USAGE_LIMIT_SIGNATURES: tuple[str, ...] = (
34
83
  "hit your session limit",
35
84
  "usage limit reached",
@@ -91,9 +140,15 @@ CONFIG_ENTRY_EXTRA_ARGS_INVALID_REASON: str = (
91
140
  )
92
141
  """Reason detail when a chain entry's extra_args value is the wrong shape."""
93
142
 
143
+ CONFIG_ENTRY_CREDENTIALS_PATH_INVALID_REASON: str = (
144
+ "a chain entry's 'credentials_path' is not a non-empty string"
145
+ )
146
+ """Reason detail when a chain entry's credentials_path value is the wrong shape."""
147
+
94
148
  CONFIG_MISSING_MESSAGE_TEMPLATE: str = (
95
149
  "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."
150
+ "{config_path} and list your account binaries. Try order comes from weekly "
151
+ "remaining; config order is the tiebreak."
97
152
  )
98
153
  """Guidance shown when the config file is absent."""
99
154
 
@@ -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."""
@@ -1,27 +1,76 @@
1
1
  """Named constants for the host-aware `/code-review` invoker.
2
2
 
3
- `invoke_code_review.py` imports every scalar and structural constant it
4
- needs from this module (and shared flag tokens from
5
- `grok_worker_constants`) so the prompt, model alias, and JSON keys are
6
- never hardcoded twice.
3
+ ::
4
+
5
+ ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER
6
+ ok: ("low", "medium", "high", "xhigh", "max")
7
+ flag: "ultra" (rejected; needs an interactive terminal)
8
+ RECORD_STAMP_FLAG
9
+ ok: "--record-stamp"
10
+
11
+ Effort tokens re-export the hooks enforcement constants (single source).
12
+ Scalar flags, JSON keys, and mint-loop messages live here for the invoker.
7
13
  """
8
14
 
9
15
  from __future__ import annotations
10
16
 
17
+ import importlib.util
18
+ import sys
19
+ from pathlib import Path
20
+ from types import ModuleType
21
+
22
+
23
+ def _load_enforcement_constants_module() -> ModuleType:
24
+ """Load the hooks enforcement constants by explicit file path.
25
+
26
+ Binds a private module name so a foreign ``config`` package on
27
+ ``sys.path`` cannot win the import and drift the effort token set.
28
+ """
29
+ package_root_directory = Path(__file__).resolve().parent.parent.parent
30
+ constants_file_path = (
31
+ package_root_directory
32
+ / "hooks"
33
+ / "blocking"
34
+ / "config"
35
+ / "code_review_enforcement_constants.py"
36
+ )
37
+ module_name = "_code_review_enforcement_constants_for_scripts"
38
+ cached_module = sys.modules.get(module_name)
39
+ if cached_module is not None:
40
+ return cached_module
41
+ module_spec = importlib.util.spec_from_file_location(
42
+ module_name,
43
+ constants_file_path,
44
+ )
45
+ if module_spec is None or module_spec.loader is None:
46
+ raise ImportError(
47
+ f"could not load code-review enforcement constants from {constants_file_path}"
48
+ )
49
+ constants_module = importlib.util.module_from_spec(module_spec)
50
+ sys.modules[module_name] = constants_module
51
+ module_spec.loader.exec_module(constants_module)
52
+ return constants_module
53
+
54
+
55
+ _ENFORCEMENT_CONSTANTS = _load_enforcement_constants_module()
56
+
57
+ ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER: tuple[str, ...] = (
58
+ _ENFORCEMENT_CONSTANTS.ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER
59
+ )
60
+ """Effort tokens ordered low to max; single source from enforcement constants."""
61
+
62
+ RECORD_STAMP_FLAG: str = _ENFORCEMENT_CONSTANTS.SANCTIONED_STAMP_MINTER_FLAG
63
+ """CLI flag that forces chain mode and mints a clean stamp on a stable pass."""
64
+
65
+ DEFAULT_CODE_REVIEW_EFFORT: str = "high"
66
+ """Default effort when the caller omits the positional effort token."""
67
+
11
68
  CODE_REVIEW_SLASH_COMMAND: str = "/code-review"
12
69
  """Built-in Claude Code slash command that runs the repository review."""
13
70
 
14
- CODE_REVIEW_EFFORT: str = "high"
15
- """Effort level passed to the built-in review slash command."""
16
-
17
71
  CODE_REVIEW_FIX_FLAG: str = "--fix"
18
72
  """Slash-command flag that applies automatic fixes for review findings."""
19
73
 
20
- CODE_REVIEW_PROMPT: str = (
21
- f"{CODE_REVIEW_SLASH_COMMAND} {CODE_REVIEW_EFFORT} {CODE_REVIEW_FIX_FLAG}"
22
- )
23
- """Single-turn prompt that runs the built-in review slash command with fixes."""
24
-
25
74
  CODE_REVIEW_MODEL_ALIAS: str = "opus"
26
75
  """CLI `--model` short alias the review always pins to."""
27
76
 
@@ -69,3 +118,71 @@ HOST_PROFILE_ERROR_RETURNCODE: int = 1
69
118
 
70
119
  SUCCESSFUL_REVIEW_RETURNCODE: int = 0
71
120
  """Return code required before a clean stamp may advance past CODE_REVIEW."""
121
+
122
+ RESULT_KEY_STAMP_MINTED: str = "stamp_minted"
123
+ """JSON result key holding whether a clean stamp was written this run."""
124
+
125
+ RESULT_KEY_PASS_COUNT: str = "pass_count"
126
+ """JSON result key holding how many review passes the mint loop ran."""
127
+
128
+ RESULT_KEY_BOUND_HASH: str = "bound_hash"
129
+ """JSON result key holding the surface hash bound into a minted stamp, or null."""
130
+
131
+ CLI_EFFORT_METAVAR: str = "effort"
132
+ """Argparse metavar for the positional effort token."""
133
+
134
+ CLI_EFFORT_HELP: str = (
135
+ "Review effort token: low, medium, high, xhigh, or max "
136
+ "(ultra is rejected; default high)."
137
+ )
138
+ """Help text for the positional effort argument."""
139
+
140
+ CLI_RECORD_STAMP_HELP: str = (
141
+ "Force chain mode, loop a capped number of review passes, and mint a "
142
+ "clean stamp only when a pass exits 0 with a stable surface hash."
143
+ )
144
+ """Help text for the --record-stamp flag."""
145
+
146
+ INVALID_EFFORT_RETURNCODE: int = 2
147
+ """Return code when the caller passes an unknown or unsupported effort token."""
148
+
149
+ STAMP_DID_NOT_CONVERGE_RETURNCODE: int = 1
150
+ """Return code when the mint loop hits its pass cap without a stable clean pass."""
151
+
152
+ MAXIMUM_STAMP_MINT_PASSES: int = 3
153
+ """Cap on review passes under --record-stamp before the invoker gives up."""
154
+
155
+ EFFORT_TOKEN_LIST_SEPARATOR: str = ", "
156
+ """Separator used when listing allowed effort tokens in error messages."""
157
+
158
+ INVALID_EFFORT_MESSAGE: str = (
159
+ "invalid effort {effort!r}: must be one of {allowed}; "
160
+ "'ultra' is rejected because it requires an interactive terminal"
161
+ )
162
+ """Stderr template when the caller supplies an unknown or ultra effort token."""
163
+
164
+ STAMP_DID_NOT_CONVERGE_MESSAGE: str = (
165
+ "code-review stamp minting did not converge after {pass_count} passes "
166
+ "(surface kept changing or review return codes stayed non-zero); no stamp written"
167
+ )
168
+ """Stderr template when the mint loop hits the pass cap without minting."""
169
+
170
+ STAMP_STORE_IMPORT_FAILURE_MESSAGE: str = (
171
+ "code-review stamp store could not be imported for --record-stamp: {error}"
172
+ )
173
+ """Stderr template when --record-stamp cannot load the stamp store module."""
174
+
175
+ STAMP_STORE_MODULE_FILE_NAME: str = "code_review_stamp_store.py"
176
+ """File name of the stamp store module under hooks/blocking."""
177
+
178
+ STAMP_STORE_MODULE_NAME: str = "code_review_stamp_store"
179
+ """Import name of the stamp store module."""
180
+
181
+ STAMP_STORE_LIVE_SURFACE_HASH_NAME: str = "live_surface_hash"
182
+ """Attribute name of the live surface-hash helper on the stamp store module."""
183
+
184
+ STAMP_STORE_RECORD_CLEAN_STAMP_NAME: str = "record_clean_stamp"
185
+ """Attribute name of the stamp-mint helper on the stamp store module."""
186
+
187
+ STAMP_STORE_RESOLVE_REPO_ROOT_NAME: str = "resolve_repo_root"
188
+ """Attribute name of the repo-root resolver on the stamp store module."""
@@ -0,0 +1,55 @@
1
+ """Behavioral tests for the host-aware code-review invoker constants module.
2
+
3
+ These assert the single-source wiring: the effort tokens and the record-stamp
4
+ flag the invoker exposes are the exact values the hooks enforcement module
5
+ holds, so a threshold change in one place cannot drift the other.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import importlib.util
11
+ from pathlib import Path
12
+ from types import ModuleType
13
+
14
+ from dev_env_scripts_constants import code_review_constants as review_constants
15
+
16
+ _PACKAGE_ROOT = Path(__file__).resolve().parent.parent.parent
17
+ _ENFORCEMENT_CONSTANTS_PATH = (
18
+ _PACKAGE_ROOT
19
+ / "hooks"
20
+ / "blocking"
21
+ / "config"
22
+ / "code_review_enforcement_constants.py"
23
+ )
24
+
25
+
26
+ def _load_enforcement_module() -> ModuleType:
27
+ module_spec = importlib.util.spec_from_file_location(
28
+ "code_review_enforcement_constants_under_test", _ENFORCEMENT_CONSTANTS_PATH
29
+ )
30
+ assert module_spec is not None
31
+ assert module_spec.loader is not None
32
+ enforcement_module = importlib.util.module_from_spec(module_spec)
33
+ module_spec.loader.exec_module(enforcement_module)
34
+ return enforcement_module
35
+
36
+
37
+ def test_effort_tokens_reexport_the_enforcement_source() -> None:
38
+ enforcement_module = _load_enforcement_module()
39
+ assert review_constants.ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER == getattr(
40
+ enforcement_module, "ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER"
41
+ )
42
+
43
+
44
+ def test_record_stamp_flag_matches_sanctioned_minter_flag() -> None:
45
+ enforcement_module = _load_enforcement_module()
46
+ assert review_constants.RECORD_STAMP_FLAG == getattr(
47
+ enforcement_module, "SANCTIONED_STAMP_MINTER_FLAG"
48
+ )
49
+
50
+
51
+ def test_default_effort_is_a_known_token() -> None:
52
+ assert (
53
+ review_constants.DEFAULT_CODE_REVIEW_EFFORT
54
+ in review_constants.ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER
55
+ )
@@ -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."""