cards/docs/FEATURE_IDEAS.md
Till JS 9f019d8e2f
Some checks are pending
CI / validate (push) Waiting to run
feat(cards): leech detection in /me/stats + Stats-Page-Sektion
Karten mit ≥4 Lapses werden im Stats-Endpoint als `leech_cards`
geliefert (mit Front-Snippet, Deck-Name, Lapses-Count, sortiert
desc, max 20). Stats-Page zeigt eine rote „Schwierige Karten"-
Sektion mit Link in den Card-Editor.

* apps/api/src/routes/me.ts: GROUP BY card → SUM(lapses) ≥ 4
  Filter, frontSnippetFor()-Helper für alle 6 Card-Types
  (basic, basic-reverse, cloze, image-occlusion, audio-front,
  typing, multiple-choice). Cloze-Markup wird gestrippt damit
  der Snippet UI-tauglich ist.
* apps/web Stats-Page: neue CardSurface mit Warning-Icon, scrollbare
  Liste mit Front + Deck + Lapses-Badge, Empty-State.
* i18n in DE/EN/FR/IT/ES (5 Strings + plural-Form).

104/104 Tests grün (kein neuer Test — bestehende /me/stats-Tests
covern die Aggregations-Form), web check 0 errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 19:01:48 +02:00

19 KiB

Feature Ideas

Stand: 2026-05-12. Korrektur-Pass nach Code-Review gegen den aktuellen Cardecky-Stand. Items, die zwischen Erst-Erstellung (2026-05-11) und heute bereits implementiert wurden, sind unten in „Seit letzter Liste gebaut" geführt, damit der Diff nachvollziehbar bleibt, und aus den Themen-Sektionen entfernt.


🔴 Akute Sicherheitslücke — Live verifiziert 2026-05-12

Dev-Stub-Auth-Bypass ist auf Production aktiv. curl -H "X-User-Id: <beliebig>" https://cardecky-api.mana.how/api/v1/decks liefert 200 OK (ohne Header: 401), und Dev-Stub-User bekommen founder-Tier (auth.ts:90).

Repo-Patch — gebaut 2026-05-12, noch nicht deployed

  1. apps/api/src/middleware/auth.ts:39 auf opt-in geflippt: ALLOW_DEV_STUB = process.env.CARDS_AUTH_DEV_STUB === 'true'. Vergessene env-var ⇒ Bypass AUS.
  2. infrastructure/docker-compose.production.yml: Default flipped auf :-false, plus NODE_ENV: production für cards-api ergänzt (fehlte zuvor — nur cards-web hatte es).
  3. infrastructure/.env.production.example: CARDS_AUTH_DEV_STUB=false als Pflichtzeile mit Kommentar.
  4. apps/api/vitest.config.ts + apps/api/tests/setup.ts angelegt, damit die Test-Suiten (5 Files mit X-User-Id-Auth) den Stub automatisch aktivieren. Verifikation: pnpm test → 16 Files, 100 Tests grün.

Verbleibend bei dir (Mac Mini + Deploy)

  1. git pull / Rebase auf der Prod-Box, dann docker compose -f infrastructure/docker-compose.production.yml \ up -d --build cards-api.
  2. Verifizieren mit curl -i -H "X-User-Id: probe" https://cardecky-api.mana.how/api/v1/decks → erwartet 401 (vorher 200).
  3. Optional: .env.production auf dem Mac Mini um die explizite CARDS_AUTH_DEV_STUB=false-Zeile ergänzen (jetzt nur noch Doku-Konsistenz — der Compose-Default ist schon fail-secure).
  4. Forensik: cards.decks.user_id und cards.cards.user_id gegen mana_auth.users joinen — IDs ohne Match wären Indizien für ausgenutzten Bypass.

Seit letzter Liste gebaut (Stand-Korrektur)

