diff --git a/docs/superpowers/specs/2026-03-27-preferred-sources-design.md b/docs/superpowers/specs/2026-03-27-preferred-sources-design.md new file mode 100644 index 0000000..0876556 --- /dev/null +++ b/docs/superpowers/specs/2026-03-27-preferred-sources-design.md @@ -0,0 +1,72 @@ +# Design: Preferred Sources + +**Date**: 2026-03-27 +**Scope**: Allow users to mark sources as preferred so they are processed first during synthesis generation. + +--- + +## 1. Data Model + +Add `is_preferred BOOLEAN NOT NULL DEFAULT false` to the `sources` table. All existing sources default to non-preferred. + +**Bulk update endpoint:** `PUT /api/v1/sources/preferred` with body `{ "source_ids": ["uuid1", "uuid2"] }`. Sets the listed sources to `preferred = true` and all others (for the same user) to `false`. This is an idempotent "set the full list" operation. + +--- + +## 2. Pipeline Shuffle + +### Source ordering (before wave chunking) + +After `rotate_sources`, split into preferred and non-preferred, preserving rotation order within each group: + +``` +rotated = rotate_sources(all_sources) +preferred = rotated.filter(is_preferred) +non_preferred = rotated.filter(!is_preferred) +ordered = preferred ++ non_preferred +waves = ordered.chunks(window_size) +``` + +Preferred sources are processed in earlier waves, maximizing their chance of filling the synthesis. + +### URL shuffle (within each wave) + +After extracting links from a wave's sources, split candidate URLs by whether their source was preferred: + +``` +preferred_urls = wave_urls.filter(source is preferred) +other_urls = wave_urls.filter(source is not preferred) +shuffle(preferred_urls) +shuffle(other_urls) +final = preferred_urls ++ other_urls +``` + +--- + +## 3. Frontend + +### ThemeManager source list + +- Checkbox on each source row (left side) +- Preferred sources get a star icon or subtle highlight +- Counter at bottom: "X source(s) prioritaire(s)" +- Auto-save: on check/uncheck, immediately call `PUT /api/v1/sources/preferred` with updated ID list + +--- + +## 4. Files + +- **Create:** `backend/migrations/20260327000029_add_source_preferred.sql` +- **Create:** `backend/tests/api_sources_preferred_test.rs` +- **Modify:** `backend/src/models/source.rs` — add `is_preferred: bool` to `Source` +- **Modify:** `backend/src/db/sources.rs` — add to queries, add `update_preferred` function +- **Modify:** `backend/src/handlers/sources.rs` — add `update_preferred` handler +- **Modify:** `backend/src/router.rs` — add route +- **Modify:** `backend/src/services/synthesis.rs` — preferred-first ordering + shuffle +- **Modify:** `backend/tests/pipeline_test.rs` — test preferred ordering +- **Modify:** `frontend/src/pages/ThemeManager.tsx` — checkboxes, counter, auto-save +- **Modify:** `frontend/src/api/sources.ts` — add `updatePreferred` +- **Modify:** `frontend/src/types.ts` — add `is_preferred` to `Source` +- **Modify:** `frontend/src/i18n/fr.ts` — labels +- **Modify:** `e2e/tests/sources.spec.ts` — add preferred test +- **Modify:** `CLAUDE.md` — migration count