---
stepsCompleted: [step-01-init, step-02-context, step-03-starter, step-04-decisions, step-05-patterns, step-06-structure, step-07-validation, step-08-complete]
inputDocuments:
  - "prd.md"
  - "product-brief-gitpulse.md"
  - "product-brief-gitpulse-distillate.md"
  - "research/market-git-dashboard-ia-research-2026-04-24.md"
workflowType: 'architecture'
project_name: 'GitPulse'
user_name: 'Ramez'
date: '2025-04-25'
lastStep: 8
status: 'complete'
completedAt: '2025-04-25'
---

# Architecture Decision Document

_This document builds collaboratively through step-by-step discovery. Sections are appended as we work through each architectural decision together._

## Project Context Analysis

### Requirements Overview

**Functional Requirements (46 FRs across 7 capability areas):**

| Area | FRs | Architectural Implication |
|------|-----|--------------------------|
| Repository Discovery | FR1-FR7 | Filesystem scanner, `.gitpulseignore` parser, local cache |
| Dashboard & Visualization | FR8-FR14 | React component library, virtualized rendering, state management |
| Batch Operations | FR15-FR21 | Git process spawner, async execution engine, per-repo error aggregation |
| Smart Status (AI) | FR22-FR30 | AI provider abstraction layer, per-task routing, prompt engineering |
| System Tray & Notifications | FR31-FR35 | Platform abstraction, native OS bindings, background monitoring |
| Settings & Configuration | FR36-FR41 | Local persistence layer, settings schema, OS keychain integration |
| Onboarding & Distribution | FR42-FR46 | Build/packaging pipeline, cross-platform CI, code signing |

**Non-Functional Requirements (27 NFRs driving architecture):**

- **Performance (NFR1-8):** Tauri + Rust answers binary size, RAM, startup time, and scan speed targets. Virtualized rendering for 100+ repos. Async AI loading prevents UI blocking.
- **Security (NFR9-15):** OS keychain for API keys, git process isolation, privacy-by-default, zero telemetry.
- **Integration (NFR16-20):** Provider abstraction with graceful fallback, Git >= 2.20 compatibility, native OS APIs.
- **Reliability (NFR21-24):** Fault-tolerant scan/batch engines, crash recovery via persisted state, settings survival across updates.
- **Accessibility (NFR25-27):** Keyboard navigation, ARIA markup, high contrast support.

**Scale & Complexity:**

- Primary domain: Cross-platform desktop application (Rust backend + React frontend + native OS integrations)
- Complexity level: Medium — no regulatory compliance, but significant cross-platform surface area, multi-provider AI integration, and native system integration requirements
- Estimated architectural components: 8-10 major modules

### Technical Constraints & Dependencies

- **Tauri v2** — defines the IPC boundary (Rust ↔ React), binary packaging format, and system integration APIs
- **Rust backend** — handles all filesystem I/O, git process execution, AI API calls, and native OS interactions
- **React frontend** — runs in platform-native webview (WebView2/WebKit/WebKitGTK); no server-side rendering
- **Git CLI dependency** — requires Git >= 2.20 installed; GitPulse shells out to git commands
- **Ollama dependency (optional)** — local AI via Ollama API v1; auto-detection of running instance
- **Cloud API dependencies (optional)** — user-provided API keys for Gemini, DeepSeek, OpenAI, Anthropic
- **No database** — file-based storage (JSON/TOML) for settings and scan cache
- **No server component** — fully local; only outbound requests are AI API calls and update checks
- **Single binary distribution** — ~15MB target; no installer wizard
- **Solo developer** — architecture must be maintainable by one person; avoid over-engineering

### Cross-Cutting Concerns Identified

1. **IPC Boundary Design** — Every React ↔ Rust interaction crosses the Tauri IPC bridge. Command/event API design determines modularity and testability.
2. **Error Handling Strategy** — Partial success pattern (FR20, NFR22) applies to batch ops, AI calls, and scanning. Unified error handling prevents inconsistency.
3. **State Management** — Scan results, repo status, AI responses, settings, and tray state need coordinated management. Rust-side = source of truth; React-side = presentation cache.
4. **Platform Abstraction** — System tray (FR31), notifications (FR32), auto-launch (FR34), keychain (NFR9) all need platform-specific implementations behind common interfaces.
5. **Privacy & Data Flow** — Privacy indicator (FR27) requires knowing whether data is local or cloud-bound at all times. Must permeate AI provider layer and UI.
6. **Offline Resilience** — Every feature works without network. AI degrades gracefully, updates are manual, no cloud dependencies. Architecture never assumes connectivity.

