claude-dev-env 1.95.0 → 2.0.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 (240) hide show
  1. package/_shared/advisor/CLAUDE.md +2 -2
  2. package/_shared/advisor/advisor-protocol.md +20 -20
  3. package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +15 -12
  4. package/_shared/advisor/scripts/model_tier_run_validator.py +11 -10
  5. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +25 -19
  6. package/_shared/advisor/scripts/tests/test_tier_model_ids.py +17 -17
  7. package/_shared/advisor/scripts/tier_model_ids.py +18 -18
  8. package/_shared/pr-loop/CLAUDE.md +1 -0
  9. package/_shared/pr-loop/scripts/CLAUDE.md +2 -1
  10. package/_shared/pr-loop/scripts/README.md +1 -0
  11. package/_shared/pr-loop/scripts/code_rules_gate.py +253 -1980
  12. package/_shared/pr-loop/scripts/code_rules_gate_parts/CLAUDE.md +32 -0
  13. package/_shared/pr-loop/scripts/code_rules_gate_parts/__init__.py +7 -0
  14. package/_shared/pr-loop/scripts/code_rules_gate_parts/added_line_maps.py +268 -0
  15. package/_shared/pr-loop/scripts/code_rules_gate_parts/enforcer_loading.py +172 -0
  16. package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_arguments.py +70 -0
  17. package/_shared/pr-loop/scripts/code_rules_gate_parts/gate_running.py +326 -0
  18. package/_shared/pr-loop/scripts/code_rules_gate_parts/git_blob_readers.py +85 -0
  19. package/_shared/pr-loop/scripts/code_rules_gate_parts/git_file_sets.py +331 -0
  20. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_running.py +369 -0
  21. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/conftest.py +14 -0
  22. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_added_line_maps.py +118 -0
  23. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_enforcer_loading.py +17 -0
  24. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_arguments.py +29 -0
  25. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_gate_running.py +99 -0
  26. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_blob_readers.py +69 -0
  27. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_git_file_sets.py +137 -0
  28. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_running.py +116 -0
  29. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_violation_scoping.py +75 -0
  30. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_wrapper_plumb_check.py +49 -0
  31. package/_shared/pr-loop/scripts/code_rules_gate_parts/violation_scoping.py +328 -0
  32. package/_shared/pr-loop/scripts/code_rules_gate_parts/wrapper_plumb_check.py +206 -0
  33. package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +24 -17
  34. package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviews_disabled_constants.py +1 -0
  35. package/_shared/pr-loop/scripts/reviews_disabled.py +19 -2
  36. package/_shared/pr-loop/scripts/test_code_rules_gate.py +278 -0
  37. package/_shared/pr-loop/scripts/tests/test_code_rules_gate_constants.py +6 -39
  38. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +43 -0
  39. package/_shared/pr-loop/worker-spawn.md +186 -0
  40. package/agents/code-verifier.md +1 -1
  41. package/bin/ever-shipped-skills.mjs +3 -0
  42. package/bin/expand_home_directory_tokens.mjs +1 -1
  43. package/bin/install.mjs +5 -2
  44. package/hooks/advisory/refactor_guard.py +3 -4
  45. package/hooks/blocking/CLAUDE.md +7 -1
  46. package/hooks/blocking/block_main_commit.py +2 -2
  47. package/hooks/blocking/claude_md_orphan_file_blocker.py +75 -699
  48. package/hooks/blocking/claude_md_orphan_file_blocker_parts/CLAUDE.md +28 -0
  49. package/hooks/blocking/claude_md_orphan_file_blocker_parts/__init__.py +1 -0
  50. package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/__init__.py +1 -0
  51. package/hooks/blocking/claude_md_orphan_file_blocker_parts/config/orphan_blocker_constants.py +18 -0
  52. package/hooks/blocking/claude_md_orphan_file_blocker_parts/decision.py +81 -0
  53. package/hooks/blocking/claude_md_orphan_file_blocker_parts/references.py +307 -0
  54. package/hooks/blocking/claude_md_orphan_file_blocker_parts/scan_plan.py +124 -0
  55. package/hooks/blocking/claude_md_orphan_file_blocker_parts/subtree_scan.py +179 -0
  56. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/conftest.py +10 -0
  57. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_decision.py +34 -0
  58. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_references.py +42 -0
  59. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_scan_plan.py +27 -0
  60. package/hooks/blocking/claude_md_orphan_file_blocker_parts/tests/test_subtree_scan.py +30 -0
  61. package/hooks/blocking/code_rules_boolean_mustcheck.py +1 -1
  62. package/hooks/blocking/code_rules_mock_completeness.py +1 -1
  63. package/hooks/blocking/code_rules_optional_params.py +2 -2
  64. package/hooks/blocking/code_rules_shared.py +1 -1
  65. package/hooks/blocking/code_rules_test_assertions.py +1 -1
  66. package/hooks/blocking/code_rules_typeddict_stub.py +1 -1
  67. package/hooks/blocking/gh_pr_author_enforcer.py +1 -1
  68. package/hooks/blocking/inventory_intent_records/CLAUDE.md +26 -0
  69. package/hooks/blocking/inventory_intent_records/__init__.py +1 -0
  70. package/hooks/blocking/inventory_intent_records/config/__init__.py +1 -0
  71. package/hooks/blocking/inventory_intent_records/config/intent_records_constants.py +20 -0
  72. package/hooks/blocking/inventory_intent_records/records.py +271 -0
  73. package/hooks/blocking/inventory_intent_records/tests/conftest.py +10 -0
  74. package/hooks/blocking/inventory_intent_records/tests/test_records.py +80 -0
  75. package/hooks/blocking/package_inventory_stale_blocker.py +54 -384
  76. package/hooks/blocking/package_inventory_stale_blocker_parts/CLAUDE.md +26 -0
  77. package/hooks/blocking/package_inventory_stale_blocker_parts/__init__.py +1 -0
  78. package/hooks/blocking/package_inventory_stale_blocker_parts/config/__init__.py +1 -0
  79. package/hooks/blocking/package_inventory_stale_blocker_parts/config/inventory_blocker_constants.py +16 -0
  80. package/hooks/blocking/package_inventory_stale_blocker_parts/decision.py +84 -0
  81. package/hooks/blocking/package_inventory_stale_blocker_parts/inventory_detection.py +307 -0
  82. package/hooks/blocking/package_inventory_stale_blocker_parts/tests/conftest.py +10 -0
  83. package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_decision.py +38 -0
  84. package/hooks/blocking/package_inventory_stale_blocker_parts/tests/test_inventory_detection.py +61 -0
  85. package/hooks/blocking/pii_payload_scan.py +138 -42
  86. package/hooks/blocking/pii_prevention_blocker.py +185 -291
  87. package/hooks/blocking/pii_prevention_blocker_parts/CLAUDE.md +24 -0
  88. package/hooks/blocking/pii_prevention_blocker_parts/__init__.py +1 -0
  89. package/hooks/blocking/pii_prevention_blocker_parts/config/__init__.py +1 -0
  90. package/hooks/blocking/pii_prevention_blocker_parts/config/repository_resolution_constants.py +28 -0
  91. package/hooks/blocking/pii_prevention_blocker_parts/repository_exemption.py +214 -0
  92. package/hooks/blocking/pii_prevention_blocker_parts/repository_resolution.py +208 -0
  93. package/hooks/blocking/pr_description_command_parser.py +8 -4
  94. package/hooks/blocking/precommit_code_rules_gate.py +3 -3
  95. package/hooks/blocking/tdd_enforcer.py +97 -608
  96. package/hooks/blocking/tdd_enforcer_parts/CLAUDE.md +30 -0
  97. package/hooks/blocking/tdd_enforcer_parts/__init__.py +1 -0
  98. package/hooks/blocking/tdd_enforcer_parts/candidate_paths.py +142 -0
  99. package/hooks/blocking/tdd_enforcer_parts/config/__init__.py +1 -0
  100. package/hooks/blocking/tdd_enforcer_parts/config/tdd_enforcer_constants.py +32 -0
  101. package/hooks/blocking/tdd_enforcer_parts/content_analysis.py +268 -0
  102. package/hooks/blocking/tdd_enforcer_parts/decisions.py +92 -0
  103. package/hooks/blocking/tdd_enforcer_parts/freshness.py +80 -0
  104. package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +63 -0
  105. package/hooks/blocking/tdd_enforcer_parts/path_classification.py +119 -0
  106. package/hooks/blocking/tdd_enforcer_parts/tests/conftest.py +10 -0
  107. package/hooks/blocking/tdd_enforcer_parts/tests/test_candidate_paths.py +31 -0
  108. package/hooks/blocking/tdd_enforcer_parts/tests/test_content_analysis.py +30 -0
  109. package/hooks/blocking/tdd_enforcer_parts/tests/test_decisions.py +34 -0
  110. package/hooks/blocking/tdd_enforcer_parts/tests/test_freshness.py +28 -0
  111. package/hooks/blocking/tdd_enforcer_parts/tests/test_git_tracking.py +48 -0
  112. package/hooks/blocking/tdd_enforcer_parts/tests/test_path_classification.py +36 -0
  113. package/hooks/blocking/test_inventory_deadlock_resolution.py +154 -0
  114. package/hooks/blocking/test_pii_payload_scan.py +168 -0
  115. package/hooks/blocking/test_tdd_enforcer_restore.py +108 -0
  116. package/hooks/blocking/tests/conftest.py +10 -0
  117. package/hooks/blocking/tests/test_pii_prevention_blocker.py +260 -0
  118. package/hooks/blocking/tests/test_repository_exemption.py +105 -0
  119. package/hooks/blocking/tests/test_repository_resolution.py +108 -0
  120. package/hooks/diagnostic/hook_log_extractor.py +12 -10
  121. package/hooks/git-hooks/post_commit.py +3 -4
  122. package/hooks/hooks_constants/CLAUDE.md +2 -2
  123. package/hooks/hooks_constants/banned_identifiers_constants.py +0 -1
  124. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
  125. package/hooks/hooks_constants/local_identity.py +59 -8
  126. package/hooks/hooks_constants/pii_prevention_constants.py +0 -6
  127. package/hooks/hooks_constants/test_local_identity.py +105 -3
  128. package/hooks/pyproject.toml +13 -36
  129. package/hooks/session/plugin_data_dir_cleanup.py +0 -1
  130. package/hooks/validation/mypy_validator.py +2 -2
  131. package/hooks/validators/health_check.py +1 -0
  132. package/hooks/validators/mypy_integration.py +2 -0
  133. package/hooks/validators/ruff_integration.py +3 -0
  134. package/hooks/workflow/auto_formatter.py +5 -4
  135. package/package.json +1 -1
  136. package/scripts/CLAUDE.md +4 -0
  137. package/scripts/dev_env_scripts_constants/CLAUDE.md +6 -4
  138. package/scripts/dev_env_scripts_constants/code_review_constants.py +71 -0
  139. package/scripts/dev_env_scripts_constants/grok_worker_constants.py +435 -0
  140. package/scripts/dev_env_scripts_constants/timing.py +7 -1
  141. package/scripts/grok_headless_runner.py +294 -0
  142. package/scripts/grok_worker_preflight.py +410 -0
  143. package/scripts/invoke_code_review.py +463 -0
  144. package/scripts/resolve_worker_spawn.py +619 -0
  145. package/scripts/spawn_grok_batch.py +672 -0
  146. package/scripts/test_grok_headless_runner.py +626 -0
  147. package/scripts/test_grok_worker_preflight.py +1054 -0
  148. package/scripts/test_invoke_code_review.py +672 -0
  149. package/scripts/test_resolve_worker_spawn.py +1014 -0
  150. package/scripts/test_spawn_grok_batch.py +1017 -0
  151. package/skills/CLAUDE.md +5 -3
  152. package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +72 -13
  153. package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +121 -14
  154. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +78 -0
  155. package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +121 -0
  156. package/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +196 -6
  157. package/skills/autoconverge/CLAUDE.md +3 -3
  158. package/skills/autoconverge/SKILL.md +9 -3
  159. package/skills/autoconverge/reference/CLAUDE.md +2 -2
  160. package/skills/autoconverge/reference/convergence.md +33 -11
  161. package/skills/autoconverge/reference/stop-conditions.md +16 -5
  162. package/skills/autoconverge/workflow/CLAUDE.md +2 -1
  163. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +7 -2
  164. package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +300 -0
  165. package/skills/autoconverge/workflow/converge.contract.test.mjs +5 -5
  166. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +29 -29
  167. package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
  168. package/skills/autoconverge/workflow/converge.mjs +200 -16
  169. package/skills/bugteam/CLAUDE.md +2 -2
  170. package/skills/bugteam/CONSTRAINTS.md +3 -2
  171. package/skills/bugteam/PROMPTS.md +7 -6
  172. package/skills/bugteam/SKILL.md +18 -13
  173. package/skills/bugteam/reference/audit-and-teammates.md +215 -35
  174. package/skills/bugteam/reference/design-rationale.md +1 -1
  175. package/skills/bugteam/reference/obstacles/CLAUDE.md +1 -1
  176. package/skills/bugteam/reference/team-setup.md +8 -2
  177. package/skills/codex-review/CLAUDE.md +46 -0
  178. package/skills/codex-review/SKILL.md +181 -0
  179. package/skills/codex-review/reference/CLAUDE.md +15 -0
  180. package/skills/codex-review/reference/cli-contract.md +253 -0
  181. package/skills/codex-review/reference/loop-integration.md +118 -0
  182. package/skills/codex-review/scripts/codex_down_classifier.py +98 -0
  183. package/skills/codex-review/scripts/codex_review_scripts_constants/CLAUDE.md +18 -0
  184. package/skills/codex-review/scripts/codex_review_scripts_constants/__init__.py +1 -0
  185. package/skills/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
  186. package/skills/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +86 -0
  187. package/skills/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
  188. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +45 -0
  189. package/skills/codex-review/scripts/codex_usage_probe.py +573 -0
  190. package/skills/codex-review/scripts/fixtures/auth_failure_synthetic.txt +1 -0
  191. package/skills/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
  192. package/skills/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
  193. package/skills/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
  194. package/skills/codex-review/scripts/fixtures/structured_findings.txt +13 -0
  195. package/skills/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
  196. package/skills/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
  197. package/skills/codex-review/scripts/fixtures/usage_limit_synthetic.txt +1 -0
  198. package/skills/codex-review/scripts/parse_codex_findings.py +207 -0
  199. package/skills/codex-review/scripts/run_codex_review.py +415 -0
  200. package/skills/codex-review/scripts/test_codex_down_classifier.py +143 -0
  201. package/skills/codex-review/scripts/test_codex_usage_probe.py +678 -0
  202. package/skills/codex-review/scripts/test_parse_codex_findings.py +130 -0
  203. package/skills/codex-review/scripts/test_run_codex_review.py +812 -0
  204. package/skills/codex-review/test_skill_scaffold.py +192 -0
  205. package/skills/grok-spawn/CLAUDE.md +28 -0
  206. package/skills/grok-spawn/SKILL.md +226 -0
  207. package/skills/grok-spawn/reference/flag-profiles.md +132 -0
  208. package/skills/grok-spawn/reference/worker-briefs.md +152 -0
  209. package/skills/grokify/SKILL.md +9 -1
  210. package/skills/grokify/capability-claims.test.mjs +28 -0
  211. package/skills/grokify/evals/README.md +72 -0
  212. package/skills/grokify/evals/parse-payload.test.mjs +171 -0
  213. package/skills/grokify/evals/run-capability-evals.mjs +545 -0
  214. package/skills/orchestrator/SKILL.md +15 -11
  215. package/skills/orchestrator-refresh/SKILL.md +5 -5
  216. package/skills/pr-converge/SKILL.md +34 -13
  217. package/skills/pr-converge/reference/convergence-gates.md +42 -15
  218. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  219. package/skills/pr-converge/reference/ground-rules.md +1 -1
  220. package/skills/pr-converge/reference/per-tick.md +130 -42
  221. package/skills/pr-converge/reference/state-schema.md +10 -0
  222. package/skills/pr-converge/scripts/CLAUDE.md +2 -0
  223. package/skills/pr-converge/scripts/_pr_converge_path_setup.py +5 -1
  224. package/skills/pr-converge/scripts/check_convergence.py +605 -29
  225. package/skills/pr-converge/scripts/check_convergence_availability.py +232 -0
  226. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -235
  227. package/skills/pr-converge/scripts/check_convergence_thread_gates.py +1 -1
  228. package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -2
  229. package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +4 -0
  230. package/skills/pr-converge/scripts/test_check_convergence.py +71 -3
  231. package/skills/pr-converge/scripts/test_check_convergence_availability.py +326 -0
  232. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -0
  233. package/skills/pr-converge/scripts/test_check_convergence_contract.py +89 -17
  234. package/skills/pr-converge/scripts/test_check_convergence_fixture.py +179 -0
  235. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -68
  236. package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +24 -0
  237. package/skills/pr-converge/test_step5_host_branch.py +106 -0
  238. package/skills/pr-loop-cloud-transport/SKILL.md +2 -0
  239. package/skills/reviewer-gates/SKILL.md +7 -5
  240. package/skills/team-advisor/SKILL.md +7 -7
