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.
- package/CLAUDE.md +53 -49
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +4 -2
- package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
- package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
- package/_shared/pr-loop/worker-spawn.md +1 -1
- package/agents/CLAUDE.md +2 -1
- package/agents/caveman.md +0 -1
- package/agents/clasp-deployment-orchestrator.md +0 -1
- package/agents/clean-coder.md +0 -1
- package/agents/code-advisor.md +0 -1
- package/agents/code-quality-agent.md +1 -2
- package/agents/code-verifier.md +0 -1
- package/agents/deep-research.md +0 -1
- package/agents/docs-agent.md +0 -1
- package/agents/git-commit-crafter.md +0 -1
- package/agents/issue-tracker.md +42 -0
- package/agents/plan-packet-validator.md +0 -1
- package/agents/pr-description-writer.md +0 -1
- package/agents/test_agent_frontmatter.py +67 -18
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +143 -141
- package/bin/CLAUDE.md +68 -5
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-constants.mjs +88 -0
- package/bin/install.mjs +1138 -114
- package/bin/install.prune.test.mjs +869 -19
- package/bin/install.test.mjs +906 -2
- package/commands/implement.md +1 -1
- package/commands/right-size.md +1 -1
- package/docs/CLAUDE.md +1 -0
- package/docs/host-pool-health-monitor.md +102 -0
- package/docs/references/CLAUDE.md +4 -2
- package/docs/references/advisor-tool.md +13 -0
- package/docs/references/code-review-enforcement.md +10 -0
- package/docs/references/team-advisor-skill.md +14 -0
- package/hooks/blocking/CLAUDE.md +1 -0
- package/hooks/blocking/code_review_pr_create_gate.py +7 -3
- package/hooks/blocking/code_review_push_gate.py +9 -4
- package/hooks/blocking/code_review_stamp_directory_write_blocker.py +8 -0
- package/hooks/blocking/config/__init__.py +5 -5
- package/hooks/blocking/config/code_review_enforcement_constants.py +4 -1
- package/hooks/blocking/config/test_code_review_enforcement_constants.py +5 -0
- package/hooks/blocking/config/verified_commit_constants.py +160 -159
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
- package/hooks/blocking/test_code_review_pr_create_gate.py +14 -0
- package/hooks/blocking/test_code_review_push_gate.py +16 -0
- package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +19 -0
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +10 -1
- package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
- package/hooks/blocking/test_verification_verdict_store.py +54 -0
- package/hooks/blocking/test_verified_commit_gate.py +581 -581
- package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
- package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
- package/hooks/blocking/verification_verdict_store.py +1039 -1036
- package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
- package/hooks/blocking/verifier_verdict_minter.py +280 -280
- package/hooks/git-hooks/test_pre_push.py +25 -0
- package/hooks/hooks.json +10 -0
- package/hooks/hooks_constants/CLAUDE.md +2 -1
- package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
- package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
- package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
- package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
- package/hooks/pyproject.toml +1 -0
- package/hooks/validators/CLAUDE.md +1 -0
- package/hooks/validators/config/__init__.py +0 -0
- package/hooks/validators/config/directory_exemption_constants.py +183 -0
- package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
- package/hooks/validators/conftest.py +4 -0
- package/hooks/validators/ruff_integration.py +49 -5
- package/hooks/validators/run_all_validators.py +206 -9
- package/hooks/validators/test_directory_exemption_constants.py +185 -0
- package/hooks/validators/test_python_antipattern_checks.py +110 -5
- package/hooks/validators/test_ruff_integration.py +92 -1
- package/hooks/validators/test_run_all_validators.py +115 -68
- package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
- package/package.json +10 -2
- package/rules/CLAUDE.md +1 -0
- package/rules/docstring-prose-matches-implementation.md +45 -44
- package/rules/state-what-is.md +25 -0
- package/rules/verified-commit-gate-skip.md +1 -1
- package/scripts/CLAUDE.md +1 -0
- package/scripts/Capture-PoolHealth.ps1 +410 -0
- package/scripts/_code_review_test_support.py +404 -0
- package/scripts/claude_chain_runner.py +141 -1
- package/scripts/conftest.py +16 -1
- package/scripts/dev_env_scripts_constants/CLAUDE.md +1 -1
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +9 -0
- package/scripts/resolve_worker_spawn.py +626 -626
- package/scripts/spawn_grok_batch.py +672 -672
- package/scripts/test_claude_chain_runner.py +131 -0
- package/scripts/test_invoke_code_review_chain.py +70 -0
- package/scripts/test_invoke_code_review_cli.py +192 -0
- package/scripts/test_invoke_code_review_contract.py +256 -0
- package/scripts/test_invoke_code_review_git.py +123 -0
- package/scripts/test_invoke_code_review_mode.py +99 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -1014
- package/skills/CLAUDE.md +2 -0
- package/skills/auditing-claude-config/SKILL.md +114 -114
- package/skills/autoconverge/SKILL.md +427 -427
- package/skills/autoconverge/reference/convergence.md +24 -3
- package/skills/autoconverge/workflow/CLAUDE.md +1 -0
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +3 -3
- package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1263
- package/skills/autoconverge/workflow/converge.mjs +167 -0
- package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
- package/skills/closeout/SKILL.md +33 -50
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
- package/skills/codex-review/scripts/run_codex_review.py +233 -1
- package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
- package/skills/condensing-instructions/SKILL.md +81 -0
- package/skills/copilot-review/SKILL.md +119 -119
- package/skills/e-code-review/SKILL.md +52 -0
- package/skills/e-code-review/reference/fix.md +54 -0
- package/skills/e-code-review/reference/loop.md +43 -0
- package/skills/e-code-review/reference/low.md +57 -0
- package/skills/e-code-review/reference/medium.md +153 -0
- package/skills/e-code-review/reference/xhigh.md +182 -0
- package/skills/e-simplify/SKILL.md +97 -0
- package/skills/issue-tracker/SKILL.md +92 -0
- package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
- package/skills/issue-tracker/reference/handoff-schema.md +64 -0
- package/skills/issue-tracker/reference/operation-matrix.md +41 -0
- package/skills/orchestrator/SKILL.md +162 -21
- package/skills/orchestrator/scripts/status_gate.py +625 -0
- package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
- package/skills/orchestrator/scripts/test_status_gate.py +439 -0
- package/skills/orchestrator-refresh/SKILL.md +110 -35
- package/skills/plan-to-pr/SKILL.md +155 -0
- package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
- package/skills/plan-to-pr/reference/model-routing.md +36 -0
- package/skills/plan-to-pr/reference/packet-contract.md +43 -0
- package/skills/plan-to-pr/reference/packet-schema.json +57 -0
- package/skills/plan-to-pr/reference/process-inventory.md +22 -0
- package/skills/plan-to-pr/reference/review-loop.md +33 -0
- package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
- package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
- package/skills/plan-to-pr/reference/task-seeds.md +14 -0
- package/skills/plan-to-pr/reference/task-ticket.md +38 -0
- package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
- package/skills/plan-to-pr/scripts/config/constants.py +193 -0
- package/skills/plan-to-pr/scripts/create_packet.py +173 -0
- package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
- package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
- package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
- package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
- package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
- package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
- package/skills/plan-to-pr/scripts/validate_run.py +173 -0
- package/skills/plan-to-pr/test_skill_contract.py +207 -0
- package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
- package/skills/pr-converge/SKILL.md +472 -469
- package/skills/pr-converge/reference/examples.md +3 -3
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +7 -4
- package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
- package/skills/pr-converge/reference/per-tick.md +5 -5
- package/skills/pr-converge/reference/progress-checklist.md +1 -1
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
- package/skills/pr-converge/test_step5_host_branch.py +1 -1
- package/skills/pr-fix-protocol/SKILL.md +1 -1
- package/skills/privacy-hygiene/SKILL.md +68 -68
- package/skills/prototype/workflows/promotion.md +1 -1
- package/skills/release-notes-html/SKILL.md +164 -0
- package/skills/task-build/CLAUDE.md +8 -7
- package/skills/task-build/SKILL.md +16 -8
- package/skills/task-build/reference/tool-routing.md +19 -0
- package/scripts/test_invoke_code_review.py +0 -966
- 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
|