vmware-debug 2.3.0__tar.gz → 2.3.1__tar.gz

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 (99) hide show
  1. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/.claude-plugin/plugin.json +1 -1
  2. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/.mcp.json +1 -1
  3. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/PKG-INFO +8 -3
  4. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/README-CN.md +4 -0
  5. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/README.md +4 -0
  6. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/RELEASE_NOTES.md +10 -1
  7. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/openclaw.plugin.json +1 -1
  8. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/package.json +1 -1
  9. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/pyproject.toml +4 -3
  10. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/server.json +2 -2
  11. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/skills/vmware-debug/SKILL.md +3 -1
  12. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/setup-guide.md +2 -2
  13. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_audit_isolation.py +7 -2
  14. vmware_debug-2.3.1/tests/eval/regression/test_cli_policy_denial.py +23 -0
  15. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/uv.lock +16 -14
  16. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/__init__.py +1 -1
  17. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/cli.py +17 -1
  18. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/.clawhubignore +0 -0
  19. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/.gitignore +0 -0
  20. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/SECURITY.md +0 -0
  21. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/docs/plans/2026-08-28-investigation-layer-design.md +0 -0
  22. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/agent-guardrails.md +0 -0
  23. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/capabilities.md +0 -0
  24. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/cli-reference.md +0 -0
  25. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/event-envelope.md +0 -0
  26. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/skills/vmware-debug/references/routing.md +0 -0
  27. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/__init__.py +0 -0
  28. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/conftest.py +0 -0
  29. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/__init__.py +0 -0
  30. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/__init__.py +0 -0
  31. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/_family.py +0 -0
  32. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/_scores.json +0 -0
  33. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/_scoring.py +0 -0
  34. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/_skill.py +0 -0
  35. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/conftest.py +0 -0
  36. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/test_entity_reachability.py +0 -0
  37. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/test_error_actionability.py +0 -0
  38. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/test_tool_description_quality.py +0 -0
  39. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
  40. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/__init__.py +0 -0
  41. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/_vsphere_event_vocabulary.py +0 -0
  42. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_capability_grader.py +0 -0
  43. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_case_id_non_ascii.py +0 -0
  44. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_case_plan_max_steps.py +0 -0
  45. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_classification_against_vsphere_vocabulary.py +0 -0
  46. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_debug_regressions.py +0 -0
  47. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_environment_resolver_isolation.py +0 -0
  48. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_every_tool_parameter_is_documented.py +0 -0
  49. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_evidence_payload_contract.py +0 -0
  50. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_host_lifecycle_classification.py +0 -0
  51. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_investigation_metrics.py +0 -0
  52. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_real_alert_titles_classify.py +0 -0
  53. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_result_envelope.py +0 -0
  54. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_skill_name_aliases.py +0 -0
  55. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_spike_resolution.py +0 -0
  56. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/eval/regression/test_tool_annotations.py +0 -0
  57. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_conclusion.py +0 -0
  58. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_evidence.py +0 -0
  59. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_grading.py +0 -0
  60. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_grading_per_hypothesis.py +0 -0
  61. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_hypotheses.py +0 -0
  62. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_ids.py +0 -0
  63. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_ledger_concurrency.py +0 -0
  64. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_plan.py +0 -0
  65. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_readiness.py +0 -0
  66. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_store.py +0 -0
  67. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_timeline_close.py +0 -0
  68. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_case_tools.py +0 -0
  69. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_knowledge.py +0 -0
  70. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_mcp_audit.py +0 -0
  71. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_policy_roles.py +0 -0
  72. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_safe_error_passthrough.py +0 -0
  73. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/tests/test_timeline.py +0 -0
  74. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/envelope.py +0 -0
  75. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/mcp/__init__.py +0 -0
  76. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/mcp/tools.py +0 -0
  77. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/mcp_server/__init__.py +0 -0
  78. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/mcp_server/server.py +0 -0
  79. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/__init__.py +0 -0
  80. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/__init__.py +0 -0
  81. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/api.py +0 -0
  82. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/conclusion.py +0 -0
  83. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/evidence.py +0 -0
  84. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/grading.py +0 -0
  85. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/hypotheses.py +0 -0
  86. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/ids.py +0 -0
  87. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/knowledge.py +0 -0
  88. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/model.py +0 -0
  89. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/payloads.py +0 -0
  90. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/plan.py +0 -0
  91. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/readiness.py +0 -0
  92. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/sources.py +0 -0
  93. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/store.py +0 -0
  94. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/cases/timeline.py +0 -0
  95. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/skill_names.py +0 -0
  96. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/ops/timeline.py +0 -0
  97. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/policy_roles.py +0 -0
  98. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/rules/evidence_sources.yaml +0 -0
  99. {vmware_debug-2.3.0 → vmware_debug-2.3.1}/vmware_debug/rules/grading_rules.yaml +0 -0
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "vmware-debug",
4
4
  "displayName": "VMware Debug",