## Starter Template Evaluation

### Primary Technology Domain

Desktop Application — Tauri v2 + Rust backend + React frontend

### Selected Starter: create-tauri-app (react-ts)

**Rationale:** Official scaffold, minimal opinion, Tauri v2 native, actively maintained (90k+ GitHub stars), fits solo developer context.

**Initialization Command:**

```bash
npm create tauri-app@latest -- --template react-ts --manager npm gitpulse
```

**Architectural Decisions Provided by Starter:**

| Decision | Choice | Rationale |
|----------|--------|-----------|
| Build tool | Vite | Default Tauri v2 bundler, fast HMR, optimized builds |
| Frontend language | TypeScript | Type safety for IPC boundary with Rust |
| Frontend framework | React 18+ | Specified by PRD |
| Package manager | npm | Standard, no friction |
| Rust toolchain | Cargo (via Tauri) | Standard Rust |
| IPC system | Tauri commands/events | Official v2 pattern |
| Permissions | Capabilities system (v2) | Replaces v1 allowlist |

**Stack Additions (post-scaffold):**

| Concern | Recommendation | Why |
|---------|---------------|-----|
| State management | Zustand | Lightweight, no boilerplate, fits solo dev |
| Styling | Tailwind CSS | Rapid prototyping, utility-first |
| Testing (unit) | Vitest | Native Vite, fast, TypeScript compatible |
| Testing (e2e) | Playwright (webview) | Cross-platform, real UI testing |
| Linting | ESLint + Prettier | Standard React/Rust |
| Tauri plugins | `tauri-plugin-store`, `tauri-plugin-shell`, `tauri-plugin-fs` | Settings persistence, git execution, filesystem access |

**Note:** Project initialization using this command should be the first implementation story.

## Core Architectural Decisions

### Decision Priority Analysis

**Already Decided (from PRD + Starter):**
- Framework: Tauri v2 + Rust + React TypeScript
- Build: Vite | Package manager: npm
- State: Zustand | Styling: Tailwind CSS
- Testing: Vitest (unit) + Playwright (e2e)
- No database, no authentication, no cloud hosting

**Critical Decisions (made in this step):**
- Data storage strategy (file-based)
- Security implementation (keyring, process isolation)
- IPC boundary design (commands vs events)
- Frontend module structure
- Cross-platform build pipeline

**Deferred Decisions (Post-MVP):**
- File watcher architecture (Phase 2)
- MCP integration design (Phase 3)
- Auto-update mechanism (Phase 1.5)
- CLI companion architecture (Phase 2)

### Data Architecture

**Storage Strategy: File-based, no database.**

| Data Type | Format | Storage | Rationale |
|-----------|--------|---------|-----------|
| User settings (scan roots, AI config, notification thresholds) | TOML | `tauri-plugin-store` (OS app data dir) | Human-readable, Rust-native, editable by power users |
| Scan cache (repo list, git status results) | JSON | `tauri-plugin-store` (OS app data dir) | Machine-to-machine, fast parse, no manual editing needed |
| API keys | Encrypted | OS keychain via Rust `keyring` crate | NFR9: not plaintext, native security |
| `.gitpulseignore` rules | Glob | Per-directory `.gitpulseignore` files | .gitignore syntax, user-controlled |

**Persistence approach:** `tauri-plugin-store` handles all file I/O, platform-specific paths, and atomic writes. No manual file management needed.

### Security Architecture

**API Key Storage:**
- Crate: `keyring` (Rust) — wraps Windows Credential Manager, macOS Keychain, Linux secret-service/kerberos
- Keys never transit to frontend — only a "configured/not configured" boolean crosses IPC
- Cloud API keys stored per-provider with service name `gitpulse:{provider_name}`

**Git Process Isolation:**
- All git commands executed via `tokio::process::Command` — async, isolated child processes
- Each git invocation runs in its own process with timeout (configurable, default 60s)
- stdout/stderr captured per-repo, never mixed across batch operations
- No shell expansion — arguments passed as `Vec<String>`, preventing injection

**Privacy Architecture:**
- Default mode: all processing local. No outbound network requests except manual update check
- AI provider layer tracks "active provider type" (local/cloud) in Rust state
- Frontend queries privacy status via IPC command — drives PrivacyBadge component (FR27)
- When cloud provider is active, all prompts include metadata that data will transit externally

