Wires the tag model into the list detail view end-to-end.
`/lists/[id]/+page.svelte`:
* switches the cold-load to loadListItems() so every row carries its
tag array via the PostgREST embedding (one query, not N+1)
* items state typed as ItemWithTags; the existing realtime shopping_items
feed shallow-merges the row payload back onto the cached tag array,
so attaches survive an UPDATE echo for the same row
* new tagLinkChannel subscribes to shopping_item_tags `*` events and
refreshes just the affected item's tags from the server — the delta
payload doesn't carry the joined tag rows, refetching is simplest
* item-row chips render below the name on a separate wrap-friendly
line; each chip is its own filter button (TagChip onSelect)
* filter bar between the title and the list shows the collective's
tags (active ones first); selecting toggles a tag; multi-selection
is intersection; the selection persists in `?tags=foo,bar` via
history.replaceState so the URL is deep-linkable
* edit overlay gains a "Tags" section powered by TagPicker — attach +
detach are optimistic and write through to shopping_item_tags; the
"Create" affordance creates the tag then attaches it in one click
Drag-reorder handlers were updated to merge `e.detail.items` (the dndzone
subset, which is the post-filter unchecked rows) with EVERY off-zone row,
not just the checked half. Without this, dragging while a tag filter is
active would drop hidden rows from `items`.
E2E (tags.test.ts): TG-01 verifies the full create → attach → filter
loop (3 items shown after activating the filter, seed items hidden); TG-02
deep-links via `?tags=a,b` and asserts intersection; TG-03 sanity-checks
the picker for a second user in the same collective (cross-collective
denial is fully covered by the Vitest IT-03 integration assertion).
All 3 e2e tests pass against the dev stack.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>