Files
sepehr 4f7e808855 Initial commit: GitPulse project scaffold
Next.js dashboard with git statistics, AI-powered summaries via Ollama,
and research documents for project planning.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 16:53:39 +02:00

34 KiB

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

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_scanscan:progress + scan:complete
Batch operations Command → Events batch_pullbatch:repo_result (streaming per-repo)
AI queries Command → Events smart_statusai: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:

// 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 side — all commands return Result<T, AppError>
#[tauri::command]
async fn start_scan(roots: Vec<String>) -> Result<ScanResult, AppError> { ... }
// TypeScript side — typed wrappers
type CommandResult<T> = { status: "ok"; data: T } | { status: "error"; error: AppError };

Event Payload Format:

{ 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:

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):

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

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

  3. 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

  • Project context thoroughly analyzed
  • Scale and complexity assessed
  • Technical constraints identified
  • Cross-cutting concerns mapped

Architectural Decisions

  • Critical decisions documented with versions
  • Technology stack fully specified
  • Integration patterns defined
  • Performance considerations addressed

Implementation Patterns

  • Naming conventions established
  • Structure patterns defined
  • Communication patterns specified
  • Process patterns documented

Project Structure

  • Complete directory structure defined
  • Component boundaries established
  • Integration points mapped
  • 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:

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

Then configure Zustand, Tailwind CSS, Vitest per the stack additions table.