Skip to content

fix: Windows Update install never applied updates (fake success)#609

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/windows-update-install-fake-success
Jun 3, 2026
Merged

fix: Windows Update install never applied updates (fake success)#609
laurentiu021 merged 1 commit into
mainfrom
fix/windows-update-install-fake-success

Conversation

@laurentiu021
Copy link
Copy Markdown
Owner

@laurentiu021 laurentiu021 commented Jun 3, 2026

Summary

Windows Update silently failed to install anything while reporting success. Reproduced live: select 20 updates, click Install — the UI shows "Installed 20 update(s)", live console is empty, and a re-scan immediately returns the same 20 updates. Reported by user during v1.17.3 testing.

Root cause

Three compounding bugs introduced in PR #473 (feat: add individual update selection):

  1. KB prefix mismatch — at scan time, KBArticleIDs are joined as "KB" + ids so UpdateEntry.KB holds e.g. "KB5034441". At install time, the same string is passed to Install-WindowsUpdate -KBArticleID 'KB5034441'. PSWindowsUpdate's -KBArticleID expects bare digits ('5034441'). With the prefix, the cmdlet matches zero updates and exits silently.
  2. Filter excludes valid updatesu.KB.All(c => char.IsLetterOrDigit(c)) rejects:
    • Updates with multiple KBs ("KB5034441,5034442" — comma is not a letter/digit)
    • Updates without a KB (Defender Definitions, drivers — empty string)
  3. Fake success reportingStatusMessage = $"Installed {selected.Count} update(s)" was hardcoded from the selection count, never from the cmdlet's actual result.

Combined: when the user selects 20 updates, the filter quietly drops most of them, sends a malformed KBArticleID for the rest, the cmdlet installs nothing, and the UI claims full success.

Fix

  • Title-based install pipeline — selected updates are matched against the live feed by Title (which is unique and works for all update types including those without a KB):
    $titles = @('...','...')
    Get-WindowsUpdate -MicrosoftUpdate | Where-Object { $titles -contains $_.Title } |
      Install-WindowsUpdate -AcceptAll -IgnoreReboot -Verbose
  • Real result parsingInstall-WindowsUpdate output is emitted as JSON via a __RESULT__: marker and parsed into per-title Installed/Failed/Downloaded outcomes.
  • Honest status reporting"Installed X/Y. Failed: Z. Not applied: W." derived from actual results, not selection count.
  • Per-row status updatesUpdateEntry.Status is now an ObservableProperty; each row's Status column updates as the install progresses (Installing…Installed / Failed / Not applied).
  • Unified update list — "List updates" now returns Standard + Feature upgrades + Hidden in one categorized table (Security, Cumulative, Defender, Driver, Servicing, .NET, Feature upgrade, Hidden). The separate "Feature upgrades" button is removed.

Files changed

  • SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs — unified scan, Title-based install, result parser
  • SysManager/SysManager/Models/UpdateEntry.cs — Status now ObservableProperty
  • SysManager/SysManager/Views/WindowsUpdateView.xaml — removed "Feature upgrades" button
  • SysManager/SysManager.Tests/WindowsUpdateViewModelTests.cs — 8 new ParseInstallResults tests
  • CHANGELOG.md, README.md, csproj version → 1.17.4

Test plan

  • dotnet build -c Release SysManager.csproj — 0 errors
  • dotnet build -c Release SysManager.Tests.csproj — 0 errors
  • 8 new unit tests for ParseInstallResults (empty, all installed, mixed, succeeded, error, invalid JSON, single object)
  • CI green on this PR
  • Manual install verification on test machine: select 20 updates, install, confirm console shows real PSWindowsUpdate output, status bar shows real counts, re-scan returns fewer updates

Summary by CodeRabbit

Release Notes v1.17.4

  • New Features

    • Unified updates display with consistent handling across all update types, including items without KB IDs
    • Live install progress console with real-time per-update status tracking
    • Accurate install reporting showing real counts of successful, failed, and unapplied updates
    • Automatic pending-reboot detection with history retention
  • Bug Fixes

    • Improved Windows Update installation matching and error reporting
  • Chores

    • Version bumped to 1.17.4

