R0 (Doku): - Archiv unter docs/marketplace/archive/ aus managarten-Tag cards-decommission-base: MARKETPLACE_PLAN (654 Z., Vollvision mit mana-credits-Flow, Anti-Patterns), COMPETITORS, GUIDELINES, cards-server_CLAUDE. - docs/playbooks/MARKETPLACE_RESTORE.md mit Schema-Naming-Entscheidung (eigenes marketplace-pgSchema), Wellen R0-R6, Cardecky-Skill- Integration, Lizenz-Modell. - CLAUDE.md Invariante 2: Strategie-B gilt nur für Study-/FSRS-/Sync- Schicht; Marketplace-Restore ist explizite Ausnahme. - STATUS.md: Phase 12 R0+R1 durch. R1 (Schema): - 16 Tabellen + 5 Enums im neuen marketplace-pgSchema (authors, decks, deck_versions, deck_cards, tag_definitions, deck_tags, deck_stars, deck_subscriptions, deck_forks, deck_pull_requests, card_discussions, deck_reports, ai_moderation_log, deck_purchases, author_payouts, author_follows). - drizzle.config.ts: schemaFilter ['cards', 'marketplace']. - Greenfield cards-pgSchema unangetastet. - DB-CHECK decks_price_requires_license verifiziert (paid Deck mit CC-BY wirft sauber ab). - type-check + 56 API-Tests grün, drizzle-kit push idempotent. Decks dormant (kein Code-Pfad ruft die Tabellen). R2 (Backend α/β: Author-Profile + Publish + AI-Mod) als nächstes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 KiB
Cardecky — Projekt-Leitlinien
Verbindliche Regeln für den Spinoff. Ziel: in wenigen Wochen ein ausspielbares Web-MVP, das ausschließlich seinen Core Gameloop beherrscht und alles andere von zentralen Mana-Bausteinen erbt.
Status: Planungsphase, noch kein Code.
Name: Cardecky.
App-Domain: cardecky.mana.how (Subdomain unter *.mana.how, SSO über mana-auth).
Marketing-Landing: cardecky.com (eigene Domain, statisch, SEO/Akquise — keine Auth, leitet auf cardecky.mana.how für die App).
Zugang: offen für jeden eingeloggten Mana-User (requiredTier: 'public', kein Beta-Gate).
1. Mission in einem Satz
Die schönste, einfachste Karteikarten-App mit Spaced Repetition — zuerst nur Web, später Mobile, KI-Generierung als Phase 2.
2. Game-Dev-Prinzip: zuerst nur der Core Gameloop
Wie bei einem Spielprototyp gilt: alles, was nicht zum Loop gehört, wird zurückgestellt. Erst wenn der Loop sich gut anfühlt und Nutzer ihn freiwillig wiederholen, wird gebaut, was drumherum gehört.
Der Core Gameloop von Cardecky
Start
│
▼
"Du hast N Karten heute fällig" ─────► (wenn 0: "Alles gelernt — komm später wieder")
│
▼
[Lernen starten]
│
▼
Vorderseite zeigen ──► User denkt ──► Tap/Space ──► Rückseite zeigen
│
▼
Selbst-Bewertung: 1=nochmal · 2=schwer · 3=gut · 4=leicht
│
▼
FSRS rechnet next-due ──► nächste Karte (oder Session-Ende)
│
▼
Session-Ende: "X Karten gelernt, nächste in Y Stunden"
│
└─► zurück zum Start
Sekundäre Loops (Karten erstellen, Decks verwalten) werden gebaut, sind aber UI-arm. Tertiäre Loops (KI-Generierung, Voice, Sharing) sind Phase 2 und werden in Phase 1 nicht angefasst.
Was Phase 1 enthält
- Decks anlegen / löschen / umbenennen
- Karten manuell erstellen (Markdown-Inhalt)
- Kartentypen: Basic, Basic + Reverse, Cloze, Type-In (siehe §6)
- Lernsession mit FSRS v6, inklusive per-User-Parameter-Tuning
- "Heute fällig"-Übersicht + Streak-Zähler
- Tags auf Decks (das Modul hat sie ohnehin schon, raus wäre Mehrarbeit)
- PWA-installierbar, offline-fähig
- Auth via mana-auth, Sync via mana-sync
Was Phase 1 absichtlich NICHT enthält
- KI-Generierung von Karten (kein PDF-Upload, keine Bild→Karte)
- Voice/TTS-Lernen
- Anki-Import / Export
- Statistik-Dashboards (nur Streak + Tagessumme)
- Public Decks / Marktplatz / Sharing
- Stripe / Bezahlung
- Mobile-App (PWA-tauglich aber kein Expo)
- Eigene Domain & Marketing-Landing
- Mehrsprachigkeit über Deutsch hinaus
- Bilder / Audio in Karten
- Image-Occlusion-Karten, Audio-Karten, Multiple-Choice
- Custom Card-Templates / WYSIWYG-Editor
- Erweiterte Suche
Jede dieser Features ist legitim — aber nur, wenn der Loop steht.
3. Goldene Regeln
- Simpel schlägt vollständig. Wenn ein Feature nicht zum Core Gameloop gehört, kommt es in einen Phase-2-Backlog, nicht in den Code.
- Open Source only. Jede Library, jedes Tool, jeder Dienst muss eine OSI-konforme Lizenz haben (MIT, Apache 2.0, BSD, MPL, AGPL akzeptabel). Keine Closed-Source-SDKs, keine proprietären APIs als Pflichtabhängigkeit.
- Bevorzugt was im Verein schon läuft. Neue Technologie nur einführen, wenn ein konkreter Engpass es verlangt und kein vorhandenes Tool es löst.
- Zentrale Mana-Dienste statt Eigenbau. Auth, Sync, Analytics, Notifications, Media usw. werden NICHT neu gebaut — siehe §5.
- Local-First wie der Rest des Verein-Stacks. IndexedDB als Quelle der Wahrheit, Sync nach Postgres im Hintergrund.
cardecky.mana.howals Subdomain unter*.mana.how. Kein eigenes Auth-System, kein eigenes Hosting-Setup — Eintrag inPRODUCTION_TRUSTED_ORIGINS+ Cloudflare-Tunnel-Route reichen.- Eine UI-Schicht, ein Theme. Wir verwenden
@mana/shared-theme(-ui)und@mana/shared-uiso weit es geht — kein paralleles Design-System. - Erweiterbare Daten, simples UI. Das Datenmodell denkt zukünftige Kartentypen mit (siehe §6), das UI zeigt in Phase 1 nur die vier definierten Typen.
4. Tech-Stack (Phase 1)
Alles bereits im Verein verwendet, alles OSI-Open-Source.
Frontend
| Schicht | Wahl | Lizenz |
|---|---|---|
| Framework | SvelteKit 2 | MIT |
| UI-Sprache | Svelte 5 (Runes) | MIT |
| Sprache | TypeScript 5 | Apache-2.0 |
| Styling | Tailwind CSS 4 | MIT |
| Build/Dev | Vite | MIT |
| PWA | @vite-pwa/sveltekit (über @mana/shared-pwa) |
MIT |
| Icons | über @mana/shared-icons |
MIT |
| Markdown-Render | marked + DOMPurify |
MIT |
Datenhaltung (Client)
| Schicht | Wahl | Lizenz |
|---|---|---|
| Local Store | IndexedDB via Dexie | Apache-2.0 |
| Local-Store-Wrapper | @mana/local-store (intern) |
— |
| Verschlüsselung | AES-GCM-256 via @mana/shared-crypto (Phase 2 — Hooks bereits an allen Schreib-/Lese-Pfaden, Wirkung deferred bis Vault-Server-Roundtrip steht; siehe src/lib/data/crypto.ts) |
— |
Spaced Repetition
| Schicht | Wahl | Lizenz |
|---|---|---|
| Algorithmus | FSRS v6 (Free Spaced Repetition Scheduler) | BSD-3 |
| TS-Implementation | ts-fsrs (offizielle Portierung, mit Optimizer) |
MIT |
| Per-User-Tuning | ts-fsrs-Optimizer, läuft client-seitig nach ≥ 50 Reviews |
MIT |
Deployment
| Schicht | Wahl | Lizenz |
|---|---|---|
| Adapter | @sveltejs/adapter-node |
MIT |
| Container | Docker, hinter Cloudflare Tunnel | Apache-2.0 |
| Host | Mac mini (siehe docker-compose.macmini.yml) |
— |
Tooling
| Schicht | Wahl | Lizenz |
|---|---|---|
| Paket-Manager | pnpm 9 | MIT |
| Monorepo-Orchestrierung | Turborepo (vorhanden) | MPL-2.0 |
| Linting | ESLint (@mana/eslint-config) |
MIT |
| Formatierung | Prettier | MIT |
| Tests (Unit) | Vitest | MIT |
| Tests (E2E) | Playwright | Apache-2.0 |
| TS-Config | @mana/test-config, @mana/shared-vite-config |
— |
Backend in Phase 1: keiner
Phase 1 braucht keinen eigenen Service. Lese-/Schreibpfad geht
ausschließlich über IndexedDB → mana-sync (existiert) → Postgres.
Erst wenn KI-Generierung (Phase 2) dazukommt, entsteht
services/cards-server (Hono + Bun, analog zu allen anderen
Verein-Services).
5. Zentrale Mana-Bausteine (Pflicht in Phase 1)
Services (laufen bereits, nur konsumieren)
| Service | Port | Wofür in Cardecky |
|---|---|---|
mana-auth |
3001 | SSO, JWT, Sessions, Tier-Claims. Cardecky-Origin in PRODUCTION_TRUSTED_ORIGINS eintragen. |
mana-sync |
3050 | Sync der cards-AppId-Daten (Decks, Karten, Reviews, StudyBlocks). |
mana-user |
3062 | Profilinfos / Settings. |
mana-analytics |
3064 | Page-Views, Loop-Events (siehe §11). |
mana-events |
3115 | Domain-Events für Streak-Logik. |
mana-notify |
3040 | "Du hast X Karten fällig"-Push (Phase 1.5). |
mana-credits |
3061 | Erst Phase 2 (KI-Generierung). |
mana-subscriptions |
3063 | Erst Phase 2 (Pro-Tier). |
mana-llm, mana-stt, mana-tts |
– | Erst Phase 2. |
mana-media |
3015 | Erst wenn Bilder in Karten erlaubt sind. |
Workspace-Pakete (@mana/*)
| Paket | Wofür in Cardecky |
|---|---|
@mana/shared-auth |
Client-seitiger Auth-Hook (SSO-Flow, JWT-Handling). |
@mana/shared-auth-ui |
Login/Logout-Komponenten. |
@mana/shared-hono |
(sobald cards-server existiert) Auth-/Health-/Error-Middleware. |
@mana/shared-branding |
App-Registry-Eintrag (Tier=public, Branding, Subdomain). |
@mana/shared-types |
Geteilte TS-Typen. |
@mana/shared-utils |
Utility-Funktionen. |
@mana/shared-ui |
UI-Komponenten. |
@mana/shared-theme, @mana/shared-theme-ui |
Theme-Tokens, Dark/Light. |
@mana/shared-tailwind |
Tailwind-Preset. |
@mana/shared-i18n |
Übersetzungsfundament (Phase 1: nur DE registriert). |
@mana/shared-icons |
Icon-Set. |
@mana/shared-privacy |
Visibility-Enum für Decks (Sharing erst Phase 2, aber Feld vorbereitet). |
@mana/shared-crypto |
AES-GCM-256 für sensible Felder. |
@mana/shared-pwa |
Manifest, Service-Worker, Install-Prompt. |
@mana/shared-vite-config |
Vite-Defaults. |
@mana/shared-error-tracking |
Error-Reporting. |
@mana/shared-logger |
Strukturiertes Logging (Server-Seite, sobald relevant). |
@mana/shared-stores |
Geteilte Local-Store-Helpers. |
@mana/shared-tags |
Tags auf Decks. |
@mana/local-store |
Dexie-Setup, Sync-Hooks. |
@mana/eslint-config |
Lint-Regeln. |
@mana/test-config |
Vitest-Defaults. |
@mana/feedback |
In-App-Feedback-Widget. |
@mana/help |
Hilfe-Overlay. |
Erst Phase 2 oder später: @mana/shared-llm, @mana/shared-ai,
@mana/local-llm, @mana/local-stt, @mana/credits, @mana/qr-export,
@mana/wallpaper-generator, @mana/website-blocks,
@mana/shared-research, @mana/shared-uload, @mana/shared-storage.
Datenpfad
Cardecky übernimmt 1:1 das Mana-Datenpfad-Pattern:
User-Aktion → Store → encryptRecord → Dexie → Hooks (_pendingChanges)
→ mana-sync → Postgres (mana_platform.cards.*) → andere Clients
appId = cards. Tabellen: cardDecks, cards, cardReviews,
cardStudyBlocks, deckTags.
6. Datenmodell — erweiterbar gedacht
Heutiges Modul kennt nur front/back. Damit weitere Kartentypen
ohne Schema-Bruch dazukommen, wechseln wir auf ein Felder-Map +
Typ-Diskriminator:
type CardType =
| 'basic' // Phase 1: front/back
| 'basic-reverse' // Phase 1: erzeugt zwei Lernrichtungen aus einer Karte
| 'cloze' // Phase 1: Lückentext, eine Subkarte pro Cluster
| 'type-in' // Phase 1: User tippt Antwort, exact-match-Vergleich
| 'image-occlusion' // Phase 2
| 'audio' // Phase 2
| 'multiple-choice' // ggf. Phase 2
interface LocalCard extends BaseRecord {
deckId: string
type: CardType
fields: Record<string, string> // basic: { front, back } · cloze: { text, extra? }
// FSRS-State liegt nicht hier, sondern in cardReviews (1:N pro Subkarte)
order: number
}
interface LocalCardReview extends BaseRecord {
cardId: string
subIndex: number // basic-reverse → 0|1, cloze → c1, c2, …
stability: number // FSRS
difficulty: number // FSRS
due: string // ISO
reps: number
lapses: number
state: 'new' | 'learning' | 'review' | 'relearning'
lastReview?: string
}
interface LocalCardStudyBlock extends BaseRecord {
date: string // YYYY-MM-DD
cardsReviewed: number
durationMs: number
}
Cloze-Syntax: Anki-kompatibel: {{c1::Wort}}, {{c1::Wort::Hinweis}}.
Eine Cloze-Karte mit Cluster c1+c2 erzeugt 2 Reviews
(subIndex 1, subIndex 2).
Markdown: marked + DOMPurify rendern Front/Back. Cloze-Tags
werden vor dem Markdown-Parser zu HTML-Spans umgewandelt, damit sie im
Render erhalten bleiben.
Migration aus dem Bestand: existierende front/back-Karten werden
beim ersten Schema-Upgrade auf type='basic' mit
fields={front, back} migriert. Alte Spalten bleiben für eine
Übergangsversion lesbar (siehe docs/DATABASE_MIGRATIONS.md).
7. Daten-Contract mit dem mana-Modul
Wichtig: das bestehende cards-Modul in der Mana-Web-App bleibt
erhalten. Cardecky und das mana-Modul schreiben in dieselben
Postgres-Tabellen.
Daher gilt:
- Schema-Änderungen werden gemeinsam im mana-Modul und im Cardecky-Code rolled out (nie nur auf einer Seite).
- Encryption-Registry-Einträge müssen in beiden Frontends identisch sein (Field-Allowlist).
- Migrationen über
docs/DATABASE_MIGRATIONS.md.
Reihenfolge: Phase 0 (mana-Modul um neue Tabellen + Kartentyp-Felder
- FSRS erweitern) wird vor dem Standalone-Build durchgezogen. So gibt es nie zwei Wahrheiten zur Datenstruktur.
8. Definition of Done für Phase 1
Phase 1 ist fertig, wenn:
- Ein eingeloggter Mana-User kann auf
cardecky.mana.how- mindestens ein Deck anlegen,
- Karten manuell hinzufügen (Basic, Basic+Reverse, Cloze, Type-In),
- Markdown im Front/Back nutzen (Bold, Listen, Code, Links),
- eine Lernsession starten und mit FSRS-Bewertung durchspielen,
- die App schließen und am nächsten Tag die richtigen fälligen Karten wiederfinden.
- FSRS-Per-User-Tuning läuft automatisch nach ≥ 50 Reviews und überschreibt die Default-Parameter.
- Die App ist als PWA installierbar und offline-bedienbar (Karten lernen ohne Netz).
- Auth läuft komplett über mana-auth (kein Eigen-Login).
- Daten landen in Postgres und sind im bestehenden mana-Modul sichtbar (gleiche Datenquelle, kein Drift).
pnpm validate:allgrün.- Mindestens drei Smoke-E2E-Tests (Playwright):
- „Login → Deck anlegen → Basic-Karte → Lernsession → bewerten"
- „Cloze-Karte mit zwei Clustern → erzeugt zwei Subkarten"
- „Type-In: korrekte Antwort = grün, falsche = rot"
- Container baut & läuft auf dem Mac mini hinter Cloudflare Tunnel (
cardecky.mana.how).
Alles andere ist Phase 2.
9. Repo-Struktur (Phase 1)
apps/cards/
├── apps/
│ └── web/ # SvelteKit-App, einziges Surface in Phase 1
│ ├── src/
│ │ ├── lib/
│ │ │ ├── data/ # Dexie + Sync-Anbindung
│ │ │ ├── fsrs/ # ts-fsrs-Wrapper + Optimizer-Hook
│ │ │ ├── cards/ # Kartentyp-Renderer (basic, cloze, type-in)
│ │ │ ├── stores/ # Decks, Cards, Reviews, StudyBlocks
│ │ │ └── ui/ # Komponenten (DeckList, CardEditor, Session)
│ │ └── routes/
│ │ ├── +layout.svelte
│ │ ├── +page.svelte # Heute fällig + Decks
│ │ ├── decks/[id]/+page.svelte # Deck-Detail + Karten
│ │ └── learn/[deckId]/+page.svelte # Lernsession
│ ├── package.json
│ ├── svelte.config.js
│ └── vite.config.ts
├── GUIDELINES.md # ← dieses Dokument
└── README.md
apps/cards/apps/mobile/ und apps/cards/apps/landing/ sind erst
Phase 2/3.
10. PR-Checkliste
Bei jedem Pull-Request gefragt:
- Gehört die Änderung zum Core Gameloop?
- Wenn nein: rechtfertigt sie sich aus einer Pflicht (Auth, Sync, Build)?
- Wird ein bestehendes
@mana/*Paket genutzt statt neu zu bauen? - Ist jede neue Dependency Open-Source und im Verein bereits in Verwendung?
- Sind Datenmodell-Änderungen mit dem mana-Modul konsistent?
- Bricht die Änderung das Versprechen "Erweiterbare Daten, simples UI"?
11. Analytics-Events (Mindestumfang Phase 1)
Über mana-analytics:
cards_session_started—{ deckId, dueCount }cards_card_rated—{ cardId, type, grade (1–4), elapsedMs }cards_session_completed—{ deckId, cardCount, durationMs }cards_deck_created—{ deckId }cards_card_created—{ deckId, type }cards_fsrs_optimized—{ reviewCount, paramsHash }cards_pwa_installed— Standard-PWA-Event
Reicht für die Core-Loop-Validierung. Mehr Events erst, wenn eine konkrete Frage entsteht, die Daten beantworten sollen.
12. Hinweis im mana-Modul
Sobald cardecky.mana.how live ist, bekommt das mana-Modul einen
dezenten Hinweis (z.B. ein Banner oder Badge über der ListView):
"Cardecky gibt es jetzt auch als eigenständige App". Kein Pop-up, kein
forcierter Redirect — User entscheiden selbst.