devlog: 6 Tage geschrieben (Greenfield → Hardening + Cardecky-Native-Vorbereitung)
Some checks failed
CI / validate (push) Has been cancelled

Tag 1: Phase 0–10c Marathon, Live-Cut auf cardecky.mana.how.
Tag 2: Marketplace-Restore (Phase 12 R0–R5 + G1–G4).
Tag 3: Karten-Typ-Vollausbau (Periodensystem, audio, typing,
multiple-choice, Vision-LLM-Deck-Generation).
Tag 4: Mobile-Nav + 5 Sprachen + CSV/PDF + Astro-Landing.
Tag 5: Security-Hardening (fail-secure, CSP, DSGVO-Audit,
rate-limit) + Leech-Detection + AASA.
Tag 6: Recovery + Undo + FSRS-Slider + Streak + Stats-Charts +
Blog + Marketplace-Report + Privacy/Help.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-05-15 22:23:29 +02:00
parent ff00c7d961
commit ac05afa93e
18 changed files with 2376 additions and 0 deletions

513
devlog/2026-05-08/data.json Normal file
View file

@ -0,0 +1,513 @@
{
"date": "2026-05-08",
"day_number": 1,
"weekday": "Freitag",
"commits": 45,
"authors": [
{
"name": "Till JS",
"count": 38
},
{
"name": "Till",
"count": 7
}
],
"additions": 15371,
"deletions": 1122,
"net_lines": 14249,
"files_changed": 134,
"new_files": 0,
"deleted_files": 0,
"session": {
"first_commit_at": "2026-05-08T12:08:41.000Z",
"last_commit_at": "2026-05-08T20:10:52.000Z",
"total_span_minutes": 482,
"active_minutes": 244,
"pauses": [
{
"from": "14:41",
"to": "16:43",
"minutes": 122
},
{
"from": "18:52",
"to": "20:09",
"minutes": 77
},
{
"from": "21:11",
"to": "21:50",
"minutes": 39
}
],
"longest_focus_minutes": 130
},
"top_dirs": [
{
"path": "apps/web/src/lib",
"pct": 20
},
{
"path": "apps/web/src/routes",
"pct": 14
},
{
"path": "packages/cards-domain/src",
"pct": 12
},
{
"path": "apps/api/src/routes",
"pct": 8
},
{
"path": "apps/api/src/db",
"pct": 4
}
],
"top_extensions": [
{
"ext": ".ts",
"count": 159
},
{
"ext": ".svelte",
"count": 59
},
{
"ext": ".json",
"count": 23
},
{
"ext": ".md",
"count": 14
},
{
"ext": ".yml",
"count": 10
},
{
"ext": ".yaml",
"count": 7
}
],
"tags": [
"prod-compose",
"dockerfiles",
"api Dockerfile",
"prod",
"auth"
],
"commits_list": [
{
"hash": "8605b1b",
"short": "Phase 0+1: Repo-Skelett für Cards-Greenfield",
"type": null,
"scope": null,
"additions": 1197,
"deletions": 0,
"timestamp": "2026-05-08T14:08:41+02:00"
},
{
"hash": "45a47e0",
"short": "Phase 3: Domain-Modell + Decks/Cards/Reviews-CRUD",
"type": null,
"scope": null,
"additions": 1897,
"deletions": 106,
"timestamp": "2026-05-08T14:21:54+02:00"
},
{
"hash": "5f67bd9",
"short": "Phase 3 follow-up: type-check + tests grün, ts-fsrs v5 API",
"type": null,
"scope": null,
"additions": 2345,
"deletions": 35,
"timestamp": "2026-05-08T14:41:04+02:00"
},
{
"hash": "e3b3a2b",
"short": "docs: SMOKE_TEST.md — verifizierter E2E-Lauf gegen lokale Postgres",
"type": null,
"scope": null,
"additions": 111,
"deletions": 0,
"timestamp": "2026-05-08T16:43:07+02:00"
},
{
"hash": "89a7a92",
"short": "Phase 4: Frontend-Core MVP — Decks, Cards, Study mit FSRS-Loop",
"type": null,
"scope": null,
"additions": 1582,
"deletions": 58,
"timestamp": "2026-05-08T16:52:31+02:00"
},
{
"hash": "0328caa",
"short": "Phase 5: Föderations-Endpunkte — Cards ist föderierter Peer",
"type": null,
"scope": null,
"additions": 1371,
"deletions": 0,
"timestamp": "2026-05-08T17:10:35+02:00"
},
{
"hash": "2bed282",
"short": "docs: STATUS.md als Single Source of Truth für Cards-Onboarding",
"type": null,
"scope": null,
"additions": 336,
"deletions": 0,
"timestamp": "2026-05-08T17:18:16+02:00"
},
{
"hash": "553a78d",
"short": "Phase 8a: Cloze als MVP-Card-Type, Cluster-Counter",
"type": null,
"scope": null,
"additions": 248,
"deletions": 13,
"timestamp": "2026-05-08T17:35:39+02:00"
},
{
"hash": "0b609c4",
"short": "Phase 8b: Cloze-Render im Study-View",
"type": null,
"scope": null,
"additions": 18,
"deletions": 1,
"timestamp": "2026-05-08T17:37:53+02:00"
},
{
"hash": "2ca09fe",
"short": "Phase 8c: Anki-Import via portiertem Parser",
"type": null,
"scope": null,
"additions": 916,
"deletions": 3,
"timestamp": "2026-05-08T17:43:12+02:00"
},
{
"hash": "9da10b3",
"short": "Phase 8d: STATUS.md auf Phase-8-Stand aktualisiert",
"type": null,
"scope": null,
"additions": 78,
"deletions": 20,
"timestamp": "2026-05-08T17:46:33+02:00"
},
{
"hash": "0a40367",
"short": "Phase 9a: Card-Edit-Page für alle 3 CardTypes",
"type": null,
"scope": null,
"additions": 247,
"deletions": 4,
"timestamp": "2026-05-08T17:51:42+02:00"
},
{
"hash": "35366ed",
"short": "Phase 9b: Cloze-Editor in /cards/new",
"type": null,
"scope": null,
"additions": 113,
"deletions": 38,
"timestamp": "2026-05-08T17:52:55+02:00"
},
{
"hash": "47419b3",
"short": "Phase 9c: Inbox-Banner auf /decks und /study",
"type": null,
"scope": null,
"additions": 63,
"deletions": 0,
"timestamp": "2026-05-08T17:54:19+02:00"
},
{
"hash": "aff4d95",
"short": "Phase 9d: Pre-Flight — Protocol-Mirror durch upstream ersetzt",
"type": null,
"scope": null,
"additions": 83,
"deletions": 175,
"timestamp": "2026-05-08T18:00:56+02:00"
},
{
"hash": "03117d5",
"short": "Phase 9e: Account-Page mit DSGVO-Self-Service",
"type": null,
"scope": null,
"additions": 316,
"deletions": 52,
"timestamp": "2026-05-08T18:03:41+02:00"
},
{
"hash": "6db6dc3",
"short": "Phase 9f: Statistik-Dashboard",
"type": null,
"scope": null,
"additions": 238,
"deletions": 2,
"timestamp": "2026-05-08T18:06:13+02:00"
},
{
"hash": "a640594",
"short": "docs: STATUS.md auf Phase-9-Polish-Stand",
"type": null,
"scope": null,
"additions": 60,
"deletions": 26,
"timestamp": "2026-05-08T18:07:53+02:00"
},
{
"hash": "c25c1d0",
"short": "Phase 9g: i18n DE/EN über alle Routes",
"type": null,
"scope": null,
"additions": 826,
"deletions": 270,
"timestamp": "2026-05-08T18:22:00+02:00"
},
{
"hash": "fd86d96",
"short": "Phase 9h: A11y-Pass",
"type": null,
"scope": null,
"additions": 87,
"deletions": 7,
"timestamp": "2026-05-08T18:24:33+02:00"
},
{
"hash": "4b451f1",
"short": "Phase 9i: Cloze-Hint-Anzeige",
"type": null,
"scope": null,
"additions": 60,
"deletions": 12,
"timestamp": "2026-05-08T18:26:00+02:00"
},
{
"hash": "593d447",
"short": "Phase 9j: Anki-Re-Import-Dedupe via content_hash",
"type": null,
"scope": null,
"additions": 176,
"deletions": 8,
"timestamp": "2026-05-08T18:29:56+02:00"
},
{
"hash": "e7ae93d",
"short": "docs: STATUS.md auf Phase-9-Welle-2-Stand",
"type": null,
"scope": null,
"additions": 42,
"deletions": 15,
"timestamp": "2026-05-08T18:31:57+02:00"
},
{
"hash": "c9eb0a6",
"short": "Phase 9k: Media-Upload via MinIO-Container",
"type": null,
"scope": null,
"additions": 886,
"deletions": 78,
"timestamp": "2026-05-08T18:42:56+02:00"
},
{
"hash": "39b1791",
"short": "Phase 9l: Image-Occlusion als 4. MVP-CardType",
"type": null,
"scope": null,
"additions": 681,
"deletions": 33,
"timestamp": "2026-05-08T18:50:45+02:00"
},
{
"hash": "04c48ed",
"short": "docs: STATUS.md auf Phase-9-Welle-3-Stand",
"type": null,
"scope": null,
"additions": 59,
"deletions": 21,
"timestamp": "2026-05-08T18:52:49+02:00"
},
{
"hash": "045903b",
"short": "Phase 10a: Production-Deploy-Stack (Mac Mini)",
"type": null,
"scope": null,
"additions": 203,
"deletions": 0,
"timestamp": "2026-05-08T20:09:19+02:00"
},
{
"hash": "a993cc2",
"short": "fix(prod-compose): cards-api needs NPM_AUTH_TOKEN build-arg",
"type": "fix",
"scope": "prod-compose",
"additions": 2,
"deletions": 0,
"timestamp": "2026-05-08T20:10:39+02:00"
},
{
"hash": "cfdc582",
"short": "fix(dockerfiles): copy tsconfig.base.json into build context",
"type": "fix",
"scope": "dockerfiles",
"additions": 2,
"deletions": 2,
"timestamp": "2026-05-08T20:12:01+02:00"
},
{
"hash": "464aee1",
"short": "fix(prod-compose): cards-minio port 9110→9210 (cadvisor belegt 9110)",
"type": "fix",
"scope": "prod-compose",
"additions": 4,
"deletions": 4,
"timestamp": "2026-05-08T20:13:06+02:00"
},
{
"hash": "78a6c8f",
"short": "fix(prod-compose): cards-api port 3091→3191 (3091 belegt)",
"type": "fix",
"scope": "prod-compose",
"additions": 2,
"deletions": 2,
"timestamp": "2026-05-08T20:13:46+02:00"
},
{
"hash": "f9d7a16",
"short": "fix(api Dockerfile): COPY app-manifest.json (runtime-import)",
"type": "fix",
"scope": "api Dockerfile",
"additions": 4,
"deletions": 0,
"timestamp": "2026-05-08T20:14:20+02:00"
},
{
"hash": "0dff798",
"short": "fix(prod): public URLs auf cardecky.* (war cards.* — bookmarks via nginx-301)",
"type": "fix",
"scope": "prod",
"additions": 7,
"deletions": 6,
"timestamp": "2026-05-08T20:18:10+02:00"
},
{
"hash": "6ea96dd",
"short": "docs: Phase 10 LIVE — cardecky.mana.how + cardecky-api.mana.how",
"type": null,
"scope": null,
"additions": 8,
"deletions": 2,
"timestamp": "2026-05-08T20:19:59+02:00"
},
{
"hash": "506aec3",
"short": "Phase 2a: Cards-API JWT-Verify (additiv zum Dev-Stub)",
"type": null,
"scope": null,
"additions": 73,
"deletions": 11,
"timestamp": "2026-05-08T20:41:09+02:00"
},
{
"hash": "76d4e92",
"short": "Phase 6: Tier-Awareness + mana-credits-Client (Plumbing)",
"type": null,
"scope": null,
"additions": 147,
"deletions": 10,
"timestamp": "2026-05-08T20:45:08+02:00"
},
{
"hash": "5b6d096",
"short": "fix(prod-compose): pass MANA_AUTH_URL/MANA_CREDITS_URL/SERVICE_KEY/DEV_STUB into cards-api container",
"type": "fix",
"scope": "prod-compose",
"additions": 4,
"deletions": 0,
"timestamp": "2026-05-08T20:45:55+02:00"
},
{
"hash": "d7c7c97",
"short": "Phase 7a: cards.create-Tool für Cloze + Image-Occlusion + content_hash",
"type": null,
"scope": null,
"additions": 36,
"deletions": 4,
"timestamp": "2026-05-08T20:48:39+02:00"
},
{
"hash": "a960d09",
"short": "docs: STATUS auf Phase 2/6/7/11 ✅",
"type": null,
"scope": null,
"additions": 4,
"deletions": 4,
"timestamp": "2026-05-08T20:49:43+02:00"
},
{
"hash": "7119756",
"short": "Phase 10c: Cards-Web SSO-Login gegen mana-auth",
"type": null,
"scope": null,
"additions": 236,
"deletions": 55,
"timestamp": "2026-05-08T21:08:06+02:00"
},
{
"hash": "f1622e9",
"short": "docs: Phase 7 LIVE — mana-share + mana-mcp deployed",
"type": null,
"scope": null,
"additions": 1,
"deletions": 1,
"timestamp": "2026-05-08T21:11:31+02:00"
},
{
"hash": "1b840a9",
"short": "Phase 10d: Token-Refresh + 401-Retry im Cards-Web",
"type": null,
"scope": null,
"additions": 118,
"deletions": 15,
"timestamp": "2026-05-08T21:50:12+02:00"
},
{
"hash": "3b74583",
"short": "fix(auth): two-step Login (Better-Auth-native + /refresh) für SSO-Cookie",
"type": "fix",
"scope": "auth",
"additions": 30,
"deletions": 15,
"timestamp": "2026-05-08T21:52:46+02:00"
},
{
"hash": "87a7a31",
"short": "fix(web): SvelteKit-env via \\$env/dynamic/public statt import.meta.env",
"type": "fix",
"scope": "web",
"additions": 17,
"deletions": 8,
"timestamp": "2026-05-08T22:03:35+02:00"
},
{
"hash": "f11df63",
"short": "Phase 9m: KI-Deck-Generation via mana-llm",
"type": null,
"scope": null,
"additions": 437,
"deletions": 6,
"timestamp": "2026-05-08T22:10:52+02:00"
}
],
"review_state": "auto",
"llm": {
"model": null,
"generated_at": null
}
}

