claude-dev-env 4.3.0 → 7.1.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 (480) 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 +5 -4
  12. package/.agents/skills/pull-request/SKILL.md +11 -13
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
  15. package/.agents/skills/pull-request/scripts/pull_request.py +35 -44
  16. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
  17. package/.agents/skills/pull-request/scripts/test_pull_request.py +35 -3
  18. package/.agents/skills/session-continuity/README.md +189 -0
  19. package/.agents/skills/session-continuity/SKILL.md +114 -0
  20. package/.agents/skills/session-continuity/continuity.mjs +375 -0
  21. package/.agents/skills/session-continuity/continuity.test.mjs +303 -0
  22. package/.agents/skills/skill-builder/SKILL.md +3 -196
  23. package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
  24. package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
  25. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  26. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  27. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  28. package/bin/cde.mjs +36 -8
  29. package/bin/cde.test.mjs +48 -0
  30. package/bin/ever-shipped-skills.mjs +2 -20
  31. package/bin/ever-shipped-skills.test.mjs +8 -0
  32. package/bin/expand_home_directory_tokens.mjs +4 -5
  33. package/bin/expand_home_directory_tokens.test.mjs +128 -0
  34. package/bin/git_hooks_installer.mjs +22 -3
  35. package/bin/git_hooks_installer.test.mjs +159 -5
  36. package/bin/install-session-continuity.mjs +78 -0
  37. package/bin/install-session-continuity.test.mjs +42 -0
  38. package/bin/install.agents-home.test.mjs +15 -13
  39. package/bin/install.codex-rules.test.mjs +5 -2
  40. package/bin/install.cursor-rules.test.mjs +5 -2
  41. package/bin/install.mjs +506 -63
  42. package/bin/install.policy-lint.test.mjs +130 -0
  43. package/bin/install.profiles.test.mjs +10 -0
  44. package/bin/install.prune.test.mjs +286 -37
  45. package/bin/install.pstack-plugin.test.mjs +114 -0
  46. package/bin/install.test.mjs +537 -120
  47. package/bin/install.transaction.test.mjs +3 -3
  48. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  49. package/docs/CODE_RULES.md +1 -1
  50. package/hooks/advisory/pr_done_reminder.py +342 -0
  51. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  52. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  53. package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
  54. package/hooks/blocking/code_rules_paired_test.py +22 -15
  55. package/hooks/blocking/precommit_code_rules_gate.py +5 -8
  56. package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
  57. package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
  58. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
  59. package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
  60. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  61. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  62. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  63. package/hooks/blocking/test_pre_tool_use_dispatcher.py +43 -342
  64. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  65. package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
  66. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  67. package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
  68. package/hooks/git-hooks/post_commit.py +33 -0
  69. package/hooks/git-hooks/pre_commit.py +18 -49
  70. package/hooks/git-hooks/pre_push.py +45 -541
  71. package/hooks/git-hooks/test_installed_policy_lint.py +208 -0
  72. package/hooks/git-hooks/test_native_hook_support.py +129 -0
  73. package/hooks/git-hooks/test_native_policy_commit.py +120 -0
  74. package/hooks/git-hooks/test_post_commit.py +43 -0
  75. package/hooks/git-hooks/test_pre_commit.py +38 -148
  76. package/hooks/git-hooks/test_pre_push.py +48 -1631
  77. package/hooks/git-hooks/test_verification_notice.py +60 -0
  78. package/hooks/git-hooks/test_verification_start.py +173 -0
  79. package/hooks/git-hooks/verification_notice.py +192 -0
  80. package/hooks/git-hooks/verification_notice_context.py +253 -0
  81. package/hooks/git-hooks/verification_notice_state.py +237 -0
  82. package/hooks/git-hooks/verification_start.py +100 -0
  83. package/hooks/hooks.json +1 -120
  84. package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
  85. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
  86. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
  87. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
  88. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  89. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -93
  90. package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
  91. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
  92. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
  93. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  94. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +46 -201
  95. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
  96. package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
  97. package/hooks/validators/run_all_validators.py +5 -1
  98. package/hooks/validators/security_checks.py +57 -2
  99. package/hooks/validators/test_hook_timing_harness.py +2 -5
  100. package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
  101. package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
  102. package/hooks/validators/test_security_checks.py +57 -0
  103. package/package.json +2 -2
  104. package/rules/confirm-implementation-forks.md +2 -0
  105. package/rules/durable-post-artifacts.md +2 -1
  106. package/rules/git-workflow.md +18 -6
  107. package/rules/pstack-host-mapping.md +48 -0
  108. package/rules/vault-context.md +3 -5
  109. package/rules/verify-runtime-state.md +7 -0
  110. package/scripts/audit-hooks-cli.mjs +5 -0
  111. package/scripts/audit-hooks.mjs +72 -16
  112. package/scripts/automatic_advisory/__init__.py +1 -0
  113. package/scripts/automatic_advisory/checkout.py +51 -0
  114. package/scripts/automatic_advisory/cli.py +334 -0
  115. package/scripts/automatic_advisory/config/__init__.py +1 -0
  116. package/scripts/automatic_advisory/config/constants.py +93 -0
  117. package/scripts/automatic_advisory/config/timing.py +4 -0
  118. package/scripts/automatic_advisory/configuration.py +197 -0
  119. package/scripts/automatic_advisory/execution.py +350 -0
  120. package/scripts/automatic_advisory/git.py +69 -0
  121. package/scripts/automatic_advisory/model.py +107 -0
  122. package/scripts/automatic_advisory/process_host.py +32 -0
  123. package/scripts/automatic_advisory/publisher.py +84 -0
  124. package/scripts/automatic_advisory/runner.py +399 -0
  125. package/scripts/automatic_advisory/state.py +116 -0
  126. package/scripts/automatic_advisory/window_flags.py +41 -0
  127. package/scripts/automatic_advisory/windows_job.py +122 -0
  128. package/scripts/check.ps1 +0 -1
  129. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  130. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  131. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  132. package/scripts/codex_compat_materializer.py +99 -13
  133. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  134. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +35 -43
  135. package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +10 -10
  136. package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +14 -1
  137. package/scripts/local_report_cli.py +184 -0
  138. package/scripts/local_report_core.py +247 -0
  139. package/scripts/local_report_publisher.py +12 -0
  140. package/scripts/local_report_validation.py +357 -0
  141. package/scripts/local_verification/__init__.py +23 -0
  142. package/scripts/local_verification/check_runner.py +364 -0
  143. package/scripts/local_verification/check_support.py +99 -0
  144. package/scripts/local_verification/cli.py +158 -0
  145. package/scripts/local_verification/command_runner.py +84 -0
  146. package/scripts/local_verification/config/__init__.py +1 -0
  147. package/scripts/local_verification/config/constants.py +89 -0
  148. package/scripts/local_verification/config/timing.py +1 -0
  149. package/scripts/local_verification/git_state.py +146 -0
  150. package/scripts/local_verification/manifest.py +224 -0
  151. package/scripts/local_verification/model.py +174 -0
  152. package/scripts/local_verification/runner.py +234 -0
  153. package/scripts/policy_lint/adapter_detectors.py +13 -6
  154. package/scripts/policy_lint/adapter_pairing.py +58 -3
  155. package/scripts/policy_lint/adapters.py +7 -4
  156. package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
  157. package/scripts/policy_lint/config/constants.py +5 -0
  158. package/scripts/policy_lint/engine.py +48 -4
  159. package/scripts/policy_lint/model.py +1 -0
  160. package/scripts/policy_lint/registry.py +1 -1
  161. package/scripts/policy_lint/selection.py +2 -1
  162. package/scripts/policy_lint/selection_git.py +2 -0
  163. package/scripts/pr_verification/__init__.py +1 -0
  164. package/scripts/pr_verification/branch_refs.py +114 -0
  165. package/scripts/pr_verification/config/__init__.py +1 -0
  166. package/scripts/pr_verification/config/constants.py +84 -0
  167. package/scripts/pr_verification/config/timing.py +5 -0
  168. package/scripts/pr_verification/github.py +137 -0
  169. package/scripts/pr_verification/github_api.py +376 -0
  170. package/scripts/pr_verification/github_labels.py +88 -0
  171. package/scripts/pr_verification/github_parsing.py +182 -0
  172. package/scripts/pr_verification/github_transport.py +91 -0
  173. package/scripts/pr_verification/lock.py +122 -0
  174. package/scripts/pr_verification/model.py +43 -0
  175. package/scripts/pr_verification/requirements.txt +1 -0
  176. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  177. package/scripts/shared_tree_paths.py +32 -3
  178. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +97 -0
  179. package/scripts/test_shared_tree_paths.py +33 -0
  180. package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
  181. package/scripts/tests/test-audit-hooks.mjs +94 -2
  182. package/scripts/tests/test_automatic_advisory.py +376 -0
  183. package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
  184. package/scripts/tests/test_automatic_advisory_polling.py +216 -0
  185. package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
  186. package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
  187. package/scripts/tests/test_automatic_advisory_settings.py +48 -0
  188. package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
  189. package/scripts/tests/test_closed_pr_label.py +157 -0
  190. package/scripts/tests/test_codex_compat_materializer.py +189 -6
  191. package/scripts/tests/test_github_labels.py +101 -0
  192. package/scripts/tests/test_github_transport.py +125 -0
  193. package/scripts/tests/test_local_report_publisher.py +396 -0
  194. package/scripts/tests/test_local_report_publisher_cli.py +85 -0
  195. package/scripts/tests/test_local_verification.py +348 -0
  196. package/scripts/tests/test_local_verification_cli.py +98 -0
  197. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  198. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  199. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  200. package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
  201. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  202. package/scripts/tests/test_policy_lint_selection.py +25 -2
  203. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  204. package/scripts/tests/test_pr_verification_github.py +392 -0
  205. package/scripts/tests/test_pr_verification_lock.py +168 -0
  206. package/scripts/tests/test_rules.py +3 -7
  207. package/scripts/tests/test_verification_notice.py +347 -0
  208. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  209. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  210. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  211. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  212. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  213. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  214. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  215. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  216. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  217. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  218. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  219. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  220. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  221. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  222. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  223. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  224. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  225. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  226. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  227. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  228. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  229. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  230. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  231. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  232. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  233. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  234. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  235. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  236. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  237. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  238. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  239. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  240. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  241. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  242. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  243. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  244. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  245. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  246. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  247. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  248. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  249. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  250. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  251. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  252. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  253. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  254. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  255. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  256. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  257. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  258. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  259. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  260. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  261. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  262. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  263. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  264. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  265. package/.agents/skills/autoconverge/SKILL.md +0 -431
  266. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  267. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  268. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  269. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  270. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  271. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  272. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  273. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  274. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  275. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  276. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  277. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  278. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  279. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  280. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  281. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  282. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  283. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  284. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  285. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  286. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  287. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  288. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  289. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  290. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  291. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  292. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  293. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  294. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  295. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  296. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  297. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  298. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  299. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  300. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  301. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  302. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  303. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  304. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  305. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  306. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  307. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  308. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  309. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  310. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  311. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  312. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  313. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  314. package/.agents/skills/hitl/SKILL.md +0 -45
  315. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  316. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  317. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  318. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  319. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  320. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  321. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  322. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  323. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  324. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  325. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  326. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  327. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  328. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  329. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  330. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  331. package/.agents/skills/prototype/SKILL.md +0 -85
  332. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  333. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  334. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  335. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  336. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  337. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  338. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  339. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  340. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  341. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  342. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  343. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  344. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  345. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  346. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  347. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  348. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  349. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  350. package/.agents/skills/rebase/AGENTS.md +0 -1
  351. package/.agents/skills/rebase/SKILL.md +0 -178
  352. package/.agents/skills/review-router/SKILL.md +0 -51
  353. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  354. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  355. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  356. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  357. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  358. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  359. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  360. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  361. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  362. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  363. package/.agents/skills/review-tier/SKILL.md +0 -56
  364. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  365. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  366. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  367. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  368. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  369. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  370. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  371. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  372. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  373. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  374. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  375. package/.agents/skills/session-log/AGENTS.md +0 -1
  376. package/.agents/skills/session-log/SKILL.md +0 -170
  377. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  378. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  379. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  380. package/.agents/skills/session-tidy/SKILL.md +0 -191
  381. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  382. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  383. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  384. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  385. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  386. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  387. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  388. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  389. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  390. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  391. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  392. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  393. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  394. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  395. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  396. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  397. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  398. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  399. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  400. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  401. package/.agents/skills/update/AGENTS.md +0 -1
  402. package/.agents/skills/update/SKILL.md +0 -190
  403. package/_shared/pr-loop/AGENTS.md +0 -1
  404. package/_shared/pr-loop/audit-contract.md +0 -183
  405. package/_shared/pr-loop/audit-reply-template.md +0 -151
  406. package/_shared/pr-loop/code-rules-gate.md +0 -65
  407. package/_shared/pr-loop/fix-protocol.md +0 -60
  408. package/_shared/pr-loop/gh-payloads.md +0 -75
  409. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  410. package/_shared/pr-loop/precatch-rubric.md +0 -66
  411. package/_shared/pr-loop/preflight-proposal.md +0 -56
  412. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  413. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  414. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  415. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  416. package/_shared/pr-loop/state-schema.md +0 -45
  417. package/_shared/pr-loop/worker-spawn.md +0 -189
  418. package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
  419. package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
  420. package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
  421. package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
  422. package/hooks/blocking/destructive_command_blocker.py +0 -2073
  423. package/hooks/blocking/fable_spawn_gate.py +0 -187
  424. package/hooks/blocking/luna_fast_mode_gate.py +0 -160
  425. package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
  426. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
  427. package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
  428. package/hooks/blocking/pii_prevention_blocker.py +0 -365
  429. package/hooks/blocking/piped_pytest_blocker.py +0 -1223
  430. package/hooks/blocking/question_to_user_enforcer.py +0 -130
  431. package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
  432. package/hooks/blocking/sensitive_file_protector.py +0 -167
  433. package/hooks/blocking/session_edit_stage_gate.py +0 -684
  434. package/hooks/blocking/session_handoff_blocker.py +0 -182
  435. package/hooks/blocking/shell_substitution_blocker.py +0 -350
  436. package/hooks/blocking/test_ask_user_question_shape.py +0 -69
  437. package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
  438. package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
  439. package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
  440. package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
  441. package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
  442. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
  443. package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
  444. package/hooks/blocking/test_fable_spawn_gate.py +0 -381
  445. package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
  446. package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
  447. package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
  448. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
  449. package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
  450. package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
  451. package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
  452. package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
  453. package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
  454. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
  455. package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
  456. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
  457. package/hooks/blocking/test_sensitive_file_protector.py +0 -365
  458. package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
  459. package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
  460. package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
  461. package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
  462. package/hooks/blocking/test_session_handoff_blocker.py +0 -318
  463. package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
  464. package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
  465. package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
  466. package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
  467. package/hooks/blocking/unscoped_search_blocker.py +0 -391
  468. package/hooks/blocking/write_existing_file_blocker.py +0 -124
  469. package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
  470. package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
  471. package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
  472. package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
  473. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
  474. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
  475. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
  476. package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
  477. package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
  478. package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
  479. /package/.agents/skills/{_shared/pr-loop → session-continuity}/AGENTS.md +0 -0
  480. /package/{_shared/pr-loop → .agents/skills/session-continuity}/CLAUDE.md +0 -0