### API & Communication (Tauri IPC)

**Dual IPC pattern: Commands (request/response) + Events (pub/sub)**

| Operation Type | IPC Pattern | Flow |
|---------------|-------------|------|
| Scan trigger | Command → Events | `start_scan` → `scan:progress` + `scan:complete` |
| Batch operations | Command → Events | `batch_pull` → `batch:repo_result` (streaming per-repo) |
| AI queries | Command → Events | `smart_status` → `ai:result` (async, non-blocking) |
| Settings CRUD | Command only | `get_settings` / `update_settings` (synchronous) |
| Tray actions | Command only | `tray_pull` / `tray_status` (quick operations) |

**Error handling across IPC:**
- All Tauri commands return `Result<T, AppError>` where `AppError` is a structured error type
- Frontend receives typed errors — no string matching
- Batch operations emit per-repo events with `Result<RepoResult, RepoError>` — partial success pattern

### Rust Backend Module Structure

```
src-tauri/src/
├── commands/
│   ├── mod.rs            # Module registration
│   ├── scanner.rs        # FR1-FR7: scan commands
│   ├── dashboard.rs      # FR8-FR14: query/filter/sort
│   ├── batch.rs          # FR15-FR21: batch git ops
│   ├── ai.rs             # FR22-FR30: Smart Status
│   ├── tray.rs           # FR31-FR35: system tray
│   └── settings.rs       # FR36-FR41: configuration
├── providers/
│   ├── mod.rs            # AI provider trait definition
│   ├── ollama.rs         # Ollama API v1 implementation
│   ├── gemini.rs         # Google Gemini implementation
│   ├── deepseek.rs       # DeepSeek implementation
│   ├── openai.rs         # OpenAI implementation
│   └── anthropic.rs      # Anthropic implementation
├── git.rs                # Git process execution engine
├── ignore.rs             # .gitpulseignore glob parser
├── scanner.rs            # Recursive disk scanner logic
├── state.rs              # Shared application state (Tauri managed)
└── lib.rs                # Tauri builder, plugin registration, command registration
```

**AI Provider Abstraction:**
```rust
// providers/mod.rs
#[async_trait]
pub trait AiProvider {
    async fn health_summary(&self, repo_context: &RepoContext) -> Result<String, AiError>;
    async fn suggest_commit_message(&self, diff: &str) -> Result<String, AiError>;
    async fn cross_repo_analysis(&self, repos: &[RepoContext]) -> Result<String, AiError>;
    fn provider_type(&self) -> ProviderType;
    fn is_local(&self) -> bool;
}
```
Each provider implements this trait. The `is_local()` method drives the privacy indicator.

### Frontend Architecture

```
src/
├── components/
│   ├── ui/                # Primitives: Button, Badge, Card, Input, Dialog
│   ├── dashboard/
│   │   ├── CardGrid.tsx    # FR8: card grid view
│   │   ├── RepoCard.tsx    # FR8, FR13: individual repo tile
│   │   ├── ListView.tsx    # FR9: sortable/filterable list
│   │   ├── RepoRow.tsx     # FR9: individual repo row
│   │   └── ViewToggle.tsx  # FR10: grid/list toggle
│   ├── batch/
│   │   ├── BatchPanel.tsx  # FR15-FR18: batch operation controls
│   │   └── BatchResults.tsx # FR19-FR21: per-repo results display
│   ├── ai/
│   │   ├── SmartStatus.tsx # FR24-FR26: AI summaries display
│   │   ├── PrivacyBadge.tsx # FR27: local/cloud indicator
│   │   └── AiConfig.tsx    # FR22-FR23, FR29: provider configuration
│   └── settings/
│       └── SettingsPanel.tsx # FR36-FR41: configuration UI
├── hooks/
│   ├── useScan.ts         # Scan state + triggers
│   ├── useRepos.ts        # Repository list + filters
│   ├── useBatch.ts        # Batch operation state
│   └── useAi.ts           # AI provider state + privacy mode
├── stores/
│   ├── repoStore.ts       # Zustand: repo list, filters, sort
│   ├── scanStore.ts       # Zustand: scan state, progress
│   └── aiStore.ts         # Zustand: AI config, active provider, privacy
├── lib/
│   ├── invoke.ts          # Typed Tauri invoke wrappers
│   └── events.ts          # Typed Tauri event listeners
├── types.ts               # Shared TypeScript types (mirrors Rust structs)
├── App.tsx
└── main.tsx
```

