# Dragon-ISO Installer Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Rebrand the WiX v5 MSI installer from TeamsISO to Dragon-ISO, producing `Dragon-ISO-Setup-1.0.0.0.msi` for end-user download.

**Architecture:** Rename the `.wixproj` file, rewrite `Package.wxs` with Dragon-ISO branding and a simple `WixUI_Minimal` UI (no directory picker), and fix a bug in `release.yml` where the signing step references the wrong executable filename.

**Tech Stack:** WiX Toolset v5, MSBuild, PowerShell; no unit tests (installer files are build-verified by `dotnet build`)

---

## File Map

| File | Action | Responsibility |
|---|---|---|
| `installer/TeamsISO.Installer.wixproj` | Rename + rewrite | MSBuild project — output name, publish dir, asset dir |
| `installer/Dragon-ISO.Installer.wixproj` | Created by rename | Same as above, Dragon-ISO branded |
| `installer/Package.wxs` | Rewrite | WiX source — all installer logic, shortcuts, metadata |
| `.forgejo/workflows/release.yml` | Fix line 119 | Fix `Dragon-ISO.exe` → `DragonISO.exe` (exe filename matches AssemblyName) |

> **Important:** `Dragon-ISO.App.csproj` has `<AssemblyName>DragonISO</AssemblyName>` (no hyphen). The published executable is therefore `DragonISO.exe`, not `Dragon-ISO.exe`. All shortcut targets and signing steps must use `DragonISO.exe`.

---

## Task 1: Rename the .wixproj file

**Files:**
- Rename: `installer/TeamsISO.Installer.wixproj` → `installer/Dragon-ISO.Installer.wixproj`

- [ ] **Step 1: Rename the file using git mv**

```powershell
cd C:\Users\zacga\source\repos\Dragon-ISO
git mv installer/TeamsISO.Installer.wixproj installer/Dragon-ISO.Installer.wixproj
```

- [ ] **Step 2: Verify the rename**

```powershell
Get-ChildItem installer/
```

Expected: `Dragon-ISO.Installer.wixproj` and `Package.wxs` (no `TeamsISO.Installer.wixproj`)

---

## Task 2: Rewrite Dragon-ISO.Installer.wixproj

**Files:**
- Modify: `installer/Dragon-ISO.Installer.wixproj`

- [ ] **Step 1: Replace the file content entirely**

Write the following to `installer/Dragon-ISO.Installer.wixproj`:

```xml
<Project Sdk="WixToolset.Sdk/5.0.2">

  <PropertyGroup>
    <OutputType>Package</OutputType>
    <OutputName>Dragon-ISO-Setup-$(Version)</OutputName>

    <!-- 64-bit MSI; suppresses ICE80 on components in Program Files (x64). -->
    <Platform>x64</Platform>
    <InstallerPlatform>x64</InstallerPlatform>

    <!--
      Built artifact location. The installer expects a published build of
      Dragon-ISO.App rooted here. CI / local script:
        dotnet publish src/Dragon-ISO.App/Dragon-ISO.App.csproj
            -c Release -r win-x64 --self-contained false
            -o $(SolutionDir)publish/Dragon-ISO
    -->
    <PublishDir>$(MSBuildThisFileDirectory)..\publish\Dragon-ISO\</PublishDir>

    <!-- Pass MSBuild values into WiX preprocessor. -->
    <DefineConstants>PublishDir=$(PublishDir);AssetsDir=$(MSBuildThisFileDirectory)..\src\Dragon-ISO.App\Assets\</DefineConstants>

    <!-- Code-signing hook (set externally for release builds; left empty for dev). -->
    <SignOutput Condition=" '$(SignOutput)' == '' ">false</SignOutput>
  </PropertyGroup>

  <!--
    Reference the WiX UI extension so the MSI shows a friendly progress UI
    instead of the silent default.
  -->
  <ItemGroup>
    <PackageReference Include="WixToolset.UI.wixext" Version="5.0.2" />
  </ItemGroup>

</Project>
```

- [ ] **Step 2: Verify the file reads back correctly**

```powershell
Get-Content installer/Dragon-ISO.Installer.wixproj | Select-String "OutputName|PublishDir|AssetsDir"
```

