diff --git a/docs/notes/endpoint-telemetry-and-ui-phases.md b/docs/notes/endpoint-telemetry-and-ui-phases.md index 7b290a93..81099cd4 100644 --- a/docs/notes/endpoint-telemetry-and-ui-phases.md +++ b/docs/notes/endpoint-telemetry-and-ui-phases.md @@ -136,8 +136,25 @@ inventory-snapshot plumbing and one migration. Root Dispersion in the same seconds format BEFORE Phase Offset, so a first-match parser silently reports network latency as clock skew. That bug was written and caught here; do not rewrite it. -- **Disk errors / SMART predictive failure** - collect and surface on the - existing Storage card (Phase 4). +- **Disk errors / SMART predictive failure** - DONE. Landed as its own "Disk + health" card rather than folded into the Storage card as originally planned: + Storage is per VOLUME (C:, D:) and this is per PHYSICAL device, and one drive + can back several volumes or none the operator sees, so the two do not share a + key and could not be one table. + The important finding: the classic ATA SMART class + (`MSStorageDriver_FailurePredictStatus`) returns ZERO instances on an NVMe + machine. Verified on a two-NVMe box where both drives were present and + healthy. An agent collecting only that class would have reported an empty + disk health list on every modern endpoint and read as "no problems" - the + exact silent-healthy failure mode this telemetry exists to prevent. So the + agent collects BOTH `MSFT_PhysicalDisk` (the only NVMe-aware source) and + `Win32_DiskDrive.Status` (ATA/SATA, and hosts where the Storage namespace is + unavailable), and the server does not reconcile them - same reasoning as the + Phase 1 mapped-drive merge, where two disagreeing sources was the signal. + Note `MSFT_PhysicalDisk` returns raw enum INTEGERS through + ManagementObjectSearcher, not the friendly strings `Get-PhysicalDisk` prints + (Health=0 Op=2 Media=4 Bus=17 for a healthy NVMe); the mapping lives in the + collector so the wire stays self-describing. - **NIC link speed and duplex** - collect, pair into the existing Network interfaces collection, NOT surfaced yet. - **Boot type** - fast startup vs true cold boot. Explains why uptime lies and @@ -199,8 +216,12 @@ Depends on Phase 2 data existing. - **Mapped drives card** - `Get-SmbMapping`-shaped. `Disconnected` in red, `Ok` in green. Reuse existing badge/severity styling rather than inventing new CSS. Room reserved for the future one-click fix action. -- **Storage card** - add disk error / SMART predictive failure to the existing - card rather than a new one. +- **Storage card** - superseded. Disk health shipped in Phase 2 as its own + "Disk health" card (position 18, directly under Storage) instead of being + folded in: Storage is per volume, disk health is per physical device, so they + do not share a key. Rows sort failure-first, then unknown, then OK - a failing + drive must not sit below the fold on a multi-disk box, and a drive that + stopped answering is a weaker claim of health than one that said OK. - **Windows Update card** - BLOCKED, not scheduled. Depends on Phase 2 data that is pinned pending a COM-interference investigation. Do not build the card until the collection question is settled. diff --git a/plugins/endpoints/AGENT-CONTRACT.md b/plugins/endpoints/AGENT-CONTRACT.md index 348f72f9..0f3e0b9d 100644 --- a/plugins/endpoints/AGENT-CONTRACT.md +++ b/plugins/endpoints/AGENT-CONTRACT.md @@ -68,6 +68,17 @@ arrays. `adopt` is first-contact only. "osinfo": { /* opaque object: build, version, install date */ }, "domain": "shopfloor.example.com", // domain or workgroup membership "pendingreboot": false, // omit when unknown; absent != false + "diskhealth": [ // per PHYSICAL device; see notes + { + "deviceid": "0", + "model": "Samsung SSD 990 PRO 1TB", + "mediatype": "SSD", + "bustype": "NVMe", + "healthstatus": "Healthy", + "predictfailure": false, // precomputed; null = drive did not answer + "source": "physicaldisk" // or "diskdrive" + } + ], "usercontext": { // omitted entirely when nobody is signed in "netuse": [ { @@ -133,6 +144,22 @@ Notes: genuinely empty now"; omit when you simply did not collect it this cycle. - `pendingreboot` follows that rule strictly: omit it when the check failed. A literal `false` asserts the endpoint is confirmed NOT holding a reboot. +- `diskhealth` is per PHYSICAL device and is NOT the same list as `disks`, which + is per volume. One physical drive can back several volumes or none the + operator sees, so the two do not share a key and are not merged. +- `diskhealth` carries BOTH agent sources and the server does not reconcile + them. `source: "physicaldisk"` is MSFT_PhysicalDisk, the only source that + reports NVMe at all - the classic ATA SMART class + (`MSStorageDriver_FailurePredictStatus`) returns zero instances on an NVMe + machine, so an agent collecting only that would report an empty health list on + modern hardware and look like "no problems". `source: "diskdrive"` is + Win32_DiskDrive.Status, covering ATA/SATA and hosts where the Storage + namespace is unavailable. When both answer for one drive and disagree, both + rows are shown; that disagreement is the signal. +- `predictfailure` is precomputed by the agent so an alert is a boolean test + rather than a string match against a Windows enum the server would have to + keep in sync. `null` means the drive did not answer: unknown, NOT healthy. + `healthstatus` keeps the human-readable source value alongside it. - `usercontext` is gathered by a probe the agent runs inside the signed-in user's session, because mapped drives, user-owned listeners and idle time do not exist in the agent's own LocalSystem session 0. It is omitted when no one