Skip to content

feat(release): split backend out of installer (download on first run, ~1.8GB → ~50MB)#73

Open
debpalash wants to merge 1 commit into
mainfrom
feat/split-backend-post-install
Open

feat(release): split backend out of installer (download on first run, ~1.8GB → ~50MB)#73
debpalash wants to merge 1 commit into
mainfrom
feat/split-backend-post-install

Conversation

@debpalash
Copy link
Copy Markdown
Owner

@debpalash debpalash commented May 17, 2026

Summary

Splits the PyInstaller backend out of the Tauri installer so the installer drops from ~1.8 GB to ~50 MB. Backend is downloaded + extracted on first launch from a separate GitHub Release asset.

Originally branched 3 weeks ago (Apr 23) — opening now after stabilization milestone planning surfaced installer size as a recurring user-friction point.

Why

GitHub Releases enforces a 2 GB per-asset cap. Linux .deb and Windows MSI both overshot this when shipping PyInstaller's frozen backend (~1.8 GB after CPU-torch slim) bundled inside the installer. NSIS and WiX both failed during their own size-bounded packaging steps too. The macOS DMG snuck under via HFS compression but the cross-platform parity broke.

Changes

  • Tauri installer ships WITHOUT the PyInstaller backendtauri.conf.json bundle.resources is now empty for the backend path. Installer drops from ~1.8 GB to ~50 MB.

  • CI packages backend separatelyrelease.yml adds a step after tauri-action that tarballs the frozen backend as `omnivoice-backend__.tar.gz` and uploads it to the same draft release via gh release upload. Each tarball is gz-compressed, comfortably under 2 GB.

  • First-launch downloader — new ensure_backend_ready() in frontend/src-tauri/src/lib.rs checks three locations in order:

    1. Resource dir (legacy install path, for compat)
    2. app_local_data_dir (new home for downloaded backend)
    3. Dev-mode dist/ fallback

    If none match, it downloads the platform-version-matching tarball from the GH Release and extracts into app_local_data_dir. Blocking on first run, no-op thereafter.

  • find_bundled_backend + backend_exe_name are now platform-aware — append `.exe` on Windows, scan all three roots.

  • New Rust deps: `ureq` (HTTP), `tar` + `flate2` (archive extract). No tokio — ureq is synchronous, matches existing setup() flow.

⚠️ Known: rebase needed

Branch is 93 commits behind main. Conflicts expected in:

  • `frontend/src-tauri/src/lib.rs` — pill-mode widget creation (Phase 0 work) was added to this file after this branch was created
  • `frontend/src-tauri/tauri.conf.json` — widget window config + bundle resources both touched

Auto-merges cleanly:

  • `.github/workflows/release.yml`
  • `frontend/src-tauri/Cargo.toml`

Reviewer should rebase against latest main in the GitHub UI or locally — resolve the lib.rs conflicts carefully to preserve both the split-backend logic and the new `WebviewWindowBuilder` widget creation from `b479f9b`.

Test plan

  • Rebase against main; resolve conflicts in lib.rs + tauri.conf.json
  • Verify pill-mode widget still creates after rebase (`bun desktop-prod:pill`)
  • Verify backend download on first launch with a fresh data dir
  • Tag a test release; confirm installer + backend tarball both attach + installer size dropped to ~50MB
  • Linux .deb under 2 GB; Windows MSI under 2 GB; macOS DMG still works

Relation to milestone

Not in original v0.3.x stabilization scope per PROJECT.md, but solves a real install-friction concern. Decision: defer to v0.4 unless installer-size complaints land soon.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Application now automatically downloads and provisions the backend on first run if not already present, with fallback location detection.
  • Chores

    • Updated release and packaging process to distribute backend artifacts separately from the application.

Review Change Stack

…first run

All three desktop platforms previously built a single asset — installer +
PyInstaller backend bundled together — that overshot GH Releases' 2 GB
per-asset cap on Linux and Windows. The mac DMG got under the cap thanks
to HFS compression, but Linux .deb and Windows MSI couldn't. NSIS and WiX
both failed during their own size-bounded packaging steps too.

