cards/docs/playbooks/MARKETPLACE_RESTORE.md
Till JS 9a7068dd19 Phase 12 R0+R1: Marketplace-Restore-Plan + Schema in marketplace-pgSchema
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>
2026-05-09 15:05:22 +02:00

15 KiB
Raw Blame History

Marketplace-Restore — Playbook

Status: Plan, R0+R1 in Arbeit. Stand 2026-05-09. Vorgänger: das alte services/cards-server/ aus managarten/ (mana- monorepo) wurde am 2026-05-08 zusammen mit apps/cards/ dekommissioniert, weil beides eine Kopplung war — siehe Decommission-Commits bc158cb0b (cards-server), 9cd871749 (apps/cards), dd1bab09d (cards-core). Rollback-Tag: cards-decommission-base im managarten-Repo.

Dieser Plan dokumentiert den Restore des Marketplace-Backends in die Standalone-Cards-App, additiv zur bestehenden Greenfield-API.

TL;DR

  • Strategie-B-Klarstellung: „Kein Code aus mana-monorepo" galt der Study-/FSRS-/Sync-Schicht (Dexie raus, server-authoritative). Marketplace war nie davon betroffen — er wurde nur mit-rausgerissen, weil er an apps/cards gekoppelt war.
  • Restore vs. Neubau: Restore. ~13.000 Zeilen reifer Code, 8 Phasen in Produktion gelaufen, Plan-Doku in Goldstandard-Qualität (654 Zeilen), bekannte Limitierungen sauber dokumentiert. Neubau wäre 46 Wochen, Restore ~14 Tage.
  • Schema-Naming-Entscheidung: Eigenes marketplace-pgSchema in derselben cards-DB. Begründung: saubere Read/Write-Trennung, Backup-Granularität (pg_dump --schema=marketplace), RLS-Policies pro Schema möglich, keine Kollisionen mit den existierenden cards.{decks,cards,reviews,…}-Tabellen.
  • Service-Topologie: Single-Service. Marketplace-Routen kommen unter /api/v1/marketplace/* in den bestehenden cards/apps/api. Kein zweiter Hono-Prozess, kein zweiter Container. YAGNI bei deinem Volumen.
  • Frontend: alte Routes 1:1 nach cards/apps/web/src/routes//explore, /d/[slug], /u/[slug], /me/{published,subscribed,forks,purchases}, /admin/reports. Imports auf Verdaccio-Pakete umstellen, Theming- Bridge-Aliase greifen automatisch.
  • Restore-Reihenfolge: R0 (Doku) → R1 (Schema) → R2 (Auth + Routes α/β) → R3 (γ + δ) → R4 (ε) → R5 (Frontend) → R6 (Smoke + erste Cardecky-Decks publishen).
  • Was nicht im ersten Wurf: Paid Decks (ζ.1) und Moderation-UI (η.1). Schema-Tabellen kommen mit, aber Code-Pfade bleiben dormant bis nach erstem Live-Test.

Was die alte Implementation konnte (Phase α–η.1)

Inhalt der archivierten Dokumente — Original-Wahrheit unter cards/docs/marketplace/archive/:

Datei Was drin
MARKETPLACE_PLAN_2026-05-07.md 654-Zeilen-Vollvision: Datenmodell, mana-credits-Flow, Cold-Start-Strategie, Anti-Patterns, Phasen-Status
COMPETITORS_2026-05.md 353-Zeilen-Konkurrenz-Analyse: Quizlet, AnkiHub, Brainscape, AnkiPro, AnkiApp, RemNote, Mnemosyne
GUIDELINES.md 367 Zeilen Community-Guidelines + Lizenz-Modell (SPDX + Cardecky-Personal-Use-1.0 + Cardecky-Pro-Only-1.0)
cards-server_CLAUDE.md 110 Zeilen Tech-Stack-Doku des Original-Service

Phasen-Status zum Zeitpunkt der Decommission (2026-05-08):

Phase Status Was lief produktiv
α — Skelett live 17-Tabellen-Schema, JWT-Auth, Container, Tunnel cardecky-api.mana.how
β — Author-Workflow live Profil-Claim, Publish, Lizenz-Picker (SPDX), Preis-Eingabe, AI-Mod-First-Pass
γ — Discovery live /explore, Stars, Follows, Author-Profile, Trending, Search (ILIKE)
δ — Subscribe + Smart-Merge live Pull, Diff-View „+N · ~N · N", FSRS-State erhalten über Karten-Hash-Diff
ε — PRs + Discussions live ✏️ Verbessern" auf jeder Karte, Author-Merge, Inline-Threads, Notify-Mails
ζ.1 — Paid Decks live 4-Schritt-Reserve→Purchase→Commit→Grant, 80/20-Split (90/10 für verified_mana)
η.1 — Moderation live Reports, Admin-Inbox, Takedown-Workflow (kaskadiert auf PRs), Author-Ban

Bekannte Limitierungen (siehe MARKETPLACE_PLAN_2026-05-07.md §13a): PR-Merge-stale-blind, Reconciler-Lücke bei Paid-Pipeline, Mention-System fehlt, Discussion-Threading 1-Level, kein Refund-Self-Service. Alles dokumentiert, nichts unbekannt.


Architektur-Anpassungen für den Restore

1. DB-Topologie: eigenes marketplace-pgSchema

Alt (in mana_platform-DB, geteilt mit allen mana-Services):

mana_platform.cards.{authors, decks, deck_versions, …}        — 17 Tabellen

Neu (in standalone cards-DB des Standalone-Repos):

cards.cards.{decks, cards, reviews, media_files, tags, imports}  — Greenfield, bleibt
cards.marketplace.{authors, decks, deck_versions, deck_cards, …} — Restore, neu

Begründung für ein eigenes pgSchema statt Tabellen-Prefix (published_decks, published_deck_versions, …):

  • Sauberer Read-Path. Public-Endpoints sehen nur das marketplace- Schema. Greenfield-Code (private Decks/Karten/Reviews) sieht ausschließlich cards. Kein Risiko, dass ein SELECT * FROM decks versehentlich beide Welten mischt.
  • Backup-Granularität. pg_dump --schema=marketplace exportiert nur den Marketplace-Stand für Compliance/Recovery. Privater Lern- Stand der User bleibt unangetastet.
  • RLS-Policies pro Schema — falls wir je Row-Level-Security einführen für public-decks-take-down-Workflows, ist das pro Schema konfigurierbar.
  • Drizzle-kit-Push-Disziplin. schemaFilter: ['cards', 'marketplace'] hält beide Pushes sauber. Schema-Drift fängt sich auf Schema-Ebene.

Drizzle-Variablennamen halten den public-Prefix aus dem alten Code: publicDecks, publicDeckVersions, publicDeckCards. So bleibt das intent klar, und Imports kollidieren nicht mit cards.decks aus dem Greenfield.

2. Auth-Modell: identisch, kein Refactor

Alter cards-server: JWKS-Cache gegen mana-auth. Greenfield-cards-api: JWKS-Cache gegen mana-auth. Identisch. Service-Key-Auth (X-Service-Key) für Mana-Webhooks ebenfalls 1:1 übernommen.

Optional-Auth-Middleware für Public-Endpoints (/explore, /d/:slug) muss aus dem alten Code mitkommen — der erlaubt anonymen Read und gibt zugleich personalisierte Daten (z.B. „Bist du Subscriber?") wenn ein Bearer mit-übermittelt ist.

3. Service-Topologie: Single-Service

Alle Marketplace-Routen unter /api/v1/marketplace/* in cards/apps/api/src/routes/marketplace/:

cards/apps/api/src/routes/marketplace/
├── authors.ts
├── decks.ts            (publish, list, version reads)
├── engagement.ts       (stars, subscribe, fork)
├── discussions.ts      (card-discussions threads)
├── pull-requests.ts
├── moderation.ts       (reports + admin)
├── purchases.ts        (paid decks — dormant in R3, aktiv ab späterer Welle)
└── explore.ts          (discovery + search)

Hauptserver-Mount in cards/apps/api/src/index.ts:

app.route('/api/v1/marketplace/authors', authorsRouter())
app.route('/api/v1/marketplace/decks', decksRouter())
// …

Vorteile: ein Prozess, ein Container, ein Tunnel-Endpoint (cardecky-api.mana.how), eine JWT-Validierung, eine Drizzle-DB-Connection.

4. Frontend: additive Routes, gleicher Stack

cards/apps/web/ ist SvelteKit + Svelte 5 (runes-only). Alter apps/cards/apps/web/ war es auch. Routen werden 1:1 übernommen, mit drei Anpassungen:

  • Imports: @mana/shared-* kommt heute aus Verdaccio (npm.mana.how). pnpm add @mana/shared-ui@^0.1.1 @mana/shared-share-protocol ….
  • Theming: alte Components nutzten alte Token. Greenfield-Bridge- Aliase in app.css mappen die meisten alten Token aufs 12er-Mana- Vokabular. Test im Browser, ggf. Anpassungen.
  • Auth-Hook: dev-stub.svelte.ts bleibt für Phase-2-Lücke. Sobald echte mana-auth-Login-Flow ausgerollt ist (siehe STATUS.md), weicht der Stub für @mana/shared-auth.

5. Hash-Implementierung: bestehende @cards/domain benutzen

Alter cards-server/src/lib/hash.ts: eigenständige SHA-256-Implementierung. Greenfield: cardContentHash in @cards/domain (Web-Crypto, deterministisch).

Beim Restore: lib/hash.ts nicht mit-übernehmen, sondern Marketplace-Code auf cardContentHash aus @cards/domain umbiegen. Eine Hash-Definition für die ganze App.

6. mana-Service-Calls: identische Pattern

Alter cards-server rief mana-credits, mana-llm, mana-media, mana-notify über MANA_*_URL-env-Vars. Greenfield-cards-api macht das genauso. Code 1:1 übernehmen.


Wellen-Plan

Welle Zustand Was passiert Blocker
R0 🟡 in Arbeit Doku-Restore: Archive aus cards-decommission-base + dieser Plan + Strategie-B-Klarstellung
R1 ⏸ pending Schema-Restore: 7 Schema-Files in cards/apps/api/src/db/schema/marketplace/, drizzle-kit push grün gegen lokale cards-DB R0
R2 ⏸ pending Backend Phase α + β: Author-Profile + Publish + AI-Mod-Stub R1
R3 ⏸ pending Backend Phase γ + δ: Discovery + Subscribe + Smart-Merge R2
R4 ⏸ pending Backend Phase ε: Pull-Requests + Card-Discussions R3
R5 ⏸ pending Frontend-Routes: /explore, /d/[slug], /u/[slug], /me/{published,subscribed,forks} R4
R6 ⏸ pending E2E-Smoke: erstes Cardecky-Deck publishen, von Till's Account subscriben, Smart-Merge testen R5

Aufwand-Schätzung gesamt: ~14 Tage Real-Arbeit.

Bewusst aus dem ersten Restore-Wurf rausgelassen:

  • ζ.1 Paid Decks — Schema-Tabellen kommen in R1 mit, aber Routes/UI bleiben dormant. Re-Aktivierung als eigene Welle nach Live-Validation. Begründung: mana-credits-Integration ist heikel (4-step-Pipeline mit reservation-commit-grant), Author-Erlöse sind ein Verein-Compliance- Thema (Steuern, AGB, Refund-Policy), und solange Cardecky synthetic Decks publisht, gibt's keinen Need.
  • η.1 Moderation-UI — Schema-Tabellen + API-Endpoints kommen mit, Admin-Frontend (/admin/reports) wird ausgelassen bis erste echte User da sind. Take-Down via SQL für die ersten Wochen.
  • θ Deep AI (Auto-Tags, Embeddings, TTS) — bleibt explizit später- Phase, war im Original-Plan auch nicht für den ersten Wurf vorgesehen.

Cardecky-Skill-Integration

Der /cards-deck-Skill (siehe ~/.claude/skills/cards-deck/SKILL.md) produziert Decks unter dem Cardecky-Plattform-User. Beim Marketplace- Restore wird Cardecky automatisch zum Marketplace-Author:

  1. Bei R2 wird ein Init-Skript einen marketplace.authors-Row für Cardecky-User-ID anlegen (slug='cardecky', display_name='Cardecky', pseudonym=false, verified_mana=true — der Verein vergibt das Badge an seinen eigenen KI-Author).
  2. Skill-Stufe 5 (Publish) wird erweitert um einen optionalen Schritt: nach Anlegen des privaten Decks kann der Skill ein POST /api/v1/marketplace/decks/:id/publish mit semver=1.0.0 und einem auto-generierten Changelog hinterher schicken — dann ist das Deck sofort im /explore sichtbar.
  3. Default bleibt aber „nur privat anlegen", weil:
    • das Validate-Stage (Stufe 4) eine menschliche Sichtung verdient, bevor 30 Karten öffentlich werden;
    • Reviewer-Stops nach Stufe 3 sind zwingend.

Der Skill braucht keine Architektur-Änderung — er addiert nur einen optionalen 6. Schritt.


Lizenz-Modell (aus dem Original übernommen)

Aus MARKETPLACE_PLAN_2026-05-07.md §3 + GUIDELINES.md:

SPDX-ID Erlaubt Wann
CC0-1.0 alles, kein Attribution-Pflicht für Public-Domain-fähige Karten
CC-BY-4.0 alles, mit Attribution meist gewählt für Wissens-Decks
CC-BY-SA-4.0 alles, ShareAlike + Attribution für Wikipedia-derivierte Decks
Cardecky-Personal-Use-1.0 nur persönlicher Lern-Use, kein Re-Publish Default für kostenlose Decks
Cardecky-Pro-Only-1.0 nur via Kauf, kein Re-Publish, kein Fork Pflicht für paid Decks (DB-CHECK enforced)

Der DB-CHECK auf decks.price_credits = 0 OR license = 'Cardecky-Pro-Only-1.0' ist im Schema beibehalten — Code-Bug kann nicht stillschweigend ein Paid-Deck mit CC-Lizenz publishen.


Cold-Start-Strategie (aus dem Original)

Kommt im ersten Restore-Wurf nicht aktiv zum Tragen, aber der Original- Plan §9 hat drei Hebel definiert, die bei Re-Launch greifen:

  1. Verein-Seed-Decks — 50 hochwertige Cardecky-published Decks (Sprachen, Geschichte, Allgemeinwissen, Programmierung). Der /cards-deck-Skill ist genau das Werkzeug dafür.
  2. Anki-Top-100-Import-Service — populäre CC-BY-Anki-Decks mit Original-Author-Attribution importieren, Original-Author bekommt verified_mana-Badge bei Registrierung.
  3. Influencer-Outreach — 1020 Anki-Power-Authoren (AnKing & Konsorten) gezielt ansprechen, sehr Author-freundlicher Cut.

Hebel 2 + 3 sind Wachstumsmaßnahmen, nicht Phase-1. Hebel 1 ist sofort umsetzbar mit dem bestehenden Skill.


Anti-Patterns aus dem Original-Plan §13 (gelten weiter)

  • Kein 1-5-Sterne-Rating-System. Stars (Bookmark) ja, Bewertungen nein.
  • Kein Reddit-Style-Voting auf Karten/PRs/Discussions. Hacker-News-Effekt.
  • Kein „Karten der Woche" allein-algorithmisch. Editorial + Trending- Liste, aber niemals reiner Algo-Feed.
  • Kein Anki-Bashing im Marketing. Bridge nicht Burning.
  • Keine Pflicht-Klarnamen. Pseudonyme bleiben gleichberechtigt.
  • Kein Marketplace-Cut über 30 %. Standard 80/20, verified 90/10.

Offene Punkte

  • PR-Merge-stale-blind aus dem Original. Bekannte Limitierung: wenn Author zwischen PR-Open und Merge selbst eine Karte ändert, deren previousContentHash der PR matched, gewinnt stumm der PR. Im ersten Restore-Wurf so übernehmen wie war; späterer Fix via optimistic locking auf baseVersionId der PR-Row mit Reject bei Mismatch.
  • Reconciler-Cron für Paid-Pipeline-Inkonsistenzen. Original §13a beschreibt das Loch: bei Commit-/Grant-Failure nach Schritt 2 bleibt eine Purchase-Row mit creditsTransaction = null. Beim Restore initial nicht aktiv (Paid-Decks dormant), aber sobald Phase ζ reaktiviert wird, ist Reconciler ein muss-haben.
  • cards-decommission-base-Tag im managarten-Repo. Falls jemand managarten löscht oder das Tag nochmal entfernt, geht der Restore- Pfad verloren. Empfehlung: Schema-Files + ausgewählte Code-Snippets einmal nach cards/docs/marketplace/archive/ kopieren (Doks sind schon drin, Code-Files folgen optional bei R1+R2).
  • Schema-Migrations-Pfad bei späterer Drizzle-Version. Greenfield- Cards plant Migration auf Drizzle 0.45/zod-4 mit der Plattform mit (mana/docs/MIGRATION_DRIZZLE_ZOD.md). Marketplace-Schema kommt mit Drizzle 0.38 — sollte mit upgradeen, idealerweise atomar.
  • Karten-Hash-Konsistenz zwischen Greenfield und Marketplace. Greenfield-@cards/domain cardContentHash ist die SoT. Marketplace- deck_cards.content_hash muss mit demselben Algorithmus berechnet werden — sonst funktioniert Smart-Merge nicht. Beim R2-Port-Pass testen, dass cardContentHash({type,fields}) aus @cards/domain byte-identisch ist mit dem alten cards-server/src/lib/hash.ts. Wenn nicht: alten Code anpassen, nicht @cards/domain brechen.