vmware-debug 1.11.2__tar.gz → 1.11.4__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 (94) hide show
  1. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/.claude-plugin/plugin.json +2 -2
  2. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/.mcp.json +1 -1
  3. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/PKG-INFO +28 -10
  4. vmware_debug-1.11.4/README-CN.md +72 -0
  5. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/README.md +25 -7
  6. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/RELEASE_NOTES.md +61 -0
  7. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/SECURITY.md +18 -5
  8. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/pyproject.toml +3 -3
  9. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/server.json +3 -3
  10. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/skills/vmware-debug/SKILL.md +25 -12
  11. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/skills/vmware-debug/references/agent-guardrails.md +4 -4
  12. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/skills/vmware-debug/references/capabilities.md +4 -1
  13. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/skills/vmware-debug/references/cli-reference.md +3 -1
  14. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/skills/vmware-debug/references/setup-guide.md +18 -7
  15. vmware_debug-1.11.4/tests/__init__.py +9 -0
  16. vmware_debug-1.11.4/tests/conftest.py +42 -0
  17. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/_scoring.py +2 -2
  18. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/_skill.py +2 -2
  19. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/test_entity_reachability.py +2 -2
  20. vmware_debug-1.11.4/tests/eval/regression/test_audit_isolation.py +67 -0
  21. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_capability_grader.py +3 -3
  22. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_environment_resolver_isolation.py +1 -1
  23. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_investigation_metrics.py +1 -1
  24. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_tool_annotations.py +3 -2
  25. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_conclusion.py +5 -5
  26. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_evidence.py +2 -2
  27. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_grading.py +5 -5
  28. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_hypotheses.py +1 -1
  29. vmware_debug-1.11.4/tests/test_case_ledger_concurrency.py +417 -0
  30. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_readiness.py +1 -1
  31. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_store.py +5 -5
  32. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_timeline_close.py +2 -2
  33. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_knowledge.py +1 -1
  34. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/uv.lock +5 -5
  35. vmware_debug-1.11.4/vmware_debug/__init__.py +9 -0
  36. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/cli.py +29 -4
  37. vmware_debug-1.11.4/vmware_debug/mcp/__init__.py +3 -0
  38. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/mcp/tools.py +5 -3
  39. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/mcp_server/server.py +28 -20
  40. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/api.py +6 -3
  41. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/conclusion.py +37 -27
  42. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/evidence.py +49 -30
  43. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/hypotheses.py +23 -14
  44. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/knowledge.py +1 -1
  45. vmware_debug-1.11.4/vmware_debug/ops/cases/store.py +396 -0
  46. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/timeline.py +74 -57
  47. vmware_debug-1.11.2/README-CN.md +0 -47
  48. vmware_debug-1.11.2/vmware_debug/__init__.py +0 -8
  49. vmware_debug-1.11.2/vmware_debug/mcp/__init__.py +0 -2
  50. vmware_debug-1.11.2/vmware_debug/ops/cases/store.py +0 -184
  51. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/.gitignore +0 -0
  52. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/docs/plans/2026-08-28-investigation-layer-design.md +0 -0
  53. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/skills/vmware-debug/references/event-envelope.md +0 -0
  54. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/skills/vmware-debug/references/routing.md +0 -0
  55. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/__init__.py +0 -0
  56. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/__init__.py +0 -0
  57. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/_family.py +0 -0
  58. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/_scores.json +0 -0
  59. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/conftest.py +0 -0
  60. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/test_error_actionability.py +0 -0
  61. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/test_tool_description_quality.py +0 -0
  62. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
  63. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/__init__.py +0 -0
  64. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/_vsphere_event_vocabulary.py +0 -0
  65. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_case_id_non_ascii.py +0 -0
  66. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_case_plan_max_steps.py +0 -0
  67. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_classification_against_vsphere_vocabulary.py +0 -0
  68. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_debug_regressions.py +0 -0
  69. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_every_tool_parameter_is_documented.py +0 -0
  70. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_evidence_payload_contract.py +0 -0
  71. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_host_lifecycle_classification.py +0 -0
  72. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_result_envelope.py +0 -0
  73. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_skill_name_aliases.py +0 -0
  74. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/eval/regression/test_spike_resolution.py +0 -0
  75. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_ids.py +0 -0
  76. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_plan.py +0 -0
  77. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_case_tools.py +0 -0
  78. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_safe_error_passthrough.py +0 -0
  79. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/tests/test_timeline.py +0 -0
  80. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/envelope.py +0 -0
  81. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/mcp_server/__init__.py +0 -0
  82. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/__init__.py +0 -0
  83. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/__init__.py +0 -0
  84. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/grading.py +0 -0
  85. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/ids.py +0 -0
  86. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/model.py +0 -0
  87. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/payloads.py +0 -0
  88. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/plan.py +0 -0
  89. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/readiness.py +0 -0
  90. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/cases/sources.py +0 -0
  91. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/skill_names.py +0 -0
  92. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/ops/timeline.py +0 -0
  93. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/rules/evidence_sources.yaml +0 -0
  94. {vmware_debug-1.11.2 → vmware_debug-1.11.4}/vmware_debug/rules/grading_rules.yaml +0 -0
@@ -2,8 +2,8 @@
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "vmware-debug",
4
4
  "displayName": "VMware Debug",