5
- "version": "2.3.0",
5
+ "version": "2.3.1",
6
6
  "description": "VMware incident timeline, root-cause routing, local case ledger; no vSphere access. 14 MCP tools.",
7
7
  "author": {
8
8
  "name": "Wei Zhou",
@@ -5,7 +5,7 @@
5
5
  "command": "uvx",
6
6
  "args": [
7
7
  "--from",
8
- "vmware-debug==2.3.0",
8
+ "vmware-debug==2.3.1",
9
9
  "vmware-debug-mcp"
10
10
  ]
11
11
  }
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vmware-debug
3
- Version: 2.3.0
3
+ Version: 2.3.1
4
4
  Summary: VMware diagnostic brain — offline incident triage, log/event correlation, and root-cause routing across the VMware skill family
5
5
  Project-URL: Homepage, https://github.com/vmware-skills/VMware-Debug
6
6
  Project-URL: Repository, https://github.com/vmware-skills/VMware-Debug
7
7
  Project-URL: Issues, https://github.com/vmware-skills/VMware-Debug/issues
8
8
  Project-URL: Changelog, https://github.com/vmware-skills/VMware-Debug/blob/main/RELEASE_NOTES.md
9
- Author-email: Wei Zhou <wei-wz.zhou@broadcom.com>
9
+ Author-email: Wei Zhou <zhouwei008@gmail.com>
10
10
  License-Expression: MIT
11
11
  Keywords: ai-ops,debug,diagnostics,mcp,rca,troubleshooting,vmware,vsphere
12
12
  Classifier: Development Status :: 4 - Beta
@@ -14,7 +14,8 @@ Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Topic :: System :: Monitoring
16
16
  Requires-Python: >=3.10
17
- Requires-Dist: mcp[cli]<2.0,>=1.28
17
+ Requires-Dist: certifi>=2026.10.19
18
+ Requires-Dist: mcp[cli]<2.0,>=1.30
18
19
  Requires-Dist: pyyaml<7.0,>=6.0
19
20
  Requires-Dist: rich<15.0,>=13.0
20
21
  Requires-Dist: typer<1.0,>=0.12
@@ -25,6 +26,8 @@ Description-Content-Type: text/markdown
25
26
 
26
27
  # VMware Debug
27
28
 
29
+ For a read-only check of this skill with your model and agent host, use the shared [model-fit self-test](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md) in the marketplace root.
30
+
28
31
  > **Disclaimer**: Community-maintained open-source project, **not affiliated with,
29
32
  > endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and
30
33
  > "vSphere" are trademarks of Broadcom. Source is publicly auditable under the MIT
@@ -105,6 +108,8 @@ event actually is.
105
108
 
106
109
  ## Security
107
110
 
111
+ CLI policy refusals print the reason and rule, exit with code 1, and retain one `denied` audit row.
112
+
108
113
  Offline and credential-free by construction: it connects to no vCenter/NSX/Aria, has no destructive surface on
109
114
  VMware, and holds no secret to leak. The only writes are the local case ledger: evidence, gaps, hypotheses and
