claude-dev-env 2.16.0 → 2.17.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 (535) hide show
  1. package/.agents/agents/AGENTS.md +6 -0
  2. package/.agents/skills/AGENTS.md +34 -35
  3. package/.agents/skills/_shared/pr-loop/AGENTS.md +22 -15
  4. package/.agents/skills/_shared/pr-loop/portable-driver.md +6 -7
  5. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +2 -2
  6. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +1 -1
  7. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +25 -16
  8. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +1 -5
  9. package/.agents/skills/autoconverge/reference/convergence-gates.md +31 -0
  10. package/.agents/skills/autoconverge/reference/convergence.md +8 -10
  11. package/.agents/skills/autoconverge/reference/gotchas.md +1 -1
  12. package/.agents/skills/autoconverge/reference/stop-conditions.md +1 -1
  13. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +1 -1
  14. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +1 -1
  15. package/.agents/skills/autoconverge/workflow/converge.mjs +3 -3
  16. package/.agents/skills/cert-classification-rule/SKILL.md +224 -0
  17. package/.agents/skills/cert-classification-rule/scripts/resort_cert_folders.py +158 -0
  18. package/.agents/skills/eli5/SKILL.md +128 -0
  19. package/.agents/skills/eli5/reference/task-seeds.md +11 -0
  20. package/.agents/skills/fresh-branch/scripts/test_create_fresh_branch.py +2 -3
  21. package/.agents/skills/name-by-capability-audit/SKILL.md +67 -0
  22. package/.agents/skills/name-by-capability-audit/reference/fetch-commands.md +29 -0
  23. package/.agents/skills/name-by-capability-audit/reference/offense-examples.md +32 -0
  24. package/.agents/skills/name-by-capability-audit/reference/report-template.md +29 -0
  25. package/.agents/skills/name-by-capability-audit/reference/rule-checklist.md +38 -0
  26. package/.agents/skills/name-by-capability-audit/reference/task-seeds.md +9 -0
  27. package/.agents/skills/pr-cleanup/SKILL.md +99 -0
  28. package/.agents/skills/prototype/reference/honest-limitations.md +1 -1
  29. package/.agents/skills/prototype/reference/promotion-tasks.md +1 -1
  30. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +2 -2
  31. package/.agents/skills/prototype/workflows/promotion.md +2 -2
  32. package/.agents/skills/review-router/SKILL.md +51 -0
  33. package/.agents/skills/review-router/reference/route-policy.json +7 -0
  34. package/.agents/skills/review-router/reference/task-seeds.md +1 -0
  35. package/.agents/skills/review-router/scripts/conftest.py +4 -0
  36. package/.agents/skills/review-router/scripts/review_router.py +50 -0
  37. package/.agents/skills/review-router/scripts/review_router_cli.py +211 -0
  38. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +1 -0
  39. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  40. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +13 -0
  41. package/.agents/skills/review-router/scripts/test_review_router.py +23 -0
  42. package/.agents/skills/review-router/scripts/test_review_router_cli.py +47 -0
  43. package/.agents/skills/review-tier/SKILL.md +56 -0
  44. package/.agents/skills/review-tier/reference/task-seeds.md +1 -0
  45. package/.agents/skills/review-tier/reference/tier-policy.json +9 -0
  46. package/.agents/skills/review-tier/scripts/conftest.py +4 -0
  47. package/.agents/skills/review-tier/scripts/review_tier.py +332 -0
  48. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +1 -0
  49. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  50. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +29 -0
  51. package/.agents/skills/review-tier/scripts/test_generation.py +171 -0
  52. package/.agents/skills/review-tier/scripts/test_review_tier.py +33 -0
  53. package/.agents/skills/session-log/SKILL.md +1 -1
  54. package/.agents/skills/shared-extraction-audit/SKILL.md +160 -0
  55. package/.agents/skills/shared-extraction-audit/reference/examples.md +46 -0
  56. package/.agents/skills/shared-extraction-audit/reference/offense-taxonomy.md +100 -0
  57. package/.agents/skills/source-command-logifix/SKILL.md +29 -0
  58. package/.agents/skills/source-command-sr-loop/SKILL.md +58 -0
  59. package/.agents/skills-archived/AGENTS.md +45 -0
  60. package/.agents/{skills → skills-archived}/anthropic-plan/AGENTS.md +1 -1
  61. package/.agents/{skills → skills-archived}/bugteam/scripts/reflow_skill_md.py +3 -3
  62. package/.agents/{skills → skills-archived}/codex-review/test_skill_scaffold.py +1 -1
  63. package/.agents/{skills → skills-archived}/copilot-finding-triage/SKILL.md +1 -1
  64. package/.agents/{skills → skills-archived}/pr-converge/SKILL.md +6 -6
  65. package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/AGENTS.md +1 -1
  66. package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/constants.py +1 -1
  67. package/.agents/{skills → skills-archived}/pr-converge/reference/multi-pr-orchestration.md +1 -1
  68. package/.agents/{skills → skills-archived}/pr-converge/reference/per-tick.md +3 -3
  69. package/.agents/{skills → skills-archived}/pr-converge/scripts/reflow_skill_md.py +2 -2
  70. package/.agents/{skills → skills-archived}/pr-converge/test_portable_pacer_gate.py +6 -2
  71. package/.agents/{skills → skills-archived}/pr-converge/workflows/schedule-wakeup-loop.md +1 -1
  72. package/.agents/{skills → skills-archived}/split-pr/reference/proposal-format.md +1 -1
  73. package/AGENTS.md +34 -58
  74. package/_shared/AGENTS.md +2 -0
  75. package/_shared/advisor/reference/sol-rung.md +2 -2
  76. package/_shared/advisor/reference/third-party-bind.md +1 -1
  77. package/_shared/pr-loop/audit-reply-template.md +4 -0
  78. package/_shared/pr-loop/fix-protocol.md +7 -6
  79. package/_shared/pr-loop/gh-payloads.md +8 -6
  80. package/_shared/pr-loop/precatch-rubric.md +6 -5
  81. package/_shared/pr-loop/scripts/AGENTS.md +15 -0
  82. package/_shared/pr-loop/scripts/_pr_converge_path_setup.py +38 -0
  83. package/_shared/pr-loop/scripts/check_bugbot_ci.py +279 -0
  84. package/_shared/pr-loop/scripts/check_convergence.py +923 -0
  85. package/_shared/pr-loop/scripts/check_convergence_availability.py +275 -0
  86. package/_shared/pr-loop/scripts/check_convergence_gates.py +279 -0
  87. package/_shared/pr-loop/scripts/check_convergence_thread_gates.py +219 -0
  88. package/_shared/pr-loop/scripts/check_pending_reviews.py +154 -0
  89. package/_shared/pr-loop/scripts/codex_down_classifier.py +98 -0
  90. package/_shared/pr-loop/scripts/codex_review_scripts_constants/.claude/CLAUDE.md +1 -0
  91. package/_shared/pr-loop/scripts/codex_review_scripts_constants/AGENTS.md +18 -0
  92. package/_shared/pr-loop/scripts/codex_review_scripts_constants/__init__.py +1 -0
  93. package/_shared/pr-loop/scripts/codex_review_scripts_constants/classifier_constants.py +35 -0
  94. package/_shared/pr-loop/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +82 -0
  95. package/_shared/pr-loop/scripts/codex_review_scripts_constants/findings_constants.py +18 -0
  96. package/_shared/pr-loop/scripts/codex_review_scripts_constants/run_constants.py +53 -0
  97. package/_shared/pr-loop/scripts/codex_usage_probe.py +570 -0
  98. package/_shared/pr-loop/scripts/conftest.py +43 -0
  99. package/_shared/pr-loop/scripts/fetch_copilot_reviews.py +134 -0
  100. package/_shared/pr-loop/scripts/fixtures/auth_failure_synthetic.txt +1 -0
  101. package/_shared/pr-loop/scripts/fixtures/config_load_failure_v0.125.0.txt +1 -0
  102. package/_shared/pr-loop/scripts/fixtures/freeform_findings_v0.144.3.txt +6 -0
  103. package/_shared/pr-loop/scripts/fixtures/model_rejection_v0.125.0.jsonl +5 -0
  104. package/_shared/pr-loop/scripts/fixtures/structured_findings.txt +13 -0
  105. package/_shared/pr-loop/scripts/fixtures/success_stream_v0.144.3.jsonl +6 -0
  106. package/_shared/pr-loop/scripts/fixtures/unknown_failure_synthetic.txt +1 -0
  107. package/_shared/pr-loop/scripts/fixtures/usage_limit_synthetic.txt +1 -0
  108. package/_shared/pr-loop/scripts/parse_codex_findings.py +207 -0
  109. package/_shared/pr-loop/scripts/post_fix_reply.py +168 -0
  110. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/.claude/CLAUDE.md +1 -0
  111. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/AGENTS.md +18 -0
  112. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/__init__.py +0 -0
  113. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +70 -0
  114. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/pr_converge_constants.py +118 -0
  115. package/_shared/pr-loop/scripts/pr_converge_scripts_constants/reflow_skill_md_constants.py +13 -0
  116. package/_shared/pr-loop/scripts/pr_converge_skill_constants/.claude/CLAUDE.md +1 -0
  117. package/_shared/pr-loop/scripts/pr_converge_skill_constants/AGENTS.md +26 -0
  118. package/_shared/pr-loop/scripts/pr_converge_skill_constants/__init__.py +0 -0
  119. package/_shared/pr-loop/scripts/pr_converge_skill_constants/constants.py +69 -0
  120. package/_shared/pr-loop/scripts/run_codex_review.py +609 -0
  121. package/_shared/pr-loop/scripts/test__pr_converge_path_setup.py +30 -0
  122. package/_shared/pr-loop/scripts/test_check_bugbot_ci.py +312 -0
  123. package/_shared/pr-loop/scripts/test_check_convergence.py +438 -0
  124. package/_shared/pr-loop/scripts/test_check_convergence_availability.py +391 -0
  125. package/_shared/pr-loop/scripts/test_check_convergence_codex.py +517 -0
  126. package/_shared/pr-loop/scripts/test_check_convergence_contract.py +400 -0
  127. package/_shared/pr-loop/scripts/test_check_convergence_fixture.py +179 -0
  128. package/_shared/pr-loop/scripts/test_check_convergence_gates.py +84 -0
  129. package/_shared/pr-loop/scripts/test_check_convergence_thread_gates.py +99 -0
  130. package/_shared/pr-loop/scripts/test_codex_down_classifier.py +143 -0
  131. package/_shared/pr-loop/scripts/test_codex_usage_probe.py +683 -0
  132. package/_shared/pr-loop/scripts/test_parse_codex_findings.py +130 -0
  133. package/_shared/pr-loop/scripts/test_run_codex_review.py +969 -0
  134. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +1 -1
  135. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +1 -1
  136. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +1 -1
  137. package/_shared/pr-loop/state-schema.md +20 -28
  138. package/_shared/process-tree/AGENTS.md +3 -3
  139. package/bin/ever-shipped-skills.mjs +29 -1
  140. package/bin/install-constants.mjs +15 -6
  141. package/bin/install.agents-home.test.mjs +13 -0
  142. package/bin/install.cursor-rules.test.mjs +3 -3
  143. package/bin/install.mjs +80 -20
  144. package/bin/install.profile-root.test.mjs +1 -1
  145. package/bin/install.prune.test.mjs +23 -204
  146. package/bin/install.test.mjs +8 -5
  147. package/bin/resolve-install-root.mjs +21 -0
  148. package/bin/resolve-package-managed-directory.mjs +4 -4
  149. package/commands/AGENTS.md +2 -0
  150. package/commands/sr-loop.md +3 -0
  151. package/docs/references/prose-style-enforcement.md +3 -7
  152. package/hooks/AGENTS.md +0 -3
  153. package/hooks/blocking/AGENTS.md +0 -2
  154. package/hooks/blocking/ask_user_question_shape_blocker.py +83 -0
  155. package/hooks/blocking/pre_tool_use_dispatcher.py +2 -19
  156. package/hooks/blocking/state_description_blocker.py +1 -1
  157. package/hooks/blocking/test_ask_user_question_shape.py +69 -0
  158. package/hooks/blocking/test_pre_tool_use_dispatcher.py +17 -58
  159. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +9 -104
  160. package/hooks/blocking/test_state_description_blocker.py +1 -0
  161. package/hooks/blocking/test_stop_dispatcher.py +1 -26
  162. package/hooks/hooks.json +1 -1
  163. package/hooks/hooks_constants/AGENTS.md +0 -3
  164. package/hooks/hooks_constants/ask_user_question_shape.py +1 -1
  165. package/hooks/hooks_constants/ask_user_question_shape_constants.py +73 -0
  166. package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +1 -2
  167. package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -1
  168. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -7
  169. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  170. package/hooks/hooks_constants/test_ask_user_question_shape.py +1 -1
  171. package/hooks/hooks_constants/test_prose_metrics_parity.py +29 -107
  172. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  173. package/hooks/hooks_constants/working_style_prompt_constants.py +9 -10
  174. package/hooks/session/AGENTS.md +4 -1
  175. package/hooks/session/test_working_style_prompt.py +8 -4
  176. package/hooks/session/working_style_prompt.py +4 -2
  177. package/package.json +1 -4
  178. package/rules/AGENTS.md +2 -5
  179. package/rules/asd-ste100-language.md +39 -0
  180. package/rules/ask-user-question-required.md +1 -1
  181. package/rules/long-horizon-autonomy.md +1 -1
  182. package/scripts/AGENTS.md +1 -0
  183. package/scripts/Show-Asset.ps1 +106 -0
  184. package/scripts/test_validate_instruction_pairs.py +30 -3
  185. package/scripts/tests/test_engine.py +4 -4
  186. package/scripts/tests/test_rules.py +5 -5
  187. package/system-prompts/AGENTS.md +8 -0
  188. package/system-prompts/software-engineer.xml +8 -3
  189. package/.agents/skills/grokify/evals/README.md +0 -72
  190. package/.agents/skills/grokify/evals/parse-payload.test.mjs +0 -171
  191. package/.agents/skills/grokify/evals/run-capability-evals.mjs +0 -545
  192. package/_shared/pr-loop/scripts/README.md +0 -21
  193. package/docs/imagegen.md +0 -22
  194. package/hooks/blocking/eli11_reply_enforcer.py +0 -436
  195. package/hooks/blocking/plain_language_blocker.py +0 -758
  196. package/hooks/blocking/test_eli11_reply_enforcer.py +0 -390
  197. package/hooks/blocking/test_plain_language_blocker.py +0 -683
  198. package/hooks/blocking/test_plain_language_blocker_allowlist.py +0 -228
  199. package/hooks/diagnostic/AGENTS.md +0 -75
  200. package/hooks/diagnostic/CLAUDE.md +0 -1
  201. package/hooks/diagnostic/hook_log_extractor.py +0 -875
  202. package/hooks/diagnostic/hook_log_init.py +0 -202
  203. package/hooks/diagnostic/hook_log_stop_wrapper.py +0 -26
  204. package/hooks/diagnostic/migrations/2026-04-25-drop-themes-hook-events.sql +0 -3
  205. package/hooks/diagnostic/migrations/AGENTS.md +0 -16
  206. package/hooks/diagnostic/migrations/CLAUDE.md +0 -1
  207. package/hooks/diagnostic/migrations/README.md +0 -76
  208. package/hooks/diagnostic/queries/AGENTS.md +0 -19
  209. package/hooks/diagnostic/queries/CLAUDE.md +0 -1
  210. package/hooks/diagnostic/queries/block_details_for_hook.sql +0 -26
  211. package/hooks/diagnostic/queries/blocks_by_category.sql +0 -10
  212. package/hooks/diagnostic/queries/blocks_by_tool.sql +0 -9
  213. package/hooks/diagnostic/queries/blocks_last_7_days.sql +0 -11
  214. package/hooks/diagnostic/queries/top_blockers_last_24_hours.sql +0 -12
  215. package/hooks/diagnostic/queries/top_blockers_overall.sql +0 -12
  216. package/hooks/diagnostic/requirements-hook-logs-dev.txt +0 -2
  217. package/hooks/diagnostic/requirements-hook-logs.txt +0 -1
  218. package/hooks/diagnostic/schema.sql +0 -51
  219. package/hooks/diagnostic/test_hook_log_extractor.py +0 -1518
  220. package/hooks/diagnostic/test_hook_log_init.py +0 -227
  221. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +0 -17
  222. package/hooks/hooks_constants/eli11_reply_enforcer_constants.py +0 -94
  223. package/hooks/hooks_constants/hook_log_extractor_constants.py +0 -233
  224. package/hooks/hooks_constants/plain_language_blocker_constants.py +0 -366
  225. package/hooks/hooks_constants/test_hook_log_extractor_constants.py +0 -123
  226. package/rules/doc-prose-cuts.md +0 -58
  227. package/rules/eli11-replies.md +0 -36
  228. package/rules/opus5-communication-contract.md +0 -45
  229. package/rules/plain-language.md +0 -9
  230. /package/.agents/{skills → skills-archived}/anthropic-plan/.claude/CLAUDE.md +0 -0
  231. /package/.agents/{skills → skills-archived}/anthropic-plan/SKILL.md +0 -0
  232. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/.claude/CLAUDE.md +0 -0
  233. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/AGENTS.md +0 -0
  234. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/.claude/CLAUDE.md +0 -0
  235. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/AGENTS.md +0 -0
  236. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/__init__.py +0 -0
  237. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/anthropic_plan_scripts_constants/validate_packet_constants.py +0 -0
  238. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/test_validate_packet.py +0 -0
  239. /package/.agents/{skills → skills-archived}/anthropic-plan/scripts/validate_packet.py +0 -0
  240. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/.claude/CLAUDE.md +0 -0
  241. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/AGENTS.md +0 -0
  242. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/README.md +0 -0
  243. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/build-prompt.md +0 -0
  244. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/reuse-audit.md +0 -0
  245. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/source-map.md +0 -0
  246. /package/.agents/{skills → skills-archived}/anthropic-plan/templates/visual-plan.template.html +0 -0
  247. /package/.agents/{skills → skills-archived}/anthropic-plan/test_skill_contract.py +0 -0
  248. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/.claude/CLAUDE.md +0 -0
  249. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/AGENTS.md +0 -0
  250. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/plan-packet.contract.test.mjs +0 -0
  251. /package/.agents/{skills → skills-archived}/anthropic-plan/workflow/plan-packet.mjs +0 -0
  252. /package/.agents/{skills → skills-archived}/auditing-claude-config/.claude/CLAUDE.md +0 -0
  253. /package/.agents/{skills → skills-archived}/auditing-claude-config/AGENTS.md +0 -0
  254. /package/.agents/{skills → skills-archived}/auditing-claude-config/SKILL.md +0 -0
  255. /package/.agents/{skills → skills-archived}/auditing-claude-config/reference/probe-hook.md +0 -0
  256. /package/.agents/{skills → skills-archived}/beat-sheet/SKILL.md +0 -0
  257. /package/.agents/{skills → skills-archived}/beat-sheet/reference/visual-beats.md +0 -0
  258. /package/.agents/{skills → skills-archived}/bugteam/.claude/CLAUDE.md +0 -0
  259. /package/.agents/{skills → skills-archived}/bugteam/AGENTS.md +0 -0
  260. /package/.agents/{skills → skills-archived}/bugteam/CONSTRAINTS.md +0 -0
  261. /package/.agents/{skills → skills-archived}/bugteam/EXAMPLES.md +0 -0
  262. /package/.agents/{skills → skills-archived}/bugteam/PROMPTS.md +0 -0
  263. /package/.agents/{skills → skills-archived}/bugteam/SKILL.md +0 -0
  264. /package/.agents/{skills → skills-archived}/bugteam/reference/.claude/CLAUDE.md +0 -0
  265. /package/.agents/{skills → skills-archived}/bugteam/reference/AGENTS.md +0 -0
  266. /package/.agents/{skills → skills-archived}/bugteam/reference/README.md +0 -0
  267. /package/.agents/{skills → skills-archived}/bugteam/reference/audit-and-teammates.md +0 -0
  268. /package/.agents/{skills → skills-archived}/bugteam/reference/copilot-gap-analysis.md +0 -0
  269. /package/.agents/{skills → skills-archived}/bugteam/reference/design-rationale.md +0 -0
  270. /package/.agents/{skills → skills-archived}/bugteam/reference/github-pr-reviews.md +0 -0
  271. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/.claude/CLAUDE.md +0 -0
  272. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/AGENTS.md +0 -0
  273. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-assign-ids.md +0 -0
  274. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-capture-excerpts.md +0 -0
  275. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-walk-categories.md +0 -0
  276. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/audit-write-xml.md +0 -0
  277. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-append-summary.md +0 -0
  278. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-apply-fixes.md +0 -0
  279. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-git-add-commit.md +0 -0
  280. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-git-push.md +0 -0
  281. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-post-reply.md +0 -0
  282. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-publish-summary.md +0 -0
  283. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-py-compile.md +0 -0
  284. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-read-files.md +0 -0
  285. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-resolve-thread.md +0 -0
  286. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-test-suite.md +0 -0
  287. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-violation-count.md +0 -0
  288. /package/.agents/{skills → skills-archived}/bugteam/reference/obstacles/fix-write-xml.md +0 -0
  289. /package/.agents/{skills → skills-archived}/bugteam/reference/team-setup.md +0 -0
  290. /package/.agents/{skills → skills-archived}/bugteam/scripts/.claude/CLAUDE.md +0 -0
  291. /package/.agents/{skills → skills-archived}/bugteam/scripts/AGENTS.md +0 -0
  292. /package/.agents/{skills → skills-archived}/bugteam/scripts/README.md +0 -0
  293. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_code_rules_gate.py +0 -0
  294. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_fix_hookspath.py +0 -0
  295. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_preflight.py +0 -0
  296. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/.claude/CLAUDE.md +0 -0
  297. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/AGENTS.md +0 -0
  298. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/__init__.py +0 -0
  299. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/probe_code_rules_enforcer_check_constants.py +0 -0
  300. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/reflow_skill_md_constants.py +0 -0
  301. /package/.agents/{skills → skills-archived}/bugteam/scripts/bugteam_scripts_constants/windows_safe_rmtree_constants.py +0 -0
  302. /package/.agents/{skills → skills-archived}/bugteam/scripts/probe_code_rules_enforcer_check.py +0 -0
  303. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_code_rules_gate.py +0 -0
  304. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_fix_hookspath.py +0 -0
  305. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_bugteam_preflight.py +0 -0
  306. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_probe_code_rules_enforcer_check.py +0 -0
  307. /package/.agents/{skills → skills-archived}/bugteam/scripts/test_windows_safe_rmtree.py +0 -0
  308. /package/.agents/{skills → skills-archived}/bugteam/scripts/windows_safe_rmtree.py +0 -0
  309. /package/.agents/{skills → skills-archived}/bugteam/sources.md +0 -0
  310. /package/.agents/{skills → skills-archived}/bugteam/test_skill_additions.py +0 -0
  311. /package/.agents/{skills → skills-archived}/closeout/SKILL.md +0 -0
  312. /package/.agents/{skills → skills-archived}/closeout/reference/handoff-prompt-template.md +0 -0
  313. /package/.agents/{skills → skills-archived}/closeout/reference/pii-redaction-checklist.md +0 -0
  314. /package/.agents/{skills → skills-archived}/codex-review/.claude/CLAUDE.md +0 -0
  315. /package/.agents/{skills → skills-archived}/codex-review/AGENTS.md +0 -0
  316. /package/.agents/{skills → skills-archived}/codex-review/SKILL.md +0 -0
  317. /package/.agents/{skills → skills-archived}/codex-review/reference/.claude/CLAUDE.md +0 -0
  318. /package/.agents/{skills → skills-archived}/codex-review/reference/AGENTS.md +0 -0
  319. /package/.agents/{skills → skills-archived}/codex-review/reference/cli-contract.md +0 -0
  320. /package/.agents/{skills → skills-archived}/codex-review/reference/loop-integration.md +0 -0
  321. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_down_classifier.py +0 -0
  322. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/.claude/CLAUDE.md +0 -0
  323. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/AGENTS.md +0 -0
  324. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/__init__.py +0 -0
  325. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/classifier_constants.py +0 -0
  326. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/codex_usage_probe_constants.py +0 -0
  327. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/findings_constants.py +0 -0
  328. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_review_scripts_constants/run_constants.py +0 -0
  329. /package/.agents/{skills → skills-archived}/codex-review/scripts/codex_usage_probe.py +0 -0
  330. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/auth_failure_synthetic.txt +0 -0
  331. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/config_load_failure_v0.125.0.txt +0 -0
  332. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/freeform_findings_v0.144.3.txt +0 -0
  333. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/model_rejection_v0.125.0.jsonl +0 -0
  334. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/structured_findings.txt +0 -0
  335. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/success_stream_v0.144.3.jsonl +0 -0
  336. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/unknown_failure_synthetic.txt +0 -0
  337. /package/.agents/{skills → skills-archived}/codex-review/scripts/fixtures/usage_limit_synthetic.txt +0 -0
  338. /package/.agents/{skills → skills-archived}/codex-review/scripts/parse_codex_findings.py +0 -0
  339. /package/.agents/{skills → skills-archived}/codex-review/scripts/run_codex_review.py +0 -0
  340. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_codex_down_classifier.py +0 -0
  341. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_codex_usage_probe.py +0 -0
  342. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_parse_codex_findings.py +0 -0
  343. /package/.agents/{skills → skills-archived}/codex-review/scripts/test_run_codex_review.py +0 -0
  344. /package/.agents/{skills → skills-archived}/comments/SKILL.md +0 -0
  345. /package/.agents/{skills → skills-archived}/condensing-instructions/SKILL.md +0 -0
  346. /package/.agents/{skills → skills-archived}/copilot-finding-triage/reference/tier-rubric.md +0 -0
  347. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/conftest.py +0 -0
  348. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/__init__.py +0 -0
  349. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/__init__.py +0 -0
  350. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/copilot_finding_triage_constants/config/notify_ntfy_constants.py +0 -0
  351. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/notify_ntfy.py +0 -0
  352. /package/.agents/{skills → skills-archived}/copilot-finding-triage/scripts/test_notify_ntfy.py +0 -0
  353. /package/.agents/{skills → skills-archived}/copilot-finding-triage/templates/notification.md +0 -0
  354. /package/.agents/{skills → skills-archived}/copilot-review/.claude/CLAUDE.md +0 -0
  355. /package/.agents/{skills → skills-archived}/copilot-review/AGENTS.md +0 -0
  356. /package/.agents/{skills → skills-archived}/copilot-review/SKILL.md +0 -0
  357. /package/.agents/{skills → skills-archived}/copilot-review/templates/subagent-prompt.md +0 -0
  358. /package/.agents/{skills → skills-archived}/descriptions/SKILL.md +0 -0
  359. /package/.agents/{skills → skills-archived}/emergencies/SKILL.md +0 -0
  360. /package/.agents/{skills → skills-archived}/grokify/SKILL.md +0 -0
  361. /package/.agents/{skills → skills-archived}/grokify/capability-claims.test.mjs +0 -0
  362. /package/.agents/{skills → skills-archived}/grokify/templates/handoff-template.md +0 -0
  363. /package/.agents/{skills → skills-archived}/plan-to-pr/SKILL.md +0 -0
  364. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/final-validation-tasks.md +0 -0
  365. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/model-routing.md +0 -0
  366. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/packet-contract.md +0 -0
  367. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/packet-schema.json +0 -0
  368. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/process-inventory.md +0 -0
  369. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/review-loop.md +0 -0
  370. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/run-record.schema.json +0 -0
  371. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/self-audit-tasks.md +0 -0
  372. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/task-seeds.md +0 -0
  373. /package/.agents/{skills → skills-archived}/plan-to-pr/reference/task-ticket.md +0 -0
  374. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/config/__init__.py +0 -0
  375. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/config/constants.py +0 -0
  376. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/create_packet.py +0 -0
  377. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/load_skill_constants.py +0 -0
  378. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_create_packet.py +0 -0
  379. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_packet.py +0 -0
  380. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_protocol.py +0 -0
  381. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/test_validate_run.py +0 -0
  382. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_packet.py +0 -0
  383. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_protocol.py +0 -0
  384. /package/.agents/{skills → skills-archived}/plan-to-pr/scripts/validate_run.py +0 -0
  385. /package/.agents/{skills → skills-archived}/plan-to-pr/test_skill_contract.py +0 -0
  386. /package/.agents/{skills → skills-archived}/plan-to-pr/test_task_ticket_contract.py +0 -0
  387. /package/.agents/{skills → skills-archived}/pr-converge/.claude/CLAUDE.md +0 -0
  388. /package/.agents/{skills → skills-archived}/pr-converge/AGENTS.md +0 -0
  389. /package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/.claude/CLAUDE.md +0 -0
  390. /package/.agents/{skills → skills-archived}/pr-converge/pr_converge_skill_constants/__init__.py +0 -0
  391. /package/.agents/{skills → skills-archived}/pr-converge/reference/.claude/CLAUDE.md +0 -0
  392. /package/.agents/{skills → skills-archived}/pr-converge/reference/AGENTS.md +0 -0
  393. /package/.agents/{skills → skills-archived}/pr-converge/reference/convergence-gates.md +0 -0
  394. /package/.agents/{skills → skills-archived}/pr-converge/reference/examples.md +0 -0
  395. /package/.agents/{skills → skills-archived}/pr-converge/reference/fix-protocol.md +0 -0
  396. /package/.agents/{skills → skills-archived}/pr-converge/reference/ground-rules.md +0 -0
  397. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/.claude/CLAUDE.md +0 -0
  398. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/AGENTS.md +0 -0
  399. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-post-replies.md +0 -0
  400. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-publish-summary.md +0 -0
  401. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-push.md +0 -0
  402. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-read-filelines.md +0 -0
  403. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-reset-state.md +0 -0
  404. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-resolve-threads.md +0 -0
  405. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +0 -0
  406. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-stage-commit.md +0 -0
  407. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-trigger-bugbot.md +0 -0
  408. /package/.agents/{skills → skills-archived}/pr-converge/reference/obstacles/fix-write-test.md +0 -0
  409. /package/.agents/{skills → skills-archived}/pr-converge/reference/progress-checklist.md +0 -0
  410. /package/.agents/{skills → skills-archived}/pr-converge/reference/state-schema.md +0 -0
  411. /package/.agents/{skills → skills-archived}/pr-converge/reference/stop-conditions.md +0 -0
  412. /package/.agents/{skills → skills-archived}/pr-converge/scripts/.claude/CLAUDE.md +0 -0
  413. /package/.agents/{skills → skills-archived}/pr-converge/scripts/AGENTS.md +0 -0
  414. /package/.agents/{skills → skills-archived}/pr-converge/scripts/README.md +0 -0
  415. /package/.agents/{skills → skills-archived}/pr-converge/scripts/_pr_converge_path_setup.py +0 -0
  416. /package/.agents/{skills → skills-archived}/pr-converge/scripts/caller-window-pid.ps1 +0 -0
  417. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_bugbot_ci.py +0 -0
  418. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence.py +0 -0
  419. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_availability.py +0 -0
  420. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_gates.py +0 -0
  421. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_convergence_thread_gates.py +0 -0
  422. /package/.agents/{skills → skills-archived}/pr-converge/scripts/check_pending_reviews.py +0 -0
  423. /package/.agents/{skills → skills-archived}/pr-converge/scripts/conftest.py +0 -0
  424. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue-caller.cmd +0 -0
  425. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue-stop-others.ps1 +0 -0
  426. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue.ahk +0 -0
  427. /package/.agents/{skills → skills-archived}/pr-converge/scripts/cursor-agents-continue.cmd +0 -0
  428. /package/.agents/{skills → skills-archived}/pr-converge/scripts/fetch_copilot_reviews.py +0 -0
  429. /package/.agents/{skills → skills-archived}/pr-converge/scripts/post_fix_reply.py +0 -0
  430. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/.claude/CLAUDE.md +0 -0
  431. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/AGENTS.md +0 -0
  432. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/__init__.py +0 -0
  433. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +0 -0
  434. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/pr_converge_constants.py +0 -0
  435. /package/.agents/{skills → skills-archived}/pr-converge/scripts/pr_converge_scripts_constants/reflow_skill_md_constants.py +0 -0
  436. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test__pr_converge_path_setup.py +0 -0
  437. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_bugbot_ci.py +0 -0
  438. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence.py +0 -0
  439. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_availability.py +0 -0
  440. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_codex.py +0 -0
  441. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_contract.py +0 -0
  442. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_fixture.py +0 -0
  443. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_gates.py +0 -0
  444. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_check_convergence_thread_gates.py +0 -0
  445. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_cursor_agents_continue.py +0 -0
  446. /package/.agents/{skills → skills-archived}/pr-converge/scripts/test_reflow_skill_md.py +0 -0
  447. /package/.agents/{skills → skills-archived}/pr-converge/test_step5_host_branch.py +0 -0
  448. /package/.agents/{skills → skills-archived}/pr-converge/workflows/.claude/CLAUDE.md +0 -0
  449. /package/.agents/{skills → skills-archived}/pr-converge/workflows/AGENTS.md +0 -0
  450. /package/.agents/{skills → skills-archived}/pr-fix-protocol/SKILL.md +0 -0
  451. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/SKILL.md +0 -0
  452. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/reference/identity-and-hooks.md +0 -0
  453. /package/.agents/{skills → skills-archived}/pr-loop-cloud-transport/reference/substitution-matrix.md +0 -0
  454. /package/.agents/{skills → skills-archived}/pr-loop-lifecycle/SKILL.md +0 -0
  455. /package/.agents/{skills → skills-archived}/pr-loop-lifecycle/reference/teardown-publish-permissions.md +0 -0
  456. /package/.agents/{skills → skills-archived}/recall/.claude/CLAUDE.md +0 -0
  457. /package/.agents/{skills → skills-archived}/recall/AGENTS.md +0 -0
  458. /package/.agents/{skills → skills-archived}/recall/SKILL.md +0 -0
  459. /package/.agents/{skills → skills-archived}/release-notes-html/SKILL.md +0 -0
  460. /package/.agents/{skills → skills-archived}/remember/.claude/CLAUDE.md +0 -0
  461. /package/.agents/{skills → skills-archived}/remember/AGENTS.md +0 -0
  462. /package/.agents/{skills → skills-archived}/remember/SKILL.md +0 -0
  463. /package/.agents/{skills → skills-archived}/reviewer-gates/SKILL.md +0 -0
  464. /package/.agents/{skills → skills-archived}/reviews/SKILL.md +0 -0
  465. /package/.agents/{skills → skills-archived}/show/SKILL.md +0 -0
  466. /package/.agents/{skills → skills-archived}/show/references/accessibility.md +0 -0
  467. /package/.agents/{skills → skills-archived}/show/references/art.md +0 -0
  468. /package/.agents/{skills → skills-archived}/show/references/charts.md +0 -0
  469. /package/.agents/{skills → skills-archived}/show/references/core-design.md +0 -0
  470. /package/.agents/{skills → skills-archived}/show/references/erds.md +0 -0
  471. /package/.agents/{skills → skills-archived}/show/references/flowcharts.md +0 -0
  472. /package/.agents/{skills → skills-archived}/show/references/host-and-html.md +0 -0
  473. /package/.agents/{skills → skills-archived}/show/references/illustrative-diagrams.md +0 -0
  474. /package/.agents/{skills → skills-archived}/show/references/interaction.md +0 -0
  475. /package/.agents/{skills → skills-archived}/show/references/mockups.md +0 -0
  476. /package/.agents/{skills → skills-archived}/show/references/quality-gates.md +0 -0
  477. /package/.agents/{skills → skills-archived}/show/references/structural-diagrams.md +0 -0
  478. /package/.agents/{skills → skills-archived}/show/references/subject-inventory.md +0 -0
  479. /package/.agents/{skills → skills-archived}/show/references/svg-contract.md +0 -0
  480. /package/.agents/{skills → skills-archived}/show/routing.yaml +0 -0
  481. /package/.agents/{skills → skills-archived}/show/samples/pr1262-v2.svg +0 -0
  482. /package/.agents/{skills → skills-archived}/show/scripts/README.md +0 -0
  483. /package/.agents/{skills → skills-archived}/show/scripts/validate-artifact.py +0 -0
  484. /package/.agents/{skills → skills-archived}/show/scripts/validate-package.py +0 -0
  485. /package/.agents/{skills → skills-archived}/show/templates/html-widget.html +0 -0
  486. /package/.agents/{skills → skills-archived}/show/templates/svg-base.svg +0 -0
  487. /package/.agents/{skills → skills-archived}/show/tests/fixtures/css-var.svg +0 -0
  488. /package/.agents/{skills → skills-archived}/show/tests/fixtures/dead-ref.svg +0 -0
  489. /package/.agents/{skills → skills-archived}/show/tests/fixtures/filled-glyph.svg +0 -0
  490. /package/.agents/{skills → skills-archived}/show/tests/fixtures/inherited-fill.svg +0 -0
  491. /package/.agents/{skills → skills-archived}/show/tests/fixtures/invalid.svg +0 -0
  492. /package/.agents/{skills → skills-archived}/show/tests/fixtures/large-canvas.svg +0 -0
  493. /package/.agents/{skills → skills-archived}/show/tests/fixtures/unfilled-connector.svg +0 -0
  494. /package/.agents/{skills → skills-archived}/show/tests/fixtures/valid.html +0 -0
  495. /package/.agents/{skills → skills-archived}/show/tests/test_validate-artifact.py +0 -0
  496. /package/.agents/{skills → skills-archived}/show/tests/test_validators.py +0 -0
  497. /package/.agents/{skills → skills-archived}/show/workflows/create-visual.md +0 -0
  498. /package/.agents/{skills → skills-archived}/show/workflows/review-visual.md +0 -0
  499. /package/.agents/{skills → skills-archived}/split-pr/SKILL.md +0 -0
  500. /package/.agents/{skills → skills-archived}/split-pr/reference/path-layers.md +0 -0
  501. /package/.agents/{skills → skills-archived}/split-pr/reference/split-further-loop.md +0 -0
  502. /package/.agents/{skills → skills-archived}/split-pr/reference/splitting-principles.md +0 -0
  503. /package/.agents/{skills → skills-archived}/split-pr/scripts/analyze_pr.py +0 -0
  504. /package/.agents/{skills → skills-archived}/split-pr/scripts/categorize_files.py +0 -0
  505. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/__init__.py +0 -0
  506. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/dependency_constants.py +0 -0
  507. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/git_operations_constants.py +0 -0
  508. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/packing_constants.py +0 -0
  509. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/plan_constants.py +0 -0
  510. /package/.agents/{skills → skills-archived}/split-pr/scripts/config/split_pr_constants.py +0 -0
  511. /package/.agents/{skills → skills-archived}/split-pr/scripts/execute_split_slices.py +0 -0
  512. /package/.agents/{skills → skills-archived}/split-pr/scripts/pack_files_into_slices.py +0 -0
  513. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_dependency_graph.py +0 -0
  514. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_git_operations.py +0 -0
  515. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_layer_order.py +0 -0
  516. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_paginate.py +0 -0
  517. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_process_runner.py +0 -0
  518. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_script_types.py +0 -0
  519. /package/.agents/{skills → skills-archived}/split-pr/scripts/split_pr_title.py +0 -0
  520. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_analyze_pr.py +0 -0
  521. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_categorize_files.py +0 -0
  522. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_categorize_files_packing.py +0 -0
  523. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_execute_split_slices.py +0 -0
  524. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_dependency_graph.py +0 -0
  525. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_git_operations.py +0 -0
  526. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_layer_order.py +0 -0
  527. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_paginate.py +0 -0
  528. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_script_types.py +0 -0
  529. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_split_pr_title.py +0 -0
  530. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_dependency_graph.py +0 -0
  531. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan.py +0 -0
  532. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan_contract.py +0 -0
  533. /package/.agents/{skills → skills-archived}/split-pr/scripts/test_verify_plan_path_normalization.py +0 -0
  534. /package/.agents/{skills → skills-archived}/split-pr/scripts/verify_dependency_graph.py +0 -0
  535. /package/.agents/{skills → skills-archived}/split-pr/scripts/verify_plan.py +0 -0
