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) <noreply@anthropic.com>
328 lines
14 KiB
Markdown
328 lines
14 KiB
Markdown
# 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.**
|