Split the two:

- Tauri installer ships WITHOUT the PyInstaller backend
  (`tauri.conf.json` bundle.resources is now empty). Installer sizes drop
  from ~1.8 GB to ~50 MB.
- CI packages the frozen backend as
  `omnivoice-backend_<version>_<triple>.tar.gz` after tauri-action, and
  uploads it to the same draft release via `gh release upload`. Each
  tarball is gz-compressed + comfortably under 2 GB with the CPU-only
  torch wheel + strip=True from earlier PRs.
- On first launch, `ensure_backend_ready()` checks three locations in
  order: resource dir (legacy), app_local_data_dir (new home for the
  downloaded backend), and the dev-mode `dist/` fallback. If none match,
  it downloads the tarball matching the current platform + app version
  from the GH Release and extracts into app_local_data_dir. Blocking on
  first run, no-op thereafter.
- find_bundled_backend + backend_exe_name are platform-aware — they
  append .exe on Windows and scan all three roots.

Dependencies added to src-tauri/Cargo.toml: ureq (HTTP), tar + flate2
(archive extract). No tokio — ureq is synchronous, which matches the
existing setup() flow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 17, 2026

📝 Walkthrough

Walkthrough

The PR shifts backend distribution from static Tauri bundling to first-run download-and-extract from GitHub releases. The release CI now creates and uploads versioned backend tarballs. The Tauri app adds three dependencies for HTTP download and tarball extraction, removes static resource bundling, and implements platform-aware backend discovery and conditional bootstrap before spawning.

Changes

Backend Bootstrap from Release Assets

Layer / File(s) Summary
Release Workflow Backend Publishing
.github/workflows/release.yml
CI workflow creates a per-target versioned tar.gz from dist/omnivoice-backend and uploads it to the GitHub release for the current tag.
Bootstrap Dependencies and Tauri Configuration
frontend/src-tauri/Cargo.toml, frontend/src-tauri/tauri.conf.json
Cargo adds ureq, tar, and flate2 for download and extraction. tauri.conf.json removes the static backend resource mapping to enable first-run download instead.
Backend Discovery and Platform Abstraction
frontend/src-tauri/src/lib.rs
Imports and BACKEND_RELEASE_REPO constant enable URL construction. New find_bundled_backend() and backend_exe_name() helpers search multiple locations (resource dir, app-local data dir, dev fallback) and handle OS-specific executable naming. Platform triple mapping translates runtime OS/arch to release asset identifiers.
Download and Extract Backend Tarball
frontend/src-tauri/src/lib.rs
download_and_extract_backend() fetches the tarball from the GitHub release URL, validates HTTP status, and stream-decompresses and unpacks into the app-local data directory. LogReader wraps the decompression stream to emit periodic download progress logs.
Bootstrap Orchestration and Integration
frontend/src-tauri/src/lib.rs
ensure_backend_ready() checks for an existing bundled backend; if missing and a matching release asset exists, downloads and extracts it, then verifies readiness. Integrated into Tauri's run() setup to execute before backend spawning when TAURI_SKIP_BACKEND is not set.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A tarball hops from the cloud so bright,
Downloaded fresh on the first app night,
No bundled bloat, just wings to fly,
The backend whispers, "I'm here!" with a sigh! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately reflects the main architectural change: moving the backend from installer bundle to on-demand download, with specific impact metrics (size reduction from ~1.8GB to ~50MB).
Description check ✅ Passed PR description comprehensively covers summary, detailed changes, rationale, testing approach, and known rebasing issues; follows template structure with all critical sections completed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/split-backend-post-install
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/split-backend-post-install

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src-tauri/src/lib.rs (1)

440-452: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t ignore backend bootstrap failure in packaged startup.

Line 446 discards ensure_backend_ready result. If download fails, startup continues into fallback logic, which can degrade into an invalid dev-style path instead of a deterministic packaged failure mode.