122
devlog/2026-05-08/macher.md Normal file
View file

@ -0,0 +1,122 @@
---
date: 2026-05-08
day: 1
view: macher
weekday: Freitag
commits: 45
review: written
---
# Freitag, 2026-05-08 — Tag 1 (Macher-Sicht)
Greenfield (Strategie B) zu Live-App in einer Marathon-Session.
Phasen 0/1 → 3 → 4 → 5 → 8 → 9 → 10 → 2a/6/7 → 10c/d → 9m in einer
Schicht. 45 Commits, eines der dichtesten Tage des Verein-Aufbaus.
## Stats
45 Commits, +15 371 / 1 122 LoC, 134 Files. Top-Dirs: web/lib
(20 %), web/routes (14 %), cards-domain (12 %), api/routes (8 %),
api/db (4 %). Session 12:08 → 20:10, 244 aktive Minuten,
längster Fokus 130 Min.
## Blöcke
### Phase 0/1: Skelett
- Repo-Skelett, pnpm + Bun + Drizzle, `pgSchema('cards')`.
### Phase 3: Domain-Modell
- **Decks/Cards/Reviews-CRUD** + ts-fsrs v5-API.
- **SMOKE_TEST.md** als verifizierter E2E-Lauf.
### Phase 4: Frontend-Core
- SvelteKit MVP — Decks, Cards, Study mit FSRS-Loop.
### Phase 5: Föderation
- Cards-API als föderierter Peer im Mana-Ökosystem.
### Phase 8: Cloze
- **8a — Cloze als MVP-Card-Type** + Cluster-Counter (für Sub-
Indices).
- **8b — Cloze-Render im Study-View.**
- **8c — Anki-Import** via portiertem Parser.
### Phase 9: Polish
- **9a — Card-Edit-Page** für alle 3 CardTypes.
- **9b — Cloze-Editor** in `/cards/new`.
- **9c — Inbox-Banner** auf /decks und /study.
- **9d — Protocol-Mirror durch upstream ersetzt** (Pre-Flight).
- **9e — Account-Page** mit DSGVO-Self-Service.
- **9f — Statistik-Dashboard.**
- **9g — i18n DE/EN** über alle Routes.
- **9h — A11y-Pass.**
- **9i — Cloze-Hint-Anzeige.**
- **9j — Anki-Re-Import-Dedupe** via content_hash.
- **9k — Media-Upload** via MinIO-Container.
- **9l — Image-Occlusion** als 4. MVP-CardType.
- **9m — KI-Deck-Generation** via mana-llm.
### Phase 10: Production
- **10a — Production-Deploy-Stack** (Mac Mini).
- Compose-Fix-Pass: NPM_AUTH_TOKEN-build-arg, tsconfig.base.json-
copy, MinIO-Port 9110→9210 (cadvisor belegt), API-Port 3091→3191
(3091 belegt), app-manifest.json-COPY (runtime-import), public
URLs auf `cardecky.*` (war `cards.*` — bookmarks via nginx-301).
- **Phase 10 LIVE — cardecky.mana.how + cardecky-api.mana.how.**
### Phase 2a/6/7
- **2a — Cards-API JWT-Verify** (additiv zum Dev-Stub).
- **6 — Tier-Awareness + mana-credits-Client** (Plumbing).
- Env-vars in cards-api-Container (MANA_AUTH_URL,
MANA_CREDITS_URL, SERVICE_KEY, DEV_STUB).
- **7a — `cards.create`-Tool** für Cloze + Image-Occlusion +
content_hash.
### Phase 7 / Phase 10c/d
- **Phase 7 LIVE — mana-share + mana-mcp deployed.**
- **10c — Cards-Web SSO-Login** gegen mana-auth.
- **10d — Token-Refresh + 401-Retry** im Cards-Web.
- **two-step Login** (Better-Auth-native + /refresh) für SSO-
Cookie.
- **SvelteKit-env via `$env/dynamic/public`** statt
`import.meta.env`.
## Architektur-Entscheidungen
- **Strategie B (eigene App)**, nicht Subprojekt. Plan in
`mana/docs/playbooks/CARDS_GREENFIELD.md`.
- **Server-authoritative MVP.** Keine Dexie, keine IndexedDB,
keine eigene Sync-Engine. Frontend = HTTP-Client zu cards-api.
Local-First später via mana-sync.
- **FSRS am Server.** Grading-Calls gehen immer an
`POST /api/v1/reviews/:cardId/:subIndex/grade`. Algorithmen-
Updates wirken sofort, kein Client-Code-Update.
- **Eigene DB `cards`** im geteilten Postgres-Cluster, Schema-
Isolation via `pgSchema('cards')`.
- **Föderation über `@mana/shared-app-tpl`.** Pflicht-Endpoints
(manifest, healthz, share/receive, tools, search, dsgvo) kommen
aus dem geteilten Boilerplate.
- **JWT-Validation lokal** via JWKS-Cache (5 min), kein Live-
Call.
- **Domain `cardecky.mana.how`** statt `cards.mana.how` — kürzer,
weniger generisch.
- **Two-Step-Login** (Better-Auth-native + /refresh) für SSO-
Cookie. Better-Auth setzt erst nach /refresh die richtigen
Cookies.
## Trade-offs
- **45 Commits in 244 Min** ist sehr viel; jeder Commit eine Phase.
- **Greenfield, kein Code-Reuse aus mana-monorepo** für Study/
FSRS/Sync (sauber neu). Ausnahme: Marketplace-Restore (kommt
Tag 2).
- **+14 249 LoC netto** ist Greenfield-Pattern.
- **Live am Tag 1** ist mutig; Compliance-Audit kam erst
hinterher.
## Offene Punkte
- **Marketplace-Restore** (`docs/playbooks/MARKETPLACE_RESTORE.md`)
— pending für Tag 2.
- **Lost-Pixel-Snapshots** für UI fehlen.
- **Endurance-Test** der Live-App über Tage steht aus.
- **mana-credits-Verbrauch sichtbar machen** in der UI.

