Aufgaben, Systeme und Kanäle werden alle drei als Liste geführt und ab jetzt
auch gleich bearbeitet: anlegen, ändern, entfernen, zu- und abschalten — dort,
wo man sie sieht. Die Maske und die drei Listengriffe stehen einmal zentral
statt dreimal je Reiter; sonst unterscheiden sie sich nach der ersten Korrektur.
- AgentEntityDialog: eine Maske für alle drei Entitäten, über den React-
Schlüssel je Eintrag neu aufgebaut statt über einen Effekt zurückgesetzt
- agentEntityEditing: upsertById, removeById, newEntityId als reine Funktionen
- Systeme sind über Provider, Service und Hook schreibbar (saveSystems) und
tragen neu einen Aktiv-Schalter
- Systemreiter: eine einzige Liste statt «Lesende»/«Schreibende Systemzugänge»,
kein Einleitungsbalken mehr; Aktiv, Zugriff und Verbindung stehen je Eintrag
- Thomas steht als «Agentenverwalter» in der Navigation, zählt aber nicht als
sechster Agent in Sitzungszimmer, Auslastung und Begrüssung
- Chatverlauf scrollt in sich, ohne die Seite springen zu lassen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
**Sitzungszimmer statisch.** Die Kamera folgte dem Zeiger um bis zu 7,5
Zentimeter — als Belebung gedacht, als Wackeln wahrgenommen. Entfernt ist nur
der Aufruf der Parallaxe samt der nun unbenutzten Methode; Kameraposition,
Blickziel, Sitzordnung, Bilder und Beschriftungen sind unverändert. Das
Erkennen des Agenten unter dem Zeiger und der Klick auf seinen Platz bleiben —
das ist keine Bewegung.
**«Meine Objekte» ohne Querscrollen.** Bei automatischer Spaltenbreite richtet
sich jede Spalte nach ihrem längsten Inhalt; bei elf Spalten summierte sich das
über die Fensterbreite hinaus. Jetzt feste Anteile, die zusammen 100 % ergeben,
schmaleres Zellenpolster und Wortumbruch statt `nowrap` beim Mieter. Keine
Spalte entfernt, kein Wert gekürzt.
**Chats antworten.** Bisher meldete das Absenden nur, der Chat sei nicht
angebunden. Jetzt beantwortet jeder Agent — Thomas eingeschlossen — drei
Fragetypen aus dem Bearbeitungsverlauf seiner eigenen Seite: Rolle, Anzahl
Vorgänge im Log, Vorgänge im Vormonat. Schlüsselwortlogik statt Sprachmodell,
wie gefordert.
Die Zeitfrage wird vor der Mengenfrage geprüft: «Wie viele Aufträge wurden
letzten Monat bearbeitet?» enthält auch das Wort «Aufträge» und würde sonst als
Frage nach dem Log gelesen. Findet sich im Zeitraum nichts, sagt der Agent das
und nennt zum Vergleich den Gesamtbestand — statt eine plausible Zahl zu
erfinden. Eine erfundene Zahl im Chat ist schlimmer als keine Antwort: sie
sieht aus wie eine Auskunft.
Der Verlauf lebt nur, solange die Seite offen ist. Ein Verlauf, der Tage
überdauert, weckte die Erwartung, der Agent erinnere sich.
**KI-Hinweis überall gleich.** «KI kann Fehler machen, bitte überprüfen Sie
sämtliche Angaben» steht als eine Konstante und eine Komponente in allen sechs
Detailansichten. Bei Livia ersetzt er den Quellenvermerk und den Block
«Empfohlene nächste Handlung»; die Herkunft steht ohnehin oben bei der Quelle.
Geprüft im Browser gegen den Produktionsbau: Szene reagiert nicht mehr auf
Mausbewegung (Pixelvergleich), Tabelle 11 Spalten ohne Querscrollen
(scrollWidth = clientWidth), alle sechs Chats antworten agentenspezifisch mit
Zahlen, die den Mockdaten entsprechen, unbekannte Fragen werden sauber
abgewiesen, Hinweis in allen sechs Detailansichten vorhanden, 0 Laufzeitfehler.
423/423 Tests grün, tsc und eslint sauber.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Modellwahl läuft über eine Umgebungsvariable im Hosting-Dashboard. Damit
sah man nirgends in der Anwendung, welches Modell gerade urteilt — und eine
Einstellung, die man nur im Dashboard prüfen kann, prüft niemand.
Der Refresh gibt das Modell jetzt zurück, die Kopfzeile zeigt es neben dem
Zeitstempel. Wer wissen will, ob die Umstellung angekommen ist, sieht es dort,
wo er ohnehin hinschaut.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Zefix-CSV nennt keine Firmennamen, sondern Tageszahlen je Branche. Als
Lead-Karte zwischen sechs echten Firmenmeldungen war sie ein Fremdkörper: Man
kann eine Branchenzahl nicht anrufen, und genau das verspricht eine Lead-Karte
mit «Empfohlene nächste Handlung».
Aggregierte Einträge werden weiterhin gelesen und beurteilt — die Quelle bleibt
angebunden und belegt, dass Livia auch strukturierte Daten verarbeitet —, zählen
aber als Beobachtungsposten statt als Lead. Das entspricht der «Early
Watch»-Einordnung der Vorgabe (§19), und die Zahlen der Kopfzeile gehen
weiterhin auf.
Die Quellenbeschreibung sagt jetzt selbst, warum dort keine Karte erscheint,
statt dass sich das jemand aus dem Ausbleiben erschliessen muss.
Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen, Build erfolgreich,
Live-Lauf 49 Einträge — Zefix erscheint nicht mehr als Lead, Summe aus
verworfen, Watchlist und Leads ergibt die Gesamtzahl.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
«Live · 24» liest sich genauso gut als «24 gefundene Leads» — beim ersten
Zeigen wurde es auch so verstanden. Die Zahl trägt jetzt ihre Einheit, mit
Einzahl bei genau einem Eintrag.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Livia liest jetzt tatsächlich aus dem Netz statt aus Mockdaten. Ein Knopf
«Research aktualisieren» ruft serverseitig drei Quellen ab, beurteilt die
Einträge in einem einzigen LLM-Aufruf und zeigt nur an, was plausibel zu
Flächenbedarf oder Flächenfreisetzung führen könnte.
Kein Crawler, kein Scheduler, keine Queue, keine Datenbank. Der Hoster kann
bereits serverlose Funktionen — es hat bisher nur niemand eine gebraucht.
Deshalb genügt eine einzige Datei unter `api/`, drei Abruffunktionen, eine
Normalisierung und ein Analyseaufruf.
Quellen, alle drei live geprüft:
- Zürcher Handelskammer, 10 Artikel
- Greater Zurich Area, 9 Artikel
- Zefix / Open Data Kanton Zürich, CSV direkt
Zur Zefix-Quelle eine Feststellung, die von der Aufgabenstellung abweicht: die
CSV enthält keine Firmennamen. Sie führt Tageszahlen von Neugründungen je
NOGA-Branche für den Kanton Zürich. Daraus lässt sich kein Unternehmenslead
bauen, sondern nur ein aggregiertes Frühsignal zum Zielmarkt — und genau so
wird es übergeben. Namen zu erfinden, um das erwartete Format zu treffen, wäre
die eine Sache, die Livia nie tun darf.
Die drei Quellen stehen im Frontend unter «Angebundene Kanäle & Systeme» mit
Status und anklickbarem Originallink; jeder erzeugte Lead verlinkt zusätzlich
die Seite, aus der er stammt. Die Adressen kommen aus derselben Liste, die der
Abruf benutzt — der angezeigte Link kann damit nicht von dem abweichen, was
tatsächlich gelesen wurde.
Der Textextraktor sucht den Artikeltext im engsten passenden Container. Ohne
das standen bei der Handelskammer Telefonnummern und Öffnungszeiten am Anfang
jedes Textes, und das Modell hätte die Fusszeile mitbeurteilt.
Der LLM-Schlüssel liegt ausschliesslich serverseitig in `ANTHROPIC_API_KEY`.
Fehlt er, sagt die Oberfläche offen, dass gelesen, aber nicht beurteilt wurde —
statt eine leere Liste als «nichts gefunden» auszugeben.
Für die lokale Entwicklung mountet ein Vite-Plugin dieselbe Handler-Datei, die
Vercel in der Produktion ausliefert; es gibt keine zweite Fassung des
Endpoints. `tsconfig.api.json` nimmt `api/` in `tsc -b` auf, damit ein
Tippfehler dort nicht erst beim Deployment auffällt.
Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen, 423/423 Tests grün, Build
erfolgreich, Live-Lauf im Browser mit 20 real gelesenen Einträgen aus 3/3
Quellen ohne Laufzeitfehler. Die LLM-Beurteilung selbst ist mangels Schlüssel
noch ungetestet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Das bisherige Bild zeigte einen ernst blickenden Mann ohne Lächeln — die
Vorgabe «visuell deutlich freundlicher» war damit nicht erfüllt, auch nachdem
Thomas den Chat-Einstieg der übrigen Agenten bekommen hatte. Ein freundlicher
Rahmen um ein strenges Gesicht ändert nichts am ersten Eindruck.
Neu ist Kachel P015 aus dem Porträtbogen des Auftraggebers: derselbe Typ
Mensch — Mann mittleren Alters, Brille, dunkelblauer Blazer — aber mit offenem
Ausdruck. Die Ähnlichkeit ist Absicht, damit die Änderung sich als
freundlicheres Bild derselben Person liest und nicht als Personalwechsel.
Bestehender Bildstil bleibt: neutraler heller Hintergrund, gleicher
Bildausschnitt, gleiche Kantenlänge wie die fünf Agentenporträts.
Herkunft und Lizenz stehen in `HERKUNFT.md`, dort auch der Hinweis auf die
begrenzte Auflösung: die Quelle ist ein Kontaktbogen mit dreissig Porträts auf
1536 × 1024 Bildpunkten, eine Kachel misst darin rund 176 × 176. Das Bild ist
auf 512 × 512 hochskaliert und damit weicher als die fünf Porträts aus den
1200 × 1200 grossen Originalen der Präsentation.
Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen, 423/423 Tests grün,
Produktionsbuild erfolgreich. Im Browser kontrolliert auf Thomas' Seite und im
Meetingraum der Startseite — kein Laufzeitfehler, kein stilistischer Bruch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Runde 8 hat Livia zur Research- & Market-Intelligence-Agentin gemacht. Die
Exposé-Erstellung war der Kern ihrer alten Rolle und fällt mit ihr weg — sie
wird nicht nachgeordnet weitergeführt. Ein zweiter Reiter hätte die Funktion
sichtbar gelassen und die neue Positionierung damit halbiert.
Entfernt: 23 Dateien (Arbeitsbereich, dreistufiger Assistent, Dossier mit elf
Bereichen, Provider, Services, Hooks, Feldkatalog, Mockdaten, deterministischer
Textbaustein) sowie `generateExposeText` aus `IAIService` und beiden
Implementierungen.
Livias Arbeitsplatz zeigt jetzt nur noch die Recherche-Leads — ohne
Reiterbalken, weil eine Leiste mit einem Eintrag eine Alternative behauptet,
die es nicht gibt. Ihr Personalblatt, die Bearbeitungsverlaufs-Einträge und die
Teamdokumentation sind auf die neue Rolle nachgezogen; die beiden
Beispielvorgänge zeigen jetzt eine Leaderzeugung und ein Review mit
widersprüchlichen Quellen statt Lagebeschrieb und Exposétext.
Bei Nora entfällt die Mehrfachauswahl in der Objektliste: sie diente allein der
Weiterleitung an die Exposé-Erstellung. Eine Auswahl ohne Ziel wäre ein
Bedienelement, das nichts auslöst. Die Matching-Darstellung selbst bleibt
unverändert.
Der Wiederaufbau ist in `archive/livia/` beschrieben; das Manifest hält jetzt
fest, welche seiner Annahmen eingetreten sind und welche nicht — Livia bleibt
im Team, nur die Funktion ist weg.
Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen, 423/423 Tests grün,
Produktionsbuild erfolgreich, Smoke-Test über sieben Seiten ohne
Laufzeitfehler, 12/12 Abnahmekriterien erfüllt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Smoke-Test der fünf betroffenen Seiten hat zwei Fehler aus Runde 8
aufgedeckt, die Typecheck, Lint und Tests nicht sehen konnten.
Thomas' Chatkopf blieb stehen, statt in den Titelbalken zu wandern. Seine Seite
bot nur 307 Pixel Scrollweg, der Chatkopf misst aber rund 400 — der
IntersectionObserver löste deshalb nie aus. Bei den anderen Agenten leistet die
lange Liste unter dem Chat den nötigen Weg; hier haben die drei
Verwaltungsbereiche ihre eigene Höhe und scrollen innen, also muss die
Bedingung ausdrücklich stehen. Die Mindesthöhe des Abschnitts ist jetzt eine
Bildschirmhöhe abzüglich des obersten Balkens; `TOP_BAR_PX` kommt dafür aus
`AgentWorkspaceHero` statt als zweite Zahl danebenzustehen.
Livias Zählzeile schrieb «1 Risiken». Einzahl und Mehrzahl werden nun für alle
drei Werte der Zeile unterschieden.
Geprüft im Browser gegen den Produktionsbuild: fünf Seiten ohne Laufzeitfehler,
Thomas' kompakter Chat erscheint nach dem Scrollen im Balken, Chance- und
Risiko-Beispiele sichtbar, alle fünf Vertragstypen bei Ferdi vorhanden, keine
Matching-Darstellung auf Livias Seite.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Livia wird von der Exposé-Master zur Research- & Market-Intelligence-Agentin.
Sie schaut nach draussen: verknüpfte Newsquellen, Geschäftsberichte,
Handelsregister und amtliche Publikationen werden zu qualitativen Leads
verdichtet — Unternehmen, Veränderung, Ort, Zeitpunkt, Begründung, Quellen.
Die Exposé-Funktion bleibt vollständig erhalten, ist aber nachgeordnet und
liegt im zweiten Reiter ihres Arbeitsplatzes.
Das Matching bleibt ausdrücklich bei Nora. Beide Arbeitsplätze teilen sich
dieselbe Liste (`UnifiedLeadList`) und dieselbe Detailansicht (`LeadDetail`);
bei Livia läuft sie mit `showMatching={false}`. Damit ist die Grenze im Code
gezogen und nicht bloss in der Beschriftung. Es gibt keine zweite Lead-Ablage:
ein Lead, zwei Arbeitsplätze.
Neu ist die Einordnung Chance/Risiko als Spalte in Noras Liste und als Block
in der Detailansicht, je mit Begründung und konkreter nächster Handlung. Bei
einem Risiko wird das betroffene Mietverhältnis benannt und auf internes
Re-Letting hingewiesen. `leadSignalTypeOf()` leitet die Einordnung für
Altbestände aus dem Signaltyp ab, damit keine Migration nötig ist.
Thomas bekommt denselben Chat-Einstieg wie die fünf anderen — dieselbe
Komponente, dieselbe Sticky-Mechanik, keine Sonderlösung. Dafür scrollt seine
Seite als Ganzes; die drei Verwaltungsbereiche behalten ihren inneren Scroll.
Ferdi wird vom Reminder-Agenten zur Contract Intelligence: Kündigungsfristen,
echte und unechte Optionen, Verhandlungsfenster und Vertragsgespräche. Der
Unterschied zwischen echter und unechter Option ist der Kern — die echte
Option hat eine Frist, die verfällt, die unechte nur ein Fenster, in dem
verhandelt werden sollte. Alle neuen Termine orientieren sich am
Entscheidungszeitpunkt, nicht am Vertragsende: `decisionDate` und `dueDate`
liegen bewusst davor.
Auslastungsanzeige nachgezogen: Livia zählt ihre Recherche statt offener
Exposés, Nora den Abgleich statt des Findens.
Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen, 423/423 Tests grün,
Produktionsbuild erfolgreich.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sinas Bild wirkte unscharf. Die Datei ist es nicht — sie hat wie alle
anderen 512 Bildpunkte. Ihr Kreis erscheint aber nur rund 170 Bildpunkte
gross, weil sie am weitesten hinten sitzt. Bei so starker Verkleinerung
greift die Grafikkarte auf eine vorberechnete, viertel so grosse
Zwischenfassung zurück; das Ergebnis ist ein weiches Gesicht.
Drei Änderungen:
- Jedes Porträt wird beim Laden auf seine tatsächliche Anzeigegrösse
gerechnet (Ferdi 384, Livia 275, Sina 229, Thomas 226 statt überall
512). Die Grafikkarte zeigt damit die Vorlage nahezu Punkt für Punkt,
die Zwischenfassung entfällt.
- Nach dem Verkleinern wird nachgeschärft, in der Stärke abhängig davon,
wie weit verkleinert wurde. Ohne das verliert ein herunter gerechnetes
Foto seine Zeichnung.
- Die Szene rechnet intern mindestens doppelt so fein wie der Bildschirm
darstellt. Das kostet wenig, weil nicht laufend gezeichnet wird, und
hilft auch Fensterprofilen und Kulisse.
Geladen wird dafür von Hand statt über den TextureLoader von three.js:
dessen Rückruf liefert die Bildmasse nicht zuverlässig, und ohne sie
lässt sich weder zuschneiden noch massgerecht verkleinern. Die Zählung
beim Ladeverwalter bleibt erhalten, damit die Szene nach dem letzten
Bild wie bisher einmal neu zeichnet. Der quadratische Zuschnitt sitzt
jetzt ebenfalls hier statt in den Texturkoordinaten.
422 Tests, Lint und Build sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Runde 7.
§1 Hintergrund. Die Häuserzeile auf der linken Seite zeigte verwässerte
Doppelstrukturen. Drei Ursachen, alle behoben:
- Die Fortsetzung lief mit Massstab 2.45 und war damit gegenüber dem
Hauptfoto um das 2.4-fache zu gross. Der Faktor ist jetzt aus den
Fotos gemessen (Münsterhöhe über der Wasserlinie) und beträgt 1.06.
- Die 320 Pixel breite Überblendung mischte zwei verschiedene
Häuserreihen zu halbdurchsichtigen Geisterhäusern. In der Häuserzeile
wird jetzt praktisch hart getrennt, in Himmel und Wasser weiterhin
weich — dort gibt es nichts zu vermischen.
- Die Umschaltlinie lag zwischen den Dächern, sodass die Gauben des
Hauptfotos als Reihe schwebender Klötzchen im Himmel stehen blieben.
Sie liegt jetzt über allen Dächern beider Aufnahmen.
Dazu: Helligkeit zeilenweise statt global angeglichen, Himmel der
Fortsetzung spaltenweise entlang der echten Dachkante entfernt, fehlende
Bildbereiche über die Zeilenfarbe statt über gestreckte Randzeilen
aufgefüllt, Häuserzeile nachgeschärft.
§2 Agentenbilder. Alle Kreise deutlich grösser, tiefenlogisch gestaffelt
und nur noch knapp über der Tischkante statt frei im Raum schwebend.
Gegenüber vorher: Ferdi und Bruno 2.11×, Livia und Nora 2.22×,
Sina 2.50×, Thomas 3.18×. Ferdi und Bruno rücken näher an die vordere
Tischkante. Radius und Höhe stehen jetzt je Platz in der Sitzordnung;
die Werte sind gemeinsam durchgerechnet, sodass sich im Bild kein
einziges Kreispaar überschneidet.
Nebenbei: waagrechter Bildwinkel nach oben begrenzt, damit flache
Fenster den Raum nicht zerren; Sockelleiste vor das Wandpaneel gesetzt
gegen die ausgefranste Kante.
Geprüft bei 1600×900, 1366×768 und 1280×620: sechs Beschriftungen ohne
Überlappung, kein Querbalken, Klick auf Ferdi führt auf seine Seite,
Menü bündig am rechten Rand. 422 Tests, Lint und Build sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Porträts lagen bisher nur als kleine, liegende Zuschnitte aus einem
HTML-Konzept vor — Ferdi etwa 215×160 Bildpunkte bei 6 KB. Im 3D-Meetingraum
waren sie damit sichtbar unscharf, und Hochskalieren hilft dagegen nicht.
Die Originale liegen in `PropertyOn_Engeli_v5.pptx` als quadratische Bilder mit
1200×1200 Bildpunkten. Sie zeigen dieselben fünf Personen: Kleidung, Frisur und
Ausschnitt stimmen mit den bisherigen Zuschnitten überein, und die Präsentation
nennt Name und Funktion direkt neben jedem Bild. Jede Zuordnung wurde einzeln
gegen den alten Zuschnitt geprüft — eine frühere Quelle trug dieselben Vornamen
für eine andere Belegschaft, und ein vertauschtes Gesicht wäre schlimmer als ein
unscharfes.
`scripts/build-agent-portraits.mjs` beschneidet den Folienrand, skaliert auf
512×512 und schreibt die Dateien. Das reicht für jede Darstellung im Programm —
im Raum sind die Kreise höchstens rund 100 Bildpunkte gross — und hält das
Bündel klein: rund 30 KB je Porträt statt 250 KB im Original.
Die Verarbeitung läuft ohne Bildbibliothek auf einem Canvas im vorhandenen
Chrome, wie schon beim Basel-Panorama. Keine neue Abhängigkeit.
Geprüft: 422 Tests, Typecheck, Build, ESLint, Token-Schwelle (884/925), Szene
im Browser ohne Konsolenfehler.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Vier Korrekturen aus der Nachprüfung.
**Naht in der linken Fensterfront.** Sie stammte von zwei getrennten
Fotoflächen — vorne und links. Eine Stossstelle lässt sich nicht wegblenden;
sie verschwindet nur, wenn es keine zwei Bilder mehr gibt. Die Kulisse ist
jetzt eine gekrümmte Fläche mit einer einzigen Panoramadatei: auf einem
durchgehenden Zylinder ist eine Naht geometrisch unmöglich.
Das Panorama entsteht einmalig aus den beiden Referenzfotos
(`scripts/build-basel-panorama.mjs`), jede Quelle genau einmal — kein Kacheln,
kein Spiegeln. Das gute Motiv vom Tischende bleibt in Ausschnitt und Farbe
unverändert und steht weiterhin in der Frontscheibe; die Panoramaaufnahme setzt
es nach links fort, helligkeitsangeglichen und über 320 Pixel überblendet. Aus
der Fortsetzung wird nur der Abschnitt östlich des Münsters verwendet — beide
Vorlagen zeigen es, nebeneinander stünde es zweimal im Bild. Himmel und Wasser
sind aus den Randzeilen gestreckt, damit oben und unten keine Bildgrenze bleibt.
**Stühle und Körper entfernt.** Sitzmöbel, Rümpfe, Schultern, Hälse, Arme und
Hinterköpfe sind ersatzlos weg. Die Agenten bestehen nur noch aus ihrem runden
Porträt mit weissem Ring.
**Positionen neu.** Ferdi und Bruno sind die direkten Nachbarn links und rechts
vorne, die übrigen gestaffelt dahinter, Thomas unverändert vis-à-vis. Die
Kreise liegen deutlich über der Blickachse: lagen sie auf Augenhöhe,
projizierten alle fünf auf fast dieselbe Bildhöhe und die hinteren
verschwanden hinter den vorderen — seitliches Auseinanderziehen half dagegen
nicht, weil ein tieferer Platz in der Perspektive zur Mitte wandert.
Dazu ein echter Fehler, der dabei auffiel: die grosszügigen Trefferquader der
vorderen Kreise fingen die Strahlen der hinteren ab, ein Klick auf Livia landete
bei Ferdi. Die Trefferfläche ist jetzt eine Kugel knapp um den sichtbaren Kreis.
**Bildschärfe.** Die Textur wird auf einen mittigen quadratischen Ausschnitt
begrenzt — ohne das zieht die Kreisgeometrie ein liegendes Foto in die Breite,
was Gesichter verzerrt und Schärfe kostet. Dazu anisotrope Filterung und
Mipmaps. Höher aufgelöste Originale der fünf Agenten waren nicht auffindbar:
die Porträts in `Agenten_Verwaltung_Fotos.html` liegen zwar in 256×256 vor,
gehören aber zu einer anderen Belegschaft — geprüft und verworfen, statt Gesichter
zu vertauschen.
**Ortsangabe** unten rechts entfernt. **Menü** liegt bündig an der rechten
Fensterkante: der Spalt kam vom Anker am eingerückten Menüsymbol, jetzt ist das
Klappmenü an eine Bildschirmstelle gebunden.
Geprüft: 422 Tests, Typecheck, Build, ESLint, Token-Schwelle (884/925). Im
Browser 1280 und 1600 px: sechs Beschriftungen ohne Überlappung, sechs Kreise
ohne Überlappung, alle sechs per Maus auf der richtigen Subpage, Menü bündig
ohne waagrechten Scrollbalken, keine Konsolenfehler.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Anpassungsrunde 6. Die Kartendarstellung aus Runde 5 ist ersetzt: die
Startseite zeigt oben einen räumlichen Meetingraum aus der Sicht des
Betrachters, der selbst am Kopfende des Tisches sitzt.
**3D statt Kulisse.** Zwei Versuche mit CSS-Flächen sind daran gescheitert,
dass gezeichnete Wände neben fotografierten Porträts erkennbar gezeichnet
bleiben. Diese Runde nutzt three.js — die einzige neue Abhängigkeit, und §19
lässt sie ausdrücklich zu, wenn eine schlanke 3D-Lösung die Anforderung trägt.
Keine Engine darüber hinaus: kein React-Three-Fiber, keine Physik, keine
Shader, keine Post-Processing-Kette, keine Orbit-Steuerung.
Der Raum: Boden, Decke, rechts eine geschlossene Wand mit Eichenpaneel und
einem schmalen Streifen im Markenblau, links und gegenüber bodentiefe
Glasfassaden mit Pfosten. Draussen tragen die beiden Referenzfotos aus dem
Auftrag die Basler Aussicht — vorne Rhein, Altstadt und Münster, links der
Rheinverlauf mit der Wettsteinbrücke. Die Wasserlinie beider Kulissen liegt auf
derselben Welthöhe, damit der Blick nach vorne und nach links zusammengehört
und nicht wie zwei zusammengesetzte Bilder wirkt (§4).
Am Tisch sitzen alle fünf bestehenden Agenten mit ihren bestehenden Porträts,
Namen, Funktionen und Zielseiten — alles aus `AGENT_WORKSPACES`, damit nichts
doppelt gepflegt wird (§24, Schritt 7). Neu ist Thomas, Personalverwalter,
vis-à-vis am gegenüberliegenden Kopfende; sein Klick führt in die bestehende
Ansicht «Meine Agenten».
Gerendert wird nur bei Anlass — Grösse, Maus, Hover. Ein Dauerloop wäre für
eine feste Szene verschwendete Rechenzeit (§21). Die Kamera folgt der Maus um
wenige Zentimeter, mehr nicht (§20).
Die Beschriftungen sind HTML über dem Bild, nicht Textur in der Szene: als
Textur wären sie bei Tiefe unlesbar. Ein Entzerrungsdurchgang nach der
Projektion hebt Plaketten so weit an, bis keine mehr überlappt — gerechnet und
nicht handverlesen, damit es auf jeder Fenstergrösse hält. Bei Hover kommt eine
Zeile mit der aktuellen Auslastung dazu; ruhend bleiben es Name und Funktion.
**Navigation.** Die linke Seitenleiste ist gelöscht, nicht ausgeblendet: samt
Breitenreservierung, Einklapplogik und mobilem Drawer. Der Inhalt beginnt
überall bei x=0. An der Stelle des früheren Profilsymbols steht neben der
Glocke ein Menüsymbol; das Klappmenü öffnet rechts an der Kopfzeile nach unten
und führt Startseite, Meine Agenten samt aller Agenten als eingerückte,
rechtsbündige Unterpunkte, und Profil mit Profil, Einstellungen, Produkttour
und Abmelden. Alle Aktionen und Routen sind die bestehenden.
Entfallen sind die Kartenkomponenten aus Runde 5 samt Präsenzschicht.
Geprüft (§25): 422 Tests, Typecheck, Build, ESLint, Token-Schwelle (884/925).
Im Browser 430–2560 px: keine alte Sidebar, kein leerer Streifen links, sechs
Beschriftungen ohne Überlappung, alle sechs Agenten per Maus und per Tastatur
erreichbar und auf der richtigen Subpage, Menü öffnet und schliesst per Klick
und Escape, alle Menürouten korrekt, alle bestehenden Seiten laden, kein
horizontaler Overflow, keine Konsolenfehler.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Auf dem Bildschirm des Nutzers waren die oberen Karten oben abgeschnitten —
Porträt und Name fehlten — und rechts lief ein zweiter Scrollbalken innerhalb
des Belegschaftsbereichs. Drei Ursachen, alle aus derselben Wurzel: der
Bereich hatte eine gedeckelte Höhe und durfte in sich scrollen.
1. Feste Höhe entfernt. Der Bereich richtet sich nach seinem Inhalt und hat
nur noch eine Mindesthöhe. Passte der Inhalt vorher nicht in den Deckel,
scrollte er in sich selbst und schnitt oben ab — ein Bereich über einer
ohnehin scrollenden Seite darf das nie.
2. Fünf Karten stehen ab 1200 px in einer Reihe statt in zwei. Zwei Reihen
brauchten rund 250 px mehr Höhe; genau dieser Betrag drückte Titel und
Filterbalken aus dem Bild. Eine Reihe ist zugleich das ehrlichere Bild:
fünf Kolleginnen und Kollegen nebeneinander, keine Hierarchie.
3. `minWidth: 0` auf der Karte. Ohne das kann eine Flexkarte nicht unter ihre
Inhaltsbreite schrumpfen — die fünf Karten passten rechnerisch in eine
Reihe, brachen aber trotzdem um.
Dazu: lange deutsche Komposita («Besichtigungsassistent», «Marktbeobachtung»)
brechen ohne Zutun nicht um und liefen aus schmalen Karten heraus. Funktion,
Abteilung, Personalnummer und beide Textblöcke brechen jetzt im Wort statt
abgeschnitten zu werden — eine halbe Funktionsbezeichnung ist keine, und ohne
Personalnummer ist jemand kein Mitarbeitender.
Geprüft über zehn Auflösungen von 1024×700 bis 3440×1440, ausdrücklich auch
die Windows-Skalierungen 125 % und 150 % (effektiv 1536×864 und 1280×720):
nichts abgeschnitten, nirgends Innenscroll, Titel «Meine Objekte» überall im
Bild. Dazu 433 Tests, Typecheck, Build, ESLint, Token-Schwelle (889/925).
Verbleibend: Bei einer Inhaltshöhe unter 620 px (1280×600, 1024×700) steht der
Balken «Filter & Übersicht» rund 30 px unter der Kante. Der Titel bleibt
sichtbar; der Balken kommt mit der kleinsten Scrollbewegung.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Auf 2560×1440 sah die Startseite schlecht aus, und der Fehler war meiner: die
Prüfung reichte nur bis 1920 px. Darüber lief nichts mehr auf Anschlag.
Zwei Ursachen:
Die Karten hatten keine Breitengrenze. Bei drei Spalten ohne Deckel wurden sie
auf 2560 px rund 733 px breit bei 207 px Höhe, auf einem Ultrawide sogar
1027 px — flache Streifen, in denen zwei Textzeilen verloren stehen. Der
Inhalt ist jetzt auf 1280 px gefasst und mittig gestellt, die Karten bleiben
damit auf jeder Breite rund 395 px. Der Hintergrund bleibt randlos.
Die Höhe wuchs unbegrenzt mit dem Viewport, während die Karten ihre
Inhaltshöhe behielten. Auf einem 1440er Monitor stand dadurch über und unter
den Karten ein halber Bildschirm leere dunkle Fläche. Die Höhe ist jetzt auf
760 px gedeckelt. Der Titel «Meine Objekte» bleibt sichtbar — er rückt nach
oben, und mehr von der Objektliste wird sichtbar.
Geprüft über neun Auflösungen von 1366×641 bis 3440×1440, jeweils fünf Karten
vollständig sichtbar, keine Überlappung, kein Innenscroll, Titel und
«Filter & Übersicht» im Viewport. Dazu 433 Tests, Typecheck, Build, ESLint,
Token-Schwelle (889/925).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Auf ausdrücklichen Wunsch: Giorgio entfällt vollständig. Das weicht bewusst
von §6 des Auftrags ab, der ihn als Personalverwalter und Vorgesetzten
vorsah — festgehalten, damit die Abweichung später nachvollziehbar bleibt.
Entfernt: Rollendefinition `AGENT_SUPERVISOR`, sein gezeichnetes Porträt, sein
Eintrag in der Auslastung und in der Belegschaftsansicht sowie die Erwähnung
in der Produkttour. Die Personalverwaltung bleibt unverändert über den
Menüpunkt «Meine Agenten» erreichbar — sie brauchte nie eine eigene Person am
Bildschirm.
Mit Giorgio entfällt auch die einzige Kennzahl, die aus den Freigabevorgängen
kam; `computeAgentWorkload` braucht `agentWorkItems` deshalb nicht mehr.
Ordner und Komponenten heissen jetzt nach ihrem Inhalt: `components/staff/`,
`AgentStaffOverview`, `StaffBackdrop`. «Meetingraum» war seit dem Umbau auf
Personenkarten irreführend, und ein falscher Name kostet den Nächsten mehr
Zeit als die Umbenennung.
Fünf statt sechs Karten heisst Flexlayout statt Dreierraster: drei oben, zwei
mittig darunter, gleich breit und ohne Lücke. Die Karten geben ihre Höhe nicht
mehr selbst vor — mit `height: 100%` beanspruchte jede die volle Fläche und
die zweite Zeile fiel aus dem sichtbaren Bereich.
Geprüft: 433 Tests, Typecheck, Build, ESLint, Token-Schwelle (889/925).
Browser 1280–1920 px und Tablet: fünf Karten vollständig sichtbar, keine
Überlappung, Titel und «Filter & Übersicht» beim Einstieg sichtbar, alle fünf
Klicks auf der richtigen Subpage, «Meine Agenten» weiterhin erreichbar, keine
Konsolenfehler, keine 404.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der nachgebaute Meetingraum entfällt. Eine gezeichnete Kulisse bleibt neben
fotografierten Porträts erkennbar gezeichnet — und ein Raum allein macht
niemanden menschlich. Menschlich wirkt im echten Büro dasselbe wie hier: dass
jemand da ist, etwas zu sagen hat und nachweisbar gearbeitet hat.
Die Startseite zeigt deshalb sechs Personenkarten vor einer abgedunkelten
Büroaufnahme. Jede Karte trägt, was ein Personalblatt trägt:
- Porträt mit Dienstpunkt — ist da oder pausiert
- Name, Funktion, Abteilung, Personalnummer
- Autonomiestufe: «Autonom», «Freigabe erforderlich», «Nur Vorschlag».
Sie sagt, wie viel Verantwortung jemand trägt, und trennt Personal von
Automatisierung.
- Ein Satz in der ersten Person zur aktuellen Lage, statt einer Kennzahl:
«Bei 12 Fristen ist der Termin durch. Fürs Eskalieren brauche ich deine
Freigabe.»
- Die zuletzt protokollierte Handlung mit Zeitangabe. Diese Zeile ist der
Unterschied zwischen Behauptung und Beleg — eine Kennzahl kann jede
Software anzeigen.
Darüber fasst ein Anriss zusammen, wie viele Mitarbeitende heute eine
Entscheidung brauchen, samt Dienststand der Belegschaft.
Schichten nach CLAUDE.md §4.2: `agentPresenceService` entscheidet, was als
letzte Handlung gilt und wie eine Zeitangabe in Alltagssprache lautet;
`agentWorkloadService` hält Zeitfenster, Schwellen und Formulierungen. Beide
nehmen den Stichzeitpunkt als Parameter — dieselbe Datenlage ergibt immer
dasselbe Ergebnis und ist ohne Renderer prüfbar (22 Tests).
Giorgio hat kein Personalblatt; Abteilung, Personalnummer und Stufe kommen
aus der zentralen Rollendefinition. Wer kein Dossier hat, gilt als im Dienst —
sonst stünde die halbe Belegschaft still, weil Stammdaten fehlen.
Bild: Unsplash, freie kommerzielle Lizenz. Herkunft und Austauschhinweis in
`src/assets/meeting-room/HERKUNFT.md`.
Geprüft: 435 Tests, Typecheck, Build, ESLint, Token-Schwelle (889/925).
Browser 1280–1920 px und Tablet: sechs Karten ohne Überlappung, Titel und
«Filter & Übersicht» beim Einstieg sichtbar, alle sechs Klicks auf der
richtigen Subpage, keine Konsolenfehler, keine 404.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die gezeichnete Raumkulisse ist ersetzt. Aus Farbflächen konstruierte Wände
bleiben neben fotografierten Agentenporträts erkennbar gezeichnet — die
Bildwirkung muss aus dem Foto kommen, nicht aus Geometrie. Entfallen sind
damit die beiden SVG-Stadtansichten, die CSS-3D-Flächen und die Farbwelt
DS_MEETING_ROOM.
Neu trägt die Szene die Basler Aussicht aus dem Auftrag als Foto (§5), mit
abgestuftem Verlauf für Lesbarkeit und Übergang zur Objektliste. Ein Austausch
des Motivs ist ein Einzeiler in `roomBackdrop.ts`.
Mehrwert statt Kulisse: Jedes Namensschild zeigt die aktuelle Auslastung
seines Agenten — überfällige Fristen, Termine der Woche, offene Exposés, neue
Marktchancen, Objekte mit Datenlücken, offene Freigaben. Darüber fasst ein
Anriss zusammen, wie viele Bereiche heute Aufmerksamkeit brauchen. Damit
beantwortet die Startseite die Frage, mit der sie geöffnet wird, statt sie
auf sechs Zahlen zu verteilen.
- `agentWorkloadService` hält die fachlichen Festlegungen: Zeitfenster,
Dringlichkeitsschwellen und Formulierungen. Der Stichzeitpunkt ist ein
Parameter, nicht die Systemuhr — dieselbe Datenlage ergibt dasselbe
Ergebnis, und der Dienst ist ohne Renderer prüfbar (10 neue Tests).
- `useAgentWorkload` sammelt nur die Bestände ein.
- Noras Zahl wird nie «dringend»: sie schlägt vor, sie entscheidet nicht.
- Die Schilder bemessen ihre Höchstbreite an der Szenenbreite (`cqw`), nicht
am Fenster. Am Fenster gemessen überlappten sie auf schmalen Laptops, weil
die Szene um das Seitenmenü schmaler ist.
Geprüft: 423 Tests, Typecheck, Build, ESLint, Token-Schwelle (889/925).
Im Browser 1280–1920 px: keine Überlappungen, Titel und «Filter & Übersicht»
beim Einstieg sichtbar, alle sechs Agentenklicks auf der richtigen Subpage,
Tablet-Ansicht mit allen sechs Agenten, keine Konsolenfehler, keine 404.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Hauptnavigation und Startseite (§2)
- Hauptseite «Übersicht» samt Route, Menüeintrag, Hook, Service und den nur
dort verwendeten Widgets entfernt; /supply/dashboard leitet auf die
Startseite um, damit Lesezeichen nicht ins Leere laufen.
- «Meine Objekte» heisst im Menü «Startseite»; die frühere Überschrift
«Objektverwaltung» heisst jetzt «Meine Objekte».
- Die Startseite scrollt als Ganzes. Die Höhe des Meetingraums rechnet gegen
die Viewport-Höhe, damit Titel und Balken «Filter & Übersicht» beim Einstieg
auf jeder Monitorgrösse sichtbar bleiben (geprüft 1280–1920 px).
Agenten-Meetingraum (§3)
- Zentralperspektive aus clip-path-Flächen, ohne 3D-Engine und ohne
Animationsbibliothek: rechts geschlossene Wand, links und gegenüber
boden- bis deckenhohe Glasfronten mit Blick auf Rhein, Basler Münster,
Altstadt und Wettsteinbrücke.
- Sitzordnung, Zielrouten, Grössen und Plakettenversatz liegen in einer
zentralen Konfiguration (meetingRoomSeats.ts).
- Neuer Agent Giorgio, Personalverwalter, sitzt vis-à-vis und führt in
«Meine Agenten». Er steht bewusst nicht in AGENT_WORKSPACES: er hat keine
Arbeitsseite, sondern ist die Adminfläche selbst.
- Auf schmalen Schirmen tritt eine vereinfachte Kachelanordnung an die Stelle
der Perspektive; ausgeblendet wird kein Agent.
Sticky Chat bei allen Agenten (§5, §9–§11)
- Scrollt der Chatkopf unter den globalen Balken, übernimmt dort eine kompakte
Fassung mit Porträt, Name, Frage und Eingabefeld.
- Ein gemeinsamer Zustand (agentChatStore) statt zweier Chat-Instanzen:
Eingabetext und Fokus überstehen den Wechsel in beide Richtungen.
- Noras Seite scrollte bisher gar nicht — Chatkopf ausserhalb des
Scrollbereichs, zwei getrennt scrollende Spalten darunter. Behoben über den
Seitenaufbau, nicht über eine Nora-Sonderlösung.
Nora: CRM-Kanal und gemeinsame Lead-Liste (§12, §13)
- Dritter Kanal «CRM» mit eigenem Domänentyp, Mockdaten, Provider, Service und
Hook nach dem bestehenden Schichtenmuster.
- Die Reiter «KI-Signale»/«Netzwerk» sind einer gemeinsamen Liste gewichen.
Jede Zeile trägt ihre Kanalherkunft als Badge; die Herkunft steht im
Datenmodell (LeadChannel) und wird nicht aus der Darstellung geraten.
- Der frühere Reiterbalken ist der Filter «Kanaltyp» mit Alle/KI Signal/
Netzwerk/CRM, Standard «Alle», mit Empty State und Rücksetzung.
Produkttour und Profilmenü (§8)
- Tour auf die neue Struktur umgeschrieben: Startseite, Meetingraum, Agenten,
sticky Chat, Leadkanäle. «Übersicht» kommt nicht mehr vor, «Meine Objekte»
bezeichnet nur noch den Objektbereich.
- Bereich «Demo-Modus» mit «Verwaltung» und «Bürosuche» entfernt.
Geprüft: 413 Tests, Typecheck, Build, ESLint, Token-Schwelle (889/925).
Im Browser: Navigation, Agentenklicks aller sechs Plätze, Kanalfilter und
Detailschubladen, Sticky Chat bei vier Agenten, 1280–1920 px sowie
Tablet- und Mobilbreite, keine 404 für Bilder oder Routen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Setzt das Umsetzungsbriefing Runde 4 im bestehenden Frontend um.
Informationsarchitektur
- «Teamübersicht» heisst «Meine Agenten»; ihr bisheriger Inhalt (Kreisgrafik,
Auswertung, Verbindungszusammenfassung) ist entfallen.
- Personalverwaltung, Bearbeitungsverlauf und Kanäle & Systeme sind jetzt
Reiter derselben Seite (?section=), gestaltet wie die Auswahl im
Bearbeitungsverlauf. Es wird nur der gewählte Bereich gerendert.
- Die alten Unterseitenpfade leiten um, damit Lesezeichen nicht brechen.
Agentenbestand
- Reto und Lea vollständig entfernt — aus Navigation, Dossiers, Protokoll,
Verbindungen, Vorgängen und Porträtbestand.
- Retos Aufgaben liegen bei Bruno: Nachbereitung, WhatsApp-Anruf,
Protokoll und Kundennotiz, Ablage im CRM.
- Livia ist «Exposé Master»: Lageberichte, Inserate, Angebotsbroschüren.
- Sidebar führt Ferdi, Bruno, Livia, Nora, Sina mit Porträt und Funktion.
Agentenseiten
- Ein gemeinsamer AgentWorkspaceHero auf allen fünf Seiten.
- Ferdi: Auswertungskarten, Priorität und Typfarben entfallen; Fälligkeit nur
bei fünf Tagen oder weniger rot; Objektlinks nach «Meine Objekte»; neu die
Terminplanung im verbundenen Kalender mit typgerechtem PDF-Ausschnitt.
- Sina: reduzierte, filter- und sortierbare Objektübersicht; Detailansicht
direkt editierbar, leere Pflichtfelder rot umrandet.
- Nora: Signale ohne Prozentsätze und Konfidenzstufen, nur belegbare Angaben;
Mehrfachauswahl leitet Objekte an Livia weiter.
- Livia: aktive und archivierte Leads, Arbeitsbereich gleitet an den oberen
Rand; dreistufiger Exposé-Prozess Hochladen → Exposé → Export.
- Bruno: neue Seite mit Auftragsliste und Vor-/Nachbereitungs-Drawer;
Glocke warnt bei Besichtigung unter 24 Stunden ohne Bericht.
Datenschicht
- Neu: Kalender, Exposé-Leads, Exposé-Entwürfe, Besichtigungsaufträge —
je Domain, Provider, Service und Hook.
- IAIService um generateExposeText erweitert; der Entwurf nutzt ausschliesslich
erfasste Objektdaten und meldet Lücken, statt sie zu füllen.
- Alle Objektverweise zeigen auf reale Einträge aus «Meine Objekte»; neue
Detailroute /supply/properties/:propertyId.
Offen: Chat, Kalender, CRM und DMS sind Frontend-Simulation ohne Anbindung.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kopfzeile: Der zusaetzliche Titelbalken mit dem Chip Verwaltung und dem
wiederholten Seitennamen entfaellt auf allen Hauptseiten, ebenso der
Einstieg AI Assistent. Ohne Trennlinie geht die Zeile bruchlos in den
Seiteninhalt ueber; Glocke und Profil bleiben rechts stehen. Der
Pilotkunde heisst sichtbar Beispiel Immobilien AG, die technische Kennung
org-wincasa bleibt unangetastet.
Flaechensuche und Anfragencenter sind aus Navigation, Routing und Branch
entfernt. Verweise, die dadurch ins Leere zeigten, haengen neu am Match
Center; der Quicklink ins Anfragencenter im Vorgangsdetail ist entfallen.
Meine Objekte: feste vier Karten je Zeile auf Desktop, zwei auf Tablet,
eine auf Mobil. Die Dichteauswahl 3/5/10 und der Datenpflege-Hinweis sind
weg; die Datenpflege bleibt als eigener Hauptreiter erreichbar.
Objektbilder liegen erstmals lokal. Zuvor zeigten 98 Objekte auf Unsplash
und teilten sich 29 Fotos, verschiedene Adressen trugen also dasselbe
Bild. Neu traegt jede Objekt-ID genau eine eigene Datei; die Zuordnung
laeuft ueber die stabile ID, nicht ueber den Titel. Die Dateien sind
erzeugte Vektorgrafiken, keine Fotografien - echte Aufnahmen ersetzen sie
spaeter unter demselben Dateinamen ohne Codeaenderung. Vite bettet sie
nicht mehr als Base64 ein, damit alle Objekte gleich behandelt sind.
Teamuebersicht zeigt nur noch das Kernteam; die Stufenfilter sind
entfallen und die aeusseren Ringe werden nicht mehr gezeichnet statt nur
durchsichtig zu sein - unsichtbare, aber anspringbare Schaltflaechen sind
eine Falle. Die Nabe nennt daher die Zahl der gezeigten Mitarbeitenden
statt der Gesamtzahl. Die Personalverwaltung hat nur noch die
Dossieransicht, der Umschalter und das drehbare Organigramm sind weg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Darstellung reservierte Platz fuer Ringe, die gar nicht gezeichnet
wurden: das Kernteam sass auf dem innersten Ring, zwei Drittel des
Quadrats blieben leer. Neu rueckt der aeusserste sichtbare Ring an den
Rand, Portraits und Schrift wachsen gedaempft mit.
Zweitens verdeckte die Nabe die Beschriftungen. Der Transform am Ring
eroeffnet einen eigenen Stacking-Context, in dem das zIndex der Portraits
gefangen blieb; die spaeter im DOM stehende Nabe gewann. Der Ring traegt
nun selbst ein zIndex.
Die Nabe ergibt sich ausserdem aus dem tatsaechlich freien Raum bis zum
Kernring statt aus starren 19 Prozent, und Beschriftungen sind auf den
Bogenabstand zum Nachbarn begrenzt, damit sie sich nicht ueberschreiben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Gezielte UI-Bereinigung ohne neue Architektur. Bestehende Komponenten angepasst,
Datenstrukturen und Routing unverändert.
GLOBAL
Chip «Verwaltung» und Seitenname in der Kopfzeile entfallen auf /supply/team/*;
die Seiten tragen ihren Titel bereits gross im Inhalt. Andere Module behalten die
Zeile. Demo-Modus-Hinweis und alle Untertitel entfernt. Autonomiegrad aus der
Oberfläche genommen — die Daten bleiben unangetastet.
PERSONALVERWALTUNG
Beschrieb auf zwei Sätze gekürzt, Input/Kernablauf/Output entfallen: das war
Prozessnotation, sie beschrieb wie gearbeitet wird, nicht wofür man jemanden hat.
Zuständigkeiten stehen jetzt im Kopfbereich statt tief im Dossier. Reiterfolge
neu: Agentenbeschrieb, Aufgaben, Kennzahlen, Kanäle & Systeme, Protokoll.
Kanäle und Systeme sind über eine dünne Hülle zusammengeführt — die bestehende
Logik samt Konfigurationsdialog bleibt unberührt. Aufgaben dreispaltig ohne
Zeitplan, Ereignisfilter im Protokoll entfernt.
Der abgelöste Reiter «Systeme» wird weiterhin aufgelöst, damit bestehende
Verweise nicht still auf den ersten Reiter zurückfallen.
ORGANIGRAMM — die eigentliche Korrektur
Der falsch gewählte Agent lag an der Geometrie: `rotate(a) translateY(r)` schiebt
bei a = 0 nach UNTEN, die Drehung wurde aber als `180 − index·step` berechnet.
Damit stand der gewählte Mitarbeitende oben und das Dossier darunter gehörte zum
falschen. Jetzt `−index·step`.
Dazu beruhigt: Zugempfindlichkeit von 0.5 auf 0.22 Grad je Pixel, 10 px Totzone
gegen Zittern, kurzer Zug wechselt höchstens einen Platz, Einrasten in 250 ms,
Auswahl erst nach dem Einrasten. Kreis von 720 auf 460 px.
BEARBEITUNGSVERLAUF
Filter ohne Vorgangstyp, Priorität und Kanal; Suche in Zeile eins, Rest in Zeile
zwei. Karten tragen nur noch Agent, Titel mit Liegenschaft, zwei Zeilen Text und
Zeitstempel — Priorität, Status, Vorgangstyp und Kanal standen auf praktisch
jeder Karte und trugen damit keine Information mehr.
Drawer ohne Verarbeitungsschritte und Aktionshistorie; nur die auslösende
Nachricht plus Quicklink ins Anfragencenter statt des ganzen Verlaufs.
«Fundstellen» heisst «Quellen». Der Entscheidungshinweis heisst jetzt
«Hierzu brauche ich Ihre Entscheidung». Aktionsleiste: pendent Freigeben,
Zurückweisen, Quellen anzeigen, Inserat anzeigen — erledigt nur «Objekt anzeigen».
KANÄLE & SYSTEME, TEAMÜBERSICHT
Kanäle vor Systemen als eigene Abschnitte, vier Karten pro Zeile, Beschreibung
und Statusbadge raus, Regler mit Klartext Aktiv/Inaktiv/Geplant. Die Kreisgrafik
skaliert nach Anzahl sichtbarer Ringe und ist auf die Viewport-Höhe begrenzt,
damit das Kernteam ohne Scrollen vollständig sichtbar bleibt.
Drei Testzusicherungen prüfen jetzt die Abwesenheit statt der Anwesenheit von
Objekt-ID und Rückfragegrund auf der Karte — beides wurde bewusst entfernt.
Typecheck grün, ESLint über die geänderten Dateien grün, Build grün, 413 Tests grün.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Setzt die Referenzgrafik «Agenten_Bewirtschaftung_v2» um: die 36 digitalen
Mitarbeitenden in konzentrischen Ringen, Kernteam innen und gold gerahmt.
DATENBASIS
Alle 36 Mitarbeitenden samt Porträts sind aus dem Konzeptdokument extrahiert.
Die Stufen stecken bereits in den Quelldaten und decken sich exakt mit den
Bereichen: lvl 1 = 7 Kernteam, lvl 2 = 18 Zentrale Dienste, lvl 3 = 7 GB
Kommerziell, lvl 4 = 4 GB Wohnen. Auch die Reiterbeschriftungen sind wörtlich
übernommen — keine neuen Stufennamen erfunden.
Neu getrennt: `AgentDirectoryEntry` trägt die Stammdaten aller 36 für die
Kreisdarstellung, `TeamAgent` bleibt das vollständige Dossier der sieben
Kernteammitglieder. Für die übrigen 29 werden bewusst keine Aufgaben, Kanäle
oder Tätigkeiten erfunden; ein Klick zeigt nur eine Vorschau aus dem Verzeichnis.
ABWEICHUNG VOM AUFTRAG
Der Auftrag nennt als Kernteam Ferdi, Sami, Leo, Bruno, Reto, Milo, Nora.
Referenzgrafik und Konzeptdaten sagen übereinstimmend Ferdi, Sina, Lea, Bruno,
Reto, Livia, Nora — dort tragen genau diese sieben `kern: true` und `lvl: 1`.
Da der Auftrag die Grafik selbst als verbindlich erklärt und Namen laut §10 kein
Primärschlüssel sind, folge ich Grafik und Daten. Sami, Leo und Milo kommen in
keiner Quelle vor.
OBERFLÄCHE
- Teamübersicht: Beschreibungstext und «Demo zurücksetzen» entfernt, Kreis mit
kumulativen Stufenreitern direkt unter dem Titel
- Personalverwaltung: Umschaltung Dossieransicht / Organigramm über ?view=,
Organigramm als oben abgeschnittener Halbkreis mit drehbarem Agentenrad
(Ziehen, Touch, Pfeiltasten, Klick), aktiv ist wer unten mittig einrastet
- Dossier: Untermenü direkt unter den Stammdaten, «Agentenbeschrieb» als erster
Reiter, Zuständigkeiten auf drei gekürzt
- Kanäle & Systeme: vier Karten pro Zeile, Kategorie-Sinnbild links vor dem
Titel, Porträtgruppe statt Namensliste, ein Regler statt Verbinden/Trennen
- Bearbeitungsverlauf: neutrale Karten, Farbe nur noch bei Ausnahmepriorität und
abweichendem Status, mehr Weissraum
Markenlogos gibt es im Repo nicht und wurden nicht nachgezeichnet — je Kategorie
steht ein neutrales Sinnbild. Fehlendes Asset, bewusst so gelöst.
Positionsdaten stehen ausschliesslich in `agentRingConfig.ts`; Vollkreis und
Halbkreis teilen sich dieselbe Geometrie.
AUFGERÄUMT
`AgentCard` ist durch die Kreisdarstellung ersetzt und entfernt, seine Zusagen
sind in `TeamOverviewRing.test.tsx` übergegangen — inklusive der wichtigsten:
ausserhalb des Kernteams führt kein Klick in ein Dossier.
Zwei echte Fehler unterwegs behoben: jsdom kennt keinen `ResizeObserver` (jetzt
mit Rückfallebene statt Absturz), und das Rad las eine Ref im Render.
413 Tests grün, Build grün, ESLint über den Property-On-Code sauber,
check:tokens unverändert bei 2249.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bisher zeigten die Avatare Initialen auf einer Tonfarbe. Ich hatte das bewusst
so gebaut, weil erfundene Gesichter in einer Kundendemo irreführend wären — das
Konzeptdokument «Agenten_Bewirtschaftung_v2.html» trifft diese Entscheidung
jedoch bereits: dort hängt an jedem der 36 Agenten ein Porträt als eingebettetes
JPEG. Die sieben Kernagenten sind daraus extrahiert.
- src/assets/team/*.jpg — sieben Porträts, je rund 6 KB, Zuordnung über die
Mailadresse aus dem Konzept, die exakt unseren Agenten-IDs entspricht
- agentPhotos.ts hält die Zuordnung in der Darstellungsschicht. Ein Domain-Typ,
der Binärdateien importiert, wäre an den Bundler gekoppelt und weder im Test
noch später gegen ein echtes Backend sauber verwendbar.
- Fehlt zu einer ID ein Bild, zeigt der Avatar weiterhin die Initialen; dasselbe
greift, wenn das Laden fehlschlägt
Der zugängliche Name hängt weiterhin an genau einem Element, nie an zweien: mit
Porträt am erzeugten <img> über `alt`, ohne Porträt am Container über role="img"
und aria-label.
vite-env.d.ts ergänzt — die Referenz auf vite/client fehlte, ohne sie kennt
TypeScript die Modul-Deklarationen für Assets nicht.
415 Tests grün, Build grün, die sieben JPEGs werden korrekt als Assets emittiert.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Vorher gab es im Repo kein Muster für Komponententests — @testing-library/react
wurde ausschliesslich für renderHook verwendet. test/teamTestUtils.tsx stellt den
fehlenden Kontext bereit (Router, MUI-Theme, frischer QueryClient je Test).
test/setup.ts registriert jetzt afterEach(cleanup). vitest.config.ts setzt
`globals` nicht, deshalb erkennt Testing Library kein globales afterEach und
richtet sein automatisches Cleanup nie ein; ohne diese Zeile stapeln sich
gerenderte Komponenten und getByRole scheitert ab dem zweiten Test einer Datei.
Abgedeckt: Kennzahlenberechnung, Freigabe-, Zurückweisungs-, Anpassungs- und
Rückfrageprozess, lokale Persistenz samt Schemaversion und Demo-Reset,
Badge-Familie, Vorgangs- und Agentenkarte, Aufgaben-Aktivierung,
Einstellungsvalidierung und die Position des Navigationseintrags.
Die Tests haben fünf echte Mängel aufgedeckt, alle in diesem Commit behoben:
- AgentAvatar hatte keinen zugänglichen Namen. MUI reicht `alt` nur an den
img-Slot weiter, und ohne `src` rendert Avatar gar kein <img> — der Wert landete
nirgends im DOM. Jetzt role="img" + aria-label direkt am Element.
- WorkItemCard verschluckte objectLabel, wenn keine objectId gesetzt war. Genau
dort steht bei zuordnungsfreien Vorgängen die entscheidende Einordnung.
- aria-current rendert nicht mehr "false", sondern entfällt.
- AgentChannelBadge war auf `string` statt AgentChannelType typisiert.
414 Tests grün (vorher 263), Build grün, ESLint über den Property-On-Code sauber,
check:tokens unverändert bei 2249.
README ersetzt das unveränderte Vite-Template durch Startanleitung und die
Architekturentscheidungen samt bekannter Altlasten.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Vier Seiten, eingebettet in die bestehende App-Shell — keine zweite Sidebar,
keine zweite Kopfzeile, kein eigenes Property-On-Branding:
- Teamübersicht: Auswertung (drei Kennzahlen, gemeinsam filterbar nach Zeitraum,
Bereich und Mitarbeiter), Kernteam, Kanäle & Systeme — in dieser Reihenfolge
- Bearbeitungsverlauf: zwei Reiter, neun Filterdimensionen, Detail-Drawer mit
Ergebnis, Eingabedaten, Verarbeitungsschritten, Fundstellen, Nachrichtenverlauf
und Aktionshistorie; Freigeben, Anpassen, Zurückweisen, Rückfrage beantworten
- Personalverwaltung: dreigeteilt (Hauptnavigation, Agentenliste, Dossier) mit
fünf Reitern — Aufgaben, Kanäle, Systeme, Einstellungen, Protokoll
- Kanäle & Systeme: neun Verbindungen, mehrstufiger Assistent, ERP-Auswahl,
deterministischer Verbindungstest
Navigation: NavItem um `children` erweitert, "Teamübersicht" steht exakt zwischen
"Meine Objekte" und "Reminder Manager", die drei Subreiter bleiben eingerückt
sichtbar, solange man im Funktionsbereich ist.
Subreiter sind echte Routen, keine lokalen Tabs. Die App kannte bisher keine
Feature-Route mit eigenem Outlet und baut Tabs sonst über lokalen useState — hier
trug das nicht: die Subreiter müssen in der Sidebar stehen und deep-linkbar sein.
Die Routen sind flache Geschwister, kein Outlet-Baum.
Freigabepflichtige Aktionen laufen über einen Bestätigungsdialog mit sichtbarer
Konsequenz; die 1-Klick-Bestätigung ist die fachlich vorgesehene Ausnahme. Jeder
Status trägt neben der Farbe immer einen Text.
Null rohe Hex-Werte: check:tokens bleibt unverändert bei 2249. Der in der
Spezifikation genannte dunkelblaue Banner "Heute geleistet …" existiert in dieser
Codebasis nicht — er wird deshalb auch nicht erzeugt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Property On wird als Funktionsbereich "Teamübersicht" in Property Match
eingebettet — sieben digitale Mitarbeiter, die ausführen, während der Mensch
freigibt, anpasst oder zurückweist.
Durchgehend Provider → Service → Hook, kein übersprungener Layer:
- domain/: teamAgent, agentWorkItem, agentProtocol, agentConnection, agentFilters
- mock-data/: 7 Personalblätter aus dem Agenten-Katalog (Aufgabenzahlen exakt:
Ferdi 9, Sina 7, Lea 11, Bruno 9, Reto 10, Livia 11, Nora 8), 14 Vorgänge,
38 Protokolleinträge, 9 Verbindungen
- provider/: vier Mockup-Provider mit versionierter localStorage-Persistenz
- services/: Fachlogik inkl. Freigabe, Zurückweisung, Kennzahlen, Demo-Reset
- hooks/: React Query, Mutationen entwerten Vorgänge, Kennzahlen und Protokoll
gemeinsam — sonst zeigt die Oberfläche widersprüchliche Zahlen
Namensgebung: Der Entitätstyp heisst TeamAgent, nicht Agent. Im Repo existiert
bereits PowerOn als KI-Backend-Proxy, und Claude Code legt Arbeitskopien unter
agent-* ab; ein nackter Typ Agent wäre in Suchen nicht auffindbar. Alle
Satellitentypen sind Agent*-präfixiert, weil domain/index.ts per export *
bündelt und generische Namen dort kollidieren.
Eigener Protokolltyp statt ActivityEvent: den Namen gibt es im Repo dreifach
und gegenseitig inkompatibel (domain/activityEvent.ts, governanceService.ts,
PropertyActivityLogPanel.tsx). Ein Anschluss hätte den Konflikt zementiert.
Demo-Uhr in lib/teamClock.ts: die Mockdaten sind auf den 20.05.2026 verankert.
Gegen die Systemzeit gerechnet wären die Filter "heute / diese Woche / dieser
Monat" an jedem anderen Kalendertag leer und die Auswertung sähe kaputt aus,
obwohl sie korrekt rechnet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`npx tsc -b` brach mit 16 Fehlern ab. Neue Fehler wären darin untergegangen.
Echte Defekte:
- LatentInquiriesTab: OwnPropertyMatchList wurde verwendet, aber nie importiert
— der Zweig "Eigene Objekte" auf Mobil hätte zur Laufzeit geworfen
- unitService: throwServiceError nimmt ein Argument, nicht zwei
- SavedNeedDetail: MUI v9 kennt kein `inputProps` mehr, ersetzt durch slotProps
- Review{Status,Priority}Badge: `showBorder` existiert an GenericBadge nicht;
die Default-Variante "outlined" zeichnet den Rand ohnehin, Darstellung unverändert
- UnifiedResultFeed: die Ternärkette verglich theme.text gegen zwei Werte, die
SCORE_THEME seit einer Palettenumstellung nicht mehr führt — sie lief immer in
denselben Zweig. Auf diesen einen Wert zusammengezogen, Darstellung unverändert,
nebenbei zwei rohe Hex-Werte weniger
Der Rest waren ungenutzte Importe und Bindings (noUnusedLocals).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 3/5/10-Spalten-Umschalter im Ergebnis-Header (nur Grid-Ansicht, in localStorage gemerkt) analog zu "Meine Objekte"
- Karten-Bild auf aspectRatio 15/8 statt fester Höhe → keine Verzerrung bei beliebiger Spaltenzahl
- Orts-Overlay vom Bild entfernt → Match-Score überdeckt das Ort-Label nicht mehr
- Sidebar bleibt bis < lg (1200px) ausgeklappt → Navigation + Account auf Laptops sichtbar
- Größere vertikale Abstände im Ergebnis-Feed
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add EmbeddedPropertyCard and surface matching properties inside the
public need detail view. Add needToParsedCriteria() mapper to convert a
stored Need back into parsed criteria for re-running search/matching.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "Gesendet" empty-state description embedded an ASCII double-quote inside a German „…" pair, which Vite/oxc parsed as the end of the attribute string (PARSE_ERROR). Reworded without embedded quotes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 of the messaging rework: one conversation/thread model (Inquiry) is the single source of truth, read perspectivally by both sides.
- domain/inquiry: add kind (INQUIRY|OFFER), tenantOrgId (demand org), offeredPropertyIds
- provider/service: InquiryFilters gains tenantOrgId+kind; new createInquiry (demand→supply) & createOffer (supply→demand) materialize threads; addMessage bumps unread; reply carries senderType (tenant vs supply_user)
- hooks: useCreateInquiry, useCreateOffer; reply payload carries sender perspective
- demand: InquiryQuickDialog → createInquiry via provider (session-based tenant/org, no hardcode); Anfragen.tsx reads via React Query (tenantOrgId) with Gesendet/Erhalten tabs, replies via provider, shows offered properties for OFFER threads; inquiryStore reduced to dialog-only (removes Zustand server-data island)
- supply: OfferChatComposer send now materializes an OFFER conversation into the seeker's inbox (routed via latentNeed.tenantOrgId); Anfragencenter gains a "Gesendet" tab; ActiveInquiriesTab filters by perspective (org + kind)
- seed: merge demand inquiries with tenantOrgId=org-mobimo + 2 OFFER seeds; provider seeds from both sets
Closes the loop: a sent inquiry reaches the Verwaltung and a sent offer reaches the Nachfrager — both answerable in one thread.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A newly created need (from "Jetzt suchen") had no organizationId, so it fell out of the org-filtered need list — the results "Aktive Suche" banner then showed a different (first saved) profile while the result cards already reflected the new search. needService.create now injects the current user's org, so banner and results stay consistent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
External third-party scrapes (Homegate/ImmoScout/newhome/MatchOffice) were mis-tagged as VERIFIED_PORTFOLIO in mock data and surfaced as "own platform" matches. Skip them in match generation — demand results now only contain genuine platform objects, Maison Work, and Future Availability.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Released pre-market unit now shows a single always-visible config: fit-out/parking/cost-bearer (auto-save) THEN the KI price recommendation with explicit deviation vs. current price
- Removes the duplicate price (base price no longer shown here; only the pre-market expected price is set), and removes the edit-only pencil for released units
- Not-yet-released units keep the compact read strip + pencil editor
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- getMarketRent now returns CHF/m²/YEAR (stored medians are monthly → ×12); fixes annual-vs-monthly mismatch that also affected MarketPricePanel and NegotiationInsightsPanel
- recommendPreMarketRent: anchor on the unit's current rent and adjust within a bounded ±15% by market momentum (vacancy=supply, demand strength, days-on-market, trend) instead of jumping to the city-wide (prime-skewed) median — no more "double the price" suggestions; median only used as a headroom check
- tests updated to annual market values
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- MockupUnitProvider.update: immutable update (new unit/units/property refs) so React Query detects the change and re-renders — fixes "parking/price edit doesn't take effect"
- UnitFieldsEditor: group Ausbaustandard + Wer-baut-aus + MAB into one "Ausbau" section; conditions (price/availability/parking) on top; removed the plain expected-price field
- PreMarketUnitGrid: expected price is now a KI recommendation view (indexed suggestion + rationale) shown only for released units, with an adjustable override + "Vorschlag übernehmen"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Extract UnitFieldsEditor (price, availability, Ausbaustandard, Wer-baut-aus, MAB, parking allocation, expected price) — single source for per-unit editing
- UnitStructurePanel: always-visible per-unit details strip (fit-out, cost-bearer, parking, expected price) in read mode; pencil opens the shared editor
- PreMarketUnitGrid: per-unit details shown + same editor reachable per unit (pencil) so fit-out/price/parking are adjustable right in the release flow
- Removes the previous edit-only / fragmented per-unit fields
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- lib/rentEstimate: deterministic market estimate from Standort-Intelligence (median rent benchmark + vacancy/trend) → fair rent, verdict BELOW/AT/ABOVE, deltaPct, rationale; suggestFutureRent indexes today's price by city rent trend
- MarketPricePanel in MatchDetail: asking vs. fair market rent + verdict badge + rationale; shows pre-market expected price (heute → erwartet)
- unit.expectedRentPerSqm: future pre-market price; scorer prefers it over current rent for FUTURE_AVAILABILITY
- UnitStructurePanel editor: expected-price field (with indexed suggestion) shown for pre-market-released units
- tests: rentEstimate verdict thresholds + future-rent indexing
Note: rent estimate is a deterministic market-data calc (not an LLM) — honest & testable; can be routed through IAIService later if a real model is wanted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- unit: add fitOutByLandlord, mieterausbaubeitragPerSqm, parkingSpots (parking = allocation from object pool)
- lib/unitFacts.resolveUnitFacts: single resolver (unit value ?? object value) used by scorer + card adapter
- matchSyncService: pre-market unit scored via resolved facts (fit-out, MAB, cost-bearer, parking allocation)
- matchCardAdapter: fit-out label/viability/investment via resolveUnitFacts
- UnitStructurePanel: consolidate ALL per-unit overrides into the one expandable row editor (price, availability, Ausbaustandard, Wer-baut-aus, MAB, parking allocation); show parking on row
- PreMarketUnitGrid/Panel: drop the duplicate per-unit fit-out select (now in the unit editor)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- unit.availableFrom: explicit per-unit availability date
- UnitStructurePanel: inline expandable editor (pencil) for per-unit price + availability via useUpdateUnit
- fitOutByLandlord now boolean|undefined ("not chosen"); toggle has no default for SHELL/BASIC
- NewListing: validate() blocks save until cost-bearer chosen for Rohbau/Edelrohbau
- PropertyDetailView.saveEdit: same guard for existing listings; required asterisk + hint in both forms
- newListingMapper persists explicit tenant(false) choice (was collapsed to undefined)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- domain/unit: optional per-unit fitOut override (falls back to property.hardFacts.fitOut)
- matchSyncService: pre-market unit scored with its own fitOut; matchCardAdapter prefers unit fitOut
- PreMarketUnitGrid/PreMarketPanel: per-unit Ausbaustandard override in the release list (default "Wie Objekt")
- PropertyDetailOverview: edit/read "Wer baut aus?" for existing listings (SHELL/BASIC), central FIT_OUT_LABELS
- Phase 1 unit-centric overview: header shows area+count, price as range, "Verfügbar ab → pro Einheit"; unit table shows per-unit price + availability; "Objekt & Lage" labels marked as object-defaults for multi-unit
- NeedExtendedRequirements: "Eigenes Ausbaubudget" only shown when min fit-out set + clearer helper text
- UnitStructurePanel: spin-off prefill carries fitOutByLandlord
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mieterausbau / fit-out economics — surface true total cost of occupancy:
- Annuity calc (annuityFactor + effectiveAnnualBurdenPerSqm) replaces straight-line ÷5; FITOUT_ANNUITY_RATE=5%
- New hardFacts.fitOutByLandlord: "Wer baut aus?" toggle in NewListing — landlord-borne fit-out is priced into rent (no surcharge), tenant-borne SHELL/BASIC adds annuitized cost minus MAB
- scoreBudget now compares effective annual burden (rent + fit-out annuity) vs budget instead of cold rent only; FULL/PREMIUM and landlord-borne unchanged
- FitOutCostPanel + CompareTableBody compute annuitized, tenant-aware burden
- Central FIT_OUT_LABELS with industry/international vocabulary (Rohbau·Core&Shell, Edelrohbau·CAT A, etc.)
- Activate existing generateFitOutAdvice via useFitOutAdvice hook + new FitOutAdvicePanel (MIETERAUSBAU/BKZ/MAB-Amortisation + negotiation tip), shown for tenant-borne SHELL/BASIC
- MAB field only asked when tenant builds out (optional) — one new toggle, no extra data burden for property managers
Fix: DQ/confidence modifiers were computed but never applied to finalScore (hardcoded 0 in output) — now folded into rawFinal and exposed. Trust-first: weak data quality lowers the score. Resolves 3 pre-existing red tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add per-profile notification config (toggle, score threshold, email) with Accordion UI in SavedNeedDetail
- NotificationButton: badge only lights up for unseen matches, dismisses on open (localStorage), uses per-profile minScore
- SavedNeedDetail: replace deterministicMatchScore with real match data (useMatchesByNeed), sort by score desc, deduplicate by property
- SavedProfilesTab & SavedNeedCard: top score bar now shows best real match score instead of criteria confidence
- "Treffer" count uses per-profile notificationConfig.minScore (≥80 default) consistently across card, panel, and notification badge
- Extract PropertyMatchRow to own file with Anfrage/Merken/Vergleichen/Details→/Zur Einheit→ action buttons
- Details → navigates to /demand/results/:matchId using real match ID format (m__propId__prop__needId)
- Add InquiryQuickDialog + AddToPipelineDialog to AISearch.tsx so dialogs work from SavedProfilesTab
- Org-isolation: useNeeds filters by organizationId, org-mobimo gets 4 own search profiles
- Mock data: all 18 org-wincasa need names changed to descriptive profile names; 4 new org-mobimo needs added
- useUpdateNeed mutation hook for updating need fields (notificationConfig etc.)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>