@@ -0,0 +1,364 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from collections.abc import Callable
5
+ from pathlib import Path
6
+ from typing import TypeAlias
7
+
8
+ from .check_support import (
9
+ _prepare_check_invocation,
10
+ _record_missing_check,
11
+ _write_logs,
12
+ )
13
+ from .command_runner import run_command as _run_command
14
+ from .config import (
15
+ CLI_CHECK_FINISH_MESSAGE_TEMPLATE,
16
+ CLI_CHECK_START_MESSAGE_TEMPLATE,
17
+ COLLECTED_TESTS_PATTERN,
18
+ COLLECTION_ERROR_KIND,
19
+ COLLECTION_STDERR_LOG_SUFFIX,
20
+ COLLECTION_STDOUT_LOG_SUFFIX,
21
+ CRASH_ERROR_KIND,
22
+ FAILED_STATUS,
23
+ INCOMPLETE_STATUS,
24
+ MINIMUM_TESTS_ERROR_KIND,
25
+ NONZERO_EXIT_ERROR_KIND,
26
+ PASSED_STATUS,
27
+ PYTEST_COLLECTION_FLAG,
28
+ PYTEST_QUIET_FLAG,
29
+ SAFE_LOG_NAME_PATTERN,
30
+ SKIPPED_TESTS_ERROR_KIND,
31
+ SKIPPED_TESTS_PATTERN,
32
+ STDERR_LOG_SUFFIX,
33
+ STDOUT_LOG_SUFFIX,
34
+ SUCCESS_EXIT_CODE,
35
+ )
36
+ from .model import (
37
+ CheckLogPaths,
38
+ CheckRecord,
39
+ CheckSpec,
40
+ CommandCapture,
41
+ VerificationManifest,
42
+ )
43
+
44
+ ProgressCallback: TypeAlias = Callable[[str], None]
45
+
46
+
47
+ def execute_all_checks(
48
+ manifest: VerificationManifest,
49
+ repository_path: Path,
50
+ base_revision: str,
51
+ logs_directory: Path,
52
+ progress_callback: ProgressCallback | None,
53
+ ) -> tuple[CheckRecord, ...]:
54
+ """Execute every check in the manifest.
55
+
56
+ Args:
57
+ manifest: Checks to execute.
58
+
59
+ Returns:
60
+ One record for each check.
61
+ """
62
+ return tuple(
63
+ _execute_check(
64
+ each_check,
65
+ repository_path,
66
+ base_revision,
67
+ logs_directory,
68
+ progress_callback,
69
+ )
70
+ for each_check in manifest.checks
71
+ )
72
+
73
+
74
+ def _execute_check(
75
+ check: CheckSpec,
76
+ repository_path: Path,
77
+ base_revision: str,
78
+ logs_directory: Path,
79
+ progress_callback: ProgressCallback | None,
80
+ ) -> CheckRecord:
81
+ _emit_progress(
82
+ progress_callback,
83
+ CLI_CHECK_START_MESSAGE_TEMPLATE.format(check_id=check.check_id),
84
+ )
85
+ check_log_paths = _build_check_log_paths(check, logs_directory)
86
+ check_directory, all_arguments = _prepare_check_invocation(
87
+ check, repository_path, base_revision
88
+ )
89
+ if not check_directory.is_dir():
90
+ check_record = _record_missing_check(
91
+ check, all_arguments, check_directory, check_log_paths
92
+ )
93
+ return _finish_with_progress(progress_callback, check, check_record)
94
+ check_record = _run_existing_check(
95
+ check, all_arguments, check_directory, check_log_paths
96
+ )
97
+ return _finish_with_progress(progress_callback, check, check_record)
98
+
99
+
100
+ def _build_check_log_paths(check: CheckSpec, logs_directory: Path) -> CheckLogPaths:
101
+ safe_check_id = SAFE_LOG_NAME_PATTERN.sub("_", check.check_id)
102
+ return CheckLogPaths(
103
+ logs_directory / (safe_check_id + STDOUT_LOG_SUFFIX),
104
+ logs_directory / (safe_check_id + STDERR_LOG_SUFFIX),
105
+ logs_directory / (safe_check_id + COLLECTION_STDOUT_LOG_SUFFIX),
106
+ logs_directory / (safe_check_id + COLLECTION_STDERR_LOG_SUFFIX),
107
+ )
108
+
109
+
110
+ def _run_existing_check(
111
+ check: CheckSpec,
112
+ all_arguments: tuple[str, ...],
113
+ check_directory: Path,
114
+ check_log_paths: CheckLogPaths,
115
+ ) -> CheckRecord:
116
+ all_collection_capture = _collect_minimum_tests(
117
+ check,
118
+ all_arguments,
119
+ check_directory,
120
+ check_log_paths.collection_stdout,
121
+ check_log_paths.collection_stderr,
122
+ )
123
+ started_at = time.monotonic()
124
+ command_capture = _run_command(
125
+ all_arguments, check_directory, check.timeout_seconds
126
+ )
127
+ duration_seconds = time.monotonic() - started_at
128
+ return _finish_check(
129
+ check,
130
+ all_arguments,
131
+ check_directory,
132
+ check_log_paths,
133
+ all_collection_capture,
134
+ command_capture,
135
+ duration_seconds,
136
+ )
137
+
138
+
139
+ def _finish_with_progress(
140
+ progress_callback: ProgressCallback | None,
141
+ check: CheckSpec,
142
+ check_record: CheckRecord,
143
+ ) -> CheckRecord:
144
+ _emit_progress(
145
+ progress_callback,
146
+ CLI_CHECK_FINISH_MESSAGE_TEMPLATE.format(
147
+ check_id=check.check_id, status=check_record.status
148
+ ),
149
+ )
150
+ return check_record
151
+
152
+
153
+ def _emit_progress(
154
+ progress_callback: ProgressCallback | None, progress_message: str
155
+ ) -> None:
156
+ if progress_callback is not None:
157
+ progress_callback(progress_message)
158
+
159
+
160
+ def _finish_check(
161
+ check: CheckSpec,
162
+ all_arguments: tuple[str, ...],
163
+ check_directory: Path,
164
+ check_log_paths: CheckLogPaths,
165
+ all_collection_capture: tuple[CommandCapture, int | None] | None,
166
+ command_capture: CommandCapture,
167
+ duration_seconds: float,
168
+ ) -> CheckRecord:
169
+ _write_logs(
170
+ check_log_paths.stdout,
171
+ check_log_paths.stderr,
172
+ command_capture.stdout_text,
173
+ command_capture.stderr_text,
174
+ )
175
+ return _build_finished_record(
176
+ check,
177
+ all_arguments,
178
+ check_directory,
179
+ check_log_paths,
180
+ all_collection_capture,
181
+ command_capture,
182
+ duration_seconds,
183
+ )
184
+
185
+
186
+ def _build_finished_record(
187
+ check: CheckSpec,
188
+ all_arguments: tuple[str, ...],
189
+ check_directory: Path,
190
+ check_log_paths: CheckLogPaths,
191
+ all_collection_capture: tuple[CommandCapture, int | None] | None,
192
+ command_capture: CommandCapture,
193
+ duration_seconds: float,
194
+ ) -> CheckRecord:
195
+ status, error_kind, error_message = _classify_record(
196
+ check, all_collection_capture, command_capture
197
+ )
198
+ return CheckRecord(
199
+ check.check_id,
200
+ status,
201
+ all_arguments,
202
+ str(check_directory),
203
+ command_capture.exit_code,
204
+ duration_seconds,
205
+ check_log_paths.stdout,
206
+ check_log_paths.stderr,
207
+ error_kind,
208
+ error_message,
209
+ check.minimum_tests,
210
+ all_collection_capture[1] if all_collection_capture is not None else None,
211
+ all_collection_capture[0].exit_code
212
+ if all_collection_capture is not None
213
+ else None,
214
+ )
215
+
216
+
217
+ def _classify_record(
218
+ check: CheckSpec,
219
+ all_collection_capture: tuple[CommandCapture, int | None] | None,
220
+ command_capture: CommandCapture,
221
+ ) -> tuple[str, str | None, str | None]:
222
+ status, error_kind, error_message = _classify_command(command_capture)
223
+ if all_collection_capture is None:
224
+ return status, error_kind, error_message
225
+ status, error_kind, error_message = _classify_collection(
226
+ check, all_collection_capture, status, error_kind, error_message
227
+ )
228
+ return _classify_skipped_tests(
229
+ check,
230
+ all_collection_capture,
231
+ command_capture,
232
+ status,
233
+ error_kind,
234
+ error_message,
235
+ )
236
+
237
+
238
+ def _collect_minimum_tests(
239
+ check: CheckSpec,
240
+ all_arguments: tuple[str, ...],
241
+ check_directory: Path,
242
+ stdout_log_path: Path,
243
+ stderr_log_path: Path,
244
+ ) -> tuple[CommandCapture, int | None] | None:
245
+ if check.minimum_tests is None:
246
+ return None
247
+ collection_arguments = _collection_arguments(all_arguments)
248
+ collection_capture = _run_command(
249
+ collection_arguments, check_directory, check.timeout_seconds
250
+ )
251
+ _write_logs(
252
+ stdout_log_path,
253
+ stderr_log_path,
254
+ collection_capture.stdout_text,
255
+ collection_capture.stderr_text,
256
+ )
257
+ collected_tests = _parse_collected_tests(collection_capture)
258
+ return collection_capture, collected_tests
259
+
260
+
261
+ def _collection_arguments(all_arguments: tuple[str, ...]) -> tuple[str, ...]:
262
+ all_collection_arguments = list(all_arguments)
263
+ if PYTEST_COLLECTION_FLAG not in all_collection_arguments:
264
+ all_collection_arguments.append(PYTEST_COLLECTION_FLAG)
265
+ if PYTEST_QUIET_FLAG not in all_collection_arguments:
266
+ all_collection_arguments.append(PYTEST_QUIET_FLAG)
267
+ return tuple(all_collection_arguments)
268
+
269
+
270
+ def _parse_collected_tests(command_capture: CommandCapture) -> int | None:
271
+ all_text = command_capture.stdout_text + command_capture.stderr_text
272
+ test_match = COLLECTED_TESTS_PATTERN.search(all_text)
273
+ if test_match is None:
274
+ return 0 if command_capture.exit_code == 5 else None
275
+ return int(test_match.group("count"))
276
+
277
+
278
+ def _parse_skipped_tests(command_capture: CommandCapture) -> int:
279
+ all_text = command_capture.stdout_text + command_capture.stderr_text
280
+ skipped_match = SKIPPED_TESTS_PATTERN.search(all_text)
281
+ if skipped_match is None:
282
+ return 0
283
+ return int(skipped_match.group("count"))
284
+
285
+
286
+ def _classify_skipped_tests(
287
+ check: CheckSpec,
288
+ all_collection_capture: tuple[CommandCapture, int | None],
289
+ command_capture: CommandCapture,
290
+ status: str,
291
+ error_kind: str | None,
292
+ error_message: str | None,
293
+ ) -> tuple[str, str | None, str | None]:
294
+ if status != PASSED_STATUS:
295
+ return status, error_kind, error_message
296
+ if not _has_skipped_required_tests(check, all_collection_capture, command_capture):
297
+ return status, error_kind, error_message
298
+ return INCOMPLETE_STATUS, SKIPPED_TESTS_ERROR_KIND, "Required tests were skipped"
299
+
300
+
301
+ def _has_skipped_required_tests(
302
+ check: CheckSpec,
303
+ all_collection_capture: tuple[CommandCapture, int | None],
304
+ command_capture: CommandCapture,
305
+ ) -> bool:
306
+ if check.minimum_tests is None:
307
+ return False
308
+ collection_capture, collected_tests = all_collection_capture
309
+ if collection_capture.exit_code != SUCCESS_EXIT_CODE:
310
+ return False
311
+ if collected_tests is None or collected_tests == 0:
312
+ return False
313
+ return _parse_skipped_tests(command_capture) > 0
314
+
315
+
316
+ def _classify_collection(
317
+ check: CheckSpec,
318
+ all_collection_capture: tuple[CommandCapture, int | None],
319
+ status: str,
320
+ error_kind: str | None,
321
+ error_message: str | None,
322
+ ) -> tuple[str, str | None, str | None]:
323
+ command_capture, collected_tests = all_collection_capture
324
+ if (
325
+ check.minimum_tests is not None
326
+ and collected_tests is not None
327
+ and collected_tests < check.minimum_tests
328
+ ):
329
+ return FAILED_STATUS, MINIMUM_TESTS_ERROR_KIND, "Minimum test count was not met"
330
+ if status == FAILED_STATUS:
331
+ return status, error_kind, error_message
332
+ if command_capture.error_kind is not None:
333
+ return INCOMPLETE_STATUS, COLLECTION_ERROR_KIND, command_capture.error_message
334
+ if command_capture.exit_code != SUCCESS_EXIT_CODE:
335
+ return INCOMPLETE_STATUS, COLLECTION_ERROR_KIND, "Pytest collection failed"
336
+ if collected_tests is None:
337
+ return (
338
+ INCOMPLETE_STATUS,
339
+ COLLECTION_ERROR_KIND,
340
+ "Pytest collection count is missing",
341
+ )
342
+ return status, error_kind, error_message
343
+
344
+
345
+ def _classify_command(
346
+ command_capture: CommandCapture,
347
+ ) -> tuple[str, str | None, str | None]:
348
+ if command_capture.error_kind is not None:
349
+ return (
350
+ INCOMPLETE_STATUS,
351
+ command_capture.error_kind,
352
+ command_capture.error_message,
353
+ )
354
+ if command_capture.exit_code is None:
355
+ return INCOMPLETE_STATUS, CRASH_ERROR_KIND, "Command ended without an exit code"
356
+ if command_capture.exit_code < 0:
357
+ return INCOMPLETE_STATUS, CRASH_ERROR_KIND, "Command ended from a signal"
358
+ if command_capture.exit_code != SUCCESS_EXIT_CODE:
359
+ return (
360
+ FAILED_STATUS,
361
+ NONZERO_EXIT_ERROR_KIND,
362
+ "Command returned a nonzero exit code",
363
+ )
364
+ return PASSED_STATUS, None, None
@@ -0,0 +1,99 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ from .config import (
7
+ BASE_PLACEHOLDER,
8
+ CDE_LINT_PLACEHOLDER,
9
+ INCOMPLETE_STATUS,
10
+ PYTHON_PLACEHOLDER,
11
+ REPOSITORY_PLACEHOLDER,
12
+ REPOSITORY_POLICY_PLACEHOLDER,
13
+ START_ERROR_KIND,
14
+ UTF8_ENCODING,
15
+ )
16
+ from .model import CheckLogPaths, CheckRecord, CheckSpec
17
+
18
+
19
+ def _prepare_check_invocation(
20
+ check: CheckSpec, repository_path: Path, base_revision: str
21
+ ) -> tuple[Path, tuple[str, ...]]:
22
+ check_directory = repository_path / check.cwd
23
+ all_arguments = _substitute_arguments(
24
+ check.command_arguments, repository_path, base_revision
25
+ )
26
+ return check_directory, all_arguments
27
+
28
+
29
+ def _record_missing_check(
30
+ check: CheckSpec,
31
+ all_arguments: tuple[str, ...],
32
+ check_directory: Path,
33
+ check_log_paths: CheckLogPaths,
34
+ ) -> CheckRecord:
35
+ _write_logs(
36
+ check_log_paths.stdout, check_log_paths.stderr, "", "Missing check directory"
37
+ )
38
+ return _incomplete_record(
39
+ check,
40
+ all_arguments,
41
+ check_directory,
42
+ check_log_paths.stdout,
43
+ check_log_paths.stderr,
44
+ START_ERROR_KIND,
45
+ "Check cwd does not exist",
46
+ )
47
+
48
+
49
+ def _substitute_arguments(
50
+ all_arguments: tuple[str, ...], repository_path: Path, base_revision: str
51
+ ) -> tuple[str, ...]:
52
+ package_root = Path(__file__).resolve().parents[2]
53
+ replacement_by_placeholder = {
54
+ PYTHON_PLACEHOLDER: sys.executable,
55
+ REPOSITORY_PLACEHOLDER: str(repository_path),
56
+ BASE_PLACEHOLDER: base_revision,
57
+ CDE_LINT_PLACEHOLDER: str(package_root / "scripts" / "cde_lint.py"),
58
+ REPOSITORY_POLICY_PLACEHOLDER: str(
59
+ package_root / "scripts" / "repository_policy.py"
60
+ ),
61
+ }
62
+ return tuple(
63
+ replacement_by_placeholder.get(each_argument, each_argument)
64
+ for each_argument in all_arguments
65
+ )
66
+
67
+
68
+ def _write_logs(
69
+ stdout_log_path: Path,
70
+ stderr_log_path: Path,
71
+ stdout_text: str,
72
+ stderr_text: str,
73
+ ) -> None:
74
+ stdout_log_path.write_text(stdout_text, encoding=UTF8_ENCODING)
75
+ stderr_log_path.write_text(stderr_text, encoding=UTF8_ENCODING)
76
+
77
+
78
+ def _incomplete_record(
79
+ check: CheckSpec,
80
+ all_arguments: tuple[str, ...],
81
+ check_directory: Path,
82
+ stdout_log_path: Path,
83
+ stderr_log_path: Path,
84
+ error_kind: str,
85
+ error_message: str,
86
+ ) -> CheckRecord:
87
+ return CheckRecord(
88
+ check.check_id,
89
+ INCOMPLETE_STATUS,
90
+ all_arguments,
91
+ str(check_directory),
92
+ None,
93
+ 0.0,
94
+ stdout_log_path,
95
+ stderr_log_path,
96
+ error_kind,
97
+ error_message,
98
+ check.minimum_tests,
99
+ )
@@ -0,0 +1,158 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import sys
5
+ from collections.abc import Sequence
6
+ from pathlib import Path
7
+ from typing import Never, TextIO
8
+
9
+ scripts_directory = Path(__file__).resolve().parents[1]
10
+ if str(scripts_directory) not in sys.path:
11
+ sys.path.insert(0, str(scripts_directory))
12
+
13
+ from local_verification.config import (
14
+ CLI_AGGREGATE_MESSAGE_TEMPLATE,
15
+ CLI_ELIGIBILITY_MESSAGE_TEMPLATE,
16
+ CLI_FAILURE_MESSAGE_TEMPLATE,
17
+ CLI_REPORT_MESSAGE_TEMPLATE,
18
+ CLI_REVISION_MESSAGE_TEMPLATE,
19
+ CLI_START_MESSAGE_TEMPLATE,
20
+ INCOMPLETE_EXIT_CODE,
21
+ INVALID_INPUT_EXIT_CODE,
22
+ PASSED_STATUS,
23
+ REPORT_NEWLINE,
24
+ UNRESOLVED_REVISION,
25
+ )
26
+ from local_verification.manifest import ManifestRunFatal, load_manifest
27
+ from local_verification.model import VerificationManifest, VerificationReport
28
+ from local_verification.runner import run_verification
29
+
30
+
31
+ class _VerificationArgumentParser(argparse.ArgumentParser):
32
+ def error(self, message: str) -> Never:
33
+ raise ManifestRunFatal(message)
34
+
35
+
36
+ def main(
37
+ all_arguments: Sequence[str],
38
+ *,
39
+ stdout: TextIO = sys.stdout,
40
+ stderr: TextIO = sys.stderr,
41
+ ) -> int:
42
+ """Run the local verification manifest.
43
+ Args:
44
+ all_arguments: Command arguments.
45
+ Returns:
46
+ The verifier exit code.
47
+ """
48
+ parser = _build_parser()
49
+ try:
50
+ parsed_arguments = parser.parse_args(all_arguments)
51
+ except ManifestRunFatal as error:
52
+ stderr.write(f"{error}{REPORT_NEWLINE}")
53
+ return INVALID_INPUT_EXIT_CODE
54
+ report_path = Path(parsed_arguments.output)
55
+ if not _clear_previous_report(report_path, stderr):
56
+ return INCOMPLETE_EXIT_CODE
57
+ _write_progress(stdout, CLI_START_MESSAGE_TEMPLATE.format(report_path=report_path))
58
+ manifest = _load_manifest_for_cli(parsed_arguments.manifest, stderr)
59
+ if manifest is None:
60
+ return INVALID_INPUT_EXIT_CODE
61
+ report = _run_manifest_verification(manifest, parsed_arguments, stdout, stderr)
62
+ if report is None:
63
+ return INCOMPLETE_EXIT_CODE
64
+ _write_report_summary(report, report_path, stdout, stderr)
65
+ return report.exit_code
66
+
67
+
68
+ def _build_parser() -> argparse.ArgumentParser:
69
+ parser = _VerificationArgumentParser(prog="cde verify", add_help=True)
70
+ parser.add_argument("--manifest", required=True)
71
+ parser.add_argument("--repo", dest="repository", required=True)
72
+ parser.add_argument("--base", dest="base_revision", required=True)
73
+ parser.add_argument("--output", required=True)
74
+ return parser
75
+
76
+
77
+ def _run_manifest_verification(
78
+ manifest: VerificationManifest,
79
+ parsed_arguments: argparse.Namespace,
80
+ stdout: TextIO,
81
+ stderr: TextIO,
82
+ ) -> VerificationReport | None:
83
+ try:
84
+ return run_verification(
85
+ manifest,
86
+ Path(parsed_arguments.repository),
87
+ parsed_arguments.base_revision,
88
+ Path(parsed_arguments.output),
89
+ progress_callback=lambda progress_message: _write_progress(
90
+ stdout, progress_message
91
+ ),
92
+ )
93
+ except (OSError, ValueError) as error:
94
+ stderr.write(f"{error}{REPORT_NEWLINE}")
95
+ return None
96
+
97
+
98
+ def _clear_previous_report(report_path: Path, stderr: TextIO) -> bool:
99
+ try:
100
+ report_path.unlink(missing_ok=True)
101
+ except OSError as error:
102
+ stderr.write(f"{error}{REPORT_NEWLINE}")
103
+ return False
104
+ return True
105
+
106
+
107
+ def _write_progress(progress_stream: TextIO, progress_message: str) -> None:
108
+ progress_stream.write(f"{progress_message}{REPORT_NEWLINE}")
109
+ progress_stream.flush()
110
+
111
+
112
+ def _write_report_summary(
113
+ report: VerificationReport,
114
+ report_path: Path,
115
+ stdout: TextIO,
116
+ stderr: TextIO,
117
+ ) -> None:
118
+ _write_progress(
119
+ stdout, CLI_AGGREGATE_MESSAGE_TEMPLATE.format(status=report.aggregate_status)
120
+ )
121
+ _write_progress(
122
+ stdout,
123
+ CLI_REVISION_MESSAGE_TEMPLATE.format(
124
+ head=_format_revision(report.head_revision),
125
+ base=_format_revision(report.base_revision),
126
+ ),
127
+ )
128
+ _write_progress(
129
+ stdout,
130
+ CLI_ELIGIBILITY_MESSAGE_TEMPLATE.format(
131
+ worktree_clean=str(report.worktree_clean).lower(),
132
+ inputs_unchanged=str(report.inputs_unchanged).lower(),
133
+ publishable=str(report.publishable).lower(),
134
+ ),
135
+ )
136
+ _write_progress(stdout, CLI_REPORT_MESSAGE_TEMPLATE.format(report_path=report_path))
137
+ if report.aggregate_status != PASSED_STATUS:
138
+ stderr.write(
139
+ f"{CLI_FAILURE_MESSAGE_TEMPLATE.format(status=report.aggregate_status)}{REPORT_NEWLINE}"
140
+ )
141
+
142
+
143
+ def _format_revision(revision: str | None) -> str:
144
+ return revision or UNRESOLVED_REVISION
145
+
146
+
147
+ def _load_manifest_for_cli(
148
+ manifest_name: str, stderr: TextIO
149
+ ) -> VerificationManifest | None:
150
+ try:
151
+ return load_manifest(Path(manifest_name))
152
+ except (ManifestRunFatal, OSError) as error:
153
+ stderr.write(f"{error}{REPORT_NEWLINE}")
154
+ return None
155
+
156
+
157
+ if __name__ == "__main__":
158
+ raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1,84 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ from pathlib import Path
5
+
6
+ from .config import (
7
+ MISSING_TOOL_ERROR_KIND,
8
+ START_ERROR_KIND,
9
+ TIMEOUT_ERROR_KIND,
10
+ UTF8_ENCODING,
11
+ )
12
+ from .model import CommandCapture
13
+
14
+
15
+ def run_command(
16
+ all_arguments: tuple[str, ...], check_directory: Path, timeout_seconds: float
17
+ ) -> CommandCapture:
18
+ """Run a local command and return its captured state.
19
+
20
+ Args:
21
+ all_arguments: Command and its arguments.
22
+ check_directory: Working directory for the command.
23
+ timeout_seconds: Wall-clock limit for the command.
24
+
25
+ Returns:
26
+ The captured exit code, output, and error kind.
27
+ """
28
+ try:
29
+ completed_process = _run_subprocess(
30
+ all_arguments, check_directory, timeout_seconds
31
+ )
32
+ except FileNotFoundError as error:
33
+ return CommandCapture(None, "", "", MISSING_TOOL_ERROR_KIND, str(error))
34
+ except subprocess.TimeoutExpired as error:
35
+ return _timeout_capture(error)
36
+ except (OSError, ValueError) as error:
37
+ return CommandCapture(None, "", "", START_ERROR_KIND, str(error))
38
+ return _completed_capture(completed_process)
39
+
40
+
41
+ def _timeout_capture(error: subprocess.TimeoutExpired) -> CommandCapture:
42
+ return CommandCapture(
43
+ None,
44
+ _decode_command_text(error.stdout),
45
+ _decode_command_text(error.stderr),
46
+ TIMEOUT_ERROR_KIND,
47
+ "Command exceeded its timeout",
48
+ )
49
+
50
+
51
+ def _completed_capture(
52
+ completed_process: subprocess.CompletedProcess[str],
53
+ ) -> CommandCapture:
54
+ return CommandCapture(
55
+ completed_process.returncode,
56
+ completed_process.stdout,
57
+ completed_process.stderr,
58
+ None,
59
+ None,
60
+ )
61
+
62
+
63
+ def _run_subprocess(
64
+ all_arguments: tuple[str, ...], check_directory: Path, timeout_seconds: float
65
+ ) -> subprocess.CompletedProcess[str]:
66
+ return subprocess.run(
67
+ all_arguments,
68
+ cwd=check_directory,
69
+ shell=False,
70
+ check=False,
71
+ capture_output=True,
72
+ text=True,
73
+ encoding=UTF8_ENCODING,
74
+ errors="replace",
75
+ timeout=timeout_seconds,
76
+ )
77
+
78
+
79
+ def _decode_command_text(command_text: str | bytes | None) -> str:
80
+ if command_text is None:
81
+ return ""
82
+ if isinstance(command_text, bytes):
83
+ return command_text.decode(UTF8_ENCODING, errors="replace")
84
+ return command_text
@@ -0,0 +1 @@
1
+ from .constants import *