Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
312 changes: 312 additions & 0 deletions HANDOFF.md

Large diffs are not rendered by default.

82 changes: 71 additions & 11 deletions WINDOWS-TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,39 @@ For quick iteration without AOT: `dotnet run -f net10.0-windows10.0.26100.0`.

## P0 — Foundational COM runtime (must pass first)

- [ ] **AOT publish succeeds** and produces a native exe; `coreclr.dll` is absent (true AOT, not self-contained).
- [ ] **No `InvalidCastException` anywhere at runtime.** The whole backend uses indexed `Materialize<T>` instead of `foreach` over projected collections (the spike's AOT rule). Exercise search/list/upgrades/show and confirm none throw the spike's original cast error.
- [ ] **Default backend = COM** on the Windows build with no flags (not CLI). Sanity: search is fast/structured, IDs are never truncated with `…`.
- [ ] **Flag selection** works: `--cli`, `--com`, `--mock` pick the right backend; **`--cli` wins when both `--cli` and `--com` are passed** (precedence `--mock > --cli > --com > default`).
- [ ] **Search** (`/`) returns real catalog results with version + source columns.
- [ ] **Installed** tab lists installed packages with correct installed versions.
- [ ] **Upgrades** tab shows only packages with an available update, with the Available column populated.
- [ ] **Details** panel: selecting a row fetches metadata (publisher, description, homepage, license, release-notes URL).
- [ ] **Source filter** (`f`) cycles All / winget / msstore and re-queries correctly.
> ### 🚨 CRITICAL FINDING — the AOT build cannot activate the COM backend
> **`new PackageManager()` throws `0x80073D54` (`APPMODEL_ERROR_NO_PACKAGE`) in the Native-AOT
> build**, so `SelectBackend` catches it and **silently falls back to the CLI backend** (the
> "COM backend unavailable…" stderr note is painted over by the TUI redraw, so it's invisible).
> Confirmed by the COM-only `V` (Verify) action reporting *"Verify is only available on the COM
> backend"* in the default launch.
>
> - The **same source built non-AOT (JIT, self-contained x64) activates COM fine** (3 catalogs),
> even with the server warmed by JIT moments earlier. So the failure is **AOT-specific**, not
> server state, not apartment (both threads are MTA).
> - **Did NOT fix it:** CsWinRT AOT optimizer (`Microsoft.Windows.CsWinRT` + `CsWinRTAotOptimizerEnabled=Auto`);
> an `app.manifest` with Win10 `supportedOS` + `longPathAware`; warming the server first.
> - **Caveat:** EARLY in the session (before heavy COM-diagnostic abuse + a reboot) an AOT *spike*
> DID activate COM (3 catalogs + the AOT foreach signature), so AOT-COM is not categorically
> impossible here — the current deterministic failure may be entangled with COM-server/AppModel
> state, OR a genuine AOT activation bug the early run avoided. Machine state is compromised.
> - **DECISIVE NEXT EXPERIMENT (clean state):** fresh reboot → as the *very first* COM activity,
> run the AOT build's `--comdiag` (binary in the publish dir still has it). Activates → transient
> state, redo the real verification on COM. Still fails → genuine AOT bug → CsWinRT 3.x / upstream
> issue / `InProcCom` / ship the COM build non-AOT (JIT confirmed working).
> - **Consequence for the results below:** items marked ✅ that "passed on COM" were almost
> certainly exercising the **CLI** backend (which also yields structured search/list/details, so
> it was indistinguishable). They validate the UI + CLI path, NOT the COM backend. See HANDOFF.md.

- [x] **AOT publish succeeds** and produces a native exe; `coreclr.dll` is absent (true AOT, not self-contained). *(23.3 MB exe; verified coreclr.dll absent. On an ARM64 host the publish must run inside `Enter-VsDevShell -DevCmdArguments "-arch=x64 -host_arch=arm64"` with the VS Installer dir on PATH — ILC 10.0.8 calls bare `vswhere.exe`.)*
- [x] **No `InvalidCastException` anywhere at runtime.** The whole backend uses indexed `Materialize<T>` instead of `foreach` over projected collections (the spike's AOT rule). Exercise search/list/upgrades/show and confirm none throw the spike's original cast error. *(Clean across search, installed, upgrades, and details. Spike re-confirmed the indexed pattern on this host.)*
- [ ] ❌ **FAILS — Default backend is NOT COM.** The AOT build can't activate `PackageManager`, so the default silently runs the **CLI** backend (proven: `V` reports "only available on the COM backend"). See the critical-finding banner above. *(The fast/structured search and full IDs we saw are the CLI backend's output, which also looks structured — they did not prove COM was active.)*
- [x] **Flag selection** works: `--cli`, `--com`, `--mock` pick the right backend; **`--cli` wins when both `--cli` and `--com` are passed** (precedence `--mock > --cli > --com > default`). *(`--mock --cli --com` → 10 mock pkgs; `--cli --com` → CLI backend, V reports COM-only on real packages.)*
- [x] **Search** (Search tab `1`, then `/`) returns real catalog results with version + source columns. *(18 results for "powertoys" across winget + msstore.)*
- [x] **Installed** tab lists installed packages with correct installed versions. *(248 packages; PowerToys 0.98.1 etc., correlated via COM LocalCatalogs composite.)*
- [x] **Upgrades** tab shows only packages with an available update, with the Available column populated.
- [x] **Details** panel: selecting a row fetches metadata (publisher, description, homepage, license, release-notes URL). *(Verified on Fefedu973.UniversalSearchSuggestions: publisher, MIT license, homepage + release-notes URLs, description all populated.)*
- [x] **Source filter** (`f`) cycles All / winget / msstore and re-queries correctly. *(Functional pass. Cosmetic follow-ups: msstore Source cell unreadable when row highlighted — Accent-on-Accent contrast bug; Source column blank under single-source filter; per-row detail fetch stalled 30–60s for later rows under winget filter — logged to P2 perf watch.)*

## P1 — Operations + the two new features

Expand Down Expand Up @@ -77,10 +101,36 @@ Operations (pick a small, safe package to install/uninstall, e.g. a CLI tool):
- [ ] Deliberately break an install (e.g. delete a file from the install dir) and confirm `V` reports the **Issues** outcome with the failing check.
- [ ] (CLI backend, `--cli`) `V` reports "Verify is only available on the COM backend" rather than erroring.

**Repair** (`R` — COM-only, via `RepairPackageAsync`):

- [ ] `R` on a healthy installed package (Installed/Upgrades) confirms, then repairs: the status bar shows a determinate bar advancing through a **Repairing** phase, ending "Done" (or "(reboot required)").
- [ ] **Verify → Repair flow**: break an install, `V` → **Issues** outcome → the result dialog offers **Repair** / **Close**; choosing **Repair** runs the repair **without a second confirm** and the install is restored (`V` again reports Ok).
- [ ] A package whose installer has no repair behavior reports the friendly **"{name} doesn't support repair."** (`NoApplicableRepairer`), not a raw HRESULT.
- [ ] **Esc during a repair** cancels cooperatively ("Cancelled"); the one-op-at-a-time gate blocks starting a second op mid-repair.
- [ ] (CLI backend, `--cli`) `R` shows the neutral **"Repair is only available on the COM backend."** (not a red error), and the detail-panel `R Repair install` action is still listed.
- [ ] `R` is **not** offered in Search mode (the selected package may not be installed).

**Richer detail panel** (COM):

- [ ] The detail panel for a package shows the extra manifest fields when present: **Tags**, **Product code**, **Family name**, a clickable **Support** link, and **Documentation** links — in addition to the existing fields. Verify the links open.
- [ ] Packages without these fields don't render empty rows (the lines are omitted when absent).
- [ ] ⚠️ **Not a bug — symptom of the AOT COM-activation failure.** The extra manifest fields (**Tags**, **Product code**, **Family name**, **Support**, **Documentation**) never render because the app is running on the **CLI** backend (COM didn't activate — see the critical-finding banner), and these fields are COM-only (`null` on CLI by design, per `Models.cs`). The spike proved the COM data exists and is readable via the backend's indexed pattern, so once COM activation is fixed this should work. Re-test after the COM-on-AOT issue is resolved.
- [x] Packages without these fields don't render empty rows (the lines are omitted when absent). *(Confirmed — absent fields cleanly omitted.)*
- [ ] **New enrichment fields (COM, all conditional).** Once COM activates, confirm these render when present and are omitted when blank: **Author**, **Copyright**, **Privacy** (link), **Purchase** (link), **Installation notes** (paragraph section after Description), and for installed packages **Scope** + **Installed to** (location). These come from `CatalogPackageMetadata` (Author/Copyright/PrivacyUrl/PurchaseUrl/InstallationNotes) and `PackageVersionInfo.GetMetadata(InstalledScope/InstalledLocation)`. A package with none of them should look exactly as before.

**Dynamic sources** (`f`) — COM via `GetPackageCatalogs()`, CLI via `winget source list`:

- [ ] On a stock machine, `f` still cycles **All → Winget → MsStore → All** (the discovered list matches the two predefined sources).
- [ ] **Add a custom source** (`winget source add -n contoso <url>`), relaunch, and confirm `f` now includes **contoso** in the cycle and filtering to it scopes the list/search to that source. (This is the whole point of going dynamic — verify it on both COM and `--cli`.)
- [ ] With a source selected that no longer exists (remove it while the app holds it selected, then refresh), the filter resets to **All** rather than erroring.
Comment on lines +121 to +123

**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.
- [ ] The **Help** dialog (`?`) leads with a matching `Backend: …` line.

**Search match hint + result cap** (COM):

- [ ] Search a term that matches a package by **tag/moniker/command** (not its name) — the detail panel shows a dim `↳ matched on tag` footnote. A normal name/id match shows **no** such line.
- [ ] A very broad search (e.g. a single common letter) caps at **1000** rows and the status reads `1000+ matches — refine your search to narrow` instead of flooding the table.

**Live progress bar** (the headline feature — also tests `.Progress` delegate marshaling under AOT, the one CCW-callback unknown):

Expand All @@ -96,6 +146,15 @@ Operations (pick a small, safe package to install/uninstall, e.g. a CLI tool):
- [ ] **Batch upgrade + Esc**: the in-flight item cancels and the remaining queue stops.
- [ ] **One-op-at-a-time guard**: triggering a second operation while one is running is ignored (no second progress bar, no crash).

## P1.5 — Upstream parity ports (ported from `shanselman/winget-tui`, June 2026)

Three behavioural changes ported from upstream. Logic is unit-tested (`tests/AppBehaviorTests.cs`),
but these paths need a real terminal / real winget to confirm end-to-end.

- [ ] **Click-to-sort column headers** (upstream `66d464c4`). With the mouse, **click the `Name`, `Id`, or `Version` header** to sort by that column (ascending); **click the same header again** to reverse direction (the `↑`/`↓` arrow in the header should flip). Clicking the marker, **`Available`, or `Source`** header is a **no-op**. Verify in both **Installed** and **Upgrades** tabs (column indices differ between them). Keyboard `S` cycling must still work unchanged. *(Mouse `ScreenToCell` header detection can't be exercised from Linux unit tests.)*
- [ ] **Truncated-id upgrade falls back to name** (upstream `fd9e9dbe`). Find an Upgrades row whose **id is truncated with `…`** (winget does this to long ids in tabular output). Press **`u`**: instead of the old "Cannot upgrade: id was truncated" block, you should get a confirm reading **"Upgrade <name>? (id was truncated by winget — matching by name)"**, and on confirm the upgrade should actually run (CLI backend retries `--name --exact`). Needs the **`--cli`** backend (COM ids are never truncated). Confirm a non-truncated row still shows the plain "Upgrade <name>?" prompt.
- [ ] **Contextual empty-state message** (upstream `#228`). When the list is empty, the message should match the reason: **Upgrades + 📌-hide (`UnpinnedOnly`) → "No unpinned packages with upgrades found."**; Upgrades + 📌-only → "No pinned packages with upgrades found."; Upgrades + all → "All packages are up to date!"; an active local filter that hides everything → 'No packages match "<text>".'. Toggle the pin filter (`P`) in Upgrades and type a non-matching filter (`/`) to exercise each.

## P2 — Review-flagged real-Windows concerns & measurements

- [ ] **Shared `PackageManager` thread-agility.** The backend reuses one `PackageManager` across operations invoked from background/threadpool (MTA) threads. Watch for `RPC_E_WRONG_THREAD` or intermittent COM errors under rapid search/typing or back-to-back ops. **If seen → switch to a fresh `PackageManager` per operation** (currently shared as a perf choice). *(Open from review pass 2.)*
Expand All @@ -105,6 +164,7 @@ Operations (pick a small, safe package to install/uninstall, e.g. a CLI tool):
- [ ] **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.)*
- [ ] **(Optional) win-arm64**: repeat the P0 smoke on an arm64 host or arm64 cross-target.
- [ ] **Terminal.Gui bump `2.4.3-develop.9` → `2.4.7-develop.1`.** Spot-check the Windows-only input/render fixes that landed in the 2.4.4 release line (can't be verified from Linux): (a) type a **non-ASCII search query** (e.g. accented chars / IME) into `/` search and confirm it renders correctly (Windows VT input encoding fix #5453); (b) **paste** a Unicode string into search via bracketed paste and confirm no mojibake (clipboard fixes #5449/#5451); (c) **resize the terminal** mid-use and confirm no garbled frame at the wrong dimensions (#5461). No app code changed — these are upstream fixes the bump picks up for free.

---

Expand Down
2 changes: 1 addition & 1 deletion WingetTuiSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
prereleases; this is intentionally a moving target since the project's purpose is
to evaluate parity against current Terminal.Gui behavior.
-->
<PackageReference Include="Terminal.Gui" Version="2.4.3-develop.9" />
<PackageReference Include="Terminal.Gui" Version="2.4.7-develop.1" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net10.0-windows10.0.26100.0'">
Expand Down
Loading
Loading