**State management principle:**
- **Rust = source of truth.** All data originates from Rust (scan results, git status, AI responses).
- **React = presentation cache.** Zustand stores are populated via Tauri IPC responses and events.
- **Stores never contain business logic.** They reflect Rust state, not compute independently.

### Infrastructure & Deployment

**Cross-platform CI/CD (GitHub Actions):**
- Matrix build: Windows x64, macOS x64 + ARM, Linux x64
- Tauri CLI handles per-platform packaging
- Artifacts: `.exe` (Windows), `.dmg` (macOS), `.deb` + `.AppImage` (Linux)
- Triggered on version tags (`v*`) for releases
- Lint + test (Rust + TypeScript) on every PR

**Code signing (Phase 1.5):**
- Windows: Authenticode certificate (~$200-500/year)
- macOS: Apple Developer account ($99/year) + notarization
- Linux: GPG signing for APT repository

**Distribution:** GitHub Releases as primary channel. No auto-update in MVP (FR44: manual check only).

### Decision Impact Analysis

**Implementation Sequence:**
1. Scaffold project (`create-tauri-app`) + configure Zustand/Tailwind/Vitest
2. Rust: `git.rs` (process engine) + `scanner.rs` (disk scanner) + `ignore.rs`
3. Rust: `state.rs` (app state) + `commands/scanner.rs` (IPC)
4. React: `CardGrid` + `RepoCard` + `useScan` hook
5. Rust: `commands/batch.rs` + `git.rs` batch execution
6. React: `ListView` + `BatchPanel` + `BatchResults`
7. Rust: `providers/` trait + `ollama.rs` + `commands/ai.rs`
8. React: `SmartStatus` + `PrivacyBadge` + `AiConfig`
9. Rust: `commands/tray.rs` + `commands/settings.rs`
10. CI/CD pipeline setup + cross-platform builds

## Implementation Patterns & Consistency Rules

### Naming Patterns

**Rust Code:**
- Modules/files: `snake_case` (`scanner.rs`, `batch.rs`, `git.rs`)
- Structs/enums: `PascalCase` (`RepoStatus`, `BatchResult`, `AiProvider`)
- Functions/methods: `snake_case` (`scan_disk`, `execute_batch`, `get_health_summary`)
- Constants: `SCREAMING_SNAKE_CASE` (`DEFAULT_SCAN_TIMEOUT_SECS`)
- Tauri commands: `snake_case` (`start_scan`, `batch_pull`, `smart_status`)

**TypeScript/React Code:**
- Components: `PascalCase` (`RepoCard.tsx`, `BatchPanel.tsx`, `PrivacyBadge.tsx`)
- Hooks: `camelCase` with `use` prefix (`useScan`, `useRepos`, `useBatch`)
- Stores: `camelCase` with `Store` suffix (`repoStore.ts`, `aiStore.ts`)
- Functions/variables: `camelCase` (`invokeStartScan`, `activeProviderType`)
- Types/interfaces: `PascalCase` (`Repo`, `BatchResult`, `ScanProgress`)
- Files: `PascalCase` for components, `camelCase` for hooks/stores/lib

**IPC Boundary:**
- Command names: `snake_case` on both sides (Tauri matches directly)
- Event names: `namespace:event` format (`scan:progress`, `batch:repo_result`, `ai:result`)
- Payload types: `PascalCase` in TypeScript, mirroring Rust structs

### Structure Patterns

**Tests:** Co-located — `*.test.ts` beside tested file, `*_test.rs` beside Rust module.

**Components:** Organized by feature (dashboard, batch, ai, settings), not by type.

**Shared types:** `src/types.ts` mirrors Rust structs. Single source of truth.

**No barrel files:** Direct imports, no `index.ts` re-exports.

### Format Patterns

**IPC Command Response (all Tauri commands):**
```rust
// Rust side — all commands return Result<T, AppError>
#[tauri::command]
async fn start_scan(roots: Vec<String>) -> Result<ScanResult, AppError> { ... }
```
```typescript
// TypeScript side — typed wrappers
type CommandResult<T> = { status: "ok"; data: T } | { status: "error"; error: AppError };
```

