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
@@ -1,381 +0,0 @@
1
- """Unit tests for the fable_spawn_gate PreToolUse hook.
2
-
3
- Covers the decision table. A spawn at model ``fable`` whose prompt lacks the
4
- authorization marker is denied, and the deny reason points at the
5
- advisor-protocol document while carrying no copy of the token — one row holds
6
- the reason token-free, and its anti-corollary feeds that reason back as a
7
- retry prompt and expects a second denial. The same spawn carrying the marker
8
- is allowed. A spawn at any other model tier, and a spawn with no ``model``
9
- field, pass whether or not the marker is present. One test reads
10
- ``hooks.json`` and asserts the gate is registered on a matcher covering both
11
- spawn tool names.
12
-
13
- Token pins read the warm-up section and each consuming skill. Two doc-gate
14
- agreement tests assemble a spawn prompt out of the advisor reference wording a
15
- warm-up bind (``reference/warm-up.md``) and a drift re-spawn
16
- (``reference/lifecycle.md``) follow, and run each through the gate, so
17
- wording that stops naming the marker fails here. Two deny-path tests read the
18
- preview the gate hands the block logger and hold it bounded and scoped to the
19
- model field.
20
- """
21
-
22
- from __future__ import annotations
23
-
24
- import importlib.util
25
- import io
26
- import json
27
- import pathlib
28
- import sys
29
- from typing import Any
30
- from unittest import mock
31
-
32
- import pytest
33
-
34
- _HOOK_DIR = pathlib.Path(__file__).parent
35
- _HOOKS_TREE = _HOOK_DIR.parent
36
- for each_path in (str(_HOOK_DIR), str(_HOOKS_TREE)):
37
- if each_path not in sys.path:
38
- sys.path.insert(0, each_path)
39
-
40
- hook_spec = importlib.util.spec_from_file_location(
41
- "fable_spawn_gate",
42
- _HOOK_DIR / "fable_spawn_gate.py",
43
- )
44
- assert hook_spec is not None
45
- assert hook_spec.loader is not None
46
- hook_module = importlib.util.module_from_spec(hook_spec)
47
- hook_spec.loader.exec_module(hook_module)
48
-
49
- from hooks_constants.fable_spawn_gate_constants import (
50
- ADVISOR_PROTOCOL_DOCUMENT_PATH,
51
- AGENT_TOOL_NAME,
52
- CALLING_HOOK_NAME,
53
- DENY_ADDITIONAL_CONTEXT,
54
- FABLE_MODEL_ALIAS,
55
- FABLE_SPAWN_AUTHORIZATION_MARKER,
56
- TASK_TOOL_NAME,
57
- )
58
-
59
- _ADVISOR_SUBAGENT_TYPE = "session-advisor"
60
- _OPUS_MODEL_ALIAS = "opus"
61
- _SONNET_MODEL_ALIAS = "sonnet"
62
- _TITLE_CASE_FABLE_MODEL = "Fable"
63
- _FULL_FABLE_MODEL_ID = "claude-fable-5"
64
- _FULL_SONNET_MODEL_ID = "claude-sonnet-4-5"
65
- _PACKAGE_ROOT = _HOOKS_TREE.parent
66
- _ADVISOR_PROTOCOL_PATH = _PACKAGE_ROOT / "_shared" / "advisor" / "advisor-protocol.md"
67
- _ADVISOR_PROTOCOL_TEXT = _ADVISOR_PROTOCOL_PATH.read_text(encoding="utf-8")
68
- _ADVISOR_REFERENCE_DIR = _PACKAGE_ROOT / "_shared" / "advisor" / "reference"
69
- _ADVISOR_LIFECYCLE_TEXT = (_ADVISOR_REFERENCE_DIR / "lifecycle.md").read_text(
70
- encoding="utf-8"
71
- )
72
- _ADVISOR_WARM_UP_TEXT = (_ADVISOR_REFERENCE_DIR / "warm-up.md").read_text(
73
- encoding="utf-8"
74
- )
75
- _ALL_CONSUMING_SKILL_NAMES = ("team-advisor",)
76
- _ALL_CONSUMING_SKILL_PATHS = tuple(
77
- _PACKAGE_ROOT / ".agents" / "skills" / each_skill_name / "SKILL.md"
78
- for each_skill_name in _ALL_CONSUMING_SKILL_NAMES
79
- )
80
- _skill_pin_ids = _ALL_CONSUMING_SKILL_NAMES
81
- _BIND_SECTION_HEADING = "## Warm-up (once per session)"
82
- _MARKDOWN_SECTION_HEADING_PREFIX = "\n## "
83
- _RESPAWN_PARAGRAPH_MARKER = "**Re-spawn on drift.**"
84
- _WARM_UP_PROMPT_BULLET_MARKER = "- `prompt`: the charter below."
85
- _PARAGRAPH_SEPARATOR = "\n\n"
86
- _MULTI_KILOBYTE_PROMPT = "Audit the changed lines and report each finding. " * 200
87
- _MAXIMUM_DENY_PREVIEW_LENGTH = 120
88
- _MARKER_STATE_FIELD_NAME = "marker_present"
89
- _PROMPT_BODY_SAMPLE = "Audit the changed lines"
90
- _UNMARKED_PROMPT = "Audit the changed lines and report each finding with its path and line."
91
- _ADVISOR_BIND_PROMPT = (
92
- "You are the standing session advisor for this run. "
93
- f"{FABLE_SPAWN_AUTHORIZATION_MARKER} "
94
- "Answer every consult with ENDORSE, CORRECTION, PLAN, or STOP."
95
- )
96
-
97
-
98
- def _spawn_payload(
99
- *,
100
- tool_name: str = AGENT_TOOL_NAME,
101
- model: str | None = FABLE_MODEL_ALIAS,
102
- prompt: str = _UNMARKED_PROMPT,
103
- ) -> dict[str, Any]:
104
- """Build a PreToolUse spawn payload for the decision table.
105
-
106
- Args:
107
- tool_name: The spawning tool name.
108
- model: The model alias, or None to leave the field out entirely.
109
- prompt: The spawn prompt text.
110
-
111
- Returns:
112
- The payload mapping a hook reads from stdin.
113
- """
114
- all_tool_input: dict[str, Any] = {
115
- "subagent_type": _ADVISOR_SUBAGENT_TYPE,
116
- "description": "Bind the session advisor",
117
- "prompt": prompt,
118
- }
119
- if model is not None:
120
- all_tool_input["model"] = model
121
- return {"tool_name": tool_name, "tool_input": all_tool_input}
122
-
123
-
124
- def _run_main_with_io(input_text: str) -> str:
125
- """Run the gate's main against stdin text and return its stdout.
126
-
127
- Args:
128
- input_text: The raw stdin text the gate reads.
129
-
130
- Returns:
131
- Everything the gate wrote to stdout — empty on an allow.
132
- """
133
- with mock.patch("sys.stdin", io.StringIO(input_text)):
134
- with mock.patch("sys.stdout", new_callable=io.StringIO) as captured_stdout:
135
- try:
136
- hook_module.main()
137
- except SystemExit:
138
- pass
139
- return captured_stdout.getvalue()
140
-
141
-
142
- def _decision_for(payload: dict[str, Any]) -> str:
143
- """Return the gate's permissionDecision for one spawn payload.
144
-
145
- Args:
146
- payload: The PreToolUse payload to feed the gate.
147
-
148
- Returns:
149
- The decision string from the deny payload.
150
- """
151
- captured_output = _run_main_with_io(json.dumps(payload))
152
- return json.loads(captured_output)["hookSpecificOutput"]["permissionDecision"]
153
-
154
-
155
- def test_should_deny_fable_spawn_without_the_marker() -> None:
156
- assert _decision_for(_spawn_payload()) == "deny"
157
-
158
-
159
- def test_should_allow_fable_spawn_carrying_the_marker() -> None:
160
- payload = _spawn_payload(prompt=_ADVISOR_BIND_PROMPT)
161
- assert _run_main_with_io(json.dumps(payload)) == ""
162
-
163
-
164
- def test_should_allow_opus_spawn_without_the_marker() -> None:
165
- payload = _spawn_payload(model=_OPUS_MODEL_ALIAS)
166
- assert _run_main_with_io(json.dumps(payload)) == ""
167
-
168
-
169
- def test_should_allow_sonnet_spawn_carrying_the_marker() -> None:
170
- payload = _spawn_payload(model=_SONNET_MODEL_ALIAS, prompt=_ADVISOR_BIND_PROMPT)
171
- assert _run_main_with_io(json.dumps(payload)) == ""
172
-
173
-
174
- def test_should_allow_spawn_with_no_model_field() -> None:
175
- payload = _spawn_payload(model=None)
176
- assert _run_main_with_io(json.dumps(payload)) == ""
177
-
178
-
179
- def test_should_deny_title_case_fable_spawn_without_the_marker() -> None:
180
- assert _decision_for(_spawn_payload(model=_TITLE_CASE_FABLE_MODEL)) == "deny"
181
-
182
-
183
- def test_should_deny_full_model_id_fable_spawn_without_the_marker() -> None:
184
- assert _decision_for(_spawn_payload(model=_FULL_FABLE_MODEL_ID)) == "deny"
185
-
186
-
187
- def test_should_allow_full_model_id_fable_spawn_carrying_the_marker() -> None:
188
- payload = _spawn_payload(model=_FULL_FABLE_MODEL_ID, prompt=_ADVISOR_BIND_PROMPT)
189
- assert _run_main_with_io(json.dumps(payload)) == ""
190
-
191
-
192
- def test_should_allow_full_model_id_sonnet_spawn_without_the_marker() -> None:
193
- payload = _spawn_payload(model=_FULL_SONNET_MODEL_ID)
194
- assert _run_main_with_io(json.dumps(payload)) == ""
195
-
196
-
197
- def test_should_deny_fable_task_spawn_without_the_marker() -> None:
198
- assert _decision_for(_spawn_payload(tool_name=TASK_TOOL_NAME)) == "deny"
199
-
200
-
201
- def test_should_deny_fable_spawn_whose_prompt_field_is_absent() -> None:
202
- payload: dict[str, Any] = {
203
- "tool_name": AGENT_TOOL_NAME,
204
- "tool_input": {
205
- "subagent_type": _ADVISOR_SUBAGENT_TYPE,
206
- "model": FABLE_MODEL_ALIAS,
207
- },
208
- }
209
- assert _decision_for(payload) == "deny"
210
-
211
-
212
- def _deny_reason_for(payload: dict[str, Any]) -> str:
213
- """Return the permissionDecisionReason the gate writes for one payload.
214
-
215
- Args:
216
- payload: The PreToolUse payload the gate denies.
217
-
218
- Returns:
219
- The deny reason text.
220
- """
221
- captured_output = _run_main_with_io(json.dumps(payload))
222
- return str(
223
- json.loads(captured_output)["hookSpecificOutput"]["permissionDecisionReason"]
224
- )
225
-
226
-
227
- def test_deny_reason_names_the_document_without_the_token() -> None:
228
- deny_reason = _deny_reason_for(_spawn_payload())
229
- assert ADVISOR_PROTOCOL_DOCUMENT_PATH in deny_reason
230
- assert FABLE_SPAWN_AUTHORIZATION_MARKER not in deny_reason
231
-
232
-
233
- def _additional_context_for(payload: dict[str, Any]) -> str:
234
- """Return hookSpecificOutput.additionalContext for a denied spawn."""
235
- captured_output = _run_main_with_io(json.dumps(payload))
236
- return str(json.loads(captured_output)["hookSpecificOutput"]["additionalContext"])
237
-
238
-
239
- def test_deny_payload_carries_additional_context_for_the_spawner() -> None:
240
- """A denial injects recovery steps into additionalContext for the spawner."""
241
- additional_context = _additional_context_for(_spawn_payload())
242
- assert additional_context == DENY_ADDITIONAL_CONTEXT
243
- assert "fable-spawn-gate" in additional_context
244
- assert "opus" in additional_context.lower()
245
- assert "opus-equivalent" in additional_context.lower()
246
- assert ADVISOR_PROTOCOL_DOCUMENT_PATH in additional_context
247
- assert FABLE_SPAWN_AUTHORIZATION_MARKER not in additional_context
248
-
249
-
250
- def test_spawn_prompt_carrying_the_deny_text_still_denies() -> None:
251
- deny_reason = _deny_reason_for(_spawn_payload())
252
- assert _decision_for(_spawn_payload(prompt=deny_reason)) == "deny"
253
-
254
-
255
- def test_should_allow_when_the_payload_is_malformed_json() -> None:
256
- assert _run_main_with_io("not valid json {{{") == ""
257
-
258
-
259
- def _denial_preview_for(payload: dict[str, Any]) -> str:
260
- """Return the offending-input preview the gate hands the block logger.
261
-
262
- Args:
263
- payload: The PreToolUse payload the gate denies.
264
-
265
- Returns:
266
- The ``offending_input_preview`` argument of the logged block.
267
- """
268
- with mock.patch.object(hook_module, "log_hook_block") as recorded_block:
269
- _run_main_with_io(json.dumps(payload))
270
- return str(recorded_block.call_args.kwargs["offending_input_preview"])
271
-
272
-
273
- def test_deny_preview_stays_bounded_on_a_multi_kilobyte_prompt() -> None:
274
- preview = _denial_preview_for(_spawn_payload(prompt=_MULTI_KILOBYTE_PROMPT))
275
- assert len(preview) <= _MAXIMUM_DENY_PREVIEW_LENGTH
276
-
277
-
278
- def test_deny_preview_carries_the_model_and_marker_state_without_the_prompt() -> None:
279
- preview = _denial_preview_for(_spawn_payload(prompt=_MULTI_KILOBYTE_PROMPT))
280
- assert FABLE_MODEL_ALIAS in preview
281
- assert _MARKER_STATE_FIELD_NAME in preview
282
- assert _PROMPT_BODY_SAMPLE not in preview
283
-
284
-
285
- def _matchers_registering_the_gate() -> list[str]:
286
- """Return every PreToolUse matcher whose group runs this gate.
287
-
288
- Returns:
289
- The matcher strings of the registration groups naming the gate script.
290
- """
291
- manifest_record = json.loads(
292
- (_HOOKS_TREE / "hooks.json").read_text(encoding="utf-8")
293
- )
294
- all_matchers: list[str] = []
295
- for each_group in manifest_record["hooks"]["PreToolUse"]:
296
- for each_hook in each_group.get("hooks", []):
297
- if CALLING_HOOK_NAME in each_hook.get("command", ""):
298
- all_matchers.append(each_group.get("matcher", ""))
299
- return all_matchers
300
-
301
-
302
- def test_gate_is_registered_on_a_matcher_covering_both_spawn_tools() -> None:
303
- all_matchers = _matchers_registering_the_gate()
304
- assert len(all_matchers) == 1
305
- assert AGENT_TOOL_NAME in all_matchers[0]
306
- assert TASK_TOOL_NAME in all_matchers[0]
307
-
308
-
309
- def _bind_section_text() -> str:
310
- """Return the advisor-protocol warm-up section that documents the bind.
311
-
312
- Returns:
313
- The section text running from its heading to the next heading.
314
- """
315
- section_start = _ADVISOR_PROTOCOL_TEXT.index(_BIND_SECTION_HEADING)
316
- section_body = _ADVISOR_PROTOCOL_TEXT[section_start + len(_BIND_SECTION_HEADING) :]
317
- next_heading_start = section_body.find(_MARKDOWN_SECTION_HEADING_PREFIX)
318
- if next_heading_start < 0:
319
- return section_body
320
- return section_body[:next_heading_start]
321
-
322
-
323
- def test_advisor_protocol_bind_section_names_the_marker_token() -> None:
324
- assert FABLE_SPAWN_AUTHORIZATION_MARKER in _bind_section_text()
325
-
326
-
327
- @pytest.mark.parametrize("skill_path", _ALL_CONSUMING_SKILL_PATHS, ids=_skill_pin_ids)
328
- def test_consuming_skill_names_the_marker_token(skill_path: pathlib.Path) -> None:
329
- assert FABLE_SPAWN_AUTHORIZATION_MARKER in skill_path.read_text(encoding="utf-8")
330
-
331
-
332
- def _paragraph_starting_at(source_text: str, paragraph_marker: str) -> str:
333
- """Return the advisor-doc paragraph that opens with a marker.
334
-
335
- Args:
336
- source_text: The advisor document text holding the paragraph.
337
- paragraph_marker: The literal text opening the paragraph.
338
-
339
- Returns:
340
- The paragraph text, running from that marker to the blank line that
341
- closes it.
342
- """
343
- paragraph_body = source_text[source_text.index(paragraph_marker) :]
344
- paragraph_end = paragraph_body.find(_PARAGRAPH_SEPARATOR)
345
- if paragraph_end < 0:
346
- return paragraph_body
347
- return paragraph_body[:paragraph_end]
348
-
349
-
350
- def _respawn_spawn_prompt() -> str:
351
- """Assemble the spawn prompt a drift re-spawn writes from the lifecycle doc.
352
-
353
- The prompt comes from the re-spawn paragraph alone, so the gate reads
354
- what that one paragraph tells a session to send.
355
-
356
- Returns:
357
- The spawn prompt text a session following that paragraph sends.
358
- """
359
- return _paragraph_starting_at(_ADVISOR_LIFECYCLE_TEXT, _RESPAWN_PARAGRAPH_MARKER)
360
-
361
-
362
- def test_respawn_paragraph_prompt_passes_the_gate_at_the_fable_tier() -> None:
363
- payload = _spawn_payload(prompt=_respawn_spawn_prompt())
364
- assert _run_main_with_io(json.dumps(payload)) == ""
365
-
366
-
367
- def _warm_up_spawn_prompt() -> str:
368
- """Assemble the spawn prompt a warm-up bind writes from the warm-up doc.
369
-
370
- The prompt comes from the spawn-field prompt bullet alone, so the gate
371
- reads what that one bullet tells a session to send.
372
-
373
- Returns:
374
- The spawn prompt text a session following that bullet sends.
375
- """
376
- return _paragraph_starting_at(_ADVISOR_WARM_UP_TEXT, _WARM_UP_PROMPT_BULLET_MARKER)
377
-
378
-
379
- def test_warm_up_prompt_bullet_passes_the_gate_at_the_fable_tier() -> None:
380
- payload = _spawn_payload(prompt=_warm_up_spawn_prompt())
381
- assert _run_main_with_io(json.dumps(payload)) == ""
@@ -1,154 +0,0 @@
1
- """End-to-end tests for the inventory/orphan pending-intent deadlock resolution.
2
-
3
- Each test drives the two real hook binaries through their production ``main()``
4
- stdin path over a temp directory tree whose ``CLAUDE.md`` inventory names two
5
- sibling files. A shared ``session_id`` and an isolated ``TMPDIR`` make the two
6
- hooks read one shared intent-records file, so a deny by one hook opens the gate
7
- for the sibling's second write — in either add order.
8
- """
9
-
10
- import json
11
- import os
12
- import subprocess
13
- import sys
14
- from pathlib import Path
15
-
16
- BLOCKING_DIRECTORY = Path(__file__).resolve().parent
17
-
18
- INVENTORY_HOOK = "package_inventory_stale_blocker.py"
19
- ORPHAN_HOOK = "claude_md_orphan_file_blocker.py"
20
-
21
- SESSION_ID = "deadlock-session"
22
-
23
- INVENTORY_TABLE_HEADER = "# pkg\n\n| File | Purpose |\n|---|---|\n"
24
- ALPHA_ROW = "| `alpha.py` | Does A |\n"
25
- BETA_ROW = "| `beta.py` | Does B |\n"
26
- GAMMA_ROW = "| `gamma.py` | Does G |\n"
27
- EPSILON_ROW = "| `epsilon.py` | Does E |\n"
28
-
29
- BASE_INVENTORY = INVENTORY_TABLE_HEADER + ALPHA_ROW + BETA_ROW
30
- INVENTORY_WITH_GAMMA = BASE_INVENTORY + GAMMA_ROW
31
- INVENTORY_WITH_EPSILON = BASE_INVENTORY + EPSILON_ROW
32
-
33
- MODULE_BODY = "x = 1\n"
34
-
35
-
36
- def _package_directory(tmp_path: Path) -> Path:
37
- """Return a package directory holding a CLAUDE.md table naming two siblings."""
38
- package_directory = tmp_path / "pkg"
39
- package_directory.mkdir()
40
- (package_directory / "CLAUDE.md").write_text(BASE_INVENTORY, encoding="utf-8")
41
- (package_directory / "alpha.py").write_text(MODULE_BODY, encoding="utf-8")
42
- (package_directory / "beta.py").write_text(MODULE_BODY, encoding="utf-8")
43
- return package_directory
44
-
45
-
46
- def _run_hook(
47
- hook_filename: str, tool_name: str, tool_input: dict, temp_directory: Path
48
- ) -> subprocess.CompletedProcess:
49
- """Run one hook binary over a payload, sharing an isolated intent-records store."""
50
- payload = json.dumps(
51
- {"tool_name": tool_name, "tool_input": tool_input, "session_id": SESSION_ID}
52
- )
53
- hook_environment = dict(os.environ)
54
- hook_environment["TMPDIR"] = str(temp_directory)
55
- return subprocess.run(
56
- [sys.executable, str(BLOCKING_DIRECTORY / hook_filename)],
57
- input=payload,
58
- capture_output=True,
59
- text=True,
60
- check=False,
61
- env=hook_environment,
62
- )
63
-
64
-
65
- def _decision(completed: subprocess.CompletedProcess) -> str | None:
66
- """Return the permission decision a hook emitted, or None when it allowed."""
67
- if not completed.stdout.strip():
68
- return None
69
- return json.loads(completed.stdout)["hookSpecificOutput"]["permissionDecision"]
70
-
71
-
72
- def _reason(completed: subprocess.CompletedProcess) -> str:
73
- """Return the deny reason a hook emitted."""
74
- return json.loads(completed.stdout)["hookSpecificOutput"]["permissionDecisionReason"]
75
-
76
-
77
- def test_file_first_order_resolves_after_one_deny(tmp_path: Path) -> None:
78
- package_directory = _package_directory(tmp_path)
79
- new_file = package_directory / "gamma.py"
80
- claude_md = package_directory / "CLAUDE.md"
81
-
82
- first_write = _run_hook(
83
- INVENTORY_HOOK, "Write", {"file_path": str(new_file), "content": MODULE_BODY}, tmp_path
84
- )
85
- assert _decision(first_write) == "deny"
86
- assert "gamma.py" in _reason(first_write)
87
- assert "recorded" in _reason(first_write)
88
-
89
- row_write = _run_hook(
90
- ORPHAN_HOOK,
91
- "Write",
92
- {"file_path": str(claude_md), "content": INVENTORY_WITH_GAMMA},
93
- tmp_path,
94
- )
95
- assert _decision(row_write) is None
96
-
97
- claude_md.write_text(INVENTORY_WITH_GAMMA, encoding="utf-8")
98
- retry_write = _run_hook(
99
- INVENTORY_HOOK, "Write", {"file_path": str(new_file), "content": MODULE_BODY}, tmp_path
100
- )
101
- assert _decision(retry_write) is None
102
-
103
-
104
- def test_row_first_order_resolves_after_one_deny(tmp_path: Path) -> None:
105
- package_directory = _package_directory(tmp_path)
106
- new_file = package_directory / "gamma.py"
107
- claude_md = package_directory / "CLAUDE.md"
108
-
109
- first_row = _run_hook(
110
- ORPHAN_HOOK,
111
- "Write",
112
- {"file_path": str(claude_md), "content": INVENTORY_WITH_GAMMA},
113
- tmp_path,
114
- )
115
- assert _decision(first_row) == "deny"
116
- assert "gamma.py" in _reason(first_row)
117
- assert "recorded" in _reason(first_row)
118
-
119
- file_write = _run_hook(
120
- INVENTORY_HOOK, "Write", {"file_path": str(new_file), "content": MODULE_BODY}, tmp_path
121
- )
122
- assert _decision(file_write) is None
123
-
124
- new_file.write_text(MODULE_BODY, encoding="utf-8")
125
- retry_row = _run_hook(
126
- ORPHAN_HOOK,
127
- "Write",
128
- {"file_path": str(claude_md), "content": INVENTORY_WITH_GAMMA},
129
- tmp_path,
130
- )
131
- assert _decision(retry_row) is None
132
-
133
-
134
- def test_new_file_with_no_row_attempt_stays_blocked(tmp_path: Path) -> None:
135
- package_directory = _package_directory(tmp_path)
136
- new_file = package_directory / "delta.py"
137
- file_write = _run_hook(
138
- INVENTORY_HOOK, "Write", {"file_path": str(new_file), "content": MODULE_BODY}, tmp_path
139
- )
140
- assert _decision(file_write) == "deny"
141
- assert "delta.py" in _reason(file_write)
142
-
143
-
144
- def test_row_naming_uncreated_file_stays_blocked(tmp_path: Path) -> None:
145
- package_directory = _package_directory(tmp_path)
146
- claude_md = package_directory / "CLAUDE.md"
147
- row_write = _run_hook(
148
- ORPHAN_HOOK,
149
- "Write",
150
- {"file_path": str(claude_md), "content": INVENTORY_WITH_EPSILON},
151
- tmp_path,
152
- )
153
- assert _decision(row_write) == "deny"
154
- assert "epsilon.py" in _reason(row_write)