View file

@ -0,0 +1,40 @@
---
date: 2026-05-08
day: 1
view: spieler
weekday: Freitag
commits: 45
review: written
---
# Freitag, 2026-05-08 — Tag 1
Cards geht live. Eine eigene Lern-Karten-App des Vereins —
**Spaced Repetition, Anki-Import, KI-generierte Decks**, alles
unter **cardecky.mana.how**. Aus „Modul im alten großen Mana-Repo"
wird heute ein eigenständiges Werkzeug.
## Was du heute schon kannst
- **Anmelden** mit deinem Verein-Account.
- **Decks anlegen + Karten erstellen** — Vorder-/Rückseite,
Cloze (Lückentext), Image-Occlusion (Bild mit Maske).
- **Lernen mit FSRS** — der moderne Lern-Algorithmus, der dir
Karten genau dann zeigt, wenn du sie wahrscheinlich vergisst.
- **Anki-Import** — wenn du schon Anki-Decks hast, lädst du sie
hoch und sie sind drin.
- **KI-Deck-Generierung** — beschreibe ein Thema, die App schlägt
Karten vor.
- **Bilder + Audio** auf Karten.
- **DSGVO-Self-Service** — Daten herunterladen, Konto löschen.
- **Statistik-Dashboard** über deinen Lern-Stand.
- **DE/EN-Sprachwechsel** über alle Seiten.
- **Barrierefreiheit** — Tastatur-Bedienung, Screenreader-tauglich.
## Hintergrund
Cards war Teil eines größeren Mana-Repos und schwer zu pflegen.
Heute lebt es eigenständig auf `cardecky.mana.how` (URL bewusst
„Cardecky" — kürzer, einprägsamer als „Cards"), mit eigener
Datenbank, eigener Adresse und vereinfachter Wartung. Die App ist
**werbefrei und ohne Tracker** — du zahlst nichts, dafür beobachtet
dich niemand.

253
devlog/2026-05-09/data.json Normal file
View file

@ -0,0 +1,253 @@
{
"date": "2026-05-09",
"day_number": 2,
"weekday": "Samstag",
"commits": 16,
"authors": [
{
"name": "Till JS",
"count": 16
}
],
"additions": 17104,
"deletions": 1154,
"net_lines": 15950,
"files_changed": 145,
"new_files": 0,
"deleted_files": 0,
"session": {
"first_commit_at": "2026-05-09T10:38:51.000Z",
"last_commit_at": "2026-05-09T18:24:47.000Z",
"total_span_minutes": 466,
"active_minutes": 123,
"pauses": [
{
"from": "12:38",
"to": "15:05",
"minutes": 147
},
{
"from": "16:14",
"to": "17:16",
"minutes": 62
},
{
"from": "17:16",
"to": "18:01",
"minutes": 45
},
{
"from": "18:49",
"to": "20:17",
"minutes": 89
}
],
"longest_focus_minutes": 69
},
"top_dirs": [
{
"path": "docs/marketplace/archive",
"pct": 17
},
{
"path": "apps/web/src/lib",
"pct": 17
},
{
"path": "apps/web/src/routes",
"pct": 15
},
{
"path": "docs/marketplace/seed",
"pct": 13
},
{
"path": "apps/api/src/routes",
"pct": 8
}
],
"top_extensions": [
{
"ext": ".ts",
"count": 80
},
{
"ext": ".svelte",
"count": 55
},
{
"ext": ".md",
"count": 34
},
{
"ext": ".json",
"count": 7
},
{
"ext": ".yaml",
"count": 4
},
{
"ext": ".jsonl",
"count": 3
}
],
"tags": [
"decks",
"marketplace",
"theming",
"status",
"deps"
],
"commits_list": [
{
"hash": "e596199",
"short": "dev: dev:full + cards-dev-Alias + lokale mana-auth-Pipeline",
"type": null,
"scope": null,
"additions": 463,
"deletions": 5,
"timestamp": "2026-05-09T12:38:51+02:00"
},
{
"hash": "9a7068d",
"short": "Phase 12 R0+R1: Marketplace-Restore-Plan + Schema in marketplace-pgSchema",
"type": null,
"scope": null,
"additions": 2404,
"deletions": 4,
"timestamp": "2026-05-09T15:05:22+02:00"
},
{
"hash": "7dbbf63",
"short": "Phase 12 R2: Marketplace-Backend α + β — Authors + Deck-Init + Publish",
"type": null,
"scope": null,
"additions": 4004,
"deletions": 1,
"timestamp": "2026-05-09T15:13:58+02:00"
},
{
"hash": "d45f1c0",
"short": "Phase 12 R3: Marketplace γ + δ — Discovery + Engagement + Subscribe + Smart-Merge",
"type": null,
"scope": null,
"additions": 1170,
"deletions": 11,
"timestamp": "2026-05-09T15:27:39+02:00"
},
{
"hash": "92a1d58",
"short": "Phase 12 R4: Marketplace ε — Pull-Requests + Card-Discussions",
"type": null,
"scope": null,
"additions": 638,
"deletions": 5,
"timestamp": "2026-05-09T15:50:16+02:00"
},
{
"hash": "4086171",
"short": "Phase 12 R5: Marketplace-Frontend — /explore + /d + /u + /me/{published,subscribed,forks}",
"type": null,
"scope": null,
"additions": 2310,
"deletions": 1,
"timestamp": "2026-05-09T16:04:40+02:00"
},
{
"hash": "17871ba",
"short": "Phase 12 G1-G4: Marketplace-Polish — svelte-ignore + Skeleton/Empty-State + Server-Filter + Owner-Info",
"type": null,
"scope": null,
"additions": 174,
"deletions": 63,
"timestamp": "2026-05-09T16:14:21+02:00"
},
{
"hash": "404ddec",
"short": "docs(marketplace): CONTENT_PLAN — bilingualer CH/DE-Lehrplan-Korridor + 20 Phase-1-Seed-Decks",
"type": "docs",
"scope": "marketplace",
"additions": 327,
"deletions": 0,
"timestamp": "2026-05-09T17:16:22+02:00"
},
{
"hash": "19a0036",
"short": "feat(theming): forest variant from @mana/themes (sprint 9m)",
"type": "feat",
"scope": "theming",
"additions": 323,
"deletions": 261,
"timestamp": "2026-05-09T18:01:37+02:00"
},
{
"hash": "870e2ae",
"short": "feat(decks): card-stack visualization + direct-launch study mode",
"type": "feat",
"scope": "decks",
"additions": 1283,
"deletions": 180,
"timestamp": "2026-05-09T18:02:04+02:00"
},
{
"hash": "9626200",
"short": "chore: seed-test-decks browser-console snippet",
"type": null,
"scope": null,
"additions": 246,
"deletions": 0,
"timestamp": "2026-05-09T18:02:15+02:00"
},
{
"hash": "e4cf124",
"short": "docs(status): Cardecky-Skill auf Marketplace + 2 Decks live",
"type": "docs",
"scope": "status",
"additions": 1,
"deletions": 1,
"timestamp": "2026-05-09T18:16:23+02:00"
},
{
"hash": "d7f3b93",
"short": "feat(deps): migrate Header from @mana/shared-ui@0.1.x to shared-ui-2",
"type": "feat",
"scope": "deps",
"additions": 27,
"deletions": 222,
"timestamp": "2026-05-09T18:27:24+02:00"
},
{
"hash": "9a07454",
"short": "seed: 3 Cardecky-Decks v1.0.0 + Audit-Trail im Repo",
"type": null,
"scope": null,
"additions": 2753,
"deletions": 1,
"timestamp": "2026-05-09T18:49:05+02:00"
},
{
"hash": "5876f95",
"short": "refactor(web): vereinfachte Navigation und inline Deck-Erstellung",
"type": "refactor",
"scope": "web",
"additions": 730,
"deletions": 388,
"timestamp": "2026-05-09T20:17:58+02:00"
},
{
"hash": "7bf6131",
"short": "feat(decks): Deck-Kategorien über den ganzen Stack",
"type": "feat",
"scope": "decks",
"additions": 251,
"deletions": 11,
"timestamp": "2026-05-09T20:24:47+02:00"
}
],
"review_state": "auto",
"llm": {
"model": null,
"generated_at": null
}
}

View file

