Renames what users see. The app, the installers, the window title, the
docs and all 21 locales now say VoiceStudio, with "(previously
OmniVoice-Studio)" noted near the title of each doc surface so people
recognise it.
Deliberately NOT renamed, because renaming any of them silently breaks
an existing install — there is no legacy-path fallback anywhere in this
codebase:
- bundle identifier com.debpalash.omnivoice-studio (MSI UpgradeCode,
macOS TCC grants, managed venv, WebView localStorage, the
single-instance lock)
- data directories OmniVoice / .omnivoice and omnivoice.db
- the ~150 OMNIVOICE_* environment variables
- the X-OmniVoice-* HTTP headers (a wire protocol)
- the published Docker image paths
- the OmniVoice ENGINE, which is a model name and not this product
tests/test_identity_paths_survive_the_rename.py pins every one of those
so a future well-meaning sweep cannot orphan a user's library.
Linux .deb users install a new package name and should apt remove
omnivoice-studio; that note is in the changelog.
Removes ~110 files of process noise (all preserved in git history):
.planning/ (GSD-era phases/quick-plans/issue-clusters; workflow retired
2026-07-08), specs/ (spec-kit specs for shipped features 001-007),
design/ (pre-React ASCII mockups), research/ (legacy Gradio archive),
and .agents/ (rules for a third-party agent tool no longer in use).
The four load-bearing decision docs move to docs/adr/ with an archival
note; every live pointer follows (gguf engine module docs + quant_map,
inject-apprun.sh, pyproject/test comments, fixture README + its seed
script — kept byte-identical). The CJK allowlist drops the deleted
legacy_gradio entries; STRUCTURE.md and ROADMAP.md document the removal
instead of linking into it.
Backend suite: 2891 passed.
Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The launcher decided whether to export WEBKIT_DISABLE_COMPOSITING_MODE
by asking the host's pkg-config — but LD_LIBRARY_PATH makes the
BUNDLED libwebkit2gtk the one that actually runs, so on any machine
where the two diverge the detection read the wrong number. This was
the second bug identified during #961's investigation (the reporter
built from source, so their dev packages answered pkg-config with a
healthy version while the shipped bundle ran an older lib) and was
explicitly deferred in #1007 as not-safely-fixable at runtime.
The fix makes it knowable by construction instead: inject-apprun.sh
runs at bundle time ON the build host whose libwebkit2gtk gets
bundled, so it stamps that version into .bundled-webkitgtk-version
inside the AppDir. AppRun reads the stamp first and only falls back to
host pkg-config for bundles predating it. Empty/unreadable stamp fails
safe (workaround on), same philosophy as the missing-pkg-config path.
Tests: 3 new cases in AppRun.test.sh — marker-beats-host in both
directions (broken-marker/healthy-host and the #961 inversion,
healthy-marker/broken-host) plus empty-marker fail-safe. Also wires
AppRun.test.sh into pytest (tests/test_apprun_launcher.py) — it was
previously run by NO CI job, so the launcher could regress silently.
Also documents Windows install-to-another-drive behavior in
docs/install/windows.md (#938): local drives work via the wizard's
directory picker, mapped network drives are a Windows Installer
limitation, and the data directory moves independently of the app.
Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix(appimage): conditional WEBKIT_DISABLE_COMPOSITING_MODE launcher (#56)
WebKitGTK 2.44.x and 2.46.x have a compositing-path regression on Wayland
that blanks the AppImage's first paint on Fedora 44 / Ubuntu 24.04. Setting
WEBKIT_DISABLE_COMPOSITING_MODE=1 forces the software fallback that works,
but blindly setting it on healthy WebKit versions (2.48+) regresses those.
This wave adds a conditional AppRun launcher that detects the WebKit
version via pkg-config and only sets the env var on the broken ranges
(plus a fail-safe when pkg-config is absent or the version is unknown).
The launcher is injected into Tauri's AppImage staging dir via a
beforeBundleCommand hook — see .planning/decisions/apprun-strategy.md for
the spike outcome and rationale (Strategy B chosen).
Phase 1 Wave 3 — Plan 01-03 Task 1. Closes#56 frontend half.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(deb): relocate bundled ffprobe out of /usr/bin to avoid conflicts (#76)
Prior versions placed the bundled ffprobe at /usr/bin/ffprobe via Tauri's
externalBin, which overwrites the system ffprobe on Ubuntu 26.04 and
collides with apt-installed media-package ffprobe.
Relocate the .deb-bundled ffprobe to /usr/lib/omnivoice-studio/bin/ffprobe
via bundle.linux.deb.files, plus defensive maintainer scripts:
- preinst: ensure target dir exists for upgrade flows
- postinst: remove legacy /usr/bin/ffprobe ONLY when dpkg confirms our
package owns it (never touches a user's distro ffprobe)
- postrm: clean up the relocated path tree on purge/remove
Rust side (tools.rs::resolve_ffprobe) now probes the new path on Linux,
and backend spawn (backend.rs) carries both FFPROBE_PATH (legacy alias)
and OMNIVOICE_FFPROBE_PATH (canonical) into the backend env. Python side
(ffmpeg_utils.resolve_ffprobe) reads OMNIVOICE_FFPROBE_PATH first, falls
back to FFPROBE_PATH, then to shutil.which("ffprobe").
6 new unit tests cover the env-cascade resolution.
Phase 1 Wave 3 — Plan 01-03 Task 2. Closes#76.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(frontend): centralised apiBase resolver for Docker LAN access (#80)
Docker / LAN browser users hit the preview API at the LAN host's IP, not
their local machine — the prior frontend/src/utils/media.js:20 hardcoded
http://localhost:3900, which from a LAN client resolved to the client
machine itself.
Centralise via frontend/src/utils/apiBase.ts:
1. VITE_OMNIVOICE_API override (Docker compose / dev) always wins.
2. Tauri webview → http://localhost:3900 (unchanged behaviour).
3. Plain browser → ${window.location.protocol}//${window.location.hostname}:3900
(follows the page's origin — closes#80).
4. SSR / no-window → http://localhost:3900 (safe fallback).
Grep-sweep confirmed media.js:20 was the only hardcode site (Assumption
A4 in 01-RESEARCH.md verified). 6 new vitest cases cover the resolver.
Phase 1 Wave 3 — Plan 01-03 Task 3. Closes#80 frontend half.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(backend): macOS Gatekeeper quarantine probe + INST-01 guard (#54)
Adds backend/core/gatekeeper_detect.py which walks up from sys.executable
to find the .app bundle and runs `xattr -l` to check for the quarantine
extended attribute (com.apple.quarantine). On detection, the lifespan
startup probe logs a structured warning and emits a system_error event
through the existing event bus with error_class="GATEKEEPER_QUARANTINE",
which Wave 2's React ErrorBoundary turns into a docs deeplink.
Detection is informational only — we never auto-run `xattr -cr` (the app
itself is quarantined and cannot fix its own state per Anti-Pattern in
01-RESEARCH.md). Users get a clear pointer to the workaround docs.
GET /system/quarantine-status exposes the structured payload so the
frontend can poll on first load.
INST-01 (setuptools>=75.0 pin from PR #62) gains a PR-time guard in
tests/backend/test_pyproject.py + a user-observable smoke check in
scripts/smoke-test.sh (pkg_resources + whisperx import).
7 gatekeeper tests + 1 pyproject test added — all pass.
Phase 1 Wave 3 — Plan 01-03 Task 4. Closes#54 backend half (Wave 2 owns
the docs page + ErrorBoundary deeplink wiring).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>