110
115
  grade history are append-only; `timeline.md` is regenerated from the evidence, and `case.json` holds the current
@@ -2,6 +2,8 @@
2
2
 
3
3
  # VMware Debug(中文)
4
4
 
5
+ 要用自己的模型和 Agent 平台进行只读自测,请看 marketplace 根目录的[共享模型适配自测任务](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md)。
6
+
5
7
  > **声明**:本项目为社区维护的开源项目,**与 VMware, Inc. 或 Broadcom Inc. 无任何隶属、
6
8
  > 背书或赞助关系。** "VMware"、"vSphere" 为 Broadcom 商标。源码以 MIT 许可证公开可审计。
7
9
 
@@ -68,6 +70,8 @@ debug;debug 因此与其它包零运行时依赖。**请把每条事件的 `ev
68
70
 
69
71
  ## 安全
70
72
 
73
+ CLI 被策略拒绝时会显示原因和规则,以退出码 1 结束,并保留一条 `denied` 审计记录。
74
+
71
75
  结构上离线、无凭据:不连任何 vCenter/NSX/Aria,对 VMware 没有可破坏面,也没有秘密可泄露。
72
76
  唯一的写入是本地案卷:证据、缺口、假设和等级历史只增不改;`timeline.md` 由证据重新生成,
73
77
  `case.json` 保存当前等级和状态。多个写入者同样成立:每次写案卷都要先拿该案卷的锁文件(`.ledger.lock`,
@@ -2,6 +2,8 @@
2
2
 
3
3
  # VMware Debug
4
4
 
5
+ For a read-only check of this skill with your model and agent host, use the shared [model-fit self-test](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md) in the marketplace root.
6
+
5
7
  > **Disclaimer**: Community-maintained open-source project, **not affiliated with,
6
8
  > endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and
7
9
  > "vSphere" are trademarks of Broadcom. Source is publicly auditable under the MIT
@@ -82,6 +84,8 @@ event actually is.
82
84
 
83
85
  ## Security
84
86
 
87
+ CLI policy refusals print the reason and rule, exit with code 1, and retain one `denied` audit row.
88
+
85
89
  Offline and credential-free by construction: it connects to no vCenter/NSX/Aria, has no destructive surface on
86
90
  VMware, and holds no secret to leak. The only writes are the local case ledger: evidence, gaps, hypotheses and
87
91
  grade history are append-only; `timeline.md` is regenerated from the evidence, and `case.json` holds the current
@@ -1,3 +1,12 @@
1
+ ## v2.3.1 — readable Policy refusals and dependency security (2026-11-08)
2
+
3
+ * Update public author/project contact metadata to `zhouwei008@gmail.com`.
4
+
5
+ ### dependency security and sandbox test stability
6
+ * Require certifi 2026.10.19 or later for MCP's HTTP/TLS dependencies (GHSA-6q48-6c7w-9gw2); the lock resolves 2026.10.23.
7
+ * The audit-isolation regression still proves its write reached only the sandbox DB; it skips the production-DB comparison when the host sandbox denies even read-only access to that DB.
8
+ * CLI policy refusals now print their reason and rule without a traceback. The audited wrapper still records exactly one `denied` row before the CLI exits with code 1.
9
+
1
10
  ## v2.3.0 — roles for network callers (HLD §18)
2
11
 
3
12
  * Declares the role metadata vmware-policy ≥ 1.24.0 needs to apply **roles** to this server over HTTPS
@@ -898,4 +907,4 @@ executes fixes (advisor/executor split, mirroring vmware-harden → vmware-pilot
898
907
  ### Notes
899
908
  - Read-only by construction; remediation is routed, never executed.
900
909
  - `parse_timestamp` rejects implausible/garbage timestamps loudly rather than
901
- silently landing at the epoch.
910
+ silently landing at the epoch.
@@ -11,7 +11,7 @@
11
11
  "command": "uvx",
12
12
  "args": [
13
13
  "--from",
14
- "vmware-debug==2.3.0",
14
+ "vmware-debug==2.3.1",
15
15
  "vmware-debug-mcp"
16
16
  ]
17
17
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zw008/vmware-debug",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "VMware incident timeline, root-cause routing, local case ledger; no vSphere access. 14 MCP tools.",
5
5
  "private": true,
6
6
  "license": "MIT",
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vmware-debug"
7
- version = "2.3.0"
7
+ version = "2.3.1"
8
8
  description = "VMware diagnostic brain — offline incident triage, log/event correlation, and root-cause routing across the VMware skill family"
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  requires-python = ">=3.10"
12
- authors = [{ name = "Wei Zhou", email = "wei-wz.zhou@broadcom.com" }]
12
+ authors = [{ name = "Wei Zhou", email = "zhouwei008@gmail.com" }]
13
13
  keywords = ["vmware", "vsphere", "debug", "troubleshooting", "diagnostics", "rca", "mcp", "ai-ops"]
14
14
  classifiers = [
15
15
  "Development Status :: 4 - Beta",
@@ -20,7 +20,8 @@ classifiers = [
20
20
  dependencies = [
21
21
  "typer>=0.12,<1.0",
22
22
  "rich>=13.0,<15.0",
23
- "mcp[cli]>=1.28,<2.0",
23
+ "mcp[cli]>=1.30,<2.0",
24
+ "certifi>=2026.10.19", # GHSA-6q48-6c7w-9gw2 via MCP/httpx
24
25
  "vmware-policy>=1.21.0,<2.0",
25
26
  # Imported directly by ops/cases/grading.py for the rules file. It has
26
27
  # always arrived transitively via vmware-policy; a package should
@@ -7,12 +7,12 @@
7
7
  "url": "https://github.com/vmware-skills/VMware-Debug",
8
8
  "source": "github"
9
9
  },
10
- "version": "2.3.0",
10
+ "version": "2.3.1",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "pypi",
14
14
  "identifier": "vmware-debug",
15
- "version": "2.3.0",
15
+ "version": "2.3.1",
16
16
  "runtimeHint": "uvx",
17
17
  "packageArguments": [
18
18
  {
@@ -25,6 +25,8 @@ metadata: {"openclaw":{"requires":{"anyBins":["vmware-debug","uvx"]},"optional":
25
25
 
26
26
  # VMware Debug
27
27
 
28
+ > Self-test: [guide](https://github.com/vmware-skills/marketplace/blob/main/MODEL_FIT_SELF_TEST.md).
29
+
28
30
  > **Disclaimer**: Community-maintained open-source project, **not affiliated with,
29
31
  > endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and "vSphere"
30
32
  > are trademarks of Broadcom. Source is publicly auditable under the MIT license.
@@ -51,7 +53,7 @@ write tools touch only the local case ledger.
51
53
  ## Quick Install
52
54
 
53
55
  ```bash
