diff --git a/docs/history/fase-15-common-items.md b/docs/history/fase-15-common-items.md
new file mode 100644
index 0000000..4a3dc82
--- /dev/null
+++ b/docs/history/fase-15-common-items.md
@@ -0,0 +1,93 @@
+# Fase 15 — Common items management (weighted suggestions + exclude-on-list)
+
+**MVP2 · 7/7. Re-opened the cycle after Fase 14 with two concrete asks on the "add item" flow.**
+
+**Status: ✅ Shipped 2026-05-18.**
+
+Two user-driven additions on top of the existing `item_frequency` table from migration 006:
+
+1. **Promote items** — admins can boost specific names so they always lead the suggestion strip, regardless of historical `use_count`.
+2. **Hide items already on the list** — the dropdown no longer nudges the user to re-add names that are already present in the active list.
+
+Plan: `plan/fase-15-common-items.md`. Migration: `supabase/migrations/026_item_frequency_weight.sql`. pgTAP: `supabase/tests/018_item_frequency_weight.sql`.
+
+## What changed
+
+### 15.1 — `item_frequency.weight` + admin RPCs
+
+- `weight integer NOT NULL DEFAULT 0` added to `item_frequency`. No CHECK constraint — the column stays open so future tiers ("strong boost", "soft hide") don't force another schema diff. The UI is the contract for the 3-way mapping, not the DB.
+- `item_frequency_collective_count_idx` dropped, replaced by `item_frequency_collective_order_idx (collective_id, weight DESC, use_count DESC, last_used_at DESC)` so the suggestion query scans in order without a sort step.
+- Two SECURITY DEFINER RPCs gate writes:
+ - `public.set_item_frequency_weight(p_collective_id uuid, p_name text, p_weight integer)` — upsert: if the row doesn't exist, inserts with `use_count = 0`; if it does, sets `weight`. Raises `P0002` for non-admins, `P0001` for unauthenticated / empty name.
+ - `public.purge_item_frequency(p_collective_id uuid, p_name text)` — deletes the row from `item_frequency` only. Does NOT touch `shopping_items` (the modal copy explicitly says this; existing items on lists are unaffected). The next time someone adds that name to a list, the existing trigger recreates the row with `weight = 0`.
+- The deny-all INSERT/UPDATE/DELETE policies from migration 006 are preserved; the RPCs are the only write path. Both `GRANT EXECUTE ... TO authenticated` so PostgREST will call them.
+
+### 15.2 — Store + query
+
+- `fetchSuggestions(collectiveId, prefix, options?)` in `apps/web/src/lib/stores/lists.ts` extended with `{ excludeNames?: string[]; limit?: number }`. Ordering switches from `.order('use_count')` to `weight DESC, use_count DESC, last_used_at DESC`. `excludeNames` is lowercased + trimmed + deduped before being sent; when empty, the `.not()` filter is skipped entirely (PostgREST renders an empty `(...)` list as a SQL error, and zero entries would mean zero effect anyway). When the list exceeds `EXCLUDE_NAMES_HARD_CAP = 200`, the filter degrades to a no-op rather than fail the whole query — past that point we'd risk hitting Kong's ~16 KiB request URI cap (see "Gotchas" below).
+- New `apps/web/src/lib/stores/commonItems.ts` is the unbounded counterpart used by the manage view: `loadCommonItems` (full catalogue, same ordering, no `LIMIT`), `setWeight` (RPC + optimistic in-store update), `purge` (RPC + optimistic remove).
+
+### 15.3 — Manage UI
+
+`apps/web/src/routes/(app)/collective/manage/+page.svelte` gains a new "Common items" subsection visible to admins + members (guests see nothing):
+
+- Table per row: name, last_used + use_count blurb, a 3-way **Hide / Normal / Boost** segmented control that writes `weight = -50 / 0 / 50`, a small "Remove" button that opens a confirmation modal explaining "items already in lists are not touched".
+- Members see the same table but every action button is disabled with a tooltip "Only admins can manage common items".
+- "Add common item" button (admin only) — opens a modal with a name input + the same 3-way control, defaulting to **Boost**. Useful for seeding the catalogue before lists actually mention an item.
+- Client-side substring search filters the table; empty / filtered-empty states have their own copy.
+- `data-weight-state` attribute on each `
` exposes the current visual state to the e2e suite so the segmented control is testable without scraping classnames.
+- The segmented control is a row of three `