Suggested fix
-            if !skip_spawn {
+            let bootstrap_ok = if !skip_spawn {
                 // First-run bootstrap: the installer ships without the
                 // PyInstaller backend so it fits under GH Releases' 2 GB
                 // per-asset cap. Download + extract it into the per-user
                 // app data dir if we don't have it yet. Blocking — the
                 // webview stays on the initial loader until this resolves.
-                let _ = ensure_backend_ready(app);
-            }
+                ensure_backend_ready(app)
+            } else {
+                true
+            };
             let has_bundled = find_bundled_backend(app).is_some();
+            if !skip_spawn && !bootstrap_ok && !cfg!(debug_assertions) {
+                log::error!("Backend bootstrap failed; skipping backend spawn in packaged mode.");
+            }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src-tauri/src/lib.rs` around lines 440 - 452, The call to
ensure_backend_ready(app) is ignored, so download/extract failures let startup
continue into the dev-like fallback; change the code around
ensure_backend_ready(app) to inspect its Result/Option (the same symbol
ensure_backend_ready) and, on error, log the failure with context and abort
startup (return Err/exit early) instead of proceeding to the
find_bundled_backend/backend_healthy(BACKEND_PORT) logic; ensure the code path
respects skip_spawn and uses the actual outcome of ensure_backend_ready to
prevent entering an invalid packaged startup path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 240-257: Guard the release upload steps so they run only for tag
builds: update the steps named "Package backend tarball" / "Upload backend
tarball to draft release" to check that the workflow is running for a tag (e.g.,
use a conditional like startsWith(github.ref, 'refs/tags/')) before creating
BACKEND_TARBALL and invoking gh release upload; ensure the upload step that
calls gh release upload "$BACKEND_TARBALL" is skipped for workflow_dispatch or
non-tag refs to avoid failing when no release exists.

In `@frontend/src-tauri/src/lib.rs`:
- Around line 126-133: The code unconditionally pushes
PathBuf::from("../../dist") into roots (near the roots variable and exe_name
usage), causing release builds to probe a dev-relative binary; restrict that
push to dev/debug builds by wrapping it in a build-config check (e.g., if
cfg!(debug_assertions) { roots.push(PathBuf::from("../../dist")); }) or use an
equivalent #[cfg(debug_assertions)] guard so only non-release builds include the
"../../dist" candidate.
- Around line 187-203: The download path currently reads resp into reader and
directly creates GzDecoder/Archive and calls archive.unpack(&dest_root); add an
integrity check step before extracting: read the response bytes (or stream to a
temp file) instead of piping straight into GzDecoder, compute and verify a
checksum (e.g., SHA-256) or verify a cryptographic signature with the project's
public key against a bundled/remote expected value, and only proceed to create
flate2::read::GzDecoder::new(...) and call archive.unpack(&dest_root) if the
verification succeeds; ensure you update the variables referenced here (url,
resp, reader, gz, archive.unpack) to use the verified temp file/byte buffer and
return an io::Error when verification fails.

---

Outside diff comments:
In `@frontend/src-tauri/src/lib.rs`:
- Around line 440-452: The call to ensure_backend_ready(app) is ignored, so
download/extract failures let startup continue into the dev-like fallback;
change the code around ensure_backend_ready(app) to inspect its Result/Option
(the same symbol ensure_backend_ready) and, on error, log the failure with
context and abort startup (return Err/exit early) instead of proceeding to the
find_bundled_backend/backend_healthy(BACKEND_PORT) logic; ensure the code path
respects skip_spawn and uses the actual outcome of ensure_backend_ready to
prevent entering an invalid packaged startup path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 68280e5d-3c60-43a5-8f68-b0d30bb2d5ff

📥 Commits

Reviewing files that changed from the base of the PR and between 2d0f0e7 and 69a8525.

📒 Files selected for processing (4)
  • .github/workflows/release.yml
  • frontend/src-tauri/Cargo.toml
  • frontend/src-tauri/src/lib.rs
  • frontend/src-tauri/tauri.conf.json

Comment on lines +240 to +257
- name: Package backend tarball
shell: bash
run: |
VER="${GITHUB_REF_NAME#v}"
OUT="omnivoice-backend_${VER}_${{ matrix.rust_target }}.tar.gz"
tar -czf "$OUT" -C dist omnivoice-backend
ls -lah "$OUT"
echo "BACKEND_TARBALL=$OUT" >> "$GITHUB_ENV"

- name: Upload backend tarball to draft release
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${{ github.ref_name }}" \
"$BACKEND_TARBALL" \
--repo "$GITHUB_REPOSITORY" \
--clobber
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard release-upload steps to tag builds only.

These steps currently run for workflow_dispatch too. If no release exists for github.ref_name, Line 254 (gh release upload) fails and breaks manual runs.

Suggested fix
       - name: Package backend tarball
+        if: startsWith(github.ref, 'refs/tags/v')
         shell: bash
         run: |
           VER="${GITHUB_REF_NAME#v}"
           OUT="omnivoice-backend_${VER}_${{ matrix.rust_target }}.tar.gz"
           tar -czf "$OUT" -C dist omnivoice-backend
           ls -lah "$OUT"
           echo "BACKEND_TARBALL=$OUT" >> "$GITHUB_ENV"

       - name: Upload backend tarball to draft release
+        if: startsWith(github.ref, 'refs/tags/v')
         shell: bash
         env:
           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 240 - 257, Guard the release
upload steps so they run only for tag builds: update the steps named "Package
backend tarball" / "Upload backend tarball to draft release" to check that the
workflow is running for a tag (e.g., use a conditional like
startsWith(github.ref, 'refs/tags/')) before creating BACKEND_TARBALL and
invoking gh release upload; ensure the upload step that calls gh release upload
"$BACKEND_TARBALL" is skipped for workflow_dispatch or non-tag refs to avoid
failing when no release exists.

Comment on lines +126 to +133
roots.push(PathBuf::from("../../dist"));
for root in roots {
let candidates = [
root.join(format!("backend/omnivoice-backend/{}", exe_name)),
root.join("backend/omnivoice-backend").join(&exe_name),
root.join("omnivoice-backend").join(&exe_name),
root.join(format!("omnivoice-backend/{}", exe_name)),
];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restrict ../../dist probing to dev builds only.

Including this path unconditionally lets release builds pick up a relative dev binary if present, which is risky and can bypass intended bootstrap behavior.

Suggested fix
-    roots.push(PathBuf::from("../../dist"));
+    if cfg!(debug_assertions) {
+        roots.push(PathBuf::from("../../dist"));
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src-tauri/src/lib.rs` around lines 126 - 133, The code
unconditionally pushes PathBuf::from("../../dist") into roots (near the roots
variable and exe_name usage), causing release builds to probe a dev-relative
binary; restrict that push to dev/debug builds by wrapping it in a build-config
check (e.g., if cfg!(debug_assertions) {
roots.push(PathBuf::from("../../dist")); }) or use an equivalent
#[cfg(debug_assertions)] guard so only non-release builds include the
"../../dist" candidate.

