vmware-debug 1.9.0__tar.gz → 1.10.0__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.
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/.claude-plugin/plugin.json +1 -1
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/.mcp.json +1 -1
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/PKG-INFO +1 -1
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/RELEASE_NOTES.md +42 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/pyproject.toml +1 -1
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/server.json +2 -2
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/skills/vmware-debug/SKILL.md +48 -1
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/skills/vmware-debug/references/agent-guardrails.md +1 -1
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/_scores.json +47 -46
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/regression/test_investigation_metrics.py +103 -4
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_grading.py +29 -4
- vmware_debug-1.10.0/tests/test_knowledge.py +237 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/__init__.py +1 -1
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/mcp/tools.py +2 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/mcp_server/server.py +41 -1
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/api.py +35 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/evidence.py +8 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/grading.py +73 -0
- vmware_debug-1.10.0/vmware_debug/ops/cases/knowledge.py +375 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/.gitignore +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/README-CN.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/README.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/SECURITY.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/docs/plans/2026-08-28-investigation-layer-design.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/skills/vmware-debug/references/capabilities.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/skills/vmware-debug/references/cli-reference.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/skills/vmware-debug/references/event-envelope.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/skills/vmware-debug/references/routing.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/skills/vmware-debug/references/setup-guide.md +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/__init__.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/__init__.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/_family.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/_scoring.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/_skill.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/conftest.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/test_entity_reachability.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/test_error_actionability.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/test_tool_description_quality.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/capability/test_tool_manifest_budget.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/regression/__init__.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/regression/test_capability_grader.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/regression/test_debug_regressions.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/regression/test_result_envelope.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/regression/test_tool_annotations.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_conclusion.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_evidence.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_hypotheses.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_ids.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_plan.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_readiness.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_store.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_timeline_close.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_case_tools.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_safe_error_passthrough.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/test_timeline.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/uv.lock +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/cli.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/envelope.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/mcp/__init__.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/mcp_server/__init__.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/__init__.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/__init__.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/conclusion.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/hypotheses.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/ids.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/model.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/plan.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/readiness.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/sources.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/store.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/cases/timeline.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/ops/timeline.py +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/vmware_debug/rules/evidence_sources.yaml +0 -0
- {vmware_debug-1.9.0 → vmware_debug-1.10.0}/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": "1.
|
|
5
|
+
"version": "1.10.0",
|
|
6
6
|
"description": "Read-only VMware incident correlation: timeline, spikes, root-cause routing. 2 MCP tools.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Wei Zhou",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vmware-debug
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.0
|
|
4
4
|
Summary: VMware diagnostic brain — read-only 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
|
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
## v1.10.0 — the knowledge layer is implemented, not just described
|
|
2
|
+
|
|
3
|
+
v1.9.0 shipped the investigation layer with `applies_to` documented as the thing
|
|
4
|
+
that makes a knowledge entry decisive. Nothing parsed a knowledge file and
|
|
5
|
+
nothing checked applicability. Any file at all under the knowledge root raised
|
|
6
|
+
the reported ceiling, and anything labelled `knowledge-kb` counted as decisive.
|
|
7
|
+
The route to a wrong Confirmed that the design was written to close was open the
|
|
8
|
+
whole time, and the metric said zero only because no fixture walked it.
|
|
9
|
+
|
|
10
|
+
`case_knowledge` (the 14th tool) answers the first question anyone mounting a
|
|
11
|
+
library asks — which formats are taken — without them reading a design document:
|
|
12
|
+
|
|
13
|
+
.md .markdown YAML front-matter between --- fences, body below (preferred)
|
|
14
|
+
.yaml .yml the whole file is one entry
|
|
15
|
+
.json one entry per file
|
|
16
|
+
.jsonl one JSON object per line (what ticketing systems export)
|
|
17
|
+
.csv .tsv one entry per row; dotted columns for nested constraints
|
|
18
|
+
.txt .log a sibling <name>.yaml carries the metadata
|
|
19
|
+
|
|
20
|
+
PDF, DOCX, PPTX and HTML are named as needing conversion rather than ignored,
|
|
21
|
+
and a file that cannot be parsed is reported rather than skipped.
|
|
22
|
+
|
|
23
|
+
**An entry is decisive only if its own `applies_to` was checked against the
|
|
24
|
+
case's recorded versions and passed.** Three things follow, each found by
|
|
25
|
+
reviewing the version before it:
|
|
26
|
+
|
|
27
|
+
* Knowledge evidence must say WHICH entry it is. "Some applicable entry is
|
|
28
|
+
mounted somewhere" is a different claim from "this one applies", and the first
|
|
29
|
+
version accepted it — an agent citing the entry written for 9.x could confirm
|
|
30
|
+
a case about 8.0.3 as long as an unrelated entry happened to match.
|
|
31
|
+
* A constraint the case scope cannot answer is not a match. Silence is not a
|
|
32
|
+
pass; reading it as one is how an entry for different hardware ends a case.
|
|
33
|
+
* **A constraint the checker cannot evaluate is not a pass either.** The first
|
|
34
|
+
version understood product, build, driver and firmware and treated every other
|
|
35
|
+
key as satisfied, so `build` without a `product`, a `hardware_model` list, and
|
|
36
|
+
an outright typo all came back decisive. Unknown and satisfied must never be
|
|
37
|
+
the same answer.
|
|
38
|
+
|
|
39
|
+
Given two entries that read identically — one for `>=8.0, <9.0`, one for
|
|
40
|
+
`>=9.0` — a case scoped to vSphere 8.0.3 now reaches Confirmed citing the first
|
|
41
|
+
and stays at Probable citing the second, naming the range that excluded it.
|
|
42
|
+
|
|
1
43
|
## v1.9.0 — the investigation layer: 2 tools become 13
|
|
2
44
|
|
|
3
45
|
Correlating events answers "what happened together". A case answers "what do we
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vmware-debug"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.10.0"
|
|
8
8
|
description = "VMware diagnostic brain — read-only incident triage, log/event correlation, and root-cause routing across the VMware skill family"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/vmware-skills/VMware-Debug",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.
|
|
10
|
+
"version": "1.10.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "pypi",
|
|
14
14
|
"identifier": "vmware-debug",
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.10.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
}
|
|
@@ -100,7 +100,7 @@ a recommended plan.
|
|
|
100
100
|
- **MCP** (in an agent): the agent calls the other skills' read tools, then `incident_timeline` to correlate. This is the primary mode — that's where the cross-skill "联动" happens.
|
|
101
101
|
- **CLI** (humans): `vmware-debug triage --events events.json` correlates a JSON array you collected yourself.
|
|
102
102
|
|
|
103
|
-
## MCP Tools (
|
|
103
|
+
## MCP Tools (14 — 7 read, 7 write)
|
|
104
104
|
|
|
105
105
|
**Correlation** — stateless, for a single look:
|
|
106
106
|
|
|
@@ -115,6 +115,7 @@ a recommended plan.
|
|
|
115
115
|
|---|---|
|
|
116
116
|
| `case_open` | [WRITE] Define the event; returns a case id and the grade this environment can reach |
|
|
117
117
|
| `case_readiness` | [READ] What grade this environment can reach, per symptom category, **before** you start |
|
|
118
|
+
| `case_knowledge` | [READ] Which knowledge formats are accepted, what is mounted, and which entries apply to a case |
|
|
118
119
|
| `case_plan` | [READ] What to fetch next — skill, tool and purpose per step; recomputed from the case's current state |
|
|
119
120
|
| `case_list` | [READ] Cases, newest first |
|
|
120
121
|
| `case_get` | [READ] One case: scope, ledger sizes, grade history |
|
|
@@ -179,6 +180,52 @@ so the gap is visible now rather than when the conclusion refuses to firm up.
|
|
|
179
180
|
"readiness 78%" cannot. Two classes served by the *same* skill count as one
|
|
180
181
|
source, so it agrees with what `case_grade` will actually award.
|
|
181
182
|
|
|
183
|
+
### Mounting a knowledge library
|
|
184
|
+
|
|
185
|
+
`case_knowledge` answers "what can I add" without anyone reading this file.
|
|
186
|
+
Entries go under `$OPS_HOME/knowledge/{kb,runbook,sr,cases}/`:
|
|
187
|
+
|
|
188
|
+
| Format | How metadata travels |
|
|
189
|
+
|---|---|
|
|
190
|
+
| `.md` `.markdown` | YAML front-matter between `---` fences, body below — **preferred** |
|
|
191
|
+
| `.yaml` `.yml` | the whole file is one entry |
|
|
192
|
+
| `.json` | one entry per file |
|
|
193
|
+
| `.jsonl` | one JSON object per line — what ticketing systems export |
|
|
194
|
+
| `.csv` `.tsv` | one entry per row; use dotted columns (`driver.version`) for nested constraints |
|
|
195
|
+
| `.txt` `.log` | a sibling `<name>.yaml` carries the metadata |
|
|
196
|
+
|
|
197
|
+
PDF, DOCX, PPTX and HTML must be converted to Markdown first — `case_knowledge`
|
|
198
|
+
names them rather than ignoring them.
|
|
199
|
+
|
|
200
|
+
**Every entry needs an `applies_to` block to be decisive:**
|
|
201
|
+
|
|
202
|
+
```yaml
|
|
203
|
+
---
|
|
204
|
+
id: KB-2026-0417
|
|
205
|
+
applies_to:
|
|
206
|
+
product: vsphere
|
|
207
|
+
build: ">=8.0.3, <9.0"
|
|
208
|
+
driver: {name: nvme_pcie, version: ">=1.2.4"}
|
|
209
|
+
firmware: {vendor: dell, version: ">=52.26"}
|
|
210
|
+
---
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`product`, `build`, `driver` and `firmware` are the constraints the checker
|
|
214
|
+
evaluates. **Any other key leaves the entry non-decisive** — including a typo —
|
|
215
|
+
because an unverified constraint is not a satisfied one, and `case_knowledge`
|
|
216
|
+
names the key it could not check.
|
|
217
|
+
|
|
218
|
+
Knowledge evidence must say **which** entry it is
|
|
219
|
+
(`case_submit_evidence(source_skill="knowledge-kb", knowledge_entry_id="KB-…")`)
|
|
220
|
+
— "some applicable entry is mounted somewhere" is a different claim from "this
|
|
221
|
+
one applies", and only the second can carry a conclusion.
|
|
222
|
+
|
|
223
|
+
Matching is **by version applicability, never by similarity** — an entry written
|
|
224
|
+
for the wrong build reads exactly like the right one, and similarity is the only
|
|
225
|
+
thing that would let it through. An entry with no `applies_to` can support a
|
|
226
|
+
hypothesis but can never make a case Confirmed. A constraint the case scope
|
|
227
|
+
cannot answer is not a match either: silence is not a pass.
|
|
228
|
+
|
|
182
229
|
> **On a stock install the ceiling is Probable.** Confirmed needs a decisive
|
|
183
230
|
> source, and there is neither a hardware-diagnostic channel (no Redfish/BMC, no
|
|
184
231
|
> SMART/NVMe) nor a knowledge library mounted — `~/.vmware/knowledge/` ships
|
{vmware_debug-1.9.0 → vmware_debug-1.10.0}/skills/vmware-debug/references/agent-guardrails.md
RENAMED
|
@@ -14,7 +14,7 @@ vmware-monitor and vmware-aria against a production vSphere estate with Llama
|
|
|
14
14
|
cross-skill rules are identical across this family; the parts below marked
|
|
15
15
|
vmware-debug are specific to this skill.
|
|
16
16
|
|
|
17
|
-
vmware-debug exposes
|
|
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
19
|
skills, normalises them, and hands them over. The four writes go to the local
|
|
20
20
|
investigation ledger under `~/.vmware/cases/`, never to a VMware system. That
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
"list_symptom_categories",
|
|
10
10
|
"case_open",
|
|
11
11
|
"case_list",
|
|
12
|
-
"case_readiness"
|
|
12
|
+
"case_readiness",
|
|
13
|
+
"case_knowledge"
|
|
13
14
|
],
|
|
14
15
|
"reachable_on_surface": 8,
|
|
15
16
|
"reachable_only_via_description": 0,
|
|
16
17
|
"required_entity_params": 8,
|
|
17
18
|
"required_params_classified": "8/17",
|
|
18
|
-
"tool_count":
|
|
19
|
+
"tool_count": 14
|
|
19
20
|
},
|
|
20
21
|
"maximum": 8,
|
|
21
22
|
"pct": 100.0,
|
|
@@ -43,12 +44,12 @@
|
|
|
43
44
|
},
|
|
44
45
|
"entry_point_availability": {
|
|
45
46
|
"detail": {
|
|
46
|
-
"full_entry_points":
|
|
47
|
+
"full_entry_points": 6
|
|
47
48
|
},
|
|
48
|
-
"maximum":
|
|
49
|
-
"pct":
|
|
49
|
+
"maximum": 14,
|
|
50
|
+
"pct": 42.9,
|
|
50
51
|
"unit": "entry points",
|
|
51
|
-
"value":
|
|
52
|
+
"value": 6
|
|
52
53
|
},
|
|
53
54
|
"error_actionability": {
|
|
54
55
|
"detail": {
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
"names_artifact",
|
|
78
79
|
"states_remedy"
|
|
79
80
|
],
|
|
80
|
-
"where": "evidence.py:
|
|
81
|
+
"where": "evidence.py:289"
|
|
81
82
|
},
|
|
82
83
|
{
|
|
83
84
|
"message": "gaps.json in case {} is not valid JSON: {}. It was hand-edited. Restore it rather than deleting it \u2014",
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
"names_artifact",
|
|
86
87
|
"states_remedy"
|
|
87
88
|
],
|
|
88
|
-
"where": "evidence.py:
|
|
89
|
+
"where": "evidence.py:291"
|
|
89
90
|
},
|
|
90
91
|
{
|
|
91
92
|
"message": "Cannot read grading_rules.yaml at {}: {}",
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
"names_artifact",
|
|
94
95
|
"states_remedy"
|
|
95
96
|
],
|
|
96
|
-
"where": "grading.py:
|
|
97
|
+
"where": "grading.py:56"
|
|
97
98
|
},
|
|
98
99
|
{
|
|
99
100
|
"message": "grading_rules.yaml at {} is not valid YAML: {}. Fix it rather than deleting it \u2014 falling back to the",
|
|
@@ -101,7 +102,7 @@
|
|
|
101
102
|
"names_artifact",
|
|
102
103
|
"states_remedy"
|
|
103
104
|
],
|
|
104
|
-
"where": "grading.py:
|
|
105
|
+
"where": "grading.py:58"
|
|
105
106
|
},
|
|
106
107
|
{
|
|
107
108
|
"message": "A hypothesis needs a statement \u2014 what you think happened, in one line. An unnamed hypothesis cannot ",
|
|
@@ -195,18 +196,18 @@
|
|
|
195
196
|
"case_open": 354,
|
|
196
197
|
"case_plan": 360,
|
|
197
198
|
"case_record_gap": 337,
|
|
198
|
-
"case_submit_evidence":
|
|
199
|
+
"case_submit_evidence": 621,
|
|
199
200
|
"incident_timeline": 329
|
|
200
201
|
},
|
|
201
|
-
"manifest_tokens":
|
|
202
|
-
"mean_tokens_per_tool":
|
|
202
|
+
"manifest_tokens": 3777,
|
|
203
|
+
"mean_tokens_per_tool": 269.8,
|
|
203
204
|
"reference_context": 16384,
|
|
204
|
-
"tool_count":
|
|
205
|
+
"tool_count": 14
|
|
205
206
|
},
|
|
206
207
|
"maximum": 16384,
|
|
207
|
-
"pct":
|
|
208
|
+
"pct": 76.9,
|
|
208
209
|
"unit": "tokens",
|
|
209
|
-
"value":
|
|
210
|
+
"value": 12607
|
|
210
211
|
},
|
|
211
212
|
"parameter_documentation_coverage": {
|
|
212
213
|
"detail": {
|
|
@@ -226,10 +227,10 @@
|
|
|
226
227
|
]
|
|
227
228
|
}
|
|
228
229
|
},
|
|
229
|
-
"maximum":
|
|
230
|
-
"pct":
|
|
230
|
+
"maximum": 45,
|
|
231
|
+
"pct": 84.4,
|
|
231
232
|
"unit": "parameters",
|
|
232
|
-
"value":
|
|
233
|
+
"value": 38
|
|
233
234
|
},
|
|
234
235
|
"per_tool_token_discipline": {
|
|
235
236
|
"detail": {
|
|
@@ -237,13 +238,13 @@
|
|
|
237
238
|
"over_ceiling": {
|
|
238
239
|
"case_open": 354,
|
|
239
240
|
"case_plan": 360,
|
|
240
|
-
"case_submit_evidence":
|
|
241
|
+
"case_submit_evidence": 621
|
|
241
242
|
}
|
|
242
243
|
},
|
|
243
|
-
"maximum":
|
|
244
|
-
"pct":
|
|
244
|
+
"maximum": 14,
|
|
245
|
+
"pct": 78.6,
|
|
245
246
|
"unit": "tools",
|
|
246
|
-
"value":
|
|
247
|
+
"value": 11
|
|
247
248
|
},
|
|
248
249
|
"read_only_manifest_saving": {
|
|
249
250
|
"detail": {
|
|
@@ -263,10 +264,10 @@
|
|
|
263
264
|
"detail": {
|
|
264
265
|
"unmarked": []
|
|
265
266
|
},
|
|
266
|
-
"maximum":
|
|
267
|
+
"maximum": 14,
|
|
267
268
|
"pct": 100.0,
|
|
268
269
|
"unit": "points",
|
|
269
|
-
"value":
|
|
270
|
+
"value": 14
|
|
270
271
|
},
|
|
271
272
|
"remedy_survives_truncation": {
|
|
272
273
|
"detail": {
|
|
@@ -277,10 +278,10 @@
|
|
|
277
278
|
"envelope.py:138",
|
|
278
279
|
"conclusion.py:135",
|
|
279
280
|
"conclusion.py:144",
|
|
280
|
-
"evidence.py:
|
|
281
|
-
"evidence.py:
|
|
282
|
-
"evidence.py:
|
|
283
|
-
"grading.py:
|
|
281
|
+
"evidence.py:179",
|
|
282
|
+
"evidence.py:211",
|
|
283
|
+
"evidence.py:248",
|
|
284
|
+
"grading.py:66",
|
|
284
285
|
"hypotheses.py:47",
|
|
285
286
|
"hypotheses.py:70"
|
|
286
287
|
]
|
|
@@ -300,14 +301,14 @@
|
|
|
300
301
|
"tool_description_quality": {
|
|
301
302
|
"detail": {
|
|
302
303
|
"per_dimension_pct": {
|
|
303
|
-
"args":
|
|
304
|
-
"gotcha":
|
|
304
|
+
"args": 78.6,
|
|
305
|
+
"gotcha": 85.7,
|
|
305
306
|
"marker": 100.0,
|
|
306
|
-
"next_hop":
|
|
307
|
+
"next_hop": 57.1,
|
|
307
308
|
"what": 100.0,
|
|
308
|
-
"when": 92.
|
|
309
|
+
"when": 92.9
|
|
309
310
|
},
|
|
310
|
-
"tools_graded":
|
|
311
|
+
"tools_graded": 14,
|
|
311
312
|
"weakest_tools": {
|
|
312
313
|
"case_close": [
|
|
313
314
|
"next_hop"
|
|
@@ -319,6 +320,9 @@
|
|
|
319
320
|
"args",
|
|
320
321
|
"next_hop"
|
|
321
322
|
],
|
|
323
|
+
"case_knowledge": [
|
|
324
|
+
"next_hop"
|
|
325
|
+
],
|
|
322
326
|
"case_list": [
|
|
323
327
|
"gotcha",
|
|
324
328
|
"next_hop"
|
|
@@ -332,29 +336,26 @@
|
|
|
332
336
|
],
|
|
333
337
|
"case_submit_evidence": [
|
|
334
338
|
"args"
|
|
335
|
-
],
|
|
336
|
-
"case_timeline": [
|
|
337
|
-
"args"
|
|
338
339
|
]
|
|
339
340
|
}
|
|
340
341
|
},
|
|
341
|
-
"maximum":
|
|
342
|
-
"pct": 85.
|
|
342
|
+
"maximum": 84,
|
|
343
|
+
"pct": 85.7,
|
|
343
344
|
"unit": "points",
|
|
344
|
-
"value":
|
|
345
|
+
"value": 72
|
|
345
346
|
},
|
|
346
347
|
"tool_failure_payload_quality": {
|
|
347
348
|
"detail": {
|
|
348
349
|
"bare_string_error_lines": [],
|
|
349
|
-
"carries_hint":
|
|
350
|
-
"dict_shaped":
|
|
351
|
-
"error_return_sites":
|
|
352
|
-
"hint_names_artifact":
|
|
350
|
+
"carries_hint": 13,
|
|
351
|
+
"dict_shaped": 13,
|
|
352
|
+
"error_return_sites": 13,
|
|
353
|
+
"hint_names_artifact": 13
|
|
353
354
|
},
|
|
354
|
-
"maximum":
|
|
355
|
+
"maximum": 39,
|
|
355
356
|
"pct": 100.0,
|
|
356
357
|
"unit": "checks",
|
|
357
|
-
"value":
|
|
358
|
+
"value": 39
|
|
358
359
|
}
|
|
359
360
|
}
|
|
360
361
|
}
|
{vmware_debug-1.9.0 → vmware_debug-1.10.0}/tests/eval/regression/test_investigation_metrics.py
RENAMED
|
@@ -114,12 +114,17 @@ def test_wrong_confirmed_rate_is_zero(label, sources, gaps, _unused):
|
|
|
114
114
|
assert grade != "confirmed", f"{label!r} reached Confirmed — grade was {grade}"
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
def test_the_only_route_to_confirmed_is_the_intended_one():
|
|
117
|
+
def test_the_only_route_to_confirmed_is_the_intended_one(tmp_path):
|
|
118
118
|
"""The counterpart. A metric that can only fail is not measuring anything —
|
|
119
|
-
if nothing can reach Confirmed, 'wrong-Confirmed rate is zero' is vacuous.
|
|
120
|
-
|
|
119
|
+
if nothing can reach Confirmed, 'wrong-Confirmed rate is zero' is vacuous.
|
|
120
|
+
|
|
121
|
+
Reaching it now takes a mounted entry whose applies_to matches, which is
|
|
122
|
+
exactly the bar the design sets.
|
|
123
|
+
"""
|
|
124
|
+
_kb(tmp_path, "KB-ok.md", "applies_to:\n product: vsphere\n")
|
|
125
|
+
cid = _scoped_case({"vsphere": "8.0.3"})
|
|
121
126
|
_ev(cid, "vmware-monitor")
|
|
122
|
-
_ev(cid, "knowledge-sr")
|
|
127
|
+
_ev(cid, "knowledge-sr", knowledge_entry_id="KB-ok.md")
|
|
123
128
|
assert grade_case(cid).grade == "confirmed"
|
|
124
129
|
|
|
125
130
|
|
|
@@ -215,3 +220,97 @@ def test_every_unreachable_source_carries_a_remedy():
|
|
|
215
220
|
cid = _case()
|
|
216
221
|
for u in plan_next(cid, category=category)["unavailable"]:
|
|
217
222
|
assert u["how_to_supply"], f"{category}: {u['evidence_class']} has no remedy"
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# ── Metric 1, continued: the route the design says must not exist ──────────
|
|
226
|
+
#
|
|
227
|
+
# Added after the knowledge layer was implemented. Until then `applies_to` was
|
|
228
|
+
# prose: any file at all raised the ceiling and anything labelled `knowledge-kb`
|
|
229
|
+
# counted as decisive. The wrong-Confirmed metric passed only because no fixture
|
|
230
|
+
# exercised the one route a wrong Confirmed actually takes.
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _kb(tmp_path, name, applies_block):
|
|
234
|
+
p = tmp_path / "vmware" / "knowledge" / "kb" / name
|
|
235
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
236
|
+
p.write_text(f"---\nid: {name}\n{applies_block}---\nbody\n")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _scoped_case(versions):
|
|
240
|
+
cid = create_case(
|
|
241
|
+
Scope(
|
|
242
|
+
summary=f"scoped {next(_SEQ)}",
|
|
243
|
+
determined_by="test",
|
|
244
|
+
product_versions=versions,
|
|
245
|
+
),
|
|
246
|
+
at=AT,
|
|
247
|
+
).case_id
|
|
248
|
+
add_hypothesis(cid, "failing device")
|
|
249
|
+
return cid
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def test_a_knowledge_entry_for_the_wrong_build_cannot_confirm(tmp_path):
|
|
253
|
+
"""The canonical wrong Confirmed. The entry is real, well-formed, and about
|
|
254
|
+
the right product — and it is for 9.x while this estate runs 8.0.3."""
|
|
255
|
+
_kb(tmp_path, "KB-1.md", "applies_to:\n product: vsphere\n build: '>=9.0'\n")
|
|
256
|
+
cid = _scoped_case({"vsphere": "8.0.3"})
|
|
257
|
+
_ev(cid, "vmware-monitor")
|
|
258
|
+
_ev(cid, "knowledge-kb")
|
|
259
|
+
assert grade_case(cid).grade != "confirmed"
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_a_knowledge_entry_with_no_applies_to_cannot_confirm(tmp_path):
|
|
263
|
+
_kb(tmp_path, "KB-2.md", "")
|
|
264
|
+
cid = _scoped_case({"vsphere": "8.0.3"})
|
|
265
|
+
_ev(cid, "vmware-monitor")
|
|
266
|
+
_ev(cid, "knowledge-kb")
|
|
267
|
+
assert grade_case(cid).grade != "confirmed"
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def test_a_matching_entry_does_confirm(tmp_path):
|
|
271
|
+
"""The counterpart, so the rule above is a filter and not a wall."""
|
|
272
|
+
_kb(tmp_path, "KB-3.md", "applies_to:\n product: vsphere\n build: '>=8.0, <9.0'\n")
|
|
273
|
+
cid = _scoped_case({"vsphere": "8.0.3"})
|
|
274
|
+
_ev(cid, "vmware-monitor")
|
|
275
|
+
_ev(cid, "knowledge-kb", knowledge_entry_id="KB-3.md")
|
|
276
|
+
assert grade_case(cid).grade == "confirmed"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def test_citing_the_wrong_entry_does_not_borrow_a_matching_one(tmp_path):
|
|
280
|
+
"""Caught reviewing the applicability filter.
|
|
281
|
+
|
|
282
|
+
The first version passed when ANY mounted entry applied, so an agent citing
|
|
283
|
+
the entry written for 9.x could confirm a case about 8.0.3 as long as some
|
|
284
|
+
unrelated entry happened to match. "An entry is decisive only if ITS
|
|
285
|
+
applies_to passed" was true of the docstring and not of the code.
|
|
286
|
+
|
|
287
|
+
Knowledge evidence now has to say which entry it is, and that entry is the
|
|
288
|
+
one checked.
|
|
289
|
+
"""
|
|
290
|
+
_kb(tmp_path, "KB-right.md", "applies_to:\n product: vsphere\n build: '>=8.0, <9.0'\n")
|
|
291
|
+
_kb(tmp_path, "KB-wrong.md", "applies_to:\n product: vsphere\n build: '>=9.0'\n")
|
|
292
|
+
cid = _scoped_case({"vsphere": "8.0.3"})
|
|
293
|
+
_ev(cid, "vmware-monitor")
|
|
294
|
+
_ev(cid, "knowledge-kb", knowledge_entry_id="KB-wrong.md")
|
|
295
|
+
assert grade_case(cid).grade != "confirmed"
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def test_citing_the_right_entry_confirms(tmp_path):
|
|
299
|
+
_kb(tmp_path, "KB-right.md", "applies_to:\n product: vsphere\n build: '>=8.0, <9.0'\n")
|
|
300
|
+
_kb(tmp_path, "KB-wrong.md", "applies_to:\n product: vsphere\n build: '>=9.0'\n")
|
|
301
|
+
cid = _scoped_case({"vsphere": "8.0.3"})
|
|
302
|
+
_ev(cid, "vmware-monitor")
|
|
303
|
+
_ev(cid, "knowledge-kb", knowledge_entry_id="KB-right.md")
|
|
304
|
+
assert grade_case(cid).grade == "confirmed"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_knowledge_evidence_that_names_no_entry_is_not_decisive(tmp_path):
|
|
308
|
+
"""Without the id there is nothing to check, and 'something applicable
|
|
309
|
+
exists somewhere' is not the same claim."""
|
|
310
|
+
_kb(tmp_path, "KB-right.md", "applies_to:\n product: vsphere\n---\n")
|
|
311
|
+
cid = _scoped_case({"vsphere": "8.0.3"})
|
|
312
|
+
_ev(cid, "vmware-monitor")
|
|
313
|
+
_ev(cid, "knowledge-kb")
|
|
314
|
+
r = grade_case(cid)
|
|
315
|
+
assert r.grade != "confirmed"
|
|
316
|
+
assert any("which entry" in x for x in r.reasons), r.reasons
|
|
@@ -31,6 +31,21 @@ def isolated_home(tmp_path, monkeypatch):
|
|
|
31
31
|
monkeypatch.setenv("OPS_HOME", str(tmp_path / "vmware"))
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
@pytest.fixture
|
|
35
|
+
def mounted_kb(tmp_path):
|
|
36
|
+
"""A knowledge entry that genuinely applies to the case fixture's scope.
|
|
37
|
+
|
|
38
|
+
Submitting evidence labelled `knowledge-sr` used to be enough to confirm a
|
|
39
|
+
case; the label did all the work. Now the entry has to exist and its
|
|
40
|
+
applies_to has to match, so a test that wants Confirmed has to construct one
|
|
41
|
+
— which is the point.
|
|
42
|
+
"""
|
|
43
|
+
p = tmp_path / "vmware" / "knowledge" / "kb" / "KB-fixture.md"
|
|
44
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
p.write_text("---\nid: KB-fixture\napplies_to:\n product: vsphere\n---\nbody\n")
|
|
46
|
+
return p
|
|
47
|
+
|
|
48
|
+
|
|
34
49
|
@pytest.fixture
|
|
35
50
|
def case():
|
|
36
51
|
"""A case with H1 already registered.
|
|
@@ -39,12 +54,22 @@ def case():
|
|
|
39
54
|
unregistered hypothesis is now refused — a dangling id used to block and
|
|
40
55
|
falsify nothing while silently costing a grade level.
|
|
41
56
|
"""
|
|
42
|
-
cid = create_case(
|
|
57
|
+
cid = create_case(
|
|
58
|
+
Scope(
|
|
59
|
+
summary="vsan latency", determined_by="alarm 42",
|
|
60
|
+
product_versions={"vsphere": "8.0.3"},
|
|
61
|
+
),
|
|
62
|
+
at=AT,
|
|
63
|
+
).case_id
|
|
43
64
|
add_hypothesis(cid, "failing device")
|
|
44
65
|
return cid
|
|
45
66
|
|
|
46
67
|
|
|
47
68
|
def evidence(skill="vmware-monitor", tool="list_events", **kw):
|
|
69
|
+
# Knowledge evidence must say which entry it is; without that its
|
|
70
|
+
# applicability cannot be checked and it is not decisive.
|
|
71
|
+
if skill in ("knowledge-kb", "knowledge-sr") and "knowledge_entry_id" not in kw:
|
|
72
|
+
kw["knowledge_entry_id"] = "KB-fixture"
|
|
48
73
|
return Evidence(
|
|
49
74
|
source_skill=skill,
|
|
50
75
|
source_tool=tool,
|
|
@@ -131,7 +156,7 @@ class TestConfirmedNeedsADecisiveSource:
|
|
|
131
156
|
record_evidence(case, evidence(skill=skill))
|
|
132
157
|
assert grade_case(case).grade == "probable"
|
|
133
158
|
|
|
134
|
-
def test_a_decisive_source_reaches_confirmed(self, case):
|
|
159
|
+
def test_a_decisive_source_reaches_confirmed(self, case, mounted_kb):
|
|
135
160
|
record_evidence(case, evidence(skill="vmware-monitor"))
|
|
136
161
|
record_evidence(case, evidence(skill="knowledge-sr"))
|
|
137
162
|
assert grade_case(case).grade == "confirmed"
|
|
@@ -178,7 +203,7 @@ class TestExclusionNeedsAFalsifyingObservation:
|
|
|
178
203
|
|
|
179
204
|
|
|
180
205
|
class TestDemotion:
|
|
181
|
-
def test_a_new_gap_takes_confirmed_back_down_to_probable(self, case):
|
|
206
|
+
def test_a_new_gap_takes_confirmed_back_down_to_probable(self, case, mounted_kb):
|
|
182
207
|
record_evidence(case, evidence(skill="vmware-monitor"))
|
|
183
208
|
record_evidence(case, evidence(skill="knowledge-sr"))
|
|
184
209
|
assert grade_case(case).grade == "confirmed"
|
|
@@ -265,7 +290,7 @@ class TestTheRulesFileHasNoDecorativeKnobs:
|
|
|
265
290
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
266
291
|
p.write_text(body)
|
|
267
292
|
|
|
268
|
-
def test_relaxing_the_prerequisite_actually_relaxes_it(self, case, tmp_path):
|
|
293
|
+
def test_relaxing_the_prerequisite_actually_relaxes_it(self, case, tmp_path, mounted_kb):
|
|
269
294
|
"""One decisive source, no corroboration: Confirmed under the relaxed
|
|
270
295
|
rule, and not under the packaged one."""
|
|
271
296
|
record_evidence(case, evidence(skill="knowledge-sr"))
|