docs: split CLAUDE.md into docs/{development,deployment,history/fase-*}

CLAUDE.md shrinks to rules + status + index (175 lines, down from 476).
Dev setup, prod deploy, and per-phase build records move to docs/.
Gotchas regrouped by domain (auth, frontend, PWA, testing, backend, deploy,
platform) and unnumbered so they don't drift as new ones land.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 22:21:20 +02:00
parent 3d85d0a50e
commit 768b3cda58
11 changed files with 441 additions and 403 deletions

48
docs/history/fase-1.md Normal file
View File

@@ -0,0 +1,48 @@
# Fase 1 — what has been built
- `supabase/migrations/001_users.sql``users` table, `handle_new_user` trigger
- `supabase/migrations/002_collectives.sql``collectives`, `collective_members`, `collective_invitations`; auto-promote-oldest-admin trigger
- `supabase/migrations/003_rls.sql` — full RLS policies, `is_active_member`/`is_member`/`is_admin` helpers, `accept_invitation()` function
- `supabase/migrations/004_storage.sql``avatars` bucket (private, signed URLs)
- `apps/web/src/lib/supabase.ts` — Supabase singleton with PKCE flow
- `apps/web/src/lib/auth.ts``login()` / `logout()` via `supabase.auth.signInWithOAuth`
- `apps/web/src/lib/stores/auth.ts``currentUser`, `authLoading`, `isAuthenticated`, `displayName`
- `apps/web/src/lib/stores/collective.ts``currentCollective`, `userCollectives`, `collectiveMembers`
- `apps/web/src/lib/components/Avatar.svelte` — initials / emoji / upload with deterministic colour hash
- `apps/web/src/lib/components/ImageCropper.svelte` — lazy-loaded cropperjs, 1:1, 256×256 WebP output
- `apps/web/src/routes/+layout.svelte` — auth state listener, collective bootstrap
- `apps/web/src/routes/(app)/+layout.ts` — SSR disabled (`ssr: false`); no auth check here (see gotcha below)
- `apps/web/src/routes/(app)/+layout.svelte` — sidebar with collective switcher; auth redirect via `$effect`
- `apps/web/src/routes/auth/callback/+page.svelte` — PKCE code exchange
- `apps/web/src/routes/onboarding/+page.svelte` — create collective or join via link
- `apps/web/src/routes/(app)/collective/manage/+page.svelte` — members, roles, invite link generator
- `apps/web/src/routes/invitation/[token]/+page.svelte` — accept invitation (auth-aware)
- `apps/web/src/routes/(app)/settings/+page.svelte` — display name, avatar, language, sign out
- `apps/web/messages/en.json` + `es.json` — all Fase 1 + 2a strings
- `packages/types/src/database.ts` — manually seeded from migrations (update with `just db-types` once CLI is wired)
- `setup.sh` — idempotent local dev setup (prerequisites, .env, Docker, migrations, seed, Paraglide)
- `infra/kong-start.sh` — Kong container entrypoint; substitutes `${VAR}` placeholders in `kong.yml` before Kong starts (Kong 2.8 does not do this natively)
## Test suite — initial scaffolding
- `packages/test-utils/` — shared test infrastructure (Vitest integration tests)
- `package.json``@colectivo/test-utils`; depends on `@supabase/supabase-js`, `jose`, `pg`
- `vitest.config.ts` — loads root `.env` via `loadEnv`; single-fork mode (no parallelism races)
- `src/seed-constants.ts` — fixed UUIDs for all 5 seed users + collective + seed list
- `src/supabase-clients.ts``createClientAs(userId)` signs HS256 JWT with `SUPABASE_JWT_SECRET`; `createAdminClient()` uses service role key
- `src/db-helpers.ts``sql()` via raw `pg` Pool for direct DB manipulation (bypasses RLS); used to set `deleted_at = 8 days ago` etc.
- `tests/rls-isolation.test.ts` — F-series: Eva (non-member) sees zero rows, all writes blocked
- `tests/rls-collective.test.ts` — B-series: collective read/update/invite by role
- `tests/rls-lists.test.ts` — C-series: list CRUD, soft-delete, trash window, cross-collective isolation
- `tests/rls-items.test.ts` — D-series: item CRUD by role, cross-collective item isolation
- `tests/rls-frequency.test.ts` — E-series: frequency read-only + trigger populates on INSERT
- `supabase/tests/*.sql` each start with `CREATE EXTENSION IF NOT EXISTS pgtap;` so they're idempotent on a fresh DB
- `001_accept_invitation.sql``accept_invitation()` happy path + not_found / expired / already_used; uses `set_config('request.jwt.claim.sub', …)` to set `auth.uid()` for the test session, and looks up by `token` (not `id`)
- `002_item_frequency_trigger.sql` — trigger normalizes name with `lower(trim(...))` and increments `use_count` on repeat INSERTs. RLS write-blocks are covered by the Vitest suite instead (postgres superuser bypasses RLS here)
- `003_promote_on_admin_leave.sql` — deletes a synthetic admin from `public.users` to fire the `BEFORE DELETE` trigger and asserts the oldest remaining member is promoted
- `apps/web/playwright.config.ts` — Playwright config; `globalSetup` is a Keycloak health check (not a session cache), `reuseExistingServer: true`
- `apps/web/tests/fixtures/users.ts` — seed user constants (Ana, Borja, Carmen, David, Eva)
- `apps/web/tests/fixtures/login.ts``loginAs(page, user)` runs the real OAuth flow (Keycloak → GoTrue PKCE) and waits for the session token to land in localStorage. Each test that needs auth calls this in `beforeEach`; cached `storageState` is NOT used (see the Playwright storageState gotcha in `CLAUDE.md`)
- `apps/web/tests/e2e/auth.test.ts` — A-series: unauthenticated redirect, full login, collective visible in sidebar, sign-out, guest login
- `apps/web/tests/e2e/lists.test.ts` — C-series: create (with reload-persistence check), soft-delete → appears in trash drawer, archive, guest read-only
- `apps/web/tests/e2e/items.test.ts` — D-series: seed list renders, add item (with reload-persistence check), toggle check, desktop-hover delete, frequency suggestions, guest read-only