**Event Payload Format:**
```typescript
{ type: "scan:progress", payload: { current: 5, total: 23 }, timestamp: 1714056600000 }
```

**Dates:** ISO 8601 strings everywhere — never numeric timestamps.

**JSON field naming:** `camelCase` in IPC payloads. Rust structs use `#[serde(rename_all = "camelCase")]`.

### Communication Patterns

**Event namespaces:**

| Namespace | Events |
|-----------|--------|
| `scan` | `scan:started`, `scan:progress`, `scan:complete`, `scan:error` |
| `batch` | `batch:started`, `batch:repo_result`, `batch:complete` |
| `ai` | `ai:query_started`, `ai:result`, `ai:error`, `ai:provider_changed` |

**Zustand store pattern:**
```typescript
interface StoreState<T> {
  data: T;
  status: 'idle' | 'loading' | 'loaded' | 'error';
  error: string | null;
}
```
- Each store = single `create()` call
- Actions = thin wrappers calling Tauri invoke/event listeners
- No business logic in stores — logic lives in Rust

### Process Patterns

**Error handling (Rust):**
- All errors typed via `thiserror` — no `.unwrap()` in production code
- Central `AppError` enum: `ScanError`, `GitError`, `AiError`, `ConfigError`
- Batch errors per-repo: `Vec<RepoOperationResult>` where each item is `Result<RepoResult, RepoError>`

**Error handling (React):**
- Error boundary at App level for fatal errors
- Per-component error states via store `error: string | null`
- No silent `try/catch` — all errors surfaced in UI

**Loading states:**
- Uniform pattern: `{ status: 'idle' | 'loading' | 'loaded' | 'error' }` in each store
- Per-component loading, no global spinner
- Skeleton loading for RepoCard/RepoRow (no layout shift)

### Enforcement Guidelines

**All AI Agents MUST:**

1. Follow naming conventions: `snake_case` Rust, `PascalCase` React components, `camelCase` TypeScript functions
2. Use `#[serde(rename_all = "camelCase")]` on all Rust structs crossing IPC
3. Return `Result<T, AppError>` from all Tauri commands — never panic
4. Use `namespace:event` format for all Tauri events
5. Co-locate tests with tested files (`*.test.ts`, `*_test.rs`)
6. Use Zustand store pattern `{ data, status, error }` — no ad-hoc state
7. Use `tokio::process::Command` for all git operations — never `std::process::Command`
8. Implement `AiProvider` trait for all new AI providers — no provider-specific code outside `providers/`

**Anti-Patterns:**
- No `any` in TypeScript
- No `unwrap()` in Rust production code
- No business logic in React stores
- No barrel files (index.ts re-exports)
- No hardcoded event name strings — use constants
- No synchronous IPC calls for heavy operations — use Commands + Events

## Project Structure & Boundaries

### Complete Project Directory Structure