Diese Punkte standen in der 2026-05-11-Fassung noch als Ideen, sind aber im Code längst gelandet:

  • audio-front / typing / multiple-choice — komplette Renderer + Editor-Forms + Integration im Study-Flow (AudioFrontView.svelte, TypingView.svelte, MultipleChoiceView.svelte, Edit-Routen unter cards/[id]/edit/+page.svelte).
  • Keyboard Shortcuts im Study-Mode1/2/3/4 = Rating, Space/Enter = Reveal bzw. good (routes/study/[deckId]/+page.svelte:170-180).
  • Daily Streaks (Backend + Anzeige)me.ts:106-156 berechnet streak_days, Stats-Dashboard zeigt es an. Streak-Freeze, Streak im Header und Streak-Push fehlen noch (siehe Gamification unten).
  • CSV-Import + CSV-Exportlib/csv/{parse,import,export}.ts + CsvImport.svelte.
  • Quizlet-Importlib/quizlet/parse.ts + QuizletImport.svelte.
  • Marketplace-Smart-Merge-Pull-UIupdateAvailable-Banner und pullUpdate(deckId) im Deck-Edit (routes/decks/[id]/edit/+page.svelte:39,113,275).
  • Lizenz-Feld auf Marketplace-Decksmarketplace/decks.ts:73 hat license mit Default Cardecky-Personal-Use-1.0; Constraint bindet Preis an Lizenz-Typ.

Lern-Erlebnis

Scheduler-Verbesserungen

  • FSRS-Parameter pro User optimierents-fsrs liefert computeParameters() aus Review-History; Schema (decks.fsrs_settings) und Per-Deck-Override sind vorbereitet, aber kein Endpoint und kein UI. Größter messbarer Retention-Gewinn pro Aufwand.
  • Leech-Detection — gebaut 2026-05-12. me/stats liefert leech_cards (Threshold 4 Lapses, Limit 20, sortiert nach Lapses desc, mit Front-Snippet + Deck-Name). Stats-Page hat eine rote Leech-Sektion mit Link in den Card-Editor. i18n in DE/EN/ FR/IT/ES. Suspension/Aufteilen-Vorschläge sind nächste Welle.
  • Undo letzte Bewertung — wichtiger UX-Reflex; aktuell muss man das Review händisch zurückbauen.
  • Card Burial / Suspension — Karten temporär deaktivieren ohne Löschen; häufig angefragtes Anki-Feature.
  • Geschwister-Burial — Cloze-Cluster und basic-reverse-Seiten nicht am selben Tag wiederholen.
  • Custom Study Sessions — gefilterte Sitzungen: nur neue Karten, nur Fehler der letzten Woche, nach Tag filtern. Tag-Filter im reviews-Endpoint fehlt aktuell ganz.
  • Session-Mix-Algorithmus — heute wird due ≤ now linear abgearbeitet; Optionen „Neue Karten zuerst", „durchmischt", „nur Fehler" als saubere Filter im Queue-Loader.
  • Subdeck-Unterstützung — hierarchische Deck-Struktur (z. B. Vokabeln → Nomen / Verben).
  • Cloze-Hint-Anzeige{{c1::answer::hint}} wird aktuell beim Rendern fallen gelassen; Hint-Anzeige steht in STATUS.md als verbleibender Phase-9-Punkt.

Kartentypen, Schema vorhanden / vorbereitet

  • Bild-Front / Bild-Back — als generischer Type mit media_ref in fields. MinIO-Storage + media_files-Pfad ist seit Sprint 9k da, also rein UI-Frage.
  • Speech-In (Audio-Antwort vom User) — Whisper via mana-stt ist im Plattform-Stack vorhanden; Schema müsste expected + Audio- Vergleichs-Strategie definieren.

