Files
noteflow/client/CLAUDE.md
Travis Vasceannie 1ce24cdf7b feat: reorganize Claude hooks and add RAG documentation structure with error handling policies
- Moved all hookify configuration files from `.claude/` to `.claude/hooks/` subdirectory for better organization
- Added four new blocking hooks to prevent common error handling anti-patterns:
  - `block-broad-exception-handler`: Prevents catching generic `Exception` with only logging
  - `block-datetime-now-fallback`: Blocks returning `datetime.now()` as fallback on parse failures to prevent data corruption
  - `block-default
2026-01-15 15:58:06 +00:00

16 KiB

CLAUDE.md - Client Development Reference

This file provides guidance for working with the Tauri + React client code. For Python backend development, see src/noteflow/CLAUDE.md.

Project Overview

NoteFlow Client is a Tauri + React desktop application for intelligent meeting note-taking. It communicates with a Python gRPC backend for audio streaming, transcription, speaker diarization, and AI-powered summarization.

The client consists of:

  • React/TypeScript frontend (src/) — UI components, hooks, contexts, and API layer
  • Rust/Tauri backend (src-tauri/) — Native IPC commands, gRPC client, audio capture/playback, encryption

Development Commands

# Install dependencies
npm install

# Development (web only)
npm run dev

# Desktop development (requires Rust toolchain)
npm run tauri:dev

# Build
npm run build
npm run tauri:build

Testing

# Unit tests (Vitest)
npm run test              # Run once
npm run test:watch        # Watch mode

# Run specific test file
npx vitest run src/hooks/use-audio-devices.test.ts

# Rust tests
npm run test:rs           # Equivalent: cd src-tauri && cargo test

# E2E tests (Playwright)
npm run test:e2e

# Native E2E tests (WebdriverIO)
npm run test:native

# All tests
npm run test:all

Quality Checks

# TypeScript type checking
npm run type-check

# Linting (outputs to ../.hygeine/)
npm run lint              # Biome + ESLint
npm run lint:fix          # Auto-fix

# Formatting
npm run format            # Biome format
npm run format:check      # Check only

# Quality tests (code quality enforcement)
npm run test:quality

# Rust code quality
npm run quality:rs

Architecture

TypeScript Layer (client/src/)

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

Key Patterns:

  • API abstraction via NoteFlowAdapter interface allows swapping implementations
  • TauriAdapter uses invoke() to call Rust commands which handle gRPC
  • React Query (@tanstack/react-query) for server state management
  • Contexts for global state: ConnectionContext, WorkspaceContext, ProjectContext

Rust Layer (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

Key Patterns:

  • Commands are registered in lib.rs via app_invoke_handler! macro
  • State is managed through AppState with thread-safe Arc wrappers
  • gRPC calls use tonic client; streaming handled by StreamManager
  • Audio capture uses cpal, playback uses rodio

TypeScript ↔ Rust Bridge

The TauriAdapter calls Rust commands via Tauri's invoke():

// TypeScript (src/api/tauri-adapter.ts)
const result = await invoke<MeetingResult>('create_meeting', { request });

// Rust (src-tauri/src/commands/meeting.rs)
#[tauri::command]
pub async fn create_meeting(
    state: State<'_, AppState>,
    request: CreateMeetingRequest,
) -> Result<Meeting, String> { ... }

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

  1. Search existing modules first:

    • src/lib/ — Utilities, helpers, formatters
    • src/hooks/ — React hooks (don't recreate existing hooks)
    • src/api/ — API utilities and types
    • src-tauri/src/commands/ — Rust command utilities
    • src-tauri/src/grpc/ — gRPC client utilities
  2. Use symbolic search:

    # Find existing functions by name pattern
    grep -r "function_name" src/
    cargo grep "fn function_name" src-tauri/
    
  3. Check imports in similar files — they reveal available utilities

  4. 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
New wrapper around existing function Use existing function directly
Duplicate utility in different module Import from canonical location
"Quick" helper that duplicates logic Find and reuse existing helper
New hook when existing hook suffices Extend or compose existing hooks

Examples

BAD: Creating query_capture_config() when resolve_input_device() + select_input_config() already exist

GOOD: Using existing functions directly:

use device::{resolve_input_device, select_input_config};
let device = resolve_input_device(device_id)?;
let config = select_input_config(&device, rate, channels)?;

BAD: Writing new formatting helpers in a component

GOOD: Checking src/lib/format.ts first and adding there if truly needed


Code Quality Standards

TypeScript

Linting: Biome with strict rules

  • noExplicitAny: error — No any types
  • noNonNullAssertion: error — No ! assertions
  • noUnusedImports: error, noUnusedVariables: error
  • useConst: error, useImportType: error

Type Safety:

  • Strict TypeScript mode enabled
  • No @ts-ignore or @ts-nocheck comments
  • No as any or as unknown assertions

Rust

Clippy Configuration (src-tauri/clippy.toml):

  • cognitive-complexity-threshold: 25
  • too-many-lines-threshold: 100
  • too-many-arguments-threshold: 7

Quality Script (npm run quality:rs):

  • Magic number detection
  • Long function detection (>90 lines)
  • Deep nesting detection (>7 levels)
  • unwrap() usage detection
  • Module size limits (>500 lines flagged)

Client Logging (CRITICAL)

NEVER use console.log, console.error, console.warn, or console.debug directly.

Always use the clientlog system via src/lib/debug.ts or src/lib/client-logs.ts:

// For debug logging (controlled by DEBUG flag)
import { debug } from '@/lib/debug';
const log = debug('MyComponent');
log('Something happened', { detail: 'value' });

// For error logging (always outputs)
import { errorLog } from '@/lib/debug';
const logError = errorLog('MyComponent');
logError('Something failed', error);

// For direct clientlog access
import { addClientLog } from '@/lib/client-logs';
addClientLog({
  level: 'info',
  source: 'app',
  message: 'Event occurred',
  details: 'Additional context',
});

Why:

  • clientlog persists logs to localStorage for later viewing in Analytics
  • Logs are structured with level, source, timestamp, and metadata
  • Debug logs can be toggled at runtime via DEBUG=true in localStorage
  • Console logging is ephemeral and not accessible to users

Log Levels: debug | info | warning | error

Log Sources: app | api | sync | auth | system

Test Quality Enforcement

The src/test/code-quality.test.ts suite enforces:

  • No repeated string literals across files
  • No duplicate utility implementations
  • No TODO/FIXME comments
  • No commented-out code
  • No magic numbers
  • No hardcoded colors or API endpoints
  • No any types or type assertions
  • File size limits (500 lines max, with exceptions)
  • Centralized helpers (format/parse/convert utilities)

Key Integration Points

Adding a New Tauri Command

  1. Rust: Add command in src-tauri/src/commands/*.rs
  2. Rust: Register in src-tauri/src/lib.rsapp_invoke_handler!
  3. TypeScript: Add to src/api/tauri-adapter.ts
  4. TypeScript: Add to src/api/interface.ts (if new method)
  5. TypeScript: Add types to src/api/types/

Adding a New API Method

  1. Update interface.ts with method signature
  2. Implement in tauri-adapter.ts (production)
  3. Implement in mock-adapter.ts (development/testing)
  4. Add cached version in cached/*.ts if caching needed

gRPC Schema Changes

When the backend proto changes:

  1. Rebuild Tauri: npm run tauri:build (triggers build.rs to regenerate noteflow.rs)
  2. Update Rust types in src-tauri/src/grpc/types/
  3. Update TypeScript types in src/api/types/
  4. Update adapters as needed

Testing Patterns

  • Tests use Vitest with jsdom environment
  • @testing-library/react for component testing
  • Tauri plugins are mocked in src/test/mocks/
  • src/test/setup.ts configures jest-dom matchers
# Run single test file
npx vitest run src/hooks/use-audio-devices.test.ts

# Run tests matching pattern
npx vitest run -t "should handle"

# Run with coverage
npx vitest run --coverage

Configuration Files

File Purpose
biome.json Linting and formatting rules
tsconfig.json TypeScript configuration
vitest.config.ts Test runner configuration
src-tauri/Cargo.toml Rust dependencies
src-tauri/clippy.toml Rust linting thresholds
src-tauri/tauri.conf.json Tauri app configuration