Install-WindowsUpdate received KB numbers prefixed with 'KB' (e.g.
'KB5034441') but its -KBArticleID parameter expects bare digits, so the
cmdlet matched zero updates and exited silently. Updates without a KB
(Defender Definitions, drivers) and updates with multiple KBs were also
excluded by the selection filter. The status bar reported a fabricated
'Installed N update(s)' message based on the selection count rather than
the cmdlet's actual result.

Switch to a Title-based install pipeline that works for all updates
including those without a KB. Capture and parse Install-WindowsUpdate
output to report real counts and per-row outcomes.

Unify the update list: 'List updates' now returns Standard + Feature
upgrades + Hidden in one grouped table; the separate 'Feature upgrades'
button is removed since it is no longer needed.

- ListUpdatesAsync: single scan for Standard/Feature/Hidden, categorized
  (Security, Cumulative, Defender, Driver, Servicing, .NET, Feature
  upgrade, Hidden)
- InstallUpdatesAsync: title array injected with single-quote escaping;
  pipeline matches by Title against the live feed; output captured via
  __RESULT__: marker and parsed into per-title results
- ParseInstallResults: pure static helper, fully unit-tested
- ApplyInstallResults: updates each row's Status (Installed/Failed/Not
  applied) so the DataGrid reflects reality
- UpdateEntry.Status: now an ObservableProperty so per-row status updates
  during/after install
- Status bar shows real counts: 'Installed X/Y. Failed: Z. Not applied: W.'
- WindowsUpdateView.xaml: 'Feature upgrades' button removed
- 8 new ParseInstallResults tests; ListFeatureUpdatesCommand removed
  from command-existence theory

Closes the silent-success bug reported during a 20-update install where
the UI claimed success but the next scan returned the same 20 updates.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Version 1.17.4 refactors the Windows Update feature to unify listing (consolidating feature upgrades and adding categorization), replace KB-based installation with title-based matching, parse JSON install results to report accurate outcomes, and enable observable status notifications per-update.

Changes

Windows Update Installation Pipeline Refactoring

Layer / File(s) Summary
Observable Status Property for MVVM
SysManager/SysManager/Models/UpdateEntry.cs
Status is converted from an init-only property to an [ObservableProperty]-backed private field, enabling MVVM reactive notifications when status changes during installation.
Unified Update Listing with Categorization
SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs, SysManager/SysManager/Views/WindowsUpdateView.xaml
ListUpdatesAsync adds rule-based categorization (Defender, Driver, Cumulative, Security, etc.) and retrieves feature upgrades in the main flow; ListFeatureUpdatesAsync is removed. The UI no longer exposes a separate "Feature upgrades" button.
Title-Based Installation with Result Parsing
SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs, SysManager/SysManager.Tests/WindowsUpdateViewModelTests.cs
InstallUpdatesAsync matches and installs updates by Title (not KB), captures JSON reports, parses installed/failed/not-applied counts, and updates per-item status. Selection filtering relaxes KB validation. New ParseInstallResults helper and eight unit tests cover JSON parsing, edge cases (empty, mixed statuses, invalid JSON), and single-object inputs.
Version Bump and Documentation
SysManager/SysManager/SysManager.csproj, CHANGELOG.md, README.md
Version incremented to 1.17.4; CHANGELOG documents the unified updates table and title-based install matching; README details the new categorized DataGrid, standardized handling for all update types, and accurate install-result reporting.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • laurentiu021/SystemManager#473: Both PRs modify the Windows Update selection model and install flow by making UpdateEntry MVVM-observable and adjusting InstallUpdatesAsync to handle selected updates (this PR transitions from KB-based filtering to title-based matching).

Poem

