You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ai_synth/docs/superpowers/specs/2026-03-26-polish-optimizat...

91 lines
4.2 KiB
Markdown

# Design: Polish & Optimization Items (Audit Medium Priority)
**Date**: 2026-03-26
**Scope**: 8 polish/optimization items from the code audit
**Source**: `docs/audit/00-consolidated-summary.md` — Medium Priority section
---
## 1. Batch article history INSERTs
**Problem**: Each `trace_article` call does an individual INSERT. 20+ sequential DB round-trips per generation.
**Fix**: Add `batch_insert_entries(pool, entries: &[ArticleHistoryEntry])` in `db/article_history.rs` using `unnest()` arrays for bulk INSERT. In `synthesis.rs`, collect traces into a `Vec` and flush in a single batch call at the end of each processing phase.
**Files**: `backend/src/db/article_history.rs`, `backend/src/services/synthesis.rs`
---
## 2. Reduce `.clone()` in pipeline with `Arc`
**Problem**: `model_research`, `classification_categories`, `classify_schema`, and other immutable values are cloned into every spawned task (59 clones identified).
**Fix**: Wrap these values in `Arc<str>` or `Arc<Value>` at the start of `run_generation_inner`. Spawned tasks clone the `Arc` (cheap pointer copy) instead of the full string/JSON value. Target the heaviest clones inside the batch loops.
**Files**: `backend/src/services/synthesis.rs`
---
## 3. Cache CSS selectors with `LazyLock`
**Problem**: `Selector::parse("a[href]")` is called on every invocation of link extraction functions, re-parsing the same static selector.
**Fix**: Use `std::sync::LazyLock<Selector>` for static selectors in `source_scraper.rs` and `scraper.rs`.
**Files**: `backend/src/services/source_scraper.rs`, `backend/src/services/scraper.rs`
---
## 4. Standardize frontend data fetching on `createResource`
**Problem**: Some pages use `createResource`, others use `onMount` + manual signals for data loading. Inconsistent patterns.
**Fix**: Convert pages that use `onMount` + `createSignal` for data loading to use `createResource`. Only change pages where the pattern is clearly `onMount → fetch → setSignal` and `createResource` is a direct fit. Don't force conversions where the pattern is more complex (e.g. SSE, conditional fetching).
**Files**: Frontend pages using the `onMount` + signal pattern (identify by reading each page)
---
## 5. Use existing `Button` component
**Problem**: A reusable `Button` component exists but inline Tailwind button classes are duplicated across pages.
**Fix**: Replace obvious inline button markup with the existing `Button` component. Only do clear-cut cases where the button matches the component's API — don't force every button into it.
**Files**: Frontend pages with inline button markup
---
## 6. Align default settings between frontend and backend
**Problem**: `DEFAULT_SETTINGS` in frontend and `Default for UserSettings` in backend have different category lists and potentially other divergent values.
**Fix**: The backend is authoritative. Update `DEFAULT_SETTINGS` in `frontend/src/types.ts` to match `Default for UserSettings` in `backend/src/models/settings.rs`. The frontend defaults are only used as initial form state before the API response arrives, so they should match what the backend would return for a new user.
**Files**: `frontend/src/types.ts`
---
## 7. Remove unused `SESSION_SECRET`
**Problem**: `session_secret` is loaded from env, validated (min 64 chars), stored in `AppConfig`, but never referenced anywhere in the codebase. The app uses DB-backed session tokens, not signed cookies.
**Fix**:
- Remove `session_secret` field from `AppConfig` struct
- Remove `required_var("SESSION_SECRET")` from config loading
- Remove the `session_secret` length validation
- Remove from `.env.example`
- Update related tests
**Files**: `backend/src/config.rs`, `.env.example`
---
## 8. Wrap `AppConfig` master key in `Arc`
**Problem**: `master_encryption_key` is a cloneable `String` in `AppConfig`. Since `AppConfig` is cloned into `AppState`, the secret exists in multiple memory locations.
**Fix**: Change `master_encryption_key: String` to `master_encryption_key: Arc<String>` in `AppConfig`. Update all call sites that reference `state.config.master_encryption_key` — they already call `MasterKey::from_hex(&...)` which takes `&str`, so `Arc<String>` derefs transparently.
**Files**: `backend/src/config.rs`, and any files that access `config.master_encryption_key`