Expected output (3 lines):
```
    <OutputName>Dragon-ISO-Setup-$(Version)</OutputName>
    <PublishDir>$(MSBuildThisFileDirectory)..\publish\Dragon-ISO\</PublishDir>
    <DefineConstants>PublishDir=$(PublishDir);AssetsDir=$(MSBuildThisFileDirectory)..\src\Dragon-ISO.App\Assets\</DefineConstants>
```

---

## Task 3: Rewrite Package.wxs with Dragon-ISO branding

**Files:**
- Modify: `installer/Package.wxs`

Changes from the original TeamsISO version:
- Package Name: "TeamsISO" → "Dragon-ISO"
- SummaryInformation description and keywords updated
- MajorUpgrade error message updated
- Feature Title: "TeamsISO" → "Dragon-ISO"
- UI switched from `WixUI_InstallDir` (shows dir picker) → `WixUI_Minimal` (Welcome → Install → Finish)
- `WIXUI_INSTALLDIR` property removed (not used by WixUI_Minimal)
- ARPHELPLINK URL: teamsiso → dragon-iso
- ARPCOMMENTS: "TeamsISO" → "Dragon-ISO"
- Icon Id: "TeamsISOIcon" → "DragonISOIcon"
- Icon SourceFile: `teamsiso.ico` → `Dragon-ISO.ico`
- ARPPRODUCTICON value: "TeamsISOIcon" → "DragonISOIcon"
- Added .NET 8 Desktop Runtime detection property
- Install directory Name: "TeamsISO" → "Dragon-ISO"
- Start Menu shortcut Id/Name/Target/Icon updated
- Desktop shortcut Id/Name/Target/Icon updated
- All registry keys: `Software\Wild Dragon\TeamsISO` → `Software\Wild Dragon\Dragon-ISO`
- Shortcut targets: `TeamsISO.exe` → `DragonISO.exe` (matches AssemblyName, no hyphen)

- [ ] **Step 1: Replace Package.wxs entirely with Dragon-ISO branded content**

