claude-dev-env 4.2.0 → 7.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 (350) hide show
  1. package/.agents/agents/pr-description-writer.md +2 -2
  2. package/.agents/agents/test_agent_frontmatter.py +2 -2
  3. package/.agents/agents/test_pr_description_writer_style.py +1 -3
  4. package/.agents/skills/e-code-review/SKILL.md +12 -12
  5. package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
  6. package/.agents/skills/e-code-review/reference/fix.md +15 -17
  7. package/.agents/skills/e-code-review/reference/loop.md +35 -39
  8. package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
  9. package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
  10. package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
  11. package/.agents/skills/issue-tracker/SKILL.md +7 -0
  12. package/.agents/skills/pull-request/SKILL.md +216 -0
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +18 -0
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/__init__.py +1 -0
  15. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/__init__.py +1 -0
  16. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +69 -0
  17. package/.agents/skills/pull-request/scripts/pull_request.py +265 -0
  18. package/.agents/skills/pull-request/scripts/recover_legacy_author.py +251 -0
  19. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +58 -0
  20. package/.agents/skills/pull-request/scripts/test_pull_request.py +396 -0
  21. package/.agents/skills/pull-request/scripts/test_recover_legacy_author.py +221 -0
  22. package/.agents/skills/skill-builder/SKILL.md +3 -196
  23. package/.agents/skills/test-runner/SKILL.md +69 -0
  24. package/.agents/skills/test-runner/scripts/preflight_checks.py +365 -0
  25. package/.agents/skills/test-runner/scripts/run_tests.py +316 -0
  26. package/.agents/skills/test-runner/scripts/test_preflight_checks.py +185 -0
  27. package/.agents/skills/test-runner/scripts/test_run_tests.py +281 -0
  28. package/.agents/skills/test-runner/scripts/test_runner_constants/__init__.py +1 -0
  29. package/.agents/skills/test-runner/scripts/test_runner_constants/config/__init__.py +1 -0
  30. package/.agents/skills/test-runner/scripts/test_runner_constants/config/constants.py +96 -0
  31. package/.agents/skills-archived/pr-loop-cloud-transport/reference/identity-and-hooks.md +1 -1
  32. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  33. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  34. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  35. package/bin/ever-shipped-skills.mjs +2 -0
  36. package/bin/ever-shipped-skills.test.mjs +8 -0
  37. package/bin/install.agents-home.test.mjs +15 -13
  38. package/bin/install.mjs +37 -1
  39. package/bin/install.policy-lint.test.mjs +78 -0
  40. package/bin/install.prune.test.mjs +149 -20
  41. package/bin/install.pstack-plugin.test.mjs +114 -0
  42. package/bin/install.test.mjs +61 -7
  43. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  44. package/hooks/advisory/pr_done_reminder.py +342 -0
  45. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  46. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  47. package/hooks/blocking/code_rules_paired_test.py +22 -15
  48. package/hooks/blocking/pii_prevention_blocker.py +32 -51
  49. package/hooks/blocking/test_bash_post_call_dispatcher.py +55 -17
  50. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +1 -4
  51. package/hooks/blocking/test_code_rules_command_dispatch.py +2 -2
  52. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  53. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  54. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  55. package/hooks/blocking/test_pii_prevention_blocker.py +37 -89
  56. package/hooks/blocking/test_pre_tool_use_dispatcher.py +68 -133
  57. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  58. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +18 -18
  59. package/hooks/gh_post_body_texts.py +195 -0
  60. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  61. package/hooks/git-hooks/pre_commit.py +52 -11
  62. package/hooks/git-hooks/test_installed_policy_lint.py +159 -0
  63. package/hooks/git-hooks/test_native_policy_commit.py +87 -0
  64. package/hooks/git-hooks/test_pre_commit.py +246 -0
  65. package/hooks/hooks.json +1 -16
  66. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -2
  67. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -8
  68. package/hooks/hooks_constants/gh_post_body_texts_constants.py +12 -0
  69. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  70. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -43
  71. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +17 -7
  72. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +0 -7
  73. package/hooks/hooks_constants/test_failure_recorder_constants.py +3 -3
  74. package/hooks/hooks_constants/test_gh_post_body_texts_constants.py +19 -0
  75. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  76. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +20 -32
  77. package/hooks/observability/test_failure_recorder.py +1 -1
  78. package/hooks/pyproject.toml +1 -1
  79. package/hooks/test_gh_post_body_texts.py +79 -0
  80. package/package.json +2 -2
  81. package/rules/durable-post-artifacts.md +32 -20
  82. package/rules/gh-cli-conventions.md +23 -13
  83. package/rules/git-workflow.md +21 -8
  84. package/scripts/check.ps1 +0 -1
  85. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  86. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  87. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  88. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  89. package/scripts/durable_post_lint.py +206 -0
  90. package/scripts/durable_post_lint_config/config/constants.py +96 -0
  91. package/scripts/durable_post_lint_config/config/test_constants.py +25 -0
  92. package/scripts/policy_lint/adapter_configuration.py +53 -12
  93. package/scripts/policy_lint/adapter_detectors.py +13 -6
  94. package/scripts/policy_lint/adapters.py +7 -4
  95. package/scripts/policy_lint/config/constants.py +5 -0
  96. package/scripts/policy_lint/engine.py +48 -4
  97. package/scripts/policy_lint/model.py +1 -0
  98. package/scripts/policy_lint/registry.py +1 -1
  99. package/scripts/policy_lint/selection.py +2 -1
  100. package/scripts/policy_lint/selection_git.py +2 -0
  101. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  102. package/scripts/repository_checks/config/constants.py +0 -5
  103. package/scripts/shared_tree_paths.py +32 -3
  104. package/scripts/test_durable_post_lint.py +332 -0
  105. package/scripts/test_shared_tree_paths.py +33 -0
  106. package/scripts/tests/test_adapter_configuration.py +54 -0
  107. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  108. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  109. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  110. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  111. package/scripts/tests/test_policy_lint_selection.py +25 -2
  112. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  113. package/scripts/tests/test_rules.py +3 -7
  114. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  115. package/.agents/skills/_shared/pr-loop/AGENTS.md +0 -1
  116. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  117. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  118. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  119. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  120. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  121. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  122. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  123. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  124. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  125. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  126. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  127. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  128. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  129. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  130. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  131. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  132. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  133. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  134. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  135. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  136. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  137. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  138. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  139. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  140. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  141. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  142. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  143. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  144. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  145. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  146. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  147. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  148. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  149. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  150. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  151. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  152. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  153. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  154. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  155. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  156. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  157. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  158. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  159. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  160. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  161. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  162. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  163. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  164. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  165. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  166. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  167. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  168. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  169. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  170. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  171. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  172. package/.agents/skills/autoconverge/SKILL.md +0 -431
  173. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  174. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  175. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  176. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  177. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  178. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  179. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  180. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  181. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  182. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  183. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  184. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  185. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  186. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  187. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  188. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  189. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  190. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  191. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  192. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  193. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  194. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  195. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  196. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  197. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  198. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  199. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  200. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  201. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  202. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  203. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  204. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  205. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  206. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  207. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  208. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  209. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  210. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  211. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  212. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  213. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  214. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  215. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  216. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  217. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  218. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  219. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  220. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  221. package/.agents/skills/hitl/SKILL.md +0 -45
  222. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  223. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  224. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  225. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  226. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  227. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  228. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  229. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  230. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  231. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  232. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  233. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  234. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  235. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  236. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  237. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  238. package/.agents/skills/prototype/SKILL.md +0 -85
  239. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  240. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  241. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  242. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  243. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  244. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  245. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  246. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  247. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  248. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  249. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  250. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  251. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  252. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  253. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  254. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  255. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  256. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  257. package/.agents/skills/rebase/AGENTS.md +0 -1
  258. package/.agents/skills/rebase/SKILL.md +0 -178
  259. package/.agents/skills/review-router/SKILL.md +0 -51
  260. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  261. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  262. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  263. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  264. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  265. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  266. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  267. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  268. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  269. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  270. package/.agents/skills/review-tier/SKILL.md +0 -56
  271. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  272. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  273. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  274. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  275. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  276. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  277. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  278. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  279. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  280. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  281. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  282. package/.agents/skills/session-log/AGENTS.md +0 -1
  283. package/.agents/skills/session-log/SKILL.md +0 -170
  284. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  285. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  286. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  287. package/.agents/skills/session-tidy/SKILL.md +0 -191
  288. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  289. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  290. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  291. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  292. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  293. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  294. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  295. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  296. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  297. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  298. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  299. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  300. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  301. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  302. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  303. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  304. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  305. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  306. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  307. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  308. package/.agents/skills/update/AGENTS.md +0 -1
  309. package/.agents/skills/update/SKILL.md +0 -190
  310. package/_shared/pr-loop/AGENTS.md +0 -1
  311. package/_shared/pr-loop/CLAUDE.md +0 -1
  312. package/_shared/pr-loop/audit-contract.md +0 -183
  313. package/_shared/pr-loop/audit-reply-template.md +0 -151
  314. package/_shared/pr-loop/code-rules-gate.md +0 -65
  315. package/_shared/pr-loop/fix-protocol.md +0 -60
  316. package/_shared/pr-loop/gh-payloads.md +0 -75
  317. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  318. package/_shared/pr-loop/precatch-rubric.md +0 -66
  319. package/_shared/pr-loop/preflight-proposal.md +0 -56
  320. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  321. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  322. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  323. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  324. package/_shared/pr-loop/state-schema.md +0 -45
  325. package/_shared/pr-loop/worker-spawn.md +0 -189
  326. package/hooks/_gh_pr_author_swap_utils.py +0 -1225
  327. package/hooks/blocking/conventional_pr_title_gate.py +0 -443
  328. package/hooks/blocking/gh_body_arg_blocker.py +0 -176
  329. package/hooks/blocking/gh_pr_author_enforcer.py +0 -487
  330. package/hooks/blocking/gh_pr_author_restore.py +0 -105
  331. package/hooks/blocking/pr_description_writer_gate.py +0 -256
  332. package/hooks/blocking/test_conventional_pr_title_gate.py +0 -640
  333. package/hooks/blocking/test_gh_body_arg_blocker.py +0 -441
  334. package/hooks/blocking/test_gh_pr_author_enforcer.py +0 -1166
  335. package/hooks/blocking/test_gh_pr_author_restore.py +0 -513
  336. package/hooks/blocking/test_gh_pr_author_swap_utils.py +0 -910
  337. package/hooks/blocking/test_pr_description_writer_gate.py +0 -234
  338. package/hooks/blocking/test_preflight_check.py +0 -343
  339. package/hooks/blocking/test_test_preflight_check.py +0 -80
  340. package/hooks/blocking/test_volatile_path_in_post_blocker.py +0 -322
  341. package/hooks/blocking/volatile_path_in_post_blocker.py +0 -461
  342. package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +0 -58
  343. package/hooks/hooks_constants/gh_pr_author_swap_constants.py +0 -76
  344. package/hooks/hooks_constants/pr_description_writer_gate_constants.py +0 -73
  345. package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +0 -55
  346. package/hooks/observability/pr_description_writer_spawn_tracker.py +0 -113
  347. package/hooks/observability/test_pr_description_writer_spawn_tracker.py +0 -150
  348. package/hooks/session/gh_pr_author_session_cleanup.py +0 -175
  349. package/hooks/session/test_gh_pr_author_session_cleanup.py +0 -576
  350. package/hooks/test__gh_pr_author_swap_utils.py +0 -333