```
gitpulse/
├── .github/
│   └── workflows/
│       ├── ci.yml                    # Lint + test on PR
│       └── release.yml               # Cross-platform build + release on tag
├── src/                              # React Frontend
│   ├── components/
│   │   ├── ui/                       # Primitives
│   │   │   ├── Button.tsx
│   │   │   ├── Badge.tsx
│   │   │   ├── Card.tsx
│   │   │   ├── Dialog.tsx
│   │   │   ├── Input.tsx
│   │   │   ├── Select.tsx
│   │   │   ├── Skeleton.tsx
│   │   │   └── Tooltip.tsx
│   │   ├── dashboard/
│   │   │   ├── CardGrid.tsx           # FR8
│   │   │   ├── RepoCard.tsx           # FR8, FR13
│   │   │   ├── ListView.tsx           # FR9, FR11, FR12
│   │   │   ├── RepoRow.tsx            # FR9
│   │   │   ├── ViewToggle.tsx         # FR10
│   │   │   └── EmptyState.tsx         # <10 repos UX
│   │   ├── batch/
│   │   │   ├── BatchToolbar.tsx       # FR15-FR18
│   │   │   └── BatchResults.tsx       # FR19-FR21
│   │   ├── ai/
│   │   │   ├── SmartStatus.tsx        # FR24-FR26
│   │   │   ├── PrivacyBadge.tsx       # FR27
│   │   │   └── AiSettings.tsx         # FR22-FR23, FR29
│   │   ├── tray/
│   │   │   └── TrayMenu.tsx           # FR35
│   │   └── settings/
│   │       ├── SettingsPanel.tsx      # FR36-FR41
│   │       ├── ScanRoots.tsx          # FR2, FR36
│   │       ├── NotificationSettings.tsx # FR38
│   │       └── TraySettings.tsx       # FR39
│   ├── hooks/
│   │   ├── useScan.ts                # FR1, FR5, FR7
│   │   ├── useRepos.ts               # FR8-FR14
│   │   ├── useBatch.ts               # FR15-FR21
│   │   ├── useAi.ts                  # FR22-FR30
│   │   └── useSettings.ts            # FR36-FR41
│   ├── stores/
│   │   ├── repoStore.ts
│   │   ├── scanStore.ts
│   │   ├── batchStore.ts
│   │   ├── aiStore.ts
│   │   └── settingsStore.ts
│   ├── lib/
│   │   ├── invoke.ts                 # Typed Tauri invoke wrappers
│   │   ├── events.ts                 # Typed event listeners + constants
│   │   └── utils.ts
│   ├── types.ts                      # Shared types (mirrors Rust)
│   ├── App.tsx
│   ├── main.tsx
│   └── styles/
│       └── globals.css
├── src-tauri/                        # Rust Backend
│   ├── src/
│   │   ├── commands/
│   │   │   ├── mod.rs
│   │   │   ├── scanner.rs            # FR1-FR7
│   │   │   ├── dashboard.rs          # FR8-FR14
│   │   │   ├── batch.rs              # FR15-FR21
│   │   │   ├── ai.rs                 # FR22-FR30
│   │   │   ├── tray.rs               # FR31-FR35
│   │   │   └── settings.rs           # FR36-FR41
│   │   ├── providers/
│   │   │   ├── mod.rs                # AiProvider trait
│   │   │   ├── ollama.rs
│   │   │   ├── gemini.rs
│   │   │   ├── deepseek.rs
│   │   │   ├── openai.rs
│   │   │   └── anthropic.rs
│   │   ├── git.rs                    # Git process engine
│   │   ├── ignore.rs                 # .gitpulseignore parser
│   │   ├── scanner.rs                # Disk scanner
│   │   ├── state.rs                  # App state
│   │   ├── error.rs                  # AppError enum
│   │   ├── lib.rs                    # Tauri builder + registration
│   │   └── main.rs                   # Entry point
│   ├── capabilities/
│   │   └── default.json              # Tauri v2 permissions
│   ├── icons/
│   ├── Cargo.toml
│   ├── tauri.conf.json
│   └── build.rs
├── tests/
│   ├── e2e/
│   │   ├── scan.spec.ts
│   │   ├── dashboard.spec.ts
│   │   ├── batch.spec.ts
│   │   ├── ai.spec.ts
│   │   └── settings.spec.ts
│   └── fixtures/
│       └── test-repos/
├── package.json
├── tsconfig.json
├── vite.config.ts
├── tailwind.config.js
├── vitest.config.ts
├── playwright.config.ts
├── .gitignore
├── .eslintrc.json
├── .prettierrc
└── README.md
```

### Requirements to Structure Mapping

| FR Category | Rust Module | React Components | Store |
|-------------|------------|------------------|-------|
| FR1-FR7: Discovery | `scanner.rs`, `ignore.rs`, `commands/scanner.rs` | `CardGrid`, `RepoCard`, `EmptyState` | `scanStore` |
| FR8-FR14: Dashboard | `commands/dashboard.rs` | `CardGrid`, `ListView`, `RepoCard/Row`, `ViewToggle` | `repoStore` |
| FR15-FR21: Batch Ops | `git.rs`, `commands/batch.rs` | `BatchToolbar`, `BatchResults` | `batchStore` |
| FR22-FR30: Smart Status | `providers/*`, `commands/ai.rs` | `SmartStatus`, `PrivacyBadge`, `AiSettings` | `aiStore` |
| FR31-FR35: Tray | `commands/tray.rs` | `TrayMenu` | — |
| FR36-FR41: Settings | `commands/settings.rs` | `SettingsPanel`, `ScanRoots`, `NotificationSettings`, `TraySettings` | `settingsStore` |
| FR42-FR46: Onboarding | `lib.rs` (build config) | `main.tsx`, `App.tsx` | — |

### Architectural Boundaries

