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.
- package/CLAUDE.md +53 -48
- 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 +3 -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 +3 -4
- 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/skill-writer-agent.md +84 -0
- package/agents/test_agent_frontmatter.py +67 -18
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +105 -3
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +29 -13
- 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 +2 -0
- package/docs/host-pool-health-monitor.md +102 -0
- package/docs/references/CLAUDE.md +5 -2
- package/docs/references/advisor-tool.md +13 -0
- package/docs/references/code-review-enforcement.md +107 -0
- package/docs/references/team-advisor-skill.md +14 -0
- package/docs/wsl-docker-cowork-starter-matrix.md +89 -0
- package/hooks/blocking/CLAUDE.md +9 -1
- package/hooks/blocking/code_review_enforcement_config_bootstrap.py +53 -0
- package/hooks/blocking/code_review_gate_deny.py +74 -0
- package/hooks/blocking/code_review_pr_create_gate.py +198 -0
- package/hooks/blocking/code_review_push_gate.py +145 -0
- package/hooks/blocking/code_review_stamp_directory_write_blocker.py +348 -0
- package/hooks/blocking/code_review_stamp_store.py +233 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +7 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +15 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +212 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +138 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +49 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +38 -0
- package/hooks/blocking/code_verifier_spawn_preflight_gate.py +39 -27
- package/hooks/blocking/config/__init__.py +5 -5
- package/hooks/blocking/config/code_review_enforcement_constants.py +113 -0
- package/hooks/blocking/config/test_code_review_enforcement_constants.py +113 -0
- package/hooks/blocking/config/verified_commit_constants.py +160 -155
- package/hooks/blocking/conftest.py +2 -0
- package/hooks/blocking/convergence_gate_blocker.py +112 -23
- package/hooks/blocking/destructive_command_blocker.py +19 -6
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
- package/hooks/blocking/pr_description_proof_of_work.py +52 -34
- package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +4 -1
- package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +62 -0
- package/hooks/blocking/test_code_review_gate_deny.py +54 -0
- package/hooks/blocking/test_code_review_pr_create_gate.py +199 -0
- package/hooks/blocking/test_code_review_push_gate.py +205 -0
- package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +199 -0
- package/hooks/blocking/test_code_review_stamp_store.py +205 -0
- package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +124 -2
- package/hooks/blocking/test_convergence_gate_blocker.py +153 -5
- package/hooks/blocking/test_destructive_command_blocker.py +1 -1
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +45 -0
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
- package/hooks/blocking/test_pr_description_proof_of_work.py +151 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +17 -8
- package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
- package/hooks/blocking/test_verification_verdict_store.py +974 -903
- 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/test_volatile_path_in_post_blocker.py +114 -2
- package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
- package/hooks/blocking/verification_verdict_store.py +1039 -1014
- package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +29 -17
- package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +35 -0
- package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
- package/hooks/blocking/verifier_verdict_minter.py +280 -280
- package/hooks/blocking/volatile_path_in_post_blocker.py +69 -8
- package/hooks/git-hooks/git_hooks_constants/__init__.py +6 -0
- package/hooks/git-hooks/pre_push.py +89 -2
- package/hooks/git-hooks/test_pre_push.py +128 -0
- package/hooks/hooks.json +26 -1
- package/hooks/hooks_constants/CLAUDE.md +3 -1
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +8 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
- package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +26 -11
- package/hooks/hooks_constants/convergence_gate_blocker_constants.py +20 -3
- package/hooks/hooks_constants/destructive_command_segment_constants.py +3 -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/pr_description_proof_of_work_constants.py +0 -4
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
- package/hooks/hooks_constants/pyproject_config_discovery_constants.py +16 -0
- package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +24 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +6 -0
- package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +8 -1
- 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 +2 -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/mypy_integration.py +63 -50
- package/hooks/validators/pyproject_config_discovery.py +101 -0
- package/hooks/validators/ruff_integration.py +257 -25
- package/hooks/validators/run_all_validators.py +223 -19
- package/hooks/validators/test_directory_exemption_constants.py +185 -0
- package/hooks/validators/test_mypy_integration.py +32 -0
- package/hooks/validators/test_pyproject_config_discovery.py +94 -0
- package/hooks/validators/test_python_antipattern_checks.py +110 -5
- package/hooks/validators/test_ruff_integration.py +160 -2
- package/hooks/validators/test_run_all_validators.py +140 -68
- package/hooks/validators/test_run_all_validators_config_discovery.py +123 -0
- 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 -67
- package/rules/durable-post-artifacts.md +7 -0
- 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/codec_forwarding_test_support.py +83 -0
- package/scripts/conftest.py +23 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +2 -2
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +53 -1
- package/scripts/dev_env_scripts_constants/code_review_constants.py +129 -12
- package/scripts/dev_env_scripts_constants/test_code_review_constants.py +55 -0
- package/scripts/invoke_code_review.py +550 -38
- package/scripts/resolve_worker_spawn.py +626 -619
- 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_codec.py +77 -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/scripts/test_resolve_worker_spawn_codec.py +101 -0
- package/skills/CLAUDE.md +2 -0
- package/skills/auditing-claude-config/SKILL.md +114 -114
- package/skills/autoconverge/SKILL.md +427 -421
- package/skills/autoconverge/reference/convergence.md +26 -4
- package/skills/autoconverge/reference/multi-pr.md +6 -1
- package/skills/autoconverge/reference/stop-conditions.md +16 -10
- package/skills/autoconverge/workflow/CLAUDE.md +1 -0
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +4 -4
- package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +175 -3
- package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1244
- package/skills/autoconverge/workflow/converge.mjs +191 -8
- package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
- package/skills/autoconverge/workflow/converge_multi.mjs +7 -3
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +5 -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/fresh-branch/CLAUDE.md +2 -0
- package/skills/fresh-branch/SKILL.md +2 -0
- package/skills/fresh-branch/scripts/create_fresh_branch.py +78 -180
- package/skills/fresh-branch/scripts/fresh_branch_git_commands.py +285 -0
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -0
- package/skills/fresh-branch/scripts/pytest.ini +4 -0
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +98 -0
- package/skills/fresh-branch/scripts/test_fresh_branch_git_commands.py +310 -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/SKILL.md +86 -0
- package/skills/prototype/reference/honest-limitations.md +23 -0
- package/skills/prototype/reference/promotion-tasks.md +23 -0
- package/skills/prototype/scripts/build_sandbox_settings.py +249 -0
- package/skills/prototype/scripts/conftest.py +15 -0
- package/skills/prototype/scripts/launch_sandbox.py +205 -0
- package/skills/prototype/scripts/probe_sandbox_safety.py +311 -0
- package/skills/prototype/scripts/prototype_scripts_constants/__init__.py +1 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +41 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +23 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +45 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +10 -0
- package/skills/prototype/scripts/test_build_sandbox_settings.py +275 -0
- package/skills/prototype/scripts/test_launch_sandbox.py +303 -0
- package/skills/prototype/scripts/test_probe_sandbox_safety.py +284 -0
- package/skills/prototype/workflows/promotion.md +27 -0
- package/skills/prototype/workflows/sandbox.md +35 -0
- package/skills/release-notes-html/SKILL.md +164 -0
- package/skills/skill-builder/CLAUDE.md +3 -3
- package/skills/skill-builder/SKILL.md +5 -5
- package/skills/skill-builder/references/CLAUDE.md +1 -1
- package/skills/skill-builder/references/delegation-map.md +3 -3
- package/skills/skill-builder/references/description-field.md +1 -1
- package/skills/skill-builder/references/skill-modularity.md +2 -3
- package/skills/skill-builder/workflows/CLAUDE.md +1 -1
- package/skills/skill-builder/workflows/improve-skill.md +1 -1
- package/skills/skill-builder/workflows/new-skill.md +2 -2
- 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/skills/team-advisor/SKILL.md +2 -2
- package/scripts/test_invoke_code_review.py +0 -672
- package/skills/closeout/reference/issue-body-templates.md +0 -108
package/commands/implement.md
CHANGED
|
@@ -5,7 +5,7 @@ allowed-tools: Task, Read, Grep, Glob
|
|
|
5
5
|
|
|
6
6
|
**Implementation with Full Context - XML-Structured Guidance**
|
|
7
7
|
|
|
8
|
-
Launch a clean-coder agent with COMPREHENSIVE implementation context provided in structured XML format.
|
|
8
|
+
Launch a clean-coder agent — `model: sonnet`, worker-model routing per [`skills/orchestrator/SKILL.md`](../skills/orchestrator/SKILL.md#workflow-agent-routing); resolver-supplied sonnet-equivalent on third-party hosts — with COMPREHENSIVE implementation context provided in structured XML format.
|
|
9
9
|
|
|
10
10
|
## Process:
|
|
11
11
|
|
package/commands/right-size.md
CHANGED
|
@@ -3,7 +3,7 @@ description: Prevent over/under-engineering
|
|
|
3
3
|
allowed-tools: Task
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Launch the clean-coder agent to review code for appropriate engineering practices.
|
|
6
|
+
Launch the clean-coder agent — `model: sonnet`, worker-model routing per [`skills/orchestrator/SKILL.md`](../skills/orchestrator/SKILL.md#workflow-agent-routing); resolver-supplied sonnet-equivalent on third-party hosts — to review code for appropriate engineering practices.
|
|
7
7
|
|
|
8
8
|
The agent will evaluate whether abstractions, patterns, and practices match the actual needs of the project, ensuring code is neither over-engineered nor under-engineered for its current scale and purpose.
|
|
9
9
|
|
package/docs/CLAUDE.md
CHANGED
|
@@ -17,6 +17,8 @@ Reference documentation installed into `~/.claude/docs/` by `bin/install.mjs`. T
|
|
|
17
17
|
| `agent-spawn-protocol.md` | Full agent-spawn protocol behind the `rules/agent-spawn-protocol.md` kernel: context-sufficiency check, `/prompt-generator` prompt crafting, and the spawn step |
|
|
18
18
|
| `nas-ssh-invocation.md` | Full NAS ssh policy behind the `rules/nas-ssh-invocation.md` kernel: the OpenSSH binary form, config sources, and hook enforcement |
|
|
19
19
|
| `worker-completion-gate.md` | Full worker-completion gate behind the `rules/workers-done-before-complete.md` kernel: the checklist, examples, and run-state records |
|
|
20
|
+
| `wsl-docker-cowork-starter-matrix.md` | Host matrix: WSL/Docker/cowork component → starter → required? → shutdown; policy options with costs; no unmeasured `.wslconfig` memory cap |
|
|
21
|
+
| `host-pool-health-monitor.md` | Operator recipe for Windows pool/handle health: thresholds, clean-shell re-run of `Capture-PoolHealth.ps1`, RC2/RC3/RC4 remediation map |
|
|
20
22
|
|
|
21
23
|
## Subdirectory
|
|
22
24
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Host pool health monitor
|
|
2
|
+
|
|
3
|
+
Operator recipe for kernel pool counters, handle pressure, and pool tags on a Windows host that runs Claude Code / agent tooling. Ships as `scripts/Capture-PoolHealth.ps1` (installed to `~/.claude/scripts/`).
|
|
4
|
+
|
|
5
|
+
## Verdict this monitor tracks
|
|
6
|
+
|
|
7
|
+
Evidence capture (`results/04-pool-tags.md` in the ram-process-sprawl plan pack) shows:
|
|
8
|
+
|
|
9
|
+
| Tag | Pool | Role |
|
|
10
|
+
|-----|------|------|
|
|
11
|
+
| **File** | nonpaged (~multi-GB when bad) | File objects |
|
|
12
|
+
| **IoFE** | nonpaged | I/O / file-object adjacent |
|
|
13
|
+
| **Toke** | paged (~multi-GB when bad) | Security token objects |
|
|
14
|
+
| **Key** | paged | Registry key objects (often Git `find` storms) |
|
|
15
|
+
|
|
16
|
+
**Primary story:** process-object / object-manager pressure from agent sprawl — not a single third-party driver leak. Fix sprawl with RC2 / RC3 / RC4; re-run this recipe after those land to confirm File/Toke fall.
|
|
17
|
+
|
|
18
|
+
## Alert thresholds
|
|
19
|
+
|
|
20
|
+
| Signal | Alert when |
|
|
21
|
+
|--------|------------|
|
|
22
|
+
| `\Memory\Pool Nonpaged Bytes` | **> 2 GB** |
|
|
23
|
+
| Any process `HandleCount` | **> 2000** |
|
|
24
|
+
| `\Process(_Total)\Handle Count` | **> 500000** |
|
|
25
|
+
|
|
26
|
+
Also watch pool tags **File**, **Toke**, **IoFE**, **Key**, **FMfn** whenever any threshold fires.
|
|
27
|
+
|
|
28
|
+
## Clean-shell re-run
|
|
29
|
+
|
|
30
|
+
From a new PowerShell window (no profile, no prior session state):
|
|
31
|
+
|
|
32
|
+
```powershell
|
|
33
|
+
# After package install (npx claude-dev-env / node bin/install.mjs)
|
|
34
|
+
pwsh -NoProfile -File "$HOME\.claude\scripts\Capture-PoolHealth.ps1"
|
|
35
|
+
|
|
36
|
+
# Or from a checkout
|
|
37
|
+
pwsh -NoProfile -File "packages\claude-dev-env\scripts\Capture-PoolHealth.ps1"
|
|
38
|
+
|
|
39
|
+
# Save a snapshot
|
|
40
|
+
pwsh -NoProfile -File "$HOME\.claude\scripts\Capture-PoolHealth.ps1" `
|
|
41
|
+
-OutPath "$env:TEMP\pool-health-$(Get-Date -Format yyyyMMdd-HHmmss).txt"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Exit code: `0` = all thresholds clear; `1` = one or more alerts. Hard capture failures (for example Get-Counter) stop with a non-zero exit. Soft failures (for example pool-tag walk unavailable) print a warning and can still exit `0` when thresholds are clear.
|
|
45
|
+
|
|
46
|
+
### What the script prints
|
|
47
|
+
|
|
48
|
+
1. **Counters** — nonpaged/paged pool, commit, available MB, total handles/threads
|
|
49
|
+
2. **High-handle processes** — every process over the handle threshold (top N by default)
|
|
50
|
+
3. **Pool tags** — top nonpaged, top paged, plus File/IoFE/Toke/Key/FMfn watchlist via `NtQuerySystemInformation` class 22 (same source PoolMon uses; no admin and no WDK required)
|
|
51
|
+
4. **Remediation map** — RC issue pointers below
|
|
52
|
+
5. **Threshold verdict** — `OK` or `ALERT ...` lines (last; drives exit code)
|
|
53
|
+
|
|
54
|
+
### Counters-only one-liner (no tags)
|
|
55
|
+
|
|
56
|
+
```powershell
|
|
57
|
+
pwsh -NoProfile -Command "
|
|
58
|
+
Get-Date -Format 'yyyy-MM-dd HH:mm:ss zzz'
|
|
59
|
+
Get-Counter '\Memory\Pool Nonpaged Bytes','\Memory\Pool Paged Bytes',
|
|
60
|
+
'\Memory\Committed Bytes','\Memory\Available MBytes',
|
|
61
|
+
'\Process(_Total)\Handle Count','\Process(_Total)\Thread Count' |
|
|
62
|
+
Select-Object -ExpandProperty CounterSamples |
|
|
63
|
+
Format-Table Path, CookedValue -AutoSize
|
|
64
|
+
"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Remediation map
|
|
68
|
+
|
|
69
|
+
When nonpaged, handles, or File/Toke tags are high, apply the sprawl fixes — not a random driver unload.
|
|
70
|
+
|
|
71
|
+
| RC | Issue | What it cuts | Expected pool/handle effect |
|
|
72
|
+
|----|-------|--------------|-----------------------------|
|
|
73
|
+
| **RC2** | [#255](https://github.com/jl-cmd/claude-dev-env/issues/255) Cap MCP servers — stop per-session mcpvault/playwright/serena spawn-without-reap | Extra `node`/MCP process trees | Lower **File** / **Toke** / **Thre** object pressure |
|
|
74
|
+
| **RC3** | [#254](https://github.com/jl-cmd/claude-dev-env/issues/254) `Show-Asset.ps1` exit on parent death + timeout | Orphan UI/`Application.Run` processes | Lower process count and handle tables |
|
|
75
|
+
| **RC4** | [#253](https://github.com/jl-cmd/claude-dev-env/issues/253) Block Git `find` filesystem walks; `es.exe` primary; kill runaway `find` (>2k handles) | Orphan `Git\usr\bin\find.exe` with 10^5–10^6 **Key** handles | Sharp drop in total handles and **Key** paged; secondary relief on object manager |
|
|
76
|
+
| **RC5** | [#256](https://github.com/jl-cmd/claude-dev-env/issues/256) Attribute WSL/Docker/cowork VM starters | `vmmem*` / **VdMm** | Secondary only — measure first; no blind `.wslconfig` memory cap |
|
|
77
|
+
|
|
78
|
+
**Do not** treat multi-GB **File**/**Toke** as “install poolmon and hunt NVRM.” NVRM/VdMm appear as tens of MB in the evidence pack; the multi-GB leaders are object-class tags.
|
|
79
|
+
|
|
80
|
+
### Emergency: runaway `find.exe`
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
Get-CimInstance Win32_Process -Filter "Name='find.exe'" |
|
|
84
|
+
Select-Object ProcessId, ParentProcessId, HandleCount, CommandLine
|
|
85
|
+
# If HandleCount > 2000 and parent is dead → stop the process (RC4 policy)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## After RC2 / RC3 / RC4 land
|
|
89
|
+
|
|
90
|
+
1. Idle the agent host (no active find storms, no orphan Show-Asset, MCP set capped).
|
|
91
|
+
2. Re-run `Capture-PoolHealth.ps1` from a clean shell.
|
|
92
|
+
3. Confirm: nonpaged trend down, total handles under 500k when idle, **File**/**Toke** tag bytes down vs the 04-pool-tags baseline.
|
|
93
|
+
|
|
94
|
+
## Optional: stock `poolmon.exe`
|
|
95
|
+
|
|
96
|
+
This recipe does **not** need the WDK. Operators who prefer the Microsoft binary can install Windows Driver Kit tools and run elevated `poolmon -b` snapshots. Tag names still map through `pooltag.txt` under Windows Kits Debuggers when present.
|
|
97
|
+
|
|
98
|
+
## Related
|
|
99
|
+
|
|
100
|
+
- Parent epic: [#252](https://github.com/jl-cmd/claude-dev-env/issues/252)
|
|
101
|
+
- This child: [#257](https://github.com/jl-cmd/claude-dev-env/issues/257)
|
|
102
|
+
- Script: `packages/claude-dev-env/scripts/Capture-PoolHealth.ps1`
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# docs/references
|
|
2
2
|
|
|
3
|
-
Pointer documents to external sources and
|
|
3
|
+
Pointer documents to external sources, standard terminology, and internal tool or skill usage. Files here are loaded on demand by rules that cite them.
|
|
4
4
|
|
|
5
5
|
## Files
|
|
6
6
|
|
|
7
7
|
| File | Purpose |
|
|
8
8
|
|---|---|
|
|
9
9
|
| `dead-code-elimination.md` | External sources and standard terms behind CODE_RULES §9.8 (remove code you orphan): DCE, tree shaking, reachability analysis, and the Lava Flow anti-pattern |
|
|
10
|
+
| `code-review-enforcement.md` | How the code-review gates work: the two required efforts (push at low, PR creation at xhigh), the stamp bound to the branch-surface hash, the single sanctioned minter, the two-layer stamp-directory guard, and the bypass surfaces the gates leave open |
|
|
11
|
+
| `advisor-tool.md` | The `advisor()` tool: a no-parameter review call that forwards the full conversation history to a stronger reviewer model, and when to call it |
|
|
12
|
+
| `team-advisor-skill.md` | The `/team-advisor` skill: spawning a standing review agent at the strongest reachable tier, and how it relates to the `advisor()` tool |
|
|
10
13
|
|
|
11
14
|
## Role
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
A file naming an external concept gives a one-line definition and links a direct source. A file naming an internal tool or skill describes what it does and when to use it. They back the rule text in `rules/` and `packages/claude-dev-env/docs/CODE_RULES.md` without embedding full third-party content inline.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Advisor Tool
|
|
2
|
+
|
|
3
|
+
`advisor()` is a review tool backed by a stronger reviewer model. It takes no parameters — calling it forwards the entire conversation history automatically, so the reviewer sees the task, every tool call made, and every result seen so far.
|
|
4
|
+
|
|
5
|
+
## When to call it
|
|
6
|
+
|
|
7
|
+
Call `advisor()` before substantive work: before writing, before committing to an interpretation, before building on an assumption. Orientation work — finding files, fetching a source, seeing what exists — comes first; call `advisor()` once that orientation is done and before the substantive step begins.
|
|
8
|
+
|
|
9
|
+
Treat the advisor as more experienced than the calling agent. Consult it whenever a path forward is uncertain.
|
|
10
|
+
|
|
11
|
+
## Availability
|
|
12
|
+
|
|
13
|
+
`advisor()` is present only in environments where it has been configured as a tool. Check the available tools before relying on it; when it is absent, fall back to `/team-advisor` (see `team-advisor-skill.md` in this directory).
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Code-review enforcement
|
|
2
|
+
|
|
3
|
+
This feature ties two git actions to a clean run of the built-in
|
|
4
|
+
`/code-review --fix`:
|
|
5
|
+
|
|
6
|
+
- **`git push`** needs a clean review at effort **low** or higher.
|
|
7
|
+
- **Pull-request creation** (`gh pr create` and the MCP `create_pull_request`
|
|
8
|
+
tool) needs a clean review at effort **xhigh** or higher.
|
|
9
|
+
|
|
10
|
+
The gates follow the same shape as the `verified_commit` gate family.
|
|
11
|
+
|
|
12
|
+
## Opt-in (default off)
|
|
13
|
+
|
|
14
|
+
Enforcement is **off by default**. The master flag is
|
|
15
|
+
`CODE_REVIEW_ENFORCEMENT_ENABLED` in
|
|
16
|
+
`hooks/blocking/config/code_review_enforcement_constants.py`. Set it to
|
|
17
|
+
`True` to enable the push gate, the PR-create gate, the native pre-push
|
|
18
|
+
backstop (via the shared deny decision), and the stamp-directory write
|
|
19
|
+
blocker. When the flag is `False`, every gate allows the action and the
|
|
20
|
+
write-blocker allows stamp-directory access.
|
|
21
|
+
|
|
22
|
+
## How a stamp works
|
|
23
|
+
|
|
24
|
+
A stamp is a small JSON file that records one fact: a clean `/code-review` pass
|
|
25
|
+
ran against an exact branch surface at a given effort. Each work tree keeps one
|
|
26
|
+
file under `~/.claude/code-review-stamps/`, named by a hash of the resolved
|
|
27
|
+
work-tree path.
|
|
28
|
+
|
|
29
|
+
The stamp binds to a **branch-surface hash** — the hash of every changed path
|
|
30
|
+
and untracked file, each bound by its content digest, measured against the
|
|
31
|
+
merge base. When any byte of the change surface moves, the live hash stops
|
|
32
|
+
matching the stored hash, so the stamp stops covering the surface and the gate
|
|
33
|
+
asks for a fresh review.
|
|
34
|
+
|
|
35
|
+
A gate allows the action only when a stored stamp matches the live hash exactly
|
|
36
|
+
and its effort ranks at or above the effort the action needs. A missing,
|
|
37
|
+
unreadable, or malformed stamp reads as no coverage, so the gate fails closed.
|
|
38
|
+
|
|
39
|
+
## The single sanctioned minter
|
|
40
|
+
|
|
41
|
+
Only `invoke_code_review.py --record-stamp` writes a stamp. It forces a headless
|
|
42
|
+
`/code-review <effort> --fix` run, then mints a stamp only when the review
|
|
43
|
+
returns a clean exit code and leaves the branch surface unchanged in the same
|
|
44
|
+
pass. A pass that applies fixes mints nothing; the run loops on the new surface
|
|
45
|
+
up to a capped number of passes and mints only on a stable clean pass.
|
|
46
|
+
|
|
47
|
+
## Two layers guard the stamp directory
|
|
48
|
+
|
|
49
|
+
The gates trust one rule: only the sanctioned minter writes stamp files. Two
|
|
50
|
+
layers hold that rule.
|
|
51
|
+
|
|
52
|
+
1. **File-tool deny in `settings.json`.** `Write`, `Edit`, and `MultiEdit`
|
|
53
|
+
under `~/.claude/code-review-stamps/` are denied. This layer covers work
|
|
54
|
+
inside the repository. The installer merges hook groups into a user's
|
|
55
|
+
`settings.json` and does not ship this package's `permissions.deny`, so on a
|
|
56
|
+
user's machine this layer protects contributor work, at parity with the
|
|
57
|
+
`verified_commit` gate's own file-tool deny.
|
|
58
|
+
2. **`code_review_stamp_directory_write_blocker` hook.** This hook ships through
|
|
59
|
+
`hooks.json`, so it reaches every install. It has two arms:
|
|
60
|
+
- a shell arm that denies any Bash or PowerShell command naming the stamp
|
|
61
|
+
directory, or importing the stamp store module, or calling its mint
|
|
62
|
+
function — while it lets the sanctioned minter command through;
|
|
63
|
+
- a file-tool arm that denies any `Write`, `Edit`, or `MultiEdit` whose path
|
|
64
|
+
resolves under the stamp directory. This arm closes the plain file-tool
|
|
65
|
+
forge on every shipped install, which the package `settings.json` deny
|
|
66
|
+
cannot reach on its own.
|
|
67
|
+
|
|
68
|
+
## What the gates block
|
|
69
|
+
|
|
70
|
+
- **Casual and accidental forges.** A plain file-tool write to the stamp
|
|
71
|
+
directory, and a casual shell write to it, are both denied.
|
|
72
|
+
- **Hidden-path and split-step shell forges.** A shell command that assembles
|
|
73
|
+
the stamp path from hex, base64, or character math is decoded and denied. A
|
|
74
|
+
command that splits the directory change across steps to walk into the stamp
|
|
75
|
+
directory is traced and denied.
|
|
76
|
+
- **Lazy skips.** A push or a pull-request creation cannot go ahead without a
|
|
77
|
+
stamp that matches the live surface at the needed effort.
|
|
78
|
+
|
|
79
|
+
## What the gates do not block
|
|
80
|
+
|
|
81
|
+
The chain-mode `/code-review` runs as a subprocess spawn of the `claude`
|
|
82
|
+
binary, not a harness-recorded subagent, so there is no signed sidecar to
|
|
83
|
+
anchor a forgery-proof mint. The stamp reaches the same posture the
|
|
84
|
+
`verified_commit` gate holds, and no further. These bypass surfaces stay open:
|
|
85
|
+
|
|
86
|
+
- **Pull requests that skip the tool paths.** A PR opened through
|
|
87
|
+
`gh api -X POST .../pulls` or the GitHub web page never triggers the
|
|
88
|
+
create-PR gate.
|
|
89
|
+
- **`git push --no-verify`.** This flag tells git to skip the native pre-push
|
|
90
|
+
hook, so the native backstop does not run.
|
|
91
|
+
- **A rebuilt store.** A script that re-implements the stamp store in memory
|
|
92
|
+
and writes a matching file can mint a stamp the gates accept.
|
|
93
|
+
|
|
94
|
+
In short: these gates stop casual forges and lazy skips. They do not stop a
|
|
95
|
+
determined attacker who sets out to defeat them.
|
|
96
|
+
|
|
97
|
+
## Where the pieces live
|
|
98
|
+
|
|
99
|
+
- Gates: `hooks/blocking/code_review_push_gate.py`,
|
|
100
|
+
`hooks/blocking/code_review_pr_create_gate.py`.
|
|
101
|
+
- Stamp store: `hooks/blocking/code_review_stamp_store.py`.
|
|
102
|
+
- Directory guard: `hooks/blocking/code_review_stamp_directory_write_blocker.py`.
|
|
103
|
+
- Shared constants: `hooks/blocking/config/code_review_enforcement_constants.py`.
|
|
104
|
+
- Native backstop: `hooks/git-hooks/pre_push.py` reuses the push gate's
|
|
105
|
+
`deny_reason_for_directory` so the native hook and the Claude gate share one
|
|
106
|
+
decision source.
|
|
107
|
+
- Minter: `scripts/invoke_code_review.py --record-stamp`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Team-Advisor Skill Invocation
|
|
2
|
+
|
|
3
|
+
`/team-advisor` spawns a standing review agent for the session at the strongest reachable model tier, run at the highest reasoning effort. Use it to get a second opinion from a distinct model line than the one driving the session. For example:
|
|
4
|
+
|
|
5
|
+
- Claude session -> consult Fable xhigh
|
|
6
|
+
- Codex session -> consult Sol xhigh
|
|
7
|
+
|
|
8
|
+
## When to use it
|
|
9
|
+
|
|
10
|
+
Consult the spawned advisor before big decisions, before declaring work complete, before any commit, when a failure repeats, or when reconsidering a chosen approach.
|
|
11
|
+
|
|
12
|
+
## Relationship to the advisor tool
|
|
13
|
+
|
|
14
|
+
`/team-advisor` works standalone; it needs no `advisor()` tool present. When both are available (see `advisor-tool.md` in this directory), use `advisor()` for a fast, history-forwarding check before substantive work, and `/team-advisor` for a standing reviewer consulted at decision points.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# WSL / Docker / Cowork starter matrix
|
|
2
|
+
|
|
3
|
+
Attribution and policy surface for host memory consumers that sit under WSL2, Docker Desktop, and the Claude Cowork HCS VM. Source evidence: forensic capture summarized on [issue #256](https://github.com/jl-cmd/claude-dev-env/issues/256) (`results/05-wsl-who-started.md` in the local ram-process-sprawl evidence pack). This file records supported starters, whether each component is required for day-to-day agent work, and how to shut it down. It does **not** apply a `.wslconfig` memory cap.
|
|
4
|
+
|
|
5
|
+
## Hard rules
|
|
6
|
+
|
|
7
|
+
1. **No unmeasured `.wslconfig` memory cap.** Do not set `memory=` (or other hard caps) in `%UserProfile%\.wslconfig` until (a) the owner of the WSL VM commit is known for the workload under test and (b) a before/after private-working-set measurement is recorded on the same host boot window. A blind cap is out of scope for this matrix.
|
|
8
|
+
2. **Proven starters only.** Rows below use only process-parent, service, HCS owner, registry, and product-log evidence from the capture. Proximity without a parent edge is labeled **unknown**, not a starter claim.
|
|
9
|
+
3. **Two HCS VMs are not one.** Owner=`WSL` maps to `vmmemWSL`. Owner=`cowork-vm-*` maps to plain `vmmem`. Treat them as separate shutdown and policy targets.
|
|
10
|
+
|
|
11
|
+
## Starter matrix
|
|
12
|
+
|
|
13
|
+
| Component | Starter / owner (supported) | Required for daily agent work? | Shutdown / stop |
|
|
14
|
+
|-----------|----------------------------|--------------------------------|-----------------|
|
|
15
|
+
| **WSLService** (`wslservice.exe`) | Windows service **WSLService**, StartMode=Auto, parent `services.exe` since boot | **Platform yes** if any WSL2 distro is used; leave Auto unless WSL is retired on the host | `Stop-Service WSLService` only when deliberately disabling WSL; normal idle path is distro shutdown, not service kill |
|
|
16
|
+
| **`vmmemWSL`** (WSL2 utility VM) | HCS VM Owner=`WSL`; worker chain `vmcompute` → `vmwp` → `vmmemWSL` | **Yes while any distro is Running** (`wsl -l -v`) | `wsl --shutdown` (stops all WSL2 distros and the WSL utility VM; disrupts Docker's WSL backend too) |
|
|
17
|
+
| **First user wake of WSL VM** | **Unknown** in the capture (no Security 4688; no surviving user `wsl.exe` from the create second). Docker was **not** up yet at that create time | N/A — attribution gap | Same as `vmmemWSL` once running |
|
|
18
|
+
| **Docker Desktop / `com.docker.backend`** | Docker Desktop launches backend (product log). **HKCU Run** key registers Docker Desktop for logon. Windows service `com.docker.service` was **Stopped** / Manual — engine path is Desktop/backend user-mode | **Only when containers or Docker tooling are in active use** | Quit Docker Desktop (tray → Quit); confirm `com.docker.backend` gone. Optional: remove or disable the HKCU Run value named `Docker Desktop` so logon does not relaunch it |
|
|
19
|
+
| **Live `wsl.exe` for `docker-desktop` + Ubuntu integration** | Parent **`com.docker.backend.exe` (services)** | Same as Docker Desktop | Stop Docker Desktop; or `wsl --shutdown` (broader blast radius) |
|
|
20
|
+
| **Live `wsl.exe` for `code-index-mcp`** | Parent **`codex.exe`** ← **ChatGPT.exe** ← explorer. Command shape: `wsl.exe …/code-index-mcp` | **Only while Codex/ChatGPT needs the Ubuntu MCP indexer** | Exit Codex / ChatGPT app-server session; confirm no `wsl.exe` whose command line is `code-index-mcp` |
|
|
21
|
+
| **Plain `vmmem` (cowork HCS VM)** | HCS VM Owner=`cowork-vm-*` (name match). Chain `vmcompute` → `vmwp` → `vmmem`. **Exact user process that created the VM is unknown** (no live parent edge). Claude Desktop cmdlines carry `cowork-*` schemes; that is label association, not a proven create edge | **Only while Claude Cowork VM features are in use** | Quit Claude Desktop / Cowork UI that owns the session; if the HCS VM remains Running, treat full stop as an open procedure (see open questions). Do **not** assume `wsl --shutdown` stops this VM — it is not Owner=`WSL` |
|
|
22
|
+
| **grok as WSL parent** | **None** in the capture (high-confidence negative) | N/A | N/A |
|
|
23
|
+
| **claude as parent of live `wsl.exe`** | **None** in the capture (high-confidence negative) | N/A for WSL shells; see cowork row for the separate HCS VM | N/A for `wsl.exe` |
|
|
24
|
+
|
|
25
|
+
### Capture facts that stay fixed for this matrix
|
|
26
|
+
|
|
27
|
+
These are host-capture facts the matrix must not rewrite:
|
|
28
|
+
|
|
29
|
+
- Docker autostart comes from the **HKCU `Run` key** entry for Docker Desktop (not the stopped `com.docker.service`).
|
|
30
|
+
- **Codex `code-index-mcp`** is a live holder of `wsl.exe`; it is not the create-time owner of `vmmemWSL` when Codex starts hours later.
|
|
31
|
+
- **cowork-vm** is a **separate** HCS VM and plain `vmmem` consumer; it is not `vmmemWSL`.
|
|
32
|
+
- **grok** and **claude** were **not** parents of any live `wsl.exe` in the capture.
|
|
33
|
+
|
|
34
|
+
## Open questions
|
|
35
|
+
|
|
36
|
+
Named gaps only — do not fill these with guesses in policy or code:
|
|
37
|
+
|
|
38
|
+
1. **Exact user-mode process that first woke the WSL VM** at the `vmmemWSL` create second (Security 4688 / Sysmon not available in the capture).
|
|
39
|
+
2. **Exact process that created HCS `cowork-vm-*` / plain `vmmem`** (parent chain ends at `vmwp` / `vmcompute`).
|
|
40
|
+
3. **Whether a given Docker start was pure logon Run vs interactive tray open** (Run key proves Docker can autostart; a dead parent PID on the backend does not by itself prove which path fired).
|
|
41
|
+
4. **Identity of dead parents** of mid-session Ubuntu `wslhost` processes whose PPID is already recycled.
|
|
42
|
+
5. **Documented, safe idle-stop for the cowork HCS VM** when Claude UI is gone but `hcsdiag` still lists Owner=`cowork-vm-*` Running.
|
|
43
|
+
6. **Whether historical sessions of grok/claude started WSL earlier in a boot** — unprovable without process-creation audit history; live snapshot negatives do not extend backward.
|
|
44
|
+
|
|
45
|
+
To close (1), (2), (4), or (6): enable process-creation audit (Security 4688) or Sysmon with filters on `wsl.exe`, `vmwp.exe`, and Docker/Claude/Codex image paths, then re-capture on a clean boot.
|
|
46
|
+
|
|
47
|
+
## Policy options (with costs)
|
|
48
|
+
|
|
49
|
+
Choose explicitly. None of these options includes an unmeasured `.wslconfig` `memory=` write.
|
|
50
|
+
|
|
51
|
+
| Option | Action | RAM / sprawl effect (directional) | Cost / risk |
|
|
52
|
+
|--------|--------|-----------------------------------|-------------|
|
|
53
|
+
| **P0 — Observe only** | Keep matrix; no host change | None until a component is stopped | Continues dual-VM + Docker + Codex WSL hold when those apps run |
|
|
54
|
+
| **P1 — Docker on demand** | Remove or disable HKCU Run `Docker Desktop`; start Desktop only when containers are needed | Avoids Docker backend + docker-desktop/Ubuntu integration `wsl.exe` on boots/sessions that never use Docker | Manual start latency; first container work pays cold start; any script that assumes Docker is already up fails until launch |
|
|
55
|
+
| **P2 — Docker fully off when idle** | Quit Docker Desktop after use; optional P1 | Frees backend private set and Docker-held WSL distro activity | Must re-open Desktop before compose/build; `wsl -l -v` may still show Running until `wsl --shutdown` or Docker stops holding distros |
|
|
56
|
+
| **P3 — Codex indexer off when idle** | Exit ChatGPT/Codex app-server when not reviewing; disable code-index MCP if product settings allow | Drops Codex-held `wsl.exe … code-index-mcp` edges | Codex features that need the Ubuntu indexer fail until restart; does **not** by itself tear down `vmmemWSL` if Docker or another client still holds a distro |
|
|
57
|
+
| **P4 — WSL idle shutdown** | When no Docker/Codex/other WSL client is needed: `wsl --shutdown` | Tears down Ubuntu + docker-desktop distros and `vmmemWSL` | **Breaks** any live Docker WSL backend and any in-distro MCP until restart; never use mid-task if containers or WSL MCP are active |
|
|
58
|
+
| **P5 — Cowork VM idle policy** | Quit Claude Cowork/Desktop when the VM is not needed; re-check `hcsdiag list` for Owner=`cowork-vm-*` | Targets plain `vmmem` (~multi-GB private in the capture) without touching Owner=`WSL` | Create/stop procedure for a leftover Running cowork VM is still an open question; wrong kill path can disrupt Cowork artifacts |
|
|
59
|
+
| **P6 — Process-creation audit** | Turn on 4688 or Sysmon for wsl/vmwp/Docker/Claude/Codex | No direct RAM win; closes open questions (1)(2)(4)(6) | Audit volume, privacy review, and storage for event logs |
|
|
60
|
+
| **P7 — `.wslconfig` memory cap** | Set `memory=` under `[wsl2]` | Caps WSL utility VM commit **only after** measured before/after on this host | **Blocked here until measured.** Risk: OOM inside distros, Docker backend instability, false “fix” that leaves cowork `vmmem` untouched |
|
|
61
|
+
|
|
62
|
+
### Measurement gate for P7 (and any memory cap)
|
|
63
|
+
|
|
64
|
+
Before any `.wslconfig` memory write:
|
|
65
|
+
|
|
66
|
+
1. Record `wsl -l -v`, `hcsdiag list`, and private working set for `vmmemWSL` / plain `vmmem` / `com.docker.backend`.
|
|
67
|
+
2. Name which component the cap is meant to bound (Owner=`WSL` only — cowork is outside `.wslconfig`).
|
|
68
|
+
3. Apply cap; reboot or `wsl --shutdown` + re-start workload as required for the setting to apply.
|
|
69
|
+
4. Re-record the same counters under the same workload.
|
|
70
|
+
5. Commit the before/after note next to the policy decision (issue comment or evidence pack). Without that note, leave `.wslconfig` without a memory cap.
|
|
71
|
+
|
|
72
|
+
## Quick identification commands
|
|
73
|
+
|
|
74
|
+
Read-only checks agents and operators use on Windows:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
wsl -l -v
|
|
78
|
+
hcsdiag list
|
|
79
|
+
Get-CimInstance Win32_Service -Filter "Name='WSLService'"
|
|
80
|
+
Get-ItemProperty HKCU:\Software\Microsoft\Windows\CurrentVersion\Run
|
|
81
|
+
Get-CimInstance Win32_Process -Filter "Name='wsl.exe'" | Select ProcessId, ParentProcessId, CommandLine
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Resolve each `wsl.exe` ParentProcessId to an image name before blaming an agent binary. A missing parent is **unknown**, not proof of a named starter.
|
|
85
|
+
|
|
86
|
+
## Related
|
|
87
|
+
|
|
88
|
+
- Issue: [jl-cmd/claude-dev-env#256](https://github.com/jl-cmd/claude-dev-env/issues/256) (child of epic #252)
|
|
89
|
+
- Local forensic write-up: `results/05-wsl-who-started.md` under the host evidence pack path named in the issue body
|
package/hooks/blocking/CLAUDE.md
CHANGED
|
@@ -9,6 +9,7 @@ PreToolUse hooks that deny (block) tool calls when a rule is violated. The main
|
|
|
9
9
|
| `config/` | Shared constants for the verified-commit gate family (`verified_commit_constants.py`) |
|
|
10
10
|
| `tdd_enforcer_parts/` | Concern modules the `tdd_enforcer.py` entry hook wires together: path classification, content analysis, candidate-path resolution, freshness, git-tracking restore detection, decisions, and constants |
|
|
11
11
|
| `verified_commit_gate_parts/` | Concern modules the `verified_commit_gate.py` entry hook wires together: command tokenization, directory-change resolution, gated git-invocation resolution, deny-reason resolution, and deny-payload assembly |
|
|
12
|
+
| `code_review_stamp_write_blocker_parts/` | Concern modules the `code_review_stamp_directory_write_blocker.py` entry hook wires together: the split directory-change-into-stamp matcher and the obfuscated-stamp-path-write matcher |
|
|
12
13
|
| `claude_md_orphan_file_blocker_parts/` | Concern modules the `claude_md_orphan_file_blocker.py` entry hook wires together: reference extraction, subtree scan, scan plan, decision, and constants |
|
|
13
14
|
| `package_inventory_stale_blocker_parts/` | Concern modules the `package_inventory_stale_blocker.py` entry hook wires together: inventory detection, decision, and constants |
|
|
14
15
|
| `inventory_intent_records/` | The shared per-session pending-intent store both inventory blockers read to break the file/row add-order deadlock |
|
|
@@ -68,10 +69,14 @@ The check modules it calls are the `code_rules_<concern>.py` files below.
|
|
|
68
69
|
|
|
69
70
|
| File | Event | What it blocks |
|
|
70
71
|
|---|---|---|
|
|
72
|
+
| `orchestrator_refresh_reschedule_gate.py` | PreToolUse (ScheduleWakeup/CronCreate) | `/orchestrator-refresh` re-arm when run status is not `active` |
|
|
71
73
|
| `block_main_commit.py` | PreToolUse (Bash) | `git commit`/`git push` directly to `main` |
|
|
72
74
|
| `bot_mention_comment_blocker.py` | PreToolUse (Write/Edit) | PR review comments that @-mention a bot |
|
|
73
75
|
| `claude_md_orphan_file_blocker.py` | PreToolUse (Write/Edit/MultiEdit) | Per-directory `CLAUDE.md` table cells naming a bare filename absent from the directory subtree |
|
|
74
|
-
| `
|
|
76
|
+
| `code_review_pr_create_gate.py` | PreToolUse (Bash/MCP GitHub) | `gh pr create` or the MCP `create_pull_request` tool without a clean `xhigh` code-review stamp covering the branch surface |
|
|
77
|
+
| `code_review_push_gate.py` | PreToolUse (Bash/PowerShell) | `git push` without a clean `low` code-review stamp covering the branch surface |
|
|
78
|
+
| `code_review_stamp_directory_write_blocker.py` | PreToolUse (Bash/PowerShell/Write/Edit/MultiEdit) | Shell or file-tool writes into `~/.claude/code-review-stamps/`, and shell references to the stamp store module or its mint call, outside the sanctioned invoker |
|
|
79
|
+
| `code_verifier_spawn_preflight_gate.py` | PreToolUse (Agent) | Spawning the `code-verifier` subagent when the branch has a merge conflict vs its base or a CODE_RULES violation on a working-tree-added line, or the CODE_RULES engine fails to load |
|
|
75
80
|
| `convergence_gate_blocker.py` | PreToolUse (Bash) | Convergence workflow actions on a conflicting PR |
|
|
76
81
|
| `conventional_pr_title_gate.py` | PreToolUse (Bash) | `gh pr create`/`gh pr edit` with a `--title` that is not a Conventional Commit, in a repo whose CI runs a semantic-pull-request title check |
|
|
77
82
|
| `destructive_command_blocker.py` | PreToolUse (Bash/PowerShell) | Shell commands with destructive literals (`rm -rf`, `git reset --hard`, etc.) |
|
|
@@ -121,6 +126,9 @@ The check modules it calls are the `code_rules_<concern>.py` files below.
|
|
|
121
126
|
| File | Role |
|
|
122
127
|
|---|---|
|
|
123
128
|
| `_gh_body_arg_utils.py` | Parsing helpers for `gh_body_arg_blocker.py` |
|
|
129
|
+
| `code_review_enforcement_config_bootstrap.py` | Binds `config.code_review_enforcement_constants` to the sibling `config/` file by explicit location, so the code-review gate family resolves its constants regardless of a foreign `config` package's `sys.path` order |
|
|
130
|
+
| `code_review_gate_deny.py` | Shared deny scaffold for the push and PR-create code-review gates: the `hookSpecificOutput` deny-payload builder and the log-and-emit helper, so both gates share one deny shape |
|
|
131
|
+
| `code_review_stamp_store.py` | Reads and writes the per-work-tree code-review stamp files under `~/.claude/code-review-stamps/`, and decides whether a clean stamp at the needed effort covers the live branch surface |
|
|
124
132
|
| `pr_description_body_audit.py` | Body audit logic for `pr_description_enforcer.py` |
|
|
125
133
|
| `pr_description_command_parser.py` | `gh` command parsing for `pr_description_enforcer.py` |
|
|
126
134
|
| `pr_description_pr_number.py` | PR number extraction logic |
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Deterministic loader for the code-review enforcement constants module.
|
|
2
|
+
|
|
3
|
+
The stamp store and every code-review gate import their shared constants as
|
|
4
|
+
``from config.code_review_enforcement_constants import ...``. In the installed
|
|
5
|
+
hook tree a second, unrelated ``config`` package can sit ahead of this package
|
|
6
|
+
on ``sys.path`` and win that dotted name by path order, so the import binds to
|
|
7
|
+
the wrong file and raises ImportError on any constant the stale copy lacks.
|
|
8
|
+
This module binds the dotted name to its sibling ``config/`` file by explicit
|
|
9
|
+
location, so resolution never depends on ``sys.path`` order.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import importlib.util
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def register_code_review_enforcement_constants() -> None:
|
|
20
|
+
"""Bind the code-review enforcement constants dotted name to its config file.
|
|
21
|
+
|
|
22
|
+
::
|
|
23
|
+
|
|
24
|
+
sys.path = ["<installed hooks>", "<blocking>"] # foreign config first
|
|
25
|
+
register_code_review_enforcement_constants()
|
|
26
|
+
from config.code_review_enforcement_constants import STAMP_DIRECTORY_NAME
|
|
27
|
+
ok: bound to <blocking>/config/code_review_enforcement_constants.py
|
|
28
|
+
|
|
29
|
+
A ``from config.code_review_enforcement_constants import`` that follows this
|
|
30
|
+
call reads the entry straight from ``sys.modules``, so it resolves to this
|
|
31
|
+
file whatever else owns the ``config`` name. An entry already cached is left
|
|
32
|
+
in place, so the call is idempotent and never displaces a module a caller
|
|
33
|
+
loaded on purpose.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
None. The effect is the ``sys.modules`` registration.
|
|
37
|
+
"""
|
|
38
|
+
_bind_config_module_by_location(
|
|
39
|
+
"config.code_review_enforcement_constants",
|
|
40
|
+
Path(__file__).resolve().parent / "config" / "code_review_enforcement_constants.py",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _bind_config_module_by_location(dotted_name: str, constants_file_path: Path) -> None:
|
|
45
|
+
"""Register a dotted config name against an explicit file path in sys.modules."""
|
|
46
|
+
if dotted_name in sys.modules:
|
|
47
|
+
return
|
|
48
|
+
module_spec = importlib.util.spec_from_file_location(dotted_name, constants_file_path)
|
|
49
|
+
if module_spec is None or module_spec.loader is None:
|
|
50
|
+
return
|
|
51
|
+
constants_module = importlib.util.module_from_spec(module_spec)
|
|
52
|
+
sys.modules[dotted_name] = constants_module
|
|
53
|
+
module_spec.loader.exec_module(constants_module)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Shared deny scaffold for the code-review PreToolUse gates.
|
|
2
|
+
|
|
3
|
+
The push gate and the PR-create gate deny a blocked tool call the same way: a
|
|
4
|
+
``hookSpecificOutput`` payload carrying the deny decision and a corrective
|
|
5
|
+
reason, logged through ``log_hook_block`` before it reaches stdout. This module
|
|
6
|
+
holds that one payload builder and the log-and-emit helper, so the two gates
|
|
7
|
+
share a single deny shape and stay in step.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
_blocking_directory = str(Path(__file__).resolve().parent)
|
|
18
|
+
_hooks_directory = str(Path(__file__).resolve().parent.parent)
|
|
19
|
+
for each_bootstrap_directory in (_blocking_directory, _hooks_directory):
|
|
20
|
+
if each_bootstrap_directory not in sys.path:
|
|
21
|
+
sys.path.insert(0, each_bootstrap_directory)
|
|
22
|
+
|
|
23
|
+
from code_review_enforcement_config_bootstrap import (
|
|
24
|
+
register_code_review_enforcement_constants,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
register_code_review_enforcement_constants()
|
|
28
|
+
|
|
29
|
+
from config.code_review_enforcement_constants import (
|
|
30
|
+
DENY_PERMISSION_DECISION,
|
|
31
|
+
PRE_TOOL_USE_HOOK_EVENT_NAME,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from hooks_constants.hook_block_logger import log_hook_block
|
|
35
|
+
except ImportError as import_error:
|
|
36
|
+
raise ImportError(
|
|
37
|
+
"the code_review_gate_deny dependencies did not import; "
|
|
38
|
+
"ensure the hooks directory is importable."
|
|
39
|
+
) from import_error
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def build_code_review_deny_payload(deny_reason: str) -> dict[str, dict[str, str]]:
|
|
43
|
+
"""Build the PreToolUse deny payload for a blocked code-review gate action.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
deny_reason: The corrective message naming why the action is denied.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
The ``hookSpecificOutput`` deny payload.
|
|
50
|
+
"""
|
|
51
|
+
return {
|
|
52
|
+
"hookSpecificOutput": {
|
|
53
|
+
"hookEventName": PRE_TOOL_USE_HOOK_EVENT_NAME,
|
|
54
|
+
"permissionDecision": DENY_PERMISSION_DECISION,
|
|
55
|
+
"permissionDecisionReason": deny_reason,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def log_and_emit_code_review_deny(deny_reason: str, tool_name: str, hook_module_name: str) -> None:
|
|
61
|
+
"""Log a code-review gate block and write its deny payload to stdout.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
deny_reason: The corrective message naming why the action is denied.
|
|
65
|
+
tool_name: The name of the gated tool, recorded in the block log.
|
|
66
|
+
hook_module_name: The calling gate's module name, recorded in the log.
|
|
67
|
+
"""
|
|
68
|
+
log_hook_block(
|
|
69
|
+
calling_hook_name=hook_module_name,
|
|
70
|
+
hook_event=PRE_TOOL_USE_HOOK_EVENT_NAME,
|
|
71
|
+
block_reason=deny_reason,
|
|
72
|
+
tool_name=tool_name if isinstance(tool_name, str) else None,
|
|
73
|
+
)
|
|
74
|
+
sys.stdout.write(json.dumps(build_code_review_deny_payload(deny_reason)) + "\n")
|