@@ -11,23 +11,27 @@
11
11
 
12
12
  export const meta = {
13
13
  name: 'autoconverge',
14
- description: 'Drive one draft PR to convergence in a single autonomous run: each round runs a deterministic static sweep first, then code-review, bug-audit, and self-review lenses in parallel on the same HEAD, dedups findings, fixes once, and re-verifies; Bugbot and Copilot then run as terminal confirmation gates before a final convergence check marks the PR ready.',
14
+ description: 'Drive one draft PR to convergence in a single autonomous run: each round runs a deterministic static sweep first, then code-review, bug-audit, and self-review lenses in parallel on the same HEAD, dedups findings, fixes once, and re-verifies; Bugbot, Copilot, and Codex then run as terminal confirmation gates before a final convergence check marks the PR ready.',
15
15
  whenToUse: 'Launched by the /autoconverge skill after it resolves PR scope, enters a worktree, and grants project .claude permissions.',
16
16
  phases: [
17
17
  { title: 'Reuse', detail: 'Before convergence, one reuse lens scans the full diff for reuse improvements that are certain, behaviorally identical, and autonomously implementable, and applies the qualifying ones in one commit' },
18
18
  { title: 'Converge', detail: 'A deterministic static sweep runs first each round; then code-review, bug-audit, and self-review run in parallel on the same HEAD; one clean-coder applies all fixes; the loop repeats until every lens is clean on a stable HEAD' },
19
19
  { title: 'Bugbot gate', detail: 'A terminal confirmation gate that runs once the internal lenses are clean; when Bugbot is disabled or unavailable it spawns no agent and passes, otherwise it fetches Bugbot\'s verdict and routes any finding back into Converge' },
20
- { title: 'Copilot gate', detail: 'When the quota pre-check reports Copilot out of premium-request quota or unavailable, skip the gate with no agent spawned; otherwise request a Copilot review and poll up to the configured cap, then route findings by tier — self-healing findings flow back into Converge, and each code-concern finding goes to its own verifier agent that must execute a check against HEAD: a confirmed finding (defect reproduced by a run command) joins the fix round carrying its repro, a refuted finding (correct behavior demonstrated by a run command) is replied-to and resolved with the check evidence, and only inconclusive findings return blocker user-review for the orchestrator to gate on rather than auto-fixing or marking the PR ready; when Copilot is down or out of quota log a notice and mark the PR ready with the gate bypassed' },
20
+ { title: 'Copilot gate', detail: 'When the quota pre-check reports Copilot out of premium-request quota or unavailable, skip the gate with no agent spawned; otherwise request a Copilot review and poll up to the configured cap, then route findings by tier — self-healing findings flow back into Converge, and each code-concern finding goes to its own verifier agent that must execute a check against HEAD: a confirmed finding (defect reproduced by a run command) joins the fix round carrying its repro, a refuted finding (correct behavior demonstrated by a run command) is replied-to and resolved with the check evidence, and only inconclusive findings return blocker user-review for the orchestrator to gate on rather than auto-fixing or marking the PR ready; when Copilot is down or out of quota log a notice and proceed with the gate bypassed' },
21
+ { title: 'Codex gate', detail: 'A conditional-required terminal gate that runs after Bugbot and Copilot: when CLAUDE_REVIEWS_DISABLED lists codex, when weekly usage is at or below the shared probe threshold (or null), or when the wrapper classifies codex_down, the gate skips without blocking; when usage is above the threshold it runs the codex-review wrapper against the PR base branch, routes findings into the fix path, and on a clean pass records the codex-clean HEAD for the convergence check' },
21
22
  { title: 'Finalize', detail: 'Run check_convergence.py; mark draft=false on a full pass' },
22
23
  ],
23
24
  }