Gamification & Motivation

  • Streak-Freeze-Token — ein Streak-Schutztag pro Woche, optional durch Credits kaufbar.
  • Streak im Header — heute nur im Stats-Dashboard sichtbar; Header-Glyph mit Zahl wäre Mikro-Aufwand, sichtbare Wirkung.
  • XP + Badges — Meilensteine (erstes Deck, 100 Karten, 30-Tage- Streak).
  • Tages-Ziele — „Heute: 20 Karten" mit Progress-Bar im Dashboard.
  • Push/Email-Reminders — „Du hast heute noch 15 fällige Karten" via mana-notify. Aktuell wird mana-notify von Cards noch nicht benutzt (siehe marketplace/pull-requests.ts:41 — „Notify-Calls ausgelassen — eigene Welle").
  • Estimated Mastery Date — „Dieses Deck beherrschst du voraussichtlich in 3 Wochen" (aus FSRS-Stability ableitbar).

KI-Features

  • Auto-Cloze-Generator — Text markieren → {{c1::...}} automatisch einfügen.
  • Card-Split-Vorschlag — KI erkennt informationsreiche Karten und schlägt Aufteilung vor.
  • Erklär-Modus — Nach falscher Antwort: KI erklärt den Zusammenhang (opt-in).
  • Auto-Tagging — Karten beim Erstellen / Importieren semantisch taggen.
  • Duplicate Detection — semantische Ähnlichkeit über Decks hinweg erkennen.
  • Card Quality Score — Hinweis „Diese Karte hat zu viel Text" + Verbesserungsvorschlag.

Analytics & Insights

  • Vergessenskurven-Visualisierung — pro Deck und Tag, aus FSRS-State ableitbar.
  • Retention-Rate — aufgeschlüsselt nach Kategorie und Sprache.
  • Lernzeit-Tracking — Minuten pro Session, Wochentrend.
  • Karten-Schwierigkeits-Heatmap — welche Karten kosten die meiste Review-Zeit.
  • Wöchentliche Zusammenfassung — In-App oder per Email via mana-notify.
  • Algorithmus-Transparenz pro Karte — kleines „Wieso wurde ich befragt?"-Tooltip mit Stability/Difficulty/letztem Rating. Macht FSRS sichtbar (Mission-Wert „Souveränität").

Import / Export

  • PDF-Export — druckbare Karteikarten (A6-Format, vorder-/rückseitig). Print-Route existiert schon (decks/[id]/print/), aber als HTML-Print, nicht generiert-PDF.
  • .apkg-Export — Round-Trip mit dem Anki-Parser (Parser ist schon da). Stärkste „du kannst jederzeit weggehen"-Geste, Mission-Argument.
  • Web Clipper (Browser-Extension) — markierter Text → sofort neue Karte; eigenes Projekt, eigener Scope.
  • SuperMemo XML — für Power-User aus dem SM-Ecosystem.
  • FSRS-State-Export — Lernstand als JSON exportieren für Backup und Migration (über DSGVO-Export hinaus, in interop-tauglichem Format).

Zusammenarbeit & Community

  • Study Spaces — gemeinsame Decks für Schulklassen und Lerngruppen (braucht Gruppen-Konzept in mana-auth, das noch nicht existiert; decks.visibility = 'space' ist im Schema vorgesehen, aber ohne Spaces-Backend wirkungslos).
  • Deck-Ratings & Kommentare — Qualitätssicherung im Marketplace durch Community. marketplace/discussions.ts-Schema existiert, UI-Anbindung müsste geprüft werden.
  • Study Challenges — mit Freunden auf demselben Deck messen.
  • Kreator-Analytics — für Marketplace-Publisher: Views, Forks, Abonnenten-Retention.
  • Collaborative Decks — Team-Editing mit Rollen (Maintainer / Contributor).

Marketplace (eigener Themenblock)

Marketplace-Schema + Pull-Update-UI sind da, aber drumherum klafft einiges:

  • Pull-Request-UI — Backend (marketplace/pull-requests.ts) und API-Client (lib/api/marketplace.ts) existieren; eine Surface, wo ein Fork-Maintainer Änderungen an das Original vorschlagen kann, fehlt im Web.
  • Versions-Diff zwischen Fork und Upstream — Voraussetzung, damit Smart-Merge-Pull nicht „trust me"-Klick ist.
  • Moderations-Queue-Surfacemarketplace/moderation.ts im Schema vorhanden; Report-Funktion auf Decks/Karten im UI fehlt.
  • Lizenz-Auswahl bei Publishdecks.license hat Default Cardecky-Personal-Use-1.0; Standard-Lizenzen (CC-BY-SA, CC-0, CC-BY-NC) als Optionen im Publish-Flow.
  • Lizenz-Anzeige beim Studieren geforkter Decks — wer das Deck veröffentlicht hat, welche Lizenz greift; transparent unten in der Study-View.

UX / Plattform

  • PWA Offline-Read-Only als Zwischenschritt zur vollen PWA: Service-Worker cached die letzte Queue, Bewertungen werden gepuffert. Bricht die Server-authoritative-Invariante nicht (Puffer ist transient).
  • Dark Mode / Theme-Switcherdata-theme="forest" ist in app.html fest verdrahtet; In-App-Auswahl fehlt. Bei der laufenden 12-Token-Theming-Strategie ohnehin geplant.
  • Bulk-Operationen — mehrere Karten auswählen, verschieben, taggen, löschen.
  • Dynamic Decks / Smart Playlists — automatisch gefiltert: z. B. „alle Karten mit Tag 'Grammatik' aus 3 Decks".
  • Card History — Lernverlauf pro Karte: wann wie bewertet.

