Skip to content

Add the dictation capture and shortcut state machine - #56

Open
vulcanum-app[bot] wants to merge 4 commits into
feat/independent-dictation-settingsfrom
feat/dictation-capture-state-machine
Open

Add the dictation capture and shortcut state machine#56
vulcanum-app[bot] wants to merge 4 commits into
feat/independent-dictation-settingsfrom
feat/dictation-capture-state-machine

Conversation

@vulcanum-app

@vulcanum-app vulcanum-app Bot commented Aug 12, 2026

Copy link
Copy Markdown

Summary

  • add an independent microphone-only dictation runtime with correlated state events and bounded temporary capture
  • implement toggle and push-to-talk transitions, repeat suppression, stale-release handling, meeting exclusion, and Wayland/platform registration errors
  • transcribe directly through the worker, copy plain text to the clipboard, and avoid all recording and pipeline persistence

Validation

  • pnpm format
  • pnpm validate
  • pnpm lint:rust
  • pnpm test:all

Depends on #55.

@vulcanum-app vulcanum-app Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

CRITICAL

  • src-tauri/src/app/commands/recordings.rs:24-29 checks and releases dictation_runtime before beginning a meeting, while src-tauri/src/dictation/runtime.rs:247-288 separately checks for an active meeting before beginning dictation. Concurrent starts can both pass their checks and capture simultaneously, violating the required mutual exclusion. Make admission and the capture-state reservation atomic under shared ownership.
  • src-tauri/src/app/commands/settings.rs:215-220 dispatches every press/release in independent spawn_blocking tasks. A push-to-talk release can run before its preceding press, be ignored as stale, and leave the later press capturing indefinitely. Serialize shortcut events before applying the state machine.

WARNINGS

  • src-tauri/src/dictation/runtime.rs:462-468 drops the arboard::Clipboard immediately after writing. On supported Linux/X11 without a clipboard manager, the owner must remain alive to serve selection requests, so the reported copied dictation cannot be pasted. Retain clipboard ownership for the active app lifetime.
  • src-tauri/src/app/commands/settings.rs:37 re-registers shortcuts while a push-to-talk session can be active. Changing the shortcut unregisters the held key before release; changing the mode makes its release use toggle semantics. Either path leaves the session capturing after release. Reject/defer those changes or stop the active session before re-registration.

SUGGESTIONS

  • Add integration-level tests for serialized shortcut delivery, concurrent meeting/dictation admission, and the active push-to-talk settings-update path; current tests exercise only the pure transition helpers.
  • Local validation passed: cargo fmt --all --check, cargo clippy --all-targets --all-features -- -Dwarnings, cargo check, and cargo test (72 passed). GitHub CI’s Linux, macOS, and Windows jobs are successful.

@vulcanum-app vulcanum-app Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

CRITICAL

  • src-tauri/src/dictation/runtime.rs:275-280 deadlocks every dictation start: handle_shortcut already holds capture_admission while calling start_dictation, which locks the same non-reentrant mutex again. The first shortcut press never opens the microphone and keeps future recording/settings operations blocked. Keep a single admission lock acquisition across action selection and start, or remove the nested lock.
  • src-tauri/src/app/commands/recordings.rs:22-25 waits for capture_admission before checking dictation state. Since the dispatcher retains that lock through stop_and_transcribe and worker transcription, a meeting-start request during dictation blocks until transcription finishes and then starts instead of returning the required active-dictation error. Use nonblocking admission or release the lock once a state reservation is made.
  • src-tauri/src/app/commands/settings.rs:31-42 still permits a shortcut/mode update between a PTT press selecting Start and start_dictation marking the runtime capturing. The capture starts under the old mode, while its release is processed under the new mode; changing PTT to toggle ignores that release and leaves capture running. Reserve the pending start before settings can pass its guard.