@@ -0,0 +1,923 @@
1
+ """Verify every convergence pre-condition for a PR before marking it ready.
2
+
3
+ ::
4
+
5
+ check_convergence.py --owner O --repo R --pr-number N [flags]
6
+ exit 0 ok: every gate passed — safe to mark ready
7
+ exit 1 flag: a gate failed (FAIL lines print to stdout)
8
+ exit 2 flag: gh CLI error
9
+
10
+ check_convergence.py --owner O --repo R --pr-number N --fixture PATH [flags]
11
+ Same stdout and exit codes as the live path, reading a frozen GitHub
12
+ API snapshot from PATH instead of calling ``gh`` against current state.
13
+
14
+ Each bypass flag closes one reviewer gate when that reviewer is unavailable.
15
+ ``--bugbot-down`` skips the bugbot check-run gate. ``--copilot-down`` skips
16
+ the Copilot review and pending-review gates. ``--bugteam-post-blocked`` skips
17
+ the bugteam CLEAN-review gate. ``--codex-down`` skips the Codex review gate.
18
+
19
+ Bugbot, bugteam, and codex are off by default. Each gate runs only when
20
+ ``CLAUDE_REVIEWS_ENABLED`` lists its token, and a token in
21
+ ``CLAUDE_REVIEWS_DISABLED`` forces one off even when the opt-in lists it.
22
+ Copilot runs by default and stops only when the disabled list names it. For
23
+ Codex, sticky job-dir state ``codex_down: true`` is a further bypass source
24
+ when the original run could not re-pass ``--codex-down``.
25
+
26
+ Every reviewer waiver is disk-authoritative.
27
+ When ``~/.claude/settings.json`` is readable, its env block is the single source.
28
+ The frozen process env is only a fallback when that disk read fails (logged once).
29
+ A probe error does not waive the gate; the live GitHub checks still run.
30
+
31
+ The Codex gate is conditional-required: it demands
32
+ ``codex_clean_at == current_head`` only when the weekly usage probe reports
33
+ more than the probe's threshold percent left. At or below that threshold, null
34
+ usage, the codex token, or ``codex_down`` never blocks ready.
35
+ """
36
+
37
+ from __future__ import annotations
38
+
39
+ import argparse
40
+ import json
41
+ import os
42
+ import subprocess
43
+ import sys
44
+ from collections.abc import Callable
45
+ from pathlib import Path
46
+ from typing import NamedTuple
47
+
48
+ import _pr_converge_path_setup # noqa: F401
49
+ from check_convergence_gates import (
50
+ _check_bot_review,
51
+ _check_bugbot,
52
+ _check_bugbot_not_dirty,
53
+ _flatten_paginated_reviews,
54
+ _get_mergeable,
55
+ _get_pr_head_sha,
56
+ _gh_api_paginated,
57
+ _short_sha,
58
+ _sort_reviews_newest_first,
59
+ _evaluate_mergeable_from_pr_object,
60
+ )
61
+ from check_convergence_thread_gates import (
62
+ _check_no_pending_reviews,
63
+ _count_unresolved_bot_threads,
64
+ )
65
+ from codex_review_scripts_constants.codex_usage_probe_constants import (
66
+ USAGE_REPORT_KEY_PERCENT_LEFT,
67
+ )
68
+ from codex_usage_probe import is_codex_review_required, probe_weekly_usage_via_subprocess
69
+ from pr_converge_scripts_constants.convergence_gate_constants import (
70
+ BUGBOT_DOWN_BYPASS_NOTE,
71
+ CLAUDE_JOB_DIR_ENV_VAR_NAME,
72
+ CODEX_BYPASS_DETAIL,
73
+ CODEX_CLEAN_AT_STATE_KEY,
74
+ CODEX_CLEAN_DETAIL_TEMPLATE,
75
+ CODEX_DOWN_STATE_KEY,
76
+ CODEX_GATE_LABEL,
77
+ CODEX_MISSING_CLEAN_DETAIL_TEMPLATE,
78
+ CODEX_SKIPPED_USAGE_DETAIL,
79
+ COPILOT_DOWN_BYPASS_NOTE,
80
+ FIXTURE_DEFAULT_PENDING_DETAIL,
81
+ FIXTURE_DEFAULT_THREADS_DETAIL,
82
+ FIXTURE_KEY_CODEX_CLEAN_AT,
83
+ FIXTURE_KEY_CODEX_PERCENT_LEFT,
84
+ FIXTURE_KEY_HEAD_SHA,
85
+ FIXTURE_KEY_PENDING_REVIEWS_DETAIL,
86
+ FIXTURE_KEY_PENDING_REVIEWS_PASSED,
87
+ FIXTURE_KEY_PR_OBJECT,
88
+ FIXTURE_KEY_REVIEWS,
89
+ FIXTURE_KEY_UNRESOLVED_BOT_THREADS_DETAIL,
90
+ FIXTURE_KEY_UNRESOLVED_BOT_THREADS_PASSED,
91
+ MINIMUM_ABBREVIATED_SHA_LENGTH,
92
+ PR_CONVERGE_STATE_FILENAME,
93
+ )
94
+ from pr_converge_skill_constants.constants import (
95
+ ALL_COPILOT_CLEAN_REVIEW_STATES,
96
+ ALL_COPILOT_DIRTY_REVIEW_STATES,
97
+ BUGTEAM_LEGACY_CLEAN_TOKEN,
98
+ BUGTEAM_LEGACY_HEADER_PREFIX,
99
+ BUGTEAM_NEW_CLEAN_LABEL,
100
+ BUGTEAM_NEW_HEADER_PREFIX,
101
+ COPILOT_LOGIN_FILTER_SUBSTRING,
102
+ GH_REVIEWS_PATH_TEMPLATE,
103
+ REVIEWS_PER_PAGE,
104
+ )
105
+ from reviews_disabled import (
106
+ is_bugbot_disabled_via_env,
107
+ is_copilot_disabled_via_env,
108
+ )
109
+ from check_convergence_availability import (
110
+ _is_bugteam_disabled_via_resolved_settings,
111
+ _is_codex_disabled_via_resolved_settings,
112
+ _resolve_bugbot_waiver,
113
+ _resolve_copilot_waiver,
114
+ _waiver_from_cli_flag,
115
+ )
116
+
117
+ JsonObject = dict[str, object]
118
+ GateCondition = tuple[str, tuple[bool, str]]
119
+
120
+
121
+ class ConvergenceFixture(NamedTuple):
122
+ """Frozen GitHub API snapshot one fixture gate run evaluates."""
123
+
124
+ head_sha: str
125
+ pr_object: JsonObject
126
+ reviews: list[JsonObject]
127
+ is_zero_unresolved_bot_threads: bool
128
+ unresolved_bot_threads_detail: str
129
+ is_no_pending_reviews: bool
130
+ pending_reviews_detail: str
131
+ codex_percent_left: float | None
132
+ codex_clean_at: str | None
133
+
134
+
135
+ class GateContext(NamedTuple):
136
+ """The PR coordinates and per-reviewer bypass flags one gate run evaluates."""
137
+
138
+ owner: str
139
+ repo: str
140
+ number: int
141
+ head_sha: str
142
+ is_bugbot_down: bool
143
+ is_copilot_down: bool
144
+ is_bugteam_post_blocked: bool
145
+ is_codex_down: bool
146
+ bugbot_bypass_note: str = BUGBOT_DOWN_BYPASS_NOTE
147
+ copilot_bypass_note: str = COPILOT_DOWN_BYPASS_NOTE
148
+ live_codex_clean_at: str | None = None
149
+ fixture: ConvergenceFixture | None = None
150
+
151
+
152
+ def _is_bugteam_review(review_body: str) -> bool:
153
+ """Return True when a review body opens with a bugteam audit header prefix."""
154
+ return review_body.startswith(BUGTEAM_NEW_HEADER_PREFIX) or review_body.startswith(
155
+ BUGTEAM_LEGACY_HEADER_PREFIX
156
+ )
157
+
158
+
159
+ def _is_clean_bugteam_review(review_body: str) -> bool:
160
+ """Return True when a bugteam audit review body declares a clean pass."""
161
+ if review_body.startswith(BUGTEAM_NEW_HEADER_PREFIX):
162
+ first_line = review_body.splitlines()[0]
163
+ return BUGTEAM_NEW_CLEAN_LABEL in first_line
164
+ if review_body.startswith(BUGTEAM_LEGACY_HEADER_PREFIX):
165
+ return review_body.rstrip().endswith(BUGTEAM_LEGACY_CLEAN_TOKEN)
166
+ return False
167
+
168
+
169
+ def _bugteam_review_detail(review: JsonObject, head_sha: str) -> tuple[bool, str] | None:
170
+ """Return the pass flag and detail for a bugteam review on HEAD, or None."""
171
+ body = review.get("body", "")
172
+ if not isinstance(body, str) or not _is_bugteam_review(body):
173
+ return None
174
+ commit_id = review.get("commit_id", "")
175
+ if not isinstance(commit_id, str) or not commit_id.startswith(head_sha):
176
+ return None
177
+ review_id = review.get("id", "?")
178
+ short_commit = _short_sha(commit_id)
179
+ if _is_clean_bugteam_review(body):
180
+ return True, f"review {review_id}, clean bugteam audit, commit {short_commit}"
181
+ return False, f"review {review_id}, dirty bugteam audit, commit {short_commit}"
182
+
183
+
184
+ def _evaluate_bugteam_clean_from_reviews(
185
+ all_reviews: list[JsonObject], head_sha: str
186
+ ) -> tuple[bool, str]:
187
+ """Return whether the newest bugteam audit on HEAD is clean (pure, no network).
188
+
189
+ Args:
190
+ all_reviews: Review objects newest-first as returned by the GitHub reviews API.
191
+ head_sha: Current PR HEAD commit SHA.
192
+
193
+ Returns:
194
+ (passed, detail) for the first bugteam review on HEAD, or a missing-review failure.
195
+ """
196
+ for each_review in all_reviews:
197
+ detail = _bugteam_review_detail(each_review, head_sha)
198
+ if detail is not None:
199
+ return detail
200
+ return False, f"no bugteam review found on {_short_sha(head_sha)}"
201
+
202
+
203
+ def _check_bugteam_clean(*, owner: str, repo: str, number: int, head_sha: str) -> tuple[bool, str]:
204
+ """Return whether the newest bugteam audit review on HEAD declares a clean pass."""
205
+ endpoint = GH_REVIEWS_PATH_TEMPLATE.format(owner=owner, repo=repo, number=number)
206
+ returncode, stdout = _gh_api_paginated(f"{endpoint}?per_page={REVIEWS_PER_PAGE}")
207
+ if returncode != 0:
208
+ return False, f"gh api error: {stdout}"
209
+ all_flat = _flatten_paginated_reviews(stdout)
210
+ if all_flat is None:
211
+ return False, "unexpected gh api response shape (expected list)"
212
+ return _evaluate_bugteam_clean_from_reviews(all_flat, head_sha)
213
+
214
+
215
+ def _bypassed_note(bypass_note: str) -> str:
216
+ """Format the PASS detail line for a gate bypassed for the given reason."""
217
+ return f"bypassed ({bypass_note})"
218
+
219
+
220
+ def _bugbot_conditions(context: GateContext) -> list[GateCondition]:
221
+ """Build the Bugbot gate conditions, bypassed when Bugbot is down."""
222
+ if context.is_bugbot_down:
223
+ return [
224
+ (
225
+ "bugbot_clean_at == current_head",
226
+ (True, _bypassed_note(context.bugbot_bypass_note)),
227
+ )
228
+ ]
229
+ if context.fixture is not None:
230
+ return [
231
+ (
232
+ "bugbot_clean_at == current_head",
233
+ (False, "fixture has no bugbot check-runs; pass --bugbot-down"),
234
+ )
235
+ ]
236
+ conditions: list[GateCondition] = [
237
+ (
238
+ "bugbot_clean_at == current_head",
239
+ _check_bugbot(owner=context.owner, repo=context.repo, sha=context.head_sha),
240
+ )
241
+ ]
242
+ if conditions[-1][1][0]:
243
+ conditions.append(
244
+ (
245
+ "bugbot review body clean",
246
+ _check_bugbot_not_dirty(
247
+ owner=context.owner,
248
+ repo=context.repo,
249
+ number=context.number,
250
+ head_sha=context.head_sha,
251
+ ),
252
+ )
253
+ )
254
+ return conditions
255
+
256
+
257
+ def _bugteam_condition(context: GateContext) -> GateCondition:
258
+ """Build the bugteam CLEAN-review condition, skipped when the post was blocked."""
259
+ if context.is_bugteam_post_blocked:
260
+ return ("bugteam_clean_at == current_head", (True, "bypassed (bugteam_post_blocked)"))
261
+ if context.fixture is not None:
262
+ return (
263
+ "bugteam_clean_at == current_head",
264
+ _evaluate_bugteam_clean_from_reviews(context.fixture.reviews, context.head_sha),
265
+ )
266
+ return (
267
+ "bugteam_clean_at == current_head",
268
+ _check_bugteam_clean(
269
+ owner=context.owner,
270
+ repo=context.repo,
271
+ number=context.number,
272
+ head_sha=context.head_sha,
273
+ ),
274
+ )
275
+
276
+
277
+ def _copilot_review_condition(context: GateContext) -> GateCondition:
278
+ """Build the Copilot review gate condition, bypassed when Copilot is down."""
279
+ if context.is_copilot_down:
280
+ return (
281
+ "copilot_clean_at == current_head",
282
+ (True, _bypassed_note(context.copilot_bypass_note)),
283
+ )
284
+ if context.fixture is not None:
285
+ return (
286
+ "copilot_clean_at == current_head",
287
+ (False, "fixture has no copilot review; pass --copilot-down"),
288
+ )
289
+ return (
290
+ "copilot_clean_at == current_head",
291
+ _check_bot_review(
292
+ owner=context.owner,
293
+ repo=context.repo,
294
+ number=context.number,
295
+ head_sha=context.head_sha,
296
+ login_substring=COPILOT_LOGIN_FILTER_SUBSTRING,
297
+ clean_states=ALL_COPILOT_CLEAN_REVIEW_STATES,
298
+ dirty_states=ALL_COPILOT_DIRTY_REVIEW_STATES,
299
+ label="copilot",
300
+ ),
301
+ )
302
+
303
+
304
+ def _sha_matches_head(stamp_sha: str, head_sha: str) -> bool:
305
+ """Return True when a clean-at stamp matches the PR HEAD SHA (full or prefix).
306
+
307
+ ::
308
+
309
+ stamp 86454e3d..., head 86454e3d... -> True (full match)
310
+ stamp 86454e3, head 86454e3d... -> True (abbreviated stamp)
311
+ stamp 8, head 86454e3d... -> False (below the abbreviation floor)
312
+
313
+ A prefix shorter than an abbreviated SHA matches many commits, so a stamp
314
+ that short never passes the gate.
315
+
316
+ Args:
317
+ stamp_sha: The ``codex_clean_at`` stamp under test.
318
+ head_sha: Current PR HEAD commit SHA.
319
+
320
+ Returns:
321
+ True when the two name the same commit.
322
+ """
323
+ if len(stamp_sha) < MINIMUM_ABBREVIATED_SHA_LENGTH:
324
+ return False
325
+ if len(head_sha) < MINIMUM_ABBREVIATED_SHA_LENGTH:
326
+ return False
327
+ lowered_stamp_sha = stamp_sha.lower()
328
+ lowered_head_sha = head_sha.lower()
329
+ return lowered_head_sha.startswith(lowered_stamp_sha) or lowered_stamp_sha.startswith(
330
+ lowered_head_sha
331
+ )
332
+
333
+
334
+ def _evaluate_codex_clean(
335
+ *,
336
+ read_percent_left: Callable[[], float | None],
337
+ codex_clean_at: str | None,
338
+ head_sha: str,
339
+ ) -> tuple[bool, str]:
340
+ """Return whether the conditional Codex gate passes for the given stamp and usage.
341
+
342
+ ::
343
+
344
+ clean stamp on head -> pass (usage never read)
345
+ no stamp, percent None or <= limit -> skip (never blocks)
346
+ no stamp, percent > limit -> fail
347
+
348
+ A stamp on the current HEAD settles the gate on its own, so the usage read
349
+ stays behind it: the weekly probe spawns a Codex subprocess, and the common
350
+ already-clean tick has no reason to pay for one.
351
+
352
+ Args:
353
+ read_percent_left: Reads weekly Codex percent remaining, or None when unknown.
354
+ codex_clean_at: HEAD SHA where Codex last reported clean, or None.
355
+ head_sha: Current PR HEAD commit SHA.
356
+
357
+ Returns:
358
+ (passed, detail) for the Codex gate condition line.
359
+ """
360
+ if codex_clean_at is not None and _sha_matches_head(codex_clean_at, head_sha):
361
+ return True, CODEX_CLEAN_DETAIL_TEMPLATE % _short_sha(head_sha)
362
+ if not is_codex_review_required(read_percent_left()):
363
+ return True, CODEX_SKIPPED_USAGE_DETAIL
364
+ return False, CODEX_MISSING_CLEAN_DETAIL_TEMPLATE % _short_sha(head_sha)
365
+
366
+
367
+ def _probe_codex_percent_left() -> float | None:
368
+ """Probe weekly Codex usage and return percent remaining, or None when unknown."""
369
+ try:
370
+ usage_report = probe_weekly_usage_via_subprocess()
371
+ except (
372
+ FileNotFoundError,
373
+ OSError,
374
+ subprocess.TimeoutExpired,
375
+ subprocess.SubprocessError,
376
+ json.JSONDecodeError,
377
+ ValueError,
378
+ TypeError,
379
+ KeyError,
380
+ ):
381
+ return None
382
+ raw_percent = usage_report.get(USAGE_REPORT_KEY_PERCENT_LEFT)
383
+ if isinstance(raw_percent, bool):
384
+ return None
385
+ if isinstance(raw_percent, (int, float)):
386
+ return float(raw_percent)
387
+ return None
388
+
389
+
390
+ def _read_job_state() -> dict[str, object]:
391
+ """Read the single-PR job-dir state file, or an empty mapping when unreadable.
392
+
393
+ ::
394
+
395
+ CLAUDE_JOB_DIR unset -> {}
396
+ state file absent -> {}
397
+ corrupt or non-object -> {}
398
+ {"codex_down": true} -> {"codex_down": True}
399
+
400
+ Args:
401
+ None.
402
+
403
+ Returns:
404
+ The parsed state object, or an empty dict when no state is readable.
405
+ """
406
+ job_directory = os.environ.get(CLAUDE_JOB_DIR_ENV_VAR_NAME)
407
+ if not job_directory:
408
+ return {}
409
+ state_path = Path(job_directory) / PR_CONVERGE_STATE_FILENAME
410
+ if not state_path.is_file():
411
+ return {}
412
+ try:
413
+ payload = json.loads(state_path.read_text(encoding="utf-8"))
414
+ except (OSError, json.JSONDecodeError):
415
+ return {}
416
+ if not isinstance(payload, dict):
417
+ return {}
418
+ return payload
419
+
420
+
421
+ def _read_codex_clean_at_from_job_state(all_job_state_fields: dict[str, object]) -> str | None:
422
+ """Read ``codex_clean_at`` from an already-read job-dir state mapping."""
423
+ clean_at = all_job_state_fields.get(CODEX_CLEAN_AT_STATE_KEY)
424
+ if isinstance(clean_at, str) and clean_at:
425
+ return clean_at
426
+ return None
427
+
428
+
429
+ def _read_codex_down_from_job_state(all_job_state_fields: dict[str, object]) -> bool:
430
+ """Return True when job-dir state records sticky ``codex_down`` as JSON true.
431
+
432
+ ::
433
+
434
+ {"codex_down": true} -> True
435
+ {"codex_down": false} -> False
436
+ missing key / corrupt -> False
437
+
438
+ Only exact boolean true counts; strings, numbers, and missing values do not.
439
+
440
+ Args:
441
+ all_job_state_fields: Parsed job-dir state, empty when none is readable.
442
+
443
+ Returns:
444
+ True when the state records ``codex_down`` as JSON true.
445
+ """
446
+ return all_job_state_fields.get(CODEX_DOWN_STATE_KEY) is True
447
+
448
+
449
+ def _resolve_live_codex_clean_at(
450
+ cli_codex_clean_at: str | None, all_job_state_fields: dict[str, object]
451
+ ) -> str | None:
452
+ """Prefer the CLI stamp, then the job-dir state stamp, for the live Codex gate.
453
+
454
+ Args:
455
+ cli_codex_clean_at: Stamp passed through ``--codex-clean-at``, or None.
456
+ all_job_state_fields: Parsed job-dir state, empty when none is readable.
457
+
458
+ Returns:
459
+ The stamp the live Codex gate checks against HEAD, or None.
460
+ """
461
+ if cli_codex_clean_at:
462
+ return cli_codex_clean_at
463
+ return _read_codex_clean_at_from_job_state(all_job_state_fields)
464
+
465
+
466
+ def _codex_condition(context: GateContext) -> GateCondition:
467
+ """Build the conditional Codex gate, bypassed when Codex is down."""
468
+ if context.is_codex_down:
469
+ return (CODEX_GATE_LABEL, (True, CODEX_BYPASS_DETAIL))
470
+ if context.fixture is not None:
471
+ fixture_percent_left = context.fixture.codex_percent_left
472
+ return (
473
+ CODEX_GATE_LABEL,
474
+ _evaluate_codex_clean(
475
+ read_percent_left=lambda: fixture_percent_left,
476
+ codex_clean_at=context.fixture.codex_clean_at,
477
+ head_sha=context.head_sha,
478
+ ),
479
+ )
480
+ return (
481
+ CODEX_GATE_LABEL,
482
+ _evaluate_codex_clean(
483
+ read_percent_left=_probe_codex_percent_left,
484
+ codex_clean_at=context.live_codex_clean_at,
485
+ head_sha=context.head_sha,
486
+ ),
487
+ )
488
+
489
+
490
+ def _pending_reviews_condition(context: GateContext) -> GateCondition:
491
+ """Build the pending-requested-reviews condition, bypassed when Copilot is down."""
492
+ if context.is_copilot_down:
493
+ return (
494
+ "no pending requested reviews",
495
+ (True, _bypassed_note(context.copilot_bypass_note)),
496
+ )
497
+ if context.fixture is not None:
498
+ return (
499
+ "no pending requested reviews",
500
+ (
501
+ context.fixture.is_no_pending_reviews,
502
+ context.fixture.pending_reviews_detail,
503
+ ),
504
+ )
505
+ return (
506
+ "no pending requested reviews",
507
+ _check_no_pending_reviews(owner=context.owner, repo=context.repo, number=context.number),
508
+ )
509
+
510
+
511
+ def _mergeable_condition(context: GateContext) -> GateCondition:
512
+ """Build the mergeable condition from live gh or the frozen fixture PR object."""
513
+ if context.fixture is not None:
514
+ return (
515
+ "PR is mergeable",
516
+ _evaluate_mergeable_from_pr_object(context.fixture.pr_object),
517
+ )
518
+ return (
519
+ "PR is mergeable",
520
+ _get_mergeable(owner=context.owner, repo=context.repo, number=context.number),
521
+ )
522
+
523
+
524
+ def _threads_condition(context: GateContext) -> GateCondition:
525
+ """Build the unresolved-bot-threads condition from live gh or the fixture."""
526
+ if context.fixture is not None:
527
+ return (
528
+ "zero unresolved bot threads",
529
+ (
530
+ context.fixture.is_zero_unresolved_bot_threads,
531
+ context.fixture.unresolved_bot_threads_detail,
532
+ ),
533
+ )
534
+ return (
535
+ "zero unresolved bot threads",
536
+ _count_unresolved_bot_threads(
537
+ owner=context.owner, repo=context.repo, number=context.number
538
+ ),
539
+ )
540
+
541
+
542
+ def _state_conditions(context: GateContext) -> list[GateCondition]:
543
+ """Build the thread, mergeable, and pending-review conditions in gate order."""
544
+ return [
545
+ _threads_condition(context),
546
+ _mergeable_condition(context),
547
+ _pending_reviews_condition(context),
548
+ ]
549
+
550
+
551
+ def _build_all_conditions(context: GateContext) -> list[GateCondition]:
552
+ """Build the ordered convergence conditions the checker prints and grades."""
553
+ conditions = list(_bugbot_conditions(context))
554
+ conditions.append(_bugteam_condition(context))
555
+ conditions.append(_copilot_review_condition(context))
556
+ conditions.append(_codex_condition(context))
557
+ conditions.extend(_state_conditions(context))
558
+ return conditions
559
+
560
+
561
+ def _print_conditions(all_conditions: list[GateCondition]) -> int:
562
+ """Write one PASS/FAIL line per condition and return the aggregate exit code."""
563
+ is_all_passed = True
564
+ for each_index, (each_label, (each_passed, each_detail)) in enumerate(all_conditions, start=1):
565
+ status = "PASS" if each_passed else "FAIL"
566
+ sys.stdout.write(f"{each_index}. {each_label}: {status} — {each_detail}\n")
567
+ if not each_passed:
568
+ is_all_passed = False
569
+ sys.stdout.write("\n")
570
+ if is_all_passed:
571
+ sys.stdout.write("All pre-conditions met — PR is ready to mark ready.\n")
572
+ else:
573
+ sys.stdout.write("One or more pre-conditions not met — do not mark ready.\n")
574
+ return 0 if is_all_passed else 1
575
+
576
+
577
+ def _evaluate_convergence(context: GateContext) -> int:
578
+ """Write the HEAD line then the per-condition PASS/FAIL lines for a gate run."""
579
+ sys.stdout.write(f"HEAD: {_short_sha(context.head_sha)}\n\n")
580
+ return _print_conditions(_build_all_conditions(context))
581
+
582
+
583
+ def _load_convergence_fixture(from_path: Path) -> ConvergenceFixture:
584
+ """Load a frozen GitHub API snapshot from JSON for fixture replay.
585
+
586
+ Args:
587
+ from_path: Path to a JSON fixture file.
588
+
589
+ Returns:
590
+ ConvergenceFixture with head, reviews, mergeable PR object, and thread/pending facts.
591
+
592
+ Raises:
593
+ ValueError: When required keys are missing or mistyped.
594
+ """
595
+ payload = json.loads(from_path.read_text(encoding="utf-8"))
596
+ if not isinstance(payload, dict):
597
+ raise ValueError("fixture root must be a JSON object")
598
+ head_sha = payload.get(FIXTURE_KEY_HEAD_SHA)
599
+ pr_object = payload.get(FIXTURE_KEY_PR_OBJECT)
600
+ reviews = payload.get(FIXTURE_KEY_REVIEWS)
601
+ if not isinstance(head_sha, str) or not head_sha:
602
+ raise ValueError("fixture head_sha must be a non-empty string")
603
+ if not isinstance(pr_object, dict):
604
+ raise ValueError("fixture pr_object must be an object")
605
+ if not isinstance(reviews, list):
606
+ raise ValueError("fixture reviews must be a list")
607
+ typed_reviews = _sort_reviews_newest_first(
608
+ [each for each in reviews if isinstance(each, dict)]
609
+ )
610
+ is_threads_ok = payload.get(FIXTURE_KEY_UNRESOLVED_BOT_THREADS_PASSED)
611
+ threads_detail = payload.get(
612
+ FIXTURE_KEY_UNRESOLVED_BOT_THREADS_DETAIL, FIXTURE_DEFAULT_THREADS_DETAIL
613
+ )
614
+ is_pending_ok = payload.get(FIXTURE_KEY_PENDING_REVIEWS_PASSED)
615
+ pending_detail = payload.get(
616
+ FIXTURE_KEY_PENDING_REVIEWS_DETAIL, FIXTURE_DEFAULT_PENDING_DETAIL
617
+ )
618
+ if not isinstance(is_threads_ok, bool):
619
+ raise ValueError("fixture unresolved_bot_threads_passed must be a bool")
620
+ if not isinstance(threads_detail, str):
621
+ raise ValueError("fixture unresolved_bot_threads_detail must be a string")
622
+ if not isinstance(is_pending_ok, bool):
623
+ raise ValueError("fixture pending_reviews_passed must be a bool")
624
+ if not isinstance(pending_detail, str):
625
+ raise ValueError("fixture pending_reviews_detail must be a string")
626
+ codex_percent_left = payload.get(FIXTURE_KEY_CODEX_PERCENT_LEFT)
627
+ codex_clean_at = payload.get(FIXTURE_KEY_CODEX_CLEAN_AT)
628
+ if isinstance(codex_percent_left, bool):
629
+ raise ValueError("fixture codex_percent_left must be a number or null")
630
+ if codex_percent_left is not None and not isinstance(codex_percent_left, (int, float)):
631
+ raise ValueError("fixture codex_percent_left must be a number or null")
632
+ if codex_clean_at is not None and not isinstance(codex_clean_at, str):
633
+ raise ValueError("fixture codex_clean_at must be a string or null")
634
+ typed_percent_left: float | None = (
635
+ float(codex_percent_left) if isinstance(codex_percent_left, (int, float)) else None
636
+ )
637
+ typed_codex_clean_at: str | None = codex_clean_at if isinstance(codex_clean_at, str) else None
638
+ return ConvergenceFixture(
639
+ head_sha=head_sha,
640
+ pr_object=pr_object,
641
+ reviews=typed_reviews,
642
+ is_zero_unresolved_bot_threads=is_threads_ok,
643
+ unresolved_bot_threads_detail=threads_detail,
644
+ is_no_pending_reviews=is_pending_ok,
645
+ pending_reviews_detail=pending_detail,
646
+ codex_percent_left=typed_percent_left,
647
+ codex_clean_at=typed_codex_clean_at,
648
+ )
649
+
650
+
651
+ def check_all(
652
+ owner: str,
653
+ repo: str,
654
+ number: int,
655
+ is_bugbot_down: bool,
656
+ is_copilot_down: bool,
657
+ is_bugteam_post_blocked: bool,
658
+ is_codex_down: bool,
659
+ live_codex_clean_at: str | None,
660
+ bugbot_bypass_note: str = BUGBOT_DOWN_BYPASS_NOTE,
661
+ copilot_bypass_note: str = COPILOT_DOWN_BYPASS_NOTE,
662
+ ) -> int:
663
+ """Run every convergence gate and print one PASS/FAIL line per condition.
664
+
665
+ Args:
666
+ owner: GitHub repository owner login.
667
+ repo: GitHub repository name.
668
+ number: Pull request number to inspect.
669
+ is_bugbot_down: True bypasses the bugbot check-run and review-body gates.
670
+ is_copilot_down: True bypasses the Copilot review and pending gates.
671
+ is_bugteam_post_blocked: True skips the bugteam CLEAN-review gate.
672
+ is_codex_down: True bypasses the conditional Codex review gate.
673
+ live_codex_clean_at: Optional SHA stamp for the live Codex clean-at check.
674
+ bugbot_bypass_note: Detail note printed when the bugbot gate is bypassed.
675
+ copilot_bypass_note: Detail note printed when the Copilot gates are bypassed.
676
+
677
+ Returns:
678
+ 0 when every gate passes, 1 when at least one gate fails.
679
+ """
680
+ head_sha = _get_pr_head_sha(owner=owner, repo=repo, number=number)
681
+ context = GateContext(
682
+ owner=owner,
683
+ repo=repo,
684
+ number=number,
685
+ head_sha=head_sha,
686
+ is_bugbot_down=is_bugbot_down,
687
+ is_copilot_down=is_copilot_down,
688
+ is_bugteam_post_blocked=is_bugteam_post_blocked,
689
+ is_codex_down=is_codex_down,
690
+ bugbot_bypass_note=bugbot_bypass_note,
691
+ copilot_bypass_note=copilot_bypass_note,
692
+ live_codex_clean_at=live_codex_clean_at,
693
+ fixture=None,
694
+ )
695
+ return _evaluate_convergence(context)
696
+
697
+
698
+ def _check_all_from_fixture(
699
+ owner: str,
700
+ repo: str,
701
+ number: int,
702
+ fixture: ConvergenceFixture,
703
+ is_bugbot_down: bool,
704
+ is_copilot_down: bool,
705
+ is_bugteam_post_blocked: bool,
706
+ is_codex_down: bool,
707
+ bugbot_bypass_note: str = BUGBOT_DOWN_BYPASS_NOTE,
708
+ copilot_bypass_note: str = COPILOT_DOWN_BYPASS_NOTE,
709
+ ) -> int:
710
+ """Run every convergence gate against a frozen API snapshot.
711
+
712
+ Args:
713
+ owner: GitHub repository owner login.
714
+ repo: GitHub repository name.
715
+ number: Pull request number recorded in the snapshot.
716
+ fixture: Frozen GitHub API snapshot (no live `gh` calls).
717
+ is_bugbot_down: True bypasses the bugbot check-run and review-body gates.
718
+ is_copilot_down: True bypasses the Copilot review and pending gates.
719
+ is_bugteam_post_blocked: True skips the bugteam CLEAN-review gate.
720
+ is_codex_down: True bypasses the conditional Codex review gate.
721
+ bugbot_bypass_note: Detail note printed when the bugbot gate is bypassed.
722
+ copilot_bypass_note: Detail note printed when the Copilot gates are bypassed.
723
+
724
+ Returns:
725
+ 0 when every gate passes, 1 when at least one gate fails.
726
+ """
727
+ context = GateContext(
728
+ owner=owner,
729
+ repo=repo,
730
+ number=number,
731
+ head_sha=fixture.head_sha,
732
+ is_bugbot_down=is_bugbot_down,
733
+ is_copilot_down=is_copilot_down,
734
+ is_bugteam_post_blocked=is_bugteam_post_blocked,
735
+ is_codex_down=is_codex_down,
736
+ bugbot_bypass_note=bugbot_bypass_note,
737
+ copilot_bypass_note=copilot_bypass_note,
738
+ live_codex_clean_at=None,
739
+ fixture=fixture,
740
+ )
741
+ return _evaluate_convergence(context)
742
+
743
+
744
+ def _build_argument_parser() -> argparse.ArgumentParser:
745
+ """Build the command-line argument parser for the convergence checker."""
746
+ parser = argparse.ArgumentParser(description=__doc__)
747
+ parser.add_argument("--owner", required=True, help="GitHub repository owner")
748
+ parser.add_argument("--repo", required=True, help="GitHub repository name")
749
+ parser.add_argument("--pr-number", required=True, type=int, help="Pull request number")
750
+ parser.add_argument(
751
+ "--fixture",
752
+ default=None,
753
+ help="Path to a frozen GitHub API JSON snapshot for offline replay.",
754
+ )
755
+ parser.add_argument(
756
+ "--bugbot-down",
757
+ action="store_true",
758
+ help="Bypass the bugbot check-run gate when Cursor Bugbot is unreachable on HEAD.",
759
+ )
760
+ parser.add_argument(
761
+ "--copilot-down",
762
+ action="store_true",
763
+ help="Bypass the Copilot review and pending-review gates when Copilot is down on HEAD.",
764
+ )
765
+ parser.add_argument(
766
+ "--bugteam-post-blocked",
767
+ action="store_true",
768
+ help="Skip the bugteam CLEAN-review gate when the environment refused the CLEAN post on HEAD.",
769
+ )
770
+ parser.add_argument(
771
+ "--codex-down",
772
+ action="store_true",
773
+ help="Bypass the conditional Codex review gate when Codex is down or opted out.",
774
+ )
775
+ parser.add_argument(
776
+ "--codex-clean-at",
777
+ default=None,
778
+ help="HEAD SHA where Codex last reported clean (live path; optional when job-dir state holds it).",
779
+ )
780
+ return parser
781
+
782
+
783
+ def parse_arguments(all_argv: list[str]) -> argparse.Namespace:
784
+ """Parse command-line arguments for the convergence checker.
785
+
786
+ Args:
787
+ all_argv: Argument list excluding the program name.
788
+
789
+ Returns:
790
+ Namespace exposing owner, repo, pr_number, fixture, bugbot_down, copilot_down,
791
+ bugteam_post_blocked, codex_down, and codex_clean_at attributes.
792
+ """
793
+ return _build_argument_parser().parse_args(all_argv)
794
+
795
+
796
+ def _resolve_bugbot_down(is_bugbot_down_flag: bool) -> bool:
797
+ """Pinned contract/unit-test helper for the bugbot flag and env bypass.
798
+
799
+ Production ``main()`` resolves Bugbot waivers through ``_resolve_bugbot_waiver``.
800
+ This function serves only pinned contract and unit tests; issue #120 tracks
801
+ its removal.
802
+ """
803
+ return is_bugbot_down_flag or is_bugbot_disabled_via_env()
804
+
805
+
806
+ def _resolve_copilot_down(is_copilot_down_flag: bool) -> bool:
807
+ """Pinned contract/unit-test helper for the copilot flag and env bypass.
808
+
809
+ Production ``main()`` resolves Copilot waivers through ``_resolve_copilot_waiver``.
810
+ This function serves only pinned contract and unit tests; issue #120 tracks
811
+ its removal.
812
+ """
813
+ return is_copilot_down_flag or is_copilot_disabled_via_env()
814
+
815
+
816
+ def _resolve_bugteam_post_blocked(is_bugteam_post_blocked_flag: bool) -> bool:
817
+ """Combine the --bugteam-post-blocked flag with the resolved bugteam opt-in.
818
+
819
+ ::
820
+
821
+ flag True -> True (caller passed the flag)
822
+ flag False, reviews-enabled lists bugteam -> False (gate runs)
823
+ flag False, neither list names bugteam -> True (off by default)
824
+ flag False, reviews-disabled lists bugteam -> True (opt-out wins)
825
+
826
+ Bugteam is off by default and runs only on an explicit opt-in. Settings on
827
+ disk are the source when readable, so a settings.json opt-in reaches the
828
+ mark-ready blocker hook, which re-runs this script with no flags. The
829
+ process env is the fallback when that disk read fails.
830
+ """
831
+ return is_bugteam_post_blocked_flag or _is_bugteam_disabled_via_resolved_settings()
832
+
833
+
834
+ def _resolve_codex_down(
835
+ is_codex_down_flag: bool, all_job_state_fields: dict[str, object]
836
+ ) -> bool:
837
+ """Combine --codex-down, env opt-out, and sticky job-state ``codex_down``.
838
+
839
+ ::
840
+
841
+ flag True, env unset, job unset -> True (caller passed the flag)
842
+ flag False, reviews-disabled lists codex -> True (exported token)
843
+ flag False, env unset, job codex_down true -> True (sticky job-state)
844
+ flag False, env unset, job unset or false -> False (gate may run)
845
+
846
+ The mark-ready blocker hook re-runs this script with no flags, so the env
847
+ token and sticky job-state ``codex_down`` both carry the bypass into that
848
+ re-check when the original run cannot re-pass ``--codex-down``.
849
+
850
+ Args:
851
+ is_codex_down_flag: True when the caller passed ``--codex-down``.
852
+ all_job_state_fields: Parsed job-dir state, empty when none is readable.
853
+
854
+ Returns:
855
+ True when any of the three bypass sources is in force.
856
+ """
857
+ return (
858
+ is_codex_down_flag
859
+ or _is_codex_disabled_via_resolved_settings()
860
+ or _read_codex_down_from_job_state(all_job_state_fields)
861
+ )
862
+
863
+
864
+ def main(
865
+ all_arguments: list[str],
866
+ bugbot_bypass_note: str = BUGBOT_DOWN_BYPASS_NOTE,
867
+ copilot_bypass_note: str = COPILOT_DOWN_BYPASS_NOTE,
868
+ ) -> int:
869
+ """Run the script end-to-end against parsed CLI arguments.
870
+
871
+ Args:
872
+ all_arguments: Argument list excluding the program name.
873
+ bugbot_bypass_note: Note printed when the bugbot gate is bypassed.
874
+ copilot_bypass_note: Note printed when the Copilot gates are bypassed.
875
+
876
+ Returns:
877
+ 0 on full convergence, 1 on one or more gate failures.
878
+ """
879
+ arguments = parse_arguments(all_arguments)
880
+ all_job_state_fields = _read_job_state()
881
+ is_bugteam_post_blocked = _resolve_bugteam_post_blocked(arguments.bugteam_post_blocked)
882
+ is_codex_down = _resolve_codex_down(arguments.codex_down, all_job_state_fields)
883
+ fixture_path = getattr(arguments, "fixture", None)
884
+ if fixture_path:
885
+ bugbot_waiver = _waiver_from_cli_flag(
886
+ arguments.bugbot_down, bugbot_bypass_note
887
+ )
888
+ copilot_waiver = _waiver_from_cli_flag(
889
+ arguments.copilot_down, copilot_bypass_note
890
+ )
891
+ fixture = _load_convergence_fixture(Path(fixture_path))
892
+ return _check_all_from_fixture(
893
+ owner=arguments.owner,
894
+ repo=arguments.repo,
895
+ number=getattr(arguments, "pr_number"),
896
+ fixture=fixture,
897
+ is_bugbot_down=bugbot_waiver.is_waived,
898
+ is_copilot_down=copilot_waiver.is_waived,
899
+ is_bugteam_post_blocked=is_bugteam_post_blocked,
900
+ is_codex_down=is_codex_down,
901
+ bugbot_bypass_note=bugbot_waiver.bypass_note or bugbot_bypass_note,
902
+ copilot_bypass_note=copilot_waiver.bypass_note or copilot_bypass_note,
903
+ )
904
+ bugbot_waiver = _resolve_bugbot_waiver(arguments.bugbot_down)
905
+ copilot_waiver = _resolve_copilot_waiver(arguments.copilot_down)
906
+ return check_all(
907
+ owner=arguments.owner,
908
+ repo=arguments.repo,
909
+ number=getattr(arguments, "pr_number"),
910
+ is_bugbot_down=bugbot_waiver.is_waived,
911
+ is_copilot_down=copilot_waiver.is_waived,
912
+ is_bugteam_post_blocked=is_bugteam_post_blocked,
913
+ is_codex_down=is_codex_down,
914
+ live_codex_clean_at=_resolve_live_codex_clean_at(
915
+ arguments.codex_clean_at, all_job_state_fields
916
+ ),
917
+ bugbot_bypass_note=bugbot_waiver.bypass_note or bugbot_bypass_note,
918
+ copilot_bypass_note=copilot_waiver.bypass_note or copilot_bypass_note,
919
+ )
920
+
921
+
922
+ if __name__ == "__main__":
923
+ raise SystemExit(main(sys.argv[1:]))