Commit Graph
7 Commits
Author SHA1 Message Date
Benjamin SutterandClaude Opus 5 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>
2026-08-30 14:23:35 +02:00
Benjamin SutterandClaude Opus 5 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>
2026-08-12 18:00:00 +02:00
Benjamin SutterandClaude Opus 4.8 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>
2026-06-23 00:03:36 +02:00
Benjamin SutterandClaude Sonnet 4.6 0582031930 test: Vitest test infrastructure + 52 unit tests for matching engine
Install vitest, @vitest/coverage-v8, jsdom, @testing-library/react/jest-dom.
Add test / test:watch / test:coverage scripts to package.json.

Three test suites covering the business-critical scoring pipeline:

scoreCalculator.test.ts (28 tests)
- calcDataQualityModifier: all 5 boundary thresholds (+5 / 0 / -5 / -10 / -15)
- calcConfidenceModifier: verified/external/maison-work/future + low-conf stacking
- applyHardFilters: pass, wrong asset type, area tolerance, budget exclusion, OCCUPIED penalty, excluded city
- calculateScore: strong match ≥85, weak match <50, excluded=0, determinism,
  formula verification, DQ+confidence direction, occupied 25-point penalty,
  positive factors, allHardFactors completeness

rankingEngine.test.ts (10 tests)
- matchStrengthFromScore: STRONG/MODERATE/WEAK boundaries (78/52)
- rankMatches: score sort, type tiebreak (VERIFIED > EXTERNAL), confidence tiebreak
- buildFullMatch: all explainability fields, SHORTLIST+CONTACT for strong match,
  SCHEDULE for future signals, uncertainty indicators

matchCardAdapter.test.ts (14 tests)
- VERIFIED_PORTFOLIO: resultType, title, locationLabel, matchScore, scoreBreakdown,
  no disclaimer, reasons from positiveFactors, actions passthrough
- FUTURE_AVAILABILITY: resultType, disclaimer always present, signalProbability,
  signalQuality HIGH for probability >= 0.70

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 02:33:12 +02:00
Benjamin SutterandClaude Sonnet 4.6 a002597f5b feat: drag & drop for Pipeline Kanban columns (@dnd-kit)
Cards can be dragged between all 7 stages. Drop target highlights with
a dashed border + tinted background. A floating card overlay follows
the cursor during drag. Click-to-select still works (fires only when
no drag occurred, guarded by activeId check).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 21:34:00 +02:00
Benjamin SutterandClaude Sonnet 4.6 5b917bd619 feat: embedded interactive map in property + match detail views
Integrates react-leaflet with OpenStreetMap tiles. PropertyDetailView
and MatchDetail now show property photo + zoomable/pannable map above
the tabs/content — always visible on card click, no redirect. Map
renders at correct lat/lng with a location marker and popup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 13:34:57 +02:00
Benjamin Sutter 9e827c50f9 Initial commit 2026-05-15 00:48:18 +02:00