Revises the wardrobe plan's space-scope decision from "only personal" to the full matrix — brand has merch, clubs have Trikots, families have shared kids' wardrobes, teams have costumes/uniforms, practices have Dresscode items. All six space types get wardrobe in the allowlist; garments + outfits are stamped with spaceId/authorId/ visibility like tags/scenes/agents (post Phase 2c). Adds a sixth decision block (Space-scoped catalog, user-scoped Try-On subject): the catalog lives in its space, but Try-On references are always the *calling user's* meImages — one human, one identity, brought into every space. A brand team member trying on merch sees themselves wearing it; a club member trying on a Trikot sees themselves wearing it; natural and correct. The single edge case is family spaces where a parent might want "try on kid's shirt" — the plan punts that explicitly. The catalog side (adding items, composing outfits) works unrestricted; Try-On shows a hint that it renders the calling user. If real demand shows up later, a separate plan can introduce per-space subject references (spaceMembers[].faceMediaId or similar) — today not speculating. Membership gating falls out of the existing scopedForModule/ mana-sync-RLS stack; no extra code in wardrobe. M1 checklist updated: wardrobe is NOT in USER_LEVEL_TABLES, queries go through scopedForModule, allowlist entry covers all six types. M4 checklist gains the "in non-personal spaces show the subject hint" item. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
23 KiB
Wardrobe — Module Plan
Status (2026-04-23)
Greenfield. Das Fundament (meImages + reference-based image generation) ist komplett verfügbar — siehe docs/plans/me-images-and-reference-generation.md Stand M1-M5. Dieses Modul konsumiert es.
Ziel
Ein Nutzer pflegt seinen digitalen Kleiderschrank: einzelne Kleidungsstücke und Accessoires (Hemd, Hose, Schuhe, Brille, Uhr) mit Foto + Metadaten. Er kombiniert sie zu Outfits und nutzt KI, um sich selbst in dem Outfit zu visualisieren — als Anprobe ohne Spiegel oder als Vorschau vor dem Kauf.
Kernfragen, die dieser Plan beantwortet:
- Wie bilden wir Kleidungsstücke + Outfits im Datenmodell ab?
- Wie fließen sie in die KI-Generierung (me-images plus garment-photos)?
- Welche Mindest-UI braucht es, damit das Feature tragfähig ist?
- Wie machen wir Wardrobe-AI (Outfit-Vorschläge auf Basis von Kontext) möglich, ohne das Modul zu überladen?
Abgrenzung
- Kein generisches Fashion-Catalog / Shopping: Dieses Modul verwaltet, was dem Nutzer gehört, nicht was er kaufen könnte. Wishlist lebt weiter in
wishes. - Kein Ersatz für
inventory:inventoryist für physische Gegenstände mit Seriennummer/Garantie (Elektronik, Möbel). Kleidung hat eigene Primitive (Größe, Farbe, Kategorie, Tragevorschriften) — und eigene Use-Cases (Outfit-Komposition, Try-On). Ein Nutzer könnte seine 300€-Jacke in beiden führen — Wardrobe für Outfit-Zwecke, Inventory für Versicherungs-Zwecke. Wir doppeln den Datensatz bewusst nicht zusammen. - Kein Stylist-Coaching / Body-Shaming: Die AI schlägt Kombinationen basierend auf des Nutzers eigenem Kleiderschrank vor, nicht auf abstraktem Stil-Urteil. Kein "Du solltest…".
- Kein Online-Kauf: Wishlist→Affiliate ist ein separater Track. Wardrobe ist Besitz.
- Cross-Link zu
picture: Try-On-Ergebnisse landen in der Picture-Galerie (picture.images) wie jede andere Generierung.LocalOutfit.lastTryOnImageIdpointet dorthin. - Cross-Link zu
me-images: Wardrobe liestuseImageByPrimary('face-ref')+useImageByPrimary('body-ref')um den Nutzer zu visualisieren. Ohne primary face+body → Try-On nicht verfügbar.
Entscheidungen
1. Ein Modul, zwei Tabellen
Das Repo hat Präzedenz für ein einzelnes Modul mit mehreren Tabellen (siehe library mit kind-discriminator, invoices mit clients/settings). Wardrobe ist dasselbe Muster:
wardrobeGarments— einzelne Kleidungsstücke / AccessoireswardrobeOutfits— Zusammenstellungen (referenzieren garmentIds)
Kein separates wardrobeTryOns-Table: Try-On-Ergebnisse sind normale picture.images mit generationMode='reference' und einer Rück-Referenz outfitId. Warum kein drittes Table? Ein Try-On ist ein "snapshot of an outfit at a moment" — die einzigen neuen Daten sind outfitId-Rückreferenz + Pose-/Prompt-Kontext. Das passt als optionale Felder auf LocalImage (pattern wie referenceImageIds).
2. Garment = Foto + Kategorie, nicht Produkt-DB
Ein Garment ist für uns im MVP:
- ein Foto (mana-media-Upload, wie alles andere)
- eine Kategorie aus geschlossener Liste (top/bottom/shoes/accessory/…)
- paar freie Text-Felder (Name, Marke, Farbe, Notizen)
Wir bauen keine Produkt-Datenbank, keine Barcode-Scanning, keinen Marken-Katalog, kein EAN-Lookup. Das sind alles interessante Features, die das Modul nicht tragen muss, um nützlich zu sein. Eine einfache handy-gemachte Foto + Name-Tipperei reicht für den 80%-Fall.
3. Try-On ist ein Picture-Modul-Ergebnis
Ein Try-On-Call geht über den vorhandenen POST /api/v1/picture/generate-with-reference-Endpoint (M3 des me-images-Plans), mit einer erweiterten Referenz-Liste:
referenceMediaIds = [primaryFaceMediaId, primaryBodyMediaId, ...outfit.garmentIds.map(g => g.primaryMediaId)]
prompt = "Porträt von mir in diesem Outfit, realistisch, freundliches Lächeln"
Problem: M3 cappt bei 4 Referenzen. Ein Outfit mit 3 Kleidungsstücken + Face + Body = 5 Refs. Wardrobe M1 muss den Cap im Endpoint anheben (→ 8). OpenAI erlaubt bis 16, 8 ist ein vernünftiger Kompromiss zwischen Cost-Schutz und tatsächlichem Bedarf.
4. Outfit-Vorschläge sind ein Mission-Flow, kein Modul-Primitiv
"Mana, schlag mir ein Outfit für die Hochzeit am Samstag vor" ist eine natürliche Agent-Aufgabe, keine vordefinierte Wardrobe-Funktion. Der Persona-Runner (siehe Memory: mana-persona-runner auf :3070) kann die neuen MCP-Tools konsumieren:
wardrobe.listGarments({category?, tags?})— was besitze ichwardrobe.listOutfits({occasion?})— welche Kombinationen habe ich schonwardrobe.tryOn({outfitId, prompt?})— visualisiere mich drinwardrobe.createOutfit({name, garmentIds, ...})— speichere eine neue Kombi
Die Persona plant dann frei: "Schau Wetter + Calendar → scanne wardrobeGarments → schlage 3 Outfits vor → frag user welches ihm gefällt → tryOn → speichere das gewählte als namens 'Hochzeit April'". Das ist keine Wardrobe-Logik, das ist Agent-Composition.
Wir bauen in diesem Modul keinen eigenen "Suggester" — das wäre ein redundantes Regel-System neben dem Persona-Runner.
5. Brillen / Accessoires brauchen nur Face-Ref
Für Kategorien accessory, glasses, hat, jewelry (Hals/Ohren) ist primaryFullbody nicht nötig — der Try-On läuft nur mit primaryFace + Garment-Photo. Das spart OpenAI-Credits und schärft die Ergebnisse (kein Ganzkörper-Rendering, das die Brille verkleinert). Die UI bietet für diese Kategorien einen "Brillen-Try-On"-Preset an (face-only prompt, 1024×1024 statt Portrait).
6. Space-scoped Katalog, user-scoped Try-On-Subject
Der Kleiderschrank selbst (wardrobeGarments + wardrobeOutfits) ist space-scoped: derselbe Mechanismus den tags/scenes/agents/missions/kontextDoc nach Phase 2c nutzen (spaceId, authorId, visibility per Hook gestempelt, Queries über scopedForModule<>). Das deckt sämtliche realen Use-Cases ab:
- personal: der eigene Kleiderschrank
- brand: Merchandise (T-Shirts, Caps, Zip-Hoodies) einer Marke — der Brand-Space ist gemeinsamer Pflegeort für alle Team-Mitglieder
- club: Trikots, Vereinsbekleidung
- family: Kinder-Kleiderschrank, gemeinsam von beiden Elternteilen gepflegt
- team: Bühnenkostüme, Uniformen, Produktions-Wardrobe
- practice: Praxis-Kittel, Dresscode-Items
Alle sechs Space-Typen bekommen wardrobe in die Allowlist.
Aber: Try-On-Referenzen (meImages) bleiben user-scoped — ein Mensch hat eine Identität, die er in jeden Space mitbringt. Konsequenz: wer in einem Brand-Space ein Merch-Hemd "anprobiert", sieht sich selbst im Hemd, nicht die Marke oder einen Avatar der Marke. Ein Vereins-Mitglied das in einem Club-Space auf "Trikot anprobieren" klickt, sieht sich selbst im Trikot — auch wenn der Katalog dem Verein gehört. Das deckt den intuitiven Fall ab ("wie sehe ich in dem Vereinstrikot aus") ohne dass wir ein zweites Subject-Konzept pro Space aufmachen.
Der einzige nicht-offensichtliche Fall ist family: Eltern pflegen den Kleiderschrank des Kindes, aber meImages eines Kindes existiert nicht (das Kind hat keinen eigenen Account). Try-On würde das Elternteil ins Kinder-Shirt rendern — absurd und unbrauchbar. Für family-Wardrobe machen wir zwei Dinge:
- Der Katalog-Teil (Garments + Outfits ansehen, komponieren, neu-eintragen) funktioniert ohne Einschränkung — das ist der Hauptwert für Familien.
- Der Try-On-Button bekommt einen Hinweis "Try-On in Familien-Spaces ist auf deine eigenen Bilder angewiesen — ein Bild wie 'so sähe das an meinem Kind aus' gibt es hier nicht."
Falls später konkreter Bedarf für "Try-On auf Familienmitglied" aufkommt, ist das ein separater Plan (neues Konzept spaceMembers[].faceMediaId oder ähnliches). Heute nicht spekulieren.
Membership-Gating: fällt automatisch aus dem Space-Foundation-Stack — scopedForModule<> filtert bereits auf aktive Space-Membership, mana-sync RLS cross-checked auf PostgreSQL-Ebene. Kein extra Code in Wardrobe.
Architektur-Überblick
┌─ Client (SvelteKit) ────────────────────────────────────┐
│ /wardrobe │
│ Grid-View: alle Garments, filter by category │
│ Detail: Garment oder Outfit, Try-On-Button │
│ /wardrobe/compose/[outfitId] │
│ Drag-drop Outfit-Builder │
│ Dexie: wardrobeGarments, wardrobeOutfits │
└──────┬──────────────────────────────────────────────────┘
│ mana-sync (encrypted name/notes/description)
▼
┌─ Try-On-Flow (reuses M3 endpoint) ──────────────────────┐
│ POST /api/v1/picture/generate-with-reference │
│ referenceMediaIds = [face, body, ...garments] │
│ prompt = composed from outfit + occasion hint │
│ Result → picture.images with outfitId back-ref │
└─────────────────────────────────────────────────────────┘
┌─ MCP / Agent tools ─────────────────────────────────────┐
│ wardrobe.listGarments (read) │
│ wardrobe.listOutfits (read) │
│ wardrobe.createOutfit (write) │
│ wardrobe.tryOn (write — consumes credits) │
│ wardrobe.addGarment (write, multipart upload) │
└─────────────────────────────────────────────────────────┘
Datenmodell
LocalWardrobeGarment
export type GarmentCategory =
| 'top' // Hemd, T-Shirt, Bluse, Pullover
| 'bottom' // Hose, Rock, Shorts
| 'dress' // Kleid, Anzug-Einteiler
| 'outerwear' // Jacke, Mantel
| 'shoes'
| 'accessory' // Schal, Gürtel, Tuch
| 'glasses' // Brille, Sonnenbrille
| 'jewelry' // Kette, Ring, Uhr, Ohrring
| 'hat'
| 'bag'
| 'other';
export interface LocalWardrobeGarment extends BaseRecord {
id: string;
name: string; // "Blau-weiß gestreiftes Hemd"
category: GarmentCategory;
mediaIds: string[]; // ≥1, first entry is the primary photo
brand?: string | null;
color?: string | null; // freeform — "navy", "hellgrau", "#2a4d6e"
size?: string | null; // freeform — "M", "42", "US 10"
material?: string | null;
tags: string[]; // "formal", "summer", "favorite", "needs-ironing"
notes?: string | null;
purchasedAt?: string | null;
priceCents?: number | null;
currency?: string | null; // ISO
isArchived?: boolean; // nicht mehr tragbar / weggegeben
wearCount?: number; // optional — zählt beim Markieren als "heute getragen"
lastWornAt?: string | null;
}
Encryption-Registry-Eintrag: ['name', 'brand', 'color', 'size', 'material', 'tags', 'notes']. Kategorie, IDs, Zähler, Dates bleiben plaintext.
LocalWardrobeOutfit
export interface OutfitTryOn {
imageId: string; // points at picture.images
createdAt: string;
prompt: string;
model: string;
}
export interface LocalWardrobeOutfit extends BaseRecord {
id: string;
name: string; // "Bürooutfit Juni"
description?: string | null;
garmentIds: string[]; // refs zu LocalWardrobeGarment
occasion?: string | null; // 'work', 'casual', 'formal', 'workout', 'date', 'sleep'
season?: string[]; // ['spring', 'summer']
tags: string[];
isFavorite?: boolean;
isArchived?: boolean;
/**
* Most recent try-on (snapshot pointer). Full history lives in picture.images
* filtered by outfitId, so the UI can show a chronological strip.
*/
lastTryOn?: OutfitTryOn | null;
lastWornAt?: string | null;
}
Encryption: ['name', 'description', 'tags', 'occasion']. garmentIds, season-enum, booleans, lastTryOn-pointer plaintext.
Erweiterung auf picture.images
Ein neues optionales Feld:
// apps/mana/apps/web/src/lib/modules/picture/types.ts
interface LocalImage {
// ... bestehend
wardrobeOutfitId?: string | null; // Rück-Referenz für "alle Try-Ons dieses Outfits"
}
Plaintext (ID-Feld), kein Registry-Change.
picture/generate-with-reference-Endpoint Cap anheben
MAX_REFERENCE_IMAGES in apps/api/src/modules/picture/routes.ts von 4 auf 8. Begründung + Cost-Kalkül im Kommentar, identische Validierung.
Modul-Struktur
apps/mana/apps/web/src/lib/modules/wardrobe/
├── types.ts # GarmentCategory, LocalWardrobeGarment, LocalWardrobeOutfit, OutfitTryOn
├── collections.ts # wardrobeGarmentsTable + wardrobeOutfitsTable
├── queries.ts # useAllGarments, useGarmentsByCategory, useOutfitById, useTryOnsForOutfit
├── module.config.ts # { appId: 'wardrobe', tables: [...] }
├── stores/
│ ├── garments.svelte.ts # createGarment, updateGarment, bumpWear, archive, delete
│ └── outfits.svelte.ts # createOutfit, addGarment, removeGarment, setLastTryOn, delete
├── api/
│ ├── upload.ts # uploadGarmentPhoto → POST /api/v1/wardrobe/garments/upload (app='wardrobe')
│ └── try-on.ts # runTryOn({outfit, prompt}) — wraps /picture/generate-with-reference
├── components/
│ ├── GarmentCard.svelte # Grid tile
│ ├── GarmentForm.svelte # Create/edit Sheet
│ ├── GarmentUploadZone.svelte
│ ├── OutfitCard.svelte
│ ├── OutfitComposer.svelte # Drag-drop Auswahl + Preview
│ ├── CategoryTabs.svelte
│ └── TryOnButton.svelte # Triggert runTryOn; zeigt Credits
├── views/
│ ├── GridView.svelte # Default: Kategorien + Garments
│ ├── OutfitsView.svelte # Alle Outfits
│ ├── DetailGarmentView.svelte
│ └── DetailOutfitView.svelte # zeigt Try-On-History
├── ListView.svelte # Modul-Root mit Tabs Garments/Outfits
├── constants.ts # CATEGORY_LABELS, SEASON_LABELS, OCCASION_LABELS
└── index.ts
Route-Seiten:
apps/mana/apps/web/src/routes/(app)/wardrobe/
├── +page.svelte # → ListView
├── garment/[id]/+page.svelte # → DetailGarmentView
├── outfit/[id]/+page.svelte # → DetailOutfitView
└── compose/[[outfitId]]/+page.svelte # OutfitComposer (new or edit)
Backend
Ein neuer thin Upload-Endpoint:
POST /api/v1/wardrobe/garments/upload
wrappt uploadImageToMedia({ app: 'wardrobe', userId }). Pattern 1:1 wie /api/v1/profile/me-images/upload — drei Zeilen Unterschied (nur der app-String).
Ein neues apps/api-Module-Verzeichnis apps/api/src/modules/wardrobe/routes.ts + Route-Registrierung in apps/api/src/index.ts.
Keine neue Try-On-Route: Die Client-seitige runTryOn() ruft direkt den existierenden /api/v1/picture/generate-with-reference-Endpoint — der kennt keinen Wardrobe-Kontext, bekommt nur die mediaIds + prompt. Nach Erfolg schreibt der Client die wardrobeOutfitId-Rück-Referenz auf die entstandene picture.images-Zeile.
Cap auf 8 anheben: trivialer Einzeiler in picture/routes.ts. Credit-Berechnung bleibt identisch (pro Output-Bild, nicht pro Referenz).
MCP-Tools (packages/mana-tool-registry/src/modules/wardrobe.ts)
Vier Tools, alle user-space. Pattern ist 1:1 an me.ts aus M5 angelehnt:
wardrobe.listGarments({category?, tags?, limit?})— read. Pullt via mana-syncapp='wardrobe', entschlüsseltname+brand+notes+tags. Filter client-side.wardrobe.listOutfits({occasion?, favoriteOnly?})— read. Gleicher Pull-Pattern fürwardrobeOutfits.wardrobe.createOutfit({name, garmentIds, occasion?, tags?})— write. Validiert dass alle garmentIds existieren und dem User gehören. Schreibt viapushInsert.wardrobe.tryOn({outfitId, prompt?, accessoryOnly?})— write (kostet Credits). Liest das Outfit, holt primary face + body + garment mediaIds, composed default-prompt falls keiner mitkommt, ruft die apps/api-Generate-Route. Response propagiert zurück, inklusive Credit-Kosten.
Milestones
-
M1 — Datenschicht & Backend-Cap (~1–1.5 Tage)
- Dexie v39:
wardrobeGarments+wardrobeOutfitsmit Indices (space-scoped, also Compound-Index auf[spaceId+...]für die hot path Queries) - Types + Encryption-Registry + Collections + Queries (via
scopedForModule<>, nicht inUSER_LEVEL_TABLES— volle Space-Scope-Behandlung) - Stores (garments, outfits)
module.config.tsregistriertappId='wardrobe'wardrobein alle sechs Space-Typen der Allowlist (personal,brand,club,family,team,practice)MAX_REFERENCE_IMAGESCap auf 8 (apps/api/src/modules/picture/routes.ts) mit Comment + ClientCap im Generator- Neuer
POST /api/v1/wardrobe/garments/upload-Endpoint + Route-Registrierung wardrobeOutfitId-Feld aufLocalImage+toImage-Converter
- Dexie v39:
-
M2 — Garments-Grundlayer (~1–1.5 Tage)
- Route
/wardrobemitRoutePage CategoryTabs,GarmentCard,GarmentUploadZone,GarmentForm- Multi-File-Upload pro Kategorie (wie me-images)
- Detailseite
/wardrobe/garment/[id]— Foto, Metadaten, "heute getragen"-Button (incrementiertwearCount) - Archive / Delete / Edit flows
- Route
-
M3 — Outfits-Composer (~1–1.5 Tage)
- Route
/wardrobe/compose/[[outfitId]] - Drag-drop-Leiste mit Garments (nach Kategorie gruppiert)
- Outfit-Preview-Kachel rechts (Stapel der Garment-Thumbnails)
- Create/Edit an dieselbe Route,
[[outfitId]]optional - Detailseite
/wardrobe/outfit/[id] OutfitsViewals zweiter Tab im Root
- Route
-
M4 — Try-On-Integration (~1 Tag)
runTryOn(outfit, prompt?)inapi/try-on.ts— composed die reference-Liste aus des Nutzers eigenenuseImageByPrimary('face-ref' | 'body-ref')+ garment-mediaIds (auch in non-personal Spaces), ruft/generate-with-referenceaccessoryOnly-Preset fürglasses/jewelry/hat— nur face-ref, quadratisches FormatTryOnButton.svelteauf DetailOutfitView + auf DetailGarmentView (mit impliziten "Solo-Outfit")- Nach Erfolg:
picture.images.wardrobeOutfitIdsetzen +lastTryOn-Snapshot aufs Outfit - Empty-State wenn
primaryFaceoderprimaryFullbodyfehlen → Link zu/profile/me-images - In Non-Personal-Spaces (
brand/club/family/team/practice): Hinweis "Du siehst dich selbst im Outfit — Try-On nutzt deine persönlichen Referenzbilder, nicht die des Spaces" (Subject ist user-global, siehe Entscheidung #6) - Try-On-History als horizontaler Strip in DetailOutfitView
-
M5 — MCP-Tools (~0.5 Tag)
packages/mana-tool-registry/src/modules/wardrobe.tsmit den 4 Tools'wardrobe'zumModuleId-UnionregisterWardrobeTools()inregisterAllModules()
-
M6 — Persona-Templates (~0.5 Tag, optional)
- Persona-Template "Stil-Coach": auto-Policy für
wardrobe.list*+me.listReferenceImages, propose-Policy fürwardrobe.createOutfit+wardrobe.tryOn - Seed-Prompt: "Du bist der persönliche Stil-Coach. Schlage Outfits aus dem vorhandenen Kleiderschrank vor, basierend auf Kontext (Kalender-Event, Wetter, Nutzer-Stimmung). Nie kritisch, nie body-urteilend."
- Template-Eintrag unter
/agents/templates
- Persona-Template "Stil-Coach": auto-Policy für
-
M7 — "Heute trage ich…"-Logging (~0.5 Tag, optional)
- In GarmentCard + OutfitCard ein schnelles "heute getragen"-Flag, setzt
lastWornAt = today+ bumptwearCount - Stats-Widget: "Am häufigsten getragen", "Lange nicht mehr angehabt"
- In GarmentCard + OutfitCard ein schnelles "heute getragen"-Flag, setzt
-
M8 — Kontext-basierte Suggestion (optional, mehrere Tage)
- mana-ai Mission-Template "Outfit des Tages": liest calendar + wetter + wardrobe, erzeugt 3 Vorschläge als Proposals
- Im Workbench als Widget "Heute anziehen" (Card)
Verschlüsselung
Alle user-typed Felder verschlüsselt (siehe Registry-Einträge oben). Bild-Blobs selbst bleiben in mana-media mit Owner-RLS — exakt wie bei meImages und picture.
Für Zero-Knowledge-Nutzer gilt dasselbe wie bei me-images: ctx.getMasterKey() in MCP-Tools throwet, die Tools fallen stumm aus. Client-seitige Blob-Verschlüsselung ist Teil von M8 des me-images-Plans und greift dann auch für Wardrobe-Fotos, wenn sie über denselben uploadImageToMedia-Pfad gehen.
Cross-Modul-Impact
| Modul | Impact |
|---|---|
picture |
Neues optionales Feld wardrobeOutfitId. Cap auf generate-with-reference von 4 → 8. |
me-images |
Nichts — Wardrobe konsumiert nur useImageByPrimary. |
profile |
Nichts. |
shared-branding |
Neuer App-Eintrag wardrobe (Icon, Farbe, Tier — vermutlich beta). |
shared-types/spaces.ts |
wardrobe in alle sechs Space-Typen der Allowlist: personal, brand, club, family, team, practice (Entscheidung #6). |
Offene Fragen (vor M1 klären)
- Photo-Quality-Anforderung an Garments: reicht ein handy-Snap "Shirt auf dem Bett liegend", oder müssen wir flat-lay-erzwingen? → Empfehlung: akzeptieren was der Nutzer liefert, die gpt-image-Modelle sind robust. Falls M4-Ergebnisse systematisch schlecht werden, später ein "Bild-Guide" in die UI schreiben.
- Outfit ohne Foto hochladen: darf ein Nutzer ein Outfit komponieren, bei dem ein Garment-Bild fehlt? → Empfehlung: ja, aber Try-On ist deaktiviert, bis alle Garments mindestens ein Foto haben. UX-Hinweis im Composer.
- Multi-Foto pro Garment: sinnvoll (front / back / Detail), aber ein Primary-Foto reicht für Try-On. →
mediaIds: string[]mitmediaIds[0]als Primary. UI macht das in M2 nicht sichtbar, kommt in M7 als Erweiterung. - Kategorie-Detection via AI beim Upload: "wir laden dein Foto hoch und schlagen die Kategorie vor" — interessant, aber eine nicht-triviale extra Inferenz. → NICHT M1-Scope. Später als optional Enrichment-Step.
Space-Scope→ entschieden: alle sechs Space-Typen (siehe Entscheidung #6). Brand hat Merch, Clubs haben Trikots, Families gemeinsame Kleiderschränke, Teams Kostüme, Practices Dresscode. Try-On-Subject bleibt user-global — das deckt auch non-personal-Spaces sauber ab, mit Ausnahme von family (Kinder-Shirts "auf Kind rendern" ist out-of-scope; Katalog-Pflege funktioniert trotzdem).- Accessoire-Try-On-Prompt-Template: vorformatierter Prompt für Brillen ("Portrait frontal, freundliche Mimik, studio-Licht, ohne Hintergrundstörung, brillen-fokus") vs. freier Prompt? → Default mit Preset + der Nutzer kann überschreiben. Preset-Variante als MVP.
Verweise
- me-images Fundament:
docs/plans/me-images-and-reference-generation.md - bestehender Edit-Endpoint:
apps/api/src/modules/picture/routes.ts:248-... - Tool-Registry me-Modul als Pattern:
packages/mana-tool-registry/src/modules/me.ts - Library-Plan als Struktur-Analogon:
docs/plans/library-module.md - Spaces-Modul-Allowlist:
packages/shared-types/src/spaces.ts:63-184