Skip to content

feat: COM under Native AOT (in-proc server) + Verify/activation fixes + TUI polish#11

Merged
harder merged 5 commits into
mainfrom
fix/com-verify-and-tui-polish
Jun 27, 2026
Merged

feat: COM under Native AOT (in-proc server) + Verify/activation fixes + TUI polish#11
harder merged 5 commits into
mainfrom
fix/com-verify-and-tui-polish

Conversation

@harder

@harder harder commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Headline: COM now works under Native AOT 🎉

The Native-AOT build previously could not activate the WinGet COM backend (new PackageManager() threw 0x80073D54 APPMODEL_ERROR_NO_PACKAGE) and silently fell back to CLI — the long-standing blocker. Fixed by switching to the in-process WinGet server:

  • Reference Microsoft.WindowsPackageManager.InProcCom (native-only: WindowsPackageManager.dll + Microsoft.Management.Deployment.InProc.dll), ExcludeAssets="compile" so its winmd doesn't clash with ComInterop's managed projection.
  • Add app.manifest with the package's registration-free WinRT activatableClass entries, routing new PackageManager() to the in-proc server (needs no OOP server, no package identity → activates under AOT).

Root cause: the winrtact.dll manual-activation shim was dropped from ComInterop ≥ 1.10.x (winget-cli#5459, #4839); AOT has no CsWinRT runtime fallback to reach the registered OOP server (JIT did).

Verified on a true AOT image (no coreclr.dll): --comdiag activates (3 catalogs, both MTA threads); full read-only surface works in-proc — search, installed (299), upgrades, versions (113), installer preview, COM detail (Tags/Support/Docs), Verify. Badge reads COM · winget 1.29.190-preview. Bonus: in-proc also avoids the OOP server-wedge issue. Cost: +~7.3 MB beside the ~22.4 MB AOT exe (vs the ~112 MB JIT folder that was the fallback plan).

COM backend bug fixes (latent until COM actually ran)

  • ConnectAsync composite agreement — stopped re-setting AcceptSourceAgreements on the composite reference (throws E_ILLEGAL_STATE_CHANGE); blocked every COM search/list/detail.
  • VerifyInstalledAsync false "Issues"CheckInstalledStatus returns a block per manifest installer; non-installed installers report "ARP entry not found" (0x8A150201). Now grouped per installer: Ok when any one installer's checks all pass. zoxide/PowerShell/7-Zip now report Ok.

TUI polish (from interactive testing)

  • Installed-in-Search — installed search rows show a ✓ Installed badge + Uninstall/Upgrade actions instead of a bare Install.
  • Responsive columns — Name/Id/Version shrink on a narrow terminal so the Available column stays visible; reflows on resize.
  • Op result persistence — result line stays visible through the post-op reload instead of being masked by "Loading…".
  • Bulk-select hint — Upgrades status bar shows Spc Select / U Upgrade sel.

Verification

  • Both TFMs compile clean; unit tests pass (dotnet test -f net10.0).
  • COM activation + full read-only surface confirmed on the AOT build (--comdiag / --comsmoke).
  • The Verify fix confirmed on zoxide/PowerShell/7-Zip.

Docs (HANDOFF.md, WINDOWS-TESTING.md) updated: AOT is now the ship target.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 27, 2026 20:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses correctness issues in the WinGet COM backend discovered during a real Windows COM run, and improves the TUI UX to better reflect installed/search state and behave more predictably on narrow/resized terminals.

Changes:

  • Fix COM catalog activation by removing an illegal AcceptSourceAgreements set on composite catalog references, and improve COM search rows with correlated installed/upgrade signals.
  • Correct VerifyInstalledAsync behavior to evaluate CheckInstalledStatus per-manifest-installer and choose the best-matching installer’s check set.
  • TUI polish: installed badge/actions in Search details, responsive column widths on resize, status/result persistence across refresh, and upgrades bulk-select hints; plus updated Windows testing handoff docs.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
WINDOWS-TESTING.md Updates Windows COM testing notes and session findings (includes new session-3 details).
HANDOFF.md Expanded session notes and conclusions from Windows COM verification work.
src/Ui.cs Adds Upgrades-specific status-bar hint pairs for bulk selection/upgrade.
src/Models.cs Adds InstalledVersion to models and merges it from context into detail.
src/DetailPanel.cs Shows an installed badge in Search details and adjusts actions to offer Upgrade/Uninstall when installed.
src/ComBackend.cs Fixes composite-catalog agreement handling; exposes installed/upgrade info in search results; improves verify result selection logic.
src/App.cs Reflows column widths on resize; preserves operation result message through post-op reload; refactors column width pinning logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ComBackend.cs
Comment thread WINDOWS-TESTING.md Outdated
Comment thread src/DetailPanel.cs Outdated
Comment thread HANDOFF.md
@harder harder changed the title fix: COM Verify/activation correctness + TUI polish from Windows COM run feat: COM under Native AOT (in-proc server) + Verify/activation fixes + TUI polish Jun 27, 2026
@harder

harder commented Jun 27, 2026

Copy link
Copy Markdown
Owner Author

Addressed the Copilot review feedback in d59ecfa, and merged main (Terminal.Gui 2.4.12-develop.7 bump + the permanent --comdiag / Help-dialog COM-fallback) into the branch:

  • ComBackend.cs:668 (Verify could report Ok on incomplete data) — a per-check read error inside an installer's loop now records a failing check instead of just setting a flag, so that installer can't be picked as best and reported Ok.
  • WINDOWS-TESTING.md (real username in path) — genericized to %USERPROFILE%.
  • DetailPanel.cs:72 (comment grammar) — reworded to "one that the user already has installed".
  • HANDOFF.md:66 (zoxide Verify inconsistency) — reconciled: zoxide reported Issues before the per-installer fix and Ok after (it's the package that surfaced the bug); note updated to say so.

Reverified after the merge: both TFMs build clean with TG 2.4.12-develop.7 (no API breaks), unit tests pass, and the AOT build still activates COM in-proc (--comdiag → OK, 3 catalogs, both MTA threads).

harder and others added 4 commits June 27, 2026 16:45
…ss server

The AOT build couldn't activate the out-of-process WinGet COM server (new
PackageManager() threw 0x80073D54 APPMODEL_ERROR_NO_PACKAGE): the winrtact.dll
manual-activation shim was dropped from ComInterop >= 1.10.x and AOT has no CsWinRT
runtime fallback to reach the registered server (JIT did, which is why JIT worked).
Switch to the in-process server, which needs neither the OOP server nor package
identity, so it activates fine under AOT:

- Reference Microsoft.WindowsPackageManager.InProcCom (native-only: ships
  WindowsPackageManager.dll + Microsoft.Management.Deployment.InProc.dll), with
  ExcludeAssets=compile so its winmd doesn't clash with ComInterop's managed
  projection (kept), and NoWarn=NU1701 for the native-package restore tag.
- Add app.manifest embedding the InProc package's registration-free WinRT
  activatableClass entries, so new PackageManager() activates in-proc.

Verified on a true AOT image (no coreclr.dll) with Terminal.Gui 2.4.12-develop.7:
--comdiag activates (3 catalogs, both MTA threads); --comsmoke shows the full
read-only surface working in-proc. Badge reads 'COM · winget 1.29.190-preview'
(bundled in-proc engine). Cost: +~7.3 MB beside the ~22.4 MB AOT exe. Bonus: in-proc
also sidesteps the OOP server-wedge issue.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…h installed-state

Bugs latent until COM actually ran (the AOT build used to fall back to CLI):

- ConnectAsync: stop re-setting AcceptSourceAgreements on the *composite* catalog
  reference (throws E_ILLEGAL_STATE_CHANGE). The source refs already accept it in
  RemoteRefs; this had blocked every COM search/list/detail the moment COM activated.
- VerifyInstalledAsync: CheckInstalledStatus returns a status block per *manifest
  installer*, and the installers that aren't the one installed legitimately report
  'ARP entry not found' (0x8A150201). Old code flattened all installers and reported
  Issues on any failure, so healthy multi-installer/portable packages looked corrupt.
  Now grouped per installer: Ok when any one installer's checks all pass; the dialog
  shows that clean installer. A per-check read error records a failing check so an
  installer can't be reported Ok on incomplete data.
- SearchAsync now reads the composite's correlated InstalledVersion so installed
  search rows can offer Uninstall/Upgrade instead of a bare Install.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nce, bulk-select hint

From interactive testing on the COM build:

- Installed-in-Search: an installed search row shows a green '✓ Installed' badge and
  Uninstall/Upgrade actions instead of a bare Install (Models gains InstalledVersion).
- Responsive column widths: Name/Id/Version shrink toward minimums on a narrow
  terminal so the Available column stays visible; reflows on resize via ViewportChanged.
- The operation result line ('Done', etc.) persists through the post-op list reload
  instead of being masked by 'Loading Installed…'.
- Upgrades status bar shows 'Spc Select' / 'U Upgrade sel' to make batch select
  discoverable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AOT is now the ship target (COM activates in-proc). Documents the in-proc fix, the
Verify/ConnectAsync bug fixes, the read-only COM verification, and the TUI polish.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@harder harder force-pushed the fix/com-verify-and-tui-polish branch from d59ecfa to d604cd1 Compare June 27, 2026 21:46
@harder harder requested a review from Copilot June 27, 2026 21:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Comment thread src/ComBackend.cs Outdated
Comment on lines +690 to +696
List<VerifyCheck> best = perInstaller.OrderBy (cs => cs.Count (c => !c.Ok)).First ();

return new () { Outcome = outcome, Checks = checks };
return new ()
{
Outcome = best.TrueForAll (c => c.Ok) ? VerifyOutcome.Ok : VerifyOutcome.Issues,
Checks = best
};
Comment thread WINDOWS-TESTING.md Outdated
**Backend badge + version** (`PackageManager.Version`):

- [ ] The top-right header shows the live backend + winget version, e.g. **`COM · winget 1.x`** (or `CLI · winget 1.x` / `Mock backend`). On the AOT build this is the quickest confirmation of whether COM actually activated or silently fell back to CLI.
- [x] The top-right header shows the live backend + winget version. *(Session 3: `DescribeAsync` — which produces the badge string — returns **`COM · winget 1.29.250`** on the JIT/COM build and `CLI · winget 1.29.250` on the AOT build (ComBackend's ctor throws → CLI fallback). Confirmed at the data layer; the header render itself wants a quick TUI eyeball but the string is exactly what the badge shows.)*
Comment thread WINDOWS-TESTING.md Outdated
Comment on lines +166 to +171
> **Session 3:** the `IProgress<OpProgress>` callback path **works on COM (JIT)** — `DownloadAsync(zoxide)`
> delivered progress samples (phase **Downloading**, fraction → 1.00) with no crash, so the managed→native
> progress delivery is sound. The original "**under AOT**, the one CCW-callback unknown" framing is now
> **moot**: COM doesn't activate under AOT at all (see the resolved banner), so progress under AOT never runs;
> under JIT (the COM ship vehicle) the CCW path is standard. Install/uninstall progress *rendering* in the
> status bar still wants an interactive recheck.
Comment thread HANDOFF.md Outdated
Comment on lines +84 to +85
NOTE: the "live progress under **AOT**" CCW-marshaling item is **moot** — COM doesn't run under AOT; under JIT
(the COM ship vehicle) the callback path is standard and was confirmed via `DownloadAsync`.
Comment thread WINDOWS-TESTING.md Outdated
- [ ] **Same-id-across-catalogs** (rare): if a package id exists in multiple sources, operations resolve the first match. Only worth checking if you hit an odd case.
- [ ] **CLI-backend cancel** (`--cli`, then Esc mid-install): confirm it stops watching but does **not** kill `winget.exe` (the install continues) — documented, lower priority.
- [ ] **Measure the AOT binary size** of the COM (Windows) build and compare to the CLI/mock build, to budget the COM backend's cost. *(Open spike question.)*
- [x] **Measure the AOT binary size** of the COM (Windows) build and compare to the CLI/mock build, to budget the COM backend's cost. *(Session 3: **AOT win-x64 single exe = 22.4 MB** (no `coreclr.dll`). The **JIT self-contained** alternative — the recommended COM ship vehicle since AOT can't activate COM — is **112.7 MB** for the whole folder (~5× the AOT exe, main exe only 0.2 MB + the shared CoreCLR/framework). So keeping COM costs the AOT size advantage regardless of which non-AOT form is chosen.)*
- VerifyInstalledAsync: when the best installer's failure is an unreadable entry (a
  read-error sentinel), report VerifyOutcome.Error ('couldn't verify') instead of
  Issues ('may be corrupt'). Tracked via a per-installer ReadError flag rather than
  string-matching the sentinel label. Happy path unchanged (clean → Ok, real failures
  → Issues).
- Docs: remove stale 'COM doesn't activate under AOT / JIT is the ship vehicle' claims
  that contradicted the resolved in-proc finding — the backend badge line, the
  live-progress blockquote, the AOT size-measurement note (WINDOWS-TESTING.md), and the
  'still not verified' note (HANDOFF.md) now reflect that COM runs under AOT in-proc and
  AOT is the ship target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@harder

harder commented Jun 27, 2026

Copy link
Copy Markdown
Owner Author

Addressed the latest Copilot review in feb0673. I verified each comment against the current tree first:

Valid → fixed (5):

  • ComBackend.cs Verify — when the best installer's only failure is an unreadable entry (read-error sentinel), the outcome is now Error ("couldn't verify") instead of Issues ("may be corrupt"). Implemented with a per-installer ReadError flag rather than string-matching the sentinel. Happy path unchanged (clean → Ok, real failures → Issues).
  • WINDOWS-TESTING.md backend-badge line — updated: the AOT build now reads COM · winget 1.29.190-preview (in-proc), not a CLI fallback.
  • WINDOWS-TESTING.md live-progress blockquote — updated: COM runs under AOT in-proc; the CCW progress path applies on the AOT build (and was exercised via DownloadAsync).
  • WINDOWS-TESTING.md AOT size note — updated: AOT (22.4 MB exe + ~7.3 MB in-proc engine ≈ ~30 MB) is the ship target; the 112.7 MB JIT folder was the abandoned fallback.
  • HANDOFF.md "still not verified" note — updated: COM runs under AOT in-proc; removed the "JIT is the ship vehicle" framing. (Also fixed one more instance of the same stale claim in the DownloadAsync bullet for consistency.)

Already resolved in the current tree (4 — stale re-posts after the force-push), verified, no action:

  • Verify read-error skips a check → already records a failing sentinel (the new comment above is the refinement of this).
  • Real username in a doc path → already genericized to %USERPROFILE%.
  • DetailPanel.cs comment grammar → already "one that the user already has installed".
  • HANDOFF.md zoxide Verify Issues vs Ok → already reconciled (reports Ok after the fix; Issues was the pre-fix symptom).

Verification: both TFMs build clean, unit tests pass. The Verify change is happy-path-identical (the new Error branch only fires on an actual read exception, which can't occur on a healthy package), so no regression to the confirmed zoxide/PowerShell/7-Zip → Ok results.

@harder harder merged commit bcbbc48 into main Jun 27, 2026
2 checks passed
@harder harder deleted the fix/com-verify-and-tui-polish branch June 27, 2026 22:27
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