aspire update --self leaves stale .aspire-install.json sidecar; sidecar lies about install route
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
## Summary
`aspire update --self` overwrites the CLI binary in place but never rewrites the `.aspire-install.json` sidecar next to it. After any non-`script` install (e.g. `localhive`) is in-place self-updated to a release channel, the sidecar keeps reporting the old route forever, even though the binary on disk is now a release / daily / staging build.
## Repro
1. `./localhive.sh` — installs a locally-built CLI to `$HOME/.aspire/bin/aspire` and writes `{"source":"localhive"}` to `$HOME/.aspire/bin/.aspire-install.json` (`localhive.sh:474`).
2. From that CLI, run `aspire update --self` and pick any release channel (stable / daily / staging). `UpdateCommand.ExtractAndUpdateAsync` replaces the binary at the same path via `File.Copy(newExePath, targetExePath, overwrite: true)` and never touches the sidecar (`src/Aspire.Cli/Commands/UpdateCommand.cs:423-538`).
3. Inspect what's on disk.
## Expected
The sidecar reflects how the binary that's actually on disk got there. After a self-update from a release channel, the binary on disk is byte-identical to a binary that `get-aspire-cli.sh` would have placed there, so the sidecar should be `{"source":"script"}` — not the older route from the install that originally seeded the directory.
## Actual
Sidecar is stale: the file says `localhive` while the binary is a release / daily build that `localhive.sh` could never have produced. On a machine where I'd previously run `localhive.sh` and then `aspire update --self`'d off it:
```
$ stat -f "%Sm %N" ~/.aspire/bin/.aspire-install.json ~/.aspire/bin/aspire
May 15 15:21:58 2026 /Users/ankj/.aspire/bin/.aspire-install.json
May 17 11:21:46 2026 /Users/ankj/.aspire/bin/aspire
$ cat ~/.aspire/bin/.aspire-install.json
{"source":"localhive"}
$ ~/.aspire/bin/aspire --version
13.4.0-preview.1.26267.3+66902f57e5b26fdcc68042362ab5340bf559f8b6
```
`localhive.sh` only ever emits versions of the form `13.4.0-local.YYYYMMDD.tHHmmss` (`localhive.sh:213`), so a `preview.1..+` version on a `localhive`-routed binary is structurally impossible — the binary is the May-17 daily commit `66902f57`, the sidecar still claims the May-15 localhive identity.
## Why this matters beyond cosmetics
The `source` field in the sidecar isn't only a display label. `BundleService.ComputeDefaultExtractDir` and `CliPathHelper.GetAspireHomeDirectory` both branch on it to pick bundle extract dirs and Aspire-home (see `docs/specs/install-routes.md`). A stale `localhive` label on a release binary means downstream consumers of the sidecar treat a release install as a local-dev install for layout decisions, which can pick a different extract dir / hive than the route the binary actually came from. The route is supposed to be the source of truth for layout, and a self-update that leaves it lying breaks that contract.
## Design question for the fix
Self-update needs to think about what the sidecar should say after the operation. The minimal interpretation is "write `{"source":"script"}` on success, since the new binary came from the release CLI download URL — the same archive a script install would consume" — but there's also a broader question of whether self-update should refuse / behave differently for package-manager-owned routes (`winget`, `brew`, `dotnet-tool`) where the sidecar is owned by the package manager and shouldn't be silently rewritten by us. That overlaps with B4 in `route-crossing-analysis-2026-05-18.md` ("`aspire update --self` has no route guard and weak atomicity story").
## Context
- Tracking: #16737 (Aspire CLI acquisition coherence).
## Environment
- macOS, arm64
- Stale-sidecar binary at `~/.aspire/bin/aspire`: `13.4.0-preview.1.26267.3+66902f57e5b26fdcc68042362ab5340bf559f8b6`
Contributor guide
Assessment
This issue has not been assessed yet.