diff --git a/src/engine/v2/buildProfile.ts b/src/engine/v2/buildProfile.ts index 811dbd0bd..8187dad62 100644 --- a/src/engine/v2/buildProfile.ts +++ b/src/engine/v2/buildProfile.ts @@ -16,10 +16,10 @@ import type { const QUIZ_STYLE_TO_ARCHETYPE: Record = { minimalist: { MINIMALIST: 1.0 }, - classic: { CLASSIC: 0.7, BUSINESS: 0.2, MINIMALIST: 0.1 }, + classic: { CLASSIC: 1.0 }, streetwear: { STREETWEAR: 1.0 }, - 'smart-casual': { SMART_CASUAL: 1.0 }, - smart_casual: { SMART_CASUAL: 1.0 }, + 'smart-casual': { SMART_CASUAL: 0.7, CLASSIC: 0.3 }, + smart_casual: { SMART_CASUAL: 0.7, CLASSIC: 0.3 }, athletic: { ATHLETIC: 1.0 }, sporty: { ATHLETIC: 1.0 }, rugged: { SMART_CASUAL: 0.5, STREETWEAR: 0.5 }, @@ -87,10 +87,10 @@ const DEFAULT_ARCHETYPES: ArchetypeWeights = { }; const OCCASION_ARCHETYPE_BIAS: Record = { - work: { BUSINESS: 0.25, CLASSIC: 0.2, SMART_CASUAL: 0.15, MINIMALIST: 0.1 }, + work: { BUSINESS: 0.15, CLASSIC: 0.25, SMART_CASUAL: 0.15, MINIMALIST: 0.1 }, formal: { BUSINESS: 0.4, CLASSIC: 0.2, MINIMALIST: 0.05 }, - casual: { SMART_CASUAL: 0.2, CLASSIC: 0.1 }, - date: { CLASSIC: 0.15, SMART_CASUAL: 0.15, MINIMALIST: 0.1 }, + casual: { SMART_CASUAL: 0.2, CLASSIC: 0.1, AVANT_GARDE: 0.1, STREETWEAR: 0.1 }, + date: { CLASSIC: 0.15, SMART_CASUAL: 0.15, MINIMALIST: 0.1, AVANT_GARDE: 0.1 }, travel: { SMART_CASUAL: 0.2, MINIMALIST: 0.15 }, sport: { ATHLETIC: 0.5 }, }; diff --git a/src/engine/v2/coherence.ts b/src/engine/v2/coherence.ts index 753f0d563..a97e652d9 100644 --- a/src/engine/v2/coherence.ts +++ b/src/engine/v2/coherence.ts @@ -11,7 +11,49 @@ export interface CoherenceScores { reasons: string[]; } -export function evaluateCoherence(products: ScoredProduct[]): CoherenceScores { +interface FormalityThresholds { + hardPenalty: number; + midPenalty: number; + softPenalty: number; + mismatchTag: number; + coherentTag: number; +} + +function formalityThresholdsFor( + profile?: UserStyleProfile +): FormalityThresholds { + const primary = profile?.primaryArchetype; + if (primary === 'AVANT_GARDE') { + return { + hardPenalty: 0.85, + midPenalty: 0.8, + softPenalty: 0.7, + mismatchTag: 0.7, + coherentTag: 0.35, + }; + } + if (primary === 'SMART_CASUAL') { + return { + hardPenalty: 0.7, + midPenalty: 0.65, + softPenalty: 0.55, + mismatchTag: 0.55, + coherentTag: 0.3, + }; + } + return { + hardPenalty: 0.55, + midPenalty: 0.4, + softPenalty: 0.25, + mismatchTag: 0.45, + coherentTag: 0.25, + }; +} + +export function evaluateCoherence( + products: ScoredProduct[], + profile?: UserStyleProfile +): CoherenceScores { const reasons: string[] = []; if (products.length === 0) { return { @@ -33,16 +75,17 @@ export function evaluateCoherence(products: ScoredProduct[]): CoherenceScores { if (colorHarmony > 0.8) reasons.push('color_harmony_strong'); else if (colorHarmony < 0.45) reasons.push('color_harmony_weak'); + const thresholds = formalityThresholdsFor(profile); const formalities = products.map((p) => p.formality); const minF = Math.min(...formalities); const maxF = Math.max(...formalities); const spread = maxF - minF; let formalitySpread = 1; - if (spread > 0.55) formalitySpread = 0.35; - else if (spread > 0.4) formalitySpread = 0.6; - else if (spread > 0.25) formalitySpread = 0.85; - if (spread < 0.25) reasons.push('formality_coherent'); - if (spread > 0.45) reasons.push('formality_mismatch'); + if (spread > thresholds.hardPenalty) formalitySpread = 0.35; + else if (spread > thresholds.midPenalty) formalitySpread = 0.6; + else if (spread > thresholds.softPenalty) formalitySpread = 0.85; + if (spread < thresholds.coherentTag) reasons.push('formality_coherent'); + if (spread > thresholds.mismatchTag) reasons.push('formality_mismatch'); const archetypeTotals: Record = {}; for (const p of products) { diff --git a/src/engine/v2/composer.ts b/src/engine/v2/composer.ts index 340377203..db07c1ad5 100644 --- a/src/engine/v2/composer.ts +++ b/src/engine/v2/composer.ts @@ -132,9 +132,10 @@ function assembleReasons( } function scoreComposition( - products: ScoredProduct[] + products: ScoredProduct[], + profile: UserStyleProfile ): { coherence: ReturnType; score: number } { - const coherence = evaluateCoherence(products); + const coherence = evaluateCoherence(products, profile); const productAvg = products.reduce((acc, p) => acc + p.score, 0) / Math.max(1, products.length); const baseScore = productAvg * 0.55 + coherence.combined * 0.45; @@ -274,7 +275,7 @@ function composeForOccasion( if (seenSignatures.has(signature)) continue; seenSignatures.add(signature); - const { coherence, score } = scoreComposition(products); + const { coherence, score } = scoreComposition(products, profile); if (score < 0.35) continue; candidates.push({