VOICE-4: Settings → Service "Voice input" group #776

Closed
opened 2026-05-02 21:41:55 +00:00 by code-lead · 3 comments
Collaborator

As an operator, I want a "Voice input" group inside the Settings → Service tab where I can pick my dictation language (with auto as the default), see whether speaches is reachable and which model is active, and toggle the feature on/off — all stored in service_config like every other service knob.

This is the settings-group half of the spec's P3 section. The health-probe carve-out lives in VOICE-2 (#774); this story focuses purely on the SVC-3 settings surface.

Acceptance criteria

Settings group (Settings → Service tab)

  • Lands inside the existing SVC-3 Settings → Service tab as a new group titled "Voice input". The group's section grouping key is whatever shape SVC-3 standardises on — match it exactly, do not invent a parallel mechanism.
  • Group contents:
    • Status row: <StatusPill> (live / unreachable / disabled), model name, transcribe_url host, "Test" button calling /architect/transcribe/health?refresh=1 (from VOICE-2 (#774)).
    • speech.enabled boolean toggle.
    • speech.transcribe_url text input.
    • speech.model text input.
    • speech.default_language select populated from speech.allowed_languages_json.
    • speech.max_audio_seconds number input.
    • speech.max_audio_bytes number input.
    • Reset-to-builtin per field — same affordance SVC-3 ships for every other field.
  • All writes go through SVC-3's CRUD endpoint and persist as scope='global' rows in service_config. No localStorage. No service.json writes.
  • All controls follow apps/web/CLAUDE.md primitives: <Button>, labelled inputs, rounded-card outer / rounded-compact inner, tokenised colours.

Tests

  • Vitest: stub the health endpoint, render the settings group, assert the status pill flips green/red/grey for each variant (live / unreachable / disabled).
  • Vitest: editing speech.default_language in the group fires the SVC-3 PATCH with the right field key.

Operational notes

  • One-line callout in docs/workspace.md (and a new docs/voice-input.md if it warrants one) reminding the operator to systemctl --user enable --now speaches before the feature works.

Out of scope

  • The health endpoint itself — VOICE-2 (#774).
  • The composer mic button + recording state machine — VOICE-3.
  • Hosted-STT API key entry. Defer until we adopt a hosted provider.
  • A separate top-level /settings/voice-input route — this is a group inside Settings → Service, not a new route.

References

  • specs/workspace-chat-voice-input.md — full spec (P3 section, "Settings group" sub-block).
  • specs/config-to-db.md — SVC-3 contract.
  • apps/web/CLAUDE.md — primitives, a11y baseline, radius/shadow conventions.

Dependencies

  • Blocked by VOICE-1 (#773) — the resolver + storage shape land in VOICE-1 (#773). Native dep edge written against VOICE-1 (#773)'s issue number.
  • Blocked by #752 (SVC-3) — the settings-group contribution mechanism is whatever SVC-3 ships. Native dep edge against #752 so this story auto-assigns the moment SVC-3 closes (label heuristic: area:dashboard).
  • Soft on VOICE-2 (#774) — the "Test" button calls VOICE-2 (#774)'s health endpoint. Status pill can stub during local dev, but the merge build expects VOICE-2 (#774) to be in.
As an operator, I want a "Voice input" group inside the Settings → Service tab where I can pick my dictation language (with `auto` as the default), see whether speaches is reachable and which model is active, and toggle the feature on/off — all stored in `service_config` like every other service knob. This is the settings-group half of the spec's P3 section. The health-probe carve-out lives in VOICE-2 (#774); this story focuses purely on the SVC-3 settings surface. ## Acceptance criteria ### Settings group (Settings → Service tab) - [ ] Lands inside the existing **SVC-3** Settings → Service tab as a new group titled "Voice input". The group's section grouping key is whatever shape SVC-3 standardises on — match it exactly, do not invent a parallel mechanism. - [ ] Group contents: - Status row: `<StatusPill>` (live / unreachable / disabled), model name, `transcribe_url` host, "Test" button calling `/architect/transcribe/health?refresh=1` (from VOICE-2 (#774)). - `speech.enabled` boolean toggle. - `speech.transcribe_url` text input. - `speech.model` text input. - `speech.default_language` select populated from `speech.allowed_languages_json`. - `speech.max_audio_seconds` number input. - `speech.max_audio_bytes` number input. - Reset-to-builtin per field — same affordance SVC-3 ships for every other field. - [ ] All writes go through SVC-3's CRUD endpoint and persist as `scope='global'` rows in `service_config`. **No localStorage.** **No `service.json` writes.** - [ ] All controls follow `apps/web/CLAUDE.md` primitives: `<Button>`, labelled inputs, `rounded-card` outer / `rounded-compact` inner, tokenised colours. ### Tests - [ ] Vitest: stub the health endpoint, render the settings group, assert the status pill flips green/red/grey for each variant (live / unreachable / disabled). - [ ] Vitest: editing `speech.default_language` in the group fires the SVC-3 PATCH with the right field key. ### Operational notes - [ ] One-line callout in `docs/workspace.md` (and a new `docs/voice-input.md` if it warrants one) reminding the operator to `systemctl --user enable --now speaches` before the feature works. ## Out of scope - The health endpoint itself — VOICE-2 (#774). - The composer mic button + recording state machine — VOICE-3. - Hosted-STT API key entry. Defer until we adopt a hosted provider. - A separate top-level `/settings/voice-input` route — this is a group inside Settings → Service, not a new route. ## References - `specs/workspace-chat-voice-input.md` — full spec (P3 section, "Settings group" sub-block). - `specs/config-to-db.md` — SVC-3 contract. - `apps/web/CLAUDE.md` — primitives, a11y baseline, radius/shadow conventions. ## Dependencies - **Blocked by VOICE-1 (#773)** — the resolver + storage shape land in VOICE-1 (#773). Native dep edge written against VOICE-1 (#773)'s issue number. - **Blocked by #752 (SVC-3)** — the settings-group contribution mechanism is whatever SVC-3 ships. Native dep edge against #752 so this story auto-assigns the moment SVC-3 closes (label heuristic: `area:dashboard`). - **Soft on VOICE-2 (#774)** — the "Test" button calls VOICE-2 (#774)'s health endpoint. Status pill can stub during local dev, but the merge build expects VOICE-2 (#774) to be in.
Collaborator

🦵 @charles kicked the queue — re-running address-review on @dev.

🦵 @charles kicked the queue — re-running address-review on @dev.
Collaborator

🦵 @charles kicked the queue — re-running implement on @dev.

🦵 @charles kicked the queue — re-running implement on @dev.
Collaborator

🦵 @charles kicked the queue — re-running implement on @dev.

🦵 @charles kicked the queue — re-running implement on @dev.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
charles/claude-hooks#776
No description provided.