A11y (konkret aufs UI bezogen)

  • prefers-reduced-motion respektieren in DeckFan.svelte und DeckStack.svelte (animationslastige Komponenten).
  • Screen-Reader-Live-Region im Study-View für das Reveal-Event („Antwort eingeblendet").
  • Touch-Targets ≥ 44 px für die Rating-Buttons; Mobile-Review ist der Haupt-Use-Case und Code zeigt keine explizite Min-Höhe.
  • Fokus-Reihenfolge nach Reveal — Cursor sollte auf den Rating- Buttons landen, nicht am Body-Anfang.
  • Tastatur-Hilfe-Overlay im Study-Mode (?-Taste): zeigt die bestehenden Shortcuts an. Heute sind sie undokumentiert in der UI.

Security (eigener Block, weil Live-App)

  • Dev-Stub-Default ist opt-out, nicht opt-in — siehe akute Sicherheitslücke oben. Fix-Anweisung dort, dieser Eintrag bleibt als Erinnerung, dass die Middleware-Logik in auth.ts:39 auf opt-in geflippt gehört (=== 'true' statt !== 'false').
  • JWT in localStoragedev-stub.svelte.ts:13 speichert das Access-Token unter cards.auth.accessToken. Anfällig für XSS- Token-Diebstahl. Mitigation: DOMPurify sanitisiert Markdown (gut), aber HttpOnly-Cookie wäre der robustere Ansatz, sobald der SSO- Cookie-Pfad ohnehin schon da ist.
  • SVG-Upload erlaubtmedia.ts:13 whitelistet image/- Prefix; image/svg+xml ist im Extension-Map (media.ts:28). SVG kann eingebettetes JavaScript enthalten. Wenn SVG-Medien per <img> gerendert werden, ist es harmlos; bei <object>/<iframe> oder direkter Einbettung gibt es ein XSS-Loch. Mindestens Content-Security-Policy img-src strikt halten, oder SVG-Upload separat sanitisieren (svgo + Script-Strip).
  • Security-Headers — gebaut 2026-05-12, noch nicht deployed.
    • API: secureHeaders() aus Hono vor cors() eingehängt (apps/api/src/index.ts:3,30).
    • Web: apps/web/src/hooks.server.ts neu, setzt X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin, plus HSTS (180 Tage, includeSubDomains) wenn NODE_ENV=production.
    • CSP läuft Report-Only seit 2026-05-12. hooks.server.ts setzt Content-Security-Policy-Report-Only mit restriktiver Policy (default-src 'self', script-src 'self', img-src 'self' data: blob:, connect-src whitelist auf cardecky-api/auth.mana.how/share/mcp). Violations laufen in die Browser-DevTools-Console. Nach 1-2 Tagen Live-Beobachtung via CARDS_CSP_ENFORCE=true auf Enforce flippen. SVG-Upload- Risiko ist dann gemildert (script-src verbietet inline-JS in SVG-Inlinen).
  • CORS lässt localhost in Prod durch — gebaut 2026-05-12, noch nicht deployed. Live-Probe hatte Access-Control-Allow-Origin: http://localhost:9999 gezeigt. Patch: localhost-Regex in apps/api/src/index.ts per NODE_ENV === 'production'-Check disabled.
  • DSGVO-Delete-Honesty — gebaut 2026-05-12, noch nicht deployed. Sowohl /api/v1/dsgvo/delete (Service-Key) als auch /api/v1/me/delete (Self-Service) liefern jetzt storage_ok: true|false
    • optional storage_error im Response. Frontend zeigt im Account-Delete-Flow eine Fehler-Toast wenn storage_ok=false.
  • Service-Key-Rotation-Playbook — gebaut 2026-05-12. docs/playbooks/SERVICE_KEY_ROTATION.md mit 5-Schritt-Rotation, Tabus, Phase-F-1-Übergang. Bis die mana-auth-managed Variante fertig ist, ist das die SoT.

Operations & Resilienz

Bisher gar nicht in dieser Liste behandelt, aber Cards ist live:

  • Rate-Limiting — gebaut 2026-05-12, noch nicht deployed. In-Memory Sliding-Window-Middleware in apps/api/src/middleware/rate-limit.ts, dependency-frei. Aktive Limits: share.receive 60/min/IP, media.upload 30/min/user, decks.generate 10/min/user, decks.from-image 10/min/user, dsgvo.* 10/min/IP. Verifiziert in tests/rate-limit.test.ts (4 Tests). Bei Multi-Container-Setup später auf Redis oder Cloudflare-Rate-Limit umsteigen.
  • Audit-Log für DSGVO-Aktionen — gebaut 2026-05-12, noch nicht deployed. apps/api/src/lib/audit.ts schreibt strukturierte JSON-Zeilen auf stdout (kind: 'audit'). Aktiv auf /dsgvo/export, /dsgvo/delete, /me/export, /me/delete. Persistierung in eine audit_log-Tabelle ist additiv möglich, ohne Aufrufer anzupassen.
  • Health-Endpoint inkl. DB+MinIO-Probe — gebaut 2026-05-12, noch nicht deployed. /healthz bleibt simpel (Liveness), neu ist /healthz/details mit echtem SELECT 1 und MinIO-bucketExists- Probe, Status 200 bei beidem grün, sonst 503 mit Latency+Error je Probe.
  • Strukturiertes Logging + Request-IDs end-to-end — vermutlich via @mana/shared-hono vorhanden, aber im Frontend-Fehler-Toast bisher keine sichtbare Correlation-ID.
  • Backup-Strategie für cards-DB + MinIO-Bucket — hängt am globalen Backup-Cron-TODO, ist aber jetzt mit Prod-Daten dringend.
  • content_hash-Backfill-Skript — Pre-Sprint-9j-Karten haben content_hash = NULL, Re-Import-Dedupe ist auf diese Karten blind (siehe STATUS.md Subtilität #10).

Mission-Fit (Vereins-Werte aufs Produkt anwenden)

  • Anti-Lock-In als Feature, nicht als Disclaimer.apkg- Export plus FSRS-State-Export plus eine sichtbare „Daten mitnehmen"-Surface in account/. Heute ist es als DSGVO-Export versteckt.
  • Telemetrie-Audit dokumentieren — verifiziert: 0 Trackerapps/web/src/ enthält weder PostHog noch Plausible/Matomo/Umami, kein gtag, keine track()-Aufrufe. Das ist ein hartes Mission-Argument, das man heute kostenlos hergibt. Vorschlag: kleine Surface in account/ oder im Footer („Cards trackt dich nicht — Code-Beweis"), die auf den git grep-Befund im Repo verlinkt.
  • Tier-frei-Modus erhalten — Schema und Plumbing für mana-credits sind da (credits-client.ts, requireTier), aber MVP ist tier-frei. Bei jeder neuen Feature-Idee bewusst markieren, ob sie tier-pflichtig wird oder nicht; default frei.

DevX & Code-Hygiene

  • Playwright-E2E in CISTATUS.md spricht von „lokalem E2E-Smoke"; ein playwright.config.ts existiert im Repo nicht. Bei Live-App mit Marketplace-Forks die schmerzhafteste Regressions-Quelle.
  • Drizzle-Migrationen versionieren — Repo-Setup gebaut 2026-05-12, Prod-Bootstrap offen. 0000_baseline.sql (355 Zeilen, 25 Tabellen
    • 5 Enums) festgefrostet. apps/api/scripts/bootstrap-drizzle-tracking.ts
    • pnpm drizzle:bootstrap-tracking markiert die Baseline auf der Live-DB als „bereits angewandt", ohne SQL re-auszuführen. Lokal verifiziert: idempotent + drizzle:migrate erkennt die Migration danach als bekannt. Playbook in docs/playbooks/DRIZZLE_MIGRATIONS_BOOTSTRAP.md. Nächster Schritt beim User: einmalig auf der Prod-Box ausführen, danach ist push --force Tabu.
  • Storybook / Histoire für lib/components/ — 20+ Svelte- Komponenten ohne isolierte Vorschau; Lost-Pixel-Theming-Strategie würde davon doppelt profitieren.
  • CLAUDE.md ↔ Realität synchen — CLAUDE.md erwähnt deploy.yml neben ci.yml, im .github/workflows/-Ordner liegt aber nur ci.yml. Entweder Workflow nachziehen oder Doku korrigieren.

Offene Punkte

  • Was nach diesem Hardening-Sprint übrig bleibt:
    1. CSP setzen — eigener Mini-Sprint mit Browser-Test, weil SVG-XSS-Risiko erst mit CSP wirklich gemildert ist.
    2. Drizzle-Migrationen versionieren — bevor das erste Prod- Schema-Diff weh tut. Heute läuft alles via db:push.
    3. FSRS-Parameter-Optimierung pro User — größter messbarer Lern-Effekt, Schema schon vorbereitet.
    4. Service-Key-Rotation-Playbook in docs/playbooks/ anlegen, bis die mana-auth.app_service_keys-Anbindung (Phase F-1) fertig ist.
    5. JWT-Storage von localStorage → HttpOnly-Cookie — XSS-Token-Diebstahl-Härtung. Größerer Umbau weil der SSO-Callback-Flow das mitziehen muss.
  • PWA-Offline-Read-Only ist der Architektur-Trade-off mit dem größten Risiko: der saubere server-authoritative Pfad würde um einen Sync-Puffer erweitert. Lieber spät als verfrüht.
  • Study Spaces bleibt blockiert durch fehlendes Gruppen-Konzept in mana-auth; Schema-Slot visibility = 'space' ist vorbereitet, aber ohne Backend wirkungslos.
  • Marketplace-Reifegrad transparent in STATUS.md führen — aktuell taucht „Marketplace" in STATUS.md nur in der „Strategie- B-Ausnahme"-Notiz auf; ob Marketplace im UI freigeschaltet ist oder hinter einer Feature-Flag steckt, ist von außen nicht ablesbar.
  • Notify-Anbindung ist offenmana-notify wird von Cards noch nicht aufgerufen; sobald Streak/Weekly/PR-Notifies reinkommen sollen, ist das ein eigener Sprint.