24
25
 
26
+ const homeDirectory = (process.env.HOME || process.env.USERPROFILE || '').replace(/\\/g, '/')
27
+
25
28
  const CONFIG = {
26
29
  maxIterations: 20,
27
30
  maxConsecutiveNoLensRounds: 3,
28
31
  copilotMaxPolls: 8,
29
32
  sharedScripts: '$HOME/.claude/skills/pr-converge/scripts',
30
33
  prLoopScripts: '$HOME/.claude/_shared/pr-loop/scripts',
34
+ codexScripts: `${homeDirectory}/.claude/skills/codex-review/scripts`,
31
35
  bugteamRubric: '$HOME/.claude/_shared/pr-loop/audit-contract.md',
32
36
  precatchRubric: '$HOME/.claude/_shared/pr-loop/precatch-rubric.md',
33
37
  }
@@ -539,13 +543,15 @@ function runGeneralUtilityTask(task, context) {
539
543
  * the FINALIZE loop routes to the repair path, which loops back and re-runs this
540
544
  * same combined check; only the passing path ever attempts gh pr ready. The agent
541
545
  * edits no code, so it runs on the cheapest model at low effort.
542
- * @param {object} context carries head, bugbotDown, copilotDown, and bugteamPostBlocked
546
+ * @param {object} context carries head, bugbotDown, copilotDown, codexDown, codexCleanAt, and bugteamPostBlocked
543
547
  * @returns {Promise<object>} FINALIZE_SCHEMA result
544
548
  */
545
549
  function runConvergenceCheck(context) {
546
550
  const label = 'finalize-check'
547
551
  const bugbotDownFlag = context.bugbotDown ? ' --bugbot-down' : ''
548
552
  const copilotDownFlag = context.copilotDown ? ' --copilot-down' : ''
553
+ const codexDownFlag = context.codexDown ? ' --codex-down' : ''
554
+ const codexCleanAtFlag = context.codexCleanAt ? ` --codex-clean-at ${context.codexCleanAt}` : ''
549
555
  const bugteamPostBlockedFlag = context.bugteamPostBlocked ? ' --bugteam-post-blocked' : ''
550
556
  const bugteamPostBlockedNote = context.bugteamPostBlocked
551
557
  ? ` The --bugteam-post-blocked flag is set because the environment refused the CLEAN bugteam review post this run. The review lenses already cleared this HEAD, so the check skips the bugteam CLEAN-review gate and reports that gate as PASS — bypassed (bugteam_post_blocked) rather than failing on a review that was never allowed to land.\n`
@@ -553,19 +559,33 @@ function runConvergenceCheck(context) {
553
559
  const reviewerOptOutTokens = []
554
560
  if (context.bugbotDown) reviewerOptOutTokens.push('bugbot')
555
561
  if (context.copilotDown) reviewerOptOutTokens.push('copilot')
562
+ if (context.codexDown) reviewerOptOutTokens.push('codex')
556
563
  if (context.bugteamPostBlocked) reviewerOptOutTokens.push('bugteam')
564
+ const jobDirName = process.env.CLAUDE_JOB_DIR
565
+ const needsCodexCleanExportFallback = Boolean(context.codexCleanAt) && !jobDirName
566
+ if (needsCodexCleanExportFallback && !reviewerOptOutTokens.includes('codex')) {
567
+ reviewerOptOutTokens.push('codex')
568
+ }
557
569
  const reviewerOptOut = reviewerOptOutTokens.length > 0
558
- ? ` Reviewer(s) opted out this run (${reviewerOptOutTokens.join(', ')}), so before gh pr ready export the token(s) in this same shell session so the independent mark-ready blocker hook's convergence re-check — which re-runs check_convergence.py with no flags — inherits the bypass through the env:\n bash: export CLAUDE_REVIEWS_DISABLED="${reviewerOptOutTokens.join(',')}" (PowerShell: $env:CLAUDE_REVIEWS_DISABLED = "${reviewerOptOutTokens.join(',')}")\n`
570
+ ? ` Reviewer(s) opted out this run (${reviewerOptOutTokens.join(', ')}), so before gh pr ready export the token(s) in this same shell session so the independent mark-ready blocker hook's convergence re-check — which re-runs check_convergence.py with no flags — inherits the bypass through the env. Emit exactly one export (merge every token into a single value; never overwrite with a second assignment):\n bash: export CLAUDE_REVIEWS_DISABLED="${reviewerOptOutTokens.join(',')}" (PowerShell: $env:CLAUDE_REVIEWS_DISABLED = "${reviewerOptOutTokens.join(',')}")\n`
571
+ : ''
572
+ const codexCleanAtNote = context.codexCleanAt
573
+ ? (
574
+ jobDirName
575
+ ? ` The Codex gate reported clean on this HEAD, so the --codex-clean-at flag above satisfies this same-shell check. The independent mark-ready blocker hook re-runs check_convergence.py with no flags, and it reads the Codex clean stamp only from the single-PR job-dir state file, so before gh pr ready persist the stamp there: merge {"codex_clean_at": "${context.codexCleanAt}"} into the file named pr-converge-state.json inside the directory named by the CLAUDE_JOB_DIR environment variable (create the file with just that key when it does not exist yet, and preserve any keys already in it).\n`
576
+ : ` The Codex gate reported clean on this HEAD, so the --codex-clean-at flag above satisfies this same-shell check. CLAUDE_JOB_DIR is unset in this shell, so there is no durable stamp file for the flagless mark-ready re-check — the codex token is already included in the single CLAUDE_REVIEWS_DISABLED export above (do not emit a second export that overwrites other tokens).\n`
577
+ )
559
578
  : ''
560
579
  return convergeReadOnlyAgent(
561
580
  `Run the convergence gate for ${prCoordinates} on HEAD ${context.head} and, only when every gate passes, mark the PR ready. Do not edit code.\n\n` +
562
- `1. Run: python "${CONFIG.sharedScripts}/check_convergence.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber}${bugbotDownFlag}${copilotDownFlag}${bugteamPostBlockedFlag}\n` +
581
+ `1. Run: python "${CONFIG.sharedScripts}/check_convergence.py" --owner ${input.owner} --repo ${input.repo} --pr-number ${input.prNumber}${bugbotDownFlag}${copilotDownFlag}${codexDownFlag}${codexCleanAtFlag}${bugteamPostBlockedFlag}\n` +
563
582
  bugteamPostBlockedNote +
564
583
  ` Exit 0 -> every gate passed: set pass:true, failures:[].\n` +
565
584
  ` Exit 1 -> set pass:false and failures to each printed FAIL line verbatim.\n` +
566
585
  ` Exit 2 -> retry once; if it still errors, set pass:false, failures:["check_convergence gh error"].\n\n` +
567
586
  `2. Only when step 1 set pass:true, mark the PR ready and confirm it left draft state:\n` +
568
587
  reviewerOptOut +
588
+ codexCleanAtNote +
569
589
  ` Run: gh pr ready ${input.prNumber} --repo ${input.owner}/${input.repo}\n` +
570
590
  ` Re-query the draft state: gh api repos/${input.owner}/${input.repo}/pulls/${input.prNumber} --jq .draft\n` +
571
591
  ` Set ready:true only when the re-query prints false (the PR is no longer a draft); set ready:false when gh pr ready errors or the re-query still prints true.\n` +
@@ -639,13 +659,51 @@ const COPILOT_SCHEMA = {
639
659
  properties: {
640
660
  sha: { type: 'string' },
641
661
  clean: { type: 'boolean' },
642
- down: { type: 'boolean', description: 'true when Copilot is down or out of quota — it posts an out-of-usage notice or never surfaces a review on HEAD after the poll cap; the gate is bypassed and the run proceeds to mark-ready' },
662
+ down: { type: 'boolean', description: 'true when Copilot is down or out of quota — it posts an out-of-usage notice or never surfaces a review on HEAD after the poll cap; the gate is bypassed and the run proceeds to the Codex gate' },
643
663
  reviewUrl: { type: 'string', description: 'the Copilot review html_url when the gate carries findings, otherwise an empty string — the user-review payload links the orchestrator and the user to the review' },
644
664
  findings: COPILOT_FINDINGS_SCHEMA,
645
665
  },
646
666
  required: ['sha', 'clean', 'down', 'findings'],
647
667
  }
648
668
 
669
+ const CODEX_SKIP_REASONS = ['', 'token', 'usage']
670
+
671
+ const CODEX_FINDINGS_SCHEMA = {
672
+ type: 'array',
673
+ items: {
674
+ type: 'object',
675
+ additionalProperties: false,
676
+ properties: {
677
+ file: { type: 'string' },
678
+ line: { type: 'integer' },
679
+ severity: { type: 'string', enum: ['P0', 'P1', 'P2'] },
680
+ category: { type: 'string', enum: ['bug', 'code-standard'], description: 'code-standard for pure CODE_RULES/style violations with no behavioral impact; bug otherwise' },
681
+ title: { type: 'string' },
682
+ detail: { type: 'string' },
683
+ replyToCommentId: { type: ['integer', 'null'], description: 'GitHub review comment id to reply to and resolve, or null when the finding has no thread' },
684
+ },
685
+ required: ['file', 'line', 'severity', 'category', 'title', 'detail', 'replyToCommentId'],
686
+ },
687
+ }
688
+
689
+ const CODEX_SCHEMA = {
690
+ type: 'object',
691
+ additionalProperties: false,
692
+ properties: {
693
+ sha: { type: 'string', description: 'PR HEAD SHA this gate evaluated' },
694
+ clean: { type: 'boolean', description: 'true when Codex reported no findings on sha, or when the gate skipped without requiring a review' },
695
+ down: { type: 'boolean', description: 'true when Codex is down (wrapper classifies codex_down) or opted out via the codex token — the gate is bypassed' },
696
+ skipped: { type: 'boolean', description: 'true when the gate did not run the review wrapper (opt-out token or usage at/below the shared threshold)' },
697
+ skipReason: {
698
+ type: 'string',
699
+ enum: CODEX_SKIP_REASONS,
700
+ description: 'token when CLAUDE_REVIEWS_DISABLED lists codex; usage when weekly percent_left is null or at/below the shared probe threshold; empty string when the review ran or classified down',
701
+ },
702
+ findings: CODEX_FINDINGS_SCHEMA,
703
+ },
704
+ required: ['sha', 'clean', 'down', 'skipped', 'skipReason', 'findings'],
705
+ }
706
+
649
707
  const COPILOT_VERIFY_VERDICTS = ['confirmed', 'refuted', 'inconclusive']
650
708
 
651
709
  const COPILOT_VERIFY_SCHEMA = {
@@ -1415,6 +1473,30 @@ function classifyReviewerGateOutcome(reviewerGate) {
1415
1473
  return { kind: 'retry' }
1416
1474
  }
1417
1475
 
1476
+ /**
1477
+ * Classify a Codex terminal-gate result into the loop's next action. Codex is a
1478
+ * conditional-required sibling of Bugbot and Copilot: the agent may skip without
1479
+ * running the review (opt-out token or weekly usage at/below the shared probe
1480
+ * threshold), report down when the wrapper classifies codex_down, return findings
1481
+ * for the fix path, or report clean so the run stamps codex_clean_at for the
1482
+ * convergence check. A dead agent is a retry rather than an approval.
1483
+ * @param {object|null|undefined} codexGate the CODEX_SCHEMA result, or null on agent failure
1484
+ * @returns {{kind: string, findings?: Array<object>}} the next action
1485
+ */
1486
+ function classifyCodexGateOutcome(codexGate) {
1487
+ if (codexGate == null) return { kind: 'retry' }
1488
+ if (codexGate.skipped === true) {
1489
+ if (codexGate.skipReason === 'token') return { kind: 'skip-token' }
1490
+ if (codexGate.skipReason === 'usage') return { kind: 'skip-usage' }
1491
+ return { kind: 'retry' }
1492
+ }
1493
+ if (codexGate.down === true) return { kind: 'down' }
1494
+ const allFindings = codexGate.findings || []
1495
+ if (allFindings.length > 0) return { kind: 'fix', findings: allFindings }
1496
+ if (codexGate.clean === true) return { kind: 'clean' }
1497
+ return { kind: 'retry' }
1498
+ }
1499
+
1418
1500
  /**
1419
1501
  * Split a Copilot round's findings by routing tier. Any tier other than the
1420
1502
  * explicit 'self-healing' counts as a code concern, so a missing or unexpected
@@ -1938,7 +2020,7 @@ function resolveCleanAuditNote(auditResult, head, rounds) {
1938
2020
  * poll cap is hit; return Copilot's findings or a down signal. Copilot is down
1939
2021
  * when it posts an out-of-usage notice (the requester hit their quota) rather
1940
2022
  * than a review, or surfaces no review at all after the poll cap; the gate
1941
- * reports either as down so the run logs a notice and proceeds to mark-ready with
2023
+ * reports either as down so the run logs a notice and proceeds to the Codex gate with
1942
2024
  * the gate bypassed rather than waiting on a review that will not arrive.
1943
2025
  * @param {string} head converged PR HEAD SHA
1944
2026
  * @returns {Promise<object>} COPILOT_SCHEMA result
@@ -1964,6 +2046,44 @@ function runCopilotGate(head) {
1964
2046
  )
1965
2047
  }
1966
2048
 
2049
+ /**
2050
+ * Codex gate: conditional-required terminal confirmation after Bugbot and
2051
+ * Copilot. Honors the shared opt-out token, the weekly usage probe threshold
2052
+ * (via is_codex_review_required — never an inline percent), and the wrapper's
2053
+ * codex_down classification. When required, runs the codex-review wrapper
2054
+ * against the PR base branch and returns findings or a clean stamp for the
2055
+ * convergence check.
2056
+ * @param {string} head converged PR HEAD SHA
2057
+ * @returns {Promise<object>} CODEX_SCHEMA result
2058
+ */
2059
+ function runCodexGate(head) {
2060
+ return convergeReadOnlyAgent(
2061
+ `You are the Codex gate for ${prCoordinates}, HEAD ${head}. Do not edit code, commit, or push.\n\n` +
2062
+ `Codex is a conditional-required terminal gate. Follow these steps in order and return strictly the schema.\n\n` +
2063
+ `1. Opt-out gate. Run exactly:\n` +
2064
+ ` python "${CONFIG.prLoopScripts}/reviews_disabled.py" --reviewer codex\n` +
2065
+ ` Exit 0 means CLAUDE_REVIEWS_DISABLED lists codex -> return {sha:${'`'}${head}${'`'}, clean:true, down:true, skipped:true, skipReason:'token', findings:[]} and stop. Exit 1 means continue.\n\n` +
2066
+ `2. Usage probe. Run the probe CLI first and capture its exit code and stdout:\n` +
2067
+ ` python "${CONFIG.codexScripts}/codex_usage_probe.py"\n` +
2068
+ ` Non-zero exit means the Codex CLI or probe is broken — not a usage-threshold skip. Return {sha:${'`'}${head}${'`'}, clean:false, down:true, skipped:false, skipReason:'', findings:[]} and stop (same shape as codex_down). Never map a failed probe to skipReason:'usage'.\n` +
2069
+ ` Exit 0 prints one JSON object (percent_left may be a number or null). Decide required vs skip ONLY through the shared helper — never restate a percent threshold. Feed that stdout into:\n` +
2070
+ ` python -c "import json,sys; sys.path.insert(0, r'${CONFIG.codexScripts}'); from codex_usage_probe import is_codex_review_required; report=json.load(sys.stdin); print('required' if is_codex_review_required(report.get('percent_left')) else 'skip')"\n` +
2071
+ ` When it prints skip -> return {sha:${'`'}${head}${'`'}, clean:true, down:false, skipped:true, skipReason:'usage', findings:[]} and stop. When it prints required -> continue.\n\n` +
2072
+ `3. Resolve the PR base branch (the review target is HEAD vs base, never an invented commit range). Run exactly:\n` +
2073
+ ` gh api repos/${input.owner}/${input.repo}/pulls/${input.prNumber} --jq .base.ref\n` +
2074
+ ` Capture the printed base branch name.\n\n` +
2075
+ `4. Run the codex-review wrapper against that base. From the PR worktree root, run a short Python driver that imports the skill scripts (add "${CONFIG.codexScripts}" to sys.path) and:\n` +
2076
+ ` - creates a temp run-state directory under the OS temp dir\n` +
2077
+ ` - calls run_codex_review.run_codex_review(repository_directory=<repo root Path>, run_state_directory=<temp Path>, base_branch=<base ref from step 3>)\n` +
2078
+ ` - when outcome_class is codex_down (or classify_codex_run on a non-zero exit reports codex_down) -> return {sha:${'`'}${head}${'`'}, clean:false, down:true, skipped:false, skipReason:'', findings:[]} and stop\n` +
2079
+ ` - when outcome_class is completed: parse findings with parse_codex_findings.parse_codex_findings(agent_message)\n` +
2080
+ ` - empty findings -> return {sha:${'`'}${head}${'`'}, clean:true, down:false, skipped:false, skipReason:'', findings:[]}\n` +
2081
+ ` - non-empty findings -> map each to {file, line (integer start of line_range, or 1 when missing), severity (priority or P2), category ('code-standard' for pure CODE_RULES/style with no behavioral impact, 'bug' otherwise), title, detail (body), replyToCommentId:null} and return {sha:${'`'}${head}${'`'}, clean:false, down:false, skipped:false, skipReason:'', findings:[...]}\n\n` +
2082
+ `Never hard-code a usage percent threshold. Never re-parse CLAUDE_REVIEWS_DISABLED by hand — only reviews_disabled.py decides the token. Return strictly the schema.`,
2083
+ { label: 'codex-gate', phase: 'Codex gate', schema: CODEX_SCHEMA, agentType: 'general-purpose', ...TIERS.haikuLow },
2084
+ )
2085
+ }
2086
+
1967
2087
  /**
1968
2088
  * Spawn the verifier agent for one Copilot code-concern finding. The verifier
1969
2089
  * decides confirmed / refuted / inconclusive by executing a check against the
@@ -2413,6 +2533,8 @@ const noLensRoundCauses = new Set()
2413
2533
  let blocker = null
2414
2534
  let bugbotDown = input.bugbotDisabled || false
2415
2535
  let copilotDown = input.copilotDisabled || false
2536
+ let codexDown = false
2537
+ let codexCleanAt = null
2416
2538
  let copilotNote = null
2417
2539
  let standardsNote = null
2418
2540
  let cleanAuditNote = null
@@ -2617,9 +2739,9 @@ while (iterations < CONFIG.maxIterations) {
2617
2739
  if (phase === 'COPILOT') {
2618
2740
  if (resolveReviewerDown(reviewerAvailability?.copilot, input.copilotDisabled || false)) {
2619
2741
  copilotDown = true
2620
- copilotNote = 'Copilot was unavailable or out of premium-request quota this round — the Copilot gate was bypassed with no agent spawned and the PR was marked ready without a Copilot review'
2621
- log('Copilot gate: the shared reviewer-availability probe (or the run input) reported Copilot unavailable — skipping the Copilot gate with no agent spawned and proceeding to mark-ready with the gate bypassed.')
2622
- phase = 'FINALIZE'
2742
+ copilotNote = 'Copilot was unavailable or out of premium-request quota this round — the Copilot gate was bypassed with no agent spawned; remaining gates including Codex still run before ready'
2743
+ log('Copilot gate: the shared reviewer-availability probe (or the run input) reported Copilot unavailable — skipping the Copilot gate with no agent spawned and proceeding to the Codex gate with the Copilot gate bypassed.')
2744
+ phase = 'CODEX'
2623
2745
  continue
2624
2746
  }
2625
2747
  const copilot = await runCopilotGate(head)
@@ -2631,10 +2753,10 @@ while (iterations < CONFIG.maxIterations) {
2631
2753
  continue
2632
2754
  }
2633
2755
  if (copilotOutcome.kind === 'down') {
2634
- log('Copilot gate: Copilot is down or out of quota — no review on HEAD after the poll cap. Logging a notice and proceeding to mark-ready with the Copilot gate bypassed.')
2756
+ log('Copilot gate: Copilot is down or out of quota — no review on HEAD after the poll cap. Logging a notice and proceeding to the Codex gate with the Copilot gate bypassed.')
2635
2757
  copilotDown = true
2636
- copilotNote = 'Copilot was down or out of quota — the Copilot gate was bypassed and the PR was marked ready without a Copilot review'
2637
- phase = 'FINALIZE'
2758
+ copilotNote = 'Copilot was down or out of quota — the Copilot gate was bypassed; remaining gates including Codex still run before ready'
2759
+ phase = 'CODEX'
2638
2760
  continue
2639
2761
  }
2640
2762
  if (copilotOutcome.kind === 'fix') {
@@ -2662,7 +2784,7 @@ while (iterations < CONFIG.maxIterations) {
2662
2784
  log('Every code-concern finding was refuted with executed-check evidence and the round carries no self-healing findings — the Copilot gate passes')
2663
2785
  copilotDown = false
2664
2786
  copilotNote = null
2665
- phase = 'FINALIZE'
2787
+ phase = 'CODEX'
2666
2788
  continue
2667
2789
  }
2668
2790
  }
@@ -2673,7 +2795,7 @@ while (iterations < CONFIG.maxIterations) {
2673
2795
  if (standardsOutcome?.deferredPr) deferredPrs.push(standardsOutcome.deferredPr)
2674
2796
  copilotDown = false
2675
2797
  copilotNote = null
2676
- phase = 'FINALIZE'
2798
+ phase = 'CODEX'
2677
2799
  continue
2678
2800
  }
2679
2801
  log(`Copilot raised ${roundFindings.length} finding(s) — fixing and re-converging`)
@@ -2692,12 +2814,74 @@ while (iterations < CONFIG.maxIterations) {
2692
2814
  }
2693
2815
  copilotDown = false
2694
2816
  copilotNote = null
2817
+ phase = 'CODEX'
2818
+ continue
2819
+ }
2820
+
2821
+ if (phase === 'CODEX') {
2822
+ const codex = await runCodexGate(head)
2823
+ const codexOutcome = classifyCodexGateOutcome(codex)
2824
+ if (codexOutcome.kind === 'retry') {
2825
+ log('Codex gate agent died or returned an unreliable result — re-running the gate on the same HEAD')
2826
+ continue
2827
+ }
2828
+ if (codexOutcome.kind === 'skip-token') {
2829
+ log('Codex gate: CLAUDE_REVIEWS_DISABLED lists codex — skipping the terminal Codex gate with no review spawned and proceeding to mark-ready with the gate bypassed.')
2830
+ codexDown = true
2831
+ codexCleanAt = null
2832
+ phase = 'FINALIZE'
2833
+ continue
2834
+ }
2835
+ if (codexOutcome.kind === 'skip-usage') {
2836
+ log('Codex gate: weekly usage is at/below the shared probe threshold (or null) — skipping the terminal Codex gate; the convergence check applies the same rule.')
2837
+ codexDown = false
2838
+ codexCleanAt = null
2839
+ phase = 'FINALIZE'
2840
+ continue
2841
+ }
2842
+ if (codexOutcome.kind === 'down') {
2843
+ log('Codex gate: Codex classified codex_down — bypassing the terminal Codex gate and proceeding to mark-ready with the gate bypassed.')
2844
+ codexDown = true
2845
+ codexCleanAt = null
2846
+ phase = 'FINALIZE'
2847
+ continue
2848
+ }
2849
+ if (codexOutcome.kind === 'fix') {
2850
+ if (isStandardsOnlyRound(codexOutcome.findings)) {
2851
+ log(`Codex raised ${codexOutcome.findings.length} code-standard-only finding(s) — deferring to follow-up PRs and treating the gate as passed`)
2852
+ const standardsOutcome = await openStandardsFollowUpOnce(head, codexOutcome.findings, 'codex', { copilotDisabled: copilotDown, bugbotDisabled: bugbotDown })
2853
+ standardsNote = standardsDeferralNote(codexOutcome.findings.length, buildStandardsDeferral())
2854
+ if (standardsOutcome?.deferredPr) deferredPrs.push(standardsOutcome.deferredPr)
2855
+ codexDown = false
2856
+ codexCleanAt = head
2857
+ phase = 'FINALIZE'
2858
+ continue
2859
+ }
2860
+ log(`Codex raised ${codexOutcome.findings.length} finding(s) — fixing and re-converging`)
2861
+ const fixResult = await applyFixes(head, codexOutcome.findings, 'codex')
2862
+ const hadThreadBearingFinding = codexOutcome.findings.some((each) => collectFindingThreadIds(each).length > 0)
2863
+ const fixProgress = detectFixProgress(fixResult, head, hadThreadBearingFinding)
2864
+ if (!fixProgress.progressed) {
2865
+ blocker = fixResult?.resolvedWithoutCommit === true && !hadThreadBearingFinding
2866
+ ? `fix stalled: codex gate raised ${codexOutcome.findings.length} in-memory finding(s) with no GitHub thread, the fix judged them all stale (resolvedWithoutCommit) and moved no code on HEAD ${head} — re-raising would loop to the iteration cap`
2867
+ : `codex fix lens landed no push for ${codexOutcome.findings.length} finding(s) on HEAD ${head}`
2868
+ break
2869
+ }
2870
+ head = null
2871
+ codexDown = false
2872
+ codexCleanAt = null
2873
+ phase = 'CONVERGE'
2874
+ continue
2875
+ }
2876
+ log(`Codex gate: clean on ${head?.slice(0, 7)} — recording codex-clean HEAD for the convergence check`)
2877
+ codexDown = false
2878
+ codexCleanAt = head
2695
2879
  phase = 'FINALIZE'
2696
2880
  continue
2697
2881
  }
2698
2882
 
2699
2883
  if (phase === 'FINALIZE') {
2700
- const finalizeResult = await runConvergenceCheck({ head, bugbotDown, copilotDown, bugteamPostBlocked: cleanAuditNote !== null })
2884
+ const finalizeResult = await runConvergenceCheck({ head, bugbotDown, copilotDown, codexDown, codexCleanAt, bugteamPostBlocked: cleanAuditNote !== null })
2701
2885
  const convergenceOutcome = classifyConvergenceOutcome(finalizeResult)
2702
2886
  if (convergenceOutcome.kind === 'retry') {
2703
2887
  log('Convergence check agent died or returned no FAIL lines — re-running the check on the same HEAD')
@@ -4,14 +4,14 @@ Runs an audit-fix loop on an open pull request until all findings are resolved o
4
4
 
5
5
  ## Purpose
6
6
 
7
- Each loop: a `code-quality-agent` (fresh context, all A–P audit categories) produces an outcome XML; a `clean-coder` agent applies every fix; the lead commits, pushes, and posts a GitHub PR review (APPROVE on clean, REQUEST_CHANGES with inline anchored comments on dirty). Grants `.claude/**` write permissions at the start and revokes them at the end.
7
+ Each loop: a `code-quality-agent` (fresh context, all A–Q audit categories) produces an outcome XML; a `clean-coder` agent applies every fix; the lead commits, pushes, and posts a GitHub PR review (APPROVE on clean, REQUEST_CHANGES with inline anchored comments on dirty). Grants `.claude/**` write permissions at the start and revokes them at the end.
8
8
 
9
9
  ## Key files
10
10
 
11
11
  | File | Purpose |
12
12
  |---|---|
13
13
  | `SKILL.md` | Hub — pre-flight call, refusals, the audit-posting step (runs the shared `post_audit_thread.py` helper), progress checklist, and situation-to-reference table. Read this first. |
14
- | `PROMPTS.md` | Spawn XML, A–P category bindings, outcome XML schemas. |
14
+ | `PROMPTS.md` | Spawn XML, A–Q category bindings, outcome XML schemas. |
15
15
  | `CONSTRAINTS.md` | Invariants — what the loop must never violate. |
16
16
  | `EXAMPLES.md` | Exit scenarios: converged, cap-reached, stuck, refusal, mixed-outcome. |
17
17
  | `sources.md` | Doc URLs and verbatim quotes cited in the skill body. |
@@ -2,10 +2,11 @@
2
2
 
3
3
  ## Constraints
4
4
 
5
- - **Full A–P audit every loop, no exceptions.** PR size, "focused audit," "team overhead," "CODE_RULES already passed" — not valid reasons. Empty `<findings/>` for any category is a valid result. The audit agent walks all A–P rubrics each loop.
5
+ - **Full A–Q audit every loop, no exceptions.** PR size, "focused audit," "team overhead," "CODE_RULES already passed" — not valid reasons. Empty `<findings/>` for any category is a valid result. The audit agent walks all A–Q rubrics each loop.
6
6
  - **One run per invocation, multi-PR supported.** All PRs in a single /bugteam invocation share one `run_temp_dir`. Per-PR identity lives in the subagent name prefix (`bugfind-pr<N>-loop<L>` / `bugfix-pr<N>-loop<L>`) and the `<run_temp_dir>/pr-<N>/` subfolder containing that PR's git worktree, diff patches, and outcome XML files.
7
7
  - **Grant before any spawn, revoke before any return.** Step 0 grants project `.claude/**` permissions; Step 4 (`pr-loop-lifecycle` Close) revokes. Both are mandatory. Revoke runs on every exit path including error, cap-reached, and stuck.
8
- - **Fresh subagent per loop.** Both bugfind and bugfix are spawned new each loop. Reusing a subagent across loops accumulates context inside that subagent's window — defeats clean-room.
8
+ - **Fresh worker per loop.** Both bugfind and bugfix route through the worker-spawn dispatcher (or a fresh Agent-tool context on tier 2). Reusing a worker across loops accumulates context — defeats clean-room.
9
+
9
10
  - **One up-front confirmation = whole cycle.** The `/bugteam` invocation authorizes the entire cycle; every subsequent decision runs on that single authorization.
10
11
  - **20-loop hard cap.** Counted as **AUDIT** completions (increment in Step 3). Standards-fix passes before an audit do not advance `loop_count`. Worst case includes extra clean-coder spawns for the code-rules gate.
11
12
  - **Code rules gate before every AUDIT.** Run `python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/code_rules_gate.py" --base origin/<baseRefName>` until exit **0** before spawning **bugfind**. Same `validate_content` logic as `hooks/blocking/code_rules_enforcer.py`.
@@ -24,7 +24,7 @@ cd into `<worktree_path>` before any git or file operation.
24
24
  </scope>
25
25
 
26
26
  <bug_categories>
27
- Investigate each of the sixteen categories (A–P) explicitly. For each,
27
+ Investigate each of the seventeen categories (A–Q) explicitly. For each,
28
28
  return either at least one finding OR a verified-clean entry with the
29
29
  evidence backing the verdict. A category is verified-clean only when one
30
30
  complete execution path through the changed code has been traced from
@@ -37,7 +37,7 @@ cd into `<worktree_path>` before any git or file operation.
37
37
  When evidence contains any of these phrases, the category is not
38
38
  verified-clean -- re-audit with a concrete trace.
39
39
 
40
- Categories A–P (one-line summary; full rubric and sub-bucket
40
+ Categories A–Q (one-line summary; full rubric and sub-bucket
41
41
  decomposition for each is in
42
42
  `$HOME/.claude/audit-rubrics/category_rubrics/`; ready-to-send Variant
43
43
  C prompts — each with a PR/repo-independent generalized skeleton above
@@ -60,6 +60,7 @@ cd into `<worktree_path>` before any git or file operation.
60
60
  N. Test-name scenario verifier
61
61
  O. Docstring / fixture-prose vs implementation drift
62
62
  P. Name / regex / word-list vs behavior-contract precision
63
+ Q. Cross-surface claim consistency (terminology, PR-description claims, message-vs-guard)
63
64
  </bug_categories>
64
65
 
65
66
  <rubric_reference>
@@ -79,7 +80,7 @@ cd into `<worktree_path>` before any git or file operation.
79
80
  </constraints>
80
81
 
81
82
  <comment_posting>
82
- Load all A–P rubrics from
83
+ Load all A–Q rubrics from
83
84
  `$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/`. The prompt file
84
85
  is a template for output shape, not a straitjacket — reorganize when the
85
86
  diff demands it. The diff supplies the findings; the rubric supplies the
@@ -90,7 +91,7 @@ cd into `<worktree_path>` before any git or file operation.
90
91
  done.
91
92
 
92
93
  <self_audit_checklist>
93
- [ ] Walk all 16 categories (A–P), each with Shape A or Shape B
94
+ [ ] Walk all 17 categories (A–Q), each with Shape A or Shape B
94
95
  [ ] Assign finding IDs (loop<L>-<K>)
95
96
  [ ] Capture excerpts, validate anchors, format finding bodies
96
97
  [ ] Build findings JSON, invoke post_audit_thread.py, capture html_url
@@ -98,7 +99,7 @@ cd into `<worktree_path>` before any git or file operation.
98
99
  [ ] Write outcome XML
99
100
  </self_audit_checklist>
100
101
 
101
- 1. Audit the diff against the 16 categories above. Buffer the findings
102
+ 1. Audit the diff against the 17 categories above. Buffer the findings
102
103
  in memory; all posting happens at step 4 once anchors are validated.
103
104
  2. Assign each finding a stable finding_id of exactly the form `loop<L>-<K>`
104
105
  where <K> is 1-based within this loop.
@@ -218,7 +219,7 @@ attributes.
218
219
  </bugteam_audit>
219
220
  ```
220
221
 
221
- Verified-clean evidence per A–P category is surfaced in the agent's text-mode
222
+ Verified-clean evidence per A–Q category is surfaced in the agent's text-mode
222
223
  final report, not in this outcome XML (the writer accepts a flat findings list
223
224
  only).
224
225
 
@@ -2,19 +2,22 @@
2
2
  name: bugteam
3
3
  description: >-
4
4
  Open pull request audit–fix until convergence: CODE_RULES gate, clean-room
5
- audit (`code-quality-agent`, opus) and fix (`clean-coder`), per-loop
6
- GitHub reviews, 20-audit cap; grant then revoke `.claude/**`. Spawns
7
- background subagents (`Agent(..., run_in_background=true)`). Triggers: '/bugteam', 'run
8
- the bug team', 'auto-fix the PR until clean', 'loop audit and fix'.
5
+ audit (`code-quality-agent`, opus) and fix (`clean-coder`, opus), per-loop
6
+ GitHub reviews, 20-audit cap; grant then revoke `.claude/**`. AUDIT, FIX, and
7
+ standards-fix route through `resolve_worker_spawn.py` (headless tiers or
8
+ Agent-tool fallback). Triggers: '/bugteam', 'run the bug team', 'auto-fix
9
+ the PR until clean', 'loop audit and fix'.
9
10
  ---
10
11
 
11
12
  # Bugteam
12
13
 
13
- Audit–fix until convergence. Bugfind: `code-quality-agent`, fresh context each
14
- loop, auditing all A–P categories. Bugfix: `clean-coder`. Hard cap: 20 audit
14
+ Audit–fix until convergence. Bugfind: `code-quality-agent`, fresh process or
15
+ fresh agent context each loop, auditing all A–Q categories via the
16
+ worker-spawn dispatcher. Bugfix and standards-fix: `clean-coder` via the same
17
+ dispatcher (role `clean-coder`; tier 2 pins model opus). Hard cap: 20 audit
15
18
  loops. Grant `.claude/**` at start, revoke always at end.
16
19
 
17
- The audit agent loads the A–P category rubrics from
20
+ The audit agent loads the A–Q category rubrics from
18
21
  `$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` alongside
19
22
  [`PROMPTS.md`](PROMPTS.md) and produces a single outcome XML per loop.
20
23
 
@@ -49,9 +52,11 @@ First match wins; respond with the quoted line exactly and stop:
49
52
  - **No PR or upstream diff.** `No PR or upstream diff. /bugteam needs a target.`
50
53
  - **Dirty tree.** `Uncommitted changes detected. Stash, commit, or revert before
51
54
  /bugteam.`
52
- - **Missing subagents.** Before Step 0, confirm `code-quality-agent` and
53
- `clean-coder` exist. Else: `Required subagent type <name> not installed.
54
- /bugteam needs both code-quality-agent and clean-coder available.`
55
+ - **Missing subagents (tier 2 only).** Before a tier-2 Agent-tool spawn,
56
+ confirm `code-quality-agent` and `clean-coder` exist. Else: `Required
57
+ subagent type <name> not installed. /bugteam needs both code-quality-agent
58
+ and clean-coder available.` Headless tiers rely on the preflight's
59
+ agent-definition-file check instead.
55
60
 
56
61
  ## Audit posting
57
62
 
@@ -106,7 +111,7 @@ end-to-end mental model before starting Step 0.
106
111
  | Posting the end-of-pass audit review (APPROVE on CLEAN, REQUEST_CHANGES with inline anchored comments on DIRTY) | [§ Audit posting](#audit-posting), which runs [`_shared/pr-loop/scripts/post_audit_thread.py`](../../_shared/pr-loop/scripts/post_audit_thread.py) |
107
112
  | Posting per-finding fix replies via GitHub MCP `add_reply_to_pull_request_comment` (rendered with the unified template at [`_shared/pr-loop/audit-reply-template.md`](../../_shared/pr-loop/audit-reply-template.md)) | [reference/github-pr-reviews.md](reference/github-pr-reviews.md) |
108
113
  | Teardown, PR description rewrite composed by the lead, permission revoke, final report | [../pr-loop-lifecycle/reference/teardown-publish-permissions.md](../pr-loop-lifecycle/reference/teardown-publish-permissions.md) |
109
- | Spawn-prompt XML, A–P category bindings, outcome XML schemas | [PROMPTS.md](PROMPTS.md) |
114
+ | Spawn-prompt XML, A–Q category bindings, outcome XML schemas | [PROMPTS.md](PROMPTS.md) |
110
115
  | Per-category audit content (sub-buckets, decision criteria, ready-to-send Variant C templates) | `$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` |
111
116
  | Invariants and design rationale | [CONSTRAINTS.md](CONSTRAINTS.md), [reference/design-rationale.md](reference/design-rationale.md) |
112
117
  | Audit-contract finding shape (Shape A / B), Haiku secondary, post-fix self-audit | [../../_shared/pr-loop/audit-contract.md](../../_shared/pr-loop/audit-contract.md) |
@@ -119,11 +124,11 @@ end-to-end mental model before starting Step 0.
119
124
  - `SKILL.md` — this hub.
120
125
  - `reference/` — workflow detail per situation.
121
126
  - `scripts/` — utility scripts executed, not loaded as primary context.
122
- - `PROMPTS.md` — spawn XML, A–P category bindings, outcome schemas.
127
+ - `PROMPTS.md` — spawn XML, A–Q category bindings, outcome schemas.
123
128
  - `CONSTRAINTS.md` — invariants.
124
129
  - `EXAMPLES.md` — exit scenarios.
125
130
  - `sources.md` — doc URLs and verbatim quotes.
126
131
  - `~/.claude/audit-rubrics/` — installed by `npx claude-dev-env` from
127
- `packages/claude-dev-env/audit-rubrics/`; the audit agent reads all A–P
132
+ `packages/claude-dev-env/audit-rubrics/`; the audit agent reads all A–Q
128
133
  rubrics under `category_rubrics/` and prompts under `prompts/`. Required
129
134
  at audit time alongside `PROMPTS.md`.