claude-dev-env 2.3.0 → 2.5.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 (290) hide show
  1. package/CLAUDE.md +53 -48
  2. package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
  3. package/_shared/pr-loop/scripts/code_rules_gate.py +4 -2
  4. package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
  5. package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
  6. package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
  7. package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
  8. package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
  9. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
  10. package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
  11. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
  12. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
  13. package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
  14. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
  15. package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
  16. package/_shared/pr-loop/worker-spawn.md +1 -1
  17. package/agents/CLAUDE.md +3 -1
  18. package/agents/caveman.md +0 -1
  19. package/agents/clasp-deployment-orchestrator.md +0 -1
  20. package/agents/clean-coder.md +0 -1
  21. package/agents/code-advisor.md +0 -1
  22. package/agents/code-quality-agent.md +1 -2
  23. package/agents/code-verifier.md +3 -4
  24. package/agents/deep-research.md +0 -1
  25. package/agents/docs-agent.md +0 -1
  26. package/agents/git-commit-crafter.md +0 -1
  27. package/agents/issue-tracker.md +42 -0
  28. package/agents/plan-packet-validator.md +0 -1
  29. package/agents/pr-description-writer.md +0 -1
  30. package/agents/skill-writer-agent.md +84 -0
  31. package/agents/test_agent_frontmatter.py +67 -18
  32. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +105 -3
  33. package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +29 -13
  34. package/bin/CLAUDE.md +68 -5
  35. package/bin/ever-shipped-skills.mjs +1 -0
  36. package/bin/install-constants.mjs +88 -0
  37. package/bin/install.mjs +1138 -114
  38. package/bin/install.prune.test.mjs +869 -19
  39. package/bin/install.test.mjs +906 -2
  40. package/commands/implement.md +1 -1
  41. package/commands/right-size.md +1 -1
  42. package/docs/CLAUDE.md +2 -0
  43. package/docs/host-pool-health-monitor.md +102 -0
  44. package/docs/references/CLAUDE.md +5 -2
  45. package/docs/references/advisor-tool.md +13 -0
  46. package/docs/references/code-review-enforcement.md +107 -0
  47. package/docs/references/team-advisor-skill.md +14 -0
  48. package/docs/wsl-docker-cowork-starter-matrix.md +89 -0
  49. package/hooks/blocking/CLAUDE.md +9 -1
  50. package/hooks/blocking/code_review_enforcement_config_bootstrap.py +53 -0
  51. package/hooks/blocking/code_review_gate_deny.py +74 -0
  52. package/hooks/blocking/code_review_pr_create_gate.py +198 -0
  53. package/hooks/blocking/code_review_push_gate.py +145 -0
  54. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +348 -0
  55. package/hooks/blocking/code_review_stamp_store.py +233 -0
  56. package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +7 -0
  57. package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +15 -0
  58. package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +212 -0
  59. package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +138 -0
  60. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +49 -0
  61. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +38 -0
  62. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +39 -27
  63. package/hooks/blocking/config/__init__.py +5 -5
  64. package/hooks/blocking/config/code_review_enforcement_constants.py +113 -0
  65. package/hooks/blocking/config/test_code_review_enforcement_constants.py +113 -0
  66. package/hooks/blocking/config/verified_commit_constants.py +160 -155
  67. package/hooks/blocking/conftest.py +2 -0
  68. package/hooks/blocking/convergence_gate_blocker.py +112 -23
  69. package/hooks/blocking/destructive_command_blocker.py +19 -6
  70. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
  71. package/hooks/blocking/pr_description_proof_of_work.py +52 -34
  72. package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
  73. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +4 -1
  74. package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +62 -0
  75. package/hooks/blocking/test_code_review_gate_deny.py +54 -0
  76. package/hooks/blocking/test_code_review_pr_create_gate.py +199 -0
  77. package/hooks/blocking/test_code_review_push_gate.py +205 -0
  78. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +199 -0
  79. package/hooks/blocking/test_code_review_stamp_store.py +205 -0
  80. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +124 -2
  81. package/hooks/blocking/test_convergence_gate_blocker.py +153 -5
  82. package/hooks/blocking/test_destructive_command_blocker.py +1 -1
  83. package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +45 -0
  84. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
  85. package/hooks/blocking/test_pr_description_proof_of_work.py +151 -0
  86. package/hooks/blocking/test_pre_tool_use_dispatcher.py +17 -8
  87. package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
  88. package/hooks/blocking/test_verification_verdict_store.py +974 -903
  89. package/hooks/blocking/test_verified_commit_gate.py +581 -581
  90. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
  91. package/hooks/blocking/test_volatile_path_in_post_blocker.py +114 -2
  92. package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
  93. package/hooks/blocking/verification_verdict_store.py +1039 -1014
  94. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +29 -17
  95. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +35 -0
  96. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
  97. package/hooks/blocking/verifier_verdict_minter.py +280 -280
  98. package/hooks/blocking/volatile_path_in_post_blocker.py +69 -8
  99. package/hooks/git-hooks/git_hooks_constants/__init__.py +6 -0
  100. package/hooks/git-hooks/pre_push.py +89 -2
  101. package/hooks/git-hooks/test_pre_push.py +128 -0
  102. package/hooks/hooks.json +26 -1
  103. package/hooks/hooks_constants/CLAUDE.md +3 -1
  104. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +8 -0
  105. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
  106. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +26 -11
  107. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +20 -3
  108. package/hooks/hooks_constants/destructive_command_segment_constants.py +3 -1
  109. package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
  110. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
  111. package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -4
  112. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
  113. package/hooks/hooks_constants/pyproject_config_discovery_constants.py +16 -0
  114. package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
  115. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +24 -0
  116. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +6 -0
  117. package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +8 -1
  118. package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
  119. package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
  120. package/hooks/pyproject.toml +1 -0
  121. package/hooks/validators/CLAUDE.md +2 -0
  122. package/hooks/validators/config/__init__.py +0 -0
  123. package/hooks/validators/config/directory_exemption_constants.py +183 -0
  124. package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
  125. package/hooks/validators/conftest.py +4 -0
  126. package/hooks/validators/mypy_integration.py +63 -50
  127. package/hooks/validators/pyproject_config_discovery.py +101 -0
  128. package/hooks/validators/ruff_integration.py +257 -25
  129. package/hooks/validators/run_all_validators.py +223 -19
  130. package/hooks/validators/test_directory_exemption_constants.py +185 -0
  131. package/hooks/validators/test_mypy_integration.py +32 -0
  132. package/hooks/validators/test_pyproject_config_discovery.py +94 -0
  133. package/hooks/validators/test_python_antipattern_checks.py +110 -5
  134. package/hooks/validators/test_ruff_integration.py +160 -2
  135. package/hooks/validators/test_run_all_validators.py +140 -68
  136. package/hooks/validators/test_run_all_validators_config_discovery.py +123 -0
  137. package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
  138. package/package.json +10 -2
  139. package/rules/CLAUDE.md +1 -0
  140. package/rules/docstring-prose-matches-implementation.md +45 -67
  141. package/rules/durable-post-artifacts.md +7 -0
  142. package/rules/state-what-is.md +25 -0
  143. package/rules/verified-commit-gate-skip.md +1 -1
  144. package/scripts/CLAUDE.md +1 -0
  145. package/scripts/Capture-PoolHealth.ps1 +410 -0
  146. package/scripts/_code_review_test_support.py +404 -0
  147. package/scripts/claude_chain_runner.py +141 -1
  148. package/scripts/codec_forwarding_test_support.py +83 -0
  149. package/scripts/conftest.py +23 -0
  150. package/scripts/dev_env_scripts_constants/CLAUDE.md +2 -2
  151. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +53 -1
  152. package/scripts/dev_env_scripts_constants/code_review_constants.py +129 -12
  153. package/scripts/dev_env_scripts_constants/test_code_review_constants.py +55 -0
  154. package/scripts/invoke_code_review.py +550 -38
  155. package/scripts/resolve_worker_spawn.py +626 -619
  156. package/scripts/spawn_grok_batch.py +672 -672
  157. package/scripts/test_claude_chain_runner.py +131 -0
  158. package/scripts/test_invoke_code_review_chain.py +70 -0
  159. package/scripts/test_invoke_code_review_cli.py +192 -0
  160. package/scripts/test_invoke_code_review_codec.py +77 -0
  161. package/scripts/test_invoke_code_review_contract.py +256 -0
  162. package/scripts/test_invoke_code_review_git.py +123 -0
  163. package/scripts/test_invoke_code_review_mode.py +99 -0
  164. package/scripts/test_resolve_worker_spawn.py +1014 -1014
  165. package/scripts/test_resolve_worker_spawn_codec.py +101 -0
  166. package/skills/CLAUDE.md +2 -0
  167. package/skills/auditing-claude-config/SKILL.md +114 -114
  168. package/skills/autoconverge/SKILL.md +427 -421
  169. package/skills/autoconverge/reference/convergence.md +26 -4
  170. package/skills/autoconverge/reference/multi-pr.md +6 -1
  171. package/skills/autoconverge/reference/stop-conditions.md +16 -10
  172. package/skills/autoconverge/workflow/CLAUDE.md +1 -0
  173. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +4 -4
  174. package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +175 -3
  175. package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1244
  176. package/skills/autoconverge/workflow/converge.mjs +191 -8
  177. package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
  178. package/skills/autoconverge/workflow/converge_multi.mjs +7 -3
  179. package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +5 -0
  180. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
  181. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
  182. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
  183. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
  184. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
  185. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
  186. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
  187. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
  188. package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
  189. package/skills/closeout/SKILL.md +33 -50
  190. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
  191. package/skills/codex-review/scripts/run_codex_review.py +233 -1
  192. package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
  193. package/skills/condensing-instructions/SKILL.md +81 -0
  194. package/skills/copilot-review/SKILL.md +119 -119
  195. package/skills/e-code-review/SKILL.md +52 -0
  196. package/skills/e-code-review/reference/fix.md +54 -0
  197. package/skills/e-code-review/reference/loop.md +43 -0
  198. package/skills/e-code-review/reference/low.md +57 -0
  199. package/skills/e-code-review/reference/medium.md +153 -0
  200. package/skills/e-code-review/reference/xhigh.md +182 -0
  201. package/skills/e-simplify/SKILL.md +97 -0
  202. package/skills/fresh-branch/CLAUDE.md +2 -0
  203. package/skills/fresh-branch/SKILL.md +2 -0
  204. package/skills/fresh-branch/scripts/create_fresh_branch.py +78 -180
  205. package/skills/fresh-branch/scripts/fresh_branch_git_commands.py +285 -0
  206. package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -0
  207. package/skills/fresh-branch/scripts/pytest.ini +4 -0
  208. package/skills/fresh-branch/scripts/test_create_fresh_branch.py +98 -0
  209. package/skills/fresh-branch/scripts/test_fresh_branch_git_commands.py +310 -0
  210. package/skills/issue-tracker/SKILL.md +92 -0
  211. package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
  212. package/skills/issue-tracker/reference/handoff-schema.md +64 -0
  213. package/skills/issue-tracker/reference/operation-matrix.md +41 -0
  214. package/skills/orchestrator/SKILL.md +162 -21
  215. package/skills/orchestrator/scripts/status_gate.py +625 -0
  216. package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
  217. package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
  218. package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
  219. package/skills/orchestrator/scripts/test_status_gate.py +439 -0
  220. package/skills/orchestrator-refresh/SKILL.md +110 -35
  221. package/skills/plan-to-pr/SKILL.md +155 -0
  222. package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
  223. package/skills/plan-to-pr/reference/model-routing.md +36 -0
  224. package/skills/plan-to-pr/reference/packet-contract.md +43 -0
  225. package/skills/plan-to-pr/reference/packet-schema.json +57 -0
  226. package/skills/plan-to-pr/reference/process-inventory.md +22 -0
  227. package/skills/plan-to-pr/reference/review-loop.md +33 -0
  228. package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
  229. package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
  230. package/skills/plan-to-pr/reference/task-seeds.md +14 -0
  231. package/skills/plan-to-pr/reference/task-ticket.md +38 -0
  232. package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
  233. package/skills/plan-to-pr/scripts/config/constants.py +193 -0
  234. package/skills/plan-to-pr/scripts/create_packet.py +173 -0
  235. package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
  236. package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
  237. package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
  238. package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
  239. package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
  240. package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
  241. package/skills/plan-to-pr/scripts/validate_run.py +173 -0
  242. package/skills/plan-to-pr/test_skill_contract.py +207 -0
  243. package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
  244. package/skills/pr-converge/SKILL.md +472 -469
  245. package/skills/pr-converge/reference/examples.md +3 -3
  246. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  247. package/skills/pr-converge/reference/ground-rules.md +7 -4
  248. package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
  249. package/skills/pr-converge/reference/per-tick.md +5 -5
  250. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  251. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
  252. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
  253. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
  254. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  255. package/skills/pr-fix-protocol/SKILL.md +1 -1
  256. package/skills/privacy-hygiene/SKILL.md +68 -68
  257. package/skills/prototype/SKILL.md +86 -0
  258. package/skills/prototype/reference/honest-limitations.md +23 -0
  259. package/skills/prototype/reference/promotion-tasks.md +23 -0
  260. package/skills/prototype/scripts/build_sandbox_settings.py +249 -0
  261. package/skills/prototype/scripts/conftest.py +15 -0
  262. package/skills/prototype/scripts/launch_sandbox.py +205 -0
  263. package/skills/prototype/scripts/probe_sandbox_safety.py +311 -0
  264. package/skills/prototype/scripts/prototype_scripts_constants/__init__.py +1 -0
  265. package/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  266. package/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +41 -0
  267. package/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +23 -0
  268. package/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +45 -0
  269. package/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +10 -0
  270. package/skills/prototype/scripts/test_build_sandbox_settings.py +275 -0
  271. package/skills/prototype/scripts/test_launch_sandbox.py +303 -0
  272. package/skills/prototype/scripts/test_probe_sandbox_safety.py +284 -0
  273. package/skills/prototype/workflows/promotion.md +27 -0
  274. package/skills/prototype/workflows/sandbox.md +35 -0
  275. package/skills/release-notes-html/SKILL.md +164 -0
  276. package/skills/skill-builder/CLAUDE.md +3 -3
  277. package/skills/skill-builder/SKILL.md +5 -5
  278. package/skills/skill-builder/references/CLAUDE.md +1 -1
  279. package/skills/skill-builder/references/delegation-map.md +3 -3
  280. package/skills/skill-builder/references/description-field.md +1 -1
  281. package/skills/skill-builder/references/skill-modularity.md +2 -3
  282. package/skills/skill-builder/workflows/CLAUDE.md +1 -1
  283. package/skills/skill-builder/workflows/improve-skill.md +1 -1
  284. package/skills/skill-builder/workflows/new-skill.md +2 -2
  285. package/skills/task-build/CLAUDE.md +8 -7
  286. package/skills/task-build/SKILL.md +16 -8
  287. package/skills/task-build/reference/tool-routing.md +19 -0
  288. package/skills/team-advisor/SKILL.md +2 -2
  289. package/scripts/test_invoke_code_review.py +0 -672
  290. package/skills/closeout/reference/issue-body-templates.md +0 -108