Comment on lines +187 to +203
log::info!("Fetching backend tarball: {}", url);
let resp = ureq::get(&url)
.timeout(Duration::from_secs(60 * 30))
.call()
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("download: {}", e)))?;
if resp.status() != 200 {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("download HTTP {} from {}", resp.status(), url),
));
}

let mut reader = resp.into_reader();
let gz = flate2::read::GzDecoder::new(LogReader::new(&mut reader));
let mut archive = tar::Archive::new(gz);
archive.unpack(&dest_root)?;
log::info!("Backend extracted under {}", dest_root.display());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Release workflow: checksum/signature generation =="
rg -n "sha256|checksum|minisign|cosign|\\.sig|signature|digest" .github/workflows/release.yml

echo
echo "== Bootstrap code: checksum/signature verification =="
rg -n "sha256|checksum|minisign|cosign|verify|signature|digest" frontend/src-tauri/src/lib.rs

Repository: debpalash/OmniVoice-Studio

Length of output: 249


🏁 Script executed:

# Get context around the signature mention in release.yml
sed -n '200,220p' .github/workflows/release.yml

Repository: debpalash/OmniVoice-Studio

Length of output: 1032


🏁 Script executed:

# Search for latest.json usage in the codebase
rg -n "latest\.json" --type yaml --type ts --type rs --type toml

