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>
34 KiB
stepsCompleted, inputDocuments, workflowType, project_name, user_name, date, lastStep, status, completedAt
| stepsCompleted | inputDocuments | workflowType | project_name | user_name | date | lastStep | status | completedAt | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
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
- IPC Boundary Design — Every React ↔ Rust interaction crosses the Tauri IPC bridge. Command/event API design determines modularity and testability.
- Error Handling Strategy — Partial success pattern (FR20, NFR22) applies to batch ops, AI calls, and scanning. Unified error handling prevents inconsistency.
- State Management — Scan results, repo status, AI responses, settings, and tray state need coordinated management. Rust-side = source of truth; React-side = presentation cache.
- Platform Abstraction — System tray (FR31), notifications (FR32), auto-launch (FR34), keychain (NFR9) all need platform-specific implementations behind common interfaces.
- 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.
- 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_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>whereAppErroris 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:
- Scaffold project (
create-tauri-app) + configure Zustand/Tailwind/Vitest - Rust:
git.rs(process engine) +scanner.rs(disk scanner) +ignore.rs - Rust:
state.rs(app state) +commands/scanner.rs(IPC) - React:
CardGrid+RepoCard+useScanhook - Rust:
commands/batch.rs+git.rsbatch execution - React:
ListView+BatchPanel+BatchResults - Rust:
providers/trait +ollama.rs+commands/ai.rs - React:
SmartStatus+PrivacyBadge+AiConfig - Rust:
commands/tray.rs+commands/settings.rs - 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:
camelCasewithuseprefix (useScan,useRepos,useBatch) - Stores:
camelCasewithStoresuffix (repoStore.ts,aiStore.ts) - Functions/variables:
camelCase(invokeStartScan,activeProviderType) - Types/interfaces:
PascalCase(Repo,BatchResult,ScanProgress) - Files:
PascalCasefor components,camelCasefor hooks/stores/lib
IPC Boundary:
- Command names:
snake_caseon both sides (Tauri matches directly) - Event names:
namespace:eventformat (scan:progress,batch:repo_result,ai:result) - Payload types:
PascalCasein 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
AppErrorenum:ScanError,GitError,AiError,ConfigError - Batch errors per-repo:
Vec<RepoOperationResult>where each item isResult<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:
- Follow naming conventions:
snake_caseRust,PascalCaseReact components,camelCaseTypeScript functions - Use
#[serde(rename_all = "camelCase")]on all Rust structs crossing IPC - Return
Result<T, AppError>from all Tauri commands — never panic - Use
namespace:eventformat for all Tauri events - Co-locate tests with tested files (
*.test.ts,*_test.rs) - Use Zustand store pattern
{ data, status, error }— no ad-hoc state - Use
tokio::process::Commandfor all git operations — neverstd::process::Command - Implement
AiProvidertrait for all new AI providers — no provider-specific code outsideproviders/
Anti-Patterns:
- No
anyin 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 typedinvoke()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
AiProvidertrait - Only
commands/ai.rsaccesses providers — no other module knows about providers - Privacy status (
is_local()) surfaced via dedicated IPC command
Git Execution Boundary:
- Only
git.rsspawns git processes commands/batch.rsdelegates togit.rsfor 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.jsonfor 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:
keyringcrate (native) - OS Notifications: Tauri notification plugin (native)
- Filesystem:
tokio::fs+.gitpulseignoreparser
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):
-
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, orreact-window. This choice affects RepoCard/ListView implementation directly. -
Missing Tauri plugins in stack additions:
tauri-plugin-autostart(FR34) andtauri-plugin-notification(FR32) are not listed alongside the other plugins. These are required for system tray agent features. -
Dormancy detection not explicitly placed (FR26): No Rust module or function is named for configurable dormancy threshold logic. Should be explicit — likely in
scanner.rsorcommands/dashboard.rs.
Minor Gaps (can address during implementation):
-
i18n framework not specified: PRD requires "i18n framework ready from day 1" but no library is listed (e.g.,
react-i18next,typesafe-i18n). -
.gitpulseignoreparsing crate not named:ignore.rsis listed but the specific crate (ignorefrom ripgrep,globset, or custom) is not specified. -
Accessibility testing approach not detailed: NFR25-27 are architecturally addressed but no testing tooling is specified (e.g.,
axe-coreintegration 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
AiProvidertrait 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.