This document provides guidance for AI agents working on the Quinoa codebase.
Quinoa is a meeting recording and transcription application. It uses:
- Python (PyQt6) for the GUI and application logic
- Rust (PyO3) for audio capture via PipeWire
- Google Gemini for AI transcription
- SQLite for local storage
This project uses Ticket (tk) for task tracking. It provides persistent memory across sessions and helps you find ready-to-work tasks.
- Start of session: Run
tk readyto see what to work on. - During work: Create tickets for new discoveries:
tk create "Task description" - Taking ownership: Mark ticket as in progress:
tk start <ticket-id>
- Completion: Close the ticket:
tk close <ticket-id>
tk ls: List all open ticketstk ready: List unblocked tickets ready for worktk show <id>: View ticket detailstk query: Output tickets as JSON for programmatic filteringtk help: See all commands
All Python code must pass:
ruff check quinoa/ tests/
mypy quinoa/Rust code should compile without warnings:
cargo check --features real-audio- Python: Follow ruff's default rules (pycodestyle, pyflakes, isort, flake8-bugbear)
- Type Hints: Use modern Python 3.10+ syntax (
str | NonenotOptional[str]) - Logging: Use the
quinoalogger, neverprint()statements - Constants: Hard-coded values go in
quinoa/constants.py - Stylesheets: UI styles go in
quinoa/ui/styles.py
| Type | Location |
|---|---|
| Constants | quinoa/constants.py |
| Logging config | quinoa/logging.py |
| UI styles | quinoa/ui/styles.py |
| Database ops | quinoa/storage/database.py |
| Transcription | quinoa/transcription/ |
| Audio processing | quinoa/audio/ |
| Calendar integration | quinoa/calendar/ |
| File Search | quinoa/search/ |
| UI components | quinoa/ui/ |
quinoa/ui/main_window.py- Main application window, worker initializationquinoa/ui/middle_panel.py- Recording controls, notes/transcript viewerquinoa/ui/calendar_panel.py- Left panel with meetings listquinoa/storage/database.py- SQLite operations (thread-local connection pooling)quinoa/config.py- Configuration with keyring integration for API key storagequinoa_audio/src/capture/session.rs- Rust recording session with PipeWire integration, mic switching
- Add to
quinoa/constants.py - Import and use in relevant files
- Run
ruff checkandmypy
- Add to
quinoa/ui/styles.py - Import and apply in
main_window.pyor relevant component - Keep styles DRY - use functions for parameterized styles
- Update
_init_db()indatabase.py - Add migration logic for existing databases (check column exists before adding)
- Update relevant methods with new fields
- Add type hints to all method signatures
- Changes require rebuilding:
maturin develop --features real-audio - Test with mock backend first:
maturin develop(no features) - PipeWire must be running for real audio tests
python -m quinoa.main --testThis starts a 3-second recording and exits.
pytest tests/python/- Recording starts/stops correctly
- Pause/resume works
- Device hot-plug detected
- Mic switching during recording works
- Transcription completes
- History tab shows recordings
PipeWire requires a dedicated event loop and real-time buffer handling. Python's GIL would cause audio glitches. Rust provides the necessary performance while PyO3 makes integration seamless.
Recording microphone and system audio to separate channels allows Gemini to distinguish between the user and remote participants for better speaker diarization.
Security best practice - API keys are stored in the system keyring (GNOME Keyring, KDE Wallet) rather than plain text config files.
Use tk ls to see all open tasks and tk ready to see what is ready to be worked on.
See ROADMAP.md for high-level planning and completed milestones.
Rebuild the Rust extension:
cd quinoa_audio && maturin develop --features real-audio && cd ..Install with uv:
uv pip install -e ".[dev]"Run mypy to catch issues:
mypy quinoa/Auto-fix with ruff:
ruff check --fix quinoa/