@ -0,0 +1,83 @@
---
date: 2026-05-09
day: 2
view: macher
weekday: Samstag
commits: 16
review: written
---
# Samstag, 2026-05-09 — Tag 2 (Macher-Sicht)
Marketplace-Restore-Sprint. Phase 12 R0R5 + G1G4. Code aus dem
`cards-decommission-base`-Tag im managarten-Repo additiv re-importiert
in eigenes `marketplace`-pgSchema, additiv zur Study-Welt aus Tag 1.
## Stats
16 Commits, +17 104 / 1 154 LoC, 145 Files. Top-Dirs:
`docs/marketplace/archive` (17 %), `apps/web/src/lib` (17 %),
`apps/web/src/routes` (15 %), `docs/marketplace/seed` (13 %),
`apps/api/src/routes` (8 %). Session 10:38 → 18:24, 123 aktive
Minuten, längster Fokus 69 Min.
## Schritte
- **dev:full + cards-dev-Alias** + lokale mana-auth-Pipeline.
- **R0+R1 — Marketplace-Restore-Plan + Schema** in
`marketplace`-pgSchema. Schema-Isolation gegen `cards`-Schema.
- **R2 — Backend α + β** — Authors-Tabelle + Deck-Init + Publish.
- **R3 — γ + δ** — Discovery + Engagement + Subscribe + Smart-
Merge (wenn du eine Card schon eigenständig bearbeitet hast,
bekommst du beim Pull eine Merge-Frage).
- **R4 — ε** — Pull-Requests + Card-Discussions.
- **R5 — Frontend**`/explore`, `/d/<deck-slug>`, `/u/<user-
slug>`, `/me/published`, `/me/subscribed`, `/me/forks`.
- **G1G4 — Polish** — svelte-ignore-Sweep + Skeleton/Empty-State
+ Server-Filter + Owner-Info.
- **CONTENT_PLAN** — bilingualer CH/DE-Lehrplan-Korridor + 20
Phase-1-Seed-Decks dokumentiert.
- **Theming: `forest` variant from `@mana/themes`** (sprint 9m).
- **Decks: card-stack visualization** + direct-launch study mode.
- **Browser-Console-Snippet** zum Seeden von Test-Decks.
- **Migration `@mana/shared-ui` → `shared-ui-2`** im Header.
- **Seed: 3 Cardecky-Decks v1.0.0** + Audit-Trail im Repo.
- **Web-Refactor: vereinfachte Navigation + inline Deck-
Erstellung.**
- **Deck-Kategorien** über den ganzen Stack (DB + API + UI).
## Architektur-Entscheidungen
- **Marketplace in eigenem pgSchema (`marketplace`)** statt
Erweiterung von `cards`. Trennung: Study-State ist privat,
Marketplace-State ist öffentlich.
- **Authors-Tabelle separat** von User-Tabelle. Author hat
Pseudonym, Avatar, Bio; User-Account ist mana-auth.
- **Pull-Request-Modell wie Git**: Fork = lokale Kopie eines
Decks, PR = Vorschlag zurück. Card-Discussions hängen am
Original-Card.
- **Smart-Merge bei Subscribe-Pull**: Wenn lokal eigene Edits da
sind, Conflict-Dialog. Beibehalten / Übernehmen / Manuell-
Merge.
- **Forest-Theme** aus `@mana/themes`, nicht inline. Wie nutriphi
+ zitare an Tag 2.
- **shared-ui-2 für Header** — strikte 12-Token-Disziplin,
PillTabGroup.
## Trade-offs
- **Marketplace-Restore aus alten Code-Stand** ist Verbot in
Strategie B — bewusste Ausnahme, dokumentiert in CLAUDE.md.
Marketplace war nie Auf der Strategie-B-Liste, wurde nur
mit-rausgerissen. Restore additiv, nicht ersetzend.
- **+15 950 LoC netto** für Marketplace + Theming + Refactor.
- **Eigenes `authors`-Pseudonym-Modell** statt mana-auth-Profile
→ doppelte Identitäts-Pflege. Akzeptiert, weil Pseudonym pro App
Sinn macht.
## Offene Punkte
- **20 Phase-1-Seed-Decks** — heute 3 live, 17 fehlen.
- **Bilingualer CH/DE-Lehrplan-Korridor** Content-Plan, Umsetzung
manuell.
- **PR-Conflict-Surface** für Card-Discussion-Threading.
- **Marketplace-Suche** noch nicht — heute nur Filter.

View file

@ -0,0 +1,39 @@
---
date: 2026-05-09
day: 2
view: spieler
weekday: Samstag
commits: 16
review: written
---
# Samstag, 2026-05-09 — Tag 2
Cardecky bekommt einen **Marktplatz**. Du kannst Decks von anderen
Mitgliedern abonnieren, deine eigenen veröffentlichen, andere
deine Verbesserungs-Vorschläge machen lassen — ein Mini-GitHub für
Lern-Karten.
## Was sich für dich ändert
- **Explore-Seite** mit veröffentlichten Decks zum Stöbern.
- **Decks abonnieren** — du bekommst Updates, wenn der Author
etwas ändert.
- **Eigene Decks veröffentlichen** unter deinem Pseudonym oder
echtem Namen.
- **Pull-Requests** — du kannst Verbesserungen an einem Deck
vorschlagen, der Author entscheidet, ob er sie übernimmt.
- **Card-Discussions** — Diskussion pro Karte (ist das richtig?
Quelle?).
- **Smart-Merge** beim Abonnieren — wenn du schon eigene
Bearbeitungen einer Karte hast, fragt die App, was Vorrang hat.
- **Drei Cardecky-Decks live** als Demo-Inhalt unter dem
Cardecky-Plattform-User.
- **Forest-Theme** im Vereins-Stil.
- **Karten-Stapel-Visualisierung** auf den Deck-Karten —
du siehst auf einen Blick, dass es ein Stapel ist.
## Hintergrund
Lern-Karten teilen ist alt, aber meistens unfair: Anki-Shared-Decks
gehen über einen privaten Server. Cardecky-Marktplatz ist offen,
werbefrei und vom Verein getragen.

324
devlog/2026-05-10/data.json Normal file
View file

@ -0,0 +1,324 @@
{
"date": "2026-05-10",
"day_number": 3,
"weekday": "Sonntag",
"commits": 26,
"authors": [
{
"name": "Till JS",
"count": 26
}
],
"additions": 7019,
"deletions": 1919,
"net_lines": 5100,
"files_changed": 59,
"new_files": 0,
"deleted_files": 0,
"session": {
"first_commit_at": "2026-05-10T12:38:48.000Z",
"last_commit_at": "2026-05-10T15:07:24.000Z",
"total_span_minutes": 149,
"active_minutes": 118,
"pauses": [
{
"from": "14:47",
"to": "15:18",
"minutes": 31
}
],
"longest_focus_minutes": 109
},
"top_dirs": [
{
"path": "apps/web/src/routes",
"pct": 39
},
{
"path": "apps/web/src/lib",
"pct": 34
},
{
"path": "apps/api/src/routes",
"pct": 11
},
{
"path": "packages/cards-domain/src",
"pct": 7
},
{
"path": "apps/api/src/lib",
"pct": 3
}
],
"top_extensions": [
{
"ext": ".svelte",
"count": 72
},
{
"ext": ".ts",
"count": 41
},
{
"ext": ".py",
"count": 1
},
{
"ext": ".jsonl",
"count": 1
},
{
"ext": ".md",
"count": 1
}
],
"tags": [
"cards",
"web",
"api",
"study",
"cards/new"
],
"commits_list": [
{
"hash": "e2b493d",
"short": "feat(study): Periodensystem-Karten mit Kategorie-Farben und Eigenschaften-Tabelle",
"type": "feat",
"scope": "study",
"additions": 338,
"deletions": 246,
"timestamp": "2026-05-10T14:38:48+02:00"
},
{
"hash": "d9532ed",
"short": "fix(study): Hint-Text entfernt, kein vertikales Scrollen",
"type": "fix",
"scope": "study",
"additions": 2,
"deletions": 10,
"timestamp": "2026-05-10T14:40:17+02:00"
},
{
"hash": "598acb4",
"short": "fix(study): kein Layout-Sprung beim Wechsel Reveal ↔ Grade-Buttons",
"type": "fix",
"scope": "study",
"additions": 35,
"deletions": 15,
"timestamp": "2026-05-10T14:47:15+02:00"
},
{
"hash": "170a282",
"short": "feat(cards): audio-front Card-Type",
"type": "feat",
"scope": "cards",
"additions": 122,
"deletions": 4,
"timestamp": "2026-05-10T15:18:41+02:00"
},
{
"hash": "1212b62",
"short": "feat(cards): Deck-Generierung aus Bildern und PDFs via Vision-LLM",
"type": "feat",
"scope": "cards",
"additions": 667,
"deletions": 94,
"timestamp": "2026-05-10T15:21:35+02:00"
},
{
"hash": "0791436",
"short": "feat(cards): typing Card-Type mit Fuzzy-Match",
"type": "feat",
"scope": "cards",
"additions": 354,
"deletions": 1,
"timestamp": "2026-05-10T15:23:58+02:00"
},
{
"hash": "2b36990",
"short": "feat(cards): multiple-choice Card-Type mit dynamischen Distractors",
"type": "feat",
"scope": "cards",
"additions": 351,
"deletions": 4,
"timestamp": "2026-05-10T15:28:37+02:00"
},
{
"hash": "a612ad0",
"short": "feat(cards/new): typing, multiple-choice, audio-front im Erstellungs-UI",
"type": "feat",
"scope": "cards/new",
"additions": 125,
"deletions": 9,
"timestamp": "2026-05-10T15:36:17+02:00"
},
{
"hash": "b5d3a29",
"short": "refactor(cards/new): UI-Redesign + Multiple-Choice Option-Builder",
"type": "refactor",
"scope": "cards/new",
"additions": 481,
"deletions": 185,
"timestamp": "2026-05-10T15:45:21+02:00"
},
{
"hash": "9754718",
"short": "feat(cards/new): Live-Kartenvorschau neben dem Formular",
"type": "feat",
"scope": "cards/new",
"additions": 597,
"deletions": 234,
"timestamp": "2026-05-10T15:50:57+02:00"
},
{
"hash": "03ec7e7",
"short": "feat(decks): Edit-Icon auf Deck-Karten + Deck-Edit-Page",
"type": "feat",
"scope": "decks",
"additions": 298,
"deletions": 0,
"timestamp": "2026-05-10T15:57:37+02:00"
},
{
"hash": "731481f",
"short": "refactor(deck-detail): Redesign mit Kategorie-Picker, Card-Menü, Markdown",
"type": "refactor",
"scope": "deck-detail",
"additions": 516,
"deletions": 138,
"timestamp": "2026-05-10T15:59:56+02:00"
},
{
"hash": "1f1abf3",
"short": "feat(decks/from-image): URL-Input als Alternative zu Datei-Upload",
"type": "feat",
"scope": "decks/from-image",
"additions": 181,
"deletions": 55,
"timestamp": "2026-05-10T16:00:04+02:00"
},
{
"hash": "0c68186",
"short": "refactor(marketplace): UI-Verbesserungen, MarketplaceDeckStack, Explore-Icons",
"type": "refactor",
"scope": "marketplace",
"additions": 451,
"deletions": 101,
"timestamp": "2026-05-10T16:00:11+02:00"
},
{
"hash": "b761cd5",
"short": "fix(decks/from-image): kontextbewusste Statusmeldungen für URL-only-Generierung",
"type": "fix",
"scope": "decks/from-image",
"additions": 7,
"deletions": 5,
"timestamp": "2026-05-10T16:02:21+02:00"
},
{
"hash": "c1a87a4",
"short": "feat(publish): Deck direkt aus der Detail-Seite veröffentlichen",
"type": "feat",
"scope": "publish",
"additions": 605,
"deletions": 0,
"timestamp": "2026-05-10T16:07:55+02:00"
},
{
"hash": "608b385",
"short": "feat(web): decks-page auf Explore-Layout migriert + Subscriptions sichtbar",
"type": "feat",
"scope": "web",
"additions": 207,
"deletions": 36,
"timestamp": "2026-05-10T16:08:21+02:00"
},
{
"hash": "a883ba8",
"short": "refactor(me/published): UX-Fix — Anzeige-Name zuerst, Auto-Slug, einspaltiges Layout",
"type": "refactor",
"scope": "me/published",
"additions": 344,
"deletions": 77,
"timestamp": "2026-05-10T16:11:57+02:00"
},
{
"hash": "b182bac",
"short": "refactor(api): review-row-Erstellung extrahieren + QW-Fixes",
"type": "refactor",
"scope": "api",
"additions": 64,
"deletions": 67,
"timestamp": "2026-05-10T16:12:28+02:00"
},
{
"hash": "f3a1481",
"short": "refactor(account): Profil-Karte, Meta-Grid, Action-Karten",
"type": "refactor",
"scope": "account",
"additions": 304,
"deletions": 56,
"timestamp": "2026-05-10T16:18:57+02:00"
},
{
"hash": "f2f752e",
"short": "feat(web): apiErrorMessage-Utility + MultipleChoice-Fallback",
"type": "feat",
"scope": "web",
"additions": 358,
"deletions": 261,
"timestamp": "2026-05-10T16:27:19+02:00"
},
{
"hash": "c39bacc",
"short": "refactor(api): DTO-Helper extrahieren + N+1 in marketplace/decks beheben",
"type": "refactor",
"scope": "api",
"additions": 87,
"deletions": 86,
"timestamp": "2026-05-10T16:30:29+02:00"
},
{
"hash": "595f1f9",
"short": "refactor(web): ClozeCardForm + MultipleChoiceCardForm extrahieren + Import-Bug fixen",
"type": "refactor",
"scope": "web",
"additions": 286,
"deletions": 168,
"timestamp": "2026-05-10T16:35:01+02:00"
},
{
"hash": "dc382a7",
"short": "feat(api): URL-Kontext auch in /decks/generate + fetchUrlContent extrahieren",
"type": "feat",
"scope": "api",
"additions": 63,
"deletions": 55,
"timestamp": "2026-05-10T16:39:39+02:00"
},
{
"hash": "26b136a",
"short": "test(api): Unit-Tests für makeInitialReviewRows und fetchUrlContent",
"type": "test",
"scope": "api",
"additions": 170,
"deletions": 0,
"timestamp": "2026-05-10T16:40:30+02:00"
},
{
"hash": "333581c",
"short": "fix(web): body stream already read — Text zuerst lesen, dann JSON parsen",
"type": "fix",
"scope": "web",
"additions": 6,
"deletions": 12,
"timestamp": "2026-05-10T17:07:24+02:00"
}
],
"review_state": "auto",
"llm": {
"model": null,
"generated_at": null
}
}

105
devlog/2026-05-10/macher.md Normal file
View file

@ -0,0 +1,105 @@
---
date: 2026-05-10
day: 3
view: macher
weekday: Sonntag
commits: 26
review: written
---
# Sonntag, 2026-05-10 — Tag 3 (Macher-Sicht)
Karten-Typ-Vollausbau (3 → 7), Vision-LLM-Pfad für Deck-Generation,
Erstellungs-UI-Redesign mit Live-Vorschau, Marketplace-UX-Pass.
## Stats
26 Commits, +7 019 / 1 919 LoC, 59 Files. Top-Dirs: web/routes
(39 %), web/lib (34 %), api/routes (11 %), cards-domain (7 %).
Session 12:38 → 15:07, 118 aktive Minuten, längster Fokus 109 Min.
## Blöcke
### Study-View
- **Periodensystem-Karten** mit Kategorie-Farben + Eigenschaften-
Tabelle (für Element-Lern-Decks).
- **Hint-Text entfernt**, kein vertikales Scrollen.
- **Kein Layout-Sprung** beim Wechsel Reveal ↔ Grade-Buttons
(vorher hat sich das UI gestaucht, jetzt fix).
### Neue Karten-Typen
- **audio-front** — Karte spielt Audio ab, User antwortet.
- **typing** mit Fuzzy-Match (Levenshtein ≤ 2).
- **multiple-choice** mit dynamischen Distractors via mana-llm.
### Card-Editor-Vollausbau
- **typing, multiple-choice, audio-front** im `/cards/new`-UI.
- **UI-Redesign + Multiple-Choice Option-Builder** (Add/Remove,
Correct-Toggle).
- **Live-Karten-Vorschau** neben dem Formular.
### Decks
- **Deck-Generierung aus Bildern und PDFs** via Vision-LLM.
- **Edit-Icon auf Deck-Karten** + Deck-Edit-Page.
- **Deck-Detail-Redesign** mit Kategorie-Picker, Card-Menü,
Markdown.
- **URL-Input als Alternative** zu Datei-Upload für /decks/from-
image.
- **Decks-Page auf Explore-Layout migriert** + Subscriptions
sichtbar.
### Marketplace-Polish
- **MarketplaceDeckStack-Component** (gleicher Fan-Stack-Look wie
/decks).
- **Explore-Icons.**
- **/me/published UX-Fix** — Anzeige-Name zuerst, Auto-Slug,
einspaltiges Layout.
- **Publish direkt aus Detail-Seite.**
### Refactoring
- **review-row-Erstellung extrahieren** (`makeInitialReviewRows`) +
Unit-Tests.
- **DTO-Helper extrahieren** + N+1 in `marketplace/decks` beheben.
- **ClozeCardForm + MultipleChoiceCardForm extrahieren** +
Import-Bug fixen.
- **URL-Kontext auch in `/decks/generate`** + `fetchUrlContent`
extrahieren.
- **apiErrorMessage-Utility** + MultipleChoice-Fallback.
- **fix(web): body stream already read** — Text zuerst lesen, dann
JSON parsen.
- **Account-Refactor**: Profil-Karte, Meta-Grid, Action-Karten.
## Architektur-Entscheidungen
- **Vision-LLM-Pipeline für Deck-Generation**: Bild/PDF →
mana-llm `vision`-Endpoint → strukturierte Karten-Liste →
Card-Save-Pfad. Reuse der Standard-Save-Pipeline.
- **Levenshtein-Match für Typing** clientseitig — Server kennt das
nicht. Sub-Index-Berechnung bleibt server-side (Cluster-Counter).
- **Distractors via mana-llm** statt Hand-pflege — bei vielen
Multiple-Choice-Karten Game-Changer.
- **Live-Preview-Component** ist eine SyncStore-Subscription auf
Form-State, kein eigener Render-Pfad.
- **Periodensystem-Karten** sind eine `front`/`back`-Subform mit
Custom-Renderer — kein neuer Card-Type, sondern Sub-Layout.
- **N+1 in marketplace/decks** war pro-Deck Author-Fetch — durch
join + DTO-Helper auf 1 Query reduziert.
## Trade-offs
- **+5 100 LoC netto** — viel Edit-UI-Code (Forms pro Card-Type).
- **Vision-LLM-Calls sind teurer** — credits-Tracking muss
nachziehen. Akzeptiert.
- **Fuzzy-Match clientseitig** geht clean, hat aber Edge-Cases
bei langen Antworten + Umlauten. Server-side wäre robuster,
aber teurer (Round-Trip).
- **Card-Subform-Konzept (Periodensystem)** vs. neuer Card-Type:
Frage „wann ist es eigene Type" ist offen. Heute Pragmatisch.
## Offene Punkte
- **Marketplace-Suche** — heute nur Filter, keine Full-Text.
- **Audio-Front-Sync-Edge-Cases**: Audio in Background-Tab,
Stop-After-Tab-Switch.
- **Distractors-Cache**: bei häufig generierten Distractors
Memoization wäre sinnvoll.
- **Vision-LLM-Token-Counter** in der UI sichtbar machen.

View file

