claude-dev-env 2.4.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 (195) hide show
  1. package/CLAUDE.md +53 -49
  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 +2 -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 +0 -1
  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/test_agent_frontmatter.py +67 -18
  31. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
  32. package/bin/CLAUDE.md +68 -5
  33. package/bin/ever-shipped-skills.mjs +1 -0
  34. package/bin/install-constants.mjs +88 -0
  35. package/bin/install.mjs +1138 -114
  36. package/bin/install.prune.test.mjs +869 -19
  37. package/bin/install.test.mjs +906 -2
  38. package/commands/implement.md +1 -1
  39. package/commands/right-size.md +1 -1
  40. package/docs/CLAUDE.md +1 -0
  41. package/docs/host-pool-health-monitor.md +102 -0
  42. package/docs/references/CLAUDE.md +4 -2
  43. package/docs/references/advisor-tool.md +13 -0
  44. package/docs/references/code-review-enforcement.md +10 -0
  45. package/docs/references/team-advisor-skill.md +14 -0
  46. package/hooks/blocking/CLAUDE.md +1 -0
  47. package/hooks/blocking/code_review_pr_create_gate.py +7 -3
  48. package/hooks/blocking/code_review_push_gate.py +9 -4
  49. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
  50. package/hooks/blocking/config/__init__.py +5 -5
  51. package/hooks/blocking/config/code_review_enforcement_constants.py +4 -1
  52. package/hooks/blocking/config/test_code_review_enforcement_constants.py +5 -0
  53. package/hooks/blocking/config/verified_commit_constants.py +160 -159
  54. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
  55. package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
  56. package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
  57. package/hooks/blocking/test_code_review_push_gate.py +16 -0
  58. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
  59. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
  60. package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
  61. package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
  62. package/hooks/blocking/test_verification_verdict_store.py +54 -0
  63. package/hooks/blocking/test_verified_commit_gate.py +581 -581
  64. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
  65. package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
  66. package/hooks/blocking/verification_verdict_store.py +1039 -1036
  67. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
  68. package/hooks/blocking/verifier_verdict_minter.py +280 -280
  69. package/hooks/git-hooks/test_pre_push.py +25 -0
  70. package/hooks/hooks.json +10 -0
  71. package/hooks/hooks_constants/CLAUDE.md +2 -1
  72. package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
  73. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
  74. package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
  75. package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
  76. package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
  77. package/hooks/pyproject.toml +1 -0
  78. package/hooks/validators/CLAUDE.md +1 -0
  79. package/hooks/validators/config/__init__.py +0 -0
  80. package/hooks/validators/config/directory_exemption_constants.py +183 -0
  81. package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
  82. package/hooks/validators/conftest.py +4 -0
  83. package/hooks/validators/ruff_integration.py +49 -5
  84. package/hooks/validators/run_all_validators.py +206 -9
  85. package/hooks/validators/test_directory_exemption_constants.py +185 -0
  86. package/hooks/validators/test_python_antipattern_checks.py +110 -5
  87. package/hooks/validators/test_ruff_integration.py +92 -1
  88. package/hooks/validators/test_run_all_validators.py +115 -68
  89. package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
  90. package/package.json +10 -2
  91. package/rules/CLAUDE.md +1 -0
  92. package/rules/docstring-prose-matches-implementation.md +45 -44
  93. package/rules/state-what-is.md +25 -0
  94. package/rules/verified-commit-gate-skip.md +1 -1
  95. package/scripts/CLAUDE.md +1 -0
  96. package/scripts/Capture-PoolHealth.ps1 +410 -0
  97. package/scripts/_code_review_test_support.py +404 -0
  98. package/scripts/claude_chain_runner.py +141 -1
  99. package/scripts/conftest.py +16 -1
  100. package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
  101. package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
  102. package/scripts/resolve_worker_spawn.py +626 -626
  103. package/scripts/spawn_grok_batch.py +672 -672
  104. package/scripts/test_claude_chain_runner.py +131 -0
  105. package/scripts/test_invoke_code_review_chain.py +70 -0
  106. package/scripts/test_invoke_code_review_cli.py +192 -0
  107. package/scripts/test_invoke_code_review_contract.py +256 -0
  108. package/scripts/test_invoke_code_review_git.py +123 -0
  109. package/scripts/test_invoke_code_review_mode.py +99 -0
  110. package/scripts/test_resolve_worker_spawn.py +1014 -1014
  111. package/skills/CLAUDE.md +2 -0
  112. package/skills/auditing-claude-config/SKILL.md +114 -114
  113. package/skills/autoconverge/SKILL.md +427 -427
  114. package/skills/autoconverge/reference/convergence.md +24 -3
  115. package/skills/autoconverge/workflow/CLAUDE.md +1 -0
  116. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
  117. package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
  118. package/skills/autoconverge/workflow/converge.mjs +167 -0
  119. package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
  120. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
  121. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
  122. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
  123. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
  124. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
  125. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
  126. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
  127. package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
  128. package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
  129. package/skills/closeout/SKILL.md +33 -50
  130. package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
  131. package/skills/codex-review/scripts/run_codex_review.py +233 -1
  132. package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
  133. package/skills/condensing-instructions/SKILL.md +81 -0
  134. package/skills/copilot-review/SKILL.md +119 -119
  135. package/skills/e-code-review/SKILL.md +52 -0
  136. package/skills/e-code-review/reference/fix.md +54 -0
  137. package/skills/e-code-review/reference/loop.md +43 -0
  138. package/skills/e-code-review/reference/low.md +57 -0
  139. package/skills/e-code-review/reference/medium.md +153 -0
  140. package/skills/e-code-review/reference/xhigh.md +182 -0
  141. package/skills/e-simplify/SKILL.md +97 -0
  142. package/skills/issue-tracker/SKILL.md +92 -0
  143. package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
  144. package/skills/issue-tracker/reference/handoff-schema.md +64 -0
  145. package/skills/issue-tracker/reference/operation-matrix.md +41 -0
  146. package/skills/orchestrator/SKILL.md +162 -21
  147. package/skills/orchestrator/scripts/status_gate.py +625 -0
  148. package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
  149. package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
  150. package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
  151. package/skills/orchestrator/scripts/test_status_gate.py +439 -0
  152. package/skills/orchestrator-refresh/SKILL.md +110 -35
  153. package/skills/plan-to-pr/SKILL.md +155 -0
  154. package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
  155. package/skills/plan-to-pr/reference/model-routing.md +36 -0
  156. package/skills/plan-to-pr/reference/packet-contract.md +43 -0
  157. package/skills/plan-to-pr/reference/packet-schema.json +57 -0
  158. package/skills/plan-to-pr/reference/process-inventory.md +22 -0
  159. package/skills/plan-to-pr/reference/review-loop.md +33 -0
  160. package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
  161. package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
  162. package/skills/plan-to-pr/reference/task-seeds.md +14 -0
  163. package/skills/plan-to-pr/reference/task-ticket.md +38 -0
  164. package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
  165. package/skills/plan-to-pr/scripts/config/constants.py +193 -0
  166. package/skills/plan-to-pr/scripts/create_packet.py +173 -0
  167. package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
  168. package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
  169. package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
  170. package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
  171. package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
  172. package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
  173. package/skills/plan-to-pr/scripts/validate_run.py +173 -0
  174. package/skills/plan-to-pr/test_skill_contract.py +207 -0
  175. package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
  176. package/skills/pr-converge/SKILL.md +472 -469
  177. package/skills/pr-converge/reference/examples.md +3 -3
  178. package/skills/pr-converge/reference/fix-protocol.md +1 -1
  179. package/skills/pr-converge/reference/ground-rules.md +7 -4
  180. package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
  181. package/skills/pr-converge/reference/per-tick.md +5 -5
  182. package/skills/pr-converge/reference/progress-checklist.md +1 -1
  183. package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
  184. package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
  185. package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
  186. package/skills/pr-converge/test_step5_host_branch.py +1 -1
  187. package/skills/pr-fix-protocol/SKILL.md +1 -1
  188. package/skills/privacy-hygiene/SKILL.md +68 -68
  189. package/skills/prototype/workflows/promotion.md +1 -1
  190. package/skills/release-notes-html/SKILL.md +164 -0
  191. package/skills/task-build/CLAUDE.md +8 -7
  192. package/skills/task-build/SKILL.md +16 -8
  193. package/skills/task-build/reference/tool-routing.md +19 -0
  194. package/scripts/test_invoke_code_review.py +0 -966
  195. 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