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,334 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ import time
9
+ from collections.abc import Callable, Sequence
10
+ from dataclasses import asdict
11
+ from pathlib import Path
12
+ from typing import TextIO
13
+
14
+ scripts_directory = Path(__file__).resolve().parents[1]
15
+ if str(scripts_directory) not in sys.path:
16
+ sys.path.insert(0, str(scripts_directory))
17
+
18
+ from pr_verification.config.constants import INCOMPLETE_EXIT_CODE, SUCCESS_EXIT_CODE
19
+ from pr_verification.github import GitHubAppAuthenticator
20
+ from pr_verification.lock import SupervisorLock, SupervisorLockError
21
+ from pr_verification.model import RepositorySettings
22
+
23
+ from automatic_advisory.config.constants import (
24
+ POLL_ERROR_KEY,
25
+ POLL_ERROR_LOG_LINE_LIMIT,
26
+ REPORT_NEWLINE,
27
+ STATE_FILE_SUFFIX,
28
+ STATE_STATUS_KEY,
29
+ STATE_STATUS_NEVER_RUN,
30
+ UTF8_ENCODING,
31
+ )
32
+ from automatic_advisory.configuration import (
33
+ AdvisoryConfigurationError,
34
+ load_advisory_settings,
35
+ )
36
+ from automatic_advisory.model import AdvisorySettings, AdvisoryState
37
+ from automatic_advisory.runner import (
38
+ AdvisoryGitHub,
39
+ AutomaticAdvisoryRunner,
40
+ )
41
+ from automatic_advisory.window_flags import detached_poller_creation_flags
42
+
43
+
44
+ def build_parser() -> argparse.ArgumentParser:
45
+ """Build the automatic advisory command parser.
46
+
47
+ Returns:
48
+ Parser for status, one-shot, and polling modes.
49
+ """
50
+ parser = argparse.ArgumentParser(prog="cde automatic-advisory")
51
+ parser.add_argument("--settings", type=Path, required=True)
52
+ mode = parser.add_mutually_exclusive_group(required=True)
53
+ mode.add_argument("--once", action="store_true")
54
+ mode.add_argument("--poll", action="store_true")
55
+ mode.add_argument("--status", action="store_true")
56
+ mode.add_argument("--start", action="store_true")
57
+ parser.add_argument("--rerun", action="store_true")
58
+ return parser
59
+
60
+
61
+ def main(
62
+ all_arguments: Sequence[str],
63
+ *,
64
+ stdout: TextIO = sys.stdout,
65
+ stderr: TextIO = sys.stderr,
66
+ sleeper: Callable[[float], None] = time.sleep,
67
+ ) -> int:
68
+ """Run the selected automatic advisory command.
69
+
70
+ Args:
71
+ all_arguments: Command arguments after the executable name.
72
+ stdout: Stream for state output.
73
+ stderr: Stream for configuration and runtime errors.
74
+ sleeper: Delay function used by polling mode.
75
+
76
+ Returns:
77
+ Zero for a completed command or three for an incomplete run.
78
+ """
79
+ return _run_main_command(all_arguments, stdout, stderr, sleeper)
80
+
81
+
82
+ def _run_main_command(
83
+ all_arguments: Sequence[str],
84
+ stdout: TextIO,
85
+ stderr: TextIO,
86
+ sleeper: Callable[[float], None],
87
+ ) -> int:
88
+ try:
89
+ parsed_arguments, settings = _load_command_inputs(all_arguments)
90
+ return _run_selected_mode(parsed_arguments, settings, stdout, sleeper)
91
+ except (AdvisoryConfigurationError, OSError, SupervisorLockError) as error:
92
+ stderr.write(f"{error}{REPORT_NEWLINE}")
93
+ return INCOMPLETE_EXIT_CODE
94
+
95
+
96
+ def _load_command_inputs(
97
+ all_arguments: Sequence[str],
98
+ ) -> tuple[argparse.Namespace, AdvisorySettings]:
99
+ parsed_arguments = build_parser().parse_args(list(all_arguments))
100
+ return parsed_arguments, load_advisory_settings(parsed_arguments.settings)
101
+
102
+
103
+ def _run_selected_mode(
104
+ parsed_arguments: argparse.Namespace,
105
+ settings: AdvisorySettings,
106
+ stdout: TextIO,
107
+ sleeper: Callable[[float], None],
108
+ ) -> int:
109
+ if parsed_arguments.status:
110
+ write_status(settings, stdout)
111
+ return SUCCESS_EXIT_CODE
112
+ if parsed_arguments.start:
113
+ return start_polling(parsed_arguments.settings)
114
+ advisory_runner = _build_runner(settings)
115
+ if parsed_arguments.once:
116
+ write_states(advisory_runner.run_once(parsed_arguments.rerun), stdout)
117
+ return SUCCESS_EXIT_CODE
118
+ with SupervisorLock(settings.poll_lock_root):
119
+ return run_polling(
120
+ advisory_runner,
121
+ settings.poll_seconds,
122
+ stdout,
123
+ settings.poll_error_log_path,
124
+ sleeper,
125
+ )
126
+
127
+
128
+ def start_polling(settings_path: Path) -> int:
129
+ """Start one detached polling process and return without waiting.
130
+
131
+ Args:
132
+ settings_path: JSON settings passed to the detached process.
133
+
134
+ Returns:
135
+ Zero after the child process starts.
136
+ """
137
+ process_flags = detached_poller_creation_flags()
138
+ resolved_settings_path = settings_path.resolve()
139
+ subprocess.Popen(
140
+ (
141
+ sys.executable,
142
+ str(Path(__file__).resolve()),
143
+ "--settings",
144
+ str(resolved_settings_path),
145
+ "--poll",
146
+ ),
147
+ cwd=Path(__file__).resolve().parents[1],
148
+ stdin=subprocess.DEVNULL,
149
+ stdout=subprocess.DEVNULL,
150
+ stderr=subprocess.DEVNULL,
151
+ creationflags=process_flags,
152
+ start_new_session=True,
153
+ )
154
+ return SUCCESS_EXIT_CODE
155
+
156
+
157
+ def _build_runner(
158
+ settings: AdvisorySettings,
159
+ ) -> AutomaticAdvisoryRunner:
160
+ authenticator = GitHubAppAuthenticator(
161
+ settings.api_url,
162
+ settings.app_id,
163
+ settings.installation_id,
164
+ settings.private_key_path,
165
+ )
166
+ return AutomaticAdvisoryRunner(
167
+ settings,
168
+ None,
169
+ github_factory=lambda repository: _issue_repository_api(
170
+ authenticator, repository
171
+ ),
172
+ )
173
+
174
+
175
+ def _issue_repository_api(
176
+ authenticator: GitHubAppAuthenticator,
177
+ repository: RepositorySettings,
178
+ ) -> AdvisoryGitHub:
179
+ return authenticator.issue_repository_api(
180
+ repository,
181
+ should_write_issue_labels=True,
182
+ )
183
+
184
+
185
+ def run_polling(
186
+ advisory_runner: AutomaticAdvisoryRunner,
187
+ poll_seconds: float,
188
+ stdout: TextIO,
189
+ poll_error_log_path: Path,
190
+ sleeper: Callable[[float], None],
191
+ ) -> int:
192
+ """Run advisory checks until the caller interrupts polling.
193
+
194
+ A SupervisorLockError or OSError in a cycle writes one poll_error JSON
195
+ line to stdout and to poll-errors.log, newest 200 lines, then the next
196
+ cycle runs. A detached poller sends stdout to DEVNULL, so the log file
197
+ beside the state files is the readable copy.
198
+
199
+ Args:
200
+ advisory_runner: Runner for explicit checkout and pull request pairs.
201
+ poll_seconds: Delay between completed cycles.
202
+ stdout: Stream for each persisted state and each failed cycle.
203
+ poll_error_log_path: Log file receiving each failed cycle line.
204
+ sleeper: Delay function used between cycles.
205
+
206
+ Returns:
207
+ Zero after a user interrupt.
208
+ """
209
+ try:
210
+ _repeat_poll_cycles(
211
+ advisory_runner, poll_seconds, stdout, poll_error_log_path, sleeper
212
+ )
213
+ except KeyboardInterrupt:
214
+ return SUCCESS_EXIT_CODE
215
+
216
+
217
+ def _repeat_poll_cycles(
218
+ advisory_runner: AutomaticAdvisoryRunner,
219
+ poll_seconds: float,
220
+ stdout: TextIO,
221
+ poll_error_log_path: Path,
222
+ sleeper: Callable[[float], None],
223
+ ) -> None:
224
+ while True:
225
+ _run_one_poll_cycle(advisory_runner, stdout, poll_error_log_path)
226
+ sleeper(poll_seconds)
227
+
228
+
229
+ def _run_one_poll_cycle(
230
+ advisory_runner: AutomaticAdvisoryRunner,
231
+ stdout: TextIO,
232
+ poll_error_log_path: Path,
233
+ ) -> None:
234
+ try:
235
+ write_states(advisory_runner.run_once(), stdout)
236
+ except (OSError, SupervisorLockError) as cycle_error:
237
+ _write_poll_error(cycle_error, stdout, poll_error_log_path)
238
+
239
+
240
+ def _write_poll_error(
241
+ cycle_error: Exception,
242
+ stdout: TextIO,
243
+ poll_error_log_path: Path,
244
+ ) -> None:
245
+ error_line = (
246
+ json.dumps({POLL_ERROR_KEY: str(cycle_error)}, sort_keys=True) + REPORT_NEWLINE
247
+ )
248
+ stdout.write(error_line)
249
+ stdout.flush()
250
+ _append_poll_error_line(poll_error_log_path, error_line)
251
+
252
+
253
+ def _append_poll_error_line(poll_error_log_path: Path, error_line: str) -> None:
254
+ """Append one failed-cycle JSON line, keeping the newest 200 lines.
255
+
256
+ Skip the log on OSError or ValueError. A raise here would stop the poller.
257
+ The line already reached stdout. Undecodable bytes are replaced on read.
258
+
259
+ Args:
260
+ poll_error_log_path: Log file receiving the line.
261
+ error_line: JSON error line, newline included.
262
+ """
263
+ try:
264
+ _replace_poll_error_log(poll_error_log_path, error_line)
265
+ except (OSError, ValueError):
266
+ return
267
+
268
+
269
+ def _replace_poll_error_log(poll_error_log_path: Path, error_line: str) -> None:
270
+ poll_error_log_path.parent.mkdir(parents=True, exist_ok=True)
271
+ all_lines = _poll_error_log_lines(poll_error_log_path)
272
+ all_lines.append(error_line)
273
+ temporary_log_path = poll_error_log_path.with_name(
274
+ poll_error_log_path.name + STATE_FILE_SUFFIX
275
+ )
276
+ temporary_log_path.write_text(
277
+ "".join(all_lines[-POLL_ERROR_LOG_LINE_LIMIT:]), encoding=UTF8_ENCODING
278
+ )
279
+ os.replace(temporary_log_path, poll_error_log_path)
280
+
281
+
282
+ def _poll_error_log_lines(poll_error_log_path: Path) -> list[str]:
283
+ if not poll_error_log_path.exists():
284
+ return []
285
+ return poll_error_log_path.read_text(
286
+ encoding=UTF8_ENCODING, errors="replace"
287
+ ).splitlines(keepends=True)
288
+
289
+
290
+ def write_states(
291
+ all_states: tuple[AdvisoryState, ...],
292
+ stdout: TextIO,
293
+ ) -> None:
294
+ """Write one JSON state line for each registration.
295
+
296
+ Args:
297
+ all_states: Persisted registration states.
298
+ stdout: Stream receiving each JSON state line.
299
+ """
300
+ stdout.writelines(
301
+ json.dumps(asdict(each_state), sort_keys=True) + REPORT_NEWLINE
302
+ for each_state in all_states
303
+ )
304
+ stdout.flush()
305
+
306
+
307
+ def write_status(settings: AdvisorySettings, stdout: TextIO) -> None:
308
+ """Write the latest state or never-run marker for each registration.
309
+
310
+ Args:
311
+ settings: Explicit advisory registrations to inspect.
312
+ stdout: Stream receiving one JSON record per registration.
313
+ """
314
+ for each_registration in settings.registrations:
315
+ state_path = each_registration.state_path
316
+ if not state_path.is_file():
317
+ stdout.write(
318
+ json.dumps(
319
+ {
320
+ "repository": each_registration.repository.slug,
321
+ "pull_request": each_registration.pull_request_number,
322
+ STATE_STATUS_KEY: STATE_STATUS_NEVER_RUN,
323
+ },
324
+ sort_keys=True,
325
+ )
326
+ + REPORT_NEWLINE
327
+ )
328
+ continue
329
+ stdout.write(state_path.read_text(encoding="utf-8"))
330
+ stdout.flush()
331
+
332
+
333
+ if __name__ == "__main__":
334
+ raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1 @@
1
+ """Configuration for the automatic advisory runner."""
@@ -0,0 +1,93 @@
1
+ from __future__ import annotations
2
+
3
+ SETTINGS_VERSION = 1
4
+ SETTINGS_VERSION_KEY = "version"
5
+ SETTINGS_REGISTRATIONS_KEY = "registrations"
6
+ SETTINGS_API_URL_KEY = "api_url"
7
+ SETTINGS_APP_ID_KEY = "app_id"
8
+ SETTINGS_INSTALLATION_ID_KEY = "installation_id"
9
+ SETTINGS_SIGNING_FILE_FIELD = "private_" + "key_path"
10
+ SETTINGS_POLL_SECONDS_KEY = "poll_seconds"
11
+ SETTINGS_CHILD_TIMEOUT_SECONDS_KEY = "child_timeout_seconds"
12
+ REGISTRATION_REPOSITORY_KEY = "repository"
13
+ REGISTRATION_PULL_REQUEST_KEY = "pull_request"
14
+ REGISTRATION_CHECKOUT_KEY = "checkout"
15
+ REGISTRATION_MANIFEST_KEY = "manifest"
16
+ REGISTRATION_REPORT_KEY = "report"
17
+ REGISTRATION_STATE_KEY = "state"
18
+ REGISTRATION_BASE_REF_KEY = "base_ref"
19
+ REGISTRATION_REMOTE_KEY = "remote"
20
+ STATE_VERSION = 1
21
+ STATE_VERSION_KEY = "version"
22
+ STATE_REPOSITORY_KEY = "repository"
23
+ STATE_PULL_REQUEST_KEY = "pull_request"
24
+ STATE_STATUS_KEY = "status"
25
+ STATE_REASON_KEY = "reason"
26
+ STATE_HEAD_SHA_KEY = "head_sha"
27
+ STATE_BASE_SHA_KEY = "base_sha"
28
+ STATE_LAST_POLL_KEY = "last_poll"
29
+ STATE_LAST_RUN_KEY = "last_run"
30
+ STATE_REPORT_KEY = "report"
31
+ STATE_STATUS_WAITING = "waiting"
32
+ STATE_STATUS_OFFLINE = "offline"
33
+ STATE_STATUS_CLOSED = "closed"
34
+ STATE_STATUS_PASSED = "passed"
35
+ STATE_STATUS_FAILED = "failed"
36
+ STATE_STATUS_PENDING = "pending"
37
+ STATE_STATUS_ERROR = "error"
38
+ STATE_STATUS_NEVER_RUN = "never_run"
39
+ POLL_ERROR_KEY = "poll_error"
40
+ POLL_ERROR_LOG_FILE_NAME = "poll-errors.log"
41
+ POLL_ERROR_LOG_LINE_LIMIT = 200
42
+ ALL_TERMINAL_STATUSES = frozenset({STATE_STATUS_PASSED, STATE_STATUS_FAILED})
43
+ STATE_REASON_CHECKOUT_MISMATCH = "checkout does not match the open pull request"
44
+ STATE_REASON_DIRTY_CHECKOUT = "checkout has uncommitted changes"
45
+ STATE_REASON_PULL_REQUEST_CLOSED = "pull request is not open"
46
+ STATE_REASON_GITHUB_UNAVAILABLE = "GitHub is unavailable"
47
+ STATE_REASON_BASE_FETCH_FAILED = "base ref fetch failed"
48
+ STATE_REASON_PUBLISH_FAILED = "report publication failed"
49
+ STATE_REASON_CHILD_INCOMPLETE = "verification child did not produce a report"
50
+ STATE_REASON_UNCHANGED = "unchanged remote head and base"
51
+ GIT_EXECUTABLE = "git"
52
+ GIT_FETCH_ARGUMENT = "fetch"
53
+ GIT_NO_TAGS_FLAG = "--no-tags"
54
+ GIT_TERMINAL_PROMPT_ENVIRONMENT_KEY = "GIT_TERMINAL_PROMPT"
55
+ GIT_PROMPT_DISABLED_VALUE = "0"
56
+ GIT_FORCE_FLAG = "--force"
57
+ ALL_GIT_HEAD_ARGUMENTS = ("rev-parse", "HEAD")
58
+ ALL_GIT_STATUS_ARGUMENTS = ("status", "--porcelain=v1", "--untracked-files=all")
59
+ GIT_BASE_REFSPEC_TEMPLATE = (
60
+ "+refs/heads/{base_ref}:refs/cde/advisory-base/{pull_request}"
61
+ )
62
+ GIT_REMOTE_BASE_REFSPEC_TEMPLATE = (
63
+ "+refs/heads/{base_ref}:refs/remotes/{remote_name}/{base_ref}"
64
+ )
65
+ GIT_BASE_REF_TEMPLATE = "refs/cde/advisory-base/{pull_request}"
66
+ LOCAL_VERIFICATION_CLI_FILENAME = "cli.py"
67
+ WINDOWS_PRIORITY_CLASS_ATTRIBUTE = "BELOW_NORMAL_PRIORITY_CLASS"
68
+ WINDOWS_PLATFORM = "win32"
69
+ PYTHON_MODULE_ARGUMENT = "-m"
70
+ WINDOWS_PROCESS_HOST_MODULE = "automatic_advisory.process_host"
71
+ WINDOWS_PROCESS_START_SIGNAL = "1"
72
+ WINDOWS_PROCESS_SET_QUOTA_ACCESS = 0x0100
73
+ WINDOWS_PROCESS_TERMINATE_ACCESS = 0x0001
74
+ WINDOWS_EXTENDED_LIMIT_INFORMATION_CLASS = 9
75
+ WINDOWS_JOB_KILL_ON_CLOSE_LIMIT = 0x2000
76
+ DEFAULT_CLONE_URL_SUFFIX = ".git"
77
+ POLL_LOCK_DIRECTORY_NAME = ".poll-process"
78
+ CREATE_NEW_PROCESS_GROUP_ATTRIBUTE = "CREATE_NEW_PROCESS_GROUP"
79
+ CREATE_NO_WINDOW_ATTRIBUTE = "CREATE_NO_WINDOW"
80
+ CHILD_MANIFEST_FLAG = "--manifest"
81
+ CHILD_REPOSITORY_FLAG = "--repo"
82
+ CHILD_BASE_FLAG = "--base"
83
+ CHILD_EXECUTOR_FLAG = "--executor"
84
+ CHILD_OUTPUT_FLAG = "--output"
85
+ SCOPED_VERIFICATION_SCRIPT_PATH = ".github/ci/local_verify.py"
86
+ PYTHONPATH_ENVIRONMENT_KEY = "PYTHONPATH"
87
+ PYTEST_AUTO_WORKERS_ENVIRONMENT_KEY = "PYTEST_XDIST_AUTO_NUM_WORKERS"
88
+ PYTEST_AUTO_WORKERS_LIMIT = "2"
89
+ STATE_FILE_SUFFIX = ".tmp"
90
+ SELECTED_MANIFEST_SUFFIX = ".selected-manifest.json"
91
+ UTF8_ENCODING = "utf-8"
92
+ REPORT_NEWLINE = "\n"
93
+ JSON_INDENT_SPACES = 2
@@ -0,0 +1,4 @@
1
+ DEFAULT_POLL_SECONDS = 60.0
2
+ DEFAULT_CHILD_TIMEOUT_SECONDS = 1800.0
3
+ GIT_FETCH_TIMEOUT_SECONDS = 30.0
4
+ CHILD_CLEANUP_TIMEOUT_SECONDS = 1.0
@@ -0,0 +1,197 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import math
5
+ from collections.abc import Mapping
6
+ from pathlib import Path, PurePosixPath
7
+
8
+ from pr_verification.config.constants import GITHUB_API_URL, REPOSITORY_SLUG_PATTERN
9
+ from pr_verification.model import RepositorySettings
10
+
11
+ from .config.constants import (
12
+ DEFAULT_CLONE_URL_SUFFIX,
13
+ REGISTRATION_BASE_REF_KEY,
14
+ REGISTRATION_CHECKOUT_KEY,
15
+ REGISTRATION_MANIFEST_KEY,
16
+ REGISTRATION_PULL_REQUEST_KEY,
17
+ REGISTRATION_REMOTE_KEY,
18
+ REGISTRATION_REPORT_KEY,
19
+ REGISTRATION_REPOSITORY_KEY,
20
+ REGISTRATION_STATE_KEY,
21
+ SETTINGS_API_URL_KEY,
22
+ SETTINGS_APP_ID_KEY,
23
+ SETTINGS_CHILD_TIMEOUT_SECONDS_KEY,
24
+ SETTINGS_INSTALLATION_ID_KEY,
25
+ SETTINGS_POLL_SECONDS_KEY,
26
+ SETTINGS_REGISTRATIONS_KEY,
27
+ SETTINGS_SIGNING_FILE_FIELD,
28
+ SETTINGS_VERSION,
29
+ SETTINGS_VERSION_KEY,
30
+ )
31
+ from .config.timing import DEFAULT_CHILD_TIMEOUT_SECONDS, DEFAULT_POLL_SECONDS
32
+ from .model import AdvisoryRegistration, AdvisorySettings
33
+
34
+
35
+ class AdvisoryConfigurationError(ValueError):
36
+ """Raised when automatic advisory settings are invalid."""
37
+
38
+
39
+ def load_advisory_settings(settings_path: Path) -> AdvisorySettings:
40
+ """Load and validate the explicit automatic advisory registrations.
41
+
42
+ Args:
43
+ settings_path: JSON file containing the advisory settings.
44
+
45
+ Returns:
46
+ Validated settings for the registered checkout and pull request pairs.
47
+
48
+ Raises:
49
+ AdvisoryConfigurationError: If the file or any registration is invalid.
50
+ """
51
+ try:
52
+ parsed_settings = json.loads(settings_path.read_text(encoding="utf-8"))
53
+ except (OSError, json.JSONDecodeError) as error:
54
+ raise AdvisoryConfigurationError(str(error)) from error
55
+ all_settings = _require_mapping(parsed_settings)
56
+ _require_version(all_settings)
57
+ return _build_settings(all_settings)
58
+
59
+
60
+ def _build_settings(all_settings: Mapping[str, object]) -> AdvisorySettings:
61
+ registrations = _load_registrations(all_settings)
62
+ return AdvisorySettings(
63
+ _load_api_url(all_settings),
64
+ _require_positive_integer(all_settings, SETTINGS_APP_ID_KEY),
65
+ _require_positive_integer(all_settings, SETTINGS_INSTALLATION_ID_KEY),
66
+ _require_absolute_path(all_settings, SETTINGS_SIGNING_FILE_FIELD),
67
+ _load_positive_number(
68
+ all_settings,
69
+ SETTINGS_POLL_SECONDS_KEY,
70
+ DEFAULT_POLL_SECONDS,
71
+ ),
72
+ _load_positive_number(
73
+ all_settings,
74
+ SETTINGS_CHILD_TIMEOUT_SECONDS_KEY,
75
+ DEFAULT_CHILD_TIMEOUT_SECONDS,
76
+ ),
77
+ registrations,
78
+ )
79
+
80
+
81
+ def _load_registrations(
82
+ all_settings: Mapping[str, object],
83
+ ) -> tuple[AdvisoryRegistration, ...]:
84
+ raw_registrations = all_settings.get(SETTINGS_REGISTRATIONS_KEY)
85
+ if not isinstance(raw_registrations, list) or not raw_registrations:
86
+ raise AdvisoryConfigurationError("registrations must be a non-empty array")
87
+ all_registrations = tuple(
88
+ _load_registration(each_registration) for each_registration in raw_registrations
89
+ )
90
+ all_repositories = tuple(
91
+ each_registration.repository.slug for each_registration in all_registrations
92
+ )
93
+ if len(set(all_repositories)) != len(all_repositories):
94
+ raise AdvisoryConfigurationError(
95
+ "each repository can have only one automatic advisory registration"
96
+ )
97
+ return all_registrations
98
+
99
+
100
+ def _load_registration(raw_registration: object) -> AdvisoryRegistration:
101
+ registration = _require_mapping(raw_registration)
102
+ repository_slug = _require_text(registration, REGISTRATION_REPOSITORY_KEY)
103
+ if REPOSITORY_SLUG_PATTERN.fullmatch(repository_slug) is None:
104
+ raise AdvisoryConfigurationError("repository must use owner/name")
105
+ pull_request_number = _require_positive_integer(
106
+ registration, REGISTRATION_PULL_REQUEST_KEY
107
+ )
108
+ checkout_path = _require_absolute_path(registration, REGISTRATION_CHECKOUT_KEY)
109
+ manifest_path = _require_relative_manifest(registration)
110
+ report_path = _require_absolute_path(registration, REGISTRATION_REPORT_KEY)
111
+ state_path = _require_absolute_path(registration, REGISTRATION_STATE_KEY)
112
+ return AdvisoryRegistration(
113
+ RepositorySettings(repository_slug, repository_slug + DEFAULT_CLONE_URL_SUFFIX),
114
+ pull_request_number,
115
+ checkout_path,
116
+ manifest_path,
117
+ report_path,
118
+ state_path,
119
+ _require_text(registration, REGISTRATION_BASE_REF_KEY),
120
+ _require_text(registration, REGISTRATION_REMOTE_KEY),
121
+ )
122
+
123
+
124
+ def _load_api_url(all_settings: Mapping[str, object]) -> str:
125
+ return _require_text_or_default(
126
+ all_settings, SETTINGS_API_URL_KEY, GITHUB_API_URL
127
+ ).rstrip("/")
128
+
129
+
130
+ def _load_positive_number(
131
+ all_settings: Mapping[str, object],
132
+ field_name: str,
133
+ default_seconds: float,
134
+ ) -> float:
135
+ raw_number = all_settings.get(field_name, default_seconds)
136
+ if isinstance(raw_number, bool) or not isinstance(raw_number, (float, int)):
137
+ raise AdvisoryConfigurationError(f"{field_name} must be positive")
138
+ if raw_number <= 0 or not math.isfinite(raw_number):
139
+ raise AdvisoryConfigurationError(f"{field_name} must be positive")
140
+ return float(raw_number)
141
+
142
+
143
+ def _require_mapping(raw_payload: object) -> Mapping[str, object]:
144
+ if not isinstance(raw_payload, Mapping):
145
+ raise AdvisoryConfigurationError("settings must be an object")
146
+ return raw_payload
147
+
148
+
149
+ def _require_version(all_settings: Mapping[str, object]) -> None:
150
+ if all_settings.get(SETTINGS_VERSION_KEY) != SETTINGS_VERSION:
151
+ raise AdvisoryConfigurationError("settings version is unsupported")
152
+
153
+
154
+ def _require_text(all_settings: Mapping[str, object], field_name: str) -> str:
155
+ field_text = all_settings.get(field_name)
156
+ if not isinstance(field_text, str) or not field_text.strip():
157
+ raise AdvisoryConfigurationError(f"{field_name} must be a non-empty string")
158
+ return field_text
159
+
160
+
161
+ def _require_text_or_default(
162
+ all_settings: Mapping[str, object],
163
+ field_name: str,
164
+ default_text: str,
165
+ ) -> str:
166
+ field_text = all_settings.get(field_name, default_text)
167
+ if not isinstance(field_text, str) or not field_text.strip():
168
+ raise AdvisoryConfigurationError(f"{field_name} must be a non-empty string")
169
+ return field_text
170
+
171
+
172
+ def _require_positive_integer(
173
+ all_settings: Mapping[str, object], field_name: str
174
+ ) -> int:
175
+ field_number = all_settings.get(field_name)
176
+ if isinstance(field_number, bool) or not isinstance(field_number, int):
177
+ raise AdvisoryConfigurationError(f"{field_name} must be positive")
178
+ if field_number < 1:
179
+ raise AdvisoryConfigurationError(f"{field_name} must be positive")
180
+ return field_number
181
+
182
+
183
+ def _require_absolute_path(all_settings: Mapping[str, object], field_name: str) -> Path:
184
+ configured_path = Path(_require_text(all_settings, field_name))
185
+ if not configured_path.is_absolute():
186
+ raise AdvisoryConfigurationError(f"{field_name} must be absolute")
187
+ return configured_path.resolve()
188
+
189
+
190
+ def _require_relative_manifest(
191
+ all_settings: Mapping[str, object],
192
+ ) -> PurePosixPath:
193
+ manifest_text = _require_text(all_settings, REGISTRATION_MANIFEST_KEY)
194
+ manifest_path = PurePosixPath(manifest_text.replace("\\", "/"))
195
+ if manifest_path.is_absolute() or ".." in manifest_path.parts:
196
+ raise AdvisoryConfigurationError("manifest must stay inside checkout")
197
+ return manifest_path