diff --git a/docs/superpowers/specs/2026-04-15-installer-download-mode-design.md b/docs/superpowers/specs/2026-04-15-installer-download-mode-design.md index 7277287..14c9110 100644 --- a/docs/superpowers/specs/2026-04-15-installer-download-mode-design.md +++ b/docs/superpowers/specs/2026-04-15-installer-download-mode-design.md @@ -37,8 +37,9 @@ works from inside a source checkout on a machine with the .NET SDK installed: `CreateShortcutsStep`, `RegisterServiceStep`) are fine to keep. The installer also contains a partial "Settings" window -(`Views/SettingsWindow.xaml`, `Views/SettingsViewModel.cs`) — that wiring will -be reused for the Manage-mode branch described below. +(`Views/SettingsWindow.xaml`, `Views/SettingsViewModel.cs`) — that wiring is +reused for the Config view shown on subsequent launches (see Mode detection +below). ## High-Level Design @@ -54,7 +55,8 @@ which is never public. The installer only needs to hit `releases/ClaudeDo`. **2) An installer rewrite** that replaces the three publish/deploy steps with a single `DownloadAndExtractStep`, detects existing installs via a marker -file, and branches into Install / Update / Manage modes. +file, and on subsequent launches checks the Gitea API for updates before +deciding whether to show the Update flow or jump straight to the Config view. ## Release Artifacts @@ -71,9 +73,10 @@ Decisions: - **One combined app+worker zip** (not two separate). Reasons: one download, one extract, guaranteed version-locked pair. - **Self-contained installer exe** — user does not need .NET installed. -- **App + Worker**: framework-dependent (`--self-contained false`), same as - today. This assumes the target machine has the .NET 8 Desktop Runtime. - *Open decision — see "Decisions to revisit".* +- **App + Worker: self-contained** (`--self-contained true`, `-r win-x64`). + Zero runtime dependency on the target machine, at the cost of a larger + download (~100 MB combined zip). Decision: acceptable trade-off — the + installer is one-click, not per-user-problem-to-debug. - **Checksums file** — plain text, one line per asset (` `), verified by installer before extract. @@ -98,8 +101,8 @@ File: `.gitea/workflows/release.yml` 1. Checkout 2. Setup .NET 8 SDK 3. Derive version from tag (`${{ gitea.ref_name }}` without the `v` prefix) - 4. `dotnet publish src/ClaudeDo.App -c Release -r win-x64 --self-contained false /p:Version=$VERSION -o out/app` - 5. `dotnet publish src/ClaudeDo.Worker -c Release -r win-x64 --self-contained false /p:Version=$VERSION -o out/worker` + 4. `dotnet publish src/ClaudeDo.App -c Release -r win-x64 --self-contained true /p:Version=$VERSION -o out/app` + 5. `dotnet publish src/ClaudeDo.Worker -c Release -r win-x64 --self-contained true /p:Version=$VERSION -o out/worker` 6. `dotnet publish src/ClaudeDo.Installer -c Release -r win-x64 --self-contained true /p:Version=$VERSION /p:PublishSingleFile=true -o out/installer` 7. Zip `out/app` + `out/worker` as `ClaudeDo--win-x64.zip` with `app/` and `worker/` as top-level dirs @@ -133,17 +136,45 @@ Written at the end of every successful install or update to The installer reads this on startup (from the default install dir, or a path supplied via CLI arg) to decide which mode to run in. -### Mode detection (`InstallModeDetector`) +### Launch flow (`InstallModeDetector`) -| `install.json` state | Mode | Wizard flow | -|-------------------------------------------|---------------------|-----------------------------------------------------| -| absent | **Install** | Welcome → Paths → UiSettings → Service → Install | -| present, version < installer's version | **Update** | Welcome (shows old→new) → Install (download + swap) | -| present, version == installer's version | **Manage** | Welcome → choose: Repair · Edit Config · Uninstall | -| present, version > installer's version | **Downgrade warn** | Welcome explains; user can cancel or force Install | +On every launch, the installer checks for `install.json` first: -The installer's own version comes from its assembly (`AssemblyInformational -Version`), set by the workflow's `/p:Version=$VERSION`. +``` +install.json absent? + -> Install mode: Welcome -> Paths -> UiSettings -> Service -> Install + (writes install.json at the end) + +install.json present? + -> Query https://git.kuns.dev/api/v1/repos/releases/ClaudeDo/releases/latest + (short timeout; if offline, treat as "no update available") + + latest.tag_name > installed.version + -> Update mode: Welcome ("Update v0.1.0 -> v0.2.0, Update / Later") + If user accepts -> Install steps (download + swap service) + If user declines -> fall through to Config view + latest.tag_name <= installed.version (or API unreachable) + -> Config view: directly open Paths/UiSettings/Service tabs, + prefilled from existing ~/.todo-app/*.json. + Action buttons: Save · Repair · Uninstall. +``` + +Key properties: + +- **First run = wizard**, as today — no behavior change for new users. +- **Every subsequent run = update check first**, then either offer update or + drop straight into Config. No "Manage page" with a menu of actions — the + Config view *is* the default, and Repair/Uninstall are buttons on it. +- **Offline / API error = not fatal**: if the release endpoint can't be + reached, the installer silently skips the update check and opens Config. + The user is never blocked from managing an existing install by a network + issue. +- **Downgrade** (installed version > latest) is treated the same as "no + update available" — we don't ever offer a downgrade. + +The installer's own version (shown for reference in Config) comes from its +assembly (`AssemblyInformationalVersion`), set by the workflow's +`/p:Version=$VERSION`. The *installed* version comes from `install.json`. ### New step: `DownloadAndExtractStep` @@ -178,15 +209,25 @@ naturally non-destructive. - **Conditional:** `RegisterServiceStep` only if service is not currently registered (covers someone who unregistered it manually) -### Manage mode — branches +### Config view — actions -- **Repair:** re-download + extract (same as Update), re-create shortcuts, - re-register service. Leaves config/DB alone. -- **Edit Config:** opens Paths / UiSettings / Service pages prefilled from - existing `~/.todo-app/*.json`. Save writes the JSON files and offers - "Restart service" if worker config changed. No download. -- **Uninstall:** stops + unregisters service, removes shortcuts, deletes - `InstallDir`. Asks separately whether to delete `~/.todo-app` (config + DB). +- **Save** (primary): writes the Paths / UiSettings / Service fields to + `~/.todo-app/*.json`. If worker config changed, prompts "Restart service?" + and calls `sc stop` / `sc start` if accepted. No download. +- **Repair:** re-download + extract (same as Update flow), re-create + shortcuts, re-register service. Leaves config/DB alone. Confirmation + dialog before starting. +- **Uninstall:** confirmation dialog ("This removes ClaudeDo *and* all of + your tasks, config, and database. Type UNINSTALL to confirm."). On + confirm: + 1. Stop + unregister service (`sc stop`, `sc delete ClaudeDoWorker`) + 2. Remove Start Menu / Desktop shortcuts + 3. Delete `{InstallDir}` (including `install.json`) + 4. Delete `~/.todo-app` in full (config + DB + logs) + 5. Exit + + Everything is removed. No "keep my data" option — that was explicitly + declined in the design discussion. ### Files to add @@ -245,34 +286,31 @@ an install in a half-deleted state. ## Decisions to Revisit -1. **Framework-dependent vs self-contained App/Worker.** Framework-dependent - keeps the zip small (~10 MB) but requires the user to install the .NET 8 - Desktop Runtime separately. Self-contained is ~80 MB per process but - zero-dependency. Current plan: framework-dependent, matches today's - behavior. If install friction becomes a problem, flip the flag in the - workflow and installer messaging. - -2. **Release notes content.** Auto-generated `git log` summary vs manual +1. **Release notes content.** Auto-generated `git log` summary vs manual notes in the tag message vs empty. Start empty; revisit when there are enough releases to care. -3. **Signed installer.** Out of scope for v1. Users will see a SmartScreen +2. **Signed installer.** Out of scope for v1. Users will see a SmartScreen warning the first time. Note this in the README. -4. **Installer distribution page.** A simple `README.md` badge or a pinned +3. **Installer distribution page.** A simple `README.md` badge or a pinned "Latest release" link on the Gitea repo home is enough for v1. ## Success Criteria -- On a fresh Windows VM with no source checkout and no .NET SDK: +- On a fresh Windows VM with **no source checkout, no .NET runtime, and no + .NET SDK**: 1. Download `ClaudeDo.Installer-.exe`. 2. Run it. 3. Complete the wizard. 4. ClaudeDo App launches, Worker service is running, a task can be created and picked up. -- Running the same installer a second time shows Manage mode. +- Running the same installer a second time, with no new release published, + opens directly in the Config view after a quick update check. - Publishing a new tag, then running the installer on the existing install, - performs an update without touching `~/.todo-app/todo.db` or the config - JSONs. + offers the update; accepting performs it without touching `~/.todo-app/todo.db` + or the config JSONs. +- Uninstall leaves no trace: `{InstallDir}` gone, `~/.todo-app` gone, service + unregistered, shortcuts removed. - The entire release pipeline runs on `git.kuns.dev` with no manual steps beyond `git tag vX.Y.Z && git push --tags`.