**IPC Boundary (Rust ↔ React):**
- Rust exposes `#[tauri::command]` — React calls via typed `invoke()` wrappers
- Rust emits events — React listens via typed `listen()` wrappers
- Types defined in Rust structs (source of truth), mirrored in `src/types.ts`

**AI Provider Boundary:**
- All providers implement `AiProvider` trait
- Only `commands/ai.rs` accesses providers — no other module knows about providers
- Privacy status (`is_local()`) surfaced via dedicated IPC command

**Git Execution Boundary:**
- Only `git.rs` spawns git processes
- `commands/batch.rs` delegates to `git.rs` for all operations
- Per-repo error isolation — git failures never crash the app

**Platform Abstraction Boundary:**
- System tray, notifications, auto-launch, keychain — all via Tauri plugins (Rust-side)
- No platform-specific code in React
- Feature flags in `tauri.conf.json` for platform-specific capabilities

### Integration Points

**Internal Communication:**
```
React ──invoke()──→ Tauri IPC ──→ Rust Commands ──→ Rust Modules
React ←──listen()── Tauri Events ←── Rust State ←── Module Results
```

**External Integrations:**
- Git CLI: `tokio::process::Command` (isolated processes)
- Ollama: HTTP to `localhost:11434` (local)
- Cloud APIs: HTTPS to provider endpoints
- OS Keychain: `keyring` crate (native)
- OS Notifications: Tauri notification plugin (native)
- Filesystem: `tokio::fs` + `.gitpulseignore` parser

## Architecture Validation Results

### Coherence Validation ✅

**Decision Compatibility:** All technology choices are mutually compatible. Tauri v2 (stable) provides the native tokio runtime for async Rust, the IPC bridge, and webview packaging. React 18+ with TypeScript, Zustand, Tailwind, and Vitest is a standard, battle-tested frontend stack. `keyring` crate wraps all three OS keychains. `tauri-plugin-store` handles both TOML and JSON formats. No version conflicts or contradictory decisions found.

**Pattern Consistency:** Naming conventions are uniform (snake_case Rust, PascalCase components, camelCase TS functions). IPC dual pattern (Commands for CRUD, Events for streaming) is applied consistently across all feature areas. Error pattern (`Result<T, AppError>`) and store pattern (`{ data, status, error }`) are standardized. Event namespaces (`scan:*`, `batch:*`, `ai:*`) follow a single scheme.

**Structure Alignment:** Rust `commands/` modules map directly to React component groups by feature. `providers/` is cleanly isolated behind the `AiProvider` trait. The requirements-to-structure mapping table traces every FR category to specific Rust, React, and store files. Integration points are explicit.

### Requirements Coverage Validation

**Functional Requirements — 44/46 fully covered:**

| FR | Status | Note |
|----|--------|------|
| FR1-FR7 (Discovery) | ✅ Full | scanner.rs, ignore.rs, commands/scanner.rs, scanStore |
| FR8-FR13 (Dashboard) | ✅ Full | CardGrid, ListView, ViewToggle, RepoCard/Row, repoStore |
| FR14 (100+ repos) | ⚠️ Partial | Virtualization mentioned but no library specified |
| FR15-FR21 (Batch) | ✅ Full | commands/batch.rs, git.rs, BatchToolbar, BatchResults |
| FR22-FR25, FR27-FR30 (AI) | ✅ Full | AiProvider trait, all 5 providers, PrivacyBadge |
| FR26 (Dormancy) | ⚠️ Partial | Referenced in SmartStatus.tsx but no explicit detection module |
| FR31-FR33, FR35 (Tray) | ✅ Full | commands/tray.rs, TrayMenu, notification plugin |
| FR34 (Auto-launch) | ⚠️ Partial | Platform abstraction mentioned but `tauri-plugin-autostart` not listed |
| FR36-FR41 (Settings) | ✅ Full | SettingsPanel, all sub-panels, tauri-plugin-store |
| FR42-FR46 (Onboarding) | ✅ Full | No auth module, offline-first, CI matrix, single binary |

**Non-Functional Requirements — 25/27 fully covered:**

| NFR | Status | Note |
|-----|--------|------|
| NFR1-5, NFR7-8 (Perf) | ✅ Full | Tauri/Rust architecture, async patterns, caching |
| NFR6 (60fps) | ⚠️ Partial | Depends on virtualization choice (FR14 gap) |
| NFR9-15 (Security) | ✅ Full | keyring, process isolation, privacy architecture |
| NFR16-20 (Integration) | ✅ Full | Provider abstraction, graceful fallback |
| NFR21-24 (Reliability) | ✅ Full | Fault tolerance, persisted state |
| NFR25-27 (Accessibility) | ✅ Addressed | Keyboard nav, ARIA, high contrast mentioned |