Repository: debpalash/OmniVoice-Studio

Length of output: 97


🏁 Script executed:

# Look for where the downloaded backend is executed
rg -n "execute|run|spawn|Command" frontend/src-tauri/src/lib.rs -A 2 -B 2

Repository: debpalash/OmniVoice-Studio

Length of output: 6369


🏁 Script executed:

# Check if there's any verification function in the codebase
rg -n "fn.*verif|fn.*check.*sig|fn.*validate" frontend/src-tauri/src/ -i

Repository: debpalash/OmniVoice-Studio

Length of output: 52


🏁 Script executed:

# Find and show the full download_and_extract_backend function
rg -n "fn download_and_extract_backend" frontend/src-tauri/src/lib.rs -A 50

Repository: debpalash/OmniVoice-Studio

Length of output: 2138


🏁 Script executed:

# Search for latest.json usage in bootstrap/download code
rg -n "latest\.json" frontend/src-tauri/src/

Repository: debpalash/OmniVoice-Studio

Length of output: 52


🏁 Script executed:

# Check the full context of the download function - look for any hash/signature variables
rg -n "download_and_extract_backend|tarball|backend.*url" frontend/src-tauri/src/lib.rs -B 2 -A 10

Repository: debpalash/OmniVoice-Studio

Length of output: 3102


🏁 Script executed:

# Search entire codebase for any verification of downloaded files
rg -n "verify|signature|digest|checksum|sha" frontend/src-tauri/src/ -i

Repository: debpalash/OmniVoice-Studio

Length of output: 52


Add integrity verification before extracting/executing downloaded backend.

The app downloads and executes an untrusted backend tarball on first run, but provides only HTTP 200 status verification. There is no checksum or signature verification in this path, weakening supply-chain guarantees for the bootstrap flow.