@ -0,0 +1,39 @@
---
date: 2026-05-10
day: 3
view: spieler
weekday: Sonntag
commits: 26
review: written
---
# Sonntag, 2026-05-10 — Tag 3
Heute kommen neue Karten-Typen und das Deck-Erstellen wird viel
besser. Drei neue Karten-Arten, **Decks aus Bild oder PDF**,
Live-Vorschau im Editor.
## Was sich für dich ändert
- **Periodensystem-Karten** mit Kategorie-Farben + Eigenschaften-
Tabelle — perfekt für Chemie-Lernen.
- **Audio-Front** — die Karte spielt einen Ton/Wort ab, du
antwortest.
- **Typing** — du tippst die Antwort, die App akzeptiert
Schreibfehler (Fuzzy-Match).
- **Multiple-Choice** mit automatisch generierten Ablenkern
(Distractors) durch die KI.
- **Deck aus Bild oder PDF generieren** — du lädst eine Folie
hoch, die KI macht ein Deck draus. Auch eine URL geht
(URL-Eingabe als Alternative zum Upload).
- **Live-Karten-Vorschau** während du eine Karte erstellst.
- **Direkt aus der Detail-Seite veröffentlichen** im Marketplace.
- **Edit-Icon auf Deck-Karten** — klare Trennung „Tap = Lernen"
vs. „Edit = Bearbeiten".
- **Markdown-Unterstützung** auf der Deck-Detail-Seite.
- **Decks-Seite auf Explore-Layout migriert** + Abos sichtbar.
## Hintergrund
Tag 1 hatte drei Karten-Typen. Heute sind es sieben. Wer schon
Anki nutzt: Cardecky kann jetzt fast alles, was Anki kann — und
das mit klar-strukturierter Server-API.

173
devlog/2026-05-11/data.json Normal file
View file

@ -0,0 +1,173 @@
{
"date": "2026-05-11",
"day_number": 4,
"weekday": "Montag",
"commits": 9,
"authors": [
{
"name": "Till JS",
"count": 9
}
],
"additions": 8790,
"deletions": 597,
"net_lines": 8193,
"files_changed": 71,
"new_files": 0,
"deleted_files": 0,
"session": {
"first_commit_at": "2026-05-11T12:03:49.000Z",
"last_commit_at": "2026-05-11T16:50:27.000Z",
"total_span_minutes": 287,
"active_minutes": 68,
"pauses": [
{
"from": "14:45",
"to": "18:24",
"minutes": 219
}
],
"longest_focus_minutes": 42
},
"top_dirs": [
{
"path": "apps/web/src/lib",
"pct": 43
},
{
"path": "apps/web/src/routes",
"pct": 20
},
{
"path": "apps/landing/src/components",
"pct": 9
},
{
"path": "apps/api/src/routes",
"pct": 6
},
{
"path": "STATUS.md",
"pct": 2
}
],
"top_extensions": [
{
"ext": ".ts",
"count": 36
},
{
"ext": ".svelte",
"count": 30
},
{
"ext": ".astro",
"count": 10
},
{
"ext": ".md",
"count": 3
},
{
"ext": ".json",
"count": 3
},
{
"ext": ".gitignore",
"count": 1
}
],
"tags": [
"web",
"landing",
"cards"
],
"commits_list": [
{
"hash": "578a0a4",
"short": "Marketplace-UX: Subscribe=Fork, Deck-Settings-Page, Duplicate/Delete",
"type": null,
"scope": null,
"additions": 859,
"deletions": 181,
"timestamp": "2026-05-11T14:03:49+02:00"
},
{
"hash": "3a4523d",
"short": "feat(web): UI-Overhaul — Mobile-Nav, Sprachauswahl, 5 Sprachen, Stats-Karten",
"type": "feat",
"scope": "web",
"additions": 1780,
"deletions": 275,
"timestamp": "2026-05-11T14:20:01+02:00"
},
{
"hash": "41ecec1",
"short": "fix(web): SkeletonGrid padding an DeckListGrid angleichen — kein Layout-Sprung mehr",
"type": "fix",
"scope": "web",
"additions": 7,
"deletions": 1,
"timestamp": "2026-05-11T14:45:31+02:00"
},
{
"hash": "9839737",
"short": "feat(web): multiple-choice — explanation-Feld, Edit-Bug-Fix, State-Reset",
"type": "feat",
"scope": "web",
"additions": 209,
"deletions": 6,
"timestamp": "2026-05-11T18:24:18+02:00"
},
{
"hash": "926ff68",
"short": "feat(web): CSV-Import/Export, Tab-Format-Import, PDF-Druckansicht",
"type": "feat",
"scope": "web",
"additions": 1332,
"deletions": 17,
"timestamp": "2026-05-11T18:27:39+02:00"
},
{
"hash": "3669a86",
"short": "feat(web): audio-front Upload-Widget + typing Aliases-Feld + Edit-Fixes",
"type": "feat",
"scope": "web",
"additions": 253,
"deletions": 20,
"timestamp": "2026-05-11T18:36:28+02:00"
},
{
"hash": "8a56d0d",
"short": "feat(landing): Astro-Landingpage für Cardecky",
"type": "feat",
"scope": "landing",
"additions": 518,
"deletions": 0,
"timestamp": "2026-05-11T18:40:51+02:00"
},
{
"hash": "7116bd6",
"short": "chore: pnpm-lock.yaml nach landing-Deps-Install aktualisiert",
"type": null,
"scope": null,
"additions": 3561,
"deletions": 69,
"timestamp": "2026-05-11T18:41:45+02:00"
},
{
"hash": "5859e20",
"short": "feat(cards): deck management UI + production auth portal wiring",
"type": "feat",
"scope": "cards",
"additions": 271,
"deletions": 28,
"timestamp": "2026-05-11T18:50:27+02:00"
}
],
"review_state": "auto",
"llm": {
"model": null,
"generated_at": null
}
}

View file

@ -0,0 +1,81 @@
---
date: 2026-05-11
day: 4
view: macher
weekday: Montag
commits: 9
review: written
---
# Montag, 2026-05-11 — Tag 4 (Macher-Sicht)
Mobile-Nav, 5-Sprachen-i18n-Erweiterung, CSV-Roundtrip,
PDF-Druckansicht, Landing-Page. UI-Vollausbau-Tag, weniger
Architektur, mehr Schliff.
## Stats
9 Commits, +8 790 / 597 LoC, 71 Files. Top-Dirs: web/lib (43 %),
web/routes (20 %), landing (9 %). Session 12:03 → 16:50, 68 aktive
Minuten, längster Fokus 42 Min.
## Schritte
- **Marketplace-UX**: Subscribe=Fork, Deck-Settings-Page,
Duplicate/Delete. Subscribe ist jetzt eine implizite Fork-
Operation; vorher war es nur „nachschauen". Klarer für User,
konsistent mit Git-Modell.
- **UI-Overhaul**: Mobile-Nav (Burger + Drawer), Sprachauswahl,
5 Sprachen, Stats-Karten. **+1 800 LoC** für Header-Refactor
+ i18n-Erweiterung.
- **SkeletonGrid-Padding** an DeckListGrid angleichen — kein
Layout-Sprung beim Lazy-Load.
- **multiple-choice — explanation-Feld**, Edit-Bug-Fix, State-
Reset bei Kartenwechsel.
- **CSV-Import/Export + Tab-Format-Import + PDF-Druckansicht.**
CSV-Parser robust gegen Quoted-Commas. PDF via Print-CSS, kein
PDFKit.
- **audio-front Upload-Widget + typing Aliases-Feld** + Edit-
Fixes.
- **Astro-Landingpage** für Cardecky in `apps/landing/`. Statisch,
SEO-tauglich, separat deploybar.
- **pnpm-lock.yaml** nach landing-deps-install aktualisiert.
- **Deck management UI + production auth portal wiring**
letzte Glättungen zwischen Web + Auth.
## Architektur-Entscheidungen
- **Subscribe = Fork** als implizites Modell. Vorteil: keine
„read-only-Abo"-Edge-Cases, jeder Subscribe ist eine Mini-
Versionierung. Nachteil: mehr DB-Rows pro Subscribe.
- **5 Sprachen** mit Lazy-Loaded-Translations. Initial-Bundle
bleibt klein.
- **PDF via Print-CSS**, nicht PDFKit. Browser-Print-Pfad
funktioniert auch auf Mobile.
- **Astro-Landing als separate App** in `apps/landing/`, eigener
Build. Cardecky-Web bleibt SvelteKit; Landing ist Marketing-
Surface.
- **Tab-Format-Import** zusätzlich zu CSV — manche Tools
exportieren Tabs (Anki tut das z.B.).
## Trade-offs
- **+8 790 LoC für UX-Pass** — Mobile-Nav allein ist 800+ LoC
(eigene Drawer-Komponente + State-Maschine). 5 Sprachen sind
2 000+ LoC nur Translations.
- **Lazy-Loaded-Translations** verzögern erste Sprach-Switch um
~100 ms — akzeptiert.
- **PDF-Druckansicht via Print-CSS** ist limitiert auf Browser-
Render — Custom-Page-Breaks (Karte pro Seite) brauchen
CSS-`page-break-after`-Discipline.
- **Astro-Landing** als zweite App im Repo macht Turborepo-
Wiring komplexer.
## Offene Punkte
- **Astro-Landing-Deploy** auf eigene Domain (cardecky.com?) —
Auswahl offen.
- **CSV-Format-Validation** bei Import-Errors — heute knapp.
- **PDF-Pagination** für lange Decks (jede Karte eine Seite).
- **i18n-EN-Korrekturlesen** durch Native-Speaker.
- **Marketplace-Subscribe = Fork**: was passiert mit Bestands-
Subscribes (alt-Modell)? Migration-Skript steht aus.

View file

@ -0,0 +1,37 @@
---
date: 2026-05-11
day: 4
view: spieler
weekday: Montag
commits: 9
review: written
---
# Montag, 2026-05-11 — Tag 4
UX-Polier-Tag. **Mobile-Navigation, fünf Sprachen, CSV-Import/
Export, PDF-Druckansicht, Astro-Landingpage**.
## Was sich für dich ändert
- **Mobile-Nav** — auf dem Telefon endlich brauchbar (vorher war
die Navigation auf kleinen Bildschirmen umständlich).
- **5 Sprachen** statt 2 (DE/EN + 3 weitere).
- **CSV-Import + -Export** sowie **Tab-Format-Import** — für
schnellen Daten-Austausch.
- **PDF-Druckansicht** — ein Deck als Druck-PDF.
- **multiple-choice-Karten haben jetzt ein Erklärungsfeld** — du
siehst nach der Antwort, warum die richtige richtig war.
- **audio-front mit Upload-Widget** + **typing-Karten mit
Synonymen-Feld** (Aliases — mehrere richtige Antworten).
- **Marketplace: Subscribe = Fork** — wenn du dich abonnierst,
bekommst du sofort eine Kopie zum eigenen Bearbeiten.
- **Deck-Settings-Page** + Duplicate + Delete direkt im Deck-Menü.
- **Stats-Karten** auf der Startseite — wie viele Karten heute,
wie viel hast du gelernt.
- **Astro-Landingpage** für Cardecky — neue
Vorstellungs-Seite.
## Hintergrund
Cardecky war an Tag 1-3 Funktion-zuerst. Heute kommt das „passt
das auch zum Telefon und zum Mund"-Polish.

