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,163 @@
1
+ """Test pairing behavior for changed source documents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path, PurePosixPath
6
+
7
+ from policy_lint import adapters
8
+ from policy_lint.config.approved_test_pairs import (
9
+ APPROVED_TEST_PATHS_BY_PRODUCTION_PATH,
10
+ )
11
+ from policy_lint.model import (
12
+ ContentOrigin,
13
+ Diagnostic,
14
+ Document,
15
+ DocumentSet,
16
+ SelectionKind,
17
+ )
18
+
19
+ _MODULE_DOCSTRING_BEFORE = '''"""Before."""
20
+
21
+ def work() -> None:
22
+ """Old function documentation."""
23
+ return None
24
+ '''
25
+ _MODULE_DOCSTRING_AFTER = '''"""After."""
26
+
27
+ def work() -> None:
28
+ """Updated function documentation."""
29
+ return None
30
+ '''
31
+ _BODY_BEFORE = '''"""Before."""
32
+
33
+ def work() -> None:
34
+ return None
35
+ '''
36
+ _BODY_AFTER = '''"""After."""
37
+
38
+ def work() -> None:
39
+ return 1
40
+ '''
41
+
42
+
43
+ def _changed_document(
44
+ current_source: str, prior_source: str, suffix: str = ".py"
45
+ ) -> Document:
46
+ return Document(
47
+ PurePosixPath(f"src/feature{suffix}"),
48
+ current_source,
49
+ prior_source,
50
+ frozenset({1}),
51
+ ContentOrigin.REVISION_DIFF,
52
+ )
53
+
54
+
55
+ def _pairing_diagnostics(
56
+ temporary_path: Path, changed_document: Document
57
+ ) -> tuple[Diagnostic, ...]:
58
+ document_set = DocumentSet((changed_document,), SelectionKind.BASE, temporary_path)
59
+ return adapters.test_pairing_diagnostics(document_set)
60
+
61
+
62
+ def test_approved_pairs_name_files_that_exist() -> None:
63
+ repository_root = Path(__file__).resolve().parents[4]
64
+ for (
65
+ production_path,
66
+ all_test_paths,
67
+ ) in APPROVED_TEST_PATHS_BY_PRODUCTION_PATH.items():
68
+ assert (repository_root / production_path).is_file(), production_path
69
+ for each_test_path in all_test_paths:
70
+ assert (repository_root / each_test_path).is_file(), each_test_path
71
+
72
+
73
+ def test_pairing_ignores_python_docstring_only_changes(tmp_path: Path) -> None:
74
+ all_diagnostics = _pairing_diagnostics(
75
+ tmp_path,
76
+ _changed_document(_MODULE_DOCSTRING_AFTER, _MODULE_DOCSTRING_BEFORE),
77
+ )
78
+ assert all_diagnostics == ()
79
+
80
+
81
+ def test_pairing_requires_a_test_for_python_body_changes(tmp_path: Path) -> None:
82
+ all_diagnostics = _pairing_diagnostics(
83
+ tmp_path,
84
+ _changed_document(_BODY_AFTER, _BODY_BEFORE),
85
+ )
86
+ assert len(all_diagnostics) == 1
87
+
88
+
89
+ def test_pairing_rejects_python_syntax_errors_without_a_test(tmp_path: Path) -> None:
90
+ all_diagnostics = _pairing_diagnostics(
91
+ tmp_path,
92
+ _changed_document("def work(:\n return None\n", _BODY_BEFORE),
93
+ )
94
+ assert len(all_diagnostics) == 1
95
+
96
+
97
+ def test_pairing_keeps_non_python_changes_on_existing_matching_rules(
98
+ tmp_path: Path,
99
+ ) -> None:
100
+ all_diagnostics = _pairing_diagnostics(
101
+ tmp_path,
102
+ _changed_document(
103
+ "export const work = 1;\n", "export const work = 2;\n", ".ts"
104
+ ),
105
+ )
106
+ assert len(all_diagnostics) == 1
107
+
108
+
109
+ _APPROVED_PRODUCTION_PATH = PurePosixPath(
110
+ "packages/claude-dev-env/scripts/automatic_advisory/state.py"
111
+ )
112
+ _APPROVED_TEST_PATH = PurePosixPath(
113
+ "packages/claude-dev-env/scripts/tests/test_closed_pr_label.py"
114
+ )
115
+ _UNRELATED_PRODUCTION_PATH = PurePosixPath(
116
+ "packages/claude-dev-env/scripts/automatic_advisory/new_module.py"
117
+ )
118
+
119
+
120
+ def _body_change_at(path: PurePosixPath) -> Document:
121
+ return Document(
122
+ path, _BODY_AFTER, _BODY_BEFORE, frozenset({4}), ContentOrigin.REVISION_DIFF
123
+ )
124
+
125
+
126
+ def _diagnostic_paths(
127
+ temporary_path: Path, *all_documents: Document
128
+ ) -> tuple[PurePosixPath, ...]:
129
+ document_set = DocumentSet(all_documents, SelectionKind.BASE, temporary_path)
130
+ return tuple(
131
+ each_diagnostic.location.path
132
+ for each_diagnostic in adapters.test_pairing_diagnostics(document_set)
133
+ )
134
+
135
+
136
+ def test_pairing_accepts_an_approved_module_with_its_changed_suite(
137
+ tmp_path: Path,
138
+ ) -> None:
139
+ all_paths = _diagnostic_paths(
140
+ tmp_path,
141
+ _body_change_at(_APPROVED_PRODUCTION_PATH),
142
+ _body_change_at(_APPROVED_TEST_PATH),
143
+ )
144
+ assert all_paths == ()
145
+
146
+
147
+ def test_pairing_rejects_an_approved_module_without_its_suite(
148
+ tmp_path: Path,
149
+ ) -> None:
150
+ all_paths = _diagnostic_paths(tmp_path, _body_change_at(_APPROVED_PRODUCTION_PATH))
151
+ assert all_paths == (_APPROVED_PRODUCTION_PATH,)
152
+
153
+
154
+ def test_pairing_rejects_an_unrelated_module_beside_a_changed_approved_suite(
155
+ tmp_path: Path,
156
+ ) -> None:
157
+ all_paths = _diagnostic_paths(
158
+ tmp_path,
159
+ _body_change_at(_APPROVED_PRODUCTION_PATH),
160
+ _body_change_at(_UNRELATED_PRODUCTION_PATH),
161
+ _body_change_at(_APPROVED_TEST_PATH),
162
+ )
163
+ assert all_paths == (_UNRELATED_PRODUCTION_PATH,)
@@ -15,6 +15,7 @@ from policy_lint.model import (
15
15
  Document,
16
16
  DocumentRule,
17
17
  DocumentSet,
18
+ LintReport,
18
19
  LintRequest,
19
20
  SelectionKind,
20
21
  )
@@ -96,6 +97,164 @@ def _terminology_rule() -> ChangeSetRule:
96
97
  return terminology_rule
97
98
 
98
99
 
100
+ def _head_revision(repository_root: Path) -> str:
101
+ completed = subprocess.run(
102
+ ("git", "rev-parse", "HEAD"),
103
+ cwd=repository_root,
104
+ check=True,
105
+ capture_output=True,
106
+ text=True,
107
+ )
108
+ return completed.stdout.strip()
109
+
110
+
111
+ def _git_add(repository_root: Path, *all_paths: str) -> None:
112
+ subprocess.run(
113
+ ("git", "add", *all_paths),
114
+ cwd=repository_root,
115
+ check=True,
116
+ capture_output=True,
117
+ )
118
+
119
+
120
+ def _quota_document_paths(
121
+ repository_root: Path,
122
+ source_name: str = "quota.py",
123
+ documentation_name: str = "README.md",
124
+ ) -> tuple[Path, Path]:
125
+ source_path = repository_root / "src" / source_name
126
+ documentation_path = repository_root / "docs" / documentation_name
127
+ source_path.parent.mkdir(parents=True, exist_ok=True)
128
+ documentation_path.parent.mkdir(parents=True, exist_ok=True)
129
+ return source_path, documentation_path
130
+
131
+
132
+ def _write_text(file_path: Path, text: str) -> None:
133
+ file_path.write_text(text, encoding="utf-8")
134
+
135
+
136
+ def _commit_quota_base(
137
+ repository_root: Path,
138
+ source_text: str,
139
+ documentation_text: str,
140
+ source_name: str = "quota.py",
141
+ documentation_name: str = "README.md",
142
+ ) -> tuple[Path, Path, str]:
143
+ _ensure_git_repository(repository_root)
144
+ source_path, documentation_path = _quota_document_paths(
145
+ repository_root, source_name, documentation_name
146
+ )
147
+ _write_text(source_path, source_text)
148
+ _write_text(documentation_path, documentation_text)
149
+ _commit_repository(repository_root, "base")
150
+ return source_path, documentation_path, _head_revision(repository_root)
151
+
152
+
153
+ def _commit_identifier_then_documentation(
154
+ repository_root: Path,
155
+ source_path: Path,
156
+ documentation_path: Path,
157
+ ) -> None:
158
+ _write_text(source_path, "baseline = 1\npremium_request_interactions = 5\n")
159
+ _commit_repository(repository_root, "identifier")
160
+ _write_text(
161
+ documentation_path,
162
+ "The premium-request-budget field gates the run.\n",
163
+ )
164
+ _commit_repository(repository_root, "documentation")
165
+
166
+
167
+ def _committed_near_miss_from_base(
168
+ repository_root: Path,
169
+ ) -> tuple[Path, Path, str]:
170
+ source_path, documentation_path, comparison_revision = _commit_quota_base(
171
+ repository_root,
172
+ "baseline = 1\n",
173
+ "Baseline quota text.\n",
174
+ )
175
+ _commit_identifier_then_documentation(
176
+ repository_root, source_path, documentation_path
177
+ )
178
+ return source_path, documentation_path, comparison_revision
179
+
180
+
181
+ def _stage_unrelated_index_file(repository_root: Path) -> None:
182
+ _write_text(repository_root / "staged.txt", "unrelated index content\n")
183
+ _git_add(repository_root, "--", "staged.txt")
184
+
185
+
186
+ def _worktree_and_index_near_miss_repository(repository_root: Path) -> str:
187
+ source_path, documentation_path, comparison_revision = (
188
+ _committed_near_miss_from_base(repository_root)
189
+ )
190
+ _write_text(
191
+ source_path,
192
+ "baseline = 1\npremium_request_interactions = 5\nanother_request_total = 2\n",
193
+ )
194
+ _write_text(
195
+ documentation_path,
196
+ "The another-request-budget field gates the run.\n",
197
+ )
198
+ _git_add(repository_root, "-A")
199
+ _write_text(
200
+ documentation_path,
201
+ "The another-request-total field gates the run.\n",
202
+ )
203
+ return comparison_revision
204
+
205
+
206
+ def _existing_identifier_from_base(repository_root: Path) -> str:
207
+ source_path, documentation_path, comparison_revision = _commit_quota_base(
208
+ repository_root,
209
+ "premium_request_interactions = 5\n",
210
+ "Baseline quota text.\n",
211
+ )
212
+ _write_text(
213
+ source_path,
214
+ "premium_request_interactions = 5\npremium_request_interactions = 6\n",
215
+ )
216
+ _write_text(
217
+ documentation_path,
218
+ "The premium-request-budget field gates the run.\n",
219
+ )
220
+ _commit_repository(repository_root, "existing identifier")
221
+ return comparison_revision
222
+
223
+
224
+ def _renamed_and_deleted_near_miss_from_base(repository_root: Path) -> str:
225
+ old_source_path, deleted_documentation_path, comparison_revision = (
226
+ _commit_quota_base(
227
+ repository_root,
228
+ "baseline = 1\n",
229
+ "Obsolete quota text.\n",
230
+ "old_quota.py",
231
+ "obsolete.md",
232
+ )
233
+ )
234
+ new_source_path = repository_root / "src" / "new_quota.py"
235
+ subprocess.run(
236
+ ("git", "mv", "--", str(old_source_path), str(new_source_path)),
237
+ cwd=repository_root,
238
+ check=True,
239
+ capture_output=True,
240
+ )
241
+ _write_text(new_source_path, "baseline = 1\npremium_request_interactions = 5\n")
242
+ deleted_documentation_path.unlink()
243
+ _write_text(
244
+ repository_root / "docs" / "README.md",
245
+ "The premium-request-budget field gates the run.\n",
246
+ )
247
+ _commit_repository(repository_root, "rename and delete")
248
+ return comparison_revision
249
+
250
+
251
+ def _lint_base(repository_root: Path, comparison_revision: str) -> LintReport:
252
+ return lint(
253
+ LintRequest.base(repository_root, comparison_revision),
254
+ all_registry=(_terminology_rule(),),
255
+ )
256
+
257
+
99
258
  def test_hook_format_accepts_claude_settings() -> None:
100
259
  settings_document = Document.from_text(".claude/settings.json", "{}")
101
260
  hooks_document = Document.from_text("hooks/hooks.json", "{}")
@@ -161,7 +320,7 @@ def test_test_pairing_diagnostics_exempts_constants_only_python_modules(
161
320
  (
162
321
  Document.from_text(
163
322
  "hooks_constants/tool_names.py",
164
- "TOOL_NAME: str = \"tool\"\n",
323
+ 'TOOL_NAME: str = "tool"\n',
165
324
  ),
166
325
  ),
167
326
  SelectionKind.STAGED,
@@ -244,9 +403,7 @@ def test_hook_configuration_diagnostics_rejects_registered_action_boundary_path(
244
403
  json.dumps(
245
404
  {
246
405
  "hooks": {
247
- "PreToolUse": [
248
- {"hooks": [{"command": "hooks/blocking/deny.py"}]}
249
- ]
406
+ "PreToolUse": [{"hooks": [{"command": "hooks/blocking/deny.py"}]}]
250
407
  }
251
408
  }
252
409
  ),
@@ -263,7 +420,9 @@ def test_hook_configuration_diagnostics_ignore_description_text(tmp_path: Path)
263
420
  "hooks/hooks.json",
264
421
  json.dumps({"description": "blocking safety controls", "hooks": {}}),
265
422
  )
266
- assert adapters.hook_configuration_diagnostics(configuration_document, tmp_path) == ()
423
+ assert (
424
+ adapters.hook_configuration_diagnostics(configuration_document, tmp_path) == ()
425
+ )
267
426
 
268
427
 
269
428
  def test_hook_configuration_diagnostics_ignore_task_path(tmp_path: Path) -> None:
@@ -271,7 +430,9 @@ def test_hook_configuration_diagnostics_ignore_task_path(tmp_path: Path) -> None
271
430
  "hooks/hooks.json",
272
431
  json.dumps({"hooks": {"PostToolUse": [{"command": "task_runner.py"}]}}),
273
432
  )
274
- assert adapters.hook_configuration_diagnostics(configuration_document, tmp_path) == ()
433
+ assert (
434
+ adapters.hook_configuration_diagnostics(configuration_document, tmp_path) == ()
435
+ )
275
436
 
276
437
 
277
438
  def test_registry_omits_native_ruff_mypy_javascript_adapters() -> None:
@@ -291,7 +452,9 @@ def test_registry_registers_unique_python_validator_rule() -> None:
291
452
  def test_registry_registers_terminology_change_set_rule() -> None:
292
453
  terminology_rule = _terminology_rule()
293
454
  assert terminology_rule.rule_sets == _CHANGED_RULE_SETS
294
- assert terminology_rule.selections == frozenset({SelectionKind.STAGED})
455
+ assert terminology_rule.selections == frozenset(
456
+ {SelectionKind.STAGED, SelectionKind.BASE}
457
+ )
295
458
 
296
459
 
297
460
  def test_registry_runs_test_pairing_for_staged_and_base_changes() -> None:
@@ -313,10 +476,11 @@ def test_terminology_rule_reports_cross_surface_path_and_line(
313
476
  tmp_path,
314
477
  "baseline = 1\n",
315
478
  "premium_request_interactions = 5\n",
316
- "Quota fields use one name.\n"
317
- "The premium-request-budget field gates the run.\n",
479
+ "Quota fields use one name.\nThe premium-request-budget field gates the run.\n",
480
+ )
481
+ lint_report = lint(
482
+ LintRequest.staged(tmp_path), all_registry=(_terminology_rule(),)
318
483
  )
319
- lint_report = lint(LintRequest.staged(tmp_path), all_registry=(_terminology_rule(),))
320
484
 
321
485
  assert len(lint_report.diagnostics) == 1
322
486
  diagnostic = lint_report.diagnostics[0]
@@ -332,15 +496,65 @@ def test_terminology_rule_suppresses_identifier_present_in_prior_tree(
332
496
  _stage_terminology_change(
333
497
  tmp_path,
334
498
  "premium_request_interactions = 5\n",
335
- "premium_request_interactions = 5\n"
336
- "premium_request_interactions = 6\n",
499
+ "premium_request_interactions = 5\npremium_request_interactions = 6\n",
337
500
  "The premium-request-budget field gates the run.\n",
338
501
  )
339
- lint_report = lint(LintRequest.staged(tmp_path), all_registry=(_terminology_rule(),))
502
+ lint_report = lint(
503
+ LintRequest.staged(tmp_path), all_registry=(_terminology_rule(),)
504
+ )
505
+
506
+ assert lint_report.diagnostics == ()
507
+
508
+
509
+ def test_terminology_rule_reports_committed_changes_from_base(
510
+ tmp_path: Path,
511
+ ) -> None:
512
+ _, _, comparison_revision = _committed_near_miss_from_base(tmp_path)
513
+ _stage_unrelated_index_file(tmp_path)
514
+ lint_report = _lint_base(tmp_path, comparison_revision)
515
+ assert lint_report.failed_rules == ()
516
+ assert len(lint_report.diagnostics) == 1
517
+ assert lint_report.diagnostics[0].location is not None
518
+ assert lint_report.diagnostics[0].location.path == PurePosixPath("docs/README.md")
519
+
520
+
521
+ def test_terminology_base_uses_worktree_while_staged_uses_index(
522
+ tmp_path: Path,
523
+ ) -> None:
524
+ comparison_revision = _worktree_and_index_near_miss_repository(tmp_path)
525
+ base_report = _lint_base(tmp_path, comparison_revision)
526
+ staged_report = lint(
527
+ LintRequest.staged(tmp_path),
528
+ all_registry=(_terminology_rule(),),
529
+ )
530
+ assert base_report.failed_rules == ()
531
+ assert staged_report.failed_rules == ()
532
+ assert base_report.diagnostics == ()
533
+ assert len(staged_report.diagnostics) == 1
534
+ assert staged_report.diagnostics[0].location is not None
535
+ assert staged_report.diagnostics[0].location.path == PurePosixPath("docs/README.md")
340
536
 
537
+
538
+ def test_terminology_base_suppresses_identifier_present_in_comparison_base(
539
+ tmp_path: Path,
540
+ ) -> None:
541
+ comparison_revision = _existing_identifier_from_base(tmp_path)
542
+ lint_report = _lint_base(tmp_path, comparison_revision)
543
+ assert lint_report.failed_rules == ()
341
544
  assert lint_report.diagnostics == ()
342
545
 
343
546
 
547
+ def test_terminology_base_handles_renames_and_deletions(
548
+ tmp_path: Path,
549
+ ) -> None:
550
+ comparison_revision = _renamed_and_deleted_near_miss_from_base(tmp_path)
551
+ lint_report = _lint_base(tmp_path, comparison_revision)
552
+ assert lint_report.failed_rules == ()
553
+ assert len(lint_report.diagnostics) == 1
554
+ assert lint_report.diagnostics[0].location is not None
555
+ assert lint_report.diagnostics[0].location.path == PurePosixPath("docs/README.md")
556
+
557
+
344
558
  def test_terminology_rule_skips_file_selection(tmp_path: Path) -> None:
345
559
  document_set = DocumentSet(
346
560
  (Document.from_text("docs/README.md", "text\n"),),
@@ -28,7 +28,7 @@ def _git_environment() -> dict[str, str]:
28
28
  }
29
29
 
30
30
 
31
- def _run_git(repository_root: Path, *all_arguments: str) -> None:
31
+ def _git_stdout(repository_root: Path, *all_arguments: str) -> str:
32
32
  completed = subprocess.run(
33
33
  [constants.GIT_EXECUTABLE, *all_arguments],
34
34
  cwd=repository_root,
@@ -38,6 +38,11 @@ def _run_git(repository_root: Path, *all_arguments: str) -> None:
38
38
  env=_git_environment(),
39
39
  )
40
40
  assert completed.returncode == 0, completed.stderr
41
+ return completed.stdout.strip()
42
+
43
+
44
+ def _run_git(repository_root: Path, *all_arguments: str) -> None:
45
+ _git_stdout(repository_root, *all_arguments)
41
46
 
42
47
 
43
48
  def _initialize_repository(repository_root: Path) -> Path:
@@ -200,7 +205,9 @@ def test_staged_missing_non_delete_blob_should_raise(
200
205
  def test_git_blob_unexpected_failure_should_raise(
201
206
  tmp_path: Path, monkeypatch: pytest.MonkeyPatch
202
207
  ) -> None:
203
- def failed_git(*_all_arguments: object, **_all_keywords: object) -> subprocess.CompletedProcess[bytes]:
208
+ def failed_git(
209
+ *_all_arguments: object, **_all_keywords: object
210
+ ) -> subprocess.CompletedProcess[bytes]:
204
211
  return subprocess.CompletedProcess(
205
212
  args=[constants.GIT_EXECUTABLE],
206
213
  returncode=1,
@@ -254,6 +261,22 @@ def test_base_source_should_compare_merge_base_and_read_worktree_bytes(
254
261
  assert selected_document.origin == ContentOrigin.REVISION_DIFF
255
262
 
256
263
 
264
+ def test_base_selection_records_merge_base_revision_and_staged_does_not(
265
+ tmp_path: Path,
266
+ ) -> None:
267
+ repository_root = _initialize_repository(tmp_path)
268
+ _commit_text(repository_root, "file.py", "base-text\n")
269
+ _run_git(repository_root, "checkout", "--quiet", "-b", "feature")
270
+ _commit_text(repository_root, "file.py", "feature-committed\n")
271
+
272
+ merge_base = _git_stdout(repository_root, "merge-base", "main", "HEAD")
273
+ base_document_set = select_documents(LintRequest.base(repository_root, "main"))
274
+ staged_document_set = select_documents(LintRequest.staged(repository_root))
275
+
276
+ assert base_document_set.base_revision == merge_base
277
+ assert staged_document_set.base_revision is None
278
+
279
+
257
280
  def test_repository_source_should_read_tracked_worktree_files_only(
258
281
  tmp_path: Path,
259
282
  ) -> None:
@@ -0,0 +1,30 @@
1
+ """Preserve Git's active index without accepting repository-directory overrides."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+ from policy_lint import selection_git
9
+
10
+
11
+ def test_active_git_index_survives_environment_sanitization(
12
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
13
+ ) -> None:
14
+ index_path = tmp_path / "index"
15
+ monkeypatch.setenv("GIT_INDEX_FILE", str(index_path))
16
+ monkeypatch.setenv("GIT_DIR", "foreign-repository")
17
+ monkeypatch.setenv("GIT_WORK_TREE", "foreign-worktree")
18
+ monkeypatch.setenv("PRESERVED_SETTING", "fixture")
19
+ active_environment = selection_git._git_subprocess_environment()
20
+ assert active_environment["GIT_INDEX_FILE"] == str(index_path)
21
+ assert "GIT_DIR" not in active_environment
22
+ assert "GIT_WORK_TREE" not in active_environment
23
+ assert active_environment["PRESERVED_SETTING"] == "fixture"
24
+
25
+
26
+ def test_default_index_is_not_replaced_by_an_invented_path(
27
+ monkeypatch: pytest.MonkeyPatch,
28
+ ) -> None:
29
+ monkeypatch.delenv("GIT_INDEX_FILE", raising=False)
30
+ assert "GIT_INDEX_FILE" not in selection_git._git_subprocess_environment()