diff --git a/docs/superpowers/specs/2026-03-26-polish-optimizations-design.md b/docs/superpowers/specs/2026-03-26-polish-optimizations-design.md new file mode 100644 index 0000000..71c67d5 --- /dev/null +++ b/docs/superpowers/specs/2026-03-26-polish-optimizations-design.md @@ -0,0 +1,90 @@ +# 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` or `Arc` 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` 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` in `AppConfig`. Update all call sites that reference `state.config.master_encryption_key` — they already call `MasterKey::from_hex(&...)` which takes `&str`, so `Arc` derefs transparently. + +**Files**: `backend/src/config.rs`, and any files that access `config.master_encryption_key`