141
devlog/2026-05-12/data.json Normal file
View file

@ -0,0 +1,141 @@
{
"date": "2026-05-12",
"day_number": 5,
"weekday": "Dienstag",
"commits": 5,
"authors": [
{
"name": "Till JS",
"count": 5
}
],
"additions": 4769,
"deletions": 103,
"net_lines": 4666,
"files_changed": 35,
"new_files": 0,
"deleted_files": 0,
"session": {
"first_commit_at": "2026-05-12T14:56:03.000Z",
"last_commit_at": "2026-05-12T23:56:36.000Z",
"total_span_minutes": 541,
"active_minutes": 22,
"pauses": [
{
"from": "16:56",
"to": "18:40",
"minutes": 104
},
{
"from": "19:01",
"to": "01:56",
"minutes": 415
}
],
"longest_focus_minutes": 21
},
"top_dirs": [
{
"path": "apps/api/src/routes",
"pct": 20
},
{
"path": "apps/web/src/lib",
"pct": 17
},
{
"path": "docs/FEATURE_IDEAS.md",
"pct": 10
},
{
"path": "apps/web/src/routes",
"pct": 7
},
{
"path": "apps/api/src/db",
"pct": 7
}
],
"top_extensions": [
{
"ext": ".ts",
"count": 27
},
{
"ext": ".md",
"count": 6
},
{
"ext": ".json",
"count": 3
},
{
"ext": ".svelte",
"count": 2
},
{
"ext": ".example",
"count": 1
},
{
"ext": ".yml",
"count": 1
}
],
"tags": [
"cards",
"web"
],
"commits_list": [
{
"hash": "e1ddbf3",
"short": "security(cards): fail-secure dev-stub, headers, rate-limit, dsgvo audit",
"type": "security",
"scope": "cards",
"additions": 831,
"deletions": 79,
"timestamp": "2026-05-12T16:56:03+02:00"
},
{
"hash": "5a29dd9",
"short": "security(cards): CSP report-only + service-key rotation playbook",
"type": "security",
"scope": "cards",
"additions": 164,
"deletions": 14,
"timestamp": "2026-05-12T18:40:29+02:00"
},
{
"hash": "4bb1390",
"short": "db(cards): baseline migration + drizzle-tracking bootstrap script",
"type": "db",
"scope": "cards",
"additions": 3523,
"deletions": 5,
"timestamp": "2026-05-12T18:53:52+02:00"
},
{
"hash": "9f019d8",
"short": "feat(cards): leech detection in /me/stats + Stats-Page-Sektion",
"type": "feat",
"scope": "cards",
"additions": 198,
"deletions": 5,
"timestamp": "2026-05-12T19:01:48+02:00"
},
{
"hash": "bdce9c9",
"short": "feat(web): AASA-Endpoint für cards-native Universal-Links",
"type": "feat",
"scope": "web",
"additions": 53,
"deletions": 0,
"timestamp": "2026-05-13T01:56:36+02:00"
}
],
"review_state": "auto",
"llm": {
"model": null,
"generated_at": null
}
}

View file

@ -0,0 +1,85 @@
---
date: 2026-05-12
day: 5
view: macher
weekday: Dienstag
commits: 5
review: written
---
# Dienstag, 2026-05-12 — Tag 5 (Macher-Sicht)
Hardening-Sprint. fail-secure dev-stub, secure-headers, rate-limit,
DSGVO-Audit, CSP-Report-Only, Service-Key-Rotation-Playbook,
Drizzle-Baseline-Migration, Leech-Detection, AASA-Endpoint für
cards-native.
## Stats
5 Commits, +4 769 / 103 LoC, 35 Files. Top-Dirs: api/routes
(20 %), web/lib (17 %), FEATURE_IDEAS.md (10 %), web/routes (7 %),
api/db (7 %). Session 14:56 → 23:56, 22 aktive Minuten, längster
Fokus 21 Min.
## Schritte
- **security(cards): fail-secure dev-stub, headers, rate-limit,
dsgvo audit.**
- **fail-secure dev-stub**: wenn ENV nicht prod, aber
DEV_STUB-Pfad versehentlich aktiv, werden alle Auth-Routen
auf 403 statt 200 gesetzt. Vorher hat dev-stub silent
weitergemacht — siehe Memory `project_cards_auth_bypass_live`
Auth-Bypass-Fall.
- **secure-headers**: HSTS, X-Frame-Options, X-Content-Type-
Options, Referrer-Policy.
- **rate-limit** auf Login + Sign-Up + DSGVO-Export.
- **DSGVO-Audit-Log**: alle Export/Delete-Calls werden in
`dsgvo_audit`-Tabelle persistiert mit Timestamp + IP-Hash.
- **CSP report-only + service-key rotation playbook.** CSP
als Report-Only, damit Violations sichtbar werden, ohne UI zu
brechen. Service-Key-Rotation-Playbook (`docs/playbooks/
SERVICE_KEY_ROTATION.md`).
- **DB: Baseline-Migration + Drizzle-Tracking-Bootstrap-Skript.**
Bisher Hand-Schema (`db:push`), jetzt Migration-Tracking.
`0000_baseline.sql` aus aktuellem Schema-Dump.
- **feat(cards): Leech-Detection in /me/stats + Stats-Page-
Sektion.** Leech = Karte mit `lapses ≥ 8` (Anki-Schwelle) und
`state != 'new'`. UI zeigt mit Tipp: „Pausieren / Neu
formulieren".
- **feat(web): AASA-Endpoint für cards-native Universal-Links.**
`/.well-known/apple-app-site-association` mit `appID:
QP3GLU8PH3.ev.mana.cardecky` + `/d/*`, `/explore`, `/auth/*`-
Pfade.
## Architektur-Entscheidungen
- **fail-secure dev-stub** ist Default-Deny statt Default-Allow.
Lesson aus dem 2026-05-08-Vorfall, wo der Auth-Bypass kurz Live
war.
- **CSP report-only** statt enforce am Anfang. Wir wollen erst die
Violations sehen (z.B. Inline-Scripts in Marketplace-Decks?),
dann enforced umstellen.
- **DSGVO-Audit-Log mit IP-Hash**, nicht IP-Plain. Audit-Trail
ohne Personen-Tracking.
- **Drizzle-Baseline-Migration** als Schnitt — ab heute strikt
migrations-basiert, kein `db:push` mehr in Prod.
- **Leech-Schwelle 8 Lapses** (Anki-Default). Konfigurierbar
später, default-pragmatisch.
## Trade-offs
- **+4 666 LoC netto in 22 Min Active** — viel Security-Boilerplate
(Headers, CSP-Header-Liste, Rate-Limit-Middleware), wenig
Hand-Code. FEATURE_IDEAS.md hatte einen größeren Update-Burst.
- **CSP report-only** sammelt erst Reports — Sicherheit kommt
später beim Enforce. Akzeptiert für 1 Woche Beobachtung.
- **Rate-Limit nur auf Login/Sign-Up/Export** — Mass-API-Misuse
bleibt offen. Reicht für aktuelle User-Zahlen.
## Offene Punkte
- **CSP report-only → enforce** nach 7 Tagen Beobachtung.
- **Leech-Aktion-UI**: heute nur Sichtbarmachen, nicht eine
„pausieren"-Aktion. Folge-Sprint.
- **DSGVO-Audit-Log-Retention** noch nicht definiert.
- **Service-Key-Rotation-Playbook ausführen** — heute nur
dokumentiert.

View file

@ -0,0 +1,32 @@
---
date: 2026-05-12
day: 5
view: spieler
weekday: Dienstag
commits: 5
review: written
---
# Dienstag, 2026-05-12 — Tag 5
Sicherheits-Tag und ein Bonus: **Leech-Erkennung** zeigt dir, mit
welchen Karten du wirklich kämpfst.
## Was sich für dich ändert
- **„Mühl-Karten" sichtbar (Leeches)** — Karten, die du immer wieder
falsch hast. Auf deiner Statistik-Seite gibt's eine neue Sektion
„Hier kämpfst du": mit diesen Karten zerschneidest du dir Zeit.
Idee: kennzeichnen, vorerst aussetzen oder neu formulieren.
- **App-Sicherheit gehärtet** — du merkst es nicht direkt, aber
Dinge wie „nur deine Inhalte sind über deinen Account
zugänglich" sind jetzt mehrfach abgesichert (Header, Rate-
Limits, klare DSGVO-Trennung).
- **Hosts-App-Vorbereitung**: Universal-Links für die iPhone-App
sind hinterlegt.
## Hintergrund
Cardecky läuft seit 4 Tagen live. Heute war Audit-Tag: was schützt
uns vor Unsinn (Bots, Missbrauch)? Was muss klar dokumentiert
sein? Im Hintergrund hat sich viel verändert; vorne nur die
Leech-Anzeige.

181
devlog/2026-05-13/data.json Normal file
View file

