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,348 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+
10
+ _SCRIPTS_DIRECTORY = Path(__file__).resolve().parents[1]
11
+ if str(_SCRIPTS_DIRECTORY) not in sys.path:
12
+ sys.path.insert(0, str(_SCRIPTS_DIRECTORY))
13
+
14
+ from local_verification import check_runner
15
+ from local_verification.config.constants import (
16
+ COLLECTION_ERROR_KIND,
17
+ FAILED_STATUS,
18
+ NONZERO_EXIT_ERROR_KIND,
19
+ )
20
+ from local_verification.manifest import (
21
+ ManifestRunFatal,
22
+ compute_manifest_digest,
23
+ load_manifest,
24
+ )
25
+ from local_verification.model import (
26
+ CheckSpec,
27
+ CommandCapture,
28
+ ExclusionSpec,
29
+ VerificationManifest,
30
+ VerificationReport,
31
+ )
32
+ from local_verification.runner import run_verification
33
+
34
+
35
+ def _manifest(*checks: CheckSpec) -> VerificationManifest:
36
+ return VerificationManifest(1, tuple(checks), ())
37
+
38
+
39
+ def _python_check(check_id: str, script_text: str, cwd: str = ".") -> CheckSpec:
40
+ return CheckSpec(check_id, (sys.executable, "-c", script_text), cwd, 10.0)
41
+
42
+
43
+ def _run_git(repository_path: Path, *all_arguments: str) -> str:
44
+ completed_process = subprocess.run(
45
+ ["git", "-C", str(repository_path), *all_arguments],
46
+ capture_output=True,
47
+ check=False,
48
+ text=True,
49
+ )
50
+ assert completed_process.returncode == 0, completed_process.stderr
51
+ return completed_process.stdout.strip()
52
+
53
+
54
+ def _create_git_repository(tmp_path: Path) -> tuple[Path, str]:
55
+ repository_path = tmp_path / "repository"
56
+ repository_path.mkdir()
57
+ _run_git(repository_path, "init", "--quiet")
58
+ _run_git(repository_path, "config", "user.email", "tests@example.com")
59
+ _run_git(repository_path, "config", "user.name", "Local Verification Tests")
60
+ (repository_path / "tracked.txt").write_text("stable\n", encoding="utf-8")
61
+ _run_git(repository_path, "add", "tracked.txt")
62
+ _run_git(repository_path, "commit", "--quiet", "-m", "initial")
63
+ return repository_path, _run_git(repository_path, "rev-parse", "HEAD")
64
+
65
+
66
+ def _assert_check_outputs(report: VerificationReport, output_path: Path) -> None:
67
+ assert report.aggregate_status == "failed"
68
+ assert [each_record.status for each_record in report.checks] == [
69
+ "passed",
70
+ "failed",
71
+ "incomplete",
72
+ "incomplete",
73
+ ]
74
+ assert report.checks[1].exit_code == 7
75
+ assert report.checks[2].error_kind == "missing_tool"
76
+ assert report.checks[3].error_kind == "timeout"
77
+ assert output_path.exists()
78
+ for each_record in report.checks:
79
+ assert each_record.stdout_log.exists()
80
+ assert each_record.stderr_log.exists()
81
+ run_log_path = output_path.with_suffix(".logs") / "run.log"
82
+ assert "aggregate: failed" in run_log_path.read_text(encoding="utf-8")
83
+
84
+
85
+ def test_load_manifest_requires_unique_check_ids(tmp_path: Path) -> None:
86
+ manifest_path = tmp_path / "manifest.json"
87
+ manifest_path.write_text(
88
+ json.dumps(
89
+ {
90
+ "version": 1,
91
+ "checks": [
92
+ {
93
+ "id": "same",
94
+ "argv": ["python"],
95
+ "cwd": ".",
96
+ "timeout_seconds": 1,
97
+ },
98
+ {
99
+ "id": "same",
100
+ "argv": ["python"],
101
+ "cwd": ".",
102
+ "timeout_seconds": 1,
103
+ },
104
+ ],
105
+ }
106
+ ),
107
+ encoding="utf-8",
108
+ )
109
+
110
+ with pytest.raises(ManifestRunFatal, match="unique"):
111
+ load_manifest(manifest_path)
112
+
113
+
114
+ def test_load_manifest_preserves_exclusions_and_collection_floor(
115
+ tmp_path: Path,
116
+ ) -> None:
117
+ manifest_path = tmp_path / "manifest.json"
118
+ manifest_path.write_text(
119
+ json.dumps(
120
+ {
121
+ "version": 1,
122
+ "checks": [
123
+ {
124
+ "id": "tests",
125
+ "argv": ["python", "-m", "pytest"],
126
+ "cwd": "packages/app",
127
+ "timeout_seconds": 2,
128
+ "minimum_tests": 3,
129
+ }
130
+ ],
131
+ "exclusions": [{"selector": "manual", "reason": "requires a human"}],
132
+ }
133
+ ),
134
+ encoding="utf-8",
135
+ )
136
+
137
+ loaded_manifest = load_manifest(manifest_path)
138
+
139
+ assert loaded_manifest.checks[0].minimum_tests == 3
140
+ assert loaded_manifest.checks[0].cwd == "packages/app"
141
+ assert loaded_manifest.exclusions == (ExclusionSpec("manual", "requires a human"),)
142
+
143
+
144
+ def test_run_verification_executes_pass_fail_missing_and_timeout_checks(
145
+ tmp_path: Path,
146
+ ) -> None:
147
+ all_checks = (
148
+ _python_check("passes", ""),
149
+ _python_check("fails", "raise SystemExit(7)"),
150
+ CheckSpec("missing", ("command-that-does-not-exist",), ".", 10.0),
151
+ _python_check("times-out", "import time; time.sleep(2)"),
152
+ )
153
+ timed_check = CheckSpec("times-out", all_checks[-1].command_arguments, ".", 0.1)
154
+ output_path = tmp_path / "reports" / "verification.json"
155
+
156
+ report = run_verification(
157
+ _manifest(all_checks[0], all_checks[1], all_checks[2], timed_check),
158
+ tmp_path,
159
+ "base-sha",
160
+ output_path,
161
+ )
162
+
163
+ _assert_check_outputs(report, output_path)
164
+
165
+
166
+ def test_run_verification_keeps_argv_spaces_and_runs_outside_caller_directory(
167
+ tmp_path: Path,
168
+ ) -> None:
169
+ repository_path = tmp_path / "repository with spaces"
170
+ repository_path.mkdir()
171
+ script_path = repository_path / "folder with spaces" / "check.py"
172
+ script_path.parent.mkdir()
173
+ script_path.write_text("raise SystemExit(0)\n", encoding="utf-8")
174
+ check = CheckSpec("spaces", (sys.executable, str(script_path)), ".", 10.0)
175
+
176
+ report = run_verification(
177
+ _manifest(check),
178
+ repository_path,
179
+ "base-sha",
180
+ tmp_path / "verification.json",
181
+ )
182
+
183
+ assert report.aggregate_status == "passed"
184
+ assert report.checks[0].command_arguments == (sys.executable, str(script_path))
185
+
186
+
187
+ def test_run_verification_enforces_minimum_tests_for_pytest(tmp_path: Path) -> None:
188
+ empty_tests = tmp_path / "tests"
189
+ empty_tests.mkdir()
190
+ check = CheckSpec(
191
+ "pytest",
192
+ (sys.executable, "-m", "pytest", "tests"),
193
+ ".",
194
+ 30.0,
195
+ 1,
196
+ )
197
+
198
+ report = run_verification(
199
+ _manifest(check),
200
+ tmp_path,
201
+ "base-sha",
202
+ tmp_path / "verification.json",
203
+ )
204
+
205
+ assert report.aggregate_status == "failed"
206
+ assert report.checks[0].status == "failed"
207
+ assert report.checks[0].error_kind == "minimum_tests"
208
+ assert report.checks[0].collected_tests == 0
209
+
210
+
211
+ def test_run_verification_rejects_all_required_tests_skipped(tmp_path: Path) -> None:
212
+ skipped_test_path = tmp_path / "test_skipped.py"
213
+ skipped_test_path.write_text(
214
+ """import pytest
215
+
216
+ @pytest.mark.skip(reason="requires service")
217
+ def test_required_service():
218
+ pass
219
+ """,
220
+ encoding="utf-8",
221
+ )
222
+ check = CheckSpec(
223
+ "pytest",
224
+ (sys.executable, "-m", "pytest", "test_skipped.py"),
225
+ ".",
226
+ 30.0,
227
+ 1,
228
+ )
229
+
230
+ report = run_verification(
231
+ _manifest(check),
232
+ tmp_path,
233
+ "base-sha",
234
+ tmp_path / "verification.json",
235
+ )
236
+
237
+ assert report.aggregate_status == "incomplete"
238
+ assert report.checks[0].error_kind == "skipped_tests"
239
+ assert report.checks[0].collected_tests == 1
240
+ assert report.checks[0].exit_code == 0
241
+
242
+
243
+ def test_run_verification_records_publishable_clean_git_revision(
244
+ tmp_path: Path,
245
+ ) -> None:
246
+ repository_path, base_revision = _create_git_repository(tmp_path)
247
+ check = _python_check("passes", "")
248
+ manifest = _manifest(check)
249
+ output_path = tmp_path / "verification.json"
250
+
251
+ report = run_verification(
252
+ manifest,
253
+ repository_path,
254
+ base_revision,
255
+ output_path,
256
+ )
257
+
258
+ assert report.head_revision == base_revision
259
+ assert report.base_revision == base_revision
260
+ assert report.manifest_digest == compute_manifest_digest(manifest)
261
+ assert report.worktree_clean is True
262
+ assert report.inputs_unchanged is True
263
+ assert report.publishable is True
264
+ report_mapping = json.loads(output_path.read_text(encoding="utf-8"))
265
+ assert report_mapping["head"] == base_revision
266
+ assert report_mapping["base"] == base_revision
267
+ assert report_mapping["manifest_digest"] == report.manifest_digest
268
+ assert report_mapping["publishable"] is True
269
+
270
+
271
+ def test_run_verification_keeps_dirty_git_candidate_advisory(
272
+ tmp_path: Path,
273
+ ) -> None:
274
+ repository_path, base_revision = _create_git_repository(tmp_path)
275
+ (repository_path / "untracked.txt").write_text("local\n", encoding="utf-8")
276
+
277
+ report = run_verification(
278
+ _manifest(_python_check("passes", "")),
279
+ repository_path,
280
+ base_revision,
281
+ tmp_path / "verification.json",
282
+ )
283
+
284
+ assert report.aggregate_status == "passed"
285
+ assert report.worktree_clean is False
286
+ assert report.inputs_unchanged is True
287
+ assert report.publishable is False
288
+
289
+
290
+ def test_run_verification_invalidates_git_candidate_when_head_changes(
291
+ tmp_path: Path,
292
+ ) -> None:
293
+ repository_path, base_revision = _create_git_repository(tmp_path)
294
+ tracked_path = repository_path / "tracked.txt"
295
+ commit_script = (
296
+ "from pathlib import Path; import subprocess; "
297
+ f"Path({str(tracked_path)!r}).write_text('changed\\n'); "
298
+ f"subprocess.run(['git', '-C', {str(repository_path)!r}, 'add', 'tracked.txt'], check=True); "
299
+ f"subprocess.run(['git', '-C', {str(repository_path)!r}, 'commit', '--quiet', '-m', 'check'], check=True)"
300
+ )
301
+
302
+ report = run_verification(
303
+ _manifest(_python_check("changes-head", commit_script)),
304
+ repository_path,
305
+ base_revision,
306
+ tmp_path / "verification.json",
307
+ )
308
+
309
+ assert report.aggregate_status == "passed"
310
+ assert report.head_revision == base_revision
311
+ assert _run_git(repository_path, "rev-parse", "HEAD") != base_revision
312
+ assert report.inputs_unchanged is False
313
+ assert report.publishable is False
314
+
315
+
316
+ def test_run_verification_never_publishes_failed_or_incomplete_git_checks(
317
+ tmp_path: Path,
318
+ ) -> None:
319
+ repository_path, base_revision = _create_git_repository(tmp_path)
320
+ all_checks = (
321
+ _python_check("fails", "raise SystemExit(4)"),
322
+ CheckSpec("missing", ("missing-command",), ".", 10.0),
323
+ )
324
+
325
+ report = run_verification(
326
+ _manifest(*all_checks),
327
+ repository_path,
328
+ base_revision,
329
+ tmp_path / "verification.json",
330
+ )
331
+
332
+ assert report.aggregate_status == "failed"
333
+ assert [each_record.status for each_record in report.checks] == [
334
+ "failed",
335
+ "incomplete",
336
+ ]
337
+ assert report.publishable is False
338
+
339
+
340
+ def test_failed_command_keeps_failed_status_when_collection_also_fails() -> None:
341
+ check = CheckSpec("unit", ("pytest",), ".", 10.0, minimum_tests=1)
342
+ failed_collection = CommandCapture(1, "", "", COLLECTION_ERROR_KIND, "no collect")
343
+
344
+ status, error_kind, _ = check_runner._classify_collection(
345
+ check, (failed_collection, None), FAILED_STATUS, NONZERO_EXIT_ERROR_KIND, "x"
346
+ )
347
+
348
+ assert (status, error_kind) == (FAILED_STATUS, NONZERO_EXIT_ERROR_KIND)
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ import io
4
+ import json
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ SCRIPTS_DIRECTORY = Path(__file__).resolve().parents[1]
9
+ if str(SCRIPTS_DIRECTORY) not in sys.path:
10
+ sys.path.insert(0, str(SCRIPTS_DIRECTORY))
11
+
12
+ from local_verification.cli import main
13
+
14
+
15
+ def _write_failure_manifest(tmp_path: Path) -> Path:
16
+ manifest_path = tmp_path / "manifest.json"
17
+ manifest_path.write_text(
18
+ json.dumps(
19
+ {
20
+ "version": 1,
21
+ "checks": [
22
+ {
23
+ "id": "fails",
24
+ "argv": [sys.executable, "-c", "raise SystemExit(4)"],
25
+ "cwd": ".",
26
+ "timeout_seconds": 10,
27
+ }
28
+ ],
29
+ }
30
+ ),
31
+ encoding="utf-8",
32
+ )
33
+ return manifest_path
34
+
35
+
36
+ def _run_failure_cli(tmp_path: Path) -> tuple[int, str, str, Path]:
37
+ manifest_path = _write_failure_manifest(tmp_path)
38
+ output_path = tmp_path / "verification.json"
39
+ standard_output = io.StringIO()
40
+ standard_error = io.StringIO()
41
+ exit_code = main(
42
+ [
43
+ "--manifest",
44
+ str(manifest_path),
45
+ "--repo",
46
+ str(tmp_path),
47
+ "--base",
48
+ "base-sha",
49
+ "--output",
50
+ str(output_path),
51
+ ],
52
+ stdout=standard_output,
53
+ stderr=standard_error,
54
+ )
55
+ return exit_code, standard_output.getvalue(), standard_error.getvalue(), output_path
56
+
57
+
58
+ def test_cli_writes_json_and_returns_failure_code(tmp_path: Path) -> None:
59
+ exit_code, standard_output, standard_error, output_path = _run_failure_cli(tmp_path)
60
+
61
+ assert exit_code == 1
62
+ assert "check start: fails" in standard_output
63
+ assert "aggregate: failed" in standard_output
64
+ assert "revision: head=unresolved base=unresolved" in standard_output
65
+ assert (
66
+ "eligibility: clean=false unchanged=false publishable=false" in standard_output
67
+ )
68
+ assert f"report: {output_path}" in standard_output
69
+ assert "verification failed" in standard_error
70
+ assert (
71
+ json.loads(output_path.read_text(encoding="utf-8"))["aggregate"]["status"]
72
+ == "failed"
73
+ )
74
+
75
+
76
+ def test_cli_rejects_zero_checks_without_writing_success(tmp_path: Path) -> None:
77
+ manifest_path = tmp_path / "manifest.json"
78
+ manifest_path.write_text(json.dumps({"version": 1, "checks": []}), encoding="utf-8")
79
+ output_path = tmp_path / "verification.json"
80
+ output_path.write_text(json.dumps({"publishable": True}), encoding="utf-8")
81
+
82
+ exit_code = main(
83
+ [
84
+ "--manifest",
85
+ str(manifest_path),
86
+ "--repo",
87
+ str(tmp_path),
88
+ "--base",
89
+ "base-sha",
90
+ "--output",
91
+ str(output_path),
92
+ ],
93
+ stdout=io.StringIO(),
94
+ stderr=io.StringIO(),
95
+ )
96
+
97
+ assert exit_code == 2
98
+ assert not output_path.exists()
@@ -0,0 +1,120 @@
1
+ """Archive boundaries for automatic and explicitly requested policy checks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import subprocess
7
+ from pathlib import Path, PurePosixPath
8
+
9
+ import pytest
10
+ from policy_lint.engine import _runtime_document_set, lint
11
+ from policy_lint.model import (
12
+ Diagnostic,
13
+ Document,
14
+ DocumentRule,
15
+ DocumentSet,
16
+ LintRequest,
17
+ Location,
18
+ SelectionKind,
19
+ Severity,
20
+ TextDocument,
21
+ )
22
+
23
+
24
+ def _git(repository_root: Path, *arguments: str) -> None:
25
+ environment = {
26
+ variable_name: variable_text
27
+ for variable_name, variable_text in os.environ.items()
28
+ if not variable_name.upper().startswith("GIT_")
29
+ }
30
+ subprocess.run(
31
+ ["git", *arguments], cwd=repository_root, env=environment,
32
+ check=True, capture_output=True, text=True,
33
+ )
34
+
35
+
36
+ @pytest.fixture
37
+ def archive_repository(tmp_path: Path) -> Path:
38
+ _git(tmp_path, "init", "--quiet", "-b", "main")
39
+ _git(tmp_path, "config", "user.name", "Archive Tests")
40
+ _git(tmp_path, "config", "user.email", "archive@example.invalid")
41
+ _git(tmp_path, "config", "commit.gpgsign", "false")
42
+ for relative_path in ("active.py", "skill-archive/old.py", "src/skill-archive/live.py"):
43
+ file_path = tmp_path / relative_path
44
+ file_path.parent.mkdir(parents=True, exist_ok=True)
45
+ file_path.write_text("first\n", encoding="utf-8")
46
+ _git(tmp_path, "add", ".")
47
+ _git(tmp_path, "commit", "--quiet", "-m", "archive fixture")
48
+ for file_path in tmp_path.rglob("*.py"):
49
+ file_path.write_text("second\n", encoding="utf-8")
50
+ _git(tmp_path, "add", ".")
51
+ return tmp_path
52
+
53
+
54
+ def _marker_diagnostics(document: Document, repository_root: Path) -> tuple[Diagnostic, ...]:
55
+ assert (repository_root / document.path).is_file()
56
+ return (Diagnostic("marker", Severity.ERROR, "Checked document", Location(document.path, 1)),)
57
+
58
+
59
+ def _accept_document(document: Document) -> bool:
60
+ return bool(document.path.parts)
61
+
62
+
63
+ def _marker_rule() -> DocumentRule:
64
+ return DocumentRule(
65
+ "marker", frozenset({"changed", "repository"}),
66
+ _accept_document, _marker_diagnostics,
67
+ )
68
+
69
+
70
+ @pytest.mark.parametrize("selection", (SelectionKind.STAGED, SelectionKind.BASE, SelectionKind.REPOSITORY))
71
+ def test_automatic_checks_keep_live_code_and_exclude_only_root_archive(
72
+ archive_repository: Path, selection: SelectionKind,
73
+ ) -> None:
74
+ requests = {
75
+ SelectionKind.STAGED: LintRequest.staged(archive_repository),
76
+ SelectionKind.BASE: LintRequest.base(archive_repository, "HEAD"),
77
+ SelectionKind.REPOSITORY: LintRequest.repository(archive_repository),
78
+ }
79
+ report = lint(requests[selection], all_registry=(_marker_rule(),))
80
+ assert report.checked_documents == (
81
+ PurePosixPath("active.py"), PurePosixPath("src/skill-archive/live.py"),
82
+ )
83
+ assert len(report.diagnostics) == 2
84
+ assert report.executed_rules == ("marker",)
85
+ assert report.exit_code == 1
86
+
87
+
88
+ @pytest.mark.parametrize("selection", (SelectionKind.FILES, SelectionKind.TEXT))
89
+ def test_explicit_archive_checks_still_execute_rules(
90
+ archive_repository: Path, selection: SelectionKind,
91
+ ) -> None:
92
+ archive_path = Path("skill-archive/old.py")
93
+ request = (
94
+ LintRequest.files(archive_repository, [archive_path])
95
+ if selection is SelectionKind.FILES
96
+ else LintRequest(archive_repository, TextDocument(archive_path, "editor text\n"))
97
+ )
98
+ report = lint(request, all_registry=(_marker_rule(),))
99
+ assert report.checked_documents == (PurePosixPath("skill-archive/old.py"),)
100
+ assert len(report.diagnostics) == 1
101
+ assert report.exit_code == 1
102
+
103
+
104
+ def test_retirement_and_restoration_preserve_live_change_metadata(tmp_path: Path) -> None:
105
+ retired_path = PurePosixPath("active/retired.py")
106
+ archive_path = PurePosixPath("skill-archive/retired.py")
107
+ restored_source = PurePosixPath("skill-archive/restored.py")
108
+ restored_path = PurePosixPath("active/restored.py")
109
+ deleted_path = PurePosixPath("active/deleted.py")
110
+ document_set = DocumentSet(
111
+ (Document.from_text(archive_path, "archived"), Document.from_text(restored_path, "restored")),
112
+ SelectionKind.BASE,
113
+ tmp_path,
114
+ (deleted_path, PurePosixPath("skill-archive/deleted.py")),
115
+ ((retired_path, archive_path), (restored_source, restored_path)),
116
+ )
117
+ runtime_set = _runtime_document_set(document_set)
118
+ assert tuple(document.path for document in runtime_set.documents) == (restored_path,)
119
+ assert runtime_set.deleted_paths == (deleted_path, retired_path)
120
+ assert runtime_set.renamed_paths == ((restored_source, restored_path),)
@@ -10,7 +10,7 @@ from pathlib import Path, PurePosixPath
10
10
  import pytest
11
11
  from policy_lint import adapters
12
12
  from policy_lint.config.constants import INCOMPLETE_EXIT_CODE
13
- from policy_lint.engine import lint
13
+ from policy_lint.engine import _runtime_document_set, lint
14
14
  from policy_lint.model import (
15
15
  ContentOrigin,
16
16
  Diagnostic,
@@ -309,7 +309,11 @@ def test_state_description_diagnostics_maps_and_scopes_phrases(
309
309
  lint_report = _lint_documents(
310
310
  tmp_path,
311
311
  (source_document,),
312
- (_changed_document_rule("state-description", adapters.state_description_diagnostics),),
312
+ (
313
+ _changed_document_rule(
314
+ "state-description", adapters.state_description_diagnostics
315
+ ),
316
+ ),
313
317
  )
314
318
  assert len(lint_report.diagnostics) == 1
315
319
  assert lint_report.diagnostics[0].location is not None
@@ -351,8 +355,12 @@ def test_failed_adapter_rolls_back_continues_and_exits_incomplete(
351
355
  _source_document("pkg/second.py"),
352
356
  )
353
357
  all_rules = (
354
- _changed_document_rule("flaky", lambda document, _root: _flaky_diagnostics(document)),
355
- _changed_document_rule("stable", lambda document, _root: _stable_diagnostics(document)),
358
+ _changed_document_rule(
359
+ "flaky", lambda document, _root: _flaky_diagnostics(document)
360
+ ),
361
+ _changed_document_rule(
362
+ "stable", lambda document, _root: _stable_diagnostics(document)
363
+ ),
356
364
  )
357
365
  lint_report = _lint_documents(repository_root, all_documents, all_rules)
358
366
  assert lint_report.failed_rules == ("flaky",)
@@ -379,3 +387,16 @@ def test_process_interrupt_is_not_swallowed(tmp_path: Path) -> None:
379
387
  (_source_document("pkg/mod.py"),),
380
388
  (_changed_document_rule("interrupt", interrupt_diagnostics),),
381
389
  )
390
+
391
+
392
+ def test_runtime_document_set_keeps_base_revision_for_base_selection(
393
+ tmp_path: Path,
394
+ ) -> None:
395
+ document_set = DocumentSet(
396
+ (_source_document("pkg/mod.py"),),
397
+ SelectionKind.BASE,
398
+ tmp_path,
399
+ base_revision="abc123def",
400
+ )
401
+ runtime_document_set = _runtime_document_set(document_set)
402
+ assert runtime_document_set.base_revision == "abc123def"
@@ -0,0 +1,29 @@
1
+ """Load terminology through the verified installed shared-tree resolver."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import pytest
9
+ from policy_lint import adapters
10
+
11
+
12
+ def test_pr_loop_loader_uses_the_shared_tree_resolver(
13
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
14
+ ) -> None:
15
+ module_name = "policy_fixture_terminology"
16
+ (tmp_path / f"{module_name}.py").write_text("IS_FIXTURE = True\n", encoding="utf-8")
17
+ all_calls: list[tuple[object, ...]] = []
18
+
19
+ def resolve_directory(*all_arguments: object) -> Path:
20
+ all_calls.append(all_arguments)
21
+ return tmp_path
22
+
23
+ monkeypatch.setattr(adapters, "resolve_shared_scripts_directory", resolve_directory)
24
+ monkeypatch.setattr(sys, "path", list(sys.path))
25
+ loaded = adapters._pr_loop_script_module(module_name)
26
+ assert loaded.IS_FIXTURE is True
27
+ assert Path(loaded.__file__).parent == tmp_path
28
+ assert all_calls[0][2:] == ("pr-loop", f"{module_name}.py", 2)
29
+ monkeypatch.delitem(sys.modules, module_name)