diff --git a/.claude/scripts/onboarding-diagnostic.ps1 b/.claude/scripts/onboarding-diagnostic.ps1 index eea7c23..4c0069d 100644 --- a/.claude/scripts/onboarding-diagnostic.ps1 +++ b/.claude/scripts/onboarding-diagnostic.ps1 @@ -669,9 +669,14 @@ Invoke-Check -Id 'sec.patch' -Category 'security' -Title 'Patch posture and OS s $caption = $script:OsInfo.caption Set-Fact 'os_build' $build - # EOL map keyed by build number (consumer/Pro mainstream support cutoffs). - # Dates are end-of-servicing for that build. Kept small and explicit. - $eolMap = @{ + # EOL map keyed by build number (end-of-servicing for that build). + # IMPORTANT: client and server SKUs SHARE build numbers (17763 = Win10 1809 AND + # Server 2019; 14393 = Win10 1607 AND Server 2016; 26100 = Win11 24H2 AND Server + # 2025) but have very different lifecycles. Keying a Server build against the client + # map produces a FALSE end-of-life critical (e.g. Server 2019 flagged EOL-2020). So + # branch on SKU first. + $isServer = ($caption -match 'Server') + $eolMapClient = @{ # Windows 10 builds '10240' = @{ name='Win10 1507'; eol='2017-05-09' } '10586' = @{ name='Win10 1511'; eol='2017-10-10' } @@ -694,6 +699,17 @@ Invoke-Check -Id 'sec.patch' -Category 'security' -Title 'Patch posture and OS s '26100' = @{ name='Win11 24H2'; eol='2026-10-13' } '26200' = @{ name='Win11 25H2'; eol='2027-10-12' } } + # Windows Server builds (extended-support end dates). + $eolMapServer = @{ + '9200' = @{ name='Server 2012'; eol='2023-10-10' } + '9600' = @{ name='Server 2012 R2'; eol='2023-10-10' } + '14393' = @{ name='Server 2016'; eol='2027-01-12' } + '17763' = @{ name='Server 2019'; eol='2029-01-09' } + '20348' = @{ name='Server 2022'; eol='2031-10-14' } + '25398' = @{ name='Server 23H2'; eol='2025-10-24' } + '26100' = @{ name='Server 2025'; eol='2034-10-10' } + } + $eolMap = if ($isServer) { $eolMapServer } else { $eolMapClient } $now = (Get-Date).ToUniversalTime() $eolEntry = $null @@ -966,8 +982,16 @@ Invoke-Check -Id 'health.stability' -Category 'health' -Title 'Stability events $unexpected = Count-Events -LogName 'System' -Ids @(41) $bugcheck = Count-Events -LogName 'System' -Ids @(1001) -Source 'Microsoft-Windows-WER-SystemErrorReporting' if ($bugcheck -eq 0) { $bugcheck = Count-Events -LogName 'System' -Ids @(1001) -Source 'BugCheck' } - $diskErr = Count-Events -LogName 'System' -Ids @(7,51,153) -Source 'disk' - if ($diskErr -eq 0) { $diskErr = Count-Events -LogName 'System' -Ids @(7,51,153) } + # ids 7/51/153 are SHARED across providers: provider 'disk' (and other storage drivers) + # = a real I/O error, but 'Microsoft-Windows-Kernel-Boot' id 153 = "VBS disabled" boot + # noise — NOT a disk error. The old unfiltered fallback counted that noise as disk errors + # (false-positive stability warning on healthy servers). Count only true storage providers. + $diskProviders = @('disk','Disk','storahci','stornvme','iaStorA','iaStorAC','EhStorClass','Microsoft-Windows-Disk','volmgr','Ntfs') + $diskErr = 0 + try { + $de = Get-WinEvent -FilterHashtable @{ LogName='System'; StartTime=$start; Id=@(7,51,153) } -ErrorAction Stop + $diskErr = @($de | Where-Object { $diskProviders -contains $_.ProviderName }).Count + } catch { $diskErr = 0 } Set-Fact 'stability_14d' @{ unexpected_shutdowns = $unexpected