Some checks failed
Test / rust-fmt-check (pull_request) Failing after 11s
Test / rust-clippy (pull_request) Failing after 14s
Test / rust-tests (pull_request) Failing after 17s
Test / frontend-tests (pull_request) Successful in 1m26s
Test / frontend-typecheck (pull_request) Successful in 1m34s
PR Review Automation / review (pull_request) Successful in 2m54s
Final cleanup pass: **1. Makefile:** - GOGS_REPO: msicie/apollo_nxt-tftsr → sarman/tftsr-devops_investigation - Fixed to use correct Gitea repository **2. Removed Files:** - docs/2026-HACKATHON-SUMMARY.md (not needed) **3. Branding Corrections:** - Architecture docs: tftsr → trcaa (TRCAA is the app name, not TFTSR) - TFTSR was old/incorrect branding - Fixed in: docs/architecture/README.md, ADR-005, ADR-006 **4. CI/CD Documentation:** - docs/wiki/CICD-Pipeline.md: Woodpecker CI → Gitea Actions - Removed all GitHub Actions references - This project uses Gitea Actions exclusively **5. Code Cleanup:** - src-tauri/src/ai/openai.rs: 'TFTSR GenAI' → 'GenAI' - src-tauri/src/integrations/query_expansion.rs: VNXT → Product (removed proprietary) **6. Test Cleanup:** - tests/unit/ciDockerBuilders.test.ts.disabled: github → gitea, ghcr.io → 172.0.0.29:3000 **Verification:** All 308 Rust tests + 92 frontend tests passing Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
3.2 KiB
3.2 KiB
ADR-006: Zustand for Frontend State Management
Status: Accepted Date: 2025-Q3 Deciders: sarman
Context
The React frontend manages three distinct categories of state:
- Ephemeral session state: Current issue, AI chat messages, PII spans, 5-whys progress — exists for the duration of one triage session, should not survive page reload
- Persisted settings: Theme, active AI provider, PII pattern toggles — should survive app restart, stored locally
- Cached server data: Issue history, search results — loaded from DB on demand, invalidated on changes
Decision
Use Zustand for all three state categories, with selective persistence via localStorage for settings only.
Rationale
Alternatives considered:
| Option | Pros | Cons |
|---|---|---|
| Zustand (chosen) | Minimal boilerplate, built-in persist middleware, TypeScript-first | Smaller ecosystem than Redux |
| Redux Toolkit | Battle-tested, DevTools support | Verbose boilerplate for simple state |
| React Context | No dependency | Performance issues with frequent updates (chat messages) |
| Jotai | Atomic state, minimal | Less familiar pattern |
| TanStack Query | Excellent for async server state | Overkill for Tauri IPC (not HTTP) |
Store architecture decisions:
sessionStore — NOT persisted:
- Chat messages accumulate quickly; persisting would bloat localStorage
- Session is per-issue; loading a different issue should reset all session state
reset()method called on navigation away from triage
settingsStore — Persisted to localStorage as "trcaa-settings":
- Theme, active provider, PII pattern toggles — user preference, should survive restart
- AI providers themselves are NOT persisted here — only
active_providerstring - Actual
ProviderConfig(with encrypted API keys) lives in the backend DB, loaded viaload_ai_providers()
historyStore — NOT persisted (server-cache pattern):
- Always loaded fresh from DB on History page mount
- Search results replaced on each query
- No stale-data risk
Persistence Details
The settings store persists to localStorage:
persist(
(set, get) => ({ ...storeImpl }),
{
name: 'trcaa-settings',
partialize: (state) => ({
theme: state.theme,
active_provider: state.active_provider,
pii_enabled_patterns: state.pii_enabled_patterns,
// NOTE: ai_providers excluded — stored in encrypted backend DB
})
}
)
Why localStorage and not a Tauri store plugin:
- Settings are non-sensitive (theme, provider name, pattern toggles)
tauri-plugin-storewould add IPC overhead for every settings read- localStorage survives across WebView reloads without async overhead
Consequences
Positive:
- Minimal boilerplate — stores are ~50 LOC each
zustand/middleware/persisthandles localStorage serialization- Subscribing to partial state prevents unnecessary re-renders
- No Provider wrapping required — stores accessed via hooks anywhere
Negative:
- No Redux DevTools integration (Zustand has its own devtools but less mature)
- localStorage persistence means settings are WebView-profile-scoped (fine for single-user app)
- Manual cache invalidation in
historyStoreafter issue create/delete