XOLIB SCORE — VOLLSTÄNDIGE IMPLEMENTIERUNG¶
Claude Code Prompt · Version 2.0 · März 2026¶
KONTEXT¶
Du arbeitest an Xolib, einer KI-nativen B2B SaaS-Plattform für Hausverwaltung. Stack: Next.js, TypeScript (strict), Prisma, PostgreSQL, OpenAI GPT-4o-mini, Tailwind CSS, Lucide React. UI-Regeln: Kein Emoji, nur Lucide-Icons, Dark Theme (#0f172a / #1e293b / #334155 / #6366f1), keine hardcodierten deutschen Strings (i18n Pflicht, 8 Sprachen). Lies zuerst CLAUDE.md und MEMORY.md bevor du anfängst.
AUFGABE¶
Implementiere den Xolib Score vollständig — von Datenbankschema bis UI. Der Xolib Score ist ein objektiver, datengetriebener Gebäude-Bewertungsindex von 0–100. Er ist das strategisch wichtigste Feature der Plattform — nicht als UI-Element, sondern als Daten-Asset und zukünftiger B2B-Umsatzstrom für Banken, Versicherungen und Investoren.
Arbeite die Phasen 1–6 sequenziell ab. Teste nach jeder Phase. Committe nach jeder Phase mit aussagekräftiger Message. Aktualisiere CLAUDE.md und MEMORY.md nach Abschluss.
ARCHITEKTUR-PRINZIPIEN — NIEMALS VERLETZEN¶
- Score NIEMALS im synchronen API-Request berechnen — immer async via Background Job oder Queue
- Jede Score-Berechnung wird in PropertyScoreHistory gespeichert — kein Silent Update
- Score ist NICHT manuell überschreibbar — er ist berechnet, nicht gesetzt
- Fehlende Daten senken NICHT den Score, sondern die Konfidenz (zwei unabhängige Dimensionen)
- Erklärbarkheit vor Präzision: lieber erklärbarer 78 als unerklärlicher 79
- Bank-API-Kompatibilität von Tag 1: Schema so designen dass externe API später ohne Breaking Change möglich
- i18n auf ALLEN neuen Komponenten und API-Responses — keine hardcodierten Strings
- Jedes Feld das für ML nützlich sein könnte wird gespeichert — mehr Daten ist immer besser
PHASE 1 — DATENBANKSCHEMA¶
Füge folgende Modelle zu schema.prisma hinzu:
PropertyScore¶
model PropertyScore {
id String @id @default(cuid())
tenantId String
propertyId String @unique
totalScore Float
scoreGrade String // 'A+' | 'A' | 'B' | 'C' | 'D' | 'F'
percentile Float?
substanzScore Float
technologieScore Float
ertragScore Float
complianceScore Float
instandhaltungScore Float
potentialScore Float
potentialGap Float
scoreDelta30d Float?
scoreDelta90d Float?
trendDirection String // 'improving' | 'stable' | 'declining'
riskFlags Json // [{flag, severity, category, detectedAt, titleKey, descriptionKey}]
criticalIssues Int @default(0)
calculatedAt DateTime @default(now())
dataCompleteness Float
confidenceScore Float
version Int @default(1)
tenant Tenant @relation(fields: [tenantId], references: [id])
property Property @relation(fields: [propertyId], references: [id])
history PropertyScoreHistory[]
factors ScoreFactor[]
@@index([tenantId, totalScore(sort: Desc)])
@@index([tenantId, scoreGrade])
@@index([propertyId])
}
PropertyScoreHistory¶
model PropertyScoreHistory {
id String @id @default(cuid())
tenantId String
propertyId String
scoreId String
totalScore Float
scoreGrade String
substanzScore Float
technologieScore Float
ertragScore Float
complianceScore Float
instandhaltungScore Float
potentialScore Float
changedFactors Json
changeReason String // 'scheduled_recalc' | 'data_update' | 'manual_trigger'
triggeredBy String?
// Hash-Chaining
scoreHash String
previousHash String?
chainValid Boolean @default(true)
chainValidatedAt DateTime?
externalAnchorTx String?
externalAnchorAt DateTime?
calculatedAt DateTime @default(now())
score PropertyScore @relation(fields: [scoreId], references: [id])
@@index([propertyId, calculatedAt(sort: Desc)])
@@index([tenantId, calculatedAt(sort: Desc)])
@@index([scoreHash])
}
ScoreFactor¶
model ScoreFactor {
id String @id @default(cuid())
scoreId String
propertyId String
category String // 'substanz' | 'technologie' | 'ertrag' | 'compliance' | 'instandhaltung'
factorKey String
factorLabelKey String // i18n key
rawValue Float
normalizedValue Float
weight Float
contribution Float
dataSource String
dataVersion String // 'v1_manual' | 'v2_auto' | 'v3_iot'
confidence Float // 0-100: Konfidenz dieses einzelnen Faktors
measuredAt DateTime
score PropertyScore @relation(fields: [scoreId], references: [id])
@@index([scoreId, category])
@@index([propertyId, factorKey])
}
EnergyReading¶
model EnergyReading {
id String @id @default(cuid())
tenantId String
propertyId String
unitId String?
period String // Format: 'YYYY-MM'
heatKwh Float?
coldWaterM3 Float?
hotWaterM3 Float?
electricityKwh Float?
source String // 'techem' | 'ista' | 'brunata' | 'manual' | 'smart_meter'
createdAt DateTime @default(now())
tenant Tenant @relation(fields: [tenantId], references: [id])
property Property @relation(fields: [propertyId], references: [id])
@@unique([propertyId, unitId, period, source])
@@index([propertyId, period])
}
WorkerRating¶
model WorkerRating {
id String @id @default(cuid())
tenantId String
ticketId String @unique
serviceProviderId String
propertyId String
stars Int // 1-5
comment String?
reworkRequired Boolean @default(false)
quotedAmount Float?
finalAmount Float?
ratedAt DateTime @default(now())
ratedByUserId String
tenant Tenant @relation(fields: [tenantId], references: [id])
property Property @relation(fields: [propertyId], references: [id])
@@index([propertyId, ratedAt(sort: Desc)])
@@index([serviceProviderId])
}
GlobalChainAnchor¶
model GlobalChainAnchor {
id String @id @default(cuid())
anchorDate DateTime @unique
chainTip String
totalScores Int
externalRef String?
createdAt DateTime @default(now())
}
Erweiterungen bestehender Modelle¶
Füge zum Property-Modell hinzu:
// Substanz
constructionYear Int?
lastRenovationYear Int?
roofRenovationYear Int?
windowsRenovationYear Int?
heatingYear Int?
// Technologie
energyCertificate String? // 'A+' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G'
heatingType String? // 'gas' | 'oil' | 'heat_pump' | 'district' | 'pellet' | 'electric'
hasFiber Boolean @default(false)
hasElevator Boolean @default(false)
hasSmartMeter Boolean @default(false)
hasEvCharging Boolean @default(false)
isBarrierfree String? // 'full' | 'partial' | 'none'
Führe nach allen Schema-Änderungen durch:
npx prisma migrate dev --name add_xolib_score
npx prisma generate
PHASE 2 — BERECHNUNGS-SERVICE¶
Erstelle src/lib/score/ als neues Verzeichnis mit folgenden Dateien:
src/lib/score/types.ts¶
Alle TypeScript-Typen für Score-Berechnung. ScoreData, CategoryScore, ScoreFactorData, RiskFlag, ScoreResult.
src/lib/score/normalize.ts¶
Hilfsfunktionen zur Normalisierung (0–100):
- normalizeLinear(value, min, max) — lineare Normalisierung
- normalizeSteps(value, steps: {threshold, score}[]) — stufenbasiert
- normalizeAge(year, thresholds) — für Jahreszahlen
- confidenceFromDataVersion(version: 'v1_manual' | 'v2_auto' | 'v3_iot') → v1=75, v2=95, v3=100
src/lib/score/categories/ertrag.ts¶
Berechnet Ertrag-Score (Gewicht 25%) aus: - Belegungsquote 90 Tage (35%): 100%=100, 95%=85, 90%=65, 85%=40, <80%=0 - Zahlungspünktlichkeit 12 Monate (30%): 0 Tage Verzug=100, <3=85, <7=65, <14=35, >14=0 - Leerstandstage 12 Monate (20%): 0=100, <7=85, <30=60, <90=30, >90=0 - Mietpreis vs. Markt (15%): Erst ab 100+ Einheiten im System, vorher 50 Pkt neutral - Zahlungstrend 24 Monate (neu): >20% Verbesserung=100, stabil=60, Verschlechterung=20 - Chronische Zahler >3x verspeätet (neu): 0=100, 1=70, 2=40, 3+=0
Datenquellen: Prisma-Abfragen auf units, tenancies, payments des propertyId.
src/lib/score/categories/instandhaltung.ts¶
Berechnet Instandhaltungs-Score (Gewicht 15%) aus: - Ticket-Lösungsrate 90 Tage (25%): >95%=100, >85%=80, >70%=55, >50%=25, <50%=0 - Durchschn. Lösungszeit (20%): <1 Tag=100, <3=85, <7=65, <14=35, >14=0 - Offene kritische Tickets (25%): 0=100, 1=60, 2=30, 3+=0. -10 Pkt je Ticket >30 Tage offen - Prävention vs. Reaktion (15%): >30% präventiv=100, >20%=75, >10%=50, <10%=25 - Handwerker-Durchschnittsbewertung (neu, 20% wenn Daten vorhanden): Ø5=100, Ø4.5=85, Ø4=70, Ø3.5=50, <3=0. Nur wenn min. 5 Ratings - Nachbearbeitungsquote (neu): <5%=100, <10%=80, <20%=55, >20%=0
Datenquellen: tickets, WorkerRating des propertyId.
src/lib/score/categories/compliance.ts¶
Berechnet Compliance-Score (Gewicht 15%) aus: - Freistellungsbescheinigungen (30%): 100% gültig=100, 1 abgelaufen=60, 2+=20, fehlend=0 - Versicherungsstatus (20%): alle gültig+hochgeladen=100, fehlende Doku=50, abgelaufen=0 - Dokument-Vollständigkeit (30%): Energieausweis (25%), Gebudeversicherung (25%), Übergabeprotokolle (15%), Wartungsverträge (15%), Freistellungsbescheinigungen (20%) - Offene gesetzliche Fristen (20%): keine=100, 1 Frist <30 Tage=60, 1 überfällig=20, 2+ überfällig=0 - BK-Abrechnungspünktlichkeit (neu): fristgerecht=100, <30 Tage verspätet=60, >30 Tage=0 - BK-Abweichungsquote (neu): <50 EUR Abweichung=100, <100=80, <200=55, <400=25, >400=0
Datenquellen: serviceProviders, documents, operatingCosts des propertyId.
src/lib/score/categories/substanz.ts¶
Berechnet Substanz-Score (Gewicht 25%) aus: - Gebäudealter (25%): <5J=100, <15=85, <30=65, <50=45, >50 ohne Sanierung=20 - Letzte Kernsanierung (30%): <5J=100, <10=85, <20=60, <30=35, >30 oder unbekannt=15 - Dach-Zustand (20%): <10J=100, <20=70, <30=40, >30=10, unbekannt=40 (neutral) - Fenster-Zustand (15%): <10J=100, <20=75, <30=40, >30=10, unbekannt=40 (neutral) - Heizungs-Alter (10%): <5J=100, <10=85, <15=60, <20=30, >20=0
Datenquellen: property Stammdaten.
src/lib/score/categories/technologie.ts¶
Berechnet Technologie-Score (Gewicht 25%) aus: - Energiezertifikat (35%): A+=100, A=90, B=75, C=55, D=35, E=20, F=10, G=0, fehlt=25 - Heizungstyp (25%): Wärmepumpe=100, Fernwärme=85, Pellet=75, Gas(neu)=55, Gas(alt)=35, Öl=10 - Smart Infrastructure (25%): +25 je Feature: Glasfaser, Smart Meter, Aufzug (>3 Etagen), EV-Ladeinfra - Barrierefreiheit (15%): full=100, partial=60, none=20, unbekannt=40 - Energieverbrauch real (neu, ersetzt Zertifikat-Proxy wenn vorhanden): <50kWh/m²=100, <80=85, <120=65, <160=40, >160=15
Datenquellen: property Stammdaten, EnergyReading.
src/lib/score/categories/sentiment.ts¶
Berechnet Kommunikations-Sentiment aus Ticket-Texten: - V1 (sofort): Keyword-basierte Klassifikation - Eskalations-Keywords DE: ['Anwalt', 'Klage', 'Mietminderung', 'fristlos', 'Verbraucherzentrale', 'Rechtsanwalt', 'gerichtlich', 'Schadensersatz'] - Positive Keywords: ['Danke', 'zufrieden', 'super', 'schnell erledigt', 'freundlich'] - Je Ticket: Score 0-100 basierend auf Keyword-Treffer - Durchschnittliches Sentiment 90 Tage (40%): positiv=100, neutral=70, negativ=40, eskalierend=0 - Eskalationsrate (35%): 0%=100, <5%=80, <10%=55, <20%=25, >20%=0 - Reaktionszeit-Frustrations-Proxy (25%): Korrelation hohe Wartezeit + negative Sentiment-Folge
src/lib/score/integrity.ts¶
Hash-Chaining Implementierung:
import crypto from 'crypto';
export interface ScoreHashInput {
propertyId: string;
totalScore: number;
substanzScore: number;
technologieScore: number;
ertragScore: number;
complianceScore: number;
instandhaltungScore: number;
calculatedAt: string; // ISO string
previousHash: string | null;
}
export function calculateScoreHash(input: ScoreHashInput): string {
// Felder MÜSSEN in dieser exakten Reihenfolge serialisiert werden
const payload = JSON.stringify({
propertyId: input.propertyId,
totalScore: input.totalScore,
substanzScore: input.substanzScore,
technologieScore: input.technologieScore,
ertragScore: input.ertragScore,
complianceScore: input.complianceScore,
instandhaltungScore: input.instandhaltungScore,
calculatedAt: input.calculatedAt,
previousHash: input.previousHash ?? 'GENESIS',
});
return crypto.createHash('sha256').update(payload, 'utf8').digest('hex');
}
export async function verifyPropertyChain(propertyId: string): Promise<{
valid: boolean;
totalEntries: number;
brokenAt?: string;
brokenAtDate?: Date;
}> {
// Lädt gesamte History aufsteigend, verifiziert jeden Hash gegen Vorgänger
// Gibt erstes gebrochenes Entry zurück wenn Manipulation erkannt
}
export async function runDailyChainVerification(): Promise<void> {
// Verifiziert ALLE Properties aller aktiven Mandanten
// Schreibt Ergebnis in GlobalChainAnchor
// Loggt Fehler ohne Exception (kein Score-Prozess blockieren)
}
src/lib/score/calculatePropertyScore.ts¶
Haupt-Berechnungsfunktion:
export async function calculatePropertyScore(
propertyId: string,
reason: 'scheduled_recalc' | 'data_update' | 'manual_trigger',
triggeredBy?: string
): Promise<ScoreResult>
Ablauf: 1. Lade Property mit allen relevanten Relations 2. Berechne alle 5 Kategorien parallel (Promise.all) 3. Berechne Gesamt-Score: (substanz0.25 + technologie0.25 + ertrag0.25 + compliance0.15 + instandhaltung*0.15) 4. Berechne Potenzial-Score (was wäre bei 100% Datenvollständigkeit) 5. Berechne Konfidenz-Score 6. Berechne Score-Grade: A+=90+, A=80+, B=65+, C=50+, D=35+, F=<35 7. Berechne Risk-Flags (Liste aller kritischen/hohen Probleme) 8. Lade Previous-Hash aus letztem History-Eintrag 9. Berechne neuen Hash via calculateScoreHash() 10. Upsert PropertyScore + Insert PropertyScoreHistory + Insert ScoreFactors 11. Return ScoreResult
src/lib/score/backgroundJob.ts¶
export async function runNightlyScoreRecalculation(): Promise<void>
// Läuft täglich 02:00 Uhr
// Alle Properties aktiver Mandanten
// Fehler je Property werden geloggt, propagieren nicht
// Am Ende: runDailyChainVerification() + GlobalChainAnchor schreiben
export async function triggerScoreRecalculation(
propertyId: string,
reason: 'data_update',
triggeredBy: string
): Promise<void>
// Async, nicht awaited im Haupt-Request
// Debounce: max. 1 Neuberechnung je Property alle 5 Minuten
PHASE 3 — TRIGGER-HOOKS¶
Füge Score-Neuberechnung als After-Hook in folgende bestehende API-Endpunkte ein:
POST/PATCH /api/v1/tickets/:id— wenn Status zu RESOLVED/CLOSED wechselt → triggerScoreRecalculationPOST /api/v1/payments— bei neuer Zahlung → triggerScoreRecalculationPATCH /api/v1/payments/:id— wenn als überfällig markiert → triggerScoreRecalculationPOST/PATCH /api/v1/tenancies— bei Mietverhältnis-Änderung → triggerScoreRecalculationPATCH /api/v1/properties/:id— wenn Score-relevante Felder geändert → triggerScoreRecalculationPOST /api/v1/worker-ratings— nach Rating-Eingabe → triggerScoreRecalculation
Wichtig: triggerScoreRecalculation IMMER ohne await aufrufen — nie den API-Request blockieren.
PHASE 4 — API-ENDPUNKTE¶
Erstelle folgende neue Endpunkte unter src/app/api/v1/:
GET /api/v1/properties/[id]/score¶
Response:
{
score: PropertyScore & { factors: ScoreFactor[] },
history: PropertyScoreHistory[], // letzte 12 Monate
integrity: {
hash: string,
chainValid: boolean,
lastVerified: Date,
totalVersions: number,
},
benchmark: {
percentile: number,
sampleSize: number,
available: boolean, // false wenn <50 Objekte im System
}
}
GET /api/v1/properties/[id]/score/history¶
Query-Params: ?from=&to=&limit=100 Für Chart-Darstellung optimiert — nur Score-Werte, keine Faktoren.
POST /api/v1/properties/[id]/score/recalculate¶
Nur für OWNER_ADMIN. Async — liefert { jobId, status: 'queued' } zurück.
GET /api/v1/score/portfolio¶
Alle Objekte des Mandanten mit Score, Grade, Trend, Top-Risiko. Sortierbar, filterbar. Für Portfolio-Dashboard-Matrix.
GET /api/v1/score/portfolio/export¶
PDF-Export aller Portfolio-Scores. Für Banken und Investoren.
POST /api/v1/worker-ratings¶
Erstellt WorkerRating nach Ticket-Abschluss. Validierung: ticketId muss Status RESOLVED/CLOSED haben. Nach Erstellung: triggerScoreRecalculation.
PHASE 5 — UI-KOMPONENTEN¶
Erstelle unter src/components/score/ folgende Komponenten.
Design-Vorgaben: Dark Theme, Lucide Icons, keine Emojis, i18n auf allen Strings, Tailwind.
ScoreHero.tsx¶
Volle Seitenbreite, sichtbar ohne Scrollen. Enthält: - LINKS: Große Score-Zahl + Grade-Badge + Trend-Indikator (Pfeil + Delta-Wert) - MITTE: 5 Kategorie-Kreisanzeigen (je Score + Label + Klick-Handler für Drill-Down) - Farb-Kodierung: Grün >=75 / Gelb 50-74 / Rot <50 - RECHTS: Konfidenz-Badge + Berechnungsdatum + Integritäts-Badge ('Hash-Kette gültig') - UNTEN: Drei Kennzahlen: Potenzial-Score / Benchmark-Percentile / Aktive Risiko-Flags - KI-Insight-Box: Automatisch generierter Satz zur wichtigsten Score-Veränderung
ScoreTrendChart.tsx¶
Recharts LineChart. Gesamt-Score über Zeit + optionale Kategorie-Linien (Toggle). Ereignis-Marker auf der Timeline (Hover zeigt was sich verändert hat). Benchmark-Linie (Marktdurchschnitt, gestrichelt). Zeitraum-Toggle: 3M / 6M / 12M / Gesamt. CSV-Export-Button.
ScoreCategoryGrid.tsx¶
5 Karten in Grid. Je Karte: - Kategorie-Name + Score + Gewicht - Liste aller Faktoren: Name · Wert · Score · Datenquelle · Messdatum - Datenstatus-Icon je Faktor: ✓ aktuell / ⏱ veraltet / ✗ fehlend / ★ manuell - Fehlende Daten mit CTA: 'Energieverbrauch fehlt — +6 Pkt bei Eingabe. Jetzt ergänzen →' - KI-Empfehlung: größte Verbesserungsmöglichkeit in dieser Kategorie - Klick öffnet ScoreFactorDrawer
ScoreFactorDrawer.tsx¶
Drawer von rechts (480px). Öffnet bei Klick auf Kategorie-Karte. Enthält: - Alle Faktoren der Kategorie als detaillierte Tabelle mit Rohdaten - Inline-Eingabe für manuelle Felder direkt im Drawer - Score-Preview: 'Nach Eingabe würde Score auf XX steigen' (Live-Berechnung) - Sparkline (Mini-Trendlinie) je Faktor letzte 6 Monate - Quellen-Transparenz: exakte Anzahl Datenpunkte und Zeitraum - Hash-Integritäts-Badge mit letztem Verifikations-Timestamp
RiskFlagPanel.tsx¶
Liste aller aktiven Risk-Flags. Je Flag: - Titel + Beschreibung + Kategorie + Schweregrad (KRITISCH/HOCH/MITTEL) - Erkannt-seit-Datum + Empfohlene Maßnahme + Geschätzte Score-Verbesserung - Direkte CTAs: 'Ticket erstellen' / 'Dokument hochladen' / 'Handwerker beauftragen' - Gelöste Flags letzte 30 Tage als graue Sektion: 'Behoben — Score +X Pkt'
ScoreIntegrityPanel.tsx¶
Standardmäßig zugeklappt (Accordion). Für Banken und externe Partner. Enthält: - Score-ID (zitierfähig) + SHA-256 Hash (anzeigen + kopieren) - Chain-Status: Anzahl Versionen + letzte Verifikation + Status - Externe Verankerung: Link zu GitHub oder Bitcoin TX - Verifikations-Anleitung: Step-by-Step für Nicht-Techniker (aufklappbar) - Download: Score-Zertifikat als PDF (digital signiert) - API-Endpunkt-Anzeige für direkte Bank-Abfrage
PortfolioScoreMatrix.tsx¶
Tabelle aller Objekte des Mandanten. Spalten: Name · Score · Grade · Trend · Konfidenz · Top-Risiko. Sortierbar nach allen Spalten. Filter: Grade-Filter / Trend-Filter / Risiko-Filter. Portfolio-Kennzahlen oben: Ø Score · Verteilung A/B/C/D/F als Stacked Bar · Anzahl kritischer Flags. Benchmark-Vergleich Zeile. PDF-Export für gesamten Portfolio-Report.
WorkerRatingModal.tsx¶
Öffnet automatisch wenn Ticket-Status auf RESOLVED/CLOSED wechselt. 1–5 Sterne Rating + optionaler Kommentar. Checkbox: 'Nachbearbeitung notwendig'. Felder: 'Angebotsbetrag' + 'Rechnungsbetrag' (optional). Submit löst triggerScoreRecalculation aus.
PHASE 6 — SCORE-SEITE¶
Erstelle src/app/[locale]/(dashboard)/properties/[id]/score/page.tsx
Vollständige Score-Analyse-Seite. Layout: 1. ScoreHero (volle Breite) 2. ScoreTrendChart (volle Breite) 3. ScoreCategoryGrid (volle Breite) 4. RiskFlagPanel (volle Breite) 5. ScoreIntegrityPanel (volle Breite, zugeklappt)
Navigation: Link von Property-Detail-Seite ('Score analysieren →'). Score-Badge auf Property-Karte in Listen-Ansicht (Grade-Badge + Score-Zahl).
Cron-Job registrieren für nächtliche Berechnung (02:00 Uhr):
- Wenn bereits ein Cron-System vorhanden: dort eintragen
- Sonst: /api/cron/score-recalculation Endpunkt mit CRON_SECRET Authentifizierung erstellen
NACH ABSCHLUSS¶
- Führe alle bestehenden Tests aus:
npm test - Schreibe Unit-Tests für:
- Alle normalizeSteps() Berechnungen (Grenzwerte)
- calculateScoreHash() Deterministik
- verifyPropertyChain() mit manipuliertem Eintrag
- Commit:
feat: implement xolib score v1+v2 with hash-chaining integrity - Aktualisiere CLAUDE.md: neue Modelle, neue Endpunkte, neue Komponenten
- Aktualisiere MEMORY.md: Score-Architektur als wichtigsten strategischen Kontext
WICHTIGE KONTEXTINFORMATIONEN¶
- Xolib hat bereits: 46 Prisma-Modelle, 84 API-Endpunkte, 8 KI-Agenten, GPT-4o-mini Integration
- Bestehende Modelle die der Score nutzt: Property, Tenant, Unit, Tenancy, Payment, Ticket, ServiceProvider, Document, OperatingCost
- Der Score ist mandanten-isoliert (tenantId) — kein Cross-Tenant-Vergleich ohne explizite Pseudonymisierung
- Löschen von PropertyScoreHistory ist dauerhaft verboten — Audit-Trail muss vollständig bleiben
- Der Score ist das strategische Daten-Asset das langfristig als Bank-API monetarisiert wird — jede Implementierungsentscheidung muss das berücksichtigen