Synchronize VoiceStudio release metadata, lockfiles, installers, container references, documentation, and the dated v0.5.2 changelog after all planned fixes landed.
Closes #1716.\n\nDefines supported, best-effort override, unverified, and unsupported architecture evidence; marks RX 6700 XT/gfx1031 over WSL2 ROCDXG as unverified; and requires routing, utilization, and CPU-fallback evidence before claiming acceleration.
Prepare the tested main branch for the v0.5.1 patch release with synchronized version sources, mirrors, lockfiles, release notes, and install guidance.
Closes#1687.
Refresh current v0.5.0/0.5 tag examples, document API-key and share-PIN behavior, and require encrypted private-overlay access for remote deployments. Keeps the Docker install guide and changelog synchronized.
* feat(startup): bind the socket in ~1s and narrate startup step by step
The structural fix for the "can't reach the local backend" class (~1 in 5
of every issue ever filed): uvicorn served nothing until torch import
(10-20s cold), the 30-router fan-out, an import-time DB migration, the
cuDNN preload, and alembic all finished — every slow or fragile step
rendered as an unexplained dead backend.
main.py now keeps module scope fast and defers the heavy work:
- _phase_a_build (executor thread): prefs/env restore + #963 migration,
yt-dlp overlay, cuDNN preload, torchaudio, model_manager, router
imports — order preserved, literal imports so PyInstaller still traces.
- _phase_a_finalize (event loop, no awaits → atomic wrt requests):
include_router, mounts, MCP, SPA, openapi bust.
- _phase_b: the old lifespan startup body; handles on app.state so
shutdown survives a startup that never finished.
- Eager mode (pytest / OMNIVOICE_EAGER_INIT=1) runs everything at import
— byte-equivalent behavior for the ~100 lifespan-less TestClient sites
and for embedders (dump_api_routes, probe boot runner opt in).
While starting: /health answers 503 with the current step, new
/startup/progress serves the full ledger (always 200), and
StartupGateMiddleware 503s everything else with the [starting] marker
(same skip-the-Report-button convention as [shutting_down]). A deferred
failure keeps import-crash semantics: traceback to stderr → shell crash
forensics, run sentinel stays uncleared, exit 1 names the failed step.
Shell: startup_progress() probe (marker-header-gated so a foreign
responder can't narrate the splash) feeds per-step log lines into the
launch poll and the supervisor's reconnect wait. --health-check absorbs
the deferred init (60→180s); --diagnose runs Phase A up front so it
still sees restored prefs. Docker HEALTHCHECK semantics unchanged
(curl -f fails on 503 exactly as it did on connection-refused).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(startup): join the Phase A thread on shutdown; async fail-path sleep
Bot-review harvest on #1550: cancelling the deferred-startup task cannot
stop the executor thread inside Phase A's blocking imports — shutdown now
waits (bounded, only when a build started and hasn't finished) on a
thread-completion event so interpreter teardown can't race a mid-import
(#1000 class). The failure path's last-poll beat is now awaited, not
time.sleep — a blocking sleep froze the very loop that beat exists to let
serve. Also: dump_api_routes forces eager (assignment, not setdefault),
and the integration test's child gets DEVNULL instead of an undrained
pipe that could wedge a cold boot.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(startup): close the Phase A submission race; CodeQL nits
Review finds on #1550: shutdown could sample _phase_a_started unset
while the executor callable was queued-but-not-running, skipping the
thread join. started is now set BEFORE submission, the submission is
shielded so a cancel can't strand a queued callable that would never set
_phase_a_finished, and the wrapper sets finished on every exit including
the already-built early return. Contract pinned by
test_phase_a_thread_join_contract. Plus explanatory comments on the new
bare excepts and a consistent return in the gate's websocket branch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The About page reported 'Compute device: cpu / GPU active: no / VRAM 0.00 GB' on a machine with a working GPU. Every line was true and none was usable — it is also exactly what a machine with no GPU at all reports, so the report could not distinguish a driver that isn't loaded from a container that cannot open the device from a ROCm older than the card.
The probe already knew all of it; torch.cuda.is_available() returning False simply produced no note. Each cause now reads differently: a missing device node names the --device flags, a permissions failure names --group-add and how to find the host's real render/video GIDs (copied numbers are the most common way this ends up on CPU in Docker), a card newer than the shipped ROCm points at rocminfo, an HSA_OVERRIDE_GFX_VERSION that is doing more harm than good is named first because it is both likelier and cheaper to test, and an unreachable NVIDIA driver gets its own advice.
The probe never diagnoses from a measurement it did not complete: when torch.cuda.is_available() itself raises, the exception is reported and no device findings are asserted beside it. Metadata access that raises is contained too — this runs on the path whose whole job is to explain a failure, so it cannot become one.
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.
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.
* release: v0.4.1
Seven user-reported issues fixed since v0.4.0 (#1221–#1229). Version bumped
across the single source of truth (frontend/package.json) and its three
toolchain mirrors; [Unreleased] renamed to the release section that
release.yml extracts verbatim as the GitHub Release body.
Docker tag examples in docs/install/docker.md and deploy/dockerhub-overview.md
updated to 0.4.1 (docs-sync rule).
* release: #1239 review — sync Cargo.lock to 0.4.1
Greptile: the manifest said 0.4.1 while Cargo.lock still recorded 0.4.0, so a
`cargo build --locked` (and the Tauri bundler's own locked build) would fail
on the mismatch. Regenerating locally updated it but it was never staged.
* release: re-sync [0.4.1] after the fix merges, date it 2026-07-27
Picks up everything merged since the section was first written: the first-run
wizard chrome (#1241), the MCP host allowlist (#1249), the macOS 12 startup
crash (#1245), and the six error-message fixes (#1247, #1251, #1254, #1256,
#1257, #1262).
Deliberately NOT included:
- the dub delete-resurrection fix (#1252, #1253) — split to #1270 after it
needed six rounds of correction, the last two finding that the fix did not
close the reported case and that its own bound reintroduced it;
- the Linux AppImage WebKit fix (#1258, #1244) — held on #1265 pending
confirmation on a Mesa 26.1 host, which nobody has run.
Two P1s, both correct:
- Being IN the override map was treated as proof of compatibility. If the
wheel ships neither the native arch nor the remap target, setting
HSA_OVERRIDE_GFX_VERSION only changes WHICH kernel is missing — gfx1151 with
a gfx1030-only build was routed to the GPU and would fail at launch. Both
arch_unsupported() and _configure_rocm_if_needed() now require the target to
be present, and fall back to CPU otherwise.
- An EMPTY arch list means the build's metadata is unavailable, not that the
GPU is unsupported. The remap branch read that unknown state as a confirmed
mismatch and would push a natively-supported gfx1151 onto foreign gfx1100
kernels. Now fails open and changes nothing, matching the fail-open contract
the rest of the probe follows.
ROCM_GFX_OVERRIDES values are now the target gfx NAME rather than the HSA
version string, so the "is the target present?" check is a direct membership
test; hsa_override_for() derives the env-var form, covered by a test that
every entry in the map converts cleanly.
Also: CHANGELOG entries shortened with refs last, and the MD028 blank line
between the two docker.md blockquotes (CodeRabbit).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The GPU compatibility gate built a CUDA-namespace tag from
`get_device_capability()` (`sm_115` on a gfx1151 Strix Halo) and looked for it
in `torch.cuda.get_arch_list()` — which on a ROCm wheel returns gfx *names*
(`gfx1100`, `gfx1151`, …). The two namespaces can never intersect, so
`check_device_compatibility()` returned False on every ROCm build and
`get_best_device()` silently returned "cpu": torch saw the GPU,
`torch.cuda.is_available()` was True, and the app ran on the CPU anyway.
The comparison was copy-pasted in three places, all with the same bug, so it
now lives once in `core.device_caps.arch_unsupported()` and branches on the
build (gfx names on ROCm, sm_/compute_ tags on CUDA):
- `model_manager.check_device_compatibility` — the CPU force-route, plus a
ROCm-specific remedy instead of telling AMD users to install a cu128 wheel
- `device_caps._probe` — the kernel-risk note that downgraded the routing badge
- `engine_env._cuda_arch_supported_for_compile` — torch.compile off on all AMD
`_configure_rocm_if_needed` also applied `HSA_OVERRIDE_GFX_VERSION` from a
static map without checking whether the GPU needed it, remapping cards the
installed build supports natively onto foreign kernels. It now applies only
when the native GFX ID is genuinely absent from the arch list, and knows
gfx1150/gfx1151 (Strix Point/Halo).
Regression test: tests/test_rocm_arch_gate.py pins the reporter's host
resolving to "cuda", a genuine ROCm mismatch still being caught, the CUDA
path (#756 Blackwell fallback) unchanged, and the narrowed override.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the exact-version / minor / ROCm pin examples in the Docker Hub
overview (deploy/dockerhub-overview.md — source of the hub.docker.com page,
re-synced on this main push) and docs/install/docker.md to the v0.4.0
release. GHCR's package page inherits the repo README and the current
org.opencontainers.image.description label, both already accurate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add a what-you-need line (RAM/disk/GPU from the README requirements
table, compressed pull sizes measured from the registry) so homelab
users can size the deployment before pulling.
- Update stale version examples (:0.3.6 / :0.3.17 -> :0.3.22).
- Fix the 'main is always one patch ahead' claim — with
AUTO_VERSION_BUMP off, main can equal the released version; say
'at or ahead of the last release', which is true in both modes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Docker image was CUDA-only, so AMD GPUs (e.g. RX 7900 XTX under
Podman) silently ran on CPU. Every preview and release now also ships a
ROCm variant built from the same Dockerfile:
- deploy/Dockerfile: parameterize the runtime base with a BASE_IMAGE
build-arg (default unchanged: pytorch/pytorch 2.8.0 CUDA). Add
PIP/UV_BREAK_SYSTEM_PACKAGES for the ROCm base's PEP-668-marked
Ubuntu 24.04 Python (no-op on the conda CUDA base), and a build-time
GPU_FLAVOR guard asserting the dependency install did not clobber the
base image's GPU torch/torchaudio — a future dep bump that forces a
torch reinstall now fails the build instead of shipping a CPU-only
"ROCm" image.
- .github/workflows/docker.yml: new build-and-push-rocm job (separate
job for runner disk — the ROCm base is ~25 GB unpacked, so it frees
the preinstalled toolchains first). Tags mirror the CUDA semantics
with a -rocm suffix (:rocm rolling preview, :stable-rocm, :X.Y.Z-rocm,
:X.Y-rocm, :sha-xxxx-rocm) on both GHCR and Docker Hub, same secret
gating. flavor latest=false so release tags can't clobber :latest.
No cache-to: the ROCm layers would blow the 10 GB GHA cache budget.
- deploy/docker-compose.yml: new opt-in 'rocm' profile passing the GPU
through via /dev/kfd + /dev/dri, with HSA_OVERRIDE_GFX_VERSION=11.0.0
documented (user-set, not baked in — backend auto-sets it for known
consumer GFX IDs).
- Docs-sync: docker.md (ROCm quick start incl. Podman/Quadlet, tag
table, troubleshooting), dockerhub-overview.md, README AMD note,
linux.md ROCm section cross-link, CHANGELOG [Unreleased].
Base image: rocm/pytorch:rocm7.2.4_ubuntu24.04_py3.12_pytorch_release_2.8.0
— torch 2.8.0 exactly matches the CUDA image (identical resolution, so
uv keeps it), py3.12 satisfies requires-python >=3.11 (the ubuntu22.04
variants are py3.10 and do not).
Closes#1165
Co-authored-by: mergetest <nizam4103@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Migrations in Docker fell back to the additive-column self-heal because
alembic.ini was never copied; the real migration chain now runs. The
HEALTHCHECK covers plain docker-run (compose files keep their own), with
a start period sized for first-boot schema creation. Docs example tag
freshened.
Co-authored-by: mergetest <test@local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Link the published Docker Hub repo (https://hub.docker.com/r/palashdeb/
omnivoice-studio) as an official image alongside GHCR in the README install
list and docker.md header. Same images, same tags.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Push the same images (same tag set: :latest rolling main, :stable/:X.Y.Z
releases, :sha-) to docker.io/palashdeb/omnivoice-studio alongside GHCR.
Gated on DOCKERHUB_USERNAME/DOCKERHUB_TOKEN secrets — without them the
build still publishes to GHCR only. Docs-sync: docker.md mirror note.
Requires repo secrets: DOCKERHUB_USERNAME, DOCKERHUB_TOKEN.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* 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>
In Docker the loopback origin gate (`require_loopback`) is unenforceable:
Docker's NAT rewrites `request.client.host` to the bridge gateway (e.g.
172.17.0.1) even for a localhost-only `-p 127.0.0.1:3900:3900` mapping, so every
request looks non-loopback. The gate then 403s the operator out of the routes
the web UI needs — `/system/*` (incl. `/system/info`, which left the version
blank, re-breaking #249 in Docker) and `/api/settings/*` (HF-token entry) —
surfacing as "Loopback origin required" all over the UI.
Fix: add an explicit, opt-in `OMNIVOICE_SERVER_MODE` flag. When set,
`require_loopback` becomes a no-op; exposure is then governed by the operator's
port mapping plus the optional share PIN (NetworkAccessMiddleware still 401s
unauthenticated non-loopback clients whenever a PIN is set). The Docker image
sets `OMNIVOICE_SERVER_MODE=1` (Dockerfile + documented in compose).
Security: the desktop build NEVER sets this, so its loopback boundary is
unchanged — LAN share guests are still denied the admin/system routes. New
unit tests lock the contract (strict 403 by default incl. the PR #81 vectors;
relaxed only under the flag). Existing non-loopback 403 tests still pass.
Docs: docker.md troubleshooting entry for "Loopback origin required".
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Docker web build has no Tauri runtime, so Settings → About → Version read
`getVersion()` (Tauri-only) and rendered a dash — leaving Docker users unable
to tell which version they were running (issue #249). The update-channel toggle
was also shown there even though the auto-updater is desktop-only.
- Backend: expose the single-source `APP_VERSION` over HTTP — add it to
`/system/info` (`app_version`) and `/health` (`version`). Both are model-free
and the latter is zero-auth.
- Frontend: Settings → About → Version falls back to `info.app_version` when
no Tauri `getVersion()` is available, so Docker shows the real 0.3.x version.
- Frontend: hide the update-channel toggle, update-endpoint row, and the
"Check for updates" button outside Tauri — the Docker image updates by
pulling a new tag, not via the in-app updater.
- Docs: fix the wrong package name in the version-check command
(`omnivoice-studio` → `omnivoice`) and document the new `/health` version
field + the in-UI version row.
Tests: assert `/system/info.app_version` and `/health.version` equal
APP_VERSION (test_router_smoke.py). The stale `:latest` tag itself was already
fixed in #252; cutting a v0.3.x release repopulates it.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(docker): fix stale :latest tag and add push-to-main trigger (closes#251, addresses #249)
Two bugs caused the Docker image to be stale (showing v0.2.7 inside a
:latest/:0.3.x-tagged image):
1. **`:latest` was never set on tag pushes.** The metadata-action rule
`type=raw,value=latest,enable={{is_default_branch}}` evaluates
`is_default_branch` as false on tag-triggered runs (which run in a
detached-HEAD context, not on the default branch). The tag rule was
replaced with `enable=${{ github.ref_type == 'tag' }}` so `:latest`
is updated on every `v*` tag push.
2. **No trigger for main-branch pushes.** There was no way to keep an
up-to-date `:main` edge image between releases. Added
`push: branches: [main]` which produces a `:main` rolling tag.
Also added a note in the workflow and docs clarifying that the
update-channel toggle (Settings → About) is a Tauri desktop feature and
does not apply to the Docker image (headless web-server build).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(docker): gate mutable tags to push events + guard :latest against prereleases (PR #252 review)
- semver / :latest / :main now require github.event_name == 'push' so a manual
workflow_dispatch only ever emits a throwaway :sha- tag (no mutable-tag rollback)
- :latest excludes prerelease tags (ref contains '-') so an rc/beta can't clobber it
- header + tag-strategy comments corrected (:sha- emits on every trigger)
Addresses greptile + coderabbit review on #252.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Docker users reported Settings -> Engines failing with 'Failed to load engines:
Failed to fetch'. Two problems: (1) the two API-base resolvers diverged
(client.ts honored VITE_API_URL; apiBase.ts honored VITE_OMNIVOICE_API), and
the docs documented VITE_OMNIVOICE_API -- which the Engines request path
ignored; (2) VITE_* is inlined at BUILD time, so a prebuilt ghcr.io image has
no working runtime override at all for reverse-proxy / split-origin deploys.
- backend: when OMNIVOICE_PUBLIC_API_BASE is set, inject it into index.html as
window.__OMNIVOICE_API_BASE__ (core/spa_inject.py; validated to a plain
http(s) URL so it can't break out of the <script>). Unset (default) ->
StaticFiles serves index.html untouched (same-origin, zero overhead).
- frontend: both resolvers (client.ts _resolveApiBase + utils/apiBase.ts) now
read the runtime global FIRST, then VITE_OMNIVOICE_API/VITE_API_URL, then
fall through to same-origin. client.ts also strips trailing slashes and
recognises __TAURI_INTERNALS__ (parity with apiBase.ts/external.ts).
- docs: docker.md + troubleshooting.md document OMNIVOICE_PUBLIC_API_BASE as the
runtime override that works on the prebuilt image (the old VITE_OMNIVOICE_API
docker run -e example never worked -- build-time inlining).
- tests: spa_inject helpers (inject + URL validation/breakout); resolver
precedence for the runtime global + VITE_OMNIVOICE_API in both test files.
Default same-origin behavior is unchanged on every platform; override is opt-in.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* 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>