From 2bed28212d59063f9fb8ac26498cb1813d6af4f8 Mon Sep 17 00:00:00 2001 From: Till Date: Fri, 8 May 2026 17:18:16 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20STATUS.md=20als=20Single=20Source=20of?= =?UTF-8?q?=20Truth=20f=C3=BCr=20Cards-Onboarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bisherige Doku war über CLAUDE.md, README.md, SMOKE_TEST.md, LESSONS_FROM_MANA_MONOREPO.md plus mana/docs/playbooks/CARDS_GREENFIELD.md zerstreut — eine fresh-AI-Session musste sich Status zusammenstückeln. STATUS.md zentralisiert: - TL;DR + Architektur-Topologie - Architektur-Entscheidungen (festgenagelt) - Phasen-Status-Tabelle (✅/🚧/⏸ pro Phase mit Verifikations-Hinweis) - Lokal-Lauf-Anleitung (5 Schritte zu cards-api + cards-web im Browser) - Verifizierte Endpoints-Liste - Pre-Flight für Phase 2 + Live-Föderation - Wichtige Pointer: Konventionen, Stack, Files, Cross-Repo-Doks - Git-Historie (6 Commits) - 6 Architektur-Subtilitäten, die nicht offensichtlich sind: Reviews-plaintext, SubIndex-Granularität, Protocol-Mirror-TEMPORARY, Inbox-Auto-Create, Dev-Auth-temporär, mana-monorepo-Decommission - Onboarding-Sequenz (5-Min-Lese-Plan) - Vorschläge für nächste Phasen Cross-References: - CLAUDE.md verweist als erstes auf STATUS.md - README.md ebenso - mana/docs/playbooks/CARDS_GREENFIELD.md (im Plattform-Repo) verweist zurück auf cards/STATUS.md für aktuellen Stand — Playbook ist der Plan, STATUS.md ist die Lage. Konvention: bei Phasen-Wechsel STATUS.md aktualisieren, nicht den Playbook (sonst Doku-Drift). Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 6 + README.md | 2 + STATUS.md | 328 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+) create mode 100644 STATUS.md diff --git a/CLAUDE.md b/CLAUDE.md index ddf62e8..f3614a3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,6 +2,12 @@ Guidance for Claude Code when working in this repository. +> **Wenn du gerade neu bist:** lies zuerst [`STATUS.md`](STATUS.md) — dort +> steht der aktuelle Phasen-Stand, was schon läuft, was offen ist, und +> wie du lokal verifizierst. Dieses CLAUDE.md ist nur die Konventions- +> und Architektur-Referenz; der Status lebt in STATUS.md, damit er nicht +> zwischen mehreren Dateien drifted. + ## Was dieses Repo ist **Cards** — eigenständige föderierte Spaced-Repetition-App des diff --git a/README.md b/README.md index 109a73b..104460d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ Notizen aus Mana, Web-Schnipsel aus dem Browser-Plugin). → Live (geplant): +**Aktueller Stand und Pickup-Onboarding: [`STATUS.md`](STATUS.md).** + ## Stack - **Frontend:** SvelteKit 2 + Svelte 5 (runes-only) diff --git a/STATUS.md b/STATUS.md new file mode 100644 index 0000000..a11bf38 --- /dev/null +++ b/STATUS.md @@ -0,0 +1,328 @@ +# Cards — Projekt-Status & Onboarding + +**Letztes Update:** 2026-05-08 +**Wenn du gerade neu bist (Mensch oder KI):** dieses Dokument soll dir +in 5 Minuten den vollen Kontext geben. Lies es vor allem anderen. + +--- + +## TL;DR + +- **Cards** ist die föderierte Spaced-Repetition-App des Vereins + **mana e.V.** Strategie-B-Greenfield (beschlossen 2026-05-08): kein + Code-Übernahme aus dem alten `mana-monorepo`, sauber neu gebaut. +- **6 saubere Commits** auf `main`. Type-check 4/4 grün, **70 Tests + grün**, lokaler E2E-Smoke-Test (Postgres → API → Frontend → Föderations- + Endpunkte) durch. +- **Phasen 0, 1, 3, 4, 5 sind durch** und verifiziert. Phase 2 (Auth- + Föderation) ist auf user-side Pre-Flight blockiert. +- Cards läuft lokal, ist im Browser benutzbar, hat alle Föderations- + Endpoints aus dem `app-manifest.json` implementiert. + +``` +┌─────────────────────────────────┐ +│ cards/ (this repo) │ +│ │ +│ apps/web/ SvelteKit + Svelte 5 ← cardecky.mana.how +│ apps/api/ Hono + Bun + Drizzle ← Postgres `cards` +│ packages/cards-domain/ Pure-TS ← FSRS, Schemas, Protocol-Mirror +│ app-manifest.json Föderations-Vertrag +└─────────────────────────────────┘ + ▲ + │ HTTP, JWT, Manifest + ▼ +┌─────────────────────────────────┐ +│ mana/ Plattform │ +│ mana-auth, mana-share, │ +│ mana-links, mana-mcp, │ +│ mana-search, mana-credits, … │ +└─────────────────────────────────┘ +``` + +--- + +## Architektur-Entscheidungen (festgenagelt) + +Diese stehen — nicht ohne explizite Diskussion antasten: + +1. **Strategie B (Greenfield).** Kein Code aus mana-monorepo. +2. **Server-authoritative MVP.** Keine Dexie, keine eigene Sync-Engine. + Local-First später via mana-sync-Federation, nicht durch eigenen + Stack. +3. **Eigene Postgres-DB `cards`** mit Schema-Isolation `pgSchema('cards')`. +4. **Föderations-Endpoints als Pflicht** — alle aus `app-manifest.json` + implementiert (siehe Phase 5 unten). +5. **Encryption initial AUS.** Nachrüstbar via mana-auth-MK. +6. **MVP-Card-Types nur `basic` + `basic-reverse`.** Schema vorbereitet + auf full-set (cloze, type-in, image-occlusion, audio, multiple-choice). + +Vollständiger Plan: [`mana/docs/playbooks/CARDS_GREENFIELD.md`](../mana/docs/playbooks/CARDS_GREENFIELD.md) +(im Plattform-Repo, weil er Verein-übergreifend gilt). + +--- + +## Phasen-Status + +| # | Phase | Status | Verifikation | +|---|---|---|---| +| 0 | Read-Day mana-monorepo-Cards-Code lesen | ✅ | `docs/LESSONS_FROM_MANA_MONOREPO.md` | +| 1 | Repo-Skelett (Turbo, pnpm, Bun, Docker, CI) | ✅ | `pnpm install` durch, 136 packages | +| 2 | Auth-Föderation (mana-auth Registrierung, JWT-Verify) | 🚧 blockiert | siehe „Pre-Flight" unten | +| 3 | Domain-Modell + Drizzle + CRUD-API | ✅ | 8 Tabellen, FSRS via ts-fsrs, 46 Tests grün, E2E-Smoke durch | +| 4 | Frontend-Core (SvelteKit, Tailwind 4, Markdown-Editor, Study-View) | ✅ | type-check + build grün, manuell testbar im Browser | +| 5 | Föderations-Endpunkte (share, tools, search, dsgvo) | ✅ | 70 Tests grün, E2E-Smoke (Quote→Inbox→Search→DSGVO-Roundtrip) | +| 6 | Subscriptions/Credits via mana-credits | ⏸ offen | autonom möglich | +| 7 | AI/MCP-Integration | ⏸ offen | braucht laufende mana-mcp | +| 8 | Anki-Import (.apkg-Parser + mana-media-Upload) | ⏸ offen | autonom möglich, ~5–7 Tage | +| 9 | Polish (DSGVO-UI, Settings, Account, Statistik, i18n, A11y) | ⏸ offen | breite Polish-Phase | +| 10 | Production-Deploy (Mac Mini, Cloudflare-Tunnel) | ⏸ offen | braucht DNS + Tunnel-Config | +| 11 | Decommission Cards-Modul aus mana-monorepo | ⏸ offen | erst nach Phase 10 | + +Legende: ✅ erledigt + verifiziert · 🚧 blockiert · ⏸ noch nicht begonnen + +--- + +## Was läuft + +### Lokal voll einsatzbereit + +```bash +cd /Users/till/Documents/Code/cards + +# 1. Dependencies (idempotent) +pnpm install + +# 2. Postgres-Container (auf :5435, kollidiert nicht mit Mana-Plattform-:5432) +pnpm docker:up + +# 3. Drizzle-Schema pushen +cd apps/api +DATABASE_URL='postgresql://cards:cards@localhost:5435/cards' \ + pnpm exec drizzle-kit push --force + +# 4. API starten (auf :3081) +DATABASE_URL='postgresql://cards:cards@localhost:5435/cards' \ + CARDS_API_PORT=3081 \ + CARDS_DSGVO_SERVICE_KEY='msk_test_dsgvo_42' \ + bun run src/index.ts & + +# 5. Web starten (auf :3082) +cd ../web && pnpm dev & + +# 6. Browser öffnen +open http://localhost:3082 +``` + +Login mit beliebigem User-ID-String (Dev-Stub speichert via +`sessionStorage`). Für Föderations-Endpunkte (share/receive) muss die +User-ID UUID-formatiert sein, z.B. `00000000-0000-0000-0000-00000000aaaa`. + +Aufräumen: `kill %1 %2 && pnpm docker:down` (Daten in +`infrastructure/.volumes/cards-postgres`). + +Vollständiger Smoke-Test-Runbook: [`docs/SMOKE_TEST.md`](docs/SMOKE_TEST.md). + +### Verifizierte Endpoints + +``` +GET /healthz → {"status":"ok"} +GET /version → {"app":"cards","version":"…","build":"…"} +GET /.well-known/mana-app.json → Manifest + +POST /api/v1/decks User-JWT CRUD +GET /api/v1/decks User-JWT +GET /api/v1/decks/:id User-JWT +PATCH/DELETE /api/v1/decks/:id User-JWT + +POST /api/v1/cards User-JWT Create + Auto-Reviews +GET /api/v1/cards?deck_id=… User-JWT +GET/PATCH/DELETE /api/v1/cards/:id User-JWT + +GET /api/v1/reviews/due User-JWT Hot-Path +POST /api/v1/reviews/:cardId/:subIndex/grade User-JWT FSRS-Transition + +POST /api/v1/share/receive User-JWT Föderations-Inbox +POST /api/v1/tools/:name User-JWT cards.create | cards.search +GET /api/v1/search?q=… User-JWT SearchResultEnvelope +GET /api/v1/dsgvo/export?user_id=… Service-Key +POST /api/v1/dsgvo/delete Service-Key +``` + +--- + +## Pre-Flight für Phase 2 + Live-Föderation + +Diese Items sind **nicht autonom machbar** — du oder ein Mensch musst +sie freischalten: + +| Item | Wer | Status | +|---|---|---| +| DNS für `cardecky.mana.how` reservieren (Cloudflare) | Mensch | offen | +| GitHub-Repo `mana-ev/cards` anlegen + Remote pushen | Mensch | offen | +| Cards in `mana-auth.apps` registrieren (Service-Key + Public-Key) | Mensch oder KI gegen laufende mana-auth | offen | +| `NPM_AUTH_TOKEN` für Verdaccio in `~/.npmrc` setzen | Mensch | offen | +| Cards-Manifest bei mana-share registrieren | KI gegen laufende mana-share | offen | + +Sobald `NPM_AUTH_TOKEN` da ist, kann der lokale Protocol-Mirror in +`packages/cards-domain/src/protocol/` durch Re-Exports aus +`@mana/shared-share-protocol` ersetzt werden — das ist eine 1-Liner- +Änderung in `cards-domain/src/index.ts` plus Imports. + +--- + +## Wichtige Pointer + +### Konventionen + Stack + +- pnpm 9.15.x, Node 20+, Bun für apps/api +- Tabs-Indent, single-quotes, 100-col Prettier (`.prettierrc.json`) +- SvelteKit 2 + Svelte 5 (**runes-only** — kein legacy `let count = 0`) +- Hono + Bun + Drizzle für API +- Drizzle 0.38 / drizzle-kit 0.30 / zod 3 (gleicher Stand wie Mana-Plattform) +- Tailwind 4 via `@tailwindcss/vite` (oklch-Theme + Dark-Mode-Auto) +- Tests: Vitest + Hono `app.request()`, später Playwright für e2e + +Volle Konventionen: [`CLAUDE.md`](CLAUDE.md) + +### Wichtige Dateien + +| Pfad | Zweck | +|---|---| +| [`STATUS.md`](STATUS.md) | dieses Dokument — Single Source of Truth für Status | +| [`CLAUDE.md`](CLAUDE.md) | Konventionen + Architektur-Invarianten + Stack-Decisions | +| [`README.md`](README.md) | Kurz-Anleitung, ein paar Befehle | +| [`docs/SMOKE_TEST.md`](docs/SMOKE_TEST.md) | Reproduzierbarer E2E-Lauf (curl-Sequenz) | +| [`docs/LESSONS_FROM_MANA_MONOREPO.md`](docs/LESSONS_FROM_MANA_MONOREPO.md) | 15 Architektur-Lessons aus dem Read-Day, 5 Kern-Entscheidungen | +| [`app-manifest.json`](app-manifest.json) | Source of Truth für Föderations-Vertrag (v1.0.0, beta-tier) | +| [`packages/cards-domain/src/`](packages/cards-domain/src/) | zod-Schemas SSOT + FSRS-Adapter + Protocol-Mirror | +| [`apps/api/src/`](apps/api/src/) | Hono-Routen, Drizzle-Schemas, Share-Handlers | +| [`apps/web/src/`](apps/web/src/) | SvelteKit-Routes, $lib/api, $lib/auth-Stub | +| [`infrastructure/docker-compose.yml`](infrastructure/docker-compose.yml) | Postgres-Container für lokal-dev | + +### Cross-Repo-Dokumente (im Plattform-Repo `mana/`) + +| Pfad | Zweck | +|---|---| +| `mana/docs/playbooks/CARDS_GREENFIELD.md` | Master-Playbook (alle Phasen, Pre-Flight, Decommission) | +| `mana/docs/FEDERATION.md` | Föderations-Architektur-Grundlagen | +| `mana/docs/SHARE_PROTOCOL.md` | Manifest- + Envelope-Schema-Spezifikation | +| `mana/docs/MANA_AUTH_FEDERATION.md` | App-Identitäts-Modell (Service-Keys, JWKS) | +| `mana/docs/SHARED_PACKAGES.md` | Versions-Disziplin Klasse A/B/C | +| `mana/docs/PORTS.md` | Port-Allokation (cards-api: **3081**, cards-web: **3082**) | +| `mana/docs/PLAN.md` | Übergreifende mana-e.V.-Roadmap inkl. Phase 6 (Cards-Greenfield) | + +--- + +## Git-Historie + +``` +0328caa Phase 5: Föderations-Endpunkte — Cards ist föderierter Peer +89a7a92 Phase 4: Frontend-Core MVP — Decks, Cards, Study mit FSRS-Loop +e3b3a2b docs: SMOKE_TEST.md — verifizierter E2E-Lauf gegen lokale Postgres +5f67bd9 Phase 3 follow-up: type-check + tests grün, ts-fsrs v5 API +45a47e0 Phase 3: Domain-Modell + Decks/Cards/Reviews-CRUD +8605b1b Phase 0+1: Repo-Skelett für Cards-Greenfield +``` + +`git remote -v` ist leer — Repo lebt lokal, GitHub-Remote folgt mit +Pre-Flight (`mana-ev/cards`). + +--- + +## Architektur-Subtilitäten, die nicht offensichtlich sind + +### 1. Reviews bleiben PLAINTEXT + +Der FSRS-Scheduler quert täglich `due <= now`. Wenn die Reviews +verschlüsselt wären, müsste man jeden Tag N Reviews entschlüsseln nur +um zu wissen welche fällig sind. Geht nicht. + +→ Wenn Encryption nachgerüstet wird: nur `cards.fields` (front/back) +und `decks.{name,description}` werden encrypted, Reviews bleiben +plaintext. Pattern aus mana-monorepo bestätigt (`crypto/registry.ts` +hat `cardReviews` plaintext-allowlisted). + +### 2. SubIndex-Granularität pro Card-Type + +Eine `basic-reverse`-Karte hat **2** Reviews (sub_index 0 = front→back, +sub_index 1 = back→front). Cloze hat 1 Review pro Cluster-Index. +Beim Card-Insert werden alle initialen Reviews in **einer Transaktion** +mit angelegt — siehe `apps/api/src/routes/cards.ts` POST-Handler. + +`subIndexCount(type)` in `@cards/domain` ist die Single Source of Truth. + +### 3. Lokales Protocol-Mirror + +`packages/cards-domain/src/protocol/` enthält eine **TEMPORARY**- +Kopie der Schemas aus `@mana/shared-share-protocol`. Solange Verdaccio +nicht offen ist (kein `NPM_AUTH_TOKEN`), halten wir sie hier lokal. + +→ Drift-Risiko: bei jedem Update der mana-Spec MUSS diese Datei +nachgezogen werden, bis der Swap erfolgt. Marker-Kommentar oben in +jeder Mirror-Datei. + +### 4. Inbox-Deck wird auto-erstellt + +Eingehende Shares (über `/share/receive`) landen alle in einem +auto-erstellten "Inbox"-Deck pro User. `ensureInboxDeck(db, userId)` +prüft auf Existenz oder legt es neu an. User kann Karten später in +echte Decks umsortieren. + +→ Naming-Hinweis: Wenn ein User schon ein Deck namens "Inbox" hat, +greift unser `ensureInboxDeck` darauf zu. Das ist gewollt (idempotent). + +### 5. Dev-Auth via X-User-Id ist EXPLICIT temporär + +`apps/api/src/middleware/auth.ts` und `apps/web/src/lib/auth/dev-stub.svelte.ts` +sind beide klar als „Phase 2 ersetzt durch echtes JWT" markiert. Beim +Swap auf mana-auth: +- API: `@mana/shared-hono` `authMiddleware()` mit JWKS-Cache +- Web: `@mana/shared-auth`-Login-Flow gegen `auth.mana.how` +- Beide aus Verdaccio (= NPM_AUTH_TOKEN-blockiert) + +### 6. Cards-Modul in mana-monorepo wird nach Live-Gang **gelöscht** + +Strategie-B-Konsequenz: nach `cardecky.mana.how` live + 2 Wochen Test +folgt ein Decommission-PR in mana-monorepo, der `apps/mana/.../modules/cards/`, +`packages/cards-core/`, `services/cards-server/` (Marketplace-Backend) +und alle DB-Schemas in `mana_platform.cards.*` entfernt. Keine zwei +Cards-Welten parallel. + +→ Diese Entscheidung ist im Greenfield-Playbook festgehalten und im +älteren `CARDS_CUTOVER.md` (jetzt überholt) als Strategie-A-Variante +diskutiert worden. + +--- + +## Wenn du gerade neu bist — Onboarding-Sequenz + +1. **Lies dieses Dokument zu Ende** (5 Min). +2. **Lies `CLAUDE.md`** (Konventionen + Stack-Decisions, 3 Min). +3. **Lies `mana/docs/playbooks/CARDS_GREENFIELD.md`** (Master-Plan, 10 Min). +4. **Optional:** `docs/LESSONS_FROM_MANA_MONOREPO.md` für Domain-Verständnis. +5. **Verifiziere lokal:** `docs/SMOKE_TEST.md` durchspielen — wenn + alle 7 Schritte grün, ist die Umgebung in Ordnung. +6. **Memory-Check (KI):** falls du auto-memory hast, prüfe + `memory/project_phasenstand.md` für eventuell neueren Stand. + +--- + +## Was als Nächstes ansteht (Vorschläge) + +In Reihenfolge meiner Empfehlung: + +1. **Phase 8 (Anki-Import)** — autonom, klar abgegrenzte Funktionalität, + ~5–7 Tage. Brauchbar als Migrations-Hilfe für künftige User. +2. **Card-Edit-Page + Inbox-Banner im Frontend** — 1–2 Tage Polish. +3. **Pre-Flight aktiv abräumen** — Mana-Plattform-Stack + mana-auth + live, dann Cards als App registrieren (Phase 2). +4. **Phase 6 (Subscriptions)** — braucht laufende mana-credits. +5. **Phase 9 (Polish)** — Settings, Account, DSGVO-UI, Statistik, i18n. + +Was nicht autonom geht: Phase 2 (Auth-Föderation), Phase 7 (mana-mcp- +Live), Phase 10 (Mac-Mini-Deploy) — alle hängen an Pre-Flight-Items. + +--- + +**Wenn du dieses Dokument liest und etwas hier nicht stimmt, ist das +Dokument schuld, nicht der Code. Update es.**