feat(domain): F004 – expand domain model layer

- need.ts: add SizeRange, MustHaveCriterion, WeightedPreference types; extend Need with desiredLocation, sizeRange, mustHaveCriteria, weightedPreferences, status
- futureSignal.ts: add SignalEvidence type; extend FutureSignal with title, evidence, matchabilityScore, reviewStatus
- aiOutput.ts: new AIOutput entity with type, inputHash, outputJson, provider/model/promptVersion/schemaVersion, reviewStatus
- activityEvent.ts: new ActivityEvent entity with actorId, action, entityType, entityId, timestamp, metadata
- unifiedResult.ts: discriminated union (VerifiedPortfolioResult | ExternalMarketResult | FutureAvailabilityResult)
- index.ts: export all three new files
- Properties.tsx: add LIGHT_INDUSTRIAL + UNKNOWN cases to exhaustive switches

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-15 10:58:27 +02:00
parent 3b45b0c121
commit 0b874865ba
10 changed files with 399 additions and 40 deletions
+71 -15
View File
@@ -1,52 +1,108 @@
import type { MatchStrength, RiskLevel } from './enums'
import type { MatchStrength, MatchStatus, RiskLevel, ResultType, ConfidenceLevel } from './enums'
// ── Score Building Blocks ─────────────────────────────────────────────────────
export interface ScoreBreakdown {
hardMatchScore: number
softFactorScore: number
confidenceModifier: number
dataQualityModifier: number
totalScore: number
hardMatchScore: number // 0100 hard criteria score
softFactorScore: number // 0100 soft factors score
confidenceModifier: number // -20 to +5 adjustment
dataQualityModifier: number // -15 to 0 adjustment
totalScore: number // final 0100
}
export interface ScoreFactor {
criterion: string
weight: number
score: number
contribution: number
weight: number // 01 relative weight
score: number // 0100
contribution: number // weighted points added to total
explanation: string
}
export interface Tradeoff {
// ── Explainability Types ──────────────────────────────────────────────────────
/** Canonical tradeoff type (F004) */
export interface TradeOff {
criterion: string
concern: string
severity: 'LOW' | 'MEDIUM' | 'HIGH'
mitigation?: string
impactOnScore?: number
}
/** @deprecated Use TradeOff — kept for backward compatibility */
export type Tradeoff = TradeOff
export interface Risk {
category: string // e.g. "Datenverfügbarkeit", "Standortrisiko"
description: string
level: RiskLevel
mitigation?: string
}
export interface MissingDataItem {
field: string
importance: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'
description: string
impact: string // how it affects the match score
}
export interface NextBestAction {
label: string // e.g. "Besichtigung anfragen"
description?: string
priority: 'HIGH' | 'MEDIUM' | 'LOW'
actionType: 'CONTACT' | 'VERIFY' | 'REVIEW' | 'SHORTLIST' | 'COMPARE' | 'SCHEDULE'
externalUrl?: string
}
export interface AlternativeStrategy {
title: string
description: string
expectedScore?: number
reasoning?: string
}
// ── Match (core entity) ───────────────────────────────────────────────────────
export interface Match {
id: string
propertyId: string
needId: string
matchScore: number
// Result reference — works for all three result types
resultId?: string // preferred: generic result ID
resultType?: ResultType
propertyId: string // legacy alias for resultId (VERIFIED_PORTFOLIO)
// Scoring
matchScore: number // 0100
matchStrength: MatchStrength
scoreBreakdown: ScoreBreakdown
confidenceLevel: number // 01 numeric
confidenceLevelLabel?: ConfidenceLevel
dataConfidenceScore?: number // 01 data quality contribution
// Explainability
positiveFactors: ScoreFactor[]
negativeFactors: ScoreFactor[]
tradeoffs: Tradeoff[]
tradeoffs: TradeOff[]
tradeOffs?: TradeOff[] // alias for F004 naming convention
risks?: Risk[]
missingData?: MissingDataItem[]
nextBestActions?: NextBestAction[]
explainabilitySummary: string
confidenceLevel: number
explanation?: string // alias / longer form
// Risk
riskLevel: RiskLevel
uncertaintyIndicators: string[]
// Alternatives
alternativeStrategies?: AlternativeStrategy[]
// Review / lifecycle
status?: MatchStatus
isApproved?: boolean // legacy — prefer status
reviewedBy?: string
reviewedAt?: string
isApproved?: boolean
organizationId?: string
createdAt: string
updatedAt: string