Write the following to `installer/Package.wxs`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
    Dragon-ISO — MSI installer (WiX v5)

    Produces: Dragon-ISO-Setup-<Version>.msi (per-machine install).

    Build:
        dotnet publish src/Dragon-ISO.App/Dragon-ISO.App.csproj -c Release -r win-x64 --self-contained false -o publish/Dragon-ISO
        dotnet build installer/Dragon-ISO.Installer.wixproj -c Release

    Runtime expectations:
        - .NET 8 Desktop runtime present on target (framework-dependent build)
        - NDI 6 Runtime present — checked in NdiRuntimeDirV6Search; absence WARNS
          but does not block install (operators can install NDI after the app)

    Exe filename note:
        Dragon-ISO.App.csproj sets AssemblyName=DragonISO (no hyphen — CLR
        assembly names cannot contain hyphens). The published executable is
        therefore DragonISO.exe. Shortcut targets reference DragonISO.exe.
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
     xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">

  <Package Name="Dragon-ISO"
           Manufacturer="Wild Dragon LLC"
           Version="1.0.0.0"
           UpgradeCode="9F4A8B2C-1D3E-4A5B-9C6D-8E7F0A1B2C3D"
           Scope="perMachine"
           Compressed="yes"
           InstallerVersion="500">

    <!--
        SummaryInformation fields surface in File Explorer's "Details" tab and
        in the Windows Installer "About" dialog. Description and Keywords are
        what users see if they right-click the MSI before installing; Comments
        is the longer copy that appears alongside the version in some
        installer dialogs.
    -->
    <SummaryInformation
        Description="Dragon-ISO — per-participant NDI ISO controller for Microsoft Teams. Splits each Teams participant into a normalized NDI source for vMix / OBS / Ross / hardware switchers."
        Manufacturer="Wild Dragon LLC"
        Keywords="Dragon-ISO, NDI, Microsoft Teams, ISO recording, broadcast, live production, vMix, OBS, switcher, Wild Dragon" />

    <!--
        MajorUpgrade: a newer install replaces an older one in-place. We
        disallow downgrades because the engine config schema only carries a
        forward-migration path; downgrading would leave operators with a
        config the older binary doesn't understand.
    -->
    <MajorUpgrade DowngradeErrorMessage="A newer version of Dragon-ISO is already installed. Uninstall it before installing this older version."
                  Schedule="afterInstallInitialize" />

    <!--
        Single MSI feature; users see only the install/uninstall screens.
    -->
    <Feature Id="Main" Title="Dragon-ISO" Level="1">
      <ComponentGroupRef Id="ApplicationFiles" />
      <ComponentGroupRef Id="Shortcuts" />
      <ComponentGroupRef Id="DesktopShortcut" />
      <ComponentGroupRef Id="ArpEntry" />
    </Feature>

    <!--
        Minimal install UI: Welcome/License → Progress → Finish.
        No directory picker — installs to Program Files\Wild Dragon\Dragon-ISO.
    -->
    <ui:WixUI Id="WixUI_Minimal" />

    <!--
        Add/Remove Programs metadata. ARPHELPLINK is the "Help" link; ARPURLINFOABOUT
        is the manufacturer/about link; ARPCONTACT is the support contact shown
        when the user clicks "Support information" from the ARP entry. ARPCOMMENTS
        is the long description displayed in some Settings → Apps surfaces.
    -->
    <Property Id="ARPHELPLINK" Value="https://forge.wilddragon.net/zgaetano/dragon-iso" />
    <Property Id="ARPURLINFOABOUT" Value="https://wilddragon.net" />
    <Property Id="ARPCONTACT" Value="Wild Dragon LLC — support@wilddragon.net" />
    <Property Id="ARPCOMMENTS" Value="Dragon-ISO turns Microsoft Teams' raw NDI broadcast into clean, normalized, per-participant NDI sources for ingestion by a live-production switcher (vMix, OBS, Ross, hardware capture). Each participant gets an individually-addressable source with configurable framerate, resolution, aspect mode, and audio routing." />
    <!-- ARPNOMODIFY is set by WixUI_Minimal; don't redeclare. -->
    <Property Id="ARPNOREPAIR" Value="1" />

    <!--
        ARP icon — references the same .ico the WPF host uses. WiX requires the
        icon resource to live next to the wxs OR be reachable at build time;
        we point at the source copy under src/Dragon-ISO.App/Assets so the icon
        embedded in the MSI matches the icon in the running exe.
    -->
    <Icon Id="DragonISOIcon" SourceFile="$(var.AssetsDir)Dragon-ISO.ico" />
    <Property Id="ARPPRODUCTICON" Value="DragonISOIcon" />

    <!--
        .NET 8 Desktop Runtime detection. The .NET apphost will surface a
        "framework not found" dialog naturally if the runtime is absent;
        this property is available for future conditional logic.
        VBScript-based install-time dialogs are deprecated in WiX v5 / Windows;
        rewriting in C++ is overkill for a soft warning on a soft dependency.
    -->
    <Property Id="DOTNET8DESKTOPRUNTIME" Value="0">
      <RegistrySearch Id="DotNet8DesktopRuntimeSearch"
                      Root="HKLM"
                      Key="SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedfx\Microsoft.WindowsDesktop.App"
                      Name="8.0.0"
                      Type="raw" />
    </Property>

    <!--
        NDI Runtime detection. We check for NDI_RUNTIME_DIR_V6 in the system
        environment block. Missing → warn during install, don't block. The
        engine surfaces a clear MessageBox with an install-NDI link at first
        launch if the runtime really isn't there.
    -->
    <Property Id="NDIRUNTIMEDIR" Value="0">
      <RegistrySearch Id="NdiRuntimeDirV6Search"
                      Root="HKLM"
                      Key="SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
                      Name="NDI_RUNTIME_DIR_V6"
                      Type="raw" />
    </Property>

    <!--
        NDI runtime detection is surfaced at first app launch (App.xaml.cs pops a
        MessageBox with an install link). We deliberately don't block install on
        a missing runtime so admins can stage the app before NDI is rolled out.
        VBScript-based install-time prompts are deprecated in WiX v5 / Windows
        and rewriting in C++ is overkill for a soft warning.
    -->

    <!--
        Install layout under Program Files\Wild Dragon\Dragon-ISO.
    -->
    <StandardDirectory Id="ProgramFiles64Folder">
      <Directory Id="ManufacturerFolder" Name="Wild Dragon">
        <Directory Id="INSTALLFOLDER" Name="Dragon-ISO" />
      </Directory>
    </StandardDirectory>

    <StandardDirectory Id="ProgramMenuFolder">
      <Directory Id="WildDragonStartMenuFolder" Name="Wild Dragon" />
    </StandardDirectory>

    <!--
        Files: harvested from the publish output dir at build time.
        WiX v5 understands <Files Include="..."> with glob patterns and
        synthesizes one Component per file with stable GUIDs.
    -->
    <ComponentGroup Id="ApplicationFiles" Directory="INSTALLFOLDER">
      <Files Include="$(var.PublishDir)**" />
    </ComponentGroup>

    <!--
        Start Menu and Desktop shortcuts — direct .exe targets.

        Don't wrap the Target in runas.exe /trustlevel:0x20000 (or anything
        else that demotes the spawned process). The SAFER-restricted token
        breaks .NET 8 WPF apphost startup: the process appears alive with
        a window, but no managed code past BAML parse executes. Verified
        empirically 2026-05-16 — letting Dragon-ISO inherit the launching
        token (medium or high integrity, doesn't matter) is the correct
        behavior. NDI discovery works fine at either integrity level.

        Exe filename: AssemblyName=DragonISO (no hyphen), so target is
        DragonISO.exe not Dragon-ISO.exe.
    -->
    <ComponentGroup Id="Shortcuts" Directory="WildDragonStartMenuFolder">
      <Component Id="StartMenuShortcut" Guid="*">
        <Shortcut Id="StartMenuDragonISO"
                  Name="Dragon-ISO"
                  Description="Per-Participant NDI ISO Controller for Microsoft Teams"
                  Target="[INSTALLFOLDER]DragonISO.exe"
                  WorkingDirectory="INSTALLFOLDER"
                  Icon="DragonISOIcon" />
        <!-- Required by ICE64: Start Menu folder must be cleaned on uninstall. -->
        <RemoveFolder Id="RemoveWildDragonStartMenuFolder"
                      Directory="WildDragonStartMenuFolder"
                      On="uninstall" />
        <RegistryValue Root="HKCU"
                       Key="Software\Wild Dragon\Dragon-ISO"
                       Name="StartMenuShortcut"
                       Type="integer"
                       Value="1"
                       KeyPath="yes" />
      </Component>
    </ComponentGroup>

    <StandardDirectory Id="DesktopFolder" />
    <ComponentGroup Id="DesktopShortcut" Directory="DesktopFolder">
      <Component Id="DesktopShortcutComponent" Guid="*">
        <Shortcut Id="DesktopDragonISO"
                  Name="Dragon-ISO"
                  Description="Per-Participant NDI ISO Controller for Microsoft Teams"
                  Target="[INSTALLFOLDER]DragonISO.exe"
                  WorkingDirectory="INSTALLFOLDER"
                  Icon="DragonISOIcon" />
        <RegistryValue Root="HKCU"
                       Key="Software\Wild Dragon\Dragon-ISO"
                       Name="DesktopShortcut"
                       Type="integer"
                       Value="1"
                       KeyPath="yes" />
      </Component>
    </ComponentGroup>

    <!--
        ARP icon registry entry. Optional — the MSI auto-fills most ARP
        fields from the Package element. We only need to store the install
        path for diagnostic / uninstall tooling.
    -->
    <ComponentGroup Id="ArpEntry" Directory="INSTALLFOLDER">
      <Component Id="ArpIconRegistry" Guid="*">
        <RegistryValue Root="HKLM"
                       Key="Software\Wild Dragon\Dragon-ISO"
                       Name="InstallPath"
                       Type="string"
                       Value="[INSTALLFOLDER]"
                       KeyPath="yes" />
      </Component>
    </ComponentGroup>

  </Package>
</Wix>
```

- [ ] **Step 2: Verify no "TeamsISO" strings remain in Package.wxs**

```powershell
Select-String -Path installer/Package.wxs -Pattern "TeamsISO"
```

Expected: no output (zero matches)

---

## Task 4: Fix release.yml — wrong exe filename in signing step

**Files:**
- Modify: `.forgejo/workflows/release.yml` line 119

The signing step references `publish/Dragon-ISO/Dragon-ISO.exe` but the app's `AssemblyName` is `DragonISO`, so the published exe is `DragonISO.exe`. Fix it.

- [ ] **Step 1: Edit release.yml to fix the exe path**

In `.forgejo/workflows/release.yml`, find and replace:

Old (line 119):
```
            'publish/Dragon-ISO/Dragon-ISO.exe'
```

New:
```
            'publish/Dragon-ISO/DragonISO.exe'
```

- [ ] **Step 2: Verify the fix**

```powershell
Select-String -Path .forgejo/workflows/release.yml -Pattern "Dragon-ISO\.exe|DragonISO\.exe"
```

Expected output:
```
.forgejo/workflows/release.yml:119:            'publish/Dragon-ISO/DragonISO.exe'
```

(One match, using `DragonISO.exe` with no hyphen)

---

## Task 5: Check WiX workload and verify build

**Files:** None (verification only)

- [ ] **Step 1: Check if WiX workload is installed**

```powershell
dotnet workload list
```

Expected: output includes `wix` in the list. If not installed, run:
```powershell
dotnet workload install wix
```

- [ ] **Step 2: Publish the app to the expected location**

```powershell
cd C:\Users\zacga\source\repos\Dragon-ISO
dotnet publish src/Dragon-ISO.App/Dragon-ISO.App.csproj `
  -c Release -r win-x64 --self-contained false `
  -o publish/Dragon-ISO
```

Expected: ends with `Build succeeded.` and creates `publish/Dragon-ISO/DragonISO.exe`

- [ ] **Step 3: Verify the exe filename in the publish output**

```powershell
Get-ChildItem publish/Dragon-ISO/ -Filter "*.exe"
```

Expected: one file named `DragonISO.exe` (confirms shortcut targets are correct)

- [ ] **Step 4: Build the MSI**

```powershell
dotnet build installer/Dragon-ISO.Installer.wixproj -c Release /p:Version=1.0.0.0
```

Expected: ends with `Build succeeded.` — no errors, no warnings.

- [ ] **Step 5: Verify the MSI was produced with the correct name**

```powershell
Get-ChildItem installer/bin -Recurse -Filter "*.msi"
```

Expected: one file named `Dragon-ISO-Setup-1.0.0.0.msi`

---

## Task 6: Commit all changes

- [ ] **Step 1: Stage the changed files**

```powershell
cd C:\Users\zacga\source\repos\Dragon-ISO
git add installer/Dragon-ISO.Installer.wixproj
git add installer/Package.wxs
git add .forgejo/workflows/release.yml
```

- [ ] **Step 2: Verify nothing unexpected is staged**

```powershell
git status
```

Expected staged files:
- `installer/Dragon-ISO.Installer.wixproj` (renamed from TeamsISO.Installer.wixproj)
- `installer/Package.wxs` (modified)
- `.forgejo/workflows/release.yml` (modified)

No other files should be staged.

- [ ] **Step 3: Commit**

```powershell
git commit -m "$(cat <<'EOF'
rebrand installer from TeamsISO to Dragon-ISO

- Rename TeamsISO.Installer.wixproj → Dragon-ISO.Installer.wixproj
- Update Package.wxs: product name, shortcuts, registry keys, ARP
  metadata, install directory, and icon all updated to Dragon-ISO
- Switch UI from WixUI_InstallDir to WixUI_Minimal (no dir picker)
- Add .NET 8 Desktop Runtime detection property
- Fix release.yml: signing step referenced Dragon-ISO.exe but
  AssemblyName=DragonISO so exe is DragonISO.exe (no hyphen)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
EOF
)"
```

---

## Testing Checklist (manual verification after install)

Once the MSI is built, install it on a test machine and verify:

- [ ] `Dragon-ISO-Setup-1.0.0.0.msi` installs without errors
- [ ] App installs to `C:\Program Files\Wild Dragon\Dragon-ISO\`
- [ ] `DragonISO.exe` is present in the install folder
- [ ] Start Menu shows `Wild Dragon → Dragon-ISO` shortcut with correct icon
- [ ] Desktop shows `Dragon-ISO` shortcut with correct icon
- [ ] Both shortcuts launch the app successfully
- [ ] Add/Remove Programs shows:
  - Name: Dragon-ISO
  - Publisher: Wild Dragon LLC
  - Version: 1.0.0.0
  - Help link: `https://forge.wilddragon.net/zgaetano/dragon-iso`
- [ ] Uninstall removes all files, shortcuts, and registry entries
- [ ] `%APPDATA%\Dragon-ISO\` (user config) is NOT removed on uninstall
