- Introduced a new section on Code Reuse emphasizing the importance of searching for existing implementations before writing new code. - Outlined a mandatory search process and identified anti-patterns to avoid, promoting best practices for code efficiency and maintenance. - Updated client submodule reference.
36 KiB
CLAUDE.md
This file provides guidance to Claude Code when working with code in this repository.
Project Overview
NoteFlow is an intelligent meeting notetaker: local-first audio capture + navigable recall + evidence-linked summaries. It is a client-server system built around a gRPC API for bidirectional audio streaming and transcription. The repository includes:
- Python backend (
src/noteflow/) — gRPC server, domain logic, infrastructure adapters - Tauri + React desktop client (
client/) — Rust for IPC, React UI (Vite)
The gRPC schema is the shared contract between backend and client; keep proto changes in sync across Python, Rust, and TypeScript.
Quick Orientation
| Entry Point | Description |
|---|---|
python -m noteflow.grpc.server |
Backend server (src/noteflow/grpc/server/__main__.py) |
cd client && npm run dev |
Web UI (Vite) |
cd client && npm run tauri dev |
Desktop Tauri dev (requires Rust toolchain) |
src/noteflow/grpc/proto/noteflow.proto |
Protobuf schema |
client/src/api/tauri-adapter.ts |
TypeScript → Rust IPC bridge |
client/src-tauri/src/commands/ |
Rust Tauri command handlers |
Build and Development Commands
# Install (editable with dev dependencies)
python -m pip install -e ".[dev]"
# Run gRPC server
python -m noteflow.grpc.server --help
# Run Tauri + React client UI
cd client && npm install && npm run dev
cd client && npm run tauri dev # Desktop (requires Rust)
# Tests
pytest # Full suite
pytest -m "not integration" # Skip external-service tests
pytest tests/domain/ # Run specific test directory
pytest -k "test_segment" # Run by pattern
# Docker (hot-reload enabled)
docker compose up -d postgres # PostgreSQL with pgvector
python scripts/dev_watch_server.py # Auto-reload server
Forbidden Docker Operations (without explicit permission)
docker compose build/up/down/restartdocker stop/docker kill- Any command that would interrupt the hot-reload server
Quality Commands (Makefile)
Always use Makefile targets instead of running tools directly.
Primary Quality Commands
make quality # Run ALL quality checks (TS + Rust + Python)
make quality-py # Python: lint + type-check + test-quality
make quality-ts # TypeScript: type-check + lint + test-quality
make quality-rs # Rust: clippy + lint
Python Quality
make lint-py # Basedpyright lint → .hygeine/basedpyright.lint.json
make type-check-py # Basedpyright strict mode
make test-quality-py # pytest tests/quality/
make lint-fix-py # Auto-fix Ruff + Sourcery issues
TypeScript Quality
make type-check # tsc --noEmit
make lint # Biome linter → .hygeine/biome.json
make lint-fix # Auto-fix Biome issues
make test-quality # Vitest quality tests
Rust Quality
make clippy # Clippy linter → .hygeine/clippy.json
make lint-rs # Code quality script → .hygeine/rust_code_quality.txt
make fmt-rs # Format with rustfmt
make fmt-check-rs # Check rustfmt formatting
Formatting
make fmt # Format all code (Biome + rustfmt)
make fmt-check # Check all formatting
E2E Tests
make e2e # Playwright tests (requires frontend on :5173)
make e2e-ui # Playwright tests with UI mode
make e2e-grpc # Rust gRPC integration tests
Architecture
src/noteflow/
├── domain/ # Entities, ports, value objects
│ ├── entities/ # Meeting, Segment, Summary, Annotation, NamedEntity, Integration, Project, Processing, SummarizationTemplate
│ ├── identity/ # User, Workspace, WorkspaceMembership, roles, context
│ ├── auth/ # OIDC discovery, claims, constants
│ ├── ports/ # Repository protocols
│ │ ├── repositories/
│ │ │ ├── transcript.py # MeetingRepository, SegmentRepository, SummaryRepository
│ │ │ ├── asset.py # AssetRepository
│ │ │ ├── background.py # DiarizationJobRepository
│ │ │ ├── external/ # WebhookRepository, IntegrationRepository, EntityRepository, UsageRepository
│ │ │ └── identity/ # UserRepository, WorkspaceRepository, ProjectRepository, MembershipRepository, SummarizationTemplateRepository
│ │ ├── unit_of_work.py # UnitOfWork protocol (supports_* capability checks)
│ │ ├── async_context.py # Async context utilities
│ │ ├── diarization.py # DiarizationEngine protocol
│ │ ├── ner.py # NEREngine protocol
│ │ └── calendar.py # CalendarProvider protocol
│ ├── webhooks/ # WebhookEventType, WebhookConfig, WebhookDelivery, payloads
│ ├── triggers/ # Trigger, TriggerAction, TriggerSignal, TriggerProvider
│ ├── summarization/# SummarizationProvider protocol
│ ├── rules/ # Business rules registry, models, builtin rules
│ ├── settings/ # Domain settings base
│ ├── constants/ # Field definitions, placeholders
│ ├── utils/ # time.py (utc_now), validation.py
│ ├── errors.py # Domain-specific exceptions
│ └── value_objects.py
├── application/ # Use-cases/services
│ ├── services/
│ │ ├── meeting/ # MeetingService (CRUD, segments, annotations, summaries, state)
│ │ ├── identity/ # IdentityService (context, workspace, defaults)
│ │ ├── calendar/ # CalendarService (connection, events, oauth, sync)
│ │ ├── summarization/ # SummarizationService, TemplateService, ConsentManager
│ │ ├── project_service/ # CRUD, members, roles, rules, active project
│ │ ├── recovery/ # RecoveryService (meeting, job, audio recovery)
│ │ ├── webhook_service.py
│ │ ├── ner_service.py
│ │ ├── export_service.py
│ │ ├── trigger_service.py
│ │ ├── retention_service.py
│ │ ├── auth_service.py
│ │ ├── auth_workflows.py
│ │ ├── auth_integration_manager.py
│ │ ├── auth_token_exchanger.py
│ │ ├── auth_types.py
│ │ ├── auth_constants.py
│ │ └── protocols.py
│ └── observability/ # Observability ports
├── infrastructure/ # Implementations
│ ├── audio/ # sounddevice capture, ring buffer, VU levels, playback, writer, reader
│ ├── asr/ # faster-whisper engine, VAD segmenter, streaming, DTOs
│ ├── auth/ # OIDC discovery, registry, presets
│ ├── diarization/ # Session, assigner, engine (streaming: diart, offline: pyannote)
│ ├── summarization/# CloudProvider, OllamaProvider, MockProvider, parsing, citation verifier, template renderer
│ ├── triggers/ # Calendar, audio_activity, foreground_app providers
│ ├── persistence/ # SQLAlchemy + asyncpg + pgvector
│ │ ├── database.py # create_async_engine, create_async_session_factory
│ │ ├── models/ # ORM models (core/, identity/, integrations/, entities/, observability/, organization/)
│ │ ├── repositories/ # Repository implementations
│ │ │ ├── meeting_repo.py
│ │ │ ├── segment_repo.py
│ │ │ ├── summary_repo.py
│ │ │ ├── annotation_repo.py
│ │ │ ├── entity_repo.py
│ │ │ ├── webhook_repo.py
│ │ │ ├── preferences_repo.py
│ │ │ ├── asset_repo.py
│ │ │ ├── summarization_template_repo.py
│ │ │ ├── diarization_job/
│ │ │ ├── integration/
│ │ │ ├── identity/
│ │ │ ├── usage_event/
│ │ │ └── _base/ # BaseRepository with _execute_scalar, _execute_scalars, _add_and_flush
│ │ ├── unit_of_work/
│ │ ├── memory/ # In-memory implementations
│ │ └── migrations/ # Alembic migrations
│ ├── security/ # Keystore (keyring + AES-GCM), protocols, crypto/
│ ├── crypto/ # Cryptographic utilities
│ ├── export/ # Markdown, HTML, PDF (WeasyPrint), formatting helpers
│ ├── webhooks/ # WebhookExecutor (delivery, signing, metrics)
│ ├── converters/ # ORM ↔ domain (orm, webhook, ner, calendar, integration, asr)
│ ├── calendar/ # Google/Outlook adapters, OAuth flow
│ ├── auth/ # OIDC registry, discovery, presets
│ ├── ner/ # spaCy NER engine
│ ├── observability/# OpenTelemetry tracing (otel.py), usage event tracking
│ ├── metrics/ # Metric collection utilities
│ ├── logging/ # Log buffer and utilities
│ └── platform/ # Platform-specific code
├── grpc/ # gRPC layer
│ ├── proto/ # noteflow.proto, generated *_pb2.py/*_pb2_grpc.py
│ ├── server/ # Bootstrap, lifecycle, setup, services, types
│ ├── service.py # NoteFlowServicer
│ ├── client.py # Python gRPC client wrapper
│ ├── meeting_store.py
│ ├── stream_state.py
│ ├── interceptors/ # Identity context propagation
│ ├── _mixins/ # Server-side gRPC mixins (see below)
│ └── _client_mixins/# Client-side gRPC mixins
├── cli/ # CLI tools
│ ├── __main__.py # CLI entry point
│ ├── retention.py # Retention management
│ ├── constants.py # CLI constants
│ └── models/ # Model commands (package)
│ ├── _download.py
│ ├── _parser.py
│ ├── _registry.py
│ ├── _status.py
│ └── _types.py
└── config/ # Pydantic settings (NOTEFLOW_ env vars)
├── settings/ # _main.py, _features.py, _triggers.py, _calendar.py, _loaders.py
└── constants/
gRPC Server Mixins (grpc/_mixins/)
_mixins/
├── streaming/ # ASR streaming (package)
│ ├── _mixin.py # Main StreamingMixin
│ ├── _session.py # Session management
│ ├── _asr.py # ASR processing
│ ├── _processing/ # Audio processing pipeline
│ │ ├── _audio_ops.py
│ │ ├── _chunk_tracking.py
│ │ ├── _congestion.py
│ │ ├── _constants.py
│ │ ├── _types.py
│ │ └── _vad_processing.py
│ ├── _partials.py # Partial transcript handling
│ ├── _cleanup.py # Resource cleanup
│ └── _types.py
├── diarization/ # Speaker diarization (package)
│ ├── _mixin.py # Main DiarizationMixin
│ ├── _jobs.py # Background job management
│ ├── _refinement.py# Offline refinement
│ ├── _streaming.py # Real-time diarization
│ ├── _speaker.py # Speaker assignment
│ ├── _status.py # Job status tracking
│ └── _types.py
├── summarization/ # Summary generation (package)
│ ├── _generation_mixin.py
│ ├── _templates_mixin.py
│ ├── _consent_mixin.py
│ ├── _template_crud.py
│ ├── _template_resolution.py
│ ├── _summary_generation.py
│ ├── _consent.py
│ └── _context_builders.py
├── meeting/ # Meeting lifecycle (package)
│ ├── meeting_mixin.py
│ ├── _project_scope.py
│ └── _stop_ops.py
├── project/ # Project management (package)
│ ├── _mixin.py
│ ├── _membership.py
│ └── _converters.py
├── oidc/ # OIDC authentication (package)
│ ├── oidc_mixin.py
│ └── _support.py
├── converters/ # Proto ↔ domain converters (package)
│ ├── _domain.py
│ ├── _external.py
│ ├── _timestamps.py
│ ├── _id_parsing.py
│ └── _oidc.py
├── errors/ # gRPC error helpers (package)
│ ├── _abort.py # abort_not_found, abort_invalid_argument
│ ├── _require.py # Requirement checks
│ ├── _fetch.py # Fetch with error handling
│ ├── _parse.py # Parsing helpers
│ └── _constants.py
├── servicer_core/ # Core servicer protocols
├── servicer_other/ # Additional servicer protocols
├── annotation.py # Segment annotations CRUD
├── export.py # Markdown/HTML/PDF export
├── entities.py # Named entity extraction
├── calendar.py # Calendar sync operations
├── webhooks.py # Webhook management
├── preferences.py # User preferences
├── observability.py # Usage tracking, metrics
├── identity.py # User/workspace identity
├── sync.py # State synchronization
├── diarization_job.py# Job status/management
├── protocols.py # ServicerHost protocol
├── _types.py
├── _audio_processing.py
├── _repository_protocols.py
└── _servicer_state.py
gRPC Client Mixins (grpc/_client_mixins/)
_client_mixins/
├── streaming.py # Client streaming operations
├── meeting.py # Meeting CRUD operations
├── diarization.py # Diarization requests
├── export.py # Export requests
├── annotation.py # Annotation operations
├── converters.py # Response converters
└── protocols.py # ClientHost protocol
Client Architecture (Tauri + React)
client/src/
├── api/ # API layer
│ ├── tauri-adapter.ts # Main Tauri IPC adapter
│ ├── mock-adapter.ts # Mock adapter for testing
│ ├── cached-adapter.ts # Caching layer
│ ├── connection-state.ts # Connection state machine
│ ├── reconnection.ts # Auto-reconnection logic
│ ├── interface.ts # Adapter interface
│ ├── transcription-stream.ts
│ ├── types/ # Type definitions
│ │ ├── core.ts # Core types (Meeting, Segment, etc.)
│ │ ├── enums.ts # Enum definitions
│ │ ├── errors.ts # Error types
│ │ ├── projects.ts # Project types
│ │ ├── diagnostics.ts
│ │ ├── requests.ts
│ │ ├── features/ # Feature-specific types
│ │ │ ├── webhooks.ts
│ │ │ ├── calendar.ts
│ │ │ ├── ner.ts
│ │ │ ├── sync.ts
│ │ │ ├── identity.ts
│ │ │ ├── oidc.ts
│ │ │ └── observability.ts
│ │ └── requests/ # Request types by domain
│ └── cached/ # Cached adapter implementations
│ ├── base.ts
│ ├── meetings.ts
│ ├── projects.ts
│ ├── diarization.ts
│ ├── annotations.ts
│ ├── templates.ts
│ ├── webhooks.ts
│ ├── calendar.ts
│ ├── entities.ts
│ ├── preferences.ts
│ ├── observability.ts
│ ├── triggers.ts
│ ├── audio.ts
│ ├── playback.ts
│ ├── apps.ts
│ └── readonly.ts
├── hooks/ # Custom React hooks
│ ├── use-diarization.ts
│ ├── use-cloud-consent.ts
│ ├── use-webhooks.ts
│ ├── use-oauth-flow.ts
│ ├── use-calendar-sync.ts
│ ├── use-entity-extraction.ts
│ ├── use-audio-devices.ts
│ ├── use-project.ts
│ ├── use-project-members.ts
│ ├── use-oidc-providers.ts
│ ├── use-auth-flow.ts
│ ├── use-integration-sync.ts
│ ├── use-integration-validation.ts
│ ├── use-secure-integration-secrets.ts
│ ├── use-guarded-mutation.ts
│ ├── use-panel-preferences.ts
│ ├── use-preferences-sync.ts
│ ├── use-meeting-reminders.ts
│ ├── use-recording-app-policy.ts
│ ├── use-post-processing.ts
│ ├── use-toast.ts
│ ├── use-mobile.tsx
│ └── post-processing/
├── contexts/ # React contexts
│ ├── connection-context.tsx # gRPC connection context
│ ├── connection-state.ts
│ ├── workspace-context.tsx # Workspace context
│ ├── workspace-state.ts
│ ├── project-context.tsx # Project context
│ └── project-state.ts
├── components/ # React components
│ ├── ui/ # Reusable UI components (shadcn/ui)
│ ├── recording/ # Recording-specific components
│ ├── settings/ # Settings panel components
│ ├── analytics/ # Analytics visualizations
│ ├── projects/ # Project components
│ ├── icons/ # Icon components
│ └── ... # Top-level components
├── pages/ # Route pages
│ ├── Home.tsx
│ ├── Meetings.tsx
│ ├── MeetingDetail.tsx
│ ├── Recording.tsx
│ ├── Projects.tsx
│ ├── ProjectSettings.tsx
│ ├── Settings.tsx
│ ├── settings/ # Settings sub-pages
│ ├── People.tsx
│ ├── Tasks.tsx
│ ├── Analytics.tsx
│ ├── Index.tsx
│ └── NotFound.tsx
├── lib/ # Utilities
│ ├── config/ # Configuration (server, defaults, app-config, provider-endpoints)
│ ├── cache/ # Client-side caching (meeting-cache)
│ ├── format.ts
│ ├── utils.ts
│ ├── time.ts
│ ├── crypto.ts
│ ├── cva.ts
│ ├── styles.ts
│ ├── tauri-events.ts
│ ├── preferences.ts
│ ├── preferences-sync.ts
│ ├── entity-store.ts
│ ├── speaker-utils.ts
│ ├── ai-providers.ts
│ ├── ai-models.ts
│ ├── integration-utils.ts
│ ├── default-integrations.ts
│ ├── status-constants.ts
│ ├── timing-constants.ts
│ ├── object-utils.ts
│ ├── error-reporting.ts
│ ├── client-logs.ts
│ ├── client-log-events.ts
│ ├── log-groups.ts
│ ├── log-converters.ts
│ ├── log-messages.ts
│ ├── log-summarizer.ts
│ └── log-group-summarizer.ts
├── types/ # Shared TypeScript types
└── test/ # Test utilities
Rust/Tauri Backend (client/src-tauri/src/)
src-tauri/src/
├── commands/ # Tauri IPC command handlers
│ ├── recording/ # capture.rs, device.rs, audio.rs, app_policy.rs
│ ├── playback/ # audio.rs, events.rs, tick.rs
│ ├── triggers/ # audio.rs, polling.rs
│ ├── meeting.rs
│ ├── diarization.rs
│ ├── annotation.rs
│ ├── export.rs
│ ├── summary.rs
│ ├── entities.rs
│ ├── calendar.rs
│ ├── webhooks.rs
│ ├── preferences.rs
│ ├── observability.rs
│ ├── sync.rs
│ ├── projects.rs
│ ├── identity.rs
│ ├── oidc.rs
│ ├── connection.rs
│ ├── audio.rs
│ ├── audio_testing.rs
│ ├── apps.rs
│ ├── apps_platform.rs
│ ├── diagnostics.rs
│ ├── shell.rs
│ └── testing.rs
├── grpc/ # gRPC client
│ ├── client/ # Client implementations by domain
│ │ ├── core.rs
│ │ ├── meetings.rs
│ │ ├── annotations.rs
│ │ ├── diarization.rs
│ │ ├── identity.rs
│ │ ├── projects.rs
│ │ ├── preferences.rs
│ │ ├── calendar.rs
│ │ ├── webhooks.rs
│ │ ├── observability.rs
│ │ ├── oidc.rs
│ │ ├── sync.rs
│ │ └── converters.rs
│ ├── types/ # Rust type definitions
│ │ ├── core.rs
│ │ ├── enums.rs
│ │ ├── identity.rs
│ │ ├── projects.rs
│ │ ├── preferences.rs
│ │ ├── calendar.rs
│ │ ├── webhooks.rs
│ │ ├── observability.rs
│ │ ├── oidc.rs
│ │ ├── sync.rs
│ │ └── results.rs
│ ├── streaming/ # Streaming converters
│ └── noteflow.rs # Generated protobuf types
├── state/ # Runtime state management
│ ├── app_state.rs
│ ├── preferences.rs
│ ├── playback.rs
│ └── types.rs
├── audio/ # Audio capture and playback
├── cache/ # Memory caching
├── crypto/ # Cryptographic operations
├── events/ # Tauri event emission
├── triggers/ # Trigger detection
├── error/ # Error types
├── identity/ # Identity management
├── config.rs # Configuration
├── constants.rs # Constants
├── helpers.rs # Helper functions
├── oauth_loopback.rs # OAuth callback server
├── main.rs # Application entry point
└── lib.rs # Library exports
Database
PostgreSQL with pgvector extension. Async SQLAlchemy with asyncpg driver.
# Alembic migrations
alembic upgrade head
alembic revision --autogenerate -m "description"
Connection via NOTEFLOW_DATABASE_URL env var or settings.
ORM Models (persistence/models/)
| Directory | Models |
|---|---|
core/ |
MeetingModel, SegmentModel, SummaryModel, AnnotationModel, DiarizationJobModel |
identity/ |
UserModel, WorkspaceModel, WorkspaceMembershipModel, ProjectModel, ProjectMembershipModel, SettingsModel |
integrations/ |
IntegrationModel, IntegrationSecretModel, CalendarEventModel, MeetingCalendarLinkModel, WebhookConfigModel, WebhookDeliveryModel |
entities/ |
NamedEntityModel, SpeakerModel |
observability/ |
UsageEventModel |
organization/ |
SummarizationTemplateModel, TaskModel, TagModel |
Testing Conventions
- Test files:
test_*.py, functions:test_* - Markers:
@pytest.mark.slow(model loading),@pytest.mark.integration(external services) - Integration tests use testcontainers for PostgreSQL
- Asyncio auto-mode enabled
- React unit tests use Vitest; e2e tests use Playwright in
client/e2e/
Test Quality Gates (tests/quality/)
After any non-trivial changes, run:
pytest tests/quality/
This suite enforces:
| Check | Description |
|---|---|
test_test_smells.py |
No assertion roulette, no conditional test logic, no loops in tests |
test_magic_values.py |
No magic numbers in assignments |
test_code_smells.py |
Code quality checks |
test_duplicate_code.py |
No duplicate code patterns |
test_stale_code.py |
No stale/dead code |
test_decentralized_helpers.py |
Helpers consolidated properly |
test_unnecessary_wrappers.py |
No unnecessary wrapper functions |
test_baseline_self.py |
Baseline validation self-checks |
Global Fixtures (tests/conftest.py)
Do not redefine these fixtures:
| Fixture | Description |
|---|---|
reset_context_vars |
Reset logging context variables |
mock_uow |
Mock Unit of Work |
crypto |
Crypto utilities |
meetings_dir |
Temporary meetings directory |
webhook_config |
Single-event webhook config |
webhook_config_all_events |
All-events webhook config |
sample_datetime |
Sample datetime |
calendar_settings |
Calendar settings |
meeting_id |
Sample meeting ID |
sample_meeting |
Sample Meeting entity |
recording_meeting |
Recording-state Meeting |
sample_rate |
Audio sample rate |
mock_grpc_context |
Mock gRPC context |
mockasr_engine |
Mock ASR engine |
mock_optional_extras |
Mock optional extras |
mock_oauth_manager |
Mock OAuth manager |
memory_servicer |
In-memory servicer |
approx_float |
Approximate float comparison |
approx_sequence |
Approximate sequence comparison |
Code Reuse (CRITICAL)
BEFORE writing ANY new code, you MUST search for existing implementations.
This is not optional. Redundant code creates maintenance burden, inconsistency, and bugs.
Mandatory Search Process
-
Search for existing functions that do what you need:
# Use Serena's symbolic tools first find_symbol with substring_matching=true search_for_pattern for broader searches -
Check related modules - if you need audio device config, check
device.rs; if you need preferences, checkpreferences.ts -
Look at imports in similar files - they reveal available utilities
-
Only create new code if:
- No existing implementation exists
- Existing code cannot be reasonably extended
- You have explicit approval for new abstractions
Anti-Patterns (FORBIDDEN)
| Anti-Pattern | Correct Approach |
|---|---|
| Creating wrapper functions for existing utilities | Use the existing function directly |
| Duplicating validation logic | Find and reuse existing validators |
| Writing new helpers without searching | Search first, ask if unsure |
| "It's faster to write new code" | Technical debt is never faster |
Examples
BAD: Creating query_capture_config() when resolve_input_device() + select_input_config() already exist
GOOD: Importing and using existing functions directly:
use device::{resolve_input_device, select_input_config};
// ... use them directly
BAD: Writing a new date formatting function
GOOD: Searching for existing formatters in lib/format.ts or utils/
Code Style
Python
- Python 3.12+, 100-char line length
- 4-space indentation
- Naming:
snake_casemodules/functions,PascalCaseclasses,UPPER_SNAKE_CASEconstants - Strict basedpyright (0 errors, 0 warnings, 0 notes required)
- Ruff for linting (E, W, F, I, B, C4, UP, SIM, RUF)
- Module soft limit 500 LoC, hard limit 750 LoC
- Generated
*_pb2.py,*_pb2_grpc.pyexcluded from lint
TypeScript
- Biome for linting and formatting
- Single quotes, 100 char width
- Strict TypeScript (noEmit checks)
Rust
rustfmtfor formattingclippyfor linting (warnings treated as errors)
Type Safety (Zero Tolerance)
Forbidden Patterns (Python)
| Pattern | Why Blocked | Alternative |
|---|---|---|
# type: ignore |
Bypasses type safety | Fix the actual type error |
# pyright: ignore |
Bypasses type safety | Fix the actual type error |
Any type annotations |
Creates type safety holes | Use Protocol, TypeVar, TypedDict, or specific types |
| Magic numbers | Hidden intent | Define typing.Final constants |
| Loops in tests | Non-deterministic | Use @pytest.mark.parametrize |
| Conditionals in tests | Non-deterministic | Use @pytest.mark.parametrize |
| Multiple assertions without messages | Hard to debug | Add assertion messages |
Type Resolution Hierarchy
When facing dynamic types:
Protocol— For duck typing (structural subtyping)TypeVar— For genericsTypedDict— For structured dictionariescast()— Last resort (with comment explaining why)
Validation Requirements
After any code changes:
source .venv/bin/activate && basedpyright src/noteflow/
Expected output: 0 errors, 0 warnings, 0 notes
Automated Enforcement (Hookify Rules)
Protected Files (Require Explicit Permission)
| File/Directory | What's Blocked |
|---|---|
Makefile |
All modifications |
tests/quality/ (except baselines.json) |
All modifications |
client/src/test/code-quality.test.ts |
All modifications to allowlists/thresholds |
pyproject.toml, ruff.toml, pyrightconfig.json |
All edits |
biome.json, tsconfig.json, .eslintrc* |
All edits |
.rustfmt.toml, .clippy.toml |
All edits |
Quality Gate Requirement
Before completing any code changes:
make quality
All quality checks must pass.
Policy: No Ignoring Pre-existing Issues
If you encounter lint errors, type errors, or test failures—even if they existed before your changes—you must either:
- Fix immediately (for simple issues)
- Add to todo list (for complex issues)
- Launch a subagent to fix (for parallelizable work)
Policy: Never Modify Quality Test Allowlists
STRICTLY FORBIDDEN without explicit user permission:
- Adding entries to allowlists/whitelists in quality tests (e.g.,
allowedNumbers,ALLOWED_LONG_FILES,allowedStorageFiles) - Increasing thresholds (e.g.,
toBeLessThanOrEqual(0)→toBeLessThanOrEqual(5)) - Adding exclusion patterns to skip files from quality checks
- Modifying filter functions to bypass detection (e.g.,
isNotMagicNumber)
When quality tests fail, the correct approach is:
- Fix the actual code that triggers the violation
- If the detection is a false positive (e.g., sprint references in comments), improve the filter logic to correctly exclude false positives while still catching real issues
- Never add arbitrary values to allowlists just to make tests pass
Example of WRONG approach:
// BAD: Adding sprint numbers to allowlist to avoid fixing code
const allowedNumbers = ['100', '200', '001', '002', '003']; // ❌
Example of CORRECT approach:
// GOOD: Improve filter to detect sprint references in comments
if (/(?:GAP|Sprint)[- ]?\d+/i.test(content)) return true; // ✓
Proto/gRPC
Proto definitions: src/noteflow/grpc/proto/noteflow.proto
Regenerate after proto changes:
python -m grpc_tools.protoc -I src/noteflow/grpc/proto \
--python_out=src/noteflow/grpc/proto \
--grpc_python_out=src/noteflow/grpc/proto \
src/noteflow/grpc/proto/noteflow.proto
Then run stub patching:
python scripts/patch_grpc_stubs.py
Sync Points (High Risk of Breakage)
When changing proto:
- Python stubs — Regenerate
*_pb2.py,*_pb2_grpc.py - Server mixins — Update
src/noteflow/grpc/_mixins/ - Python client — Update
src/noteflow/grpc/client.py - Rust types — Generated by
client/src-tauri/build.rs - Rust commands — Update
client/src-tauri/src/commands/ - TypeScript types — Update
client/src/api/types/ - TypeScript adapter — Update
client/src/api/tauri-adapter.ts
Key Subsystems
Speaker Diarization
- Streaming: diart for real-time speaker detection
- Offline: pyannote.audio for post-meeting refinement
- gRPC:
RefineSpeakerDiarization,GetDiarizationJobStatus,RenameSpeaker
Summarization
- Providers: CloudProvider (Anthropic/OpenAI), OllamaProvider (local), MockProvider (testing)
- Templates: Configurable tone, format, verbosity
- Citation verification: Links summary claims to transcript evidence
- Consent: Cloud providers require explicit user consent
Export
- Formats: Markdown, HTML, PDF (via WeasyPrint)
- Content: Transcript with timestamps, speaker labels, summary
Named Entity Recognition (NER)
- Engine: spaCy with transformer models
- Categories: person, company, product, technical, acronym, location, date, other
- Segment tracking: Entities link to source
segment_ids
Trigger Detection
- Signals: Calendar proximity, audio activity, foreground app
- Actions: IGNORE, NOTIFY, AUTO_START
Webhooks
- Events:
meeting.completed,summary.generated,recording.started,recording.stopped - Delivery: Exponential backoff retries
- Security: HMAC-SHA256 signing
Authentication
- OIDC: OpenID Connect with discovery
- Providers: Configurable via OIDC registry
Feature Flags
| Flag | Default | Controls |
|---|---|---|
NOTEFLOW_FEATURE_TEMPLATES_ENABLED |
true |
AI summarization templates |
NOTEFLOW_FEATURE_PDF_EXPORT_ENABLED |
true |
PDF export format |
NOTEFLOW_FEATURE_NER_ENABLED |
false |
Named entity extraction |
NOTEFLOW_FEATURE_CALENDAR_ENABLED |
false |
Calendar sync |
NOTEFLOW_FEATURE_WEBHOOKS_ENABLED |
true |
Webhook notifications |
Access via get_feature_flags().<flag_name> or get_settings().feature_flags.<flag_name>.
Common Pitfalls Checklist
When Adding New Features
- Update proto schema first (if gRPC involved)
- Regenerate Python stubs
- Run
scripts/patch_grpc_stubs.py - Implement server mixin
- Update Python client wrapper
- Update Rust commands
- Update TypeScript adapter
- Update TypeScript types
- Add tests (both backend and client)
- Run
make quality
When Changing Database Schema
- Update ORM models in
persistence/models/ - Create Alembic migration
- Update repository implementation
- Update UnitOfWork if needed
- Update converters in
infrastructure/converters/
When Modifying Existing Code
- Search for all usages first
- Update all call sites
- Run
make quality - Run relevant tests
Known Issues & Technical Debt
See docs/triage.md for tracked technical debt.
See docs/sprints/ for feature implementation plans.
MCP Tools Reference
Firecrawl (Web Scraping & Search)
| Tool | Use Case |
|---|---|
firecrawl_scrape |
Extract content from a single URL (markdown, HTML, screenshots) |
firecrawl_search |
Web search with optional content extraction; supports operators (site:, inurl:, -exclude) |
firecrawl_map |
Discover all URLs on a website before scraping |
firecrawl_crawl |
Multi-page crawl with depth/limit controls (use sparingly—token-heavy) |
firecrawl_extract |
LLM-powered structured data extraction with JSON schema |
Workflow: Search first without scrapeOptions, then scrape relevant URLs individually.
Sequential Thinking (Reasoning)
| Tool | Use Case |
|---|---|
sequentialthinking |
Break down complex problems into numbered thought steps; supports revision, branching, and hypothesis verification |
When to use: Multi-step analysis, design decisions, debugging with course-correction, architecture planning.
Context7 (Library Documentation)
| Tool | Use Case |
|---|---|
resolve-library-id |
Convert package name → Context7 library ID (required first step) |
query-docs |
Fetch up-to-date docs with specific query |
Workflow: resolve-library-id("fastapi") → query-docs(libraryId="/tiangolo/fastapi", query="how to handle dependencies").
Serena (Semantic Code Tools)
Navigation (prefer over grep/read for code):
| Tool | Use Case |
|---|---|
get_symbols_overview |
List top-level symbols in a file (classes, functions, variables) |
find_symbol |
Search by name path pattern; use depth for children, include_body for source |
find_referencing_symbols |
Find all references to a symbol across the codebase |
search_for_pattern |
Regex search across files (for non-symbol searches) |
Editing (atomic symbol-level changes):
| Tool | Use Case |
|---|---|
replace_symbol_body |
Replace entire symbol definition |
insert_before_symbol / insert_after_symbol |
Add code adjacent to a symbol |
rename_symbol |
Rename across entire codebase |
Memory (persistent cross-session knowledge):
| Tool | Use Case |
|---|---|
list_memories |
Show available project memories |
read_memory / write_memory |
Retrieve or store project knowledge |
Guardrails (call before major actions):
| Tool | Use Case |
|---|---|
think_about_collected_information |
Validate research completeness after search sequences |
think_about_task_adherence |
Verify alignment before editing code |
think_about_whether_you_are_done |
Confirm task completion |
Principle: Use symbolic tools over file reads; retrieve symbol bodies only when needed for understanding or editing.