The backend URL is hardcoded and downloaded directly from GitHub releases (lines 179–197), then immediately extracted (line 202) and executed (line 347) without any integrity checks. The Tauri updater has signature support, but that is not wired for the backend tarball.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src-tauri/src/lib.rs` around lines 187 - 203, The download path
currently reads resp into reader and directly creates GzDecoder/Archive and
calls archive.unpack(&dest_root); add an integrity check step before extracting:
read the response bytes (or stream to a temp file) instead of piping straight
into GzDecoder, compute and verify a checksum (e.g., SHA-256) or verify a
cryptographic signature with the project's public key against a bundled/remote
expected value, and only proceed to create flate2::read::GzDecoder::new(...) and
call archive.unpack(&dest_root) if the verification succeeds; ensure you update
the variables referenced here (url, resp, reader, gz, archive.unpack) to use the
verified temp file/byte buffer and return an io::Error when verification fails.

debpalash added a commit that referenced this pull request May 19, 2026
* docs(phase-5): research opt-in bug reporting

Phase 5 research: prefilled-URL GitHub Issues pattern, default-deny payload,
redaction layer, two-step consent UX, rate/dedup/recursion safeguards,
aggregation across Python/Rust/React error producers. Builds on Phase 1's
links.py + errorDocsMap deeplink infrastructure; uses already-installed
@tauri-apps/plugin-opener (^2.5.4). No new packages required.

Covers REPORT-01..12 with confidence levels, 8 pitfalls, subprocess-engine
error capture handoff to Phase 2, security domain mapped to ASVS, and
3-wave delivery plan (redactor + payload, consent UI, aggregation +
pre-submit search).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(phases): research for Phases 2, 3, 4, 6 (Engine + Supertonic + Spikes + Release)

* docs(stack): bump supertonic pin 1.2.3 → 1.3.1 (Phase 3 research finding)

* docs(phases): plan Phases 2-6 for v0.3.0 fat-milestone release

15 new plan files + 2 ADR decision docs across 5 phases. Combined with Phase 1's 3 plans, the v0.3.0 milestone now has 18 PLAN.md files covering all 7 phases (Phase 0 already complete via PR #71).

PHASE 2 (Engine Isolation — 4 plans):
- 02-01: SubprocessBackend primitive + echo sidecar POC + graceful is_available wrap (ENGINE-01/05)
- 02-02: _safe_torchaudio_save helper + migrate 11 WAV write sites + #48 regression (BUG-01)
- 02-03: IndexTTS sidecar entry + venv-probe bootstrap + IndexTTS2Backend rewire (ENGINE-02/03/04/07, closes #42)
- 02-04: Engine Compatibility Matrix UI + /engines/{id}/health route (ENGINE-06)

PHASE 3 (Supertonic-3 + Mirror — 2 plans):
- 03-01: Supertonic-3 engine on SubprocessBackend + SHA pin + license gate (TTS-01..06)
- 03-02: bootstrap.rs mirror cascade + UV_DEFAULT_INDEX migration + frozen enforcement + docs (INST-07..11)

PHASE 4 (Spike-first Adaptive & Specialty — 2 plans + 2 ADRs):
- 04-01: OmniVoice-GGUF hardware-adaptive engine + quant_map + bundled binaries (SPIKE-01, GGUF-01..06)
- 04-02: OmniVoice-Singing subclass + dub pipeline singing mode + segment detector (SPIKE-02, SING-01..05)
- SPIKE-01-gguf.md + SPIKE-02-singing.md ADRs in .planning/decisions/

PHASE 5 (Opt-in Bug Reporting — 3 plans):
- 05-01: Redactor + BugReporter + URL builder + rate/dedup/recursion safeguards + FastAPI router (REPORT-01/02/03/05/06/07/08/10/11)
- 05-02: BugReportDialog two-step consent + PrivacyPanel + ErrorBoundary integration + Rust panic hook (chained) (REPORT-01-Rust/04/09/12)
- 05-03: Dry-run vs 3 historical issues + cross-platform openUrl smoke + Phase 2 subprocess-errors handoff (REPORT-02 smoke, REPORT-03 expansion, REPORT-09)

PHASE 6 (Release + Retro — 4 plans):
- 06-01: rc1 prep — version bump across 4 sources + CHANGELOG + retro stub + PR-73-strategy doc (REL-01/03/06)
- 06-02: CI guards — workflow-parity actionlint + tag-shaped dry-run (Phase 0 retro options B + C; closes release-engineer gap)
- 06-03: PR #73 reimplementation (NOT rebase) — backend-split installer with mirror-cascade integration + pill-mode regression checkpoint
- 06-04: Execute the release — pre-tag gates + 4-OS clean-VM + 48h soak + tag + retro + 3 v0.4 deferral tracking issues (REL-01/02/03/04/05/06)

Scope decisions locked in plans (council session):
- SoniTranslate refactor DEFERRED to v0.4 (Phase 2 ships SubprocessBackend without migrating Soni)
- macOS notarization DEFERRED to v0.4 (Phase 6 ships xattr -cr automation per CLAUDE.md Key Decision #7)
- supertonic pin 1.2.3 → 1.3.1 (already committed in ba63733)
- SPIKE-01 and SPIKE-02 both GO; 13/13 Phase 4 reqs stay in scope
- PR #73 reimplemented, not rebased (93 commits behind main)

All 18 plans validated via gsd-sdk frontmatter.validate + verify.plan-structure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (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.

1 participant