@@ -0,0 +1,185 @@
1
+ """Focused tests for explicit test-runner preflight helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ import pytest
11
+
12
+ SCRIPTS_DIRECTORY = Path(__file__).resolve().parent
13
+ if str(SCRIPTS_DIRECTORY) not in sys.path:
14
+ sys.path.insert(0, str(SCRIPTS_DIRECTORY))
15
+
16
+ preflight_checks = importlib.import_module("preflight_checks")
17
+
18
+
19
+ def test_check_server_health_reports_unhealthy_response(
20
+ monkeypatch: pytest.MonkeyPatch,
21
+ ) -> None:
22
+ completed_process = subprocess.CompletedProcess(
23
+ args=["curl"], returncode=0, stdout="503", stderr=""
24
+ )
25
+ monkeypatch.setattr(
26
+ preflight_checks.subprocess,
27
+ "run",
28
+ lambda *_arguments, **_keywords: completed_process,
29
+ )
30
+
31
+ assert preflight_checks.check_server_health("http://localhost:3000") == (
32
+ "BLOCKED: Server at http://localhost:3000 is not healthy (HTTP 503). "
33
+ "Fix the server before running tests."
34
+ )
35
+
36
+
37
+ def test_check_django_database_requires_db_sqlite3(tmp_path: Path) -> None:
38
+ (tmp_path / "manage.py").write_text("", encoding="utf-8")
39
+
40
+ assert preflight_checks.check_django_database(tmp_path) == (
41
+ f"BLOCKED: No database file (db.sqlite3) found in {tmp_path}. "
42
+ "Run migrations before running tests."
43
+ )
44
+
45
+
46
+ def test_extract_port_from_url_uses_explicit_or_default_port() -> None:
47
+ assert preflight_checks.extract_port_from_url("http://localhost:9000") == "9000"
48
+ assert preflight_checks.extract_port_from_url("http://localhost") == "8000"
49
+
50
+
51
+ def test_check_test_db_flag_requires_detected_server_flag(
52
+ tmp_path: Path,
53
+ monkeypatch: pytest.MonkeyPatch,
54
+ ) -> None:
55
+ (tmp_path / "manage.py").write_text("", encoding="utf-8")
56
+ monkeypatch.setattr(
57
+ preflight_checks,
58
+ "_read_process_listing",
59
+ lambda: "python manage.py runserver",
60
+ )
61
+
62
+ assert preflight_checks.check_test_db_flag("http://localhost:8000", tmp_path) == (
63
+ "BLOCKED: Django server on port 8000 is not running with --test-db. "
64
+ "Restart with: python manage.py runserver --test-db 0.0.0.0:8000"
65
+ )
66
+
67
+
68
+ def test_check_test_db_flag_ignores_test_database_flag_on_another_port(
69
+ tmp_path: Path,
70
+ monkeypatch: pytest.MonkeyPatch,
71
+ ) -> None:
72
+ (tmp_path / "manage.py").write_text("", encoding="utf-8")
73
+ monkeypatch.setattr(
74
+ preflight_checks,
75
+ "_read_process_listing",
76
+ lambda: (
77
+ "python manage.py runserver --test-db 9000\n8000 python manage.py runserver"
78
+ ),
79
+ )
80
+
81
+ assert preflight_checks.check_test_db_flag("http://localhost:8000", tmp_path) == (
82
+ "BLOCKED: Django server on port 8000 is not running with --test-db. "
83
+ "Restart with: python manage.py runserver --test-db 0.0.0.0:8000"
84
+ )
85
+
86
+
87
+ def test_check_test_db_flag_skips_value_taking_runserver_options(
88
+ tmp_path: Path,
89
+ monkeypatch: pytest.MonkeyPatch,
90
+ ) -> None:
91
+ (tmp_path / "manage.py").write_text("", encoding="utf-8")
92
+ monkeypatch.setattr(
93
+ preflight_checks,
94
+ "_read_process_listing",
95
+ lambda: "python manage.py runserver --settings project.settings 8000",
96
+ )
97
+
98
+ assert preflight_checks.check_test_db_flag("http://localhost:8000", tmp_path) == (
99
+ "BLOCKED: Django server on port 8000 is not running with --test-db. "
100
+ "Restart with: python manage.py runserver --test-db 0.0.0.0:8000"
101
+ )
102
+
103
+
104
+ def test_check_test_db_flag_accepts_value_taking_options_with_test_db(
105
+ tmp_path: Path,
106
+ monkeypatch: pytest.MonkeyPatch,
107
+ ) -> None:
108
+ (tmp_path / "manage.py").write_text("", encoding="utf-8")
109
+ monkeypatch.setattr(
110
+ preflight_checks,
111
+ "_read_process_listing",
112
+ lambda: "python manage.py runserver --settings project.settings --test-db 8000",
113
+ )
114
+
115
+ assert (
116
+ preflight_checks.check_test_db_flag("http://localhost:8000", tmp_path) is None
117
+ )
118
+
119
+
120
+ def test_check_runserver_port_conflicts_reports_other_worktree(
121
+ tmp_path: Path,
122
+ monkeypatch: pytest.MonkeyPatch,
123
+ ) -> None:
124
+ project_root = tmp_path / "project"
125
+ other_project_root = tmp_path / "other-project"
126
+ project_root.mkdir()
127
+ other_project_root.mkdir()
128
+ monkeypatch.setattr(
129
+ preflight_checks,
130
+ "_get_runserver_processes_on_port",
131
+ lambda _port: [(1, str(project_root)), (2, str(other_project_root))],
132
+ )
133
+
134
+ assert (
135
+ preflight_checks.check_runserver_port_conflicts(
136
+ "http://localhost:8000", project_root
137
+ )
138
+ == f"BLOCKED: Multiple Django runserver processes are bound to port 8000 across worktrees: {other_project_root.resolve()}. Stop stale servers first."
139
+ )
140
+
141
+
142
+ def test_build_frontend_runs_build_then_collectstatic(
143
+ tmp_path: Path,
144
+ monkeypatch: pytest.MonkeyPatch,
145
+ ) -> None:
146
+ (tmp_path / "manage.py").write_text("", encoding="utf-8")
147
+ (tmp_path / "frontend").mkdir()
148
+ all_calls: list[tuple[list[str], Path]] = []
149
+
150
+ def run_process(
151
+ all_arguments: list[str], **keyword_arguments: object
152
+ ) -> subprocess.CompletedProcess[str]:
153
+ all_calls.append((all_arguments, keyword_arguments["cwd"]))
154
+ return subprocess.CompletedProcess(
155
+ args=all_arguments, returncode=0, stdout="", stderr=""
156
+ )
157
+
158
+ monkeypatch.setattr(preflight_checks.subprocess, "run", run_process)
159
+
160
+ assert preflight_checks.build_frontend(tmp_path) is None
161
+ assert all_calls == [
162
+ (["npm", "run", "build"], tmp_path / "frontend"),
163
+ (["python", "manage.py", "collectstatic", "--noinput"], tmp_path),
164
+ ]
165
+
166
+
167
+ def test_build_frontend_skips_collectstatic_without_manage_py(
168
+ tmp_path: Path,
169
+ monkeypatch: pytest.MonkeyPatch,
170
+ ) -> None:
171
+ (tmp_path / "frontend").mkdir()
172
+ all_calls: list[list[str]] = []
173
+
174
+ def run_process(
175
+ all_arguments: list[str], **_keyword_arguments: object
176
+ ) -> subprocess.CompletedProcess[str]:
177
+ all_calls.append(all_arguments)
178
+ return subprocess.CompletedProcess(
179
+ args=all_arguments, returncode=0, stdout="", stderr=""
180
+ )
181
+
182
+ monkeypatch.setattr(preflight_checks.subprocess, "run", run_process)
183
+
184
+ assert preflight_checks.build_frontend(tmp_path) is None
185
+ assert all_calls == [["npm", "run", "build"]]
@@ -0,0 +1,281 @@
1
+ """Focused behavior tests for the explicit test runner."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import subprocess
7
+ import sys
8
+ from collections.abc import Callable
9
+ from pathlib import Path
10
+
11
+ import pytest
12
+
13
+ SCRIPTS_DIRECTORY = Path(__file__).resolve().parent
14
+ if str(SCRIPTS_DIRECTORY) not in sys.path:
15
+ sys.path.insert(0, str(SCRIPTS_DIRECTORY))
16
+
17
+ run_tests = importlib.import_module("run_tests")
18
+
19
+ FRONTEND_COMMANDS = [
20
+ ["ps", "aux"],
21
+ ["npm", "run", "build"],
22
+ ["python", "manage.py", "collectstatic", "--noinput"],
23
+ [
24
+ "curl",
25
+ "-s",
26
+ "-o",
27
+ "/dev/null",
28
+ "-w",
29
+ "%{http_code}",
30
+ "--max-time",
31
+ "2",
32
+ "http://localhost:3000",
33
+ ],
34
+ ["playwright", "test"],
35
+ ]
36
+
37
+
38
+ def _create_django_project(project_root: Path, *, has_database: bool) -> None:
39
+ project_root.mkdir()
40
+ (project_root / "manage.py").write_text(
41
+ "import sys\n\nif __name__ == '__main__':\n sys.exit(0)\n",
42
+ encoding="utf-8",
43
+ )
44
+ if has_database:
45
+ (project_root / "db.sqlite3").write_bytes(b"database")
46
+
47
+
48
+ def _completed_process(
49
+ all_arguments: list[str],
50
+ return_code: int = 0,
51
+ stdout_text: str = "",
52
+ ) -> subprocess.CompletedProcess[str]:
53
+ return subprocess.CompletedProcess(
54
+ args=all_arguments,
55
+ returncode=return_code,
56
+ stdout=stdout_text,
57
+ stderr="",
58
+ )
59
+
60
+
61
+ def _build_recording_process(
62
+ all_calls: list[tuple[list[str], dict[str, object]]],
63
+ ) -> Callable[..., subprocess.CompletedProcess[str]]:
64
+ def run_process(
65
+ all_arguments: list[str],
66
+ **keyword_arguments: object,
67
+ ) -> subprocess.CompletedProcess[str]:
68
+ all_calls.append((all_arguments, keyword_arguments))
69
+ stdout_text = "200" if all_arguments[0] == "curl" else ""
70
+ return _completed_process(all_arguments, stdout_text=stdout_text)
71
+
72
+ return run_process
73
+
74
+
75
+ def test_select_runner_recognizes_supported_forms() -> None:
76
+ assert run_tests.select_runner(["pytest", "tests"]).name == "pytest"
77
+ assert run_tests.select_runner(["python", "-m", "pytest"]).name == "pytest"
78
+ assert run_tests.select_runner(["npx", "playwright", "test"]).is_playwright
79
+
80
+
81
+ def test_extract_target_url_reads_explicit_base_url() -> None:
82
+ assert (
83
+ run_tests.extract_target_url(
84
+ ["playwright", "test", "--base-url", "http://127.0.0.1:9000"], True
85
+ )
86
+ == "http://127.0.0.1:9000"
87
+ )
88
+
89
+
90
+ def test_run_preflight_skips_plain_pytest_without_django(
91
+ tmp_path: Path,
92
+ ) -> None:
93
+ selected_runner = run_tests.select_runner(["python", "-m", "pytest"])
94
+ assert (
95
+ run_tests.run_preflight(selected_runner, tmp_path, ["python", "-m", "pytest"])
96
+ is None
97
+ )
98
+
99
+
100
+ def test_run_child_process_returns_child_status(
101
+ tmp_path: Path,
102
+ monkeypatch: pytest.MonkeyPatch,
103
+ ) -> None:
104
+ child_process = _completed_process(["pytest"], return_code=6)
105
+ monkeypatch.setattr(
106
+ run_tests.subprocess, "run", lambda *_args, **_kwargs: child_process
107
+ )
108
+ assert run_tests.run_child_process(["pytest"], tmp_path) == 6
109
+
110
+
111
+ def test_main_runs_plain_pytest_without_django_preflight(
112
+ tmp_path: Path,
113
+ monkeypatch: pytest.MonkeyPatch,
114
+ ) -> None:
115
+ child_process = _completed_process(["python", "-m", "pytest", "tests"], 7)
116
+ all_calls: list[dict[str, object]] = []
117
+
118
+ def run_child(
119
+ all_arguments: list[str],
120
+ **keyword_arguments: object,
121
+ ) -> subprocess.CompletedProcess[str]:
122
+ all_calls.append({"arguments": all_arguments, **keyword_arguments})
123
+ return child_process
124
+
125
+ monkeypatch.setattr(run_tests.subprocess, "run", run_child)
126
+ exit_code = run_tests.main(
127
+ ["--project", str(tmp_path), "--", "python", "-m", "pytest", "tests"]
128
+ )
129
+
130
+ assert exit_code == 7
131
+ assert all_calls == [
132
+ {
133
+ "arguments": ["python", "-m", "pytest", "tests"],
134
+ "cwd": tmp_path.resolve(),
135
+ "check": False,
136
+ }
137
+ ]
138
+
139
+
140
+ def test_missing_django_database_stops_before_child_launch(
141
+ tmp_path: Path,
142
+ monkeypatch: pytest.MonkeyPatch,
143
+ ) -> None:
144
+ project_root = tmp_path / "django-project"
145
+ _create_django_project(project_root, has_database=False)
146
+ all_calls: list[list[str]] = []
147
+
148
+ def record_launch(all_arguments: list[str], **_keyword_arguments: object) -> None:
149
+ all_calls.append(all_arguments)
150
+
151
+ monkeypatch.setattr(run_tests.subprocess, "run", record_launch)
152
+ exit_code = run_tests.main(
153
+ ["--project", str(project_root), "--", "python", "-m", "pytest"]
154
+ )
155
+
156
+ assert exit_code != 0
157
+ assert all_calls == []
158
+
159
+
160
+ def test_django_pytest_passes_server_check_and_returns_child_status(
161
+ tmp_path: Path,
162
+ monkeypatch: pytest.MonkeyPatch,
163
+ ) -> None:
164
+ project_root = tmp_path / "django-project"
165
+ _create_django_project(project_root, has_database=True)
166
+ all_calls: list[tuple[list[str], dict[str, object]]] = []
167
+
168
+ monkeypatch.setattr(
169
+ run_tests.subprocess, "run", _build_recording_process(all_calls)
170
+ )
171
+ exit_code = run_tests.main(
172
+ ["--project", str(project_root), "--", "python", "-m", "pytest", "tests"]
173
+ )
174
+
175
+ assert exit_code == 0
176
+ assert all_calls[-1][0] == ["python", "-m", "pytest", "tests"]
177
+
178
+
179
+ def test_playwright_server_failure_stops_before_child_launch(
180
+ tmp_path: Path,
181
+ monkeypatch: pytest.MonkeyPatch,
182
+ ) -> None:
183
+ (tmp_path / "manage.py").write_text("", encoding="utf-8")
184
+ all_calls: list[list[str]] = []
185
+
186
+ def run_process(
187
+ all_arguments: list[str],
188
+ **_keyword_arguments: object,
189
+ ) -> subprocess.CompletedProcess[str]:
190
+ all_calls.append(all_arguments)
191
+ return _completed_process(all_arguments, stdout_text="000")
192
+
193
+ monkeypatch.setattr(run_tests.subprocess, "run", run_process)
194
+ exit_code = run_tests.main(
195
+ [
196
+ "--project",
197
+ str(tmp_path),
198
+ "--",
199
+ "npx",
200
+ "playwright",
201
+ "test",
202
+ "--base-url",
203
+ "http://127.0.0.1:1",
204
+ ]
205
+ )
206
+
207
+ assert exit_code != 0
208
+ assert [each_call[0] for each_call in all_calls] == ["ps", "curl"]
209
+
210
+
211
+ def test_playwright_missing_test_database_flag_stops_before_health_check(
212
+ tmp_path: Path,
213
+ monkeypatch: pytest.MonkeyPatch,
214
+ ) -> None:
215
+ (tmp_path / "manage.py").write_text("", encoding="utf-8")
216
+ process_listing = "python manage.py runserver 8000\n"
217
+ all_calls: list[list[str]] = []
218
+
219
+ def run_process(
220
+ all_arguments: list[str],
221
+ **_keyword_arguments: object,
222
+ ) -> subprocess.CompletedProcess[str]:
223
+ all_calls.append(all_arguments)
224
+ return _completed_process(all_arguments, stdout_text=process_listing)
225
+
226
+ monkeypatch.setattr(run_tests.subprocess, "run", run_process)
227
+ exit_code = run_tests.main(
228
+ [
229
+ "--project",
230
+ str(tmp_path),
231
+ "--",
232
+ "playwright",
233
+ "test",
234
+ "--base-url",
235
+ "http://localhost:8000",
236
+ ]
237
+ )
238
+
239
+ assert exit_code != 0
240
+ assert [each_call[0] for each_call in all_calls] == ["ps"]
241
+
242
+
243
+ def test_playwright_builds_frontend_and_collects_static_files(
244
+ tmp_path: Path,
245
+ monkeypatch: pytest.MonkeyPatch,
246
+ ) -> None:
247
+ project_root = tmp_path / "django-project"
248
+ _create_django_project(project_root, has_database=True)
249
+ (project_root / "frontend").mkdir()
250
+ all_calls: list[tuple[list[str], dict[str, object]]] = []
251
+ monkeypatch.setattr(run_tests, "check_runserver_port_conflicts", lambda *_: None)
252
+ monkeypatch.setattr(
253
+ run_tests.subprocess, "run", _build_recording_process(all_calls)
254
+ )
255
+
256
+ exit_code = run_tests.main(
257
+ ["--project", str(project_root), "--", "playwright", "test"]
258
+ )
259
+
260
+ assert exit_code == 0
261
+ assert [each_call[0] for each_call in all_calls] == FRONTEND_COMMANDS
262
+ assert all_calls[1][1]["cwd"] == project_root / "frontend"
263
+ assert all_calls[2][1]["cwd"] == project_root.resolve()
264
+
265
+
266
+ def test_invalid_runner_does_not_launch_a_child(
267
+ tmp_path: Path,
268
+ monkeypatch: pytest.MonkeyPatch,
269
+ ) -> None:
270
+ all_calls: list[list[str]] = []
271
+
272
+ def record_launch(all_arguments: list[str], **_keyword_arguments: object) -> None:
273
+ all_calls.append(all_arguments)
274
+
275
+ monkeypatch.setattr(run_tests.subprocess, "run", record_launch)
276
+ exit_code = run_tests.main(
277
+ ["--project", str(tmp_path), "--", "python", "-m", "unittest"]
278
+ )
279
+
280
+ assert exit_code == 2
281
+ assert all_calls == []
@@ -0,0 +1 @@
1
+ """Constants package for the test-runner command."""
@@ -0,0 +1 @@
1
+ """Configuration package for the test-runner command."""
@@ -0,0 +1,96 @@
1
+ """Names, messages, and limits used by the test-runner command."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+ DEFAULT_DJANGO_URL: str = "http://localhost:8000"
8
+ DEFAULT_PLAYWRIGHT_URL: str = "http://localhost:3000"
9
+ DJANGO_DATABASE_FILENAME: str = "db.sqlite3"
10
+ MANAGE_PY_FILENAME: str = "manage.py"
11
+ FRONTEND_DIRECTORY_NAME: str = "frontend"
12
+ PYTEST_RUNNER_NAME: str = "pytest"
13
+ PLAYWRIGHT_RUNNER_NAME: str = "playwright"
14
+ PYTHON_MODULE_FLAG: str = "-m"
15
+ PLAYWRIGHT_TEST_SUBCOMMAND: str = "test"
16
+ COMMAND_SEPARATOR: str = "--"
17
+ PROJECT_FLAG: str = "--project"
18
+ BASE_URL_FLAG: str = "--base-url"
19
+ ALL_EXECUTABLE_SUFFIXES: tuple[str, ...] = (".exe", ".cmd", ".bat")
20
+ ARGUMENT_ASSIGNMENT_SEPARATOR: str = "="
21
+ COMMAND_ARGUMENT_SEPARATOR: str = " "
22
+ DIRECTORY_LIST_SEPARATOR: str = ", "
23
+ PWD_ENVIRONMENT_NAME: str = "PWD"
24
+ PYTHON_EXECUTABLE_PREFIX: str = "python"
25
+ PYTEST_EXECUTABLE_PREFIX: str = "pytest"
26
+ PLAYWRIGHT_EXECUTABLE_NAME: str = "playwright"
27
+ NPX_EXECUTABLE_NAME: str = "npx"
28
+ RUNSERVER_COMMAND_NAME: str = "runserver"
29
+ TEST_DATABASE_FLAG: str = "--test-db"
30
+ ALL_RUNSERVER_OPTIONS_WITH_VALUES: frozenset[str] = frozenset(
31
+ {"--pythonpath", "--settings", "--verbosity"}
32
+ )
33
+ PROCESS_LIST_ARGUMENTS: tuple[str, ...] = ("ps", "aux")
34
+ ALL_PROCESS_INFORMATION_FIELDS: tuple[str, ...] = ("pid", "cmdline", "cwd")
35
+ ALL_FRONTEND_BUILD_ARGUMENTS: tuple[str, ...] = ("npm", "run", "build")
36
+ ALL_COLLECTSTATIC_ARGUMENTS: tuple[str, ...] = (
37
+ "python",
38
+ "manage.py",
39
+ "collectstatic",
40
+ "--noinput",
41
+ )
42
+ CURL_EXECUTABLE_NAME: str = "curl"
43
+ CURL_SILENT_FLAG: str = "-s"
44
+ CURL_OUTPUT_FLAG: str = "-o"
45
+ CURL_WRITE_OUT_FLAG: str = "-w"
46
+ CURL_STATUS_FORMAT: str = "%{http_code}"
47
+ CURL_MAX_TIME_FLAG: str = "--max-time"
48
+ NULL_DEVICE_PATH: str = "/dev/null"
49
+ ALL_LOCAL_SERVER_HOSTS: frozenset[str] = frozenset(
50
+ {"localhost", "127.0.0.1", "0.0.0.0"}
51
+ )
52
+ ALL_BLOCKED_STATUS_CODES: frozenset[int] = frozenset({500, 502, 503, 504})
53
+ SERVER_URL_PATTERN: re.Pattern[str] = re.compile(r"https?://[^\s\"']+")
54
+ ALL_URL_TRAILING_PUNCTUATION: str = ".,;:)]}"
55
+ DEFAULT_DJANGO_PORT: str = "8000"
56
+ PROCESS_CURL_TIMEOUT_SECONDS: int = 2
57
+ HEALTH_CHECK_TIMEOUT_SECONDS: int = 3
58
+ BUILD_TIMEOUT_SECONDS: int = 120
59
+ EXIT_CODE_SUCCESS: int = 0
60
+ EXIT_CODE_FAILURE: int = 1
61
+ EXIT_CODE_INVALID_ARGUMENTS: int = 2
62
+ MINIMUM_PLAYWRIGHT_ARGUMENT_COUNT: int = 2
63
+ MINIMUM_NPX_ARGUMENT_COUNT: int = 3
64
+ MINIMUM_PROCESS_ARGUMENT_COUNT: int = 3
65
+ MINIMUM_RUNSERVER_PROCESS_COUNT: int = 2
66
+ RUNSERVER_COMMAND_INDEX: int = 2
67
+ PLAYWRIGHT_TEST_COMMAND_INDEX: int = 2
68
+ ERROR_COMMAND_SEPARATOR_REQUIRED: str = "the child command must follow --"
69
+ ERROR_CHILD_COMMAND_REQUIRED: str = "a supported child test command is required"
70
+ ERROR_UNSUPPORTED_RUNNER: str = "only pytest and playwright test commands are supported"
71
+ ERROR_PREFLIGHT_FAILURE_TEMPLATE: str = "test preflight failed: {}"
72
+ ERROR_PROJECT_NOT_DIRECTORY_TEMPLATE: str = "project is not a directory: {}"
73
+ ERROR_CHILD_LAUNCH_TEMPLATE: str = "test child could not start: {}"
74
+ HEALTH_CHECK_ERROR_TEMPLATE: str = (
75
+ "BLOCKED: Server at {} is not healthy ({}). Fix the server before running tests."
76
+ )
77
+ UNREACHABLE_ERROR_TEMPLATE: str = (
78
+ "BLOCKED: Server at {} is unreachable. Start the server before running tests."
79
+ )
80
+ MISSING_DATABASE_ERROR_TEMPLATE: str = (
81
+ "BLOCKED: No database file ({}) found in {}. Run migrations before running tests."
82
+ )
83
+ FRONTEND_BUILD_FAILED_MESSAGE: str = (
84
+ "BLOCKED: Frontend build failed. Fix build errors before running e2e tests."
85
+ )
86
+ MISSING_TEST_DATABASE_FLAG_TEMPLATE: str = (
87
+ "BLOCKED: Django server on port {} is not running with --test-db. Restart "
88
+ "with: python manage.py runserver --test-db 0.0.0.0:{}"
89
+ )
90
+ PORT_CONFLICT_ERROR_TEMPLATE: str = (
91
+ "BLOCKED: Multiple Django runserver processes are bound to port {} across "
92
+ "worktrees: {}. Stop stale servers first."
93
+ )
94
+ PROCESS_LIST_RUNSERVER_TOKEN: str = "runserver"
95
+ PROCESS_LIST_SELF_FILTER_TOKEN: str = "grep"
96
+ UTF8_ENCODING: str = "utf-8"
@@ -26,7 +26,7 @@ Author-keyed logic accounts for the split: a self-PR check keys on the login tha
26
26
 
27
27
  ## Hooks that still gate a cloud run
28
28
 
29
- The commit and push gates read git commands, which a cloud run still issues through Bash, so they fire normally and the run follows them: `session_edit_stage_gate`, `block_main_commit`, `test_preflight_check`, and the pre-push base-ref check that needs `origin/HEAD` set (SKILL.md Step 2).
29
+ The commit and push gates read git commands, which a cloud run still issues through Bash, so they fire normally and the run follows them: `session_edit_stage_gate`, `block_main_commit`, and the pre-push base-ref check that needs `origin/HEAD` set (SKILL.md Step 2). Run tests through the `test-runner` skill when Django or Playwright readiness checks apply.
30
30
 
31
31
  ## Hooks that go quiet on the MCP path
32
32
 
@@ -107,6 +107,12 @@ ALL_GIT_DIFF_CACHED_UNIFIED_ZERO_COMMAND: tuple[str, ...] = (
107
107
  "--unified=0",
108
108
  )
109
109
 
110
+ ALL_GIT_DIFF_UNIFIED_ZERO_COMMAND: tuple[str, ...] = (
111
+ "git",
112
+ "diff",
113
+ "--unified=0",
114
+ )
115
+
110
116
  GIT_DIFF_SUBPROCESS_TIMEOUT_SECONDS: int = 30
111
117
 
112
118
  GIT_NO_MATCH_EXIT_CODE: int = 1