WARNINGS

  • src-tauri/src/app/commands/settings.rs:203-204 registers the meeting shortcut before the dictation shortcut but never validates they differ. The UI accepts equal values; dictation registration then fails after meeting registration succeeds, leaving dictation unavailable for a persisted configuration. Reject duplicate bindings before persistence/registration.
  • src-tauri/src/dictation/runtime.rs:436-438 emits dictation-state-update, but src/components/app-shell/hooks/useAppRuntime.hook.ts:208-238 has no listener and the frontend does not call dictation status/cancel commands. Capture, failure, copied text, and cancellation states are therefore not observable or actionable in any frontend window.

SUGGESTIONS

  • The previous clipboard-ownership change is resolved: the runtime retains arboard::Clipboard. The event channel also resolves the prior press/release execution-order race, once the admission-lock deadlock is corrected.
  • Local checks passed: pnpm ci-run, pnpm type-check, pnpm lint:rust, pnpm lint:py, and pnpm test:all (48 frontend, 72 Rust, 42 Python tests).
  • Add coverage for admission contention, queued PTT start plus settings update, duplicate shortcuts, and frontend dictation event/command wiring.

@vulcanum-app vulcanum-app Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

CRITICAL

  • src-tauri/src/app/commands/settings.rs:28-48 permits saving unrelated settings during an active push-to-talk capture, then calls refresh_global_hotkey, which unconditionally unregisters/re-registers all shortcuts. If release occurs in that interval, the Released event is lost and dictation remains capturing until cancellation or the duration limit. Do not refresh unchanged shortcut registrations during active capture, or reject all settings saves until the key is released.

WARNINGS

  • src-tauri/src/app/commands/settings.rs:45-49 persists settings before refresh_global_hotkey validates the dictation-specific shortcut constraints. Space, for example, passes shared settings validation but fails validate_dictation_hotkey; it remains persisted while unregister_all leaves dictation unavailable. Validate dictation registration constraints before persisting, and preserve/recover the prior registration on failure.
  • src/components/app-shell/hooks/useAppRuntime.hook.ts:225-238 starts getDictationStatus() before registering the state listener. A transition after the read resolves but before listener registration is lost, leaving a newly mounted frontend stale without a capture/cancel control. Register first and fetch status afterwards (or fetch again once registered).

SUGGESTIONS

  • Previous critical findings are resolved: no nested dictation admission lock remains, pending starts block shortcut-setting changes, duplicate shortcut bindings are rejected, and dictation status/cancel commands now reach the frontend.
  • Local validation passed: pnpm ci-run, pnpm type-check, pnpm lint:rust, pnpm lint:py, and pnpm test:all (48 frontend, 72 Rust, 42 Python tests).
  • Add tests for unchanged-shortcut saves during PTT, invalid dictation-only shortcut persistence, and the initial event-subscription/status-fetch race.

@vulcanum-app vulcanum-app Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

CRITICAL

  • src-tauri/src/dictation/runtime.rs:432-440 sends transcriptionProfile: "dictation", but worker/app/dtos.py:9,163 declares the field as Literal["conservative_vad"]. Every completed dictation is rejected by worker payload validation before transcription, so no text is copied. Send conservative_vad or add a real dictation profile end to end.
  • src-tauri/src/app/commands/settings.rs:45 calls the Wayland-rejecting validate_dictation_hotkey for every settings update. On Wayland, saving unrelated settings such as microphone or output directory now fails even when the dictation shortcut is unchanged. Validate platform-specific dictation registration only when that binding changes, while continuing to reject new unsupported bindings.

WARNINGS

  • None

SUGGESTIONS

  • Prior shortcut refresh, validation ordering, and frontend status initialization findings are resolved.
  • Local validation passed: pnpm ci-run, pnpm type-check, pnpm lint:rust, pnpm lint:py, and pnpm test:all (48 frontend, 72 Rust, 42 Python tests).
  • Add a worker-payload contract test for dictation transcription profiles and a Wayland unchanged-settings regression test.

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.

0 participants