max / audiofiles
6 files changed,
+267 insertions,
-61 deletions
| @@ -0,0 +1,59 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <!-- | |
| 3 | + | AudioFiles MSI source. Consumed by build-msi-native.ps1 (windows-x86, native | |
| 4 | + | WiX toolset) and build-msi.sh (macOS/Linux fallback via msitools wixl). | |
| 5 | + | ||
| 6 | + | Tokens replaced at build time: | |
| 7 | + | @VERSION@ — semver from crates/audiofiles-app/Cargo.toml, with ".0" suffix | |
| 8 | + | (WiX requires four-part version) | |
| 9 | + | --> | |
| 10 | + | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> | |
| 11 | + | <Product Id="*" | |
| 12 | + | Name="AudioFiles" | |
| 13 | + | Language="1033" | |
| 14 | + | Version="@VERSION@" | |
| 15 | + | Manufacturer="Maxwell Johnson" | |
| 16 | + | UpgradeCode="8B4D5A2E-7F3C-4E1A-9D6B-2C8A4F5E7D9B"> | |
| 17 | + | ||
| 18 | + | <Package InstallerVersion="200" | |
| 19 | + | Compressed="yes" | |
| 20 | + | InstallScope="perUser" | |
| 21 | + | Description="AudioFiles @VERSION@" | |
| 22 | + | Comments="Sample manager with content-addressed storage" /> | |
| 23 | + | ||
| 24 | + | <MajorUpgrade DowngradeErrorMessage="A newer version is already installed." /> | |
| 25 | + | <MediaTemplate EmbedCab="yes" /> | |
| 26 | + | ||
| 27 | + | <Directory Id="TARGETDIR" Name="SourceDir"> | |
| 28 | + | <Directory Id="LocalAppDataFolder"> | |
| 29 | + | <Directory Id="INSTALLFOLDER" Name="AudioFiles"> | |
| 30 | + | <Component Id="MainExecutable" Guid="*"> | |
| 31 | + | <File Id="AudioFilesExe" | |
| 32 | + | Source="AudioFiles.exe" | |
| 33 | + | KeyPath="yes" /> | |
| 34 | + | </Component> | |
| 35 | + | </Directory> | |
| 36 | + | </Directory> | |
| 37 | + | <Directory Id="ProgramMenuFolder"> | |
| 38 | + | <Component Id="StartMenuShortcut" Guid="*"> | |
| 39 | + | <Shortcut Id="AudioFilesShortcut" | |
| 40 | + | Name="AudioFiles" | |
| 41 | + | Target="[INSTALLFOLDER]AudioFiles.exe" | |
| 42 | + | WorkingDirectory="INSTALLFOLDER" /> | |
| 43 | + | <RegistryValue Root="HKCU" | |
| 44 | + | Key="Software\AudioFiles" | |
| 45 | + | Name="installed" | |
| 46 | + | Type="integer" | |
| 47 | + | Value="1" | |
| 48 | + | KeyPath="yes" /> | |
| 49 | + | <RemoveFolder Id="RemoveStartMenu" On="uninstall" /> | |
| 50 | + | </Component> | |
| 51 | + | </Directory> | |
| 52 | + | </Directory> | |
| 53 | + | ||
| 54 | + | <Feature Id="Complete" Title="AudioFiles" Level="1"> | |
| 55 | + | <ComponentRef Id="MainExecutable" /> | |
| 56 | + | <ComponentRef Id="StartMenuShortcut" /> | |
| 57 | + | </Feature> | |
| 58 | + | </Product> | |
| 59 | + | </Wix> |
| @@ -0,0 +1,132 @@ | |||
| 1 | + | <# | |
| 2 | + | .SYNOPSIS | |
| 3 | + | Build AudioFiles MSI natively on windows-x86 (per the native-builds-per-arch | |
| 4 | + | policy). Also produces a matching standalone .exe. | |
| 5 | + | ||
| 6 | + | .DESCRIPTION | |
| 7 | + | Runs on the windows-x86 build machine. Uses the WiX Toolset (candle.exe, | |
| 8 | + | light.exe) — install from https://wixtoolset.org/ or `winget install | |
| 9 | + | WiXToolset.WiXToolset`. | |
| 10 | + | ||
| 11 | + | Code signing is opt-in via environment variables. If unset, the script | |
| 12 | + | produces an unsigned MSI/EXE (current state — Azure certificate blocker | |
| 13 | + | documented in docs/deploy.md). | |
| 14 | + | ||
| 15 | + | .EXAMPLE | |
| 16 | + | # From repo root: | |
| 17 | + | PS> pwsh -File dist\build-msi-native.ps1 | |
| 18 | + | ||
| 19 | + | .ENVIRONMENT | |
| 20 | + | AF_SIGN_CERT Path to .pfx or thumbprint of an installed cert. | |
| 21 | + | When set, MSI and EXE are signed via signtool.exe. | |
| 22 | + | AF_SIGN_PASS Password for the .pfx (skip if cert is in store). | |
| 23 | + | AF_SIGN_TIMESTAMP_URL RFC 3161 timestamp URL. | |
| 24 | + | Default: http://timestamp.digicert.com | |
| 25 | + | #> | |
| 26 | + | ||
| 27 | + | $ErrorActionPreference = 'Stop' | |
| 28 | + | ||
| 29 | + | $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path | |
| 30 | + | $ProjectDir = Resolve-Path (Join-Path $ScriptDir '..') | |
| 31 | + | $DistDir = $ScriptDir | |
| 32 | + | $Target = 'x86_64-pc-windows-msvc' | |
| 33 | + | ||
| 34 | + | # Read version from workspace Cargo.toml (workspace.package.version) or app crate. | |
| 35 | + | $AppCargo = Join-Path $ProjectDir 'crates\audiofiles-app\Cargo.toml' | |
| 36 | + | $VersionLine = Select-String -Path $AppCargo -Pattern '^version\s*=\s*"([^"]+)"' | Select-Object -First 1 | |
| 37 | + | if (-not $VersionLine) { throw "Could not parse version from $AppCargo" } | |
| 38 | + | $Version = $VersionLine.Matches[0].Groups[1].Value | |
| 39 | + | $WixVersion = "$Version.0" # WiX requires four-part version | |
| 40 | + | Write-Host "Building AudioFiles v$Version MSI ($Target)" -ForegroundColor Cyan | |
| 41 | + | ||
| 42 | + | # Prerequisites | |
| 43 | + | foreach ($tool in @('candle.exe', 'light.exe')) { | |
| 44 | + | if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) { | |
| 45 | + | throw "$tool not on PATH. Install WiX Toolset from https://wixtoolset.org/" | |
| 46 | + | } | |
| 47 | + | } | |
| 48 | + | $installedTargets = & rustup target list --installed | |
| 49 | + | if ($installedTargets -notcontains $Target) { | |
| 50 | + | throw "Rust target $Target not installed. Run: rustup target add $Target" | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | # Step 1: Native release build | |
| 54 | + | Write-Host "==> Building release binary..." -ForegroundColor Cyan | |
| 55 | + | Push-Location $ProjectDir | |
| 56 | + | try { | |
| 57 | + | & cargo build --release -p audiofiles-app --target $Target | |
| 58 | + | if ($LASTEXITCODE -ne 0) { throw "cargo build failed (exit $LASTEXITCODE)" } | |
| 59 | + | } finally { | |
| 60 | + | Pop-Location | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | $ExeSrc = Join-Path $ProjectDir "target\$Target\release\audiofiles-app.exe" | |
| 64 | + | if (-not (Test-Path $ExeSrc)) { throw "Build artifact missing: $ExeSrc" } | |
| 65 | + | ||
| 66 | + | # Step 2: Stage files | |
| 67 | + | Write-Host "==> Staging files..." -ForegroundColor Cyan | |
| 68 | + | $Staging = Join-Path $DistDir '.msi-staging' | |
| 69 | + | if (Test-Path $Staging) { Remove-Item -Recurse -Force $Staging } | |
| 70 | + | New-Item -ItemType Directory -Path $Staging | Out-Null | |
| 71 | + | Copy-Item $ExeSrc (Join-Path $Staging 'AudioFiles.exe') | |
| 72 | + | Copy-Item (Join-Path $DistDir 'audiofiles.png') (Join-Path $Staging 'audiofiles.png') | |
| 73 | + | ||
| 74 | + | # Optional: sign the EXE before embedding it in the MSI so it's signed in-place. | |
| 75 | + | function Invoke-Signtool { | |
| 76 | + | param([string]$Path) | |
| 77 | + | if (-not $env:AF_SIGN_CERT) { return $false } | |
| 78 | + | $signtool = Get-Command 'signtool.exe' -ErrorAction SilentlyContinue | |
| 79 | + | if (-not $signtool) { throw "AF_SIGN_CERT set but signtool.exe not on PATH" } | |
| 80 | + | $timestampUrl = if ($env:AF_SIGN_TIMESTAMP_URL) { $env:AF_SIGN_TIMESTAMP_URL } else { 'http://timestamp.digicert.com' } | |
| 81 | + | $args = @('sign', '/fd', 'SHA256', '/tr', $timestampUrl, '/td', 'SHA256') | |
| 82 | + | if ($env:AF_SIGN_CERT -match '\.pfx$') { | |
| 83 | + | $args += @('/f', $env:AF_SIGN_CERT) | |
| 84 | + | if ($env:AF_SIGN_PASS) { $args += @('/p', $env:AF_SIGN_PASS) } | |
| 85 | + | } else { | |
| 86 | + | # Treat as SHA1 thumbprint of a cert in the local store. | |
| 87 | + | $args += @('/sha1', $env:AF_SIGN_CERT) | |
| 88 | + | } | |
| 89 | + | $args += $Path | |
| 90 | + | Write-Host "==> Signing $Path" -ForegroundColor Cyan | |
| 91 | + | & $signtool.Source @args | |
| 92 | + | if ($LASTEXITCODE -ne 0) { throw "signtool failed on $Path (exit $LASTEXITCODE)" } | |
| 93 | + | return $true | |
| 94 | + | } | |
| 95 | + | ||
| 96 | + | $StagedExe = Join-Path $Staging 'AudioFiles.exe' | |
| 97 | + | $signed = Invoke-Signtool -Path $StagedExe | |
| 98 | + | if (-not $signed) { | |
| 99 | + | Write-Host "==> AF_SIGN_CERT unset — producing UNSIGNED build (see docs/deploy.md)" -ForegroundColor Yellow | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | # Step 3: Generate WiX source from template | |
| 103 | + | $WxsTemplate = Join-Path $DistDir 'audiofiles.wxs.in' | |
| 104 | + | $Wxs = Join-Path $Staging 'audiofiles.wxs' | |
| 105 | + | (Get-Content -Raw $WxsTemplate).Replace('@VERSION@', $WixVersion) | Set-Content -Path $Wxs -Encoding utf8 | |
| 106 | + | ||
| 107 | + | # Step 4: candle + light → MSI | |
| 108 | + | Write-Host "==> Building MSI..." -ForegroundColor Cyan | |
| 109 | + | $MsiName = "AudioFiles_${Version}_x86_64.msi" | |
| 110 | + | $MsiPath = Join-Path $DistDir $MsiName | |
| 111 | + | if (Test-Path $MsiPath) { Remove-Item -Force $MsiPath } | |
| 112 | + | ||
| 113 | + | $Wixobj = Join-Path $Staging 'audiofiles.wixobj' | |
| 114 | + | & candle.exe -nologo -out $Wixobj $Wxs | |
| 115 | + | if ($LASTEXITCODE -ne 0) { throw "candle.exe failed (exit $LASTEXITCODE)" } | |
| 116 | + | & light.exe -nologo -b $Staging -out $MsiPath $Wixobj | |
| 117 | + | if ($LASTEXITCODE -ne 0) { throw "light.exe failed (exit $LASTEXITCODE)" } | |
| 118 | + | ||
| 119 | + | # Step 5: Sign the MSI (if signing is configured) | |
| 120 | + | Invoke-Signtool -Path $MsiPath | Out-Null | |
| 121 | + | ||
| 122 | + | # Step 6: Cleanup + ship the standalone .exe alongside | |
| 123 | + | Remove-Item -Recurse -Force $Staging | |
| 124 | + | $ExeDest = Join-Path $DistDir "AudioFiles_${Version}_x86_64.exe" | |
| 125 | + | Copy-Item $ExeSrc $ExeDest | |
| 126 | + | # Re-sign the standalone exe (the one we signed earlier was the staged copy). | |
| 127 | + | Invoke-Signtool -Path $ExeDest | Out-Null | |
| 128 | + | ||
| 129 | + | Write-Host "" | |
| 130 | + | Write-Host "Done:" -ForegroundColor Green | |
| 131 | + | Write-Host " MSI: $MsiPath" | |
| 132 | + | Write-Host " EXE: $ExeDest" |
| @@ -45,61 +45,16 @@ mkdir -p "$STAGING" | |||
| 45 | 45 | cp "$EXE_SRC" "$STAGING/AudioFiles.exe" | |
| 46 | 46 | cp "$DIST_DIR/audiofiles.png" "$STAGING/audiofiles.png" | |
| 47 | 47 | ||
| 48 | - | # Step 3: Generate WiX source | |
| 48 | + | # Step 3: Generate WiX source from the shared template | |
| 49 | + | # (canonical builder is dist/build-msi-native.ps1 on windows-x86; this script | |
| 50 | + | # is a macOS/Linux cross-compile fallback) | |
| 51 | + | WXS_TEMPLATE="$DIST_DIR/audiofiles.wxs.in" | |
| 52 | + | if [ ! -f "$WXS_TEMPLATE" ]; then | |
| 53 | + | echo "Error: WiX template missing: $WXS_TEMPLATE" | |
| 54 | + | exit 1 | |
| 55 | + | fi | |
| 49 | 56 | WXS="$STAGING/audiofiles.wxs" | |
| 50 | - | cat > "$WXS" << WXS_EOF | |
| 51 | - | <?xml version="1.0" encoding="UTF-8"?> | |
| 52 | - | <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> | |
| 53 | - | <Product Id="*" | |
| 54 | - | Name="AudioFiles" | |
| 55 | - | Language="1033" | |
| 56 | - | Version="${VERSION}.0" | |
| 57 | - | Manufacturer="Maxwell Johnson" | |
| 58 | - | UpgradeCode="8B4D5A2E-7F3C-4E1A-9D6B-2C8A4F5E7D9B"> | |
| 59 | - | ||
| 60 | - | <Package InstallerVersion="200" | |
| 61 | - | Compressed="yes" | |
| 62 | - | InstallScope="perUser" | |
| 63 | - | Description="AudioFiles ${VERSION}" | |
| 64 | - | Comments="Sample manager with content-addressed storage" /> | |
| 65 | - | ||
| 66 | - | <MajorUpgrade DowngradeErrorMessage="A newer version is already installed." /> | |
| 67 | - | <MediaTemplate EmbedCab="yes" /> | |
| 68 | - | ||
| 69 | - | <Directory Id="TARGETDIR" Name="SourceDir"> | |
| 70 | - | <Directory Id="LocalAppDataFolder"> | |
| 71 | - | <Directory Id="INSTALLFOLDER" Name="AudioFiles"> | |
| 72 | - | <Component Id="MainExecutable" Guid="*"> | |
| 73 | - | <File Id="AudioFilesExe" | |
| 74 | - | Source="AudioFiles.exe" | |
| 75 | - | KeyPath="yes" /> | |
| 76 | - | </Component> | |
| 77 | - | </Directory> | |
| 78 | - | </Directory> | |
| 79 | - | <Directory Id="ProgramMenuFolder"> | |
| 80 | - | <Component Id="StartMenuShortcut" Guid="*"> | |
| 81 | - | <Shortcut Id="AudioFilesShortcut" | |
| 82 | - | Name="AudioFiles" | |
| 83 | - | Target="[INSTALLFOLDER]AudioFiles.exe" | |
| 84 | - | WorkingDirectory="INSTALLFOLDER" /> | |
| 85 | - | <RegistryValue Root="HKCU" | |
| 86 | - | Key="Software\\AudioFiles" | |
| 87 | - | Name="installed" | |
| 88 | - | Type="integer" | |
| 89 | - | Value="1" | |
| 90 | - | KeyPath="yes" /> | |
| 91 | - | <RemoveFolder Id="RemoveStartMenu" On="uninstall" /> | |
| 92 | - | </Component> | |
| 93 | - | </Directory> | |
| 94 | - | </Directory> | |
| 95 | - | ||
| 96 | - | <Feature Id="Complete" Title="AudioFiles" Level="1"> | |
| 97 | - | <ComponentRef Id="MainExecutable" /> | |
| 98 | - | <ComponentRef Id="StartMenuShortcut" /> | |
| 99 | - | </Feature> | |
| 100 | - | </Product> | |
| 101 | - | </Wix> | |
| 102 | - | WXS_EOF | |
| 57 | + | sed "s/@VERSION@/${VERSION}.0/g" "$WXS_TEMPLATE" > "$WXS" | |
| 103 | 58 | ||
| 104 | 59 | # Step 4: Build MSI | |
| 105 | 60 | echo "==> Building MSI..." |
| @@ -9,7 +9,7 @@ See `_meta/docs/deploy.md` for shared infrastructure (machines, git remotes, col | |||
| 9 | 9 | | macOS aarch64 | .dmg | local | | |
| 10 | 10 | | Linux aarch64 | AppImage | astra | | |
| 11 | 11 | | Linux x86_64 | AppImage | pop-os | | |
| 12 | - | | Windows x86_64 | standalone .exe | windows-x86 | | |
| 12 | + | | Windows x86_64 | .msi + standalone .exe | windows-x86 | | |
| 13 | 13 | ||
| 14 | 14 | ## Build Commands | |
| 15 | 15 | ||
| @@ -26,14 +26,42 @@ scp astra:~/Code/Apps/audiofiles/dist/AudioFiles-*-aarch64.AppImage ~/Dist/audio | |||
| 26 | 26 | ssh pop-os "source ~/.cargo/env && cd ~/Code/Apps/audiofiles && git pull && bash dist/build-appimage.sh" | |
| 27 | 27 | scp pop-os:~/Code/Apps/audiofiles/dist/AudioFiles-*-x86_64.AppImage ~/Dist/audiofiles/linux-x86_64/ | |
| 28 | 28 | ||
| 29 | - | # Windows (windows-x86): | |
| 30 | - | ssh me@windows-x86 "cd C:\Users\me\Code\Apps\audiofiles; git pull; cargo build --release -p audiofiles-app" | |
| 31 | - | scp me@windows-x86:"C:/Users/me/Code/Apps/audiofiles/target/release/audiofiles-app.exe" ~/Dist/audiofiles/windows/AudioFiles_VERSION_x64.exe | |
| 29 | + | # Windows (windows-x86) — native build + MSI installer via WiX: | |
| 30 | + | ssh me@windows-x86 "cd C:\Users\me\Code\Apps\audiofiles; git pull; pwsh -File dist\build-msi-native.ps1" | |
| 31 | + | scp me@windows-x86:"C:/Users/me/Code/Apps/audiofiles/dist/AudioFiles_VERSION_x86_64.msi" ~/Dist/audiofiles/windows/ | |
| 32 | + | scp me@windows-x86:"C:/Users/me/Code/Apps/audiofiles/dist/AudioFiles_VERSION_x86_64.exe" ~/Dist/audiofiles/windows/ | |
| 33 | + | ||
| 34 | + | # Emergency cross-compile fallback (macOS/Linux host, msitools wixl + cargo-xwin): | |
| 35 | + | bash dist/build-msi.sh | |
| 32 | 36 | ``` | |
| 33 | 37 | ||
| 38 | + | ## Windows Installer | |
| 39 | + | ||
| 40 | + | Canonical builder: `dist/build-msi-native.ps1` runs on **windows-x86** with the | |
| 41 | + | WiX Toolset (`winget install WiXToolset.WiXToolset`). It produces an MSI with | |
| 42 | + | Start Menu shortcut, per-user install scope, and major-upgrade behavior. | |
| 43 | + | ||
| 44 | + | WiX source lives in `dist/audiofiles.wxs.in` (shared by both builders). | |
| 45 | + | `@VERSION@` is substituted from `crates/audiofiles-app/Cargo.toml`. | |
| 46 | + | ||
| 47 | + | ### Code signing | |
| 48 | + | ||
| 49 | + | The PowerShell builder will sign the EXE and MSI with `signtool.exe` when these | |
| 50 | + | environment variables are set: | |
| 51 | + | ||
| 52 | + | | Var | Meaning | | |
| 53 | + | |-----|---------| | |
| 54 | + | | `AF_SIGN_CERT` | Path to a `.pfx`, OR SHA1 thumbprint of a cert in the local Windows cert store | | |
| 55 | + | | `AF_SIGN_PASS` | Password for the `.pfx` (omit if cert is in store) | | |
| 56 | + | | `AF_SIGN_TIMESTAMP_URL` | RFC 3161 timestamp URL (default: `http://timestamp.digicert.com`) | | |
| 57 | + | ||
| 58 | + | If `AF_SIGN_CERT` is unset, the build proceeds **unsigned** with a warning — | |
| 59 | + | the current state (code signing blocked on Azure certificate history | |
| 60 | + | requirement; see the GO deploy doc for context, same blocker applies). | |
| 61 | + | ||
| 34 | 62 | ## Project-Specific Notes | |
| 35 | 63 | ||
| 36 | - | - Native egui app (not Tauri). No Tauri CLI, Node.js, or .msi installer needed. | |
| 64 | + | - Native egui app (not Tauri). No Tauri CLI or Node.js needed. Windows ships both a standalone `.exe` and a WiX-built `.msi` installer (see § Windows Installer above). | |
| 37 | 65 | - No OTA updater — users download new versions from the MNW storefront. | |
| 38 | 66 | - License key activation at first launch via MNW API. | |
| 39 | 67 | - Version is in workspace root `Cargo.toml` under `[workspace.package]`. |
| @@ -124,9 +124,21 @@ Run before every release. Sign-off table at the bottom. | |||
| 124 | 124 | ### Platform-Specific | |
| 125 | 125 | ||
| 126 | 126 | - [ ] **macOS**: drag-out via NSPasteboardItem; app appears in Dock; no Gatekeeper warning on a signed build | |
| 127 | - | - [ ] **Windows**: drag-out via OLE/COM; .exe installer runs; uninstall is clean | |
| 127 | + | - [ ] **Windows**: drag-out via OLE/COM; standalone .exe runs; uninstall is clean | |
| 128 | 128 | - [ ] **Linux**: AppImage launches; drag-out works on X11 and Wayland (symlink fallback) | |
| 129 | 129 | ||
| 130 | + | ### Windows Installer (run before each release on windows-x86) | |
| 131 | + | ||
| 132 | + | Build with `pwsh -File dist\build-msi-native.ps1` (or `bash dist/build-msi.sh` from macOS/Linux as a fallback). | |
| 133 | + | ||
| 134 | + | - [ ] MSI install succeeds (no UAC errors for per-user scope) | |
| 135 | + | - [ ] Start Menu shortcut "AudioFiles" present after install | |
| 136 | + | - [ ] Launching from Start Menu opens the app, library loads | |
| 137 | + | - [ ] Repair-install: re-run the same MSI — completes without error | |
| 138 | + | - [ ] Major-upgrade: install older MSI, then newer MSI — old version removed cleanly, settings preserved | |
| 139 | + | - [ ] Uninstall via Apps & Features removes Start Menu entry and program files | |
| 140 | + | - [ ] If `AF_SIGN_CERT` was set at build: SmartScreen reputation prompt absent or accepts the cert; right-click .exe → Properties → Digital Signatures shows the expected certificate | |
| 141 | + | ||
| 130 | 142 | ### Robustness | |
| 131 | 143 | ||
| 132 | 144 | - [ ] Import a corrupt audio file — rejected, no crash |
| @@ -1,12 +1,32 @@ | |||
| 1 | 1 | # audiofiles TODO | |
| 2 | 2 | ||
| 3 | 3 | ## Status | |
| 4 | - | Done: All pre-beta phases + Phase 11 + SyncKit parity. Active: None. Next: Vocal layer 2, sample forge (phases 10-16). | |
| 4 | + | Done: All pre-beta phases + Phase 11 + SyncKit parity. Active: None. Next: launch (see below), then sample forge post-launch. | |
| 5 | 5 | ||
| 6 | 6 | v0.4.0. Audit grade A- (Ultra Fuzz 1, 2026-05-09). 780 tests. Rust 2024 edition (2026-05-06). rand 0.9. 4 SERIOUS, 10 MINOR findings from 5-axis adversarial audit. Run 20 items all resolved. | |
| 7 | 7 | ||
| 8 | 8 | --- | |
| 9 | 9 | ||
| 10 | + | ## Launch (Locked Scope, 2026-05-16) | |
| 11 | + | ||
| 12 | + | Public-launch blockers. Everything else in this file is post-launch. Do not promote items into this section without explicit user decision. | |
| 13 | + | ||
| 14 | + | 1. **Test full checkout flow against live Stripe** — end-to-end: subscribe → webhook → blob sync gate passes | |
| 15 | + | 2. **Build v0.4.0 Windows installer on windows-x86** — run `pwsh -File dist\build-msi-native.ps1`. Produces signed-or-unsigned `AudioFiles_0.4.0_x86_64.msi` + `.exe`. WiX source `dist/audiofiles.wxs.in`. Macros/cross-compile fallback: `dist/build-msi.sh`. (Installer config + signing hooks landed 2026-05-16.) | |
| 16 | + | 3. **Code-sign Windows binaries** — gated on Azure cert blocker (same as GO/BB). When unblocked: set `AF_SIGN_CERT` / `AF_SIGN_PASS` / `AF_SIGN_TIMESTAMP_URL` and re-run the PS1 builder. Until then, ship unsigned (matches 0.3.x). | |
| 17 | + | 4. **Test installed build on Windows** — install the MSI, verify Start Menu shortcut, run AudioFiles, complete `docs/human_testing.md` Windows-specific section, uninstall cleanly. | |
| 18 | + | ||
| 19 | + | Explicitly **out of launch scope** (defer until after public launch): | |
| 20 | + | - Phase 10 (Plugin/CLAP processing) — entire phase | |
| 21 | + | - Phase 12 (Chop Engine), Phase 13 (Resample/Time-Stretch), Phase 14 (Layer/Stack), Phase 15 (Batch Forge), Phase 16 (Snapshot History) — entire sample-forge pivot | |
| 22 | + | - Vocal layer 2 classifier (needs new training data) | |
| 23 | + | - Multi-sample instrument UI (KeyZone) — grayed out is acceptable | |
| 24 | + | - All dependency-prune items (do in a quiet week post-launch) | |
| 25 | + | - Ultra Fuzz trust-model items (ed25519 sig, keychain, Rhai timeout) — already flagged architectural | |
| 26 | + | - UX audit power-user gaps, rust-fuzz minor items, shared code extraction | |
| 27 | + | ||
| 28 | + | --- | |
| 29 | + | ||
| 10 | 30 | ## Dependency Pruning (2026-05-13) | |
| 11 | 31 | ||
| 12 | 32 | ### High Impact |