Pull-Requests (Diff-Modell add/modify/remove, GitHub-style 3-way
merge in der DB-Transaktion):
- POST /decks/:slug/pull-requests (auth) — neuer PR mit
diff.{add,modify,remove}; previousContentHash für modify identifiziert
die zu ersetzende Karte by content-hash, type bleibt aus dem alten
Eintrag (modify ist field-only Replace)
- GET /decks/:slug/pull-requests (optional-auth) — Liste mit Status-
Filter (open/merged/closed/rejected)
- GET /pull-requests/:id (optional-auth) — Detail
- POST /pull-requests/:id/close (auth) — Author oder Deck-Owner
- POST /pull-requests/:id/reject (auth) — nur Deck-Owner; getrennt
von close, damit der PR-Author klares Feedback hat
- POST /pull-requests/:id/merge (auth) — nur Deck-Owner; baut neue
card-list aus latest version + diff (removes weglassen, modifies
fields-replace, adds anhängen mit re-counted ord), schreibt
publicDeckVersions + publicDeckCards atomar in einer Drizzle-
Transaction, bumpt latestVersionId und setzt PR auf merged.
Default-Semver-Bump: minor (1.0.0 → 1.1.0). Authorenüberschreibbar
via mergeNote/newSemver-Body-Felder.
Card-Discussions (Threads pro card_content_hash, überleben
Versions-Bumps solange Karten-Inhalt bleibt):
- POST /decks/:slug/cards/:hash/discussions (auth) — neuer Thread
oder Reply (parent_id muss in derselben card_content_hash-Gruppe
leben → 422 sonst)
- GET /cards/:hash/discussions (optional-auth) — Liste sichtbarer
Comments, hidden gefiltert
- GET /decks/:slug/discussions/counts (optional-auth) — Bulk-Count
pro card_content_hash für Deck-Übersicht (kein N+1)
- POST /discussions/:id/hide (auth) — Soft-Hide (Author oder Deck-
Owner); kein Delete, Audit-Trail bleibt
Helpers:
- lib/marketplace/semver.ts — bumpMinor, isSemver, semverGreater
(klein, ohne Range-Logik). Wird von PRs + später vom decks.ts
publish-Flow konsumiert.
Bug-Fix:
- routes/marketplace/fork.ts hatte r.use('*', authMiddleware) am
Anfang. An dem /api/v1/marketplace-Mount-Punkt fängt das Wildcard
alle nachfolgenden Router-Mounts (PRs, Discussions) → anonymer
GET /pull-requests wurde mit 401 abgelehnt. Refactor auf per-
route authMiddleware (Pattern wie in subscriptions.ts und
engagement.ts seit R3). Lessons learned dokumentiert in der
STATUS.md-Zeile.
Verifikation:
- type-check 0 errors
- 11 neue Semver-Tests, 89 gesamt grün
- E2E-Smoke gegen lokale cards-api durch:
· Cardecky publisht v1.0.0 (Apatheia, Eudaimonia, Logos)
· Till's PR: modify Eudaimonia-Back, remove Logos, add Tugendlehre
· Till's Merge-Versuch → 403 (deck_owner_only)
· Cardecky merged → v1.1.0 atomar, card_count=3, ord-Reihenfolge:
[Apatheia, Eudaimonia-mit-neuem-Back, Tugendlehre]
· Re-Merge → 409 (pr_already_merged)
· Till's Discussion-Thread + Cardecky-Reply mit parent_id
· Cross-Card-parent abgelehnt → 422
· Hide → Comment verschwindet aus Liste, total von 2 auf 1
· Bulk-Counts liefert {hash → 2}
· Smart-Merge-Pull v1.0.0→v1.1.0 wertet PR-Merge korrekt aus
(changed=2 via Eudaimonia + Logos↔Tugendlehre ord-Heuristik)
Verbleibend: R5 Frontend-Routes (/explore, /d/[slug], /u/[slug],
/me/{published,subscribed,forks}), R6 voller UI-E2E.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Routes (additiv unter /api/v1/marketplace/*):
- POST/GET /authors/me — eigenes Author-Profil anlegen/updaten/lesen
- GET /authors/:slug — public Profile-Lookup (banned-reason gestrippt)
- POST /decks — Deck-Init (Slug-Validation + Pflicht-Author-Profil +
CHECK auf paid + Pro-License)
- POST /decks/:slug/publish — Versions-Snapshot mit per-Karte
cardContentHash aus @cards/domain, per-Version-Hash, AI-Mod-Stub-Log,
atomarer latest_version_id-Bump in Drizzle-Transaction
- PATCH /decks/:slug — Metadaten-Update (Owner-Only)
- GET /decks/:slug — Public-Detail mit optional-auth-Middleware
Geport aus cards-decommission-base:services/cards-server/, mit
Greenfield-Anpassungen:
- Hashing über @cards/domain.cardContentHash (gemeinsame SoT
zwischen privatem cards.cards und marketplace.deck_cards), per-
Version-Hash als SHA-256 über sortierte Karten-Hashes mit Ord-Prefix
- AI-Moderation als R2-Stub (pass+rationale+model='stub'),
echte mana-llm-Anbindung in späterer Welle
- Auth-Middleware-Shape an Greenfield (userId/tier/authMode in
c.get(...) statt user-Object), optional-auth als Schwester für
anonymen Public-Read
- Hono-typing: outer Marketplace-Decks-Router ist Partial<AuthVars>
weil Public-GET kein JWT braucht; Auth-Subroute ist strict
Lese-Referenz:
- 3331 LOC altes cards-server-Code (routes, services, middleware,
lib) unter docs/marketplace/archive/code/ archiviert. Read-only,
nicht im Build-Path.
Verifikation:
- 16 neue Vitest-Tests (Slug + Version-Hash), 72 gesamt grün
- type-check 0 errors
- E2E-Smoke gegen lokale cards-api: Cardecky-Author + Deck
r2-stoische-ethik mit 3 Karten v1.0.0 (basic + basic + cloze),
per-Karten-Hashes geschrieben, ai_moderation_log-Row da, semver-409
+ paid-422-Errors verifiziert. Smoke-Daten danach aufgeräumt.
Verbleibend für R3+: Discovery (explore + search), Engagement (stars/
subscribe/fork), Smart-Merge mit FSRS-State-Erhalt; danach R4 PRs +
Card-Discussions, R5 Frontend-Routes.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TL;DR aktualisiert: 17 Commits, 94 Tests, Phase 9 läuft mit 6 Sprints
durch (9a Card-Edit, 9b Cloze-Editor, 9c Inbox-Banner, 9d Protocol-
Swap+npm.mana.how, 9e Account/DSGVO-Self-Service, 9f Statistik).
Architektur-Subtilität #3 von "lokaler Mirror" auf "Re-Export +
historischer Kontext" geschärft. Git-Historie + "Was als Nächstes"
auf den neuen Stand gezogen — Schwerpunkt jetzt i18n/A11y, Hint-
Anzeige, Media-Upload für Anki-Import.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TL;DR-Summary auf 11 Commits / 92 Tests / Cloze + Anki-Import
verschoben. Phasen-Tabelle: 8 ✅ mit 92 Tests grün. Architektur-
Subtilitäten ergänzt (#6 Cloze-N-Reviews, #7 Strategie-B-Ausnahme
für Anki-Parser, #8 Media-Drop, #9 sql-wasm.wasm-Pflege; alte #6
Decommission rutscht auf #10). MVP-Card-Type-Set in Punkt 6 der
festgenagelten Architektur-Entscheidungen aktualisiert. Subtilität
#2 (SubIndex-Granularität) verweist jetzt auf #6 für die text-
abhängige Cloze-Variante.
"Was als Nächstes ansteht"-Empfehlung neu sortiert: Card-Edit-Page
+ Pre-Flight-Abräumen (Verdaccio-Token liegt vor) statt Anki-Import
auf Platz 1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bisherige Doku war über CLAUDE.md, README.md, SMOKE_TEST.md,
LESSONS_FROM_MANA_MONOREPO.md plus mana/docs/playbooks/CARDS_GREENFIELD.md
zerstreut — eine fresh-AI-Session musste sich Status zusammenstückeln.
STATUS.md zentralisiert:
- TL;DR + Architektur-Topologie
- Architektur-Entscheidungen (festgenagelt)
- Phasen-Status-Tabelle (✅/🚧/⏸ pro Phase mit Verifikations-Hinweis)
- Lokal-Lauf-Anleitung (5 Schritte zu cards-api + cards-web im Browser)
- Verifizierte Endpoints-Liste
- Pre-Flight für Phase 2 + Live-Föderation
- Wichtige Pointer: Konventionen, Stack, Files, Cross-Repo-Doks
- Git-Historie (6 Commits)
- 6 Architektur-Subtilitäten, die nicht offensichtlich sind:
Reviews-plaintext, SubIndex-Granularität, Protocol-Mirror-TEMPORARY,
Inbox-Auto-Create, Dev-Auth-temporär, mana-monorepo-Decommission
- Onboarding-Sequenz (5-Min-Lese-Plan)
- Vorschläge für nächste Phasen
Cross-References:
- CLAUDE.md verweist als erstes auf STATUS.md
- README.md ebenso
- mana/docs/playbooks/CARDS_GREENFIELD.md (im Plattform-Repo) verweist
zurück auf cards/STATUS.md für aktuellen Stand — Playbook ist der
Plan, STATUS.md ist die Lage.
Konvention: bei Phasen-Wechsel STATUS.md aktualisieren, nicht den
Playbook (sonst Doku-Drift).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>