🐰 A rabbit hops through updates clean,
No more KB codes to screen!
By Title now they all align,
Status glows with MVVM shine—
Feature upgrades join the feast,
Installation troubles ceased! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main bug fix: Windows Update install was reporting fake success instead of actually applying updates. This is the primary change across the entire changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/windows-update-install-fake-success

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment on lines +466 to +475
if (results.TryGetValue(entry.Title, out var result) && !string.IsNullOrWhiteSpace(result))
{
entry.Status = IsInstalledResult(result) ? "Installed"
: IsFailedResult(result) ? "Failed"
: result;
}
else
{
entry.Status = "Not applied";
}
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs`:
- Around line 367-373: The current script sets $matched by querying only the
standard feed first and only queries the 'Upgrades' feed if nothing matched,
causing mixed selections (normal update + feature upgrade) to miss the upgrade;
modify the logic so you query both feeds up front, combine their results, then
apply the filter by titles. Specifically, replace the sequential
Get-WindowsUpdate calls with two queries (the standard feed and the -UpdateType
Software -Category 'Upgrades' feed), merge their outputs (e.g., add or union the
collections) and then run Where-Object { $titles -contains $_.Title } to
populate $matched (instead of populating $matched from only the first query and
falling back to $up).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b9fb967f-22c9-4eb8-b139-7b6b4e347a88

📥 Commits

Reviewing files that changed from the base of the PR and between 25713ce and 14e46df.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • README.md
  • SysManager/SysManager.Tests/WindowsUpdateViewModelTests.cs
  • SysManager/SysManager/Models/UpdateEntry.cs
  • SysManager/SysManager/SysManager.csproj
  • SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs
  • SysManager/SysManager/Views/WindowsUpdateView.xaml
💤 Files with no reviewable changes (1)
  • SysManager/SysManager/Views/WindowsUpdateView.xaml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build & unit tests
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (6)
SysManager/SysManager/SysManager.csproj (1)

13-15: LGTM!

CHANGELOG.md (1)

9-18: LGTM!

README.md (1)

125-137: LGTM!

SysManager/SysManager/Models/UpdateEntry.cs (1)

14-15: LGTM!

SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs (1)

420-477: LGTM!

SysManager/SysManager.Tests/WindowsUpdateViewModelTests.cs (1)

208-294: LGTM!

Comment on lines +367 to +373
$matched = Get-WindowsUpdate -MicrosoftUpdate -ErrorAction SilentlyContinue |
Where-Object {{ $titles -contains $_.Title }}
if (-not $matched -or @($matched).Count -eq 0) {{
$up = Get-WindowsUpdate -MicrosoftUpdate -UpdateType Software -Category 'Upgrades' -ErrorAction SilentlyContinue |
Where-Object {{ $titles -contains $_.Title }}
if ($up) {{ $matched = $up }}
}}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Query both feeds before filtering selected titles.

Line 367 only searches the standard feed, and Lines 369-373 only fall back to upgrades when nothing matched. If a user selects a normal update and a feature upgrade together, the standard match makes $matched non-empty, so the feature upgrade is skipped and later shows as Not applied.

Suggested fix
-                    $matched = Get-WindowsUpdate -MicrosoftUpdate -ErrorAction SilentlyContinue |
-                               Where-Object { $titles -contains $_.Title }
-                    if (-not $matched -or @($matched).Count -eq 0) {
-                        $up = Get-WindowsUpdate -MicrosoftUpdate -UpdateType Software -Category 'Upgrades' -ErrorAction SilentlyContinue |
-                              Where-Object { $titles -contains $_.Title }
-                        if ($up) { $matched = $up }
-                    }
+                    $standard = @(Get-WindowsUpdate -MicrosoftUpdate -ErrorAction SilentlyContinue)
+                    $upgrades = @(Get-WindowsUpdate -MicrosoftUpdate -UpdateType Software -Category 'Upgrades' -ErrorAction SilentlyContinue)
+                    $matched = @($standard + $upgrades) |
+                               Where-Object { $titles -contains $_.Title } |
+                               Sort-Object Title -Unique
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SysManager/SysManager/ViewModels/WindowsUpdateViewModel.cs` around lines 367
- 373, The current script sets $matched by querying only the standard feed first
and only queries the 'Upgrades' feed if nothing matched, causing mixed
selections (normal update + feature upgrade) to miss the upgrade; modify the
logic so you query both feeds up front, combine their results, then apply the
filter by titles. Specifically, replace the sequential Get-WindowsUpdate calls
with two queries (the standard feed and the -UpdateType Software -Category
'Upgrades' feed), merge their outputs (e.g., add or union the collections) and
then run Where-Object { $titles -contains $_.Title } to populate $matched
(instead of populating $matched from only the first query and falling back to
$up).

@laurentiu021 laurentiu021 merged commit 2c3f935 into main Jun 3, 2026
5 checks passed
@laurentiu021 laurentiu021 deleted the fix/windows-update-install-fake-success branch June 3, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants