28 Commits
Author SHA1 Message Date
Palash Debnath 5af8fe6967 Make Electron the default desktop and refresh setup documentation 2026-09-16 22:11:59 +05:30
Palash Debnath 0688c37f04 Merge pull request #2020 from debpalash/feat/engine-list-detail
feat(catalogue): engine list + detail panel, weights under their engine
2026-09-10 14:06:26 -07:00
Palash Debnath 5f3f35f134 Merge remote-tracking branch 'origin/main' into fix/darwin-eperm-exit-settle
# Conflicts:
#	CHANGELOG.md
2026-09-10 13:04:31 -07:00
Palash Debnath 8cf7bebb61 fix(lifecycle): wait for a Darwin exit to register after EPERM
XNU stops signalling a process as soon as it starts exiting but posts
NOTE_EXIT later in the same exit. A KILL that lands in that window gets
EPERM while the exit probe still reads "alive", so stopping a child that
TERM had just ended could fail with "Operation not permitted". This
flaked the macOS run of contained_exit_probe_preserves_a_live_child.

The EPERM branch now re-probes for up to 250 ms before treating the error
as a live, unsignalable root. Live roots, reaped roots and probe failures
are still errors.
2026-09-10 13:02:00 -07:00
Palash Debnath 09b7e77629 docs(changelog): engine list + detail panel (#2020); drop the unused weights heading key 2026-09-10 08:00:40 -07:00
Palash Debnath a7cfe288cb fix(catalogue): harvest review findings on #2013
- SetupSummary: an installed engine whose routing is "unavailable" reads
  Needs setup, not Ready (select is refused for it too); a failed /engines
  or /dictation/models fetch renders as an error with Retry instead of
  posing as "Off" / "Needs setup".
- Bulk installs (summary, model store, recommendation card) wait for every
  request to settle before re-enabling, and report which repos failed —
  one early rejection can no longer re-arm the button mid-flight.
- The weights list stays mounted across family switches (hidden under LLM)
  so download progress and Retry/Dismiss state survive navigation.
- Settings search: Hugging Face mirror terms route to Network; the legacy
  "models" tab id resolves to Storage. "Manage models" opens the TTS tab.
- Locales: uk "Рушії", zh-TW "引擎", vi "Engine" for the Engines heading.
- Docs name the family tab wherever the instruction depends on it.
2026-09-10 07:08:43 -07:00
Palash Debnath 3cae853440 feat(catalogue): one page, one axis — setup summary over per-family engines and weights
The Model Catalogue put the same decision on two axes: an Engines pane with
TTS/ASR/LLM tabs and a Models pane with TTS/ASR/Dictation/Diarisation
sections, dictation shown in both, plus storage stats, the HF token and the
voice-preview toggle parked on the model list. Settings → Voice still carried
Engines and Models entries that only pointed back here.

Now the page reads top-down: a SetupSummary (speech, transcription,
dictation, language model — engine, device, one status word, Change), the
engine list for one family, and that family's downloadable weights under it
(TTS under TTS; offline ASR, streaming dictation and diarisation under ASR;
nothing for LLM, whose engines bring their own). One storage line points at
Settings → Storage.

- ModelStoreTab takes a `family` and scopes sections and the recommendation
  preset to it (scopeReco); stats strip, HF-token toolbar and previews
  panel removed from it.
- Settings: Engines/Models categories and CataloguePointer removed; models
  directory → Storage, HF mirror → Network (both restart-flagged), voice
  previews → Storage. "Manage models" in disk usage opens the catalogue.
- Store: openCatalogue takes a family (pane key tolerated, ignored);
  pendingCatalogueTab gone.
- Engine matrix title is now the locale's plain "Engines".
- i18n: catalogue.* summary keys in all 21 locales; pane/pointer keys dropped.
- Docs: "Model Catalogue → Engines" is "Model Catalogue"; "→ Models" is
  "→ Downloaded weights".
2026-09-10 06:46:09 -07:00
Palash Debnath 8667b34ca5 Merge commit 'f18a7add' into codex/pr-queue-integration
# Conflicts:
#	CHANGELOG.md
#	docs/install/macos.md
2026-09-07 13:47:50 +05:30
Palash Debnath f18a7adddf fix(lifecycle): verify root exit after Darwin signal permission errors 2026-09-07 13:46:06 +05:30
Palash Debnath 2b2bfe1a7f fix(capture): allow the widget window to hide after recording 2026-09-07 12:23:32 +05:30
Palash Debnath 426bd1ecea fix(desktop): preserve macOS window behavior with native controls 2026-09-07 11:17:17 +05:30
Palash Debnath 7640f42dce feat(install): one-command installer URL (.sh + .ps1) + 3-OS install smoke (#1627)
* feat(install): one-command installer URL (.sh + .ps1) + 3-OS install smoke

- scripts/install.ps1: Windows source installer (winget deps, uv, bun,
  clone, uv sync, frontend build); honors OMNIVOICE_PYTHON/OMNIVOICE_REGION
- infra/install-redirect: Cloudflare Worker serving /install with
  User-Agent sniffing (curl -> sh, PowerShell -> ps1, browser -> landing
  page); proxies live from main; /install.sh + /install.ps1 aliases
- scripts/install.sh: fix stale advertised URL (main/install.sh never
  existed) and repo-root resolution so a local run no longer clones a
  duplicate repo into ~/VoiceStudio (verified on macOS arm64)
- .github/workflows/install-smoke.yml: run both installers end-to-end on
  ubuntu/macos/windows when they change
- docs-sync: install one-liners lead each platform guide; STRUCTURE.md

(#1626)

* fix(install): don't let a failed bun download pass silently

curl | sh runs an empty script and exits 0 when the download fails, so
a bun.sh hiccup surfaced much later as 'bun: command not found' (seen
on the macos-latest smoke runner). Fetch to a temp file, verify, fall
back to npm -g bun when node exists, and die with the manual command.
Same post-install verification for uv.

* fix(install): UTF-8 BOM for install.ps1 + quiet-style changelog entry

- tests/scripts/test_uninstall_ping.py requires shipped PowerShell
  scripts with non-ASCII text to carry a UTF-8 BOM (Windows PowerShell
  5.1 mis-decodes otherwise); same treatment uninstall.ps1 already gets
- test_changelog_style caps entries at ~400 chars
2026-08-21 11:47:39 +00:00
Palash DebnathandClaude Opus 5 41722afe3b refactor(launchpad): quieter, borderless design refresh (#1515)
* refactor(launchpad): quieter, borderless design refresh

The launchpad carried decoration from an earlier direction: icon chips,
corner-hung count badges, a permanently visible filled arrow, uppercase
mono card titles, and a dotted stipple divider — plus a frame that had
been invisible since the app-wide border tokens were zeroed.

Rework it around what the borderless direction actually implies:

- Feature tiles get a whisper-faint surface instead of a dead frame, and
  read as three bands (bare glyph + count / title + arrow / description).
  `--card-hue` is spent sparingly — the glyph at rest, the surface, count
  and arrow only once raised. Titles move to sans sentence case; counts
  are plain tabular numerals. Lift softened 4px -> 2px, coloured glow ->
  neutral shadow, plus an explicit focus ring and a staggered entrance.
- Hero drops the boxed "646" pill and the filled A/B-Compare button for
  quiet type, with a hairline standing in for the separation.
- Section labels trade the dotted stipple for a single fading hairline;
  rows are transparent until hover and reveal "Open" on hover/focus (it
  stays in the DOM, so AT and keyboard always reach it).
- Hero, tiles, recent files, callout and project lists now share one
  1180px column — previously only the top half was capped, so lists ran
  edge-to-edge on a wide display while the deck stayed centred.

Two bugs found and fixed while doing it:

- Buttons that had `border border-solid border-transparent` removed fell
  back to the UA default border and rendered a visible 1px outline. They
  now carry `border-0` explicitly.
- `.lp-animate` used `animation-fill-mode: both`, so after the entrance
  it kept owning `transform` — and animation-origin declarations outrank
  normal ones, which silently killed the card hover lift. Now `backwards`,
  which still holds the from-state through the stagger delay.

Also drops CSS the page has not rendered since #904: the cursor-spotlight
layer, the breath ring, and the per-card waveform strip.

Verified with headless renders at 1600/1280/940 and the empty state.

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

* fix(dictation): decode Wayland portal signals and show the capture pill

The GlobalShortcuts portal declares Activated/Deactivated as
(o session, s shortcut_id, t timestamp, a{sv} options). We decoded the
timestamp as u32, so zbus rejected every signal with

  Signature mismatch: got `(osta{sv})`, expected `(osua{sv})`

and the press was dropped as an invalid signal. Registration succeeded
and the desktop even reported the bound chord back, so the hotkey looked
wired up while doing nothing at all — on every Wayland compositor, for
the whole life of the feature (#1490). Decode the 64-bit timestamp, and
keep the 32-bit spelling as a fallback so a non-conforming portal
degrades to working rather than to silence.

With presses arriving, the second half of the failure showed: nothing
had shown the widget window since it became a hidden recorder host, so a
capture ran with no pill on screen — and a mic or Accessibility failure
rendered into a window nobody could see. Add show_dictation_pill, which
bottom-centres the capsule on the monitor under the pointer and shows it
without taking focus (Windows keeps SW_SHOWNOACTIVATE so paste still
lands in the user's document), and call it from the widget for every
state but idle. Wayland denies clients their own placement, so the
compositor picks the spot there; the pill still appears.

dispatch_dictation_capture now logs whether a press was emitted or
queued — a press that reaches Rust and produces nothing was otherwise
indistinguishable from one the compositor never delivered.

Tests: portal signals decode at both timestamp widths (the 64-bit case
fails before this change with the exact production error); pill
placement centres, respects a second monitor's origin, and clamps rather
than going off-screen; the widget shows for a state needing the user,
stays hidden while idle, and never shows for a press that arrives while
dictation is disabled.

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

* chore: sync in-progress workspace changes

Uncommitted work already in the tree, checkpointed so the branch matches
the local machine:

- Remote GPU workers: join-from-the-app flow, one-time secrets, QR join
  codes, a Compute control in the status bar, and the device-list
  Workers panel (#1516)
- Model Catalogue workspace, with Settings pointing at it
- Settings sidebar search and keyboard navigation
- Demo assets for dubbing, dictation and voice design, plus the scripts
  that render them
- Backend: validation-error handling, ASR request-path degradation, and
  the accompanying tests
- CHANGELOG entries for the above and for the Wayland dictation fix

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

* fix(tests): follow Engines to the Model Catalogue, and green the sweep

- test_supertonic3 asserted the license gate points at "Settings" while
  the engine now names Model Catalogue → Engines, which is where the
  accept button actually lives. The assertion follows the move; what it
  pins is unchanged — the hint must name a place the user can reach it.
- Carries the CJK allowlist entries for the rendered dub bundle (#1517)
  and the regenerated route snapshot for /workers/agent (#1516), both of
  which this branch inherits from the workspace sync.
- docs/install/linux.md: the dictation capsule is bottom-anchored
  everywhere except Wayland, where the protocol gives applications no
  say in their placement. Documented rather than left as a surprise
  (CodeRabbit).

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

* ci: stop a flaky dependency fetch from failing green runs

en-core-web-sm resolves to a direct GitHub release URL, and github.com
intermittently answers `http2 error: refused stream before processing
any application logic`. uv's own three retries all land within the same
few seconds and fail together, so the whole job dies on a dependency
that has nothing to do with the change under test — it cost #1518 and
#1517 an otherwise-green run tonight.

Two changes: back off between whole `uv sync` attempts, which is what
actually clears it, and pass --no-sync to the pytest steps. `uv run`
re-resolves the environment before running, so every test step was a
fresh chance to hit the same fetch even though the install step had
already synced — that is exactly how #1518 failed, in the isolated
backend/tests step, with all 5467 tests already passed.

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

* ci: one retry seam for every uv sync, not just the job that failed last

en-core-web-sm resolves to a direct GitHub *release* URL rather than a
package index, and github.com intermittently answers `http2 error:
refused stream before processing any application logic`. uv's own
retries all land inside the same ~10 seconds and fail together, so a job
dies on a dependency unrelated to the change under test. Tonight that
cost four otherwise-green runs across #1515, #1517 and #1518 — and the
first fix only covered the Tests job, so the next failure simply moved
to Smoke (Linux), which syncs separately.

The fetch is per-job, so the fix has to be per-job: scripts/uv-sync-retry.sh
backs off between whole attempts (15s, 45s, 90s) and every workflow that
syncs now goes through it — ci.yml (tests + the platform matrix),
release.yml, security.yml, evals.yml. It still fails loudly after four
attempts, so a genuinely broken lockfile is not disguised as a flake.

The Tests job also lacked the UV_HTTP_TIMEOUT / UV_HTTP_RETRIES the smoke
matrix has always set, which is part of why it was the one that kept
dying; it has them now.

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

* test(ci): pin the Intel-Mac contract by intent, not by command spelling

test_ci_verifies_intel_mac_as_the_documented_remote_only_host asserted
the literal line `run: uv sync --extra pockettts`, so routing every sync
through scripts/uv-sync-retry.sh read as a broken Intel-Mac contract. The
contract it exists to protect is that the pockettts extra installs ONLY
on backend_supported legs — which the regex now pins, while leaving how
the sync is invoked free to change.

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

* ci: keep every uv run out of the resolver, and bound the retry budget

CodeRabbit, #1517:

- `uv run` re-resolves before running, so the smoke suite, the
  worker-artifact tests, the release test run and the eval run were each
  a fresh chance to hit the flaky direct-URL fetch outside the retry
  loop. All of them pass --no-sync now; the environment is already
  synced by the step that owns the retries. security.yml's
  `uv run --with pip-audit` is deliberately left alone — it layers an
  ephemeral package rather than running the project's own tests.
- The retry count multiplied uv's own budget (UV_HTTP_RETRIES=5 with a
  120 s timeout on the smoke matrix). Three attempts and 60 s of total
  backoff outlast the refusals actually observed while staying well
  inside the jobs' timeout-minutes.
- The Intel-Mac contract test pinned the smoke command literally too, so
  --no-sync tripped it exactly like the sync line did. Same fix: assert
  the contract (smoke runs only on backend_supported legs), not its
  spelling.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 23:46:07 +00:00
Palash Debnath 5cab8e0149 feat: rename the product to VoiceStudio (previously OmniVoice-Studio)
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.
2026-08-07 01:30:58 +05:30
Palash Debnath a23e69d014 chore: point every repo reference at github.com/debpalash/VoiceStudio (#1394)
The repository was renamed. 724 references across 59 files now point at the new URL — README badges, docs, install guides, the updater's releases API call, CONTRIBUTING, the Colab link and the probe harness. GitHub redirects the old URLs, so nothing was broken in the meantime.

Deliberately NOT renamed, because each breaks something on a user's machine: the Tauri bundle identifier (the path to every existing user's data), /usr/lib/omnivoice-studio and the compose container names, and the published Docker image paths.

The image path needed a code change to STAY still: docker.yml derived it from github.repository, so the next build would have published to ghcr.io/debpalash/voicestudio while Docker Hub, a hardcoded literal, stayed put — everyone pulling the documented GHCR path would have kept receiving the last pre-rename image forever. It is now pinned, with a test that fails if it ever derives from the repo name again.

Also makes the probe's repo-name assertion shape-based: it hardcoded the old name and failed on every PR after the rename while the code it tests worked perfectly.
2026-08-06 18:15:56 +05:30
Palash DebnathandClaude Opus 5 168e8e5c61 fix(macos): declare the floor the app actually delivers (13.3, not 12) (#1314)
* fix(macos): declare the floor the app actually delivers (13.3, not 12)

The app declared minimumSystemVersion 12.0 and the docs promised Monterey,
while the frontend required Safari 16.4 in three independent places: Vite's
default build target (baseline-widely-available = safari16.4), Tailwind v4's own
documented floor, and `@property` throughout its generated utilities. On
Monterey's WKWebView 15.6 the focus ring and accent surfaces resolve invalid,
and a bundled dependency ships a RegExp lookbehind that is a PARSE-time
SyntaxError no polyfill can reach.

Option B — actually supporting 15.6 — means setting build.target back,
replacing 64 color-mix() calls, dropping Tailwind v4 and replacing that
dependency, indefinitely, for an OS that stopped receiving security updates in
late 2024. The council was unanimous on A, and the precedent is uniform (Chrome
117, Electron 27, VS Code, Firefox 116).

minimumSystemVersion is also the guard: macOS itself refuses to launch a bundle
below it, so a Monterey user gets an explicit OS refusal rather than an app that
opens to a blank window — which matters because the Tauri updater has no
per-OS gating of its own.

Docs updated in the same change (README support table, docs/install/macos.md)
and the webCompat floor assertion re-derived to 16.4, so the post-floor API
list must be revisited the next time the floor moves.

Closes #1268

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(macos): raise the floor in the macOS overlay too, and assert it

Greptile P1, and correct: Tauri merges tauri.macos.conf.json OVER the base
config for a macOS build, and that file carried its own
minimumSystemVersion: 12.0. Changing the base config alone decided nothing —
the shipped bundle would have stayed Monterey-installable while the base
config, the README and the install docs all said 13.3.

Worse, the guard I added read only the base config, so it would have gone on
passing. A test that validates the wrong file is not a guard; it now asserts
both, with a comment saying why the overlay is the one that ships.

Also per review: the changelog entry was an editorial paragraph rather than a
one-line entry, and the section was missing ### Docs. Both fixed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(webcompat): the module header still described the old 12.0 floor

The floor moved to 13.3/Safari 16.4 in this PR and the test was re-derived,
but webCompat.js still told the next reader the oldest supported WebView was
15.6 — which would make every fill here look mandatory instead of retained
for Linux's unpinnable WebKitGTK (CodeRabbit).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 01:44:41 -07:00
debpalashandClaude Fable 5 83f943bead fix: bot-review harvest (16 findings) + deterministic style/locale CI + reviewer configs
Harvested and verified every CodeRabbit/Greptile finding from PRs #1175,
#1189, #1192, #1195: 16 real ones fixed (fallback ASR preflight bypass,
VRAM release on stream exit, typed 409 parity, uv env independence,
path-privacy in errors, MCP clone_voice hardening, CaptureWidget WS
guard, test hygiene), 4 refuted with evidence, rest documented as
deliberate design or deferred.

Deterministic CI replaces hand-enforcement: tests/test_changelog_style.py
(quiet one-liner format) and tests/test_locale_parity.py (21-locale
key/placeholder lockstep with a ratchet baseline) — the latter surfaced
and fixes 151 already-broken locale strings. CodeRabbit/Greptile carry
the house rules via .coderabbit.yaml + greptile.json; CLAUDE.md gains
the harvest-before-merge and never-accept-as-is rules.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 09:47:41 +05:30
debpalashandClaude Fable 5 63fd497caf feat: TTS-only first run, platform-curated ASR, guided OS permissions, parakeet-mlx
Only the TTS model (~2.4 GB) is required on first run; ASR models are
per-platform curated picks (curated_on in models.yaml) installed on demand.
Every transcription surface returns a typed asr_model_missing error with a
one-click download CTA instead of silently pulling multi-GB Whisper weights.
Settings -> Models is a grouped, platform-aware catalog. New guided
permissions UX (wizard System Check + Settings -> Permissions + mic
pre-flight) with native mic-state checks and OS settings deep-links. New
parakeet-mlx engine brings Parakeet TDT v3 to Apple Silicon (language-gated
capture preference so multilingual dictation never regresses). Docs:
expressive-speech page, Flush/Unload + CPU-fallback triage, clone-length FAQ.
Hardening: preflight fails open for custom model pins, ROCm curation no
longer inherits NVIDIA picks, Windows mic probe reads the NonPackaged
consent key, CaptureWidget setup race fixed, offline-cache CI simulation
fixes so empty-cache runners stay green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 15:20:54 +05:30
5562aa16a7 feat(setup): media tools become invisible — bundled by default, controllable in Settings (#1071)
Most users should never learn what ffmpeg is. The Setup Wizard's SYSTEM
PREFLIGHT stops listing FFmpeg / FFprobe / yt-dlp as user-installed
requirements ("brew install ffmpeg…"): they are internal dependencies the
app provisions for itself. Genuine user facts (OS, RAM, disk, GPU,
network, Python) are untouched.

Backend
- New services/media_tools.py: per-tool status {version, path, origin:
  sidecar|bundled|system|custom}; background acquisition of a pinned,
  SHA-256-verified static ffmpeg+ffprobe build (immutable-commit fetch
  from the same upstream the static-ffmpeg pip package uses — that
  package itself was audited and rejected: mutable raw/main URL, no
  checksums, writes into site-packages); binaries are `-version`-probed
  via the existing _binary_runs before being trusted, installed under
  DATA_DIR (update-surviving, frozen-build-safe), zero new Python deps.
- ffmpeg_utils resolution chain gains the acquired-bundled tier — and
  ffprobe finally has a bundled tier at all (imageio-ffmpeg ships none),
  closing the source-install gap.
- New /media-tools router (loopback-gated, same contract as
  /system/set-env): status, acquire, {tool}/custom-path | use-system |
  restore, ytdlp/update | restore. Overrides persist via the existing
  env.FFMPEG_PATH / env.FFPROBE_PATH prefs convention — one store, no
  competing controls.
- yt-dlp updates: audited in-venv pip/uv upgrade and rejected (venv is
  uv-managed with no pip; yt-dlp is a locked dep, so the updater's
  --inexact drift sync would revert it). Instead the newest wheel —
  verified against PyPI's own sha256 — lands in a DATA_DIR overlay
  prepended to sys.path at startup: survives app updates, works in
  frozen builds, and "Restore tested version" is just deleting the
  overlay. Gallery now runs yt-dlp via `python -m yt_dlp` (module, not
  PATH) so the CLI can never be a user-install task either.
- /setup/preflight drops the three tool rows, carries a media_tools
  verdict, and self-heals: kicks the bundled download in the background
  when no tier resolves (never re-fires after a failure — the wizard's
  card owns Retry). diagnose + the ffmpeg-missing notification now point
  at Settings → Audio tools instead of package managers.

Frontend
- Wizard: new MediaEngineCard — renders NOTHING when the engine is ready,
  a one-line progress while acquiring, and only on failure an actionable
  card (Retry / Use a system copy / Choose file…).
- Settings → Audio tools (new category, System group): FFmpeg + FFprobe
  rows with version, path, origin badge, Use system copy / Choose file… /
  Restore bundled, header-level "Update bundled build"; yt-dlp row with
  Update + Restore tested version (+ restart affordance). Package-manager
  commands appear only as copyable prose, never executed.
- The FFmpeg-path override moved out of Settings → Network (pointer row
  deep-links to Audio tools; no second writer of env.FFMPEG_PATH).
  Notifications gain a settings-tab action type.
- All strings i18n (en + defaultValue), a11y labels on every control.

Tests: 29 new backend (origin classification, checksum/size/probe
rejection, override persistence, overlay update/restore, router gating +
route-shadowing) + preflight contract tests (tool rows gone, verdict
present, auto-acquire fires once); 14 new frontend (wizard hide/progress/
failure-card, Audio tools rows/badges/actions). Route snapshot
regenerated. Docs (macos/linux install, troubleshooting §7b) describe the
new reality in the same commit.

Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 12:22:49 +05:30
4aa9abe22a docs+scripts: install fixes — desktop-prod tauri resolution, Ubuntu white-screen guidance, honest GPU/prereq docs (#960 #961 #962) (#964)
* docs+scripts: install fixes — desktop-prod tauri resolution, Ubuntu white-screen guidance, honest GPU/prereq docs (#960 #961 #962)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(changelog): add the install-fixes batch under [Unreleased] (#964)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 19:34:01 +05:30
be1ec3ade0 fix(platform): declare Intel-Mac local backend unsupported — honest first-run gate + docs (#889); Windows portable-install docs (#766 follow-up) (#891)
torch >=2.3 ships no macOS x86_64 wheels (transformers 5.x needs torch >=2.6),
so `uv sync` can never resolve on an Intel Mac — per the platform-parity rule
the honest option is declaring the platform unsupported, not letting first
launch die in a raw resolver error:

- bootstrap.rs: pre-check on macOS x86_64 before any venv create / uv sync
  (first-run AND repair paths) fails fast with an actionable message
  (remote-backend escape hatch + docs link); healthy pre-torch-bump venvs are
  deliberately untouched. Unit test pins the message's load-bearing phrases.
- BootstrapSplash: routes the failure to a dedicated localized hint
  (bootstrap.hint_intel_mac, all 21 locales) and suppresses the useless
  Retry-oriented hints for it.
- README + docs/install/macos.md (+ troubleshooting #9): every Intel-Mac
  support claim now says UI-installs-but-backend-cannot-run, including the
  from-source path (also broken); remote backend documented as the only use.
- release.yml: #889 note on the macos-15-intel leg — artifact is UI-only;
  keep-or-drop is an owner call, deliberately not changed here.
- docs/install/windows.md: new "Portable install (Windows)" section promised
  in #766 — custom MSI wizard folder / msiexec INSTALLDIR=..., what lives in
  OmniVoiceStudio-Data next to the exe, and the Program-Files-greyed-out why.

Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 18:22:37 +05:30
Deepak VishwakarmaandClaude Opus 4.8 de3d83f14b docs: add rust as prerequisite for from-source builds (#704)
Adds Rust/Cargo as a from-source build prerequisite across the linux/macos/windows install docs. Thanks @Deepakv2104.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 03:03:23 +05:30
2574fccaf6 docs: community docs refresh — README, CONTRIBUTING, SECURITY, SUPPORT, Docker/macOS install (#341)
* docs: refresh community docs to match the project's current reality

- README: download badges now point to releases/latest (were frozen at
  v0.2.7); Intel-Mac note (pre-built bundle is Apple Silicon; source
  works on Intel; pre-built Intel tracked in #279)
- SECURITY: supported-versions table 0.2.x -> 0.3.x + 0.2.7 legacy row
- docs/install/docker.md: tag mapping matches docker.yml after #338 —
  :latest is the rolling main preview, :stable (new) pins releases
- PR template: removed the abolished two-RC/48h-soak ceremony; documents
  continuous-to-main
- CONTRIBUTING: new sections — what bot review looks like (CodeRabbit +
  Greptile), conventional-commit + issue-link expectations, the quality
  gates (cross-platform parity, 21-locale i18n + CJK allowlist, alembic,
  engine back-compat, local-first, loopback security posture), and a
  contribution-licensing grant that keeps the AGPL + commercial
  dual-license viable
- SUPPORT.md: new — channels, before-you-file checklist, expectations
- docs/install/macos.md: Intel caveat aligned with reality

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: codify the docs-sync hard rule — behavior changes update their docs in the same PR

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(agents): rtk rules for Antigravity — token-compressed tool output

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:58:43 +05:30
101cf2a6e7 ci(release): reinstate macOS Intel (x86_64) build target on macos-15-intel (#342)
Intel MacBook users had no installable artifact: the release matrix only
built aarch64-apple-darwin, and Rosetta 2 cannot run arm64 apps on Intel
(it only translates the other direction) — the rationale in the old
"Intel dropped" comment was backwards. Refs #279.

- Add a native `macos-15-intel` matrix leg (GitHub's designated x86_64
  migration target after macos-13 retired Dec 2025; standard image,
  supported through Aug 2027) building --target x86_64-apple-darwin
  with app,dmg,updater bundles.
- Existing per-TRIPLE steps already carry x86_64-apple-darwin cases
  (uv sidecar tar.gz, evermeet.cx ffmpeg/ffprobe — x86_64 Mach-O,
  natively correct on Intel), so the leg flows through the same
  Bundle/Build/Smoke/Verify steps untouched.
- The PR #290 signing path applies automatically: ad-hoc seal from
  tauri.conf.json signingIdentity "-", opt-in APPLE_* stable signing,
  and scripts/verify-macos-signing.sh both gated on runner.os == macOS.
- tauri-action includeUpdaterJson merges the new darwin-x86_64 platform
  key into latest.json alongside darwin-aarch64, so Intel installs
  auto-update on both Stable and Preview channels.
- docs/install/macos.md: table telling users which DMG (aarch64 vs x64)
  matches their Mac, and the from-source fallback for old releases.

Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 13:58:28 +05:30
Palash DebnathandClaude Opus 4.8 948bc76543 macOS: ad-hoc sign so users open without Terminal + signing/notarization verification (#290)
* chore(release): add macOS signing/Gatekeeper/notarization verification

Codify and enforce the macOS build-signing requirements. The release
pipeline built bundles and had opt-in Apple signing, but never verified
codesign/spctl/notarization — unsigned or broken bundles could ship silently.

- scripts/verify-macos-signing.sh: runs codesign --verify --deep --strict,
  spctl Gatekeeper assessment, per-nested-Mach-O signature check, stapler
  validate, and (opt-in) notarytool history. Report-only by default (unsigned
  dev/preview is expected); --require-signed fails on any unsigned/un-notarized
  component so a broken release stops instead of publishing an unsigned artifact.
- scripts/macos-dev-unquarantine.sh: local-dev-only quarantine stripper, with a
  loud "never a substitute for notarization" warning.
- release.yml: new "Verify macOS signing" step on the macOS leg — report-only on
  unsigned paths, STRICT on the opt-in signed stable path (same condition as
  "Configure Apple signing"), so signing/notarization failures fail the job.
- docs/macos-signing-verification.md: the canonical 10-point requirements +
  how-to-verify checklist, cross-linked to docs/install/macos.md and DESKTOP_RELEASE.md.

Verified locally: report-only PASS (exit 0) and --require-signed FAIL (exit 1)
against the real unsigned debug .app; release.yml parses as valid YAML.

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

* feat(macos): ad-hoc sign bundle so users open it without Terminal (no Apple ID)

The "app is damaged and can't be opened" error is caused by a broken/incomplete
code-signature seal (codesign --verify failed: "code has no resources but
signature indicates they must be present") on the quarantined download — there
is no GUI bypass for that variant on modern macOS, forcing users to run `xattr`.

Give the bundle a VALID ad-hoc signature at build time (free, no Apple Developer
account) via tauri.conf.json bundle.macOS.signingIdentity = "-". Verified through
a real `tauri build`: the produced .app is now flags=adhoc,runtime and passes
codesign --verify --deep --strict. A valid seal flips the Gatekeeper prompt from
the un-bypassable "damaged" to the GUI-bypassable "unidentified developer", which
users clear with right-click → Open / Settings → "Open Anyway" — no Terminal.

Still not notarized (that needs the paid Apple ID), so there's a one-time
confirmation rather than a clean double-click. The opt-in Developer-ID path is
unchanged: APPLE_SIGNING_IDENTITY (env) overrides the "-" default on the signed
stable release.

- tauri.conf.json: signingIdentity "-" (ad-hoc default).
- verify-macos-signing.sh: detect ad-hoc tier; report the no-Terminal GUI path
  in report-only, still FAIL it under --require-signed (production must notarize).
- docs/install/macos.md: lead the Gatekeeper section with right-click → Open;
  keep xattr as fallback for the harsher "damaged"/corrupted-download case.
- docs/macos-signing-verification.md: signing-tiers table + ad-hoc default note.
- release.yml: comment the ad-hoc default + env override on the signed path.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 13:26:04 +05:30
Palash DebnathandClaude Opus 4.8 f757b77a59 docs(install): clarify macOS Gatekeeper "damaged" workaround (#134) (#155)
Reword the macOS install doc's Gatekeeper section so it's findable by the
exact symptom ("App is 'damaged' / can't be opened"), and spell out both the
GUI path (right-click -> Open, or System Settings -> Privacy & Security ->
Open Anyway) and the Terminal path (xattr -dr com.apple.quarantine ...).

Explains WHY macOS shows "damaged" (the build isn't notarised yet, so it gets
quarantined) and why the workaround is safe (downloaded from the official
repo/Releases). Proper fix remains Apple Developer signing + notarisation,
already wired in release.yml behind the documented secrets.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 08:12:35 +05:30
Palash DebnathandClaude Opus 4.8 50954f7f43 feat(macos): wire Developer-ID signing + notarization; fix "app is damaged" docs (#134, #72) (#143)
The unsigned DMG triggers macOS Gatekeeper's misleading "app is damaged" block
(#134, #72). Two parts:

- release.yml: pass APPLE_CERTIFICATE / _PASSWORD / APPLE_SIGNING_IDENTITY /
  APPLE_ID / APPLE_PASSWORD / APPLE_TEAM_ID to tauri-action. It signs +
  notarizes the macOS bundle when these repo secrets are set, and is a no-op
  (today's unsigned build) when they're absent — so this is safe to merge now
  and "activates" the moment the maintainer adds an Apple Developer cert.
- docs/install/macos.md: explain the "damaged" message is Gatekeeper (not
  corruption), give the `xattr -cr` + right-click→Open workarounds, and add a
  "For maintainers" table of the required secrets. Removed the stale "tracked
  for v0.4" line (versioning rule: everything's on v0.3.0).

The in-app error→docs deeplink (GATEKEEPER_QUARANTINE) already targets the
#gatekeeper-quarantine anchor.

Refs #134, #72.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 11:40:27 +05:30
Palash DebnathandClaude Opus 4.7 715766cb04 Phase 1 Wave 2: per-OS install docs + Settings UI + error→docs deeplinks (#94)
* docs(install): per-OS install pages + drift validator + CI gate

Splits the 600-line README install section into self-contained per-OS docs
under docs/install/{macos,windows,linux,docker}.md plus a Top-10
troubleshooting index. Each OS doc is end-to-end: a user opens it and
reaches a working app following only commands inside that file.

Adds:
- docs/install/{macos,windows,linux,docker}.md  (OS-specific install paths)
- docs/install/troubleshooting.md               (top 10 install errors)
- docs/engines/cosyvoice.md                     (closes #55 docs half)
- docs/features/diarization.md                  (pyannote license flow)
- docs/setup/huggingface-token.md               (3-source cascade guide)
- scripts/validate-install-docs.py              (INST-06 docs-drift gate)
- tests/scripts/test_validate_install_docs.py   (B-5: validator self-tests)
- .github/workflows/ci.yml step running the validator on every PR

Implements INST-02 (README routing), INST-03 (macOS Gatekeeper anchor),
INST-12 docs half (Windows torch-compile-oom anchor), DOCS-01..05.

The validator is a one-way diff: every `<!-- validate -->`-tagged line
in docs must appear in scripts/desktop-prod.sh after normalisation
(prompt-prefix strip, CRLF, trailing whitespace, blank-and-comment skip).
A `<!-- validate: skip -->` marker opts out for human-readability blocks.
Its own 10 unit tests catch regressions in the gate itself.

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

* feat(deeplinks): links.py + error_docs_map (Python + TS mirror)

Adds the single source of truth for the project repo URL and the 4-class
error → docs taxonomy that both the in-app ErrorBoundary deeplink button
(Wave 2 Task 3) and the Phase 5 bug reporter will consume.

New:
- backend/core/links.py            — PROJECT_REPO_URL + BLOB_MAIN resolver
                                      (Tauri config first, pyproject fallback)
- backend/core/error_docs_map.py   — lookup(error_class) → docs URL
- frontend/src/utils/errorDocsMap.ts (TS mirror with classifyError helper)
- tests/backend/core/test_links.py + test_error_docs_map.py
- frontend/src/utils/errorDocsMap.test.ts

Resolves checker B-6 (links.py ownership) and Open Question #3 (which fork
the deeplinks resolve to — the Tauri updater endpoint wins, which points
at the desktop app fork debpalash/OmniVoice-Studio).

The TS BASE constant is documented as the second hardcoded URL drift site;
the keys-sync test (`test_keys_match_python_map` equivalent) guards the
4-class taxonomy contract between Python + TS halves.

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

* feat(ui): Settings → API Keys panel + ErrorBoundary docs deeplink

Wave 2 AUTH-03 UI half + ErrorBoundary deeplink wiring.

ErrorBoundary fallback now renders an "Open docs for this error" button
that classifies the thrown Error message (heuristic: pkg_resources → 401 /
HfHubHTTP → WebKit / white screen → quarantine / Gatekeeper) and opens the
matching docs anchor via Tauri shell.open (with a window.open fallback
in browser dev mode).

ApiKeysPanel consumes the Wave 1 resolver state endpoint:
  - 3 source rows (App / Env var / HF CLI) with set/unset indicator,
    masked token preview, whoami username + green check
  - "Active" badge on whichever source is currently serving the cascade
  - App-row only: Save (POST /api/settings/hf-token) +
    Clear (DELETE with optional "also clear HF CLI" confirm dialog)
  - "Test now" button refetches state (invalidates the resolver's
    validation cache via the same endpoint hit)

Panel mounted in the existing Settings → Credentials tab; the legacy
HF_TOKEN row from CREDENTIAL_FIELDS is filtered out so the two paths
don't fight over the same key.

Threat T-02-02: the panel never displays the full token. The masked
value comes from the resolver state endpoint; the full token only
crosses the IPC boundary on Save (POST) and is cleared from local
state on success.

Closes AUTH-03 fully (Wave 1 backend + this Wave 2 UI).

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

* feat(perf): INST-12 Disable torch.compile (Windows) toggle (backend + UI)

Wave 2 Task 4 — full INST-12 delivery per checker B-2/B-7 v0.3.0 fat-release
decision. Both the docs half (windows.md anchor, shipped in earlier commit)
and the runtime toggle are now in Phase 1.

Backend:
- backend/services/settings_store.py: adds get_text/set_text helpers for
  non-secret config (refuses to write to the encrypted hf_token key).
- backend/api/routers/settings.py: GET + PUT
  /api/settings/perf/torch-compile-disabled, both under the existing
  loopback guard (threat T-02-04).
- backend/services/engine_env.py: new `build_engine_env()` helper that
  centralises HF_TOKEN/YOUR_HF_TOKEN injection from the 3-source resolver
  AND injects TORCH_COMPILE_DISABLE=1 when the flag is set on win32.
  Phase 2 SubprocessBackend launchers should adopt the same helper.
- backend/services/sonitranslate.py: migrated to engine_env.build_engine_env()
  while preserving the source-level `env["HF_TOKEN"]` sentinel that
  test_sonitranslate_module_uses_resolver checks.

Frontend:
- frontend/src/components/settings/PerformancePanel.{jsx,css,test.jsx}:
  toggle UI with the explainer for #65; renders disabled with a "not
  applicable" badge on macOS/Linux.
- frontend/src/pages/Settings.jsx: mounts the panel into the Credentials
  tab alongside the API Keys panel.

Tests:
- tests/backend/test_perf_settings.py: 7 backend tests (default state,
  PUT persistence, T-02-04 non-loopback rejection, settings_store round-
  trip, env injection on win32, NO injection on macOS/Linux, NO injection
  when disabled).
- frontend PerformancePanel.test.jsx: 5 tests (renders from GET state,
  PUT on toggle, disabled on non-Windows platforms, pre-enabled state).

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

* docs(planning): Wave 2 SUMMARY + REQUIREMENTS status updates

- .planning/phases/01.../01-02-SUMMARY.md: full implementation report
  per template (truths, commits, tests, deviations, drift-site
  acknowledgments per W-3, launcher seam name for Phase 2,
  taxonomy keys for Phase 5).
- .planning/REQUIREMENTS.md: flips Wave 2 closures to Done:
    AUTH-03, INST-02, INST-03 (docs half), INST-06, INST-12,
    DOCS-01..05.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 06:22:10 +05:30