# Software Architect Audit Report (March 27, 2026) ## Clarification Questions 1. Should a new theme be creatable with empty categories/topic (draft mode), or must it be valid at creation? 2. Should `GET /themes/{id}/schedule` return `404` when absent, or `200` with `null`? 3. Which defaults are canonical for themes: DB/spec (`max_items_per_category=4`, `summary_length=3`) or UI/handler (`5`, `2`)? ## Assumptions - Documentation in `docs/` is the intended target behavior. - This is architecture-focused (entry points, service boundaries, major modules), not a line-by-line audit of all files. ## Prioritized Findings ### P0 - Theme creation contract is internally inconsistent and likely broken in practice. - Evidence: UI creates empty topic/categories in `/Users/oabrivard/Projects/rust/ai_synth/frontend/src/pages/ThemeManager.tsx:144`, while backend rejects empty topic/categories in `/Users/oabrivard/Projects/rust/ai_synth/backend/src/models/theme.rs:43` and enforces validation in `/Users/oabrivard/Projects/rust/ai_synth/backend/src/handlers/themes.rs:45`. - Why it matters: blocks core onboarding flow. - Direction: align API contract + UI create flow (draft-support or valid seeded defaults). ### P1 - Schedule API contract drift across spec, backend, tests, and frontend typing. - Evidence: spec says `ScheduleResponse` or 404 in `/Users/oabrivard/Projects/rust/ai_synth/docs/technical_specs.md:420`; backend returns `200 null` in `/Users/oabrivard/Projects/rust/ai_synth/backend/src/handlers/schedules.rs:40`; tests expect `200 null` in `/Users/oabrivard/Projects/rust/ai_synth/backend/tests/api_schedules_test.rs:77`; frontend assumes non-null in `/Users/oabrivard/Projects/rust/ai_synth/frontend/src/components/settings/SettingsSchedule.tsx:48`. - Why it matters: runtime fragility and contract confusion. - Direction: choose one contract and align all layers. - Theme defaults are inconsistent with DB/spec. - Evidence: DB migration defaults 4/3 in `/Users/oabrivard/Projects/rust/ai_synth/backend/migrations/20260326000028_create_themes_and_migrate.sql:8`; handler uses 5/2 in `/Users/oabrivard/Projects/rust/ai_synth/backend/src/handlers/themes.rs:56`; UI uses 5/2 in `/Users/oabrivard/Projects/rust/ai_synth/frontend/src/pages/ThemeManager.tsx:51`. - Why it matters: non-deterministic behavior and drift. - Direction: establish a single source of truth for defaults. - Reliability gap: job-store TTL cleanup exists but is not scheduled at runtime. - Evidence: cleanup function exists in `/Users/oabrivard/Projects/rust/ai_synth/backend/src/services/job_store.rs:153`; startup schedules other tasks but not job-store cleanup in `/Users/oabrivard/Projects/rust/ai_synth/backend/src/main.rs:66`; docs expect periodic cleanup in `/Users/oabrivard/Projects/rust/ai_synth/docs/technical_specs.md:778`. - Why it matters: stale in-memory entries and lock leakage risk over time. - Direction: add periodic `job_store.cleanup_expired()` task. - Scheduled generation path does not enforce 15-minute timeout used by manual generation. - Evidence: manual timeout in `/Users/oabrivard/Projects/rust/ai_synth/backend/src/handlers/generation.rs:89`; scheduler calls pipeline directly in `/Users/oabrivard/Projects/rust/ai_synth/backend/src/services/scheduler.rs:57`; requirement states 15-minute cap in `/Users/oabrivard/Projects/rust/ai_synth/docs/requirements.md:30`. - Why it matters: scheduled runs can overrun and block throughput. - Direction: apply same timeout semantics to scheduled execution. ### P2 - Core pipeline and key pages are too large. - Evidence: `/Users/oabrivard/Projects/rust/ai_synth/backend/src/services/synthesis.rs`, `/Users/oabrivard/Projects/rust/ai_synth/backend/src/services/scraper.rs`, `/Users/oabrivard/Projects/rust/ai_synth/frontend/src/pages/ThemeManager.tsx`, `/Users/oabrivard/Projects/rust/ai_synth/frontend/src/pages/Settings.tsx`. - Why it matters: weaker SRP, higher cognitive load, slower onboarding/review/testing. - Direction: split by bounded responsibilities (phase modules/services/hooks/components). - Frontend architectural consistency drift (API and data-loading patterns). - Evidence: direct `fetch` in `/Users/oabrivard/Projects/rust/ai_synth/frontend/src/pages/GenerateSynthesis.tsx:241` despite centralized client in `/Users/oabrivard/Projects/rust/ai_synth/frontend/src/api/client.ts:25`; guideline preference for `createResource` in `/Users/oabrivard/Projects/rust/ai_synth/docs/dev_guidelines.md:174`. - Direction: enforce API client boundary and consistent data-loading patterns. ## SOLID Scorecard | Dimension | Score (/5) | Notes | |---|---:|---| | Single Responsibility | 2 | Several large multi-responsibility modules. | | Open/Closed | 4 | LLM provider abstraction/factory is extensible. | | Liskov Substitution | 4 | Provider implementations conform to trait contract cleanly. | | Interface Segregation | 3 | Mostly good boundaries with a few broad surfaces. | | Dependency Inversion | 3 | Good trait usage for LLM, pipeline still tightly coupled in places. | | Overall maintainability | 3 | Good foundations, but contract drift + module size are bottlenecks. | ## Refactoring Plan 1. Phase 0: Contract decisions (0.5-1 day, low risk). - Decide canonical behavior for theme creation, schedule GET semantics, and defaults. 2. Phase 1: Correctness alignment (1-2 days, medium risk). - Align backend/frontend/tests with chosen contracts. 3. Phase 2: Reliability hardening (1 day, low-medium risk). - Add job-store cleanup scheduling and timeout parity in scheduler. 4. Phase 3: Backend decomposition (3-5 days, medium risk). - Split synthesis pipeline into phase modules with explicit contexts. 5. Phase 4: Frontend decomposition (3-5 days, medium risk). - Split large pages into composables/components and remove direct page-level fetch. 6. Phase 5: Drift prevention (1-2 days, low risk). - Add conformance tests/checks for contracts/defaults and PR architecture checklist. ## Architecture/SOLID Scorecard | Dimension | Score (/5) | Notes | |---|---:|---| | Layering clarity | 3 | Clear module boundaries, but some runtime orchestration gaps. | SRP | 2 | Multiple "god files" in backend and frontend. | OCP | 4 | LLM provider abstraction/factory is cleanly extensible. | DIP | 3 | Core synthesis is trait-based for LLM, but still tightly coupled to DB/functions. | Testability | 3 | Strong unit/integration coverage in backend; scheduler/e2e gaps remain. | Spec alignment | 2 | Multiple contract/default mismatches. ## Refactoring Plan (Architecture-Level) ### Phase 1 (1-2 days) — Contract and reliability fixes - Align theme creation contract (UI + model validation + tests). - Align schedule GET semantics across docs/handler/tests/frontend types. - Add periodic `job_store.cleanup_expired()` background task. - Apply 15-minute timeout to scheduled generation path. ### Phase 2 (3-5 days) — Backend decomposition - Split `services/synthesis.rs` into submodules: - `init.rs` (load settings/theme/provider) - `phase1_personalized.rs` - `phase2_fallback.rs` - `finalize.rs` - `tracing.rs` - Introduce explicit pipeline context structs to reduce argument fanout. ### Phase 3 (3-5 days) — Frontend decomposition - Extract page-level logic from `ThemeManager` and `Settings` into composables/hooks: - `useThemeEditor`, `useSourcesManager`, `useScheduleConfig`, `useSettingsForm` - Normalize API usage through `api/client.ts` only. ### Phase 4 (2-3 days) — Conformance hardening - Add spec-conformance tests for defaults, schedule contract, and theme creation. - Add architecture fitness checks (lint/custom tests) for forbidden contract drifts. ## Quick Wins (< 1 day) 1. Add scheduled timeout wrapper in `scheduler.rs`. 2. Add job-store periodic cleanup task in `main.rs`. 3. Fix `ThemeManager` create payload to pass valid initial `theme` and categories. 4. Unify schedule GET typing with actual backend behavior.