chore(monitoring): record agent-lifecycle contract and disk-life proof

Follow-up to the physical-disk endurance precedence fix. Records the
dependent agent-lifecycle contract section and moves the regression into the
accepted physical-disk proof file so later per-commit canonical guards find
both the contract and the verification artifact.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-09-19 17:27:25 +01:00
parent 0910542f0a
commit 8bc6aa3c86
4 changed files with 86 additions and 68 deletions
@@ -8105,3 +8105,19 @@ install token clears the block and heals to the base identity, while a token
already bound to its own derived identity (an established distinct host sharing
a hostname or machine ID, #1753) is left untouched. Focused proof lives in
`internal/monitoring/monitor_host_agents_test.go`.
### Agent endurance evidence cannot raise a disk's remaining life
The physical-disk SMART merge treats a linked host agent's `PercentageUsed` as
more precise than the Proxmox inventory, but it must not make a disk look
healthier than Proxmox already reports. An intermittent NVMe endurance reading
of `PercentageUsed` 0 previously raised the merged remaining life to 100, which
resolved a genuine low-life disk-wearout alert and let it re-fire on the next
physical-disk poll with a fresh resolved notification every poll interval
(#2112). The merge now takes the agent value only when the Proxmox value is
unreported or more pessimistic, never raising a reported remaining-life value,
while still recording the agent's SMART attributes. This does not change agent
admission, token binding or removal-block behaviour. Focused proof lives in
`internal/monitoring/physical_disk_roundtrip_test.go`
(`TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife` and
`TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife`).
@@ -4116,7 +4116,7 @@ now takes the agent value when the Proxmox value is unreported (`-1`) or when
the agent value is lower (more pessimistic); it never raises a reported
remaining-life value. `PercentageUsed` and the other merged SMART attributes are
still recorded. Focused proof lives in
`internal/monitoring/monitor_integration_test.go`
`internal/monitoring/physical_disk_roundtrip_test.go`
(`TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife` and
`TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife`). This is
synthetic merge evidence, not reporter acceptance or hardware confirmation.
@@ -217,73 +217,6 @@ func TestMergeHostAgentSMARTIntoDisks_SMARTFailureAndWearoutOverrideCoarsePVEVal
}
}
func TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife(t *testing.T) {
// A worn drive whose NVMe endurance log intermittently reads
// PercentageUsed 0 must not have its Proxmox-reported remaining life
// raised to 100. That resolved the low-life alert and let it re-fire on the
// next poll, producing repeated notifications (#2112).
disks := []models.PhysicalDisk{{
ID: "d1",
Node: "pve1",
Serial: "SER1",
Type: "nvme",
Health: "PASSED",
Wearout: 0,
}}
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
used := 0
hosts := []models.Host{{
ID: "host-1",
Sensors: models.HostSensorSummary{
SMART: []models.HostDiskSMART{{
Device: "/dev/nvme0n1",
Serial: "SER1",
Health: "PASSED",
Attributes: &models.SMARTAttributes{
PercentageUsed: &used,
},
}},
},
}}
result := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts)
if result[0].Wearout != 0 {
t.Fatalf("agent endurance reading raised the remaining life of a worn disk: wearout=%d", result[0].Wearout)
}
if result[0].SmartAttributes == nil || result[0].SmartAttributes.PercentageUsed == nil || *result[0].SmartAttributes.PercentageUsed != 0 {
t.Fatalf("agent SMART attributes were not merged: %+v", result[0].SmartAttributes)
}
}
func TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife(t *testing.T) {
disks := []models.PhysicalDisk{{
ID: "d1",
Node: "pve1",
Serial: "SER1",
Type: "nvme",
Wearout: -1,
}}
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
used := 40
hosts := []models.Host{{
ID: "host-1",
Sensors: models.HostSensorSummary{
SMART: []models.HostDiskSMART{{
Device: "/dev/nvme0n1",
Serial: "SER1",
Attributes: &models.SMARTAttributes{
PercentageUsed: &used,
},
}},
},
}}
result := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts)
if result[0].Wearout != 60 {
t.Fatalf("agent endurance did not fill unreported PVE life: wearout=%d", result[0].Wearout)
}
}
func TestMergeHostAgentSMARTIntoDisks_DoesNotReplaceFailureWithPassed(t *testing.T) {
disks := []models.PhysicalDisk{{ID: "d1", Node: "pve1", Serial: "SER1", Health: "FAILED"}}
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
@@ -120,3 +120,72 @@ func TestPhysicalDiskReadbackSourceIDFallback(t *testing.T) {
t.Fatal("nil resource has a source ID")
}
}
// TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife pins the
// #2112 regression: a worn drive whose NVMe endurance log intermittently reads
// PercentageUsed 0 must not have its Proxmox-reported remaining life raised to
// 100, which resolved the low-life alert and let it re-fire on the next poll.
func TestMergeHostAgentSMARTIntoDisks_AgentWearoutDoesNotHideLowPVELife(t *testing.T) {
disks := []models.PhysicalDisk{{
ID: "d1",
Node: "pve1",
Serial: "SER1",
Type: "nvme",
Health: "PASSED",
Wearout: 0,
}}
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
used := 0
hosts := []models.Host{{
ID: "host-1",
Sensors: models.HostSensorSummary{
SMART: []models.HostDiskSMART{{
Device: "/dev/nvme0n1",
Serial: "SER1",
Health: "PASSED",
Attributes: &models.SMARTAttributes{
PercentageUsed: &used,
},
}},
},
}}
result := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts)
if result[0].Wearout != 0 {
t.Fatalf("agent endurance reading raised the remaining life of a worn disk: wearout=%d", result[0].Wearout)
}
if result[0].SmartAttributes == nil || result[0].SmartAttributes.PercentageUsed == nil || *result[0].SmartAttributes.PercentageUsed != 0 {
t.Fatalf("agent SMART attributes were not merged: %+v", result[0].SmartAttributes)
}
}
// TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife confirms
// the agent still supplies endurance when the Proxmox inventory reports none.
func TestMergeHostAgentSMARTIntoDisks_AgentWearoutFillsUnreportedPVELife(t *testing.T) {
disks := []models.PhysicalDisk{{
ID: "d1",
Node: "pve1",
Serial: "SER1",
Type: "nvme",
Wearout: -1,
}}
nodes := []models.Node{{Name: "pve1", LinkedAgentID: "host-1"}}
used := 40
hosts := []models.Host{{
ID: "host-1",
Sensors: models.HostSensorSummary{
SMART: []models.HostDiskSMART{{
Device: "/dev/nvme0n1",
Serial: "SER1",
Attributes: &models.SMARTAttributes{
PercentageUsed: &used,
},
}},
},
}}
result := mergeHostAgentSMARTIntoDisks(disks, nodes, hosts)
if result[0].Wearout != 60 {
t.Fatalf("agent endurance did not fill unreported PVE life: wearout=%d", result[0].Wearout)
}
}