54
- uv tool install vmware-debug==2.3.0
56
+ uv tool install vmware-debug==2.3.1
55
57
  vmware-debug categories # see what it can diagnose
56
58
  ```
57
59
 
@@ -6,7 +6,7 @@ is a pure, offline correlation engine. There is no `config.yaml` and no `.env`.
6
6
  ## Install
7
7
 
8
8
  ```bash
9
- uv tool install vmware-debug==2.3.0
9
+ uv tool install vmware-debug==2.3.1
10
10
  vmware-debug categories # verify it runs
11
11
  ```
12
12
 
@@ -15,7 +15,7 @@ vmware-debug categories # verify it runs
15
15
  ```json
16
16
  {
17
17
  "command": "uvx",
18
- "args": ["--from", "vmware-debug==2.3.0", "vmware-debug-mcp"]
18
+ "args": ["--from", "vmware-debug==2.3.1", "vmware-debug-mcp"]
19
19
  }
20
20
  ```
21
21
 
@@ -62,6 +62,11 @@ def test_an_audited_write_lands_in_the_sandbox_and_not_in_the_real_db() -> None:
62
62
  real_db = REAL_HOME / ".vmware" / "audit.db"
63
63
  if not real_db.exists():
64
64
  pytest.skip("no production audit database on this machine to check against")
65
- with sqlite3.connect(f"file:{real_db}?mode=ro", uri=True) as con:
66
- leaked = con.execute("SELECT count(*) FROM audit_log WHERE tool = ?", (marker,)).fetchone()[0]
65
+ try:
66
+ with sqlite3.connect(f"file:{real_db}?mode=ro", uri=True) as con:
67
+ leaked = con.execute("SELECT count(*) FROM audit_log WHERE tool = ?", (marker,)).fetchone()[0]
68
+ except sqlite3.OperationalError as exc:
69
+ if "unable to open database file" not in str(exc):
70
+ raise
71
+ pytest.skip("production audit DB is inaccessible from this sandbox")
67
72
  assert leaked == 0, f"{leaked} row(s) from this test reached {real_db}"
@@ -0,0 +1,23 @@
1
+ """CLI policy denial must remain a denial in audit and be readable by an operator."""
2
+
3
+ import pytest
4
+ from typer.testing import CliRunner
5
+
6
+ from vmware_debug.cli import app
7
+ from vmware_policy import cli_guard
8
+ from vmware_policy.policy import PolicyDenied, PolicyResult
9
+
10
+
11
+ @pytest.mark.parametrize("args", [["categories"], ["triage"]])
12
+ def test_denied_cli_command_prints_reason_and_records_one_denial(monkeypatch, args):
13
+ rows = []
14
+ def deny(*args, **kwargs):
15
+ raise PolicyDenied(PolicyResult(allowed=False, rule="ops-review", reason="offline checks denied"))
16
+ monkeypatch.setattr(cli_guard, "guard", deny)
17
+ monkeypatch.setattr(cli_guard, "audit_call", lambda *a, **kw: rows.append(kw))
18
+ result = CliRunner().invoke(app, args, input="[]")
19
+ assert result.exit_code == 1
20
+ assert "Denied by policy" in result.output
21
+ assert "offline checks denied" in result.output and "ops-review" in result.output
22
+ assert "Traceback" not in result.output
23
+ assert len(rows) == 1 and rows[0]["status"] == "denied"
@@ -1,12 +1,12 @@
1
1
  version = 1
2
- revision = 3
2
+ revision = 5
3
3
  requires-python = ">=3.10"
4
4
  resolution-markers = [
5
5
  "python_full_version >= '3.14' and sys_platform == 'win32'",
6
6
  "python_full_version >= '3.14' and sys_platform != 'win32'",
7
7
  "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'",
8
- "python_full_version < '3.11' and sys_platform == 'win32'",
9
8
  "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'win32'",
9
+ "python_full_version < '3.11' and sys_platform == 'win32'",
10
10
  "python_full_version < '3.11' and sys_platform != 'win32'",
11
11
  ]
12
12
 
@@ -53,11 +53,11 @@ wheels = [
53
53
 
54
54
  [[package]]
55
55
  name = "certifi"
56
- version = "2026.6.17"
56
+ version = "2026.10.23"
57
57
  source = { registry = "https://pypi.org/simple" }
58
- sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" }
58
+ sdist = { url = "https://files.pythonhosted.org/packages/fd/11/b524cbd9bbdaca58608e4cf4053fd463417549e1959fa8d3aa18319f9555/certifi-2026.10.23.tar.gz", hash = "sha256:a9895e6e2758b371e0545e321a3a8fd1fcfd9829ada8824a936dfb16aaaf36cd", size = 136225, upload-time = "2026-10-23T11:36:16.478Z" }
59
59
  wheels = [
60
- { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" },
60
+ { url = "https://files.pythonhosted.org/packages/32/d1/a3652fe2c19134d0d15922177821d863cbd7e842d53a21bbe7e8e3a1b5ac/certifi-2026.10.23-py3-none-any.whl", hash = "sha256:e9b54de76e530bb6cf0ccd036787103acb58fd42ba930ea8bf948ddbaa2f61d1", size = 135082, upload-time = "2026-10-23T11:36:14.763Z" },
61
61
  ]
62
62
 
63
63
  [[package]]
@@ -356,7 +356,7 @@ name = "exceptiongroup"
356
356
  version = "1.3.1"
357
357
  source = { registry = "https://pypi.org/simple" }
358
358
  dependencies = [
359
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
359
+ { name = "typing-extensions" },
360
360
  ]
361
361
  sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
362
362
  wheels = [
@@ -469,7 +469,7 @@ wheels = [
469
469
 
470
470
  [[package]]
471
471
  name = "mcp"
472
- version = "1.28.1"
472
+ version = "1.30.0"
473
473
  source = { registry = "https://pypi.org/simple" }
474
474
  dependencies = [
475
475
  { name = "anyio" },
@@ -487,9 +487,9 @@ dependencies = [
487
487
  { name = "typing-inspection" },
488
488
  { name = "uvicorn", marker = "sys_platform != 'emscripten'" },
489
489
  ]
490
- sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" }
490
+ sdist = { url = "https://files.pythonhosted.org/packages/ba/93/0142dc84a666daf8ad51a34268f34c12fd6fda4f3810c4be2504eecc8212/mcp-1.30.0.tar.gz", hash = "sha256:445414625fce5c295faa505bb11bacece661ab6f4028d57c935db57820b7a3e4", size = 680511, upload-time = "2026-09-07T14:34:15.845Z" }
491
491
  wheels = [
492
- { url = "https://files.pythonhosted.org/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" },
492
+ { url = "https://files.pythonhosted.org/packages/f5/f4/e58bc33317c92a0203664daaf00bf6f41166cc0149e5d6870a03f7cd004a/mcp-1.30.0-py3-none-any.whl", hash = "sha256:666edb5009503e1047c9d60346a756f94b261f05cc2625f23d41c728ffc484d0", size = 234581, upload-time = "2026-09-07T14:34:14.266Z" },
493
493
  ]
494
494
 
495
495
  [package.optional-dependencies]
@@ -1293,9 +1293,10 @@ wheels = [
1293
1293
 
1294
1294
  [[package]]
1295
1295
  name = "vmware-debug"
1296
- version = "2.3.0"
1296
+ version = "2.3.1"
1297
1297
  source = { editable = "." }
1298
1298
  dependencies = [
1299
+ { name = "certifi" },
1299
1300
  { name = "mcp", extra = ["cli"] },
1300
1301
  { name = "pyyaml" },
1301
1302
  { name = "rich" },
@@ -1312,7 +1313,8 @@ dev = [
1312
1313
 
1313
1314
  [package.metadata]
1314
1315
  requires-dist = [
1315
- { name = "mcp", extras = ["cli"], specifier = ">=1.28,<2.0" },
1316
+ { name = "certifi", specifier = ">=2026.10.19" },
1317
+ { name = "mcp", extras = ["cli"], specifier = ">=1.30,<2.0" },
1316
1318
  { name = "pyyaml", specifier = ">=6.0,<7.0" },
1317
1319
  { name = "rich", specifier = ">=13.0,<15.0" },
1318
1320
  { name = "typer", specifier = ">=0.12,<1.0" },
@@ -1328,14 +1330,14 @@ dev = [
1328
1330
 
1329
1331
  [[package]]
1330
1332
  name = "vmware-policy"
1331
- version = "1.24.0"
1333
+ version = "1.24.2"
1332
1334
  source = { registry = "https://pypi.org/simple" }
1333
1335
  dependencies = [
1334
1336
  { name = "pyyaml" },
1335
1337
  { name = "rich" },
1336
1338
  { name = "typer" },
1337
1339
  ]
1338
- sdist = { url = "https://files.pythonhosted.org/packages/4a/62/3a3cb83af77e3dd3c8fc92cb3b7968bfda04a2f71bf0d6f9b72ee3198441/vmware_policy-1.24.0.tar.gz", hash = "sha256:fba12732ccbd4371232c15d9fd014f9f5d697497210aedff2515d1d205ed3577", size = 361302, upload-time = "2026-10-16T07:18:28.317Z" }
1340
+ sdist = { url = "https://files.pythonhosted.org/packages/15/8d/b9867ea1c0112caa481841c290c3fe718c0dbd738757db7877836f46267a/vmware_policy-1.24.2.tar.gz", hash = "sha256:6c816174fcd0b490bcd1f9ebaaa18b80fcb26b0eccb614e938938bb2e8452a40", size = 369315, upload-time = "2026-10-18T03:43:52.753Z" }
1339
1341
  wheels = [
1340
- { url = "https://files.pythonhosted.org/packages/3d/f2/fe3d9d384712023916e2cacddc385f14b095509c5e00aea417d1a9550e98/vmware_policy-1.24.0-py3-none-any.whl", hash = "sha256:cf615d0256134f01fb84c39b083649281a66d2c064c2eaa28786a0fa95ec6e58", size = 131460, upload-time = "2026-10-16T07:18:26.907Z" },
1342
+ { url = "https://files.pythonhosted.org/packages/20/01/bc5f984a420d22514d8c3a627f332891f2008213d455c2f682f311e355f1/vmware_policy-1.24.2-py3-none-any.whl", hash = "sha256:2734ab49a76474fd74beb221a2d6535caab1f91a477668484c145dc2802ae072", size = 134565, upload-time = "2026-10-18T03:43:50.856Z" },
1341
1343
  ]
@@ -6,4 +6,4 @@ remediation to vmware-aiops / vmware-pilot. Never writes to any VMware system
6
6
  and never executes fixes; its only writes go to the local case ledger.
7
7
  """
8
8
 
9
- __version__ = "2.3.0"
9
+ __version__ = "2.3.1"
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import json
13
13
  import sys
14
+ from functools import wraps
14
15
  from pathlib import Path
15
16
  from typing import Optional
16
17
 
@@ -20,7 +21,7 @@ from rich.table import Table
20
21
 
21
22
  from vmware_debug import __version__
22
23
  from vmware_debug.mcp.tools import build_incident_timeline, list_symptom_categories
23
- from vmware_policy import audited, cli_local
24
+ from vmware_policy import PolicyDenied, audited, cli_local, sanitize
24
25
 
25
26
 
26
27
  def _harden_console_encoding() -> None:
@@ -53,6 +54,19 @@ app = typer.Typer(
53
54
  console = Console()
54
55
 
55
56
 
57
+ def handle_cli_errors(func):
58
+ """Render policy refusals after the audited wrapper has recorded the denial."""
59
+ @wraps(func)
60
+ def wrapper(*args, **kwargs):
61
+ try:
62
+ return func(*args, **kwargs)
63
+ except PolicyDenied as exc:
64
+ rule = f" (rule: {sanitize(exc.result.rule, 100)})" if exc.result.rule else ""
65
+ typer.echo(f"Denied by policy: {sanitize(exc.result.reason, 500)}{rule}", err=True)
66
+ raise typer.Exit(1) from None
67
+ return wrapper
68
+
69
+
56
70
  @app.command()
57
71
  @cli_local("prints the installed version")
58
72
  def version() -> None:
@@ -61,6 +75,7 @@ def version() -> None:
61
75
 
62
76
 
63
77
  @app.command()
78
+ @handle_cli_errors
64
79
  @audited(tool="list_symptom_categories")
65
80
  def categories() -> None:
66
81
  """List the symptom categories debug recognises and what to check for each."""
@@ -74,6 +89,7 @@ def categories() -> None:
74
89
 
75
90
 
76
91
  @app.command()
92
+ @handle_cli_errors
77
93
  @audited(tool="build_incident_timeline")
78
94
  def triage(
79
95
  events_file: Optional[Path] = typer.Option(
File without changes
File without changes