From bfa6f31be202d1fd77eec242c4edcf48e73552ad Mon Sep 17 00:00:00 2001 From: 0x0Bug <98243573+0x0bug@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:03:09 +0200 Subject: [PATCH 1/8] feat: add event identifiers and error code details --- modules/events/diagnostic.ps1 | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/modules/events/diagnostic.ps1 b/modules/events/diagnostic.ps1 index 2179266..d576680 100644 --- a/modules/events/diagnostic.ps1 +++ b/modules/events/diagnostic.ps1 @@ -38,6 +38,55 @@ function ConvertTo-OneLineMessage { return ($singleLine.Substring(0, $MaxLength - 3) + '...') } + +function Get-EventErrorCodes { + param([AllowEmptyString()][string]$Message) + + if ([string]::IsNullOrWhiteSpace($Message)) { + return @() + } + + $seen = @{} + $codes = New-Object System.Collections.Generic.List[string] + foreach ($match in [System.Text.RegularExpressions.Regex]::Matches($Message, '(?i)(? Date: Fri, 18 Sep 2026 08:03:13 +0200 Subject: [PATCH 2/8] test: cover event error identifiers and designations --- tests/diagnostic-classification.tests.ps1 | 24 +++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/diagnostic-classification.tests.ps1 b/tests/diagnostic-classification.tests.ps1 index c7b9026..450543c 100644 --- a/tests/diagnostic-classification.tests.ps1 +++ b/tests/diagnostic-classification.tests.ps1 @@ -21,7 +21,7 @@ $now = Get-Date # Event Log fixtures: severity alone is context; exact rules, grouping, cutoff, # provider identity, and partial source availability are deterministic. $eventsScript = Join-Path $repositoryRoot 'modules\events\diagnostic.ps1' -Import-TestFunctions $eventsScript @('ConvertTo-OneLineMessage','Get-EventSignalRule','Group-EventLogEvents','Read-EventLog') +Import-TestFunctions $eventsScript @('ConvertTo-OneLineMessage','Get-EventErrorCodes','Get-EventDesignation','Get-EventSignalRule','Group-EventLogEvents','Read-EventLog') Assert-True ($null -eq (Get-EventSignalRule 'Application' 'Fixture-Provider' 1000 2)) 'A generic Error event must remain context.' Assert-True ($null -eq (Get-EventSignalRule 'System' 'Microsoft-Windows-DistributedCOM' 10016 2)) 'Expected DCOM 10016 noise must remain context.' Assert-True ($null -eq (Get-EventSignalRule 'System' 'Fixture-Kernel-Power' 41 1)) 'Event ID 41 from another provider must not match.' @@ -29,11 +29,20 @@ Assert-True ($null -eq (Get-EventSignalRule 'System' 'Microsoft-Windows-Kernel-P Assert-Equal 'EVENT_UNEXPECTED_SHUTDOWN' (Get-EventSignalRule 'System' 'Microsoft-Windows-Kernel-Power' 41 1).Code 'Kernel-Power 41 must be a documented signal.' Assert-Equal 'EVENT_FILE_SYSTEM_CORRUPTION' (Get-EventSignalRule 'System' 'Ntfs' 55 2).Code 'NTFS 55 must be a documented signal.' +$errorCodeFixture = 'Failure HRESULT 0x80070005; status 0xc0000005; duplicate 0X80070005.' +$errorCodeValues = @(Get-EventErrorCodes $errorCodeFixture) +Assert-Equal 2 $errorCodeValues.Count 'Event error-code extraction must deduplicate codes case-insensitively.' +Assert-Equal '0x80070005' $errorCodeValues[0] 'HRESULT normalization failed.' +Assert-Equal '0xC0000005' $errorCodeValues[1] 'NTSTATUS-style code normalization failed.' +Assert-Equal 0 @(Get-EventErrorCodes 'No hexadecimal error code here.').Count 'Events without hexadecimal codes must not invent one.' +Assert-Equal 'Service failed to start.' (Get-EventDesignation 'Fixture-Service' 7000 'Service failed to start. Extra diagnostic text follows.' $null) 'Generic event designation must use the first concise sentence from the event message.' +Assert-Equal 'Windows recorded an unexpected shutdown or restart' (Get-EventDesignation 'Microsoft-Windows-Kernel-Power' 41 'fixture' (Get-EventSignalRule 'System' 'Microsoft-Windows-Kernel-Power' 41 1)) 'Documented signal designation must take precedence over raw event text.' + $eventCutoff = $now.AddHours(-24) $eventFixtures = @( [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddHours(-3); Message='first'; RecordId=1 }, [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddHours(-2); Message='second'; RecordId=2 }, - [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddHours(-1); Message='representative'; RecordId=3 }, + [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddHours(-1); Message='representative error 0x80070005'; RecordId=3 }, [pscustomobject]@{ ProviderName='Other-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddMinutes(-30); Message='other provider'; RecordId=4 }, [pscustomobject]@{ ProviderName='Microsoft-Windows-Kernel-Power'; Id=41; Level=1; LevelDisplayName='Critical'; LogName='System'; TimeCreated=$now.AddDays(-2); Message='old signal'; RecordId=5 } ) @@ -41,7 +50,11 @@ $eventGroups = @(Group-EventLogEvents $eventFixtures $eventCutoff) Assert-Equal 2 $eventGroups.Count 'Different providers with the same Event ID must remain separate, and old events must be excluded.' $repeatedEventGroup = @($eventGroups | Where-Object { $_.ProviderName -eq 'Fixture-Provider' })[0] Assert-Equal 3 $repeatedEventGroup.Count 'Repeated events must be grouped.' -Assert-Equal 'representative' $repeatedEventGroup.RepresentativeMessage 'The latest event must provide the representative message.' +Assert-Equal 'representative error 0x80070005' $repeatedEventGroup.RepresentativeMessage 'The latest event must provide the representative message.' +Assert-Equal 'Fixture-Provider/7000' $repeatedEventGroup.EventIdentifier 'Grouped events must expose a stable provider/Event ID identifier.' +Assert-Equal 'representative error 0x80070005' $repeatedEventGroup.Designation 'Generic grouped events must expose a concise human-readable designation.' +Assert-Equal 1 @($repeatedEventGroup.ErrorCodes).Count 'Grouped events must expose error codes from the representative event text.' +Assert-Equal '0x80070005' $repeatedEventGroup.ErrorCodes[0] 'Grouped event error code was not normalized.' Assert-True (-not $repeatedEventGroup.IsSignal) 'A grouped generic Error event must not become a finding.' $messageFixtures = @( @@ -83,7 +96,7 @@ try { return [pscustomobject]@{ ProviderName='Microsoft-Windows-Kernel-Power'; Id=41; Level=1; LevelDisplayName='Critical'; LogName='System'; TimeCreated=$recent; Message='fixture unexpected restart'; RecordId=42 } } return @( - [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=1000; Level=2; LevelDisplayName='Error'; LogName='System'; TimeCreated=$recent; Message='generic error'; RecordId=41 }, + [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=1000; Level=2; LevelDisplayName='Error'; LogName='System'; TimeCreated=$recent; Message='Generic failure. HRESULT 0x80070005.'; RecordId=41 }, [pscustomobject]@{ ProviderName='Microsoft-Windows-Kernel-Power'; Id=41; Level=1; LevelDisplayName='Critical'; LogName='System'; TimeCreated=$recent; Message='fixture unexpected restart'; RecordId=42 } ) } @@ -109,6 +122,9 @@ finally { } $eventModuleText = $eventModuleOutput -join "`n" Assert-True ($eventModuleText.Contains('EVENT_UNEXPECTED_SHUTDOWN')) 'A documented high-signal event must emit a finding.' +Assert-True ($eventModuleText.Contains('Event identifier : Fixture-Provider/1000')) 'Event output must include provider/Event ID identifier.' +Assert-True ($eventModuleText.Contains('Designation : Generic failure.')) 'Event output must include a concise designation.' +Assert-True ($eventModuleText.Contains('Error code(s) : 0x80070005')) 'Event output must include hexadecimal Windows error codes found in the event message.' Assert-True (-not $eventModuleText.Contains('RECENT_ERROR_EVENTS')) 'A generic Error event must not emit the legacy blanket finding.' Assert-True (-not $eventModuleText.Contains('EVENT_LOG_SOURCE_UNAVAILABLE')) 'Partial event-log access must remain context.' Assert-True (-not $eventModuleText.Contains('EVENT_LOG_ASSESSMENT_UNAVAILABLE')) 'One unavailable Event Log source with working fallbacks must remain context.' From 09751440379acb121e7faa78f025a0664f47ded1 Mon Sep 17 00:00:00 2001 From: 0x0Bug <98243573+0x0bug@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:10:55 +0200 Subject: [PATCH 3/8] fix: avoid false-positive event error codes --- modules/events/diagnostic.ps1 | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/modules/events/diagnostic.ps1 b/modules/events/diagnostic.ps1 index d576680..2657408 100644 --- a/modules/events/diagnostic.ps1 +++ b/modules/events/diagnostic.ps1 @@ -46,10 +46,26 @@ function Get-EventErrorCodes { return @() } + # Event messages often contain many hexadecimal values that are not error + # codes (timestamps, fault offsets, handles, identifiers). Only accept + # values explicitly labelled as an error/status/exception code. + $labelPattern = '(?:HRESULT|NTSTATUS|(?:error|exception|failure|status|return)\s+code|status|код\s+(?:ошибки|исключения|состояния|сбоя|возврата))' + $valuePattern = '(?0x[0-9A-F]{1,16}|-?\d+)' + $regex = New-Object System.Text.RegularExpressions.Regex( + ('(?i){0}\s*[:=]?\s*{1}' -f $labelPattern, $valuePattern) + ) + $seen = @{} $codes = New-Object System.Collections.Generic.List[string] - foreach ($match in [System.Text.RegularExpressions.Regex]::Matches($Message, '(?i)(? Date: Fri, 18 Sep 2026 08:10:57 +0200 Subject: [PATCH 4/8] test: cover event code false positives --- tests/diagnostic-classification.tests.ps1 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/diagnostic-classification.tests.ps1 b/tests/diagnostic-classification.tests.ps1 index 450543c..bf2a073 100644 --- a/tests/diagnostic-classification.tests.ps1 +++ b/tests/diagnostic-classification.tests.ps1 @@ -29,20 +29,25 @@ Assert-True ($null -eq (Get-EventSignalRule 'System' 'Microsoft-Windows-Kernel-P Assert-Equal 'EVENT_UNEXPECTED_SHUTDOWN' (Get-EventSignalRule 'System' 'Microsoft-Windows-Kernel-Power' 41 1).Code 'Kernel-Power 41 must be a documented signal.' Assert-Equal 'EVENT_FILE_SYSTEM_CORRUPTION' (Get-EventSignalRule 'System' 'Ntfs' 55 2).Code 'NTFS 55 must be a documented signal.' -$errorCodeFixture = 'Failure HRESULT 0x80070005; status 0xc0000005; duplicate 0X80070005.' +$errorCodeFixture = 'Failure HRESULT 0x80070005; status 0xc0000005; duplicate HRESULT 0X80070005.' $errorCodeValues = @(Get-EventErrorCodes $errorCodeFixture) Assert-Equal 2 $errorCodeValues.Count 'Event error-code extraction must deduplicate codes case-insensitively.' Assert-Equal '0x80070005' $errorCodeValues[0] 'HRESULT normalization failed.' Assert-Equal '0xC0000005' $errorCodeValues[1] 'NTSTATUS-style code normalization failed.' Assert-Equal 0 @(Get-EventErrorCodes 'No hexadecimal error code here.').Count 'Events without hexadecimal codes must not invent one.' +$applicationErrorCodes = @(Get-EventErrorCodes 'Faulting app timestamp: 0x5c157f86; faulting module timestamp: 0x5c157efa; Exception code: 0xc0000005; Fault offset: 0x00001581; process id: 0x9adc.') +Assert-Equal 1 $applicationErrorCodes.Count 'Unlabelled hexadecimal metadata must not be reported as error codes.' +Assert-Equal '0xC0000005' $applicationErrorCodes[0] 'The labelled exception code must be retained.' +Assert-Equal '-2147024891' (Get-EventErrorCodes 'Код ошибки: -2147024891')[0] 'Labelled decimal Windows error codes must be retained.' Assert-Equal 'Service failed to start.' (Get-EventDesignation 'Fixture-Service' 7000 'Service failed to start. Extra diagnostic text follows.' $null) 'Generic event designation must use the first concise sentence from the event message.' +Assert-Equal 'Faulting application: app.exe' (Get-EventDesignation 'Application Error' 1000 "Faulting application: app.exe`r`nFaulting module: module.dll`r`nException code: 0xc0000005" $null) 'Multiline event designations must use only the first meaningful line.' Assert-Equal 'Windows recorded an unexpected shutdown or restart' (Get-EventDesignation 'Microsoft-Windows-Kernel-Power' 41 'fixture' (Get-EventSignalRule 'System' 'Microsoft-Windows-Kernel-Power' 41 1)) 'Documented signal designation must take precedence over raw event text.' $eventCutoff = $now.AddHours(-24) $eventFixtures = @( [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddHours(-3); Message='first'; RecordId=1 }, [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddHours(-2); Message='second'; RecordId=2 }, - [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddHours(-1); Message='representative error 0x80070005'; RecordId=3 }, + [pscustomobject]@{ ProviderName='Fixture-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddHours(-1); Message='Representative failure. Error code: 0x80070005'; RecordId=3 }, [pscustomobject]@{ ProviderName='Other-Provider'; Id=7000; Level=2; LevelDisplayName='Error'; LogName='Application'; TimeCreated=$now.AddMinutes(-30); Message='other provider'; RecordId=4 }, [pscustomobject]@{ ProviderName='Microsoft-Windows-Kernel-Power'; Id=41; Level=1; LevelDisplayName='Critical'; LogName='System'; TimeCreated=$now.AddDays(-2); Message='old signal'; RecordId=5 } ) @@ -50,9 +55,9 @@ $eventGroups = @(Group-EventLogEvents $eventFixtures $eventCutoff) Assert-Equal 2 $eventGroups.Count 'Different providers with the same Event ID must remain separate, and old events must be excluded.' $repeatedEventGroup = @($eventGroups | Where-Object { $_.ProviderName -eq 'Fixture-Provider' })[0] Assert-Equal 3 $repeatedEventGroup.Count 'Repeated events must be grouped.' -Assert-Equal 'representative error 0x80070005' $repeatedEventGroup.RepresentativeMessage 'The latest event must provide the representative message.' +Assert-Equal 'Representative failure. Error code: 0x80070005' $repeatedEventGroup.RepresentativeMessage 'The latest event must provide the representative message.' Assert-Equal 'Fixture-Provider/7000' $repeatedEventGroup.EventIdentifier 'Grouped events must expose a stable provider/Event ID identifier.' -Assert-Equal 'representative error 0x80070005' $repeatedEventGroup.Designation 'Generic grouped events must expose a concise human-readable designation.' +Assert-Equal 'Representative failure.' $repeatedEventGroup.Designation 'Generic grouped events must expose a concise human-readable designation.' Assert-Equal 1 @($repeatedEventGroup.ErrorCodes).Count 'Grouped events must expose error codes from the representative event text.' Assert-Equal '0x80070005' $repeatedEventGroup.ErrorCodes[0] 'Grouped event error code was not normalized.' Assert-True (-not $repeatedEventGroup.IsSignal) 'A grouped generic Error event must not become a finding.' From 7b5c531d65b91312cb9b6fba83f72a10cfa9f60b Mon Sep 17 00:00:00 2001 From: 0x0Bug <98243573+0x0bug@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:11:54 +0200 Subject: [PATCH 5/8] test: preserve scalar decimal error code assertion --- tests/diagnostic-classification.tests.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/diagnostic-classification.tests.ps1 b/tests/diagnostic-classification.tests.ps1 index bf2a073..f3cf1ab 100644 --- a/tests/diagnostic-classification.tests.ps1 +++ b/tests/diagnostic-classification.tests.ps1 @@ -38,7 +38,9 @@ Assert-Equal 0 @(Get-EventErrorCodes 'No hexadecimal error code here.').Count 'E $applicationErrorCodes = @(Get-EventErrorCodes 'Faulting app timestamp: 0x5c157f86; faulting module timestamp: 0x5c157efa; Exception code: 0xc0000005; Fault offset: 0x00001581; process id: 0x9adc.') Assert-Equal 1 $applicationErrorCodes.Count 'Unlabelled hexadecimal metadata must not be reported as error codes.' Assert-Equal '0xC0000005' $applicationErrorCodes[0] 'The labelled exception code must be retained.' -Assert-Equal '-2147024891' (Get-EventErrorCodes 'Код ошибки: -2147024891')[0] 'Labelled decimal Windows error codes must be retained.' +$decimalErrorCodes = @(Get-EventErrorCodes 'Код ошибки: -2147024891') +Assert-Equal 1 $decimalErrorCodes.Count 'A labelled decimal Windows error code must produce exactly one code.' +Assert-Equal '-2147024891' $decimalErrorCodes[0] 'Labelled decimal Windows error codes must be retained.' Assert-Equal 'Service failed to start.' (Get-EventDesignation 'Fixture-Service' 7000 'Service failed to start. Extra diagnostic text follows.' $null) 'Generic event designation must use the first concise sentence from the event message.' Assert-Equal 'Faulting application: app.exe' (Get-EventDesignation 'Application Error' 1000 "Faulting application: app.exe`r`nFaulting module: module.dll`r`nException code: 0xc0000005" $null) 'Multiline event designations must use only the first meaningful line.' Assert-Equal 'Windows recorded an unexpected shutdown or restart' (Get-EventDesignation 'Microsoft-Windows-Kernel-Power' 41 'fixture' (Get-EventSignalRule 'System' 'Microsoft-Windows-Kernel-Power' 41 1)) 'Documented signal designation must take precedence over raw event text.' From c4749a91cc1d69af5c6ab4667feeddef7cf41516 Mon Sep 17 00:00:00 2001 From: 0x0Bug <98243573+0x0bug@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:14:28 +0200 Subject: [PATCH 6/8] fix: use validation-safe event designation matching --- modules/events/diagnostic.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/events/diagnostic.ps1 b/modules/events/diagnostic.ps1 index 2657408..104bf32 100644 --- a/modules/events/diagnostic.ps1 +++ b/modules/events/diagnostic.ps1 @@ -102,9 +102,8 @@ function Get-EventDesignation { $designationSource = if ($firstLine.Count -gt 0) { [string]$firstLine[0] } else { $Message } $normalized = ConvertTo-OneLineMessage -Message $designationSource -MaxLength 160 - $sentenceMatch = [System.Text.RegularExpressions.Regex]::Match($normalized, '^.+?[.!?](?=\s|$)') - if ($sentenceMatch.Success) { - return $sentenceMatch.Value.Trim() + if ($normalized -match '^.+?[.!?](?=\s|$)') { + return $Matches[0].Trim() } return $normalized From c35421bc4877bc6abbc3826bbc5a2e5be880cfeb Mon Sep 17 00:00:00 2001 From: 0x0Bug <98243573+0x0bug@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:24:02 +0200 Subject: [PATCH 7/8] fix: keep event parser source ASCII-safe for PowerShell 5.1 --- modules/events/diagnostic.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/events/diagnostic.ps1 b/modules/events/diagnostic.ps1 index 104bf32..c06e030 100644 --- a/modules/events/diagnostic.ps1 +++ b/modules/events/diagnostic.ps1 @@ -49,7 +49,7 @@ function Get-EventErrorCodes { # Event messages often contain many hexadecimal values that are not error # codes (timestamps, fault offsets, handles, identifiers). Only accept # values explicitly labelled as an error/status/exception code. - $labelPattern = '(?:HRESULT|NTSTATUS|(?:error|exception|failure|status|return)\s+code|status|код\s+(?:ошибки|исключения|состояния|сбоя|возврата))' + $labelPattern = '(?:HRESULT|NTSTATUS|(?:error|exception|failure|status|return)\s+code|status|\u043A\u043E\u0434\s+(?:\u043E\u0448\u0438\u0431\u043A\u0438|\u0438\u0441\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F|\u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F|\u0441\u0431\u043E\u044F|\u0432\u043E\u0437\u0432\u0440\u0430\u0442\u0430))' $valuePattern = '(?0x[0-9A-F]{1,16}|-?\d+)' $regex = New-Object System.Text.RegularExpressions.Regex( ('(?i){0}\s*[:=]?\s*{1}' -f $labelPattern, $valuePattern) From f4e3dfa4730edcafd6efd7b115fc072020c95c7a Mon Sep 17 00:00:00 2001 From: 0x0Bug <98243573+0x0bug@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:24:05 +0200 Subject: [PATCH 8/8] test: keep Cyrillic fixture compatible with PowerShell 5.1 --- tests/diagnostic-classification.tests.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/diagnostic-classification.tests.ps1 b/tests/diagnostic-classification.tests.ps1 index f3cf1ab..326ff57 100644 --- a/tests/diagnostic-classification.tests.ps1 +++ b/tests/diagnostic-classification.tests.ps1 @@ -38,7 +38,8 @@ Assert-Equal 0 @(Get-EventErrorCodes 'No hexadecimal error code here.').Count 'E $applicationErrorCodes = @(Get-EventErrorCodes 'Faulting app timestamp: 0x5c157f86; faulting module timestamp: 0x5c157efa; Exception code: 0xc0000005; Fault offset: 0x00001581; process id: 0x9adc.') Assert-Equal 1 $applicationErrorCodes.Count 'Unlabelled hexadecimal metadata must not be reported as error codes.' Assert-Equal '0xC0000005' $applicationErrorCodes[0] 'The labelled exception code must be retained.' -$decimalErrorCodes = @(Get-EventErrorCodes 'Код ошибки: -2147024891') +$russianErrorText = '"\u041A\u043E\u0434 \u043E\u0448\u0438\u0431\u043A\u0438: -2147024891"' | ConvertFrom-Json +$decimalErrorCodes = @(Get-EventErrorCodes $russianErrorText) Assert-Equal 1 $decimalErrorCodes.Count 'A labelled decimal Windows error code must produce exactly one code.' Assert-Equal '-2147024891' $decimalErrorCodes[0] 'Labelled decimal Windows error codes must be retained.' Assert-Equal 'Service failed to start.' (Get-EventDesignation 'Fixture-Service' 7000 'Service failed to start. Extra diagnostic text follows.' $null) 'Generic event designation must use the first concise sentence from the event message.'