eb12119d91d3ebe43020895fa7a6b788ad799795
257
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
eb12119d91 |
docs(livia): Kostenschätzung für Sonnet 5 korrigiert
Im Kommentar zur Modellwahl stand für claude-sonnet-5 rund 0.20 USD je Lauf. Nach den aktuellen Listenpreisen (2/10 USD je Million Token) sind es rund 0.16 — genau das Doppelte von Haiku 4.5, nicht das Zweieinhalbfache. Die Preisbasis steht jetzt im Kommentar, damit die Zahlen beim nächsten Preiswechsel prüfbar bleiben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
f390caaddc |
fix(livia,nora): Runde 11 — Quellensynchronisation, Filter, Tabellenbreite, Objektdetail
1. Livia: die Systemverwaltung ist jetzt die einzige Quellenbasis. `ResearchSourcesPanel` rendert nicht mehr die statische RESEARCH_SOURCE_LIST, sondern die übergebenen Quellen aus `leseQuellenAus(agent.systems)` — dieselbe Ableitung, die auch Zählung und Lauf verwenden. Die zweite Liste ist entfernt. Damit erscheint ein frisch angebundenes System ohne Reload, verschwindet beim Deaktivieren, beim Wechsel auf «Schreiben» und beim Löschen. Die Zahl der aktiven Lesequellen steht neu auch nach einem Lauf da — vorher trat sie hinter das Ergebnis zurück, und die Wirkung eines Umschaltens war nicht sichtbar. 2. Nora: Filter nach Matchqualität und Standort in der bestehenden Filterleiste. Die Schwellen sind die bereits geführten SCORE_STRONG/SCORE_MODERATE — dieselben, nach denen sich die Farbe des Match-Werts richtet. Die Ortsliste wird aus den tatsächlichen Ergebnissen abgeleitet, nicht hartcodiert. Beide filtern auf dem bereits berechneten Wert und kombinieren mit allen bestehenden Filtern. 3. Nora: Tabelle passt ohne Querscrollen in die Content-Breite. `wordBreak: 'break-word'` war die Ursache der zerhackten Kopfzeilen («AKTI/ONE/N», «JAH/R») — ersetzt durch Umbruch an Wortgrenzen. «Aktionen» hatte 4 % und war schmaler als sein eigener Kopftext; Anteile neu verteilt, Zellabstand von 1 auf 0.75. «Miete CHF/m²/Jahr» bricht explizit nach «Miete», «Breakoutoption» heisst in zwei Wörtern «Breakout Option» und kann dadurch umbrechen. Keine Spalte entfernt, keine Skalierung. 4. Nora: Objektdetail aus der Trefferliste. Wiederverwendet die bestehende PropertyDetailView in derselben Schublade wie «Meine Objekte», geladen über die Objekt-ID. Zeilenklick und Auge öffnen dasselbe Objekt. Das Öffnen löst kein Matching aus, erzeugt keinen Protokolleintrag und erhöht die Kennzahl nicht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
15c5719762 |
fix(nora): Runde 10 Prompt 4 — Befunde aus dem Integrationsdurchlauf
Vier Dinge, die erst im Browser auffielen: - **Match-Werte von 100 % ohne Deckung.** Ein Objekt, von dem nur die Verfügbarkeit bekannt war, stand auf 100 %. Der Wert wird jetzt nach Abdeckung gedämpft: gemessen gegen die vier Kernkriterien, die immer im Nenner stehen. Fehlende Angaben zählen weiterhin nicht als Nichterfüllung — sie senken die Abdeckung, und die dämpft bis auf 40 %. - **«Belegte Objekte einbeziehen» hatte nichts einzubeziehen.** Der Bestand führte kein einziges belegtes Objekt: Der Status sagte überall «bald frei», obwohl 27 Objekte einen laufenden Mietvertrag tragen. Die Belegung kommt jetzt aus dem Vertrag — läuft er länger als zwölf Monate, ist die Fläche belegt. Dazu acht Objekte mit langlaufendem Vertrag ergänzt, damit der Schalter überhaupt etwas bewirkt. - **Sortierbare Spaltenköpfe ohne Wirkung.** In der Trefferliste waren sie anklickbar und taten nichts. Die Sortierung greift jetzt, Match-absteigend bleibt die Vorgabe. - **Noras Beschrieb sprach noch von Wochen-Mail und Review-Queue**, während Aufgaben und Systeme längst das Matching beschrieben. Geprüft im Browser gegen den Produktionsbuild: Leaddetail auf ganzer Seite, Matching-Lauf, Match-Spalte ohne waagrechte Bildlaufleiste, Schalter für belegte Objekte (90 -> 98 Kandidaten), genau ein Protokolleintrag je Lauf, Übergabe aus Livia mit genau einem selbsttätigen Lauf, Dokumentenablage samt Textgewinnung und Reload-Persistenz, Chat ohne Seitensprung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c6011ac093 |
feat(nora): Runde 10 Prompt 3 — Property Matching gegen den eigenen Bestand
Nora arbeitet jetzt mit dem Objektbestand statt mit Quellen. - Lead-Detail auf ganzer Seite statt in der Schublade; darunter der neue Bereich «Property Match». Derselbe Baustein wie bisher, nur mit einem Layoutschalter — keine zweite Detailansicht - «Das hat Livia recherchiert» heisst «Erstelltes Suchprofil» und zeigt lead.semanticSearchProfile. Ein Kasten, nicht zwei: Bestandsleads fallen auf ihren bisherigen Text zurück - Matching-Logik in features/matching/leadPropertyMatcher.ts: Das Suchprofil wird nach Merkmalen gelesen, die darin tatsächlich vorkommen, und gegen das Objekt geprüft. Ein fehlendes Objektmerkmal geht nicht in die Rechnung ein — es gilt als nicht bekannt, nicht als erfüllt. Deterministisch, kein Zufall - Trefferliste ist dieselbe Tabelle wie «Meine Objekte», ergänzt um eine erste Spalte «Match». Die übrigen Spalten werden anteilig gestaucht, damit keine waagrechte Bildlaufleiste entsteht - «Belegte Objekte einbeziehen», Vorgabe aus; Umschalten rechnet neu - MatchRun als eigene Entität mit Persistenz. Genau ein Eintrag je Lauf, auch bei der Übergabe aus Livia — eine Sperre verhindert den zweiten Lauf durch Neuaufbau der Komponente - Aus Livia übergebene Leads starten das Matching einmal; ein normal geöffneter Lead startet nichts - Kennzahlen «Anzahl durchgeführte Matchings» und «Anzahl betroffene Objekte» kommen aus dem laufenden Stand, nicht aus dem Katalog - Aufgaben, Systeme (ERP statt Zefix/SHAB/Webquellen) und Einstellungen auf das Matching zugeschnitten - Info-Knopf erklärt den Match-Wert entlang der tatsächlichen Rechnung Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
314a6a36e3 |
test(livia): Textgewinnung aus PDF und DOCX gegen erzeugte Prüfdateien
Die Extraktion ist selbst gebaut; ein stiller Fehlschlag sähe aus wie ein Dokument ohne Inhalt. Die Prüfdateien entstehen zur Laufzeit, damit im Test sichtbar bleibt, welche Struktur erwartet wird. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
30daa77937 |
feat(livia): Runde 10 Prompt 2 — eine Leadliste, dynamische Quellen, zeitliche Relevanz
Livia arbeitet nicht mehr neben dem Leadbestand, sondern in ihn hinein. - Systemzugänge tragen Titel, Beschreibung und Adresse; die Liste zeigt je Eintrag nur noch Aktiv/Inaktiv und Lesen/Schreiben — kein Berechtigungsbalken, kein Auge-Icon, keine Lese-/Schreibgruppen - Die Quellenregistry ist dynamisch: Der Lauf liest genau die aktiven Lesezugänge mit Adresse aus Livias Personalblatt. Für die drei gepflegten Quellen greift weiterhin ihr eigener Leser, für alles andere ein allgemeiner — eine Übersichtsseite, bis zu zehn Unterseiten, kein Spider. Freie Adressen werden auf http/https geprüft und gegen interne Netze gesperrt - Manuell abgelegte PDF- und Word-Dokumente fliessen in denselben Lauf. Text wird beim Ablegen nativ extrahiert (ZIP + Flate über DecompressionStream, ohne neue Abhängigkeit), Datei und Text liegen in IndexedDB - Neuer harter Filter «Zeitliche Relevanz»: das Modell beurteilt die Ereigniszeit der Veränderung, nicht das Publikationsdatum. «unknown» fällt bewusst nicht durch — ein erfundenes Datum wäre die schlechtere Antwort - Quellentypen und Beobachtungsraum sind Chips mit Freitext; jeder Wert lässt sich einzeln entfernen, auch die vorgegebenen - Info-Knopf bei «Zeitliche Relevanz» und «Mindestrelevanz» erklärt, wie der Wert entsteht — ohne erfundene Prozentgewichte - Gefundene Leads gehen in den zentralen Signalbestand statt in eine zweite Ergebnisliste; die Herkunft steht am Signal (origin: RESEARCH_RUN) - Der Lauf erzeugt einen echten Protokolleintrag mit den Zahlen des Laufs - Lead-Detail: zeitliche Einordnung mit Fundstelle, semantisches Suchprofil bei Chancen mit «Matching starten», und bei Risiko ausdrücklich «Kein internes Objekt eindeutig zugeordnet» statt einer beliebigen Mock-Immobilie - Aufgabenliste nach §11 bereinigt, «Unsichere Signale in Review Queue legen» von Nora übernommen Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
1c4b3f7dd8 |
feat(agenten): Runde 10 Prompt 1 — gemeinsame Bearbeitung für Aufgaben, Systeme und Kanäle
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> |
||
|
|
c6f3b2d6e3 |
feat(agenten): Runde 9 — statisches Sitzungszimmer, kompakte Tabelle, Chats, KI-Hinweis
**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> |
||
|
|
f2926d6bcb |
fix(livia): effort-Schalter nur an Modelle, die ihn kennen
Nach der Umstellung auf Haiku 4.5 scheiterte jeder Beurteilungsaufruf mit «This model does not support the effort parameter». Der Schalter war für Opus gesetzt und wurde bedingungslos mitgeschickt — bei einem Modell, das über eine Umgebungsvariable frei wählbar ist, darf das nicht sein. Eine Kostenoptimierung, die die Funktion zur Laufzeit kaputtmacht, ist keine. Der Schalter geht jetzt nur noch an Modelle, die ihn kennen. Bewusst als Erlaubnisliste: ein unbekanntes Modell läuft ohne ihn — das kostet etwas mehr, funktioniert aber. Die umgekehrte Voreinstellung würde raten und scheitern. Ergebnis des ersten vollständigen Haiku-Laufs auf Produktion: 49 Einträge, 38 verworfen, 2 Watchlist, 9 Leads, davon 5 mit hoher Relevanz — mehr als Opus 5 im Vergleichslauf am selben Bestand fand, bei einem Zehntel der Kosten. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f0ef9c57d4 |
feat(livia): das verwendete Modell steht im Ergebnis
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> |
||
|
|
c19f071acc |
feat(livia): Modell und Textlänge über Umgebungsvariablen einstellbar
Das Modell stand fest im Code, obwohl es die grösste Kostenstellschraube ist — ein Lauf über fünfzig Meldungen kostet mit Opus 5 rund 0.40 USD, mit Haiku 4.5 etwa 0.08. Wer die Anwendung betreibt, soll diese Abwägung treffen können, ohne den Quelltext anzufassen und neu zu deployen. `LIVIA_RESEARCH_MODEL` wählt das Modell, `LIVIA_RESEARCH_TEXT_LIMIT` die Menge Artikeltext je Meldung — der zweite grosse Hebel, weil der Immobilienbezug einer Firmenmeldung fast immer im ersten Absatz steht und jede Halbierung die Eingabekosten halbiert. Beide haben Vorgabewerte; ohne gesetzte Variablen verhält sich alles wie bisher. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e986e9f274 |
fix(livia): ein Totalausfall der Beurteilung wird nicht mehr verschwiegen
Beim Messen des Tokenverbrauchs fiel auf, dass ein Lauf «49 Informationen analysiert · 0 nicht relevant · 0 Watchlist · 0 relevante Entwicklungen» meldete — was wie ein sauberer Lauf ohne Treffer aussieht. Tatsächlich war jeder einzelne Beurteilungsaufruf mit «Your credit balance is too low» gescheitert. Der `catch` um den Teilstapel fing den Fehler ab, damit ein Ausfall nicht die übrigen mitreisst, und verlor dabei den Grund. Scheitern alle Teilstapel, ist das kein leeres Ergebnis, sondern ein Ausfall. Der Grund wird jetzt weitergereicht und erscheint als Warnung über der Liste. Scheitern nur einzelne, bleibt es beim bisherigen Verhalten: die übrigen Leads werden gezeigt. Das ist die unangenehmste Fehlerklasse überhaupt — eine, die wie ein Ergebnis aussieht. In einer Präsentation hätte niemand gemerkt, dass Livia gar nicht gearbeitet hat. Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen; der Lauf meldet den Fehler jetzt sichtbar statt eine leere Liste. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a028d1a21d |
feat(livia): Zefix als Marktindikator statt als Lead
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> |
||
|
|
97b7110a44 |
fix(livia): Einheit an der Quellenanzeige
«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> |
||
|
|
31a098f219 |
fix(livia): eigenes Zeitlimit für die Zefix-CSV
Im Produktionslauf fiel Zefix mit «This operation was aborted» aus, während beide Newsquellen sauber lieferten. Ursache ist die Dateigrösse: 10,5 MB, unkomprimiert, weil der Server kein gzip anbietet. Von einem Schweizer Anschluss dauert der Abruf 1,7 Sekunden, aus dem Rechenzentrum der Anwendung mehr als die fünfzehn, die für die Newsquellen reichlich bemessen sind. Die CSV bekommt deshalb dreissig Sekunden, die HTML-Abrufe behalten fünfzehn. Beides läuft parallel, der Ausfall einer Quelle bleibt folgenlos für die anderen — nur war es bisher immer dieselbe, die ausfiel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f58d841e7d |
feat(livia): drei Übersichtsseiten je Newsquelle statt einer
Eine Seite reicht nicht. Die Handelskammer erneuert ihre obersten zehn Meldungen im Lauf eines Tages; stehen dort gerade Personalien und Meinungsbeiträge, findet Livia zu Recht nichts, und die Demo wirkt schwach, obwohl die Mechanik stimmt. Genau das war zwischen zwei Läufen zu beobachten. Neu werden je Quelle drei Übersichtsseiten gelesen — bei der Handelskammer über `…/page/N.html`, bei Greater Zurich über `?page=N` —, gedeckelt auf 24 Artikel je Quelle. Die Artikel werden parallel geholt statt nacheinander, die Beurteilung läuft in begrenzt parallelen Fünferstapeln. Wirkung, gemessen am selben Tag: aus 20 gelesenen Einträgen und 1 Lead werden 49 Einträge und 8 Leads, darunter zwei mit hoher Relevanz — eine Arealentwicklung über 38 000 m² und eine Standorteröffnung. Laufzeit 30 Sekunden statt 16, weiterhin klar innerhalb des 60-Sekunden-Limits. Die Begrenzung der Gleichzeitigkeit ist kein Detail: alle Teilstapel auf einmal loszuschicken wäre schneller, läuft aber in die Drosselung des Anbieters — und ein gedrosselter Lauf sieht für den Nutzer aus wie ein kaputter. Ein gescheiterter Teilstapel reisst die übrigen nicht mehr mit. Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen, Build erfolgreich, Live-Lauf 49 Einträge aus 3/3 Quellen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
f53f3be1e8 |
fix(livia): Dateiendungen in den Importen der serverlosen Funktion
Erster Produktionslauf schlug mit HTTP 500 fehl, lokal lief derselbe Code grün. Ursache: Vercel kompiliert jede Datei einzeln statt sie zu bündeln, und weil das Projekt `"type": "module"` ist, verlangt Nodes ESM-Lader explizite Dateiendungen in relativen Importen. Vite löst im Entwicklungsbetrieb grosszügiger auf und verdeckt das. Das ist genau die Klasse Fehler, die kein Typecheck und kein Test findet — nur ein echter Aufruf gegen die deployte Funktion. Geprüft gegen Produktion: HTTP 200, 20 Einträge aus 3/3 Quellen in 24 Sekunden, Beurteilung läuft, Originalquellen verlinkt, keine Laufzeitfehler. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
93ecc852e3 |
fix(livia): Beurteilung in Fünferstapeln statt einem Zwanzigerstapel
Der erste Live-Lauf mit Schlüssel deckte auf, dass ein einziger Aufruf über alle zwanzig Einträge das Urteil verwässert. Eine Meldung über eine zweistellige Millioneninvestition in eine neue Brauerei samt Arealentwicklung fiel als «nicht relevant» durch; derselbe Prompt, dasselbe Modell und dieselbe Meldung ergaben in einem Stapel von drei «Investition, hohe Relevanz, 0.85». Eine höhere Effort-Stufe half nicht, sie machte es messbar schlechter. Der Hebel ist die Stapelgrösse. Die Teilstapel laufen parallel — die Laufzeit ist damit die des langsamsten und nicht deren Summe: 48 Sekunden vorher, 16 danach. Das ist zugleich die Voraussetzung fürs Deployment: die serverlose Funktion hat voreingestellt 10 Sekunden Zeit. `vercel.json` hebt das Limit nun ausdrücklich auf 60 Sekunden an, was auch bei einer langsamen Quelle reicht. Ausserdem: Der Linktext der Handelskammer trägt das Publikationsdatum vorneweg und ein »-Zeichen hinten. Beides stand bisher im Titel, den das Modell zu lesen bekam, und gehört dort nicht hin. Geprüft: tsc 0 Fehler, eslint 0 Fehler/0 Warnungen, Live-Lauf 20 Einträge aus 3/3 Quellen in 17 Sekunden. Die Urteile sind stichprobenweise gegen die Originalartikel geprüft und tragfähig — eine Bäckereieröffnung in Westaustralien und eine Übernahme in Thailand werden zutreffend als ohne Schweizer Flächenbezug verworfen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
2a0ba287f6 |
feat(livia): Research-PoC — drei reale Zürcher Quellen live
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> |
||
|
|
37b6270f50 |
feat(thomas): freundlicheres Porträt (Runde 8, §3)
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> |
||
|
|
a219d3f2ec |
feat(livia)!: Exposé-Funktion vollständig entfernt
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> |
||
|
|
f5451f649f |
fix(agenten): Thomas' Chat klappt wirklich ein, Einzahl bei Livias Zählung
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> |
||
|
|
2037e5a463 |
docs(agenten): Fallakte Livia und Agenten-Briefing sichern
`LIVIA_PROFIL_UND_PROMPT.md` hält Livia als Exposé Master fest, wie sie vor Runde 8 war: Personalblatt, Aufgabenkatalog, Kanäle und Systeme, das Exposé-Dossier mit elf Bereichen und neun Pflichtfeldern, die `missingFacts`-Regel der Textmaschine — dazu ein System-Prompt, mit dem sich diese Fassung ausserhalb des Repositories rekonstruieren lässt. `archive/livia/` ergänzt die Beschreibung um die Wiederherstellung: die 23 Dateien der Exposé-Funktion als wörtliche Kopie im Original-Pfadlayout sowie ein Manifest jeder Stelle, an der Livia in geteilten Dateien verdrahtet war. Beides ist mit Runde 8 nicht überholt, sondern der Referenzstand: die Exposé-Funktion wurde nicht entfernt, sondern nachgeordnet. Das Manifest beschreibt allerdings den Rückbau eines vollständigen Entfernens und damit ein Szenario, das so nicht eingetreten ist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ba8dc39483 |
feat(agenten): Runde 8 — Livia recherchiert, Nora matcht, Ferdi liest Verträge
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>
|
||
|
|
55274488d1 |
fix(startseite): Porträts scharf statt weich, besonders die hinteren
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> |
||
|
|
403161283c |
fix(startseite): saubere Skyline links, deutlich grössere Agentenbilder
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> |
||
|
|
9e825c04bf |
feat(agenten): Porträts in 512×512 aus den Originalen der Präsentation
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> |
||
|
|
4349641ee9 |
fix(startseite): durchgehendes Basel-Panorama, Kreise ohne Stühle
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> |
||
|
|
bf453d2b69 |
feat(startseite): echter 3D-Meetingraum, Navigation in die Topbar
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> |
||
|
|
e55be64aeb |
fix(startseite): kein Innenscroll mehr, fünf Karten in einer Reihe
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> |
||
|
|
42a9bece36 |
fix(startseite): Belegschaft auf grossen Bildschirmen fassen
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> |
||
|
|
093a958dcf |
feat(startseite): Giorgio entfernt, Belegschaft auf die fünf Agenten
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> |
||
|
|
435f2aa2f1 |
feat(startseite): digitale Belegschaft mit Präsenz, eigener Stimme und Nachweis
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> |
||
|
|
d8f8f728cc |
refactor(startseite): Meetingraum als Fotoszene mit Live-Auslastung
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> |
||
|
|
9ed21e33a7 |
feat(runde-5): Startseite mit Agenten-Meetingraum, sticky Chat und Noras Kanäle
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> |
||
|
|
d200d4e930 |
feat(agenten): Runde 4 — «Meine Agenten» mit fünf digitalen Mitarbeitenden
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> |
||
|
|
cc5582b7fd |
refactor: dritte Bereinigungsrunde Property On
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> |
||
|
|
41acb7b306 |
fix(team): Kreisgrafik fuellt die Flaeche und deckt keine Namen mehr zu
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> |
||
|
|
177641386a |
refactor(team): zweite Überarbeitungsrunde — Oberfläche bereinigen
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> |
||
|
|
8377ce03b5 |
feat(team): Kreisdarstellung der Belegschaft und Organigramm-Ansicht
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> |
||
|
|
576733d7da |
feat(team): Porträts der digitalen Mitarbeiter aus dem Konzept übernehmen
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> |
||
|
|
a7e05fb7f0 |
test(team): Render-Harness, 151 neue Tests und README
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> |
||
|
|
78e2dab655 |
feat(team): Oberfläche der Teamübersicht
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> |
||
|
|
52dc7de25e |
feat(team): Datenschicht für Property On
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> |
||
|
|
36ce548169 |
fix: 16 vorbestehende Typfehler beheben, Typecheck wieder grün
`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>
|
||
|
|
3849b78f83 |
chore: Alt-Worktree aus Git entfernen und ESLint entsperren
.claude/worktrees/agent-a82a3716/ lag mit 369 getrackten Dateien im Repo — eine vollständige, veraltete Kopie von src/. Zwei Folgen: `eslint .` brach mit 965 fatalen Parse-Errors ab, weil der Worktree eine eigene tsconfig mitbringt und tsconfigRootDir mehrdeutig wurde. Und jeder repo-weite Glob traf Duplikate, was die reale Gefahr barg, Änderungen in der Kopie statt im Original zu machen. - git rm -r --cached auf das Verzeichnis, Dateien auf der Platte bleiben - .claude/worktrees/ in .gitignore - .claude/** in die ESLint-Ignoreliste; Untracking allein genügt ESLint nicht, es liest die Platte, nicht den Index ESLint liefert damit statt 965 Parse-Errors wieder echte Befunde. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
362514681a |
feat(demand): Ergebnis-Grid — Spalten-Umschalter, sauberere Karten, mehr Abstand
- 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> |
||
|
|
07cea21143 |
feat(supply): Anfragencenter — embedded property cards in public need detail
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> |
||
|
|
be5523bcee |
fix(supply): Anfragencenter parse error — stray quote in JSX attribute string
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> |
||
|
|
a8b54af0b8 |
feat(messages): unified conversation inbox — connect demand ↔ supply, offers reach the seeker
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> |