### Implementation Readiness Validation ✅

**Decision Completeness:** All critical decisions documented with rationale. Starter template command specified. Stack additions listed with justifications. Implementation sequence provided (10 steps). Enforcement guidelines and anti-patterns are clear and actionable.

**Structure Completeness:** Full directory tree specified down to individual files. Each file annotated with the FRs it supports. Rust and React structures are symmetric. Integration points mapped explicitly (data flow diagram included).

**Pattern Completeness:** Naming conventions cover all code types (Rust, TypeScript, IPC). Error handling patterns specified for both Rust and React. Store pattern standardized. Event payload format defined. Date format standardized (ISO 8601). Anti-patterns enumerated.

### Gap Analysis Results

**Important Gaps (should address before implementation):**

1. **Virtualization library not specified (FR14/NFR6):** The architecture states "virtualized rendering for 100+ repos" but does not name the library. Options: `@tanstack/react-virtual`, `react-virtuoso`, or `react-window`. This choice affects RepoCard/ListView implementation directly.

2. **Missing Tauri plugins in stack additions:** `tauri-plugin-autostart` (FR34) and `tauri-plugin-notification` (FR32) are not listed alongside the other plugins. These are required for system tray agent features.

3. **Dormancy detection not explicitly placed (FR26):** No Rust module or function is named for configurable dormancy threshold logic. Should be explicit — likely in `scanner.rs` or `commands/dashboard.rs`.

**Minor Gaps (can address during implementation):**

4. **i18n framework not specified:** PRD requires "i18n framework ready from day 1" but no library is listed (e.g., `react-i18next`, `typesafe-i18n`).

5. **`.gitpulseignore` parsing crate not named:** `ignore.rs` is listed but the specific crate (`ignore` from ripgrep, `globset`, or custom) is not specified.

6. **Accessibility testing approach not detailed:** NFR25-27 are architecturally addressed but no testing tooling is specified (e.g., `axe-core` integration with Playwright).

### Validation Issues Addressed

All three important gaps are non-blocking for implementation start but should be resolved early. I recommend resolving gaps 1-3 at the start of the relevant implementation stories rather than blocking architecture completion on them.

### Architecture Completeness Checklist

**✅ Requirements Analysis**
- [x] Project context thoroughly analyzed
- [x] Scale and complexity assessed
- [x] Technical constraints identified
- [x] Cross-cutting concerns mapped

**✅ Architectural Decisions**
- [x] Critical decisions documented with versions
- [x] Technology stack fully specified
- [x] Integration patterns defined
- [x] Performance considerations addressed

**✅ Implementation Patterns**
- [x] Naming conventions established
- [x] Structure patterns defined
- [x] Communication patterns specified
- [x] Process patterns documented

**✅ Project Structure**
- [x] Complete directory structure defined
- [x] Component boundaries established
- [x] Integration points mapped
- [x] Requirements to structure mapping complete

### Architecture Readiness Assessment

**Overall Status:** READY FOR IMPLEMENTATION

**Confidence Level:** High — all critical decisions made, all 46 FRs traceable to structure, patterns are enforceable, and the solo developer constraint is respected (no over-engineering).

**Key Strengths:**
- Clear IPC boundary design (Commands + Events) with typed wrappers on both sides
- `AiProvider` trait cleanly isolates all 5 AI backends behind a single interface
- Privacy indicator permeates the architecture via `is_local()` on the provider trait
- Rust = source of truth, React = presentation cache eliminates state sync issues
- Implementation sequence provides a concrete story-by-story build order

**Areas for Future Enhancement:**
- Virtualization library choice for large repo lists
- i18n framework selection
- Accessibility testing tooling
- File watcher architecture (deferred to Phase 2 per plan)

### Implementation Handoff

**AI Agent Guidelines:**
- Follow all architectural decisions exactly as documented
- Use implementation patterns consistently across all components
- Respect project structure and boundaries
- Refer to this document for all architectural questions

**First Implementation Priority:**
```bash
npm create tauri-app@latest -- --template react-ts --manager npm gitpulse
```
Then configure Zustand, Tailwind CSS, Vitest per the stack additions table.
