Skip to content

fix(windows): bake app name into assembly so taskbar right-click shows it (#9)#17

Merged
Christian-Katzmann merged 2 commits into
Christian-Katzmann:mainfrom
Firnschnee:fix/windows-assembly-name
Jun 4, 2026
Merged

fix(windows): bake app name into assembly so taskbar right-click shows it (#9)#17
Christian-Katzmann merged 2 commits into
Christian-Katzmann:mainfrom
Firnschnee:fix/windows-assembly-name

Conversation

@Firnschnee
Copy link
Copy Markdown
Contributor

@Firnschnee Firnschnee commented Jun 2, 2026

Closes #9.

The problem

The taskbar right-click menu showed app-it-host instead of the app name. I assumed the fix was just -p:AssemblyName (per the issue), but real hardware made me check which embedded field the shell actually reads first.

What the field tracing showed (Windows 11)

desktop-build.ps1 published the host once as app-it-host.exe and copied it per-app under a new filename. The embedded metadata stayed app-it-host, and the taskbar right-click reads the executable's FileDescription -- not the file name -- so it kept showing app-it-host.

Setting -p:AssemblyName="<App Name>" at publish fixes it: the .csproj sets neither AssemblyTitle nor Product, so that one flag cascades into FileDescription + ProductName + OriginalFilename. Confirmed by inspecting VersionInfo on the built exe -- all three read the app name.

Because the version resource is baked at compile time, a single shared binary cannot carry per-app identity. Rewriting it post-publish would need an external resource editor (rcedit), which the no-dependencies contract rules out -- so the host is now published per app, cached per slug under assets\build\wrapper-windows\<slug>\ (mtime-aware). Trade vs the old single publish: one dotnet publish per app on first build. For the common 1-2 app case it is unchanged.

The shell-cache gotcha (worth a note for the next tester)

After rebuilding Vite Test.exe with correct metadata, the right-click still showed app-it-host. It looked like the fix had failed. It hadn't -- the Windows shell caches taskbar identity (label + icon) per executable, and we had launched many app-it-host-flavored Vite Test.exe builds earlier in the day.

Decisive test: I published a host with a name the shell had never seen (Zephyr Probe 7) and launched it. The right-click showed Zephyr Probe 7 immediately. So the fix is correct; the stale label was our own iterative-testing cache. A clean install shows the right name straight away.

Verification

  • VersionInfo.FileDescription / ProductName / OriginalFilename on the built <App Name>.exe all read the app name (was app-it-host).
  • Fresh-named host shows the correct label in the taskbar right-click on first launch.
  • Spaced -p:AssemblyName=$name survives native arg-passing under both pwsh 7 (Windows mode) and Windows PowerShell 5.1 (Legacy) -- the AssemblyName=$name token carries no literal space, so neither shell splits it. Tested both.
  • Full desktop-build.ps1 -> desktop-install.ps1 flow still builds, installs, and launches.
  • PSScriptAnalyzer clean against PSScriptAnalyzerSettings.psd1.

Notes

  • dotnet stdout is piped to Out-Host inside the new Publish-HostForApp function, so build-log lines cannot leak into the returned exe path (a PowerShell function-return gotcha I hit and fixed during testing).
  • A per-app publish failure falls that app back to the Edge launcher and flips later apps to edge too, preserving the original global-fallback intent.
  • Did not touch the .csproj: leaving AssemblyTitle/Product unset is exactly what makes the single -p:AssemblyName flag sufficient.

Test plan

  • Clean machine (or cleared icon cache): build + install a launcher, confirm the taskbar right-click shows the app name
  • Confirm an app name with a space embeds correctly (Get-Item <exe>).VersionInfo)
  • Confirm multi-app config publishes each host with its own identity

Summary by Sourcery

Publish the Windows WebView2 host per app so each generated launcher embeds the correct app name in its assembly metadata and shows the right label in the taskbar right-click menu.

Bug Fixes:

  • Correct the Windows launcher so the taskbar right-click menu and process name display the app name instead of the generic app-it-host by baking the app name into the host assembly metadata via per-app publishes.

Enhancements:

  • Introduce a per-app, mtime-aware publish-and-cache helper for the Windows WebView2 host that outputs an app-specific executable per slug while preserving the existing Edge fallback behavior.

Documentation:

  • Document the Windows fix in the changelog, including the move to per-app WebView2 host publishes and the reasoning behind the taskbar label correction.

…s it

The taskbar right-click menu showed "app-it-host" instead of the app
name (Christian-Katzmann#9). Windows reads that label from the executable's embedded
FileDescription, which the build-once-then-rename flow left at the
project default.

desktop-build.ps1 now publishes the WebView2 host per-app with
-p:AssemblyName="<App Name>". The .csproj sets neither AssemblyTitle
nor Product, so that one flag cascades into FileDescription +
ProductName + OriginalFilename, which all then read the app name.
Per-app exes are cached under assets\build\wrapper-windows\<slug>\
(mtime-aware); the trade vs the old single shared publish is one
dotnet publish per app on first build.

The per-app publish runs inside the build loop; a publish failure
falls that app back to the Edge launcher (and skips the host build
for later apps). dotnet stdout is piped to Out-Host so it cannot
pollute the function return value.

Validated on real Windows 11 hardware. Embedded metadata reads the
app name; a freshly-named host shows it in the taskbar right-click
immediately (an already-seen exe shows it after the shell cache
turns over). PSScriptAnalyzer clean against the repo settings.
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Jun 2, 2026

Reviewer's Guide

This PR changes the Windows desktop build script so that the WebView2 host executable is published per app with the app name baked into the assembly metadata, ensuring the taskbar label and right‑click menu show the app name instead of app-it-host, and documents the fix in the changelog.

File-Level Changes

Change Details Files
Publish the WebView2 host per app and bake the app name into the assembly metadata so taskbar UI shows the correct name.
  • Replace the prior single shared dotnet publish with per-app publishing, driven by the resolved wrapper .csproj and latest source mtime.
  • Introduce a Publish-HostForApp PowerShell function that publishes a self-contained single-file host to an app-specific cache directory using -p:AssemblyName=<App Name>, piping dotnet stdout to Out-Host to keep function output clean.
  • Cache each app's published executable under assets\build\wrapper-windows\<slug>\, reusing it unless host sources change.
  • Update the per-app build loop to call Publish-HostForApp, copy the resulting exe into the app directory as <App Name>.exe, and fall back to Edge mode for this and subsequent apps if publishing fails.
  • Clarify and expand comments explaining why per-app publishing is required (version resource baked at compile time, no external resource editor) and how taskbar labels map to FileDescription and related fields.
plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1
Document the Windows taskbar label fix in the changelog.
  • Add a changelog entry describing the bug where the taskbar right-click menu showed app-it-host and how per-app publishing with -p:AssemblyName fixes it.
  • Note the caching strategy and performance tradeoff, and that the behavior was validated on real Windows 11 hardware.
CHANGELOG.md

Assessment against linked issues

Issue Objective Addressed Explanation
#9 Update the Windows desktop build script so that the generated .exe embeds the app's name (via AssemblyName/FileDescription) instead of "app-it-host", ensuring the Windows taskbar right-click menu shows the app name.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • Publish-HostForApp relies on outer-scope variables ($Root, $csproj, $newestSrc), which makes the function harder to reason about and reuse; consider passing these as explicit parameters so the function has clearer dependencies.
  • The per-app caching logic assumes the published exe is named exactly "$AppName.exe" when checking $existing, but you already note that MSBuild may strip or alter unusual characters; in those cases you'll end up rebuilding on every run, so it may be worth deriving the cache key from the slug or reusing the same fallback (largest .exe) when deciding whether a rebuild is needed.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Publish-HostForApp relies on outer-scope variables ($Root, $csproj, $newestSrc), which makes the function harder to reason about and reuse; consider passing these as explicit parameters so the function has clearer dependencies.
- The per-app caching logic assumes the published exe is named exactly "$AppName.exe" when checking $existing, but you already note that MSBuild may strip or alter unusual characters; in those cases you'll end up rebuilding on every run, so it may be worth deriving the cache key from the slug or reusing the same fallback (largest .exe) when deciding whether a rebuild is needed.

## Individual Comments

### Comment 1
<location path="plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1" line_range="119-120" />
<code_context>
+# instead of "app-it-host" (issue #9). Cached per slug, mtime-aware. Returns the
+# published "<App Name>.exe" path, or $null on a build failure (the caller then
+# falls that app back to the Edge launcher).
+function Publish-HostForApp {
+    param([string]$AppName, [string]$Slug)
+
+    $appPublishDir = Join-Path $Root "assets\build\wrapper-windows\$Slug"
</code_context>
<issue_to_address>
**issue:** Consider sanitizing or validating `$AppName` before using it as `-p:AssemblyName` to avoid invalid filename/assembly characters breaking `dotnet publish`.

If `$AppName` includes characters invalid for assembly names/Windows filenames (`:`, `*`, `?`, `\`, `/`, etc.), `dotnet publish` can fail before the fallback logic runs. Consider either deriving `AssemblyName` from a sanitized slug and using `$AppName` only for user-facing text, or normalizing `$AppName` (e.g., stripping/replacing invalid chars) before passing it to `-p:AssemblyName`.
</issue_to_address>

### Comment 2
<location path="plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1" line_range="123-124" />
<code_context>
+    param([string]$AppName, [string]$Slug)
+
+    $appPublishDir = Join-Path $Root "assets\build\wrapper-windows\$Slug"
+    $existing = if (Test-Path $appPublishDir) {
+        Get-ChildItem -Path $appPublishDir -Filter "$AppName.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
+    } else { $null }
+    $needsBuild = (-not $existing) -or ($newestSrc -and $newestSrc.LastWriteTimeUtc -gt $existing.LastWriteTimeUtc)
</code_context>
<issue_to_address>
**suggestion:** Using `$AppName` inside `-Filter` can misbehave when the name contains wildcard characters; consider a more literal lookup.

Because `-Filter` does wildcard matching, names containing `*`, `?`, `[` or `]` are treated as patterns, which can cause incorrect matches when checking for an existing exe. Prefer a literal lookup, e.g. `Get-Item` on the full path (with `-ErrorAction SilentlyContinue`), or `Get-ChildItem -LiteralPath` instead of `-Filter` to avoid this issue.
</issue_to_address>

### Comment 3
<location path="plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1" line_range="146-122" />
<code_context>
+        if ($LASTEXITCODE -ne 0) { return $null }
+    }
+
+    $exe = Get-ChildItem -Path $appPublishDir -Filter "$AppName.exe" -ErrorAction SilentlyContinue |
+        Select-Object -First 1 -ExpandProperty FullName
+    if (-not $exe) {
+        # Name didn't map 1:1 (MSBuild stripped unusual chars) - take the
</code_context>
<issue_to_address>
**suggestion (performance):** If the actual exe name diverges from `$AppName.exe`, the cache check will never hit and each run will keep rebuilding that app.

You already fall back to the largest `.exe` when `$AppName.exe` doesn’t exist, but the cache check still only looks for `$AppName.exe`, so `$needsBuild` stays `true` on every run. Consider basing the cache check on the resolved exe path (e.g., any `.exe` in that directory with an mtime check) or persisting the resolved exe name and reusing it for subsequent runs.

Suggested implementation:

```
    $exe = Get-ChildItem -Path $appPublishDir -Filter "$AppName.exe" -ErrorAction SilentlyContinue |
        Select-Object -First 1 -ExpandProperty FullName
    if (-not $exe) {
        # Name didn't map 1:1 (MSBuild stripped unusual chars) - take the
        # largest .exe as the host.
        $exe = Get-ChildItem -Path $appPublishDir -Filter *.exe -ErrorAction SilentlyContinue |
            Sort-Object Length -Descending | Select-Object -First 1 -ExpandProperty FullName
    }

    if ($exe) {
        # Persist the resolved exe name so cache checks can reuse the actual host
        # file name even when it diverges from "$AppName.exe".
        $resolvedExeNamePath = Join-Path $appPublishDir '.resolved-exe'
        (Split-Path -Path $exe -Leaf) | Set-Content -Path $resolvedExeNamePath -Encoding ASCII
    }

    return $exe

```

To fully implement the caching suggestion, also update the earlier cache check logic in this script:

1. Before deciding whether `$needsBuild` is true, look for the `.resolved-exe` file in `$appPublishDir`. If it exists, read the exe name from it and construct the full path (e.g., `$resolvedExe = Join-Path $appPublishDir (Get-Content $resolvedExeNamePath)`), then use that path for the timestamp/exists check instead of hard-coding `$AppName.exe`.
2. If `.resolved-exe` does not exist yet, fall back to the old behavior (checking `$AppName.exe`) or to a wildcard `.exe` probe similar to the code above, then use that resolved path for the cache decision.
3. Ensure any logic that currently assumes the host exe is `$AppName.exe` (for cache hit, `Test-Path`, `Get-Item`, etc.) is switched to use the resolved exe path so that once the name divergence is detected and persisted, subsequent runs will hit the cache correctly.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1 Outdated
Comment thread plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1 Outdated
Comment thread plugins/app-it-windows/skills/app-it-windows/templates/desktop-build.ps1 Outdated
Reworks the Christian-Katzmann#9 fix in response to PR review (sourcery-ai):

- Publish with -p:AssemblyName=<slug> (always [a-z0-9-], a valid
  assembly/file name) and -p:AssemblyTitle/-p:Product=<App Name>.
  The slug drives the published file name, so the per-app cache
  lookup is exact - no MSBuild char-stripping to second-guess
  (the prior code keyed the cache on "<App Name>.exe" and would
  rebuild every run if MSBuild altered the name). The free-form
  app name now only feeds FileDescription/ProductName, the fields
  the taskbar reads, so it never has to be a valid file name -
  removing the invalid-char failure mode for -p:AssemblyName.
- Pass build inputs (csproj, repo root, newest-source mtime) into
  Publish-HostForApp as parameters instead of reading script scope.
- Use Get-Item -LiteralPath for the cache probe.

Verified on Windows 11: a fresh-named app shows its name in the
taskbar right-click on first launch; FileDescription/ProductName
read the app name, OriginalFilename is the slug; a second build
hits the cache (no republish). PSScriptAnalyzer clean.
@Firnschnee
Copy link
Copy Markdown
Contributor Author

Thanks @sourcery-ai - the feedback hit one real weak point: the app name was doing triple duty as assembly name, file name, and cache key. Fixed by splitting identity from the file name:

-p:AssemblyName=<slug> (always [a-z0-9-]) drives the file name, while -p:AssemblyTitle/-p:Product=<App Name> set the FileDescription/ProductName the taskbar reads. That resolves both the invalid-char risk in AssemblyName (comment 1) and the cache miss on name divergence (comment 3) - the cache now keys on <slug>.exe, which MSBuild can't alter. Also passed build inputs into Publish-HostForApp as parameters (high-level 1).

Re-validated on Windows 11: fresh-named app shows its name in the taskbar right-click on first launch.

@Christian-Katzmann Christian-Katzmann merged commit b959010 into Christian-Katzmann:main Jun 4, 2026
4 checks passed
Christian-Katzmann added a commit that referenced this pull request Jun 4, 2026
…-runtime fixes (#17, #18)

He went from first real-hardware validator to the Windows beta's recurring
real-hardware tester — three fixes now, each traced and verified on Windows 11
(window title #8, taskbar right-click app name #17, WebView2-runtime-missing
path #18). Refresh his existing nudge line to reflect that arc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Christian-Katzmann
Copy link
Copy Markdown
Owner

Merged — thank you, @Firnschnee. The field-tracing here was the difference: pinning down that the taskbar right-click reads the executable's embedded FileDescription rather than the filename, then splitting identity from the file name (AssemblyName=<slug> for an exact, always-valid cache key; AssemblyTitle/Product for the label the shell actually shows). And the Zephyr Probe 7 test — publishing a name the shell had never cached, to prove the fix was real and not a stale label — is exactly the check this needed.

You're now credited in the README's Community nudge as the Windows beta's real-hardware tester. Much appreciated.

Christian-Katzmann added a commit that referenced this pull request Jun 4, 2026
…e landed (#17, #18)

The recruitment paragraph still framed #8 as the single real-hardware
milestone ("first run … window-title bug"). It's had a run of fixes since
(#17 taskbar app name, #18 WebView2-runtime-missing path), so update it to
"a run of fixes since — a real start, not a finish line." No overclaim — it
still says "needing more," the author still runs only macOS, and the concrete
fix-by-fix list stays in the README Community nudge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

fix(windows): embed correct AssemblyName / FileDescription so taskbar right-click shows the app name

2 participants