5
- "version": "1.11.2",
6
- "description": "Read-only VMware incident correlation: timeline, spikes, root-cause routing. 2 MCP tools.",
5
+ "version": "1.11.4",
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",
9
9
  "email": "zhouwei008@gmail.com",
@@ -5,7 +5,7 @@
5
5
  "command": "uvx",
6
6
  "args": [
7
7
  "--from",
8
- "vmware-debug==1.11.2",
8
+ "vmware-debug==1.11.4",
9
9
  "vmware-debug-mcp"
10
10
  ]
11
11
  }
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vmware-debug
3
- Version: 1.11.2
4
- Summary: VMware diagnostic brain — read-only incident triage, log/event correlation, and root-cause routing across the VMware skill family
3
+ Version: 1.11.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
@@ -18,17 +18,13 @@ Requires-Dist: mcp[cli]<2.0,>=1.10
18
18
  Requires-Dist: pyyaml<7.0,>=6.0
19
19
  Requires-Dist: rich<15.0,>=13.0
20
20
  Requires-Dist: typer<1.0,>=0.12
21
- Requires-Dist: vmware-policy<2.0,>=1.11.0
21
+ Requires-Dist: vmware-policy<2.0,>=1.13.1
22
22
  Description-Content-Type: text/markdown
23
23
 
24
24
  <!-- mcp-name: io.github.vmware-skills/vmware-debug -->
25
25
 
26
26
  # VMware Debug
27
27
 
28
- > ⚠️ **Work in progress** — the core (event correlation engine, MCP tools, CLI)
29
- > is built and tested; README, `server.json`, full reference docs, and packaging
30
- > polish are still landing. Not yet published to PyPI.
31
-
32
28
  > **Disclaimer**: Community-maintained open-source project, **not affiliated with,
33
29
  > endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and
34
30
  > "vSphere" are trademarks of Broadcom. Source is publicly auditable under the MIT
@@ -37,21 +33,43 @@ Description-Content-Type: text/markdown
37
33
  The diagnostic brain of the VMware skill family. You bring the symptom (an error,
38
34
  a log dump, a slow VM); this skill runs a systematic investigation, correlates
39
35
  events from the other skills into one timeline, ranks root-cause hypotheses, and
40
- tells you what to check next. It is **read-only** — it never changes anything and
41
- never executes fixes. Remediation is routed to `vmware-aiops` (single op) or
36
+ tells you what to check next. It **never touches vSphere** — it connects to
37
+ nothing and never executes fixes; its only writes are to a local case ledger
38
+ under `$OPS_HOME`. Remediation is routed to `vmware-aiops` (single op) or
42
39
  `vmware-pilot` (multi-step, gated), mirroring the `vmware-harden → vmware-pilot`
43
40
  advisor/executor split.
44
41
 
45
42
  See [`skills/vmware-debug/SKILL.md`](skills/vmware-debug/SKILL.md) for the full
46
43
  methodology, the event-envelope contract, and symptom routing.
47
44
 
48
- ## MCP tools
45
+ ## MCP tools (14 — 7 read, 7 write)
46
+
47
+ The seven writes go to the local case ledger only; none reaches a VMware system.
48
+
49
+ **Correlation** — stateless, for a single look:
49
50
 
50
51
  | Tool | What |
51
52
  |---|---|
52
53
  | `incident_timeline` | [READ] Correlate pre-fetched events → timeline + spikes + ranked hypotheses + next-check ideas |
53
54
  | `list_symptom_categories` | [READ] List recognised symptom categories + what to check for each |
54
55
 
56
+ **Investigation ledger** — for an incident you will reason about over time:
57
+
58
+ | Tool | What |
59
+ |---|---|
60
+ | `case_open` | [WRITE] Define the event; returns a case id and the grade this environment can reach |
61
+ | `case_readiness` | [READ] What grade this environment can reach, per symptom category, **before** you start |
62
+ | `case_knowledge` | [READ] Which knowledge formats are accepted, what is mounted, and which entries apply to a case |
63
+ | `case_plan` | [READ] What to fetch next — skill, tool and purpose per step; recomputed from the case's current state |
64
+ | `case_list` | [READ] Cases, newest first |
65
+ | `case_get` | [READ] One case: scope, ledger sizes, grade history |
66
+ | `case_hypotheses` | [WRITE] Register a candidate explanation, or read the ledger of what supports and refutes each |
67
+ | `case_submit_evidence` | [WRITE] Record one retrieved fact, with its source, query and time basis |
68
+ | `case_record_gap` | [WRITE] Record what could **not** be retrieved, and how to close it |
69
+ | `case_timeline` | [WRITE] Correlate everything the case has collected into one timeline |
70
+ | `case_grade` | [WRITE] Recompute the conclusion grade from the ledger and record it |
71
+ | `case_close` | [WRITE] Record the final grade, archive, and name what was left open |
72
+
55
73
  ## Offline / Air-Gapped Install (from source)
56
74
 
57
75
  This project uses the modern PEP 517 build system (hatchling), so there is **no