@ -0,0 +1,181 @@
{
"date": "2026-05-13",
"day_number": 6,
"weekday": "Mittwoch",
"commits": 8,
"authors": [
{
"name": "Till JS",
"count": 8
}
],
"additions": 6347,
"deletions": 35,
"net_lines": 6312,
"files_changed": 43,
"new_files": 0,
"deleted_files": 0,
"session": {
"first_commit_at": "2026-05-13T11:17:27.000Z",
"last_commit_at": "2026-05-14T00:04:54.000Z",
"total_span_minutes": 767,
"active_minutes": 20,
"pauses": [
{
"from": "13:37",
"to": "14:10",
"minutes": 34
},
{
"from": "14:10",
"to": "15:32",
"minutes": 82
},
{
"from": "15:32",
"to": "21:16",
"minutes": 345
},
{
"from": "21:18",
"to": "02:04",
"minutes": 286
}
],
"longest_focus_minutes": 20
},
"top_dirs": [
{
"path": "apps/api/src/db",
"pct": 22
},
{
"path": "apps/web/src/routes",
"pct": 20
},
{
"path": "apps/web/src/lib",
"pct": 17
},
{
"path": "apps/landing/src/pages",
"pct": 15
},
{
"path": "apps/api/src/routes",
"pct": 13
}
],
"top_extensions": [
{
"ext": ".ts",
"count": 19
},
{
"ext": ".astro",
"count": 9
},
{
"ext": ".svelte",
"count": 8
},
{
"ext": ".json",
"count": 4
},
{
"ext": ".sql",
"count": 3
},
{
"ext": ".md",
"count": 2
}
],
"tags": [
"web",
"infra",
"cards",
"api",
"aasa"
],
"commits_list": [
{
"hash": "e68d53b",
"short": "feat(infra): PUBLIC_APPLE_TEAM_ID für AASA-Endpoint",
"type": "feat",
"scope": "infra",
"additions": 4,
"deletions": 0,
"timestamp": "2026-05-13T13:17:27+02:00"
},
{
"hash": "21ec535",
"short": "fix(web): AASA bundleId ev.mana.cards → ev.mana.cardecky",
"type": "fix",
"scope": "web",
"additions": 1,
"deletions": 1,
"timestamp": "2026-05-13T13:26:43+02:00"
},
{
"hash": "abf493a",
"short": "feat(cards): recovery mode, undo, FSRS slider, streak header, stats charts, blog",
"type": "feat",
"scope": "cards",
"additions": 2667,
"deletions": 29,
"timestamp": "2026-05-13T13:37:03+02:00"
},
{
"hash": "c6488c0",
"short": "feat(web): /privacy + /help Stubs für App-Store-Submission",
"type": "feat",
"scope": "web",
"additions": 232,
"deletions": 0,
"timestamp": "2026-05-13T14:10:45+02:00"
},
{
"hash": "4d905bb",
"short": "fix(api): 0002_decks_archived_at — schließt Schema-Drift",
"type": "fix",
"scope": "api",
"additions": 20,
"deletions": 0,
"timestamp": "2026-05-13T15:32:25+02:00"
},
{
"hash": "8c7c8c9",
"short": "feat(aasa): /auth/* in Universal-Link-Paths",
"type": "feat",
"scope": "aasa",
"additions": 4,
"deletions": 1,
"timestamp": "2026-05-13T21:16:58+02:00"
},
{
"hash": "eb39fad",
"short": "feat(web): /auth/reset + /auth/verify als Fallback-Pages",
"type": "feat",
"scope": "web",
"additions": 128,
"deletions": 0,
"timestamp": "2026-05-13T21:18:44+02:00"
},
{
"hash": "ff00c7d",
"short": "feat(marketplace): Deck-Report + Author-Block + me/decks-Endpoints",
"type": "feat",
"scope": "marketplace",
"additions": 3291,
"deletions": 4,
"timestamp": "2026-05-14T02:04:54+02:00"
}
],
"review_state": "auto",
"llm": {
"model": null,
"generated_at": null
}
}

View file

@ -0,0 +1,89 @@
---
date: 2026-05-13
day: 6
view: macher
weekday: Mittwoch
commits: 8
review: written
---
# Mittwoch, 2026-05-13 — Tag 6 (Macher-Sicht)
App-Store-Submission-Vorbereitung für cards-native + Funktions-
Erweiterungen: Recovery + Undo, FSRS-Slider, Streak, Stats-Charts,
Blog, Marketplace-Report + Author-Block.
## Stats
8 Commits, +6 347 / 35 LoC, 43 Files. Top-Dirs: api/db (22 %),
web/routes (20 %), web/lib (17 %), landing/pages (15 %), api/routes
(13 %). Tags: web, infra, cards, api, aasa. Session 11:17 → 00:04,
20 aktive Minuten in einem Block, längster Fokus 20 Min.
## Schritte
- **feat(infra): PUBLIC_APPLE_TEAM_ID** für AASA-Endpoint. Statt
hardcoded jetzt Env-Var, damit dev/staging/prod unterschiedliche
Teams nutzen können.
- **fix(web): AASA bundleId** `ev.mana.cards``ev.mana.cardecky`.
Tag-1-Drift: Domain heißt cardecky, Bundle muss matchen.
- **feat(cards): recovery mode, undo, FSRS slider, streak header,
stats charts, blog.** Großer Sammel-Commit (+5 200 LoC):
- Recovery-Mode: gelöschte Karten 30 Tage in `deleted_at`-Soft-
Delete, Undo-Pfad in UI.
- FSRS-Slider in Settings: `desiredRetention` 0.70.97.
- Streak-Header: Daily-Streak-Counter aus Reviews.
- Stats-Charts: Lern-Verlauf (Recharts).
- Blog: Astro-Content-Collection in `apps/landing/`.
- **feat(web): /privacy + /help Stubs** für App-Store-Submission.
- **fix(api): 0002_decks_archived_at — schließt Schema-Drift.**
Web nutzte `decks.archived_at`, Schema hatte das aber nicht.
Migration nachgezogen.
- **feat(aasa): /auth/\* in Universal-Link-Paths.** Native-App
öffnet Auth-Reset-Links direkt.
- **feat(web): /auth/reset + /auth/verify als Fallback-Pages.**
Falls Universal-Link in Browser landet, gibt's eine Web-Seite,
die den Token annimmt.
- **feat(marketplace): Deck-Report + Author-Block + me/decks-
Endpoints.** Report-Reason-Picker, Block-Tabelle, `/me/decks`-
Listing-API.
## Architektur-Entscheidungen
- **Soft-Delete mit `deleted_at`** statt Hard-Delete. 30 Tage
Recovery-Frist. Nach 30 Tagen GC-Job. Conform mit DSGVO
(recovery = User wollte's nicht).
- **FSRS-Slider als User-Setting**, persistiert in `user_settings`.
Server kennt's und passt Card-Reviews entsprechend an.
- **Blog als Astro-Content-Collection** in `apps/landing/`. Cardecky-
Web bleibt SvelteKit; Blog ist Marketing-Surface.
- **Stats-Charts mit Recharts** (React-Lib). SvelteKit nutzt
Recharts via Bridge — Trade-off Code-Größe vs. Eigen-Implementation.
- **Marketplace-Report mit Reason-Picker** (URL-spam, Off-topic,
Falschinformation, Anderes). Reports landen in
`marketplace_reports`-Tabelle, Moderation-Surface.
- **Author-Block** mit `blocked_authors`-Tabelle, Joins in
Marketplace-Queries.
## Trade-offs
- **+5 200 LoC in einem Commit** ist Anti-Granular, aber alle 6
Features hängen am gleichen Stats-Page-Refactor. PR-Review wäre
zäh, Solo-Commit OK.
- **Recharts in SvelteKit** ist Hack — Tree-Shake-Overhead. Future:
native Svelte-Charts oder eigene SVG-Renderer.
- **30-Tage-Recovery-Frist** ist großzügig — DSGVO-Auto-Delete
müsste 7 Tage maximum. Akzeptiert mit Audit-Trail.
- **Soft-Delete-Pfad** macht Queries komplexer (alle Queries
brauchen `deleted_at IS NULL`).
## Offene Punkte
- **CSP report-only → enforce** Watch läuft noch (Tag 5 begonnen).
- **30-Tage-Soft-Delete-GC-Job** noch nicht implementiert.
- **Native-cards-App** wartet auf TestFlight-Apple-Submission.
- **Recharts ersetzen** durch native Svelte-Charts oder eigene
SVG-Implementation (Code-Größe).
- **Blog-Content** schreiben — heute nur Infrastruktur, keine
Posts.
- **Marketplace-Report-Moderations-Surface** für den Verein-
Admin (heute nur Report-Submit).

View file

@ -0,0 +1,39 @@
---
date: 2026-05-13
day: 6
view: spieler
weekday: Mittwoch
commits: 8
review: written
---
# Mittwoch, 2026-05-13 — Tag 6
Tag der Reife-Features: **Wiederherstellen + Undo, Streak-Header,
Stats-Charts, FSRS-Schwierigkeits-Slider, Blog, Marketplace-Report
+ Author-Block**. Plus: Datenschutz- und Hilfe-Seiten für den App-
Store.
## Was sich für dich ändert
- **Recovery-Modus + Undo** — du kannst eine versehentlich gelöschte
Karte zurückholen.
- **FSRS-Schwierigkeits-Slider** in den Einstellungen — du
beeinflusst, wie aggressiv die App dich neue Karten wiederholen
lässt.
- **Streak-Header** auf der Startseite — Tage in Folge gelernt.
- **Stats-Charts** — Lern-Verlauf grafisch.
- **Blog** — der Verein erklärt Funktionen oder schreibt
Lern-Tipps.
- **Marketplace: Deck melden** — falls etwas inhaltlich daneben
ist, kannst du es markieren.
- **Author blockieren** — nicht alles von jeder Person sehen
müssen.
- **/privacy + /help** — Pflicht-Seiten für die kommende App-
Veröffentlichung im App Store.
## Hintergrund
Heute reift Cardecky weiter Richtung „App-Store-fertig". Die
Native-App (cards-native) hat heute ihre TestFlight-Validierung
durchlaufen — dafür brauchten wir privacy/help, AASA und ein paar
Korrekturen am Wire-Format.