@@ -0,0 +1,410 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Captures Windows pool counters, handle pressure, and kernel pool tags for host health checks.
4
+
5
+ .DESCRIPTION
6
+ Reads memory pool counters, total handle/thread counts, processes over the handle alert
7
+ threshold, and top kernel pool tags via NtQuerySystemInformation (class 22). Prints a
8
+ threshold verdict so an operator can re-run the same recipe from a clean shell.
9
+
10
+ Alert thresholds (defaults):
11
+ - Pool Nonpaged Bytes > 2 GB
12
+ - Any process Handles > 2000
13
+ - Process(_Total) handles > 500000
14
+
15
+ Attribution context (evidence pack results/04-pool-tags.md): dominant tags File (nonpaged)
16
+ and Toke (paged) mean process-object / object-manager pressure. Remediate via RC2 (MCP
17
+ lifecycle), RC3 (Show-Asset orphans), RC4 (Git find handle storms) — not a single driver.
18
+
19
+ .PARAMETER TopTagCount
20
+ How many pool tags to print per nonpaged and paged ranking. Default 20.
21
+
22
+ .PARAMETER TopProcessCount
23
+ How many high-handle processes to list. Default 15.
24
+
25
+ .PARAMETER OutPath
26
+ Optional file path. When set, the full report is also written there as UTF-8 text.
27
+
28
+ .PARAMETER NonpagedAlertBytes
29
+ Nonpaged pool alert threshold in bytes. Default 2147483648 (2 GB).
30
+
31
+ .PARAMETER ProcessHandleAlert
32
+ Per-process handle alert threshold. Default 2000.
33
+
34
+ .PARAMETER TotalHandleAlert
35
+ System-wide handle alert threshold. Default 500000.
36
+
37
+ .EXAMPLE
38
+ pwsh -NoProfile -File "$HOME\.claude\scripts\Capture-PoolHealth.ps1"
39
+
40
+ .EXAMPLE
41
+ pwsh -NoProfile -File .\Capture-PoolHealth.ps1 -OutPath "$env:TEMP\pool-health.txt"
42
+ #>
43
+ [CmdletBinding()]
44
+ param(
45
+ [int]$TopTagCount = 20,
46
+ [int]$TopProcessCount = 15,
47
+ [string]$OutPath = '',
48
+ [long]$NonpagedAlertBytes = 2147483648,
49
+ [int]$ProcessHandleAlert = 2000,
50
+ [int]$TotalHandleAlert = 500000
51
+ )
52
+
53
+ Set-StrictMode -Version Latest
54
+ $ErrorActionPreference = 'Stop'
55
+
56
+ $script:ReportLines = [System.Collections.Generic.List[string]]::new()
57
+
58
+ function Write-ReportLine {
59
+ param([string]$Text = '')
60
+ $script:ReportLines.Add($Text)
61
+ Write-Output $Text
62
+ }
63
+
64
+ function Format-BytesAsGigabytes {
65
+ param([long]$ByteCount)
66
+ return ('{0:N3} GB' -f ($ByteCount / 1GB))
67
+ }
68
+
69
+ function Get-MemoryAndHandleCounters {
70
+ # One source of truth for the counters: request order and lookup key both
71
+ # derive from this map, so a name never has to be restated at the call site.
72
+ $counterPathByName = [ordered]@{
73
+ NonpagedBytes = '\Memory\Pool Nonpaged Bytes'
74
+ PagedBytes = '\Memory\Pool Paged Bytes'
75
+ CommittedBytes = '\Memory\Committed Bytes'
76
+ CommitLimit = '\Memory\Commit Limit'
77
+ AvailableMBytes = '\Memory\Available MBytes'
78
+ TotalHandles = '\Process(_Total)\Handle Count'
79
+ TotalThreads = '\Process(_Total)\Thread Count'
80
+ }
81
+ $sample = Get-Counter -Counter @($counterPathByName.Values) -ErrorAction Stop
82
+ $valueByName = @{}
83
+ $index = 0
84
+ foreach ($eachName in $counterPathByName.Keys) {
85
+ $valueByName[$eachName] = [int64]$sample.CounterSamples[$index++].CookedValue
86
+ }
87
+ return $valueByName
88
+ }
89
+
90
+ function Get-HighHandleProcessReport {
91
+ param(
92
+ [int]$HandleThreshold,
93
+ [int]$MaximumRows
94
+ )
95
+ $allHotProcesses = @(
96
+ Get-Process -ErrorAction SilentlyContinue |
97
+ Where-Object { $_.HandleCount -gt $HandleThreshold } |
98
+ Sort-Object -Property HandleCount -Descending
99
+ )
100
+ $topProcesses = @($allHotProcesses | Select-Object -First $MaximumRows)
101
+ $cimByProcessId = @{}
102
+ if ($topProcesses.Count -gt 0) {
103
+ $processIdFilter = @($topProcesses | ForEach-Object { "ProcessId=$($_.Id)" }) -join ' OR '
104
+ try {
105
+ foreach ($eachCimProcess in @(
106
+ Get-CimInstance -ClassName Win32_Process -Filter $processIdFilter `
107
+ -ErrorAction SilentlyContinue)) {
108
+ $cimByProcessId[[int]$eachCimProcess.ProcessId] = $eachCimProcess
109
+ }
110
+ }
111
+ catch {
112
+ $null = $_
113
+ }
114
+ }
115
+ $topRows = foreach ($eachProcess in $topProcesses) {
116
+ $commandLine = ''
117
+ $parentProcessId = 0
118
+ $cimProcess = $cimByProcessId[[int]$eachProcess.Id]
119
+ if ($cimProcess) {
120
+ $commandLine = [string]$cimProcess.CommandLine
121
+ $parentProcessId = [int]$cimProcess.ParentProcessId
122
+ }
123
+ [pscustomobject]@{
124
+ ProcessId = $eachProcess.Id
125
+ Name = $eachProcess.ProcessName
126
+ HandleCount = $eachProcess.HandleCount
127
+ ParentProcessId = $parentProcessId
128
+ CommandLine = $commandLine
129
+ }
130
+ }
131
+ return [pscustomobject]@{
132
+ TotalCount = $allHotProcesses.Count
133
+ TopRows = @($topRows)
134
+ }
135
+ }
136
+
137
+ function Ensure-PoolTagCaptureType {
138
+ if ('PoolTagCapture' -as [type]) {
139
+ return
140
+ }
141
+ $source = @'
142
+ using System;
143
+ using System.Collections.Generic;
144
+ using System.Linq;
145
+ using System.Runtime.InteropServices;
146
+ using System.Text;
147
+
148
+ public static class PoolTagCapture {
149
+ const int SystemPoolTagInformation = 22;
150
+
151
+ [DllImport("ntdll.dll")]
152
+ static extern int NtQuerySystemInformation(
153
+ int systemInformationClass,
154
+ IntPtr systemInformation,
155
+ int systemInformationLength,
156
+ out int returnLength);
157
+
158
+ [StructLayout(LayoutKind.Explicit, Size = 40)]
159
+ struct SYSTEM_POOLTAG {
160
+ [FieldOffset(0)] public byte Tag0;
161
+ [FieldOffset(1)] public byte Tag1;
162
+ [FieldOffset(2)] public byte Tag2;
163
+ [FieldOffset(3)] public byte Tag3;
164
+ [FieldOffset(4)] public uint PagedAllocs;
165
+ [FieldOffset(8)] public uint PagedFrees;
166
+ [FieldOffset(16)] public ulong PagedUsed;
167
+ [FieldOffset(24)] public uint NonPagedAllocs;
168
+ [FieldOffset(28)] public uint NonPagedFrees;
169
+ [FieldOffset(32)] public ulong NonPagedUsed;
170
+ }
171
+
172
+ public static string Capture(int topN) {
173
+ int size = 2 * 1024 * 1024;
174
+ int retLen = 0;
175
+ int status = -1;
176
+ IntPtr buffer = IntPtr.Zero;
177
+ try {
178
+ for (int attempt = 0; attempt < 10; attempt++) {
179
+ buffer = Marshal.AllocHGlobal(size);
180
+ status = NtQuerySystemInformation(
181
+ SystemPoolTagInformation, buffer, size, out retLen);
182
+ if (unchecked((uint)status) == 0xC0000004u) {
183
+ Marshal.FreeHGlobal(buffer);
184
+ buffer = IntPtr.Zero;
185
+ size = Math.Max(retLen + 8192, size * 2);
186
+ continue;
187
+ }
188
+ break;
189
+ }
190
+ if (status != 0) {
191
+ return "NTSTATUS=0x" + unchecked((uint)status).ToString("X8")
192
+ + " retLen=" + retLen;
193
+ }
194
+ int count = Marshal.ReadInt32(buffer);
195
+ int baseOff = 8;
196
+ var tags = new List<SYSTEM_POOLTAG>();
197
+ for (int i = 0; i < count; i++) {
198
+ int offset = baseOff + i * 40;
199
+ if (offset + 40 > retLen) break;
200
+ tags.Add(Marshal.PtrToStructure<SYSTEM_POOLTAG>(
201
+ IntPtr.Add(buffer, offset)));
202
+ }
203
+ long sumNonPaged = 0;
204
+ long sumPaged = 0;
205
+ foreach (var eachTag in tags) {
206
+ sumNonPaged += (long)eachTag.NonPagedUsed;
207
+ sumPaged += (long)eachTag.PagedUsed;
208
+ }
209
+ var report = new StringBuilder();
210
+ report.AppendLine("retLen=" + retLen + " count=" + count
211
+ + " sizeof_tag=40 status=0 baseOff=" + baseOff);
212
+ report.AppendLine(string.Format(
213
+ "SUM NonPaged={0} ({1:N3} GB) Paged={2} ({3:N3} GB)",
214
+ sumNonPaged, sumNonPaged / 1073741824.0,
215
+ sumPaged, sumPaged / 1073741824.0));
216
+ report.AppendLine(string.Format(
217
+ "{0,-6} {1,14} {2,14}", "Tag", "NonPaged", "Paged"));
218
+ report.AppendLine("--- TOP BY NONPAGED ---");
219
+ foreach (var eachTag in tags.OrderByDescending(t => t.NonPagedUsed).Take(topN)) {
220
+ report.AppendLine(FormatTag(eachTag));
221
+ }
222
+ report.AppendLine("--- TOP BY PAGED ---");
223
+ foreach (var eachTag in tags.OrderByDescending(t => t.PagedUsed).Take(topN)) {
224
+ report.AppendLine(FormatTag(eachTag));
225
+ }
226
+ var watchTagNames = new[] { "File", "IoFE", "Toke", "Key", "FMfn" };
227
+ var watchNames = new HashSet<string>(watchTagNames, StringComparer.Ordinal);
228
+ report.AppendLine("--- WATCHLIST " + string.Join(" ", watchTagNames) + " ---");
229
+ foreach (var eachTag in tags) {
230
+ string cleanedName = TagName(eachTag).TrimEnd(' ', '.', '\0');
231
+ if (watchNames.Contains(cleanedName)) {
232
+ report.AppendLine(FormatTag(eachTag));
233
+ }
234
+ }
235
+ return report.ToString().TrimEnd();
236
+ }
237
+ finally {
238
+ if (buffer != IntPtr.Zero) {
239
+ Marshal.FreeHGlobal(buffer);
240
+ }
241
+ }
242
+ }
243
+
244
+ static string TagName(SYSTEM_POOLTAG tag) {
245
+ return string.Concat(CharOf(tag.Tag0), CharOf(tag.Tag1),
246
+ CharOf(tag.Tag2), CharOf(tag.Tag3));
247
+ }
248
+
249
+ static char CharOf(byte value) {
250
+ return (value >= 32 && value < 127) ? (char)value : '.';
251
+ }
252
+
253
+ static string FormatTag(SYSTEM_POOLTAG tag) {
254
+ return string.Format("{0,-6} {1,14} {2,14}",
255
+ TagName(tag), tag.NonPagedUsed, tag.PagedUsed);
256
+ }
257
+ }
258
+ '@
259
+ Add-Type -TypeDefinition $source -Language CSharp
260
+ }
261
+
262
+ function Get-PoolTagReport {
263
+ param([int]$TopCount)
264
+ Ensure-PoolTagCaptureType
265
+ return [PoolTagCapture]::Capture($TopCount)
266
+ }
267
+
268
+ function Get-ThresholdAlerts {
269
+ param(
270
+ [int64]$NonpagedBytes,
271
+ [int64]$TotalHandles,
272
+ [int]$HighHandleProcessCount,
273
+ [long]$NonpagedAlert,
274
+ [int]$ProcessHandleAlert,
275
+ [int]$TotalHandleAlert
276
+ )
277
+ $allAlerts = [System.Collections.Generic.List[string]]::new()
278
+ if ($NonpagedBytes -gt $NonpagedAlert) {
279
+ $allAlerts.Add((
280
+ "ALERT nonpaged={0} threshold={1}" -f
281
+ (Format-BytesAsGigabytes $NonpagedBytes),
282
+ (Format-BytesAsGigabytes $NonpagedAlert)
283
+ ))
284
+ }
285
+ if ($TotalHandles -gt $TotalHandleAlert) {
286
+ $allAlerts.Add((
287
+ "ALERT total_handles={0} threshold={1}" -f $TotalHandles, $TotalHandleAlert
288
+ ))
289
+ }
290
+ if ($HighHandleProcessCount -gt 0) {
291
+ $allAlerts.Add((
292
+ "ALERT processes_over_{0}_handles={1}" -f
293
+ $ProcessHandleAlert, $HighHandleProcessCount
294
+ ))
295
+ }
296
+ return , $allAlerts
297
+ }
298
+
299
+ # --- main ---
300
+ $timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz'
301
+ Write-ReportLine "TIMESTAMP: $timestamp"
302
+ Write-ReportLine "HOST: $env:COMPUTERNAME"
303
+ Write-ReportLine (
304
+ "THRESHOLDS: nonpaged>{0}; process_handles>{1}; total_handles>{2}" -f
305
+ (Format-BytesAsGigabytes $NonpagedAlertBytes),
306
+ $ProcessHandleAlert,
307
+ $TotalHandleAlert
308
+ )
309
+
310
+ Write-ReportLine ''
311
+ Write-ReportLine '=== COUNTERS ==='
312
+ $counterByName = Get-MemoryAndHandleCounters
313
+ $nonpagedBytes = $counterByName['NonpagedBytes']
314
+ $pagedBytes = $counterByName['PagedBytes']
315
+ $committedBytes = $counterByName['CommittedBytes']
316
+ $commitLimitBytes = $counterByName['CommitLimit']
317
+ $availableMBytes = $counterByName['AvailableMBytes']
318
+ $totalHandles = $counterByName['TotalHandles']
319
+ $totalThreads = $counterByName['TotalThreads']
320
+
321
+ Write-ReportLine ("Pool Nonpaged Bytes = {0} ({1})" -f $nonpagedBytes, (Format-BytesAsGigabytes $nonpagedBytes))
322
+ Write-ReportLine ("Pool Paged Bytes = {0} ({1})" -f $pagedBytes, (Format-BytesAsGigabytes $pagedBytes))
323
+ Write-ReportLine ("Committed Bytes = {0} ({1})" -f $committedBytes, (Format-BytesAsGigabytes $committedBytes))
324
+ Write-ReportLine ("Commit Limit = {0} ({1})" -f $commitLimitBytes, (Format-BytesAsGigabytes $commitLimitBytes))
325
+ Write-ReportLine ("Available MBytes = {0}" -f $availableMBytes)
326
+ Write-ReportLine ("Handle Count Total = {0}" -f $totalHandles)
327
+ Write-ReportLine ("Thread Count Total = {0}" -f $totalThreads)
328
+
329
+ Write-ReportLine ''
330
+ Write-ReportLine ("=== PROCESSES WITH HANDLES > {0} ===" -f $ProcessHandleAlert)
331
+ $highHandleReport = Get-HighHandleProcessReport `
332
+ -HandleThreshold $ProcessHandleAlert `
333
+ -MaximumRows $TopProcessCount
334
+ if ($highHandleReport.TopRows.Count -eq 0) {
335
+ Write-ReportLine '(none)'
336
+ }
337
+ else {
338
+ foreach ($eachProcess in $highHandleReport.TopRows) {
339
+ $commandPreview = ''
340
+ if ($eachProcess.CommandLine) {
341
+ $commandPreview = $eachProcess.CommandLine
342
+ if ($commandPreview.Length -gt 120) {
343
+ $commandPreview = $commandPreview.Substring(0, 117) + '...'
344
+ }
345
+ }
346
+ Write-ReportLine (
347
+ "PID={0} Name={1} Handles={2} Parent={3} Cmd={4}" -f
348
+ $eachProcess.ProcessId,
349
+ $eachProcess.Name,
350
+ $eachProcess.HandleCount,
351
+ $eachProcess.ParentProcessId,
352
+ $commandPreview
353
+ )
354
+ }
355
+ }
356
+
357
+ $highHandleProcessCount = $highHandleReport.TotalCount
358
+ Write-ReportLine ("High-handle process count (all): {0}" -f $highHandleProcessCount)
359
+
360
+ Write-ReportLine ''
361
+ Write-ReportLine '=== POOL TAGS (NtQuerySystemInformation class 22) ==='
362
+ try {
363
+ Write-ReportLine (Get-PoolTagReport -TopCount $TopTagCount)
364
+ }
365
+ catch {
366
+ Write-ReportLine ("POOL TAG CAPTURE FAILED: {0}" -f $_.Exception.Message)
367
+ }
368
+
369
+ Write-ReportLine ''
370
+ Write-ReportLine '=== REMEDIATION MAP (process-object pressure) ==='
371
+ Write-ReportLine 'Dominant tags File (nonpaged) + Toke (paged) -> object-manager sprawl, not a lone driver leak.'
372
+ Write-ReportLine 'RC2 #255 Cap MCP servers (mcpvault/playwright/serena spawn-without-reap) -> cuts File/Toke/Thre'
373
+ Write-ReportLine 'RC3 #254 Show-Asset.ps1 exit on parent death + timeout -> cuts orphan UI process handles'
374
+ Write-ReportLine 'RC4 #253 Block Git find walks; es.exe primary; kill find with handles >2k -> cuts Key/handle storms'
375
+ Write-ReportLine 'RC5 #256 Attribute WSL/Docker starters only after measurement (VdMm secondary)'
376
+ Write-ReportLine 'Optional: install WDK poolmon.exe for stock binary snapshots (this script needs no WDK).'
377
+
378
+ $allThresholdAlerts = Get-ThresholdAlerts `
379
+ -NonpagedBytes $nonpagedBytes `
380
+ -TotalHandles $totalHandles `
381
+ -HighHandleProcessCount $highHandleProcessCount `
382
+ -NonpagedAlert $NonpagedAlertBytes `
383
+ -ProcessHandleAlert $ProcessHandleAlert `
384
+ -TotalHandleAlert $TotalHandleAlert
385
+ Write-ReportLine ''
386
+ Write-ReportLine '=== THRESHOLD VERDICT ==='
387
+ if ($allThresholdAlerts.Count -eq 0) {
388
+ Write-ReportLine 'OK - all thresholds clear'
389
+ $thresholdExitCode = 0
390
+ }
391
+ else {
392
+ foreach ($eachAlert in $allThresholdAlerts) {
393
+ Write-ReportLine $eachAlert
394
+ }
395
+ Write-ReportLine 'REMEDIATE: process-object pressure (File/Toke tags) -> RC2 #255, RC3 #254, RC4 #253'
396
+ $thresholdExitCode = 1
397
+ }
398
+
399
+ if ($OutPath) {
400
+ $destinationDirectory = Split-Path -Parent $OutPath
401
+ if ($destinationDirectory -and -not (Test-Path -LiteralPath $destinationDirectory)) {
402
+ New-Item -ItemType Directory -Path $destinationDirectory -Force | Out-Null
403
+ }
404
+ $script:ReportLines -join [Environment]::NewLine |
405
+ Set-Content -LiteralPath $OutPath -Encoding utf8
406
+ Write-Output ""
407
+ Write-Output "Wrote report: $OutPath"
408
+ }
409
+
410
+ exit $thresholdExitCode