@@ -0,0 +1,72 @@
1
+ <!-- mcp-name: io.github.vmware-skills/vmware-debug -->
2
+
3
+ # VMware Debug(中文)
4
+
5
+ > **声明**:本项目为社区维护的开源项目,**与 VMware, Inc. 或 Broadcom Inc. 无任何隶属、
6
+ > 背书或赞助关系。** "VMware"、"vSphere" 为 Broadcom 商标。源码以 MIT 许可证公开可审计。
7
+
8
+ VMware skill 家族的**诊断大脑**。你给出症状(报错、日志、变慢的 VM),它来跑系统化排查:
9
+ 把其它 skill 取到的事件关联成一条时间线、检测突刺、给根因假设排序,并告诉你下一步该查什么。
10
+ 它**从不触碰 vSphere**——不连接任何东西,也从不执行修复;唯一的写入是 `$OPS_HOME` 下的本地案卷。修复一律路由给 vmware-aiops(单步)或
11
+ vmware-pilot(多步、带审批门控),完全复刻 vmware-harden → vmware-pilot 的「顾问/执行」分工。
12
+
13
+ ## 配套 Skill
14
+
15
+ | 需求 | Skill |
16
+ |---|---|
17
+ | 故障关联 / 根因 | **vmware-debug**(本项目) |
18
+ | 集中日志检索 | vmware-log-insight(把 `log_search` 结果喂给它) |
19
+ | vCenter 事件与告警 | vmware-monitor |
20
+ | 指标 / 异常 | vmware-aria |
21
+ | 执行修复 | vmware-aiops(单步)/ vmware-pilot(多步门控) |
22
+
23
+ ## 安装
24
+
25
+ ```bash
26
+ uv tool install vmware-debug
27
+ vmware-debug categories # 看它能诊断哪些症状类别
28
+ ```
29
+
30
+ ## MCP 工具(14 个:7 读、7 写)
31
+
32
+ 7 个写工具只写本地案卷,没有一个触及任何 VMware 系统。
33
+
34
+ **关联**——无状态,看一眼用:
35
+
36
+ - `incident_timeline`:[READ] 把已取到的事件关联成 时间线 + 突刺 + 排序后的根因假设 + 下一步检查建议
37
+ - `list_symptom_categories`:[READ] 症状类别及对应的排查路由(不知道查什么时用它)
38
+
39
+ **调查案卷**——需要持续推理的事件:
40
+
41
+ - `case_open`:[WRITE] 定义事件;返回案卷 id 和本环境能达到的结论等级
42
+ - `case_readiness`:[READ] 开始**之前**,按症状类别说明本环境能达到的等级
43
+ - `case_knowledge`:[READ] 接受哪些知识格式、挂载了什么、哪些条目适用于某个案卷
44
+ - `case_plan`:[READ] 下一步该取什么——每步给出 skill、工具和目的;按案卷当前状态重算
45
+ - `case_list`:[READ] 案卷列表,最新的在前
46
+ - `case_get`:[READ] 单个案卷:范围、账本大小、等级历史
47
+ - `case_hypotheses`:[WRITE] 登记一个候选解释,或读取各假设的支持/反驳账本
48
+ - `case_submit_evidence`:[WRITE] 记录一条取到的事实,连同来源、查询和时间基准
49
+ - `case_record_gap`:[WRITE] 记录**没能**取到的东西,以及如何补上
50
+ - `case_timeline`:[WRITE] 把案卷收集到的一切关联成一条时间线
51
+ - `case_grade`:[WRITE] 从账本重算结论等级并记录
52
+ - `case_close`:[WRITE] 记录最终等级、归档,并写明留下了哪些未决项
53
+
54
+ **事件信封**:`{ts, source, severity, entity, text, fields}`。agent 把各源事件归一成此形状再交给
55
+ debug;debug 因此与其它包零运行时依赖。**请把每条事件的 `event_type` 保留在 `fields` 里**——
56
+ 分类器除了正文之外也匹配它,而现代 vSphere 多数事件是 `EventEx`:正文是一句通用套话,
57
+ 真正说明「这是什么事件」的只有 `eventTypeId`。
58
+
59
+ ## 安全
60
+
61
+ 结构上离线、无凭据:不连任何 vCenter/NSX/Aria,对 VMware 没有可破坏面,也没有秘密可泄露。
62
+ 唯一的写入是本地案卷:证据、缺口、假设和等级历史只增不改;`timeline.md` 由证据重新生成,
63
+ `case.json` 保存当前等级和状态。多个写入者同样成立:每次写案卷都要先拿该案卷的锁文件(`.ledger.lock`,
64
+ 独占创建),所以两个进程在共享的 `$OPS_HOME` 里写同一个案卷会被串行化,而不是互相丢条目;每个文件都先写到
65
+ 临时文件名再移到位,读者不会读到写了一半的文件(在不支持硬链接的文件系统上,新的证据文件改为原地创建:
66
+ 仍然绝不覆盖,但可能被读到写了一半)。崩溃进程留下的锁会连同持有者一起报告,从不自动删除;
67
+ 确认没有进程在写该案卷后再手动删除。已发布的 1.11.3 及更早版本不拿这个锁,共享案卷目录的每台机器都要升级。
68
+ 详见 [SECURITY.md](SECURITY.md)。
69
+
70
+ ## 许可证
71
+
72
+ MIT。
@@ -2,10 +2,6 @@
2
2
 
3
3
  # VMware Debug
4
4
 
5
- > ⚠️ **Work in progress** — the core (event correlation engine, MCP tools, CLI)
6
- > is built and tested; README, `server.json`, full reference docs, and packaging
7
- > polish are still landing. Not yet published to PyPI.
8
-
9
5
  > **Disclaimer**: Community-maintained open-source project, **not affiliated with,
10
6
  > endorsed by, or sponsored by VMware, Inc. or Broadcom Inc.** "VMware" and
11
7
  > "vSphere" are trademarks of Broadcom. Source is publicly auditable under the MIT
@@ -14,21 +10,43 @@
14
10
  The diagnostic brain of the VMware skill family. You bring the symptom (an error,
15
11
  a log dump, a slow VM); this skill runs a systematic investigation, correlates
16
12
  events from the other skills into one timeline, ranks root-cause hypotheses, and
17
- tells you what to check next. It is **read-only** — it never changes anything and
18
- never executes fixes. Remediation is routed to `vmware-aiops` (single op) or
13
+ tells you what to check next. It **never touches vSphere** — it connects to
14
+ nothing and never executes fixes; its only writes are to a local case ledger
15
+ under `$OPS_HOME`. Remediation is routed to `vmware-aiops` (single op) or
19
16
  `vmware-pilot` (multi-step, gated), mirroring the `vmware-harden → vmware-pilot`
20
17
  advisor/executor split.
21
18
 
22
19
  See [`skills/vmware-debug/SKILL.md`](skills/vmware-debug/SKILL.md) for the full
23
20
  methodology, the event-envelope contract, and symptom routing.
24
21
 
25
- ## MCP tools
22
+ ## MCP tools (14 — 7 read, 7 write)
23
+
24
+ The seven writes go to the local case ledger only; none reaches a VMware system.
25
+
26
+ **Correlation** — stateless, for a single look:
26
27
 
27
28
  | Tool | What |
28
29
  |---|---|
29
30
  | `incident_timeline` | [READ] Correlate pre-fetched events → timeline + spikes + ranked hypotheses + next-check ideas |
30
31
  | `list_symptom_categories` | [READ] List recognised symptom categories + what to check for each |
31
32
 
33
+ **Investigation ledger** — for an incident you will reason about over time:
34
+
35
+ | Tool | What |
36
+ |---|---|
37
+ | `case_open` | [WRITE] Define the event; returns a case id and the grade this environment can reach |
38
+ | `case_readiness` | [READ] What grade this environment can reach, per symptom category, **before** you start |
39
+ | `case_knowledge` | [READ] Which knowledge formats are accepted, what is mounted, and which entries apply to a case |
40
+ | `case_plan` | [READ] What to fetch next — skill, tool and purpose per step; recomputed from the case's current state |
41
+ | `case_list` | [READ] Cases, newest first |
42
+ | `case_get` | [READ] One case: scope, ledger sizes, grade history |
43
+ | `case_hypotheses` | [WRITE] Register a candidate explanation, or read the ledger of what supports and refutes each |
44
+ | `case_submit_evidence` | [WRITE] Record one retrieved fact, with its source, query and time basis |
45
+ | `case_record_gap` | [WRITE] Record what could **not** be retrieved, and how to close it |
46
+ | `case_timeline` | [WRITE] Correlate everything the case has collected into one timeline |
47
+ | `case_grade` | [WRITE] Recompute the conclusion grade from the ledger and record it |
48
+ | `case_close` | [WRITE] Record the final grade, archive, and name what was left open |
49
+
32
50
  ## Offline / Air-Gapped Install (from source)
33
51
 
34
52
  This project uses the modern PEP 517 build system (hatchling), so there is **no
@@ -1,3 +1,64 @@
1
+ ## v1.11.4 — concurrent writers stop losing case entries
2
+
3
+ Two agents on one case — a supported setup, since `OPS_HOME` can point at a shared folder — lost
4
+ entries: six processes writing 150 gaps and hypotheses each kept 157 of 900 gaps and handed out
5
+ duplicate hypothesis ids. Ledger writes are now serialised by a per-case lock (`.ledger.lock`
6
+ in the case folder), and each write lands atomically. Versions up to and including 1.11.3 do not
7
+ take the lock, so **every machine that shares an `OPS_HOME` needs this version** — one older
8
+ writer on the share can still lose entries.
9
+
10
+ The manifests advertised 2 tools (14 are registered), and the docs called the case-ledger writes
11
+ read-only. Both are corrected, and the docs now say that case data is sensitive, owner-only, and
12
+ kept until you delete the case folder.
13
+
14
+ **OpenClaw hid this skill unless `vmware-debug` was on PATH.** `metadata.openclaw.requires` was
15
+ only `bins: ["vmware-debug"]` — there was no `requires.config` or `requires.env`, unlike most of the
16
+ family. OpenClaw hides a skill whose required binary is missing, and a plugin install runs the
17
+ server through uvx, which never puts `vmware-debug` on PATH, so there the skill was "needs setup / not
18
+ visible to the model" (requirement semantics verified on OpenClaw 2026.6.35). `requires` is now
19
+ `anyBins: ["vmware-debug", "uvx"]`; `optional.env` is unchanged, and the placeholder
20
+ `primaryEnv: "NONE"` is gone.
21
+
22
+ **Install commands in the skill pin this release.** ClawHub reviews SKILL.md and references/,
23
+ not the package they install, so an unpinned `uv tool install` vouched for code nobody reviewed.
24
+ Every install command for this package in the skill now names this version.
25
+
26
+ ## v1.11.3 — 398 tests never ran
27
+
28
+
29
+ **`tests/__init__.py` was missing, and the whole suite -- 398 tests -- failed at
30
+ collection.** `tests/eval/` and `tests/eval/regression/` were already packages,
31
+ so `from tests.eval.regression._vsphere_event_vocabulary import ...` resolved
32
+ only where the namespace-package fallback happened to work. It did not on
33
+ Windows. One file.
34
+
35
+ **The suite now runs on a cp936 machine.** Round 3 of the VCF 9 field testing ran
36
+ on Windows Server 2025 with locale cp936. Across the family four repos' suites --
37
+ 1687 tests -- never executed at all, dying at collection reading our own UTF-8
38
+ sources, and 101 more failed the same way. Most of those were the tests that
39
+ verify the destructive-operation guardrails: the guardrails were fine, the tests
40
+ that check them could not open a file. On the UTF-8 CI every one of them was
41
+ green. A security test that cannot run is not a security test.
42
+
43
+ Every text read and write here names its encoding now, `tests/` included -- the
44
+ previous round fixed only the package, which is why this came back. A gate in
45
+ `family_smoke` scans both trees by AST, and the whole family's suites were re-run
46
+ under an ASCII locale to confirm: 15 of 15 green, from 1 of 15.
47
+
48
+ **`--help` no longer dies on a console that cannot encode it.** On any console
49
+ whose encoding cannot carry the characters in our own help text, `--help` exited
50
+ with a `UnicodeEncodeError` traceback -- unavailable exactly on the machines
51
+ where it is most needed. Four repos were affected; the handler is now relaxed in
52
+ all fifteen so a glyph degrades instead of killing the command.
53
+
54
+ **Unknown tool arguments are refused instead of dropped.** The schema declared
55
+ `additionalProperties: false` and the runtime accepted them anyway, so a filter
56
+ argument whose name a model guessed wrong returned the *unfiltered* result with
57
+ nothing to indicate anything had been discarded. Fixed in vmware-policy 1.12.0
58
+ and in force here.
59
+
60
+ Requires vmware-policy 1.12.0.
61
+
1
62
  ## v1.11.2 — the resolver hijack, the discarded event identifier, and a parameter that did nothing
2
63
 
3
64
  Three findings from the 2026-08-30 real-hardware re-test.
@@ -15,11 +15,24 @@ or by email to the maintainer. Please do not open public issues for security bug
15
15
 
16
16
  ## Security Design
17
17
 
18
- ### Read-only and offline by construction
19
- vmware-debug has **no write tools, no network access, and no credentials**. It
20
- does not connect to vCenter, NSX, Aria, or any appliance. Its tools are pure
21
- functions over event data the orchestrating agent has already fetched with the
22
- other skills' read tools. There is no destructive surface and no secret to leak.
18
+ ### Offline by construction; writes only its own ledger
19
+ vmware-debug has **no network access and no credentials**. It does not connect to
20
+ vCenter, NSX, Aria, or any appliance. Its tools work over event data the
21
+ orchestrating agent has already fetched with the other skills' read tools. Seven
22
+ of its fourteen MCP tools write — only to the local investigation ledger under
23
+ `$OPS_HOME` (default `~/.vmware/cases`). Evidence, gaps, hypotheses and grade
24
+ history are only ever added to; `timeline.md` is regenerated from the evidence
25
+ and `case.json` holds the current grade and state. That holds with several writers
26
+ too: every write to a case takes that case's lock file (`.ledger.lock`, created
27
+ exclusively), so two processes working one case in a shared `$OPS_HOME` are
28
+ serialised rather than losing each other's entries, and every file is written to
29
+ a temporary name and moved into place, so a reader never sees half of one (on a
30
+ filesystem without hard links, a new evidence file is created in place instead:
31
+ still never overwritten, but readable half-written). A lock left by a crashed
32
+ process is reported with its holder and never removed automatically; delete it
33
+ once nothing is writing the case. Released versions up to and including 1.11.3
34
+ do not take the lock, so upgrade every machine that shares a cases folder. There
35
+ is no destructive surface against any VMware system and no secret to leak.
23
36
 
24
37
  ### No remediation execution
25
38
  debug only *diagnoses* and *recommends*. Any fix is routed to vmware-aiops
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vmware-debug"
7
- version = "1.11.2"
8
- description = "VMware diagnostic brain — read-only incident triage, log/event correlation, and root-cause routing across the VMware skill family"
7
+ version = "1.11.4"
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"
@@ -21,7 +21,7 @@ dependencies = [
21
21
  "typer>=0.12,<1.0",
22
22
  "rich>=13.0,<15.0",
23
23
  "mcp[cli]>=1.10,<2.0",
24
- "vmware-policy>=1.11.0,<2.0",
24
+ "vmware-policy>=1.13.1,<2.0",
25
25
  # Imported directly by ops/cases/grading.py for the rules file. It has
26
26
  # always arrived transitively via vmware-policy; a package should
27
27
  # declare what it imports rather than rely on a dependency's dependency.
@@ -2,17 +2,17 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.vmware-skills/vmware-debug",
4
4
  "title": "VMware Debug",
5
- "description": "Read-only VMware incident correlation: timeline, spikes, root-cause routing. 2 MCP tools.",
5
+ "description": "VMware incident timeline, root-cause routing, local case ledger; no vSphere access. 14 MCP tools.",
6
6
  "repository": {
7
7
  "url": "https://github.com/vmware-skills/VMware-Debug",
8
8
  "source": "github"
9
9
  },
10
- "version": "1.11.2",
10
+ "version": "1.11.4",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "pypi",
14
14
  "identifier": "vmware-debug",
15
- "version": "1.11.2",
15
+ "version": "1.11.4",
16
16
  "runtimeHint": "uvx",
17
17
  "packageArguments": [
18
18
  {
@@ -10,7 +10,8 @@ description: >
10
10
  don't know where to start. Always use this skill for "diagnose this VMware
11
11
  issue", "why is my VM slow", "troubleshoot this vSphere error", "what does
12
12
  this log mean", "help me figure out what broke" when the context is explicitly
13
- VMware/vSphere/ESXi/NSX. It is READ-ONLY: it never changes anything. Do NOT
13
+ VMware/vSphere/ESXi/NSX. It never touches vSphere: its only writes are to a
14
+ local case ledger. Do NOT
14
15
  use it to execute fixes — single fixes go to vmware-aiops, multi-step gated
15
16
  remediation goes to vmware-pilot. Do NOT use it for routine inventory or
16
17
  health checks with no problem to solve — use vmware-monitor.
@@ -19,7 +20,7 @@ installer:
19
20
  package: vmware-debug
20
21
  allowed-tools:
21
22
  - Bash
22
- metadata: {"openclaw":{"requires":{"bins":["vmware-debug"]},"optional":{"env":["VMWARE_AUDIT_APPROVED_BY","VMWARE_AUDIT_RATIONALE"],"bins":["vmware-policy"]},"primaryEnv":"NONE","homepage":"https://github.com/vmware-skills/VMware-Debug","os":["macos","linux"]}}
23
+ metadata: {"openclaw":{"requires":{"anyBins":["vmware-debug","uvx"]},"optional":{"env":["VMWARE_AUDIT_APPROVED_BY","VMWARE_AUDIT_RATIONALE"],"bins":["vmware-policy"]},"homepage":"https://github.com/vmware-skills/VMware-Debug","os":["macos","linux"]}}
23
24
  ---
24
25
 
25
26
  # VMware Debug
@@ -30,7 +31,8 @@ metadata: {"openclaw":{"requires":{"bins":["vmware-debug"]},"optional":{"env":["
30
31
 
31
32
  The diagnostic brain of the VMware skill family. You bring the symptom; this skill
32
33
  runs the investigation and points at the root cause. It **reads and reasons** — it
33
- never writes. Companion skills do the data collection and the fixing.
34
+ never writes to vSphere; its only writes go to its own local case ledger.
35
+ Companion skills do the data collection and the fixing.
34
36
 
35
37
  ## What This Skill Does
36
38
 
@@ -40,14 +42,16 @@ never writes. Companion skills do the data collection and the fixing.
40
42
  | Root-cause ranking | Score symptom clusters, surface the most likely cause first | Read |
41
43
  | Next-check ideas | Suggest exactly what to look at next (which skill/tool) when you're stuck | Read |
42
44
  | Remediation routing | Hand the fix to vmware-aiops (single) or vmware-pilot (gated, multi-step) | Read (routes only) |
45
+ | Investigation ledger | Open a case; record evidence, gaps and hypotheses; grade and close it | Write (local ledger only) |
43
46
 
44
- **Zero write tools. Zero network access of its own.** It correlates data the agent
45
- has already gathered with the other skills' read tools.
47
+ **No network access of its own, and no write to any VMware system.** It correlates
48
+ data the agent has already gathered with the other skills' read tools; its seven
49
+ write tools touch only the local case ledger.
46
50
 
47
51
  ## Quick Install
48
52
 
49
53
  ```bash
50
- uv tool install vmware-debug
54
+ uv tool install vmware-debug==1.11.4
51
55
  vmware-debug categories # see what it can diagnose
52
56
  ```
53
57
 
@@ -126,7 +130,7 @@ a recommended plan.
126
130
  | `case_grade` | [WRITE] Recompute the conclusion grade from the ledger and record it |
127
131
  | `case_close` | [WRITE] Record the final grade, archive, and name what was left open |
128
132
 
129
- The four writes go to `$OPS_HOME` (default `~/.vmware/cases/`) and nowhere else.
133
+ The seven writes go to `$OPS_HOME` (default `~/.vmware/cases/`) and nowhere else.
130
134
 
131
135
  **List envelope** (output of `list_symptom_categories`): `{items, returned, limit, total, truncated, hint}` — read the rows from `items`. `truncated` is always `false` here, which is the point: it states that the catalogue is complete instead of leaving you to infer it.
132
136
 
@@ -267,11 +271,20 @@ vmware-debug mcp # start stdio MCP server (proxy-
267
271
 
268
272
  ## Audit & Safety
269
273
 
270
- Read-only by construction: no write tools, no network, nothing executed. Remediation
271
- is always routed to aiops/pilot, where the double-confirm / approval / audit gates live
272
- (audit DB `~/.vmware/audit.db`). Policy rules scope by environment; debug has no config
273
- and no connection to declare one about, so it reports a constant `local` — nothing here
274
- touches a remote VMware estate. See `references/setup-guide.md`.
274
+ No network, nothing executed. The seven [WRITE] tools write only to the local case
275
+ ledger under `$OPS_HOME`; nothing here touches a remote VMware estate. Evidence, gaps,
276
+ hypotheses and grade history are only ever added to — `timeline.md` is regenerated from
277
+ the evidence and `case.json` holds the current grade and state. Remediation is always routed to
278
+ aiops/pilot, where the double-confirm / approval / audit gates live (audit DB
279
+ `~/.vmware/audit.db`). debug has no config and no connection, so it registers no
280
+ environment resolver, and its tools do not pass through the policy engine — no policy
281
+ rule applies to them. See `references/setup-guide.md`.
282
+
283
+ **Case data is sensitive and is kept until you delete it.** Each case lives in
284
+ `$OPS_HOME/cases/<case-id>/` (default `~/.vmware/cases/`), created owner-only (`0700`).
285
+ It holds whatever evidence was submitted — host names, addresses, log and event text.
286
+ Nothing is deleted automatically: `case_close` records the grade and marks the case
287
+ closed but keeps its files. Remove a case by deleting its folder.
275
288
 
276
289
  ## License
277
290
 
@@ -16,7 +16,7 @@ vmware-debug are specific to this skill.
16
16
 
17
17
  vmware-debug exposes 14 MCP tools: 7 reads and 7 writes. It connects to nothing
18
18
  and holds no credentials — the calling agent gathers events from the other
19
- skills, normalises them, and hands them over. The four writes go to the local
19
+ skills, normalises them, and hands them over. The seven writes go to the local
20
20
  investigation ledger under `~/.vmware/cases/`, never to a VMware system. That
21
21
  makes it the safest skill in the family to point a small model at — and the one
22
22
  most exposed to the model's reasoning, because its output *is* an
@@ -41,12 +41,12 @@ These are structural, so it cannot.
41
41
 
42
42
  | Guardrail you would otherwise prompt for | Now enforced by |
43
43
  |---|---|
44
- | "Work read-only and never modify anything" | **The tool surface itself.** Both tools are reads — this skill has no write tool at all, so there is nothing to withhold and nothing to switch off. |
45
- | "Diagnose only — never apply the fix you propose" | **Structural.** This skill has no tool that changes anything, and it holds no connection to vCenter, NSX or anything else. Remediation is routed to vmware-aiops or vmware-pilot by the calling agent. |
44
+ | "Work read-only and never modify anything" | **The tool surface itself.** No tool reaches a VMware system. The seven [WRITE] tools write only to this skill's own local case ledger, so there is nothing in vSphere to withhold and nothing to switch off. |
45
+ | "Diagnose only — never apply the fix you propose" | **Structural.** This skill has no tool that changes anything outside its own ledger, and it holds no connection to vCenter, NSX or anything else. Remediation is routed to vmware-aiops or vmware-pilot by the calling agent. |
46
46
  | "Do not fabricate a timeline — build it from the events I gave you" | **`incident_timeline` correlates only its input.** It is source-agnostic and has no way to fetch anything, so the timeline cannot contain an event the agent did not supply. |
47
47
  | "Tell me when the symptom is outside what you can recognise" | **`list_symptom_categories`** states the catalogue, and unmatched symptoms come back as `uncategorized` rather than being forced into the nearest signature. |
48
48
  | "Use explicit limits for queries that may return large amounts of data" | **The list envelope.** `list_symptom_categories` returns `{items, returned, limit, total, truncated, hint}` with `truncated` always `false` — which is the point: it states that the catalogue is complete instead of leaving you to infer it. |
49
- | "Log everything you looked at" | **The `@vmware_tool` decorator.** Every call is recorded to `~/.vmware/audit.db`, reads included. |
49
+ | "Log everything you looked at" | **The case ledger, for investigations.** Evidence submitted to a case is recorded with its source skill, tool, query and fetch time. Debug's own tool calls are not written to `~/.vmware/audit.db`: none of them acts on a VMware target. |
50
50
 
51
51
  ---
52
52
 
@@ -1,6 +1,9 @@
1
1
  # vmware-debug Capabilities
2
2
 
3
- Read-only, offline incident correlation. No network, no credentials, no writes.
3
+ Offline incident correlation. No network, no credentials, no writes to any VMware
4
+ system. This table covers the two stateless correlation tools; the twelve `case_*`
5
+ investigation-ledger tools (seven of which write, to the local ledger only) are
6
+ listed in `SKILL.md`, and their response sizes are not yet measured here.
4
7
 
5
8
  | Tool | What it returns | Typical response tokens |
6
9
  |---|---|---|
@@ -1,6 +1,8 @@
1
1
  # vmware-debug CLI Reference
2
2
 
3
- All commands are read-only and offline (no network, no credentials).
3
+ All commands are offline (no network, no credentials). All but `mcp` are
4
+ read-only; `mcp` starts the MCP server, whose seven `case_*` write tools record
5
+ into the local case ledger.
4
6
 
5
7
  ## triage — correlate a set of collected events
6
8
 
@@ -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
9
+ uv tool install vmware-debug==1.11.4
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", "vmware-debug-mcp"]
18
+ "args": ["--from", "vmware-debug==1.11.4", "vmware-debug-mcp"]
19
19
  }
20
20
  ```
21
21
 
@@ -33,17 +33,28 @@ routes fixes to (vmware-aiops, vmware-pilot).
33
33
 
34
34
  1. **Source Code** — https://github.com/vmware-skills/VMware-Debug (MIT).
35
35
  2. **Credentials** — none. debug holds no secrets and connects to nothing.
36
- 3. **Network** — none. All tools are local pure functions over event data the
37
- agent supplies.
38
- 4. **Writes** — none. debug only diagnoses and recommends; remediation is routed
36
+ 3. **Network** — none. The correlation tools are local pure functions over
37
+ event data the agent supplies; the case tools read and write local files.
38
+ 4. **Writes** — only to the local investigation ledger under `$OPS_HOME`
39
+ (the seven [WRITE] `case_*` tools). Evidence, gaps, hypotheses and grade
40
+ history are only ever added to; `timeline.md` is regenerated from the evidence and
41
+ `case.json` holds the current grade and state. Nothing is written to any
42
+ VMware system: debug only diagnoses and recommends; remediation is routed
39
43
  to vmware-aiops / vmware-pilot, where confirmation/approval/audit live.
44
+ Case data is sensitive: each case folder (`$OPS_HOME/cases/<case-id>/`, default
45
+ `~/.vmware/cases/`) is created owner-only (`0700`) and holds the submitted
46
+ evidence — host names, addresses, log and event text. There is no automatic
47
+ retention limit: `case_close` marks a case closed and keeps its files; delete
48
+ the folder to remove a case.
40
49
  5. **No cross-skill coupling** — events arrive as plain dicts (the event
41
50
  envelope); debug imports no other skill package at runtime.
42
51
  6. **Environment scoping** — policy rules can scope by environment, and skills
43
52
  that connect to a VMware estate may declare `environment:` (`production` /
44
53
  `staging` / `lab`) per target in their own `config.yaml` as an optional label
45
54
  an environment-scoped `deny` rule can match on. debug has no config and no
46
- connection to declare one about, so it reports a constant `local`. Since it
47
- ships no operation above read risk, nothing here is gated either way.
55
+ connection to declare one about, so it registers no environment resolver —
56
+ it has no basis to answer for any target. Its tools do not pass through the
57
+ policy engine either, so no policy rule, environment-scoped or not, applies
58
+ to them.
48
59
  7. **Static analysis** — `uvx bandit -r vmware_debug/` (release bar:
49
60
  0 Medium+).
@@ -0,0 +1,9 @@
1
+ """Test package.
2
+
3
+ ``tests/eval/`` and ``tests/eval/regression/`` were already packages while this
4
+ file was missing, so ``from tests.eval.regression._vsphere_event_vocabulary
5
+ import ...`` resolved only where the namespace-package fallback happened to
6
+ work. It did not on Windows, and the whole suite -- 398 tests -- failed at
7
+ collection. The other repos that use cross-module test imports all carry this
8
+ file; this one is the half-built package.
9
+ """
@@ -0,0 +1,42 @@
1
+ """Session-wide sandbox: the suite must not touch the operator's real files.
2
+
3
+ Installed at *import* time, not in a fixture: a fixture runs after collection
4
+ has imported every test module and, with them, the package — too late to move
5
+ a path something already resolved.
6
+
7
+ ``OPS_HOME`` moves ``vmware_policy``'s shared ``audit.db`` (and the policy,
8
+ budget and undo state beside it) and the case ledger under ``OPS_HOME/cases``. ``HOME`` moves anything resolved
9
+ from ``~``.
10
+
11
+ This repo got the sandbox on 2026-09-11, when a sibling (VMware-VDI) was found
12
+ writing test rows into the operator's real ``~/.vmware/audit.db`` because it
13
+ had none. This suite was not leaking — every case test sets ``OPS_HOME`` itself with ``monkeypatch``, so one that forgets would write into the operator's real ``~/.vmware/cases`` — but that was a property of which
14
+ tests exist today, not a protection. See
15
+ ``tests/eval/regression/test_audit_isolation.py``.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import atexit
21
+ import os
22
+ import shutil
23
+ import tempfile
24
+ from pathlib import Path
25
+
26
+ from vmware_policy.audit import reset_engine
27
+
28
+ # The operator's real home, captured before the redirect.
29
+ REAL_HOME = Path(os.path.expanduser("~"))
30
+
31
+ SANDBOX_HOME = Path(tempfile.mkdtemp(prefix="vmware-debug-tests-"))
32
+
33
+ os.environ["HOME"] = str(SANDBOX_HOME)
34
+ os.environ["OPS_HOME"] = str(SANDBOX_HOME / ".vmware")
35
+ # expanduser() consults USERPROFILE on Windows; keep every spelling pointing here.
36
+ os.environ["USERPROFILE"] = str(SANDBOX_HOME)
37
+
38
+ # The audit engine is a lazily built singleton bound to the path it first
39
+ # resolved; clear any stale binding so writes cannot go back to the real file.
40
+ reset_engine()
41
+
42
+ atexit.register(shutil.rmtree, SANDBOX_HOME, True)
@@ -132,13 +132,13 @@ class ScoreBoard:
132
132
  ),
133
133
  "scores": merged,
134
134
  }
135
- path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
135
+ path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
136
136
 
137
137
 
138
138
  def previous_scores(path: Path = SCORES_PATH) -> dict[str, Any]:
139
139
  """Load the last recorded run, or ``{}`` on a first run / unreadable file."""
140
140
  try:
141
- return json.loads(path.read_text()).get("scores", {})
141
+ return json.loads(path.read_text(encoding="utf-8")).get("scores", {})
142
142
  except (OSError, ValueError):
143
143
  return {}
144
144