FP_POC / STATUS.md
github-actions[bot]
Space-Deploy aus GitHub main
9de795a
|
Raw History Blame Contribute Delete
80.5 kB
# STATUS – wo stehen wir?
> **Übergabe-Dokument zwischen Arbeits-Sessions.** Jede Session (Mensch oder
> KI) aktualisiert dieses File nach jedem abgeschlossenen Schritt: Was ist
> fertig, was ist in Arbeit, was ist als Nächstes dran, welche bewussten
> Abweichungen gibt es. Meilenstein-Definitionen: Brain →
> `vault/50_Umsetzung/Bauplan-Meilensteine.md`.
**Stand: 2026-09-30**
> ℹ️ Die Arbeiten 2026-07-16 bis 2026-08-04 (Farbwelt 16→33 Slugs, +28 Möbel
> inkl. Vorfilter-Passung, L-Sofa 2D=3D, Viewer-Fixes, Kurator v3.3 mit
> schlankem Repair und erkannten Abschneidungen) sind nicht als eigener
> Abschnitt hier, sondern als Learning im Brain zusammengefasst:
> `vault/10_Learnings/Learning-Kurator-v33-Katalog-Varianten-Tokenbudget.md`
> (Details: Commits `3f3c1ea` … `85a71a3`).
### Scan ohne LiDAR: Video → MapAnything im Colab-Worker (2026-09-30, ADR-0016)
Bryans Vorgabe: Scan **muss ohne LiDAR** gehen (USP). Recherche: keine Gratis-
iOS-App exportiert ohne LiDAR Video + Posen (Stray Scanner nur LiDAR,
NeRFCapture nur Einzelbilder) → AR-Posen-Pflicht aus ADR-0012 fällt.
- **Neuer Standardweg** in `services/scan-worker`: normales Video →
`frames` (Schärfe = Laplace-Varianz, zeitbasiert N Keyframes/s, Default 3,
verwackelte < 35 % Median raus, max. 120) → **MapAnything**
(`facebook/map-anything-apache`, Apache) → `rekonstruktion` (Hochachse aus
Bild-oben aller Kameras, gedrehtes Video erkannt, Verfeinerung per
Bodenebene im unteren Höhenband + SVD-Fit ≤ 10°, Boden → z = 0, Diagnose
Raum-/Kamerahöhe) → optional Massstab aus bekannter Raumhöhe →
`fuse_mit_farben` (2 cm) → PLY → SpatialLM. GPU-Speicherfehler → Wiederholung
mit halb so vielen Frames. AR-Posen-Weg bleibt (`.zip` mit `poses.json`).
- **SpatialLM in eigenem venv** (`FP_SPATIALLM_PYTHON`, pinnt torch 2.4.1),
MapAnything im Colab-Python; GPU-Speicher dazwischen freigegeben.
Notebook `colab_worker.ipynb` entsprechend (Setup, Regler, Filmtipps).
- **Tests:** scan-worker 22 → 41 (Frame-Auswahl; synthetischer Raum in
beliebig gedrehtem Weltsystem inkl. gedrehtem Video und 4° Handy-Kippung;
Verdrahtung mit gestubbten GPU-Teilen inkl. Speicher-Rückfall).
**Nicht getestet: echter Colab-Lauf** (Installation, T4-Speicher, Qualität).
- **Als Nächstes:** Bryan startet Colab mit den alten R1-Videos (erster Lauf =
Gate für Installation/Speicher) → R1 neu filmen (normale Kamera-App) +
Restmasse → Messung gegen Ground Truth → Learning. Danach M7: Massstab-
Bestätigung im Korrektur-Modus, Live-Weg als Job + Polling.
### Vercel-Eingang Phase 2 – erster Deploy grün (2026-09-29)
- **Preview live** (Workflow «Deploy Vercel» Lauf #8, Branch
`claude/keen-meitner-0qijkc`): Build 14 s, Upload 20.8 MB, Vercel-Projekt
`bryan-125a/fp-poc`. Produktions-Domain ist **`fp-poc-seven.vercel.app`**
(`fp-poc.vercel.app` war vergeben; in Vercel → Domains änderbar).
- **Token-Odyssee** (Läufe #1–#7, «Could not retrieve Project Settings» bzw.
«User not found (404)»): IDs waren korrekt, der Token nicht nutzbar. Gelöst
mit neu erzeugtem persönlichem Token (Account Settings → Tokens, Scope «Full
Account»). Dafür neuer Workflow-Schritt «Vercel-Konfiguration prüfen»
(Leerraum, `prj_`/`team_`-Präfixe inkl. Vertauschung, Token-Länge,
`project ls` im Team) – bleibt drin, spart beim nächsten Mal die Suche.
- **Brain nachgeführt:** ADR-0015, Learning Vercel-Eingang,
`POC-Demo-Architektur-HF`, Scan-Fahrplan («Job + Polling Pflicht»).
- **Als Nächstes:** Bryan testet die Preview am Handy (Checkliste: Start/Weck-
schirm, Bad/Wohnen/Küche + Vorschlag, Swipe-Fotos, Export, Scan-Bundle,
Vergleich HF). Danach Branch → `main` = erster Produktions-Deploy (wartet auf
`/api/health.build`). Offen: 27-MB-Upload durch den Rewrite + Kaltstart live.
### Vercel-Eingang Phase 1 – Fundament für zwei Frontends (2026-09-28)
Ziel (mit Bryan besprochen, Variante «K5»): Frontend ZUSÄTZLICH auf Vercel
(`fp-poc-seven.vercel.app`), `/api/*` per Rewrite an den HF-Space; der Space bleibt
vollständige Referenz (Frontend + API wie bisher). Phase 1 = alles, was beide
Eingänge brauchen – wirkt schon jetzt auf HF. Abwägung K1–K7 (Timeouts,
4.5-MB-Body, Kontingente, Nutzerreise): kommt als ADR-0015 ins Brain.
- **Swipe-Bilder statisch** unter `/bilder/...` aus dem Build: Vite-Plugin
`fpBilder()` (`vite.config.ts`) – Dev-Middleware mit Path-Traversal-Schutz,
Build kopiert die 114 Fotos nach `dist/bilder/` (nur beim echten Build).
`Stil.tsx` baut URLs über `bildUrl()` (segmentweise kodiert – Dateinamen mit
Leerzeichen/Klammern). `/api/bilder` bleibt als Rückfall bestehen.
- **Weck-Logik** (`backend.ts` + `api.ts`): HTML statt JSON (HF-Ladeseite eines
schlafenden Space, auch mit Status 200; Proxy-Fehlerseite) oder Netzfehler →
EIN gemeinsamer Weckvorgang (`/health`-Pings 2/3/5 s …, max. 3 min) → Anfrage
genau einmal wiederholt (unbedenklich: alle Endpunkte ohne Server-Zustand).
App zeigt Ladezustand-Variante `wecken` bzw. Fehlerkarte «Erneut versuchen»;
der Hinweis «pnpm api starten» erscheint nur noch im Dev-Modus.
- **Kurator-Gesamtdeadline** `FP_KURATOR_DEADLINE_S` (Default 90 s, `0` = aus)
über alle LLM-Calls inkl. Repairs und 429-Backoff: unter 5 s Restzeit kein
neuer Call, HTTP-Timeout auf Restzeit gekürzt, ein Backoff, der das Budget
sprengen würde, bricht ab → ehrlicher (Teil-)Fallback + Marker
`CURATOR_DEADLINE`. Die ungültige Antwort wird jetzt VOR dem Repair gesichert
→ die Platz-Rettung greift auch, wenn für den Repair keine Zeit mehr bleibt.
Grund: Vercel-Rewrites warten max. 120 s aufs erste Byte. LLM-Eval-Workflow:
Deadline aus (misst KI-Qualität unter Drosselung, nicht Fallbacks).
- **API:** `Cache-Control: no-store` auf allen Antworten ausser `/bilder`
(Middleware liest den Pfad VOR `call_next` – Starlette mutiert `root_path` im
Mount); `/health` meldet additiv `build` (Git-SHA aus `BUILD_SHA`, schreibt
`deploy-space.yml`; `git add -f`, weil die mitkopierte `.gitignore` sie sonst
im Deploy verschluckt); `.webmanifest`-MIME in `space.py`.
- **PWA:** `manifest.webmanifest` («FP POC», standalone, Theme #243D35), Icons
180/192/512/512-maskable aus dem Signet (deckend weiss, sonst füllt iOS
schwarz), Meta-Tags in `index.html`. Bewusst KEIN Service Worker.
- **Tests:** web 223→243, engines 434→454 (Deadline mit simulierter Uhr,
Weck-Logik mit gemocktem fetch + Mutationstest). E2E-Smoke `space.py` lokal:
Manifest/Icons/`/bilder`/`/api/bilder`/`/api/health` wie erwartet.
- **Phase 2 (Code fertig, wartet auf Bryans Vercel-Projekt + Secrets):**
`vercel.json` (Build aus dem Monorepo-Root, Output `apps/web/dist`, Rewrite
`/api/*` → `bryan-hslu-fp-poc.hf.space/api/*`, Git-Deploys AUS) +
`deploy-vercel.yml`: Produktion erst, wenn `/api/health.build` = Commit-SHA
(max. 20 min, `cancel-in-progress` spart Actions-Minuten), Previews für
andere Branches; gebaut per `vercel pull/build/deploy --prebuilt` auf dem
Runner (CLI 60.1.3 gepinnt, gleiche Toolchain wie CI, Commit-Autor egal).
Ohne Secrets übersprungen. Lokaler `vercel build` geprüft: nur statisch
(22 MB, 114 Bilder, Icons, Manifest), KEINE Functions, Routing =
filesystem → Rewrite. README «Deploy» + CLAUDE.md §11 nachgeführt.
- **Als Nächstes:** Bryan legt Projekt `fp-poc` an + 3 Secrets → Preview-Test
(27-MB-Upload durch den Rewrite, Kaltstart live, Handy-Durchgang auf beiden
URLs) → Produktion; danach ADR-0015 + `POC-Demo-Architektur-HF` +
Scan-Fahrplan «Schritt 5 = Polling Pflicht».
### LLM-Läufe real: Kurator antwortet echt – Groq-Debugging-Saga (2026-07-15)
6 Workflow-Läufe (`kurator-llm-eval.yml`, Secret FP_KURATOR_API_KEY) bis zur
ersten vollständigen echten KI-Antwort. Fehlerkette (je Lauf diagnostiziert):
1. **413**: Call-A-Prompt zu gross (volle achsenTags-JSON je Kandidat) →
v3.2 Prompt-Diät (Kompakt-Stil-Notation, Kandidaten-Budget, adaptive
Kürzungs-Leiter, FP_KURATOR_MAX_PROMPT_TOKENS).
2. **413 trotz Diät**: Groq zählt Prompt + MAXIMALES Antwort-Fenster; ohne
max_tokens wird das volle Kontextfenster angenommen → v3.2.1 max_tokens
explizit (FP_KURATOR_MAX_ANTWORT_TOKENS) + 429-Backoff.
3. **400 json_validate_failed**: Qwen3 denkt DEFAULT laut, <think>-Präfix
bricht response_format=json_object, der gescheiterte Call verbrennt das
Minutenbudget → v3.2.2 reasoning_effort=none für Qwen3 (env-übersteuerbar).
4. **429**: Free-Tier TPM (Qwen 6k, Llama 12k) drosselt Serien → v3.2.3
Backoff 3×/30s + FP_EVAL_PAUSE_S zwischen Läufen.
**Ergebnis (Llama-3.3-70b-versatile):** 3/9 Diagnose-Kombis mit VOLLER echter
Antwort, alle 6 Plausibilitäts-Ampeln grün – z.B. wohnen×mittig ohne jeden
Marker: Konzept (Eiche+Leinen, erdig), 4 Stühle zum Esstisch, Farben je Item,
Boden parkett-eiche, Wände putz-warm + EINE Täfer-Akzentwand. Rest-Fallbacks
nur noch 429 (Budget). 413/400 mit Llama komplett weg.
**Empfehlung:** Space-Secret FP_KURATOR_MODEL auf llama-3.3-70b-versatile –
Einzelnutzer-App (3 Calls/Plan) liegt locker im 12k-TPM-Budget. Qwen3-Pfad
bleibt funktionsfähig (Thinking default aus). Offen: Eval-Gate-Vergleich fair
machen (9 vs 45 Läufe), Norm-Korrektur-Rate erst mit mehr echten C-Calls
aussagekräftig.
### Welle C – UI-Trio + Diagnose/LLM-Workflow + Norm-Verifikation (2026-07-14/15)
- **UI-Trio:** Einzelwand-Materialwahl (`apps/web/src/wandauswahl.ts`, Panel-
Wandliste 1-basiert angezeigt, 2D-Highlight, Norm weiterhin über
`/flaechen/pruefen`) · **«3 Vorschläge»-Karten** in SchrittVorschlag
(Mini-2D readonly + Kennzahlen; `/solve` `variantenDetails` → additiv
`variantenPlaene` best-first) · **2D-Flächen-Layer** (Default AN, gleiche
Auflösung wie 3D via `flaechen2d.ts`, Legende; AUS = alte Darstellung,
testbelegt). Offen: Relations-Score in Karten roh/negativ → normalisieren.
- **Merge-Erkenntnis C×D:** Begehbarkeits-Leiter in `varianten._waehle_begehbar`
zentralisiert — der Karten-Kopf ist IMMER der gelieferte begehbare Plan
(auch wenn nicht Score-Sieger), Rest best-first; Test präzisiert. Dabei
`by_id`-NameError im Reduktions-Zweig gefixt (Folge des Parallel-Refactors).
- **Diagnose-Harness** `scripts/kurator_diagnose.py` (ADR-0014): Raum×Profil-
Matrix, echte Kurator-Antworten + 6 Plausibilitäts-Ampeln; Baseline 45/45
ohne ⚠️, byte-deterministisch. **LLM-Lauf:** Workflow
`.github/workflows/kurator-llm-eval.yml` (Runner haben Netz zu api.groq.com,
Remote-Sessions nicht; Thinking per Input `reasoning`) — **wartet auf
Repo-Secret `FP_KURATOR_API_KEY`** (Klartext-Key als Input wird vom
Secret-Scanning korrekt blockiert).
- **Flächen-Normregeln verifiziert:** Küchen-Spritzzone 1.2→**1.5 m**
(Fliesenspiegel-OK ~1.5 m ab Boden), Nasswand 2.0 m bestätigt (DIN 18534:
≥20 cm über Brausekopf), Rutschhemmung Ziel bfu GB2/B (erst mit Produkt-
daten prüfbar). Quellen: Brain `Quellen-Flaechen-Normen`.
### Welle D – softScore bad/wohnen echt + Begehbarkeit hart am Planende (2026-07-15)
Zwei Qualitätspunkte aus dem v3-Fund («softScore bad/wohnen immer 0.0») und dem
circulation-Learning (Metrik vertrauenswürdig, Hürde war nur Hot-Path-Performance).
- **softScore stil/relation echt befüllt (alle Raumtypen konsistent):** Muster
des Küchen-Ergonomie-Patches – der Domänen-Solver schreibt NACH dem
Interpreter-Lauf in die vorhandenen Felder; Interpreter/Schema/Goldens
unberührt (Paritäts-Gesetz aus dem Weg). `stil` = mittlerer Stil-Score der
platzierten Items (Wiederverwendung `kurator.stil_score`, kein Duplikat);
`relation` = Anteil erfüllter relationaler/Anordnungs-Wünsche am fertigen
Plan (gleiche Quelle `baue_rel_map` wie Platzierung + K-Scoring, binäre
v0-Urteile `solver._relation_erfuellt`, ohne Wünsche vakuum-wahr 1.0);
`ergonomie` bleibt Küchen-spezifisch (Arbeitsdreieck). Beispielwerte
bad-sample seed 1 mit Profil: stil 0.14 · relation 1.0.
- **Begehbarkeit hart am Planende (Weg «hart am Ende», nicht im Filter):**
Messung: circulation kostet das 10–13-fache ALLER harten Regeln (~15 ms Bad /
~33 ms Wohnen vs. 1.4–2.6 ms) bei 22–47 Feasibility-Aufrufen je solve ⇒ hart
im Filter wäre ~7–10× langsamer; zudem globale Grid-Eigenschaft, nicht
inkrementell. Regel-JSON bleibt **soft** (Parität/Goldens byte-identisch);
die Härte lebt in der Liefer-Schleife: `solver.solve_begehbar` (API-Pfad ohne
Varianten) prüft den fertigen Report, bei Verletzung deterministischer
Re-Solve mit reduzierter Auswahl (letztes P3-/Ergänzungs-Item ohne
Anker-Pflicht, max. 3), danach VOLLER Plan mit sichtbarem Hinweis
(«BEGEHBARKEIT NICHT ERREICHT» im circulation-`ruleResult.hinweis` –
constraintReport ist additionalProperties:false, darum kein neues Feld).
K-Varianten (`loese_mit_varianten`): Leiter = nächstbeste BEGEHBARE Variante
→ Reduktions-Re-Solve → Hinweis; Massnahme additiv in
`varianteInfo.begehbarkeit`, Rangfolge sonst byte-identisch (dokumentiert +
getestet). Küche: letzte P3-Deko-Platzierung entfernen (max. 3) → Hinweis.
- **Tests:** neues `test_begehbarkeit.py` (54 Tests): Property ⭐ «jeder
gelieferte Plan begehbar ODER sichtbarer Hinweis» über bad/wohnen/kueche ×
Seeds + Varianten-Pfad, 0-❌-Invariante, Determinismus beider Leitern,
echter unfixbarer Bestands-Engpass (Hinweis-Pfad), Reduktions-Mechanik,
softScore-Diskriminierung/Defaults. `test_varianten`/`test_solve_endpoint`
«best ≥ v0» präzisiert (gilt, ausser die dokumentierte Leiter greift).
- **Wirkung (echter Evaluator, seeds 0–11):** bad-sample 8 direkt ok · 1 durch
Reduktion · 3 Hinweis; wohnen-sample 6 · 1 · 5; Varianten-Pfad wohnen: alle
Seeds begehbar (meist via «andere-variante»). Küche unverändert (knapp/ok).
- **Bewusst offen:** Regel-`hinweis`-Text in `data/rules/basis.json` erwähnt
noch «Hochstufung braucht Solver-Support» – Textupdate würde die 3 goldenen
Paritäts-Fixtures anfassen (dort eingebettet), bewusst verschoben. Learning-
Loop-Notiz ins Brain (Weg-1-Entscheid + Messzahlen) steht aus.
### Kurator v3.1 Welle A – Objekt-Ebenen + Anzahl + Thinking (2026-07-14, ADR-0014)
Vorgabe: Brain `ADR-0014-objekt-ebenen-und-kurator-kontrolle` + `Objekt-Ebenen-Modell`.
Eiserne Regel: jede neue KI-Freiheit ↔ deterministische Kontrolle.
- **Schemas (additiv):** `katalog-item` bekommt `objektEbene` (haupt|ergaenzung),
`ankerTyp`, `maxAnzahl` (1..6, Default 1). `kurator-vertrag` v0.6: Response
`hauptObjekte` + `ergaenzungen[{itemId,anzahl}]` (beide optional); `auswahl`
bleibt Pflicht + expandiert (jede itemId genau einmal). Codegen TS+Python neu.
- **Daten:** alle 127 Katalog-Items getaggt (bad 11 haupt/29 erg · wohnen 12/28
· kueche 38/9). **2 neue Esszimmerstuhl-Items** (wohnen, P2, ergaenzung,
ankerTyp esstisch, maxAnzahl 6, `near:esstisch:1.3`) – das 3D-Kit `stuhl` +
2D-Symbol existierten bereits (wiederverwendet, kein Duplikat). Neu
`data/kurator/anzahl-leitplanken.json` (Instanz-Korridor je Raumtyp/Fläche).
- **Kurator:** Call A zweistufig (`hauptObjekte` zuerst, dann `ergaenzungen` mit
Anker+maxAnzahl je Slot; Anzahl-Leitplanke aus Daten gerendert). Harte
Kontrollen `_validiere_ebenen`: Auswahl⊆Kandidaten · P1-Pflicht in haupt ·
anzahl 1..maxAnzahl · Anker-Haupt-Pflicht · Platz-Budget über alle Instanzen.
WEICH: Korridor (1 Repair, dann Marker `CURATOR_ANZAHL_AUSSERHALB`). Baseline
zweiphasig (haupt → verankerte Ergänzungen, Stühle det. bis 4, Korridor-Cap).
Thinking: `FP_KURATOR_REASONING` (→ reasoning_effort + reasoning_format hidden,
nur bei Env) + `FP_KURATOR_TEMP`. Alte Kataloge ohne `objektEbene` brechen nicht.
- **Solver/API:** `solve()`/`loese_mit_varianten()` neuer `mengen`-Param
(itemId→anzahl → n Placements, eindeutige IDs, `near:esstisch` je Instanz,
Determinismus unberührt). `/solve` optionales `mengen`; Baseline-Pfad leitet
es aus `ergaenzungen` ab (`mengen_aus_antwort`, kein Vertrags-Extra-Feld).
Client: `mengenAusKurator` → /solve. K-Varianten reichen mengen durch.
- **Eval:** Überlebensrate zählt jetzt Instanzen; neue Spalte «Anker-Erfüllung».
Baseline-Report neu (0.84 Überleben / Anker 100 % / 0 ❌ in 45).
- **Bewusste Abweichungen / offen:** (1) Korridore ggü. ADR-Beispiel etwas höher
gewählt, weil Stühle jetzt als Instanzen zählen. (2) Stuhl-Distanz 1.3 m (0.8 m
aus der ADR-Skizze kollidiert mit dem 1.4×1.2-Tisch). (3) **Doppelzählung
Esstisch↔Stühle:** die Esstisch-Items heissen «(inkl. 4 Stühlen)» und Preis/2D
deuten Stühle an – jetzt kommen zusätzlich echte Stuhl-Instanzen. Bewusst NICHT
angefasst (Naming/Preis/Rendering = grösserer Eingriff) → Bryan-Entscheid, ob
Esstisch entkoppeln (umbenennen/entpreisen) oder Stühle nur als eigene Instanzen.
- Gates grün (lint/typecheck/test/schema-check); NICHT committet (Orchestrator).
### Kurator-Pipeline v3 – 5 Wellen (2026-07-13, ADR-0013)
Vorgabe: Brain `Kurator-Pipeline-v3-Konzept` + `ADR-0013-kurator-pipeline-v3`.
Eiserne Regel: jede neue KI-Freiheit ↔ neue deterministische Kontrolle.
- **W1 Kohärenz/Norm:** Call A liefert zuerst `konzept` (Vertrag v0.4), Konzept
→ B+C; Stilprofil neu in B, Auswahl neu in C; **Platz-Budget hart** in
`_validiere` (Daumenregel wie Baseline, Unsatisfiability-Guard);
**Norm-Kontext aus `data/rules/flaechen.json` pro Raum gerendert**
(`norm_kontext_flaechen`: konkrete Nasswand-Indizes; Norm-Prosa aus der
Prompt-.md entfernt – eine Quelle); neu `kueche-wand-spritzzone` (1.2 m,
zu-verifizieren); Relations-Ziel-Validierung; Few-Shots; temp 0.3 + seed.
- **W2 Daten:** alle 125 Katalog-Items volle 8 Achsen + `farbVarianten`
(16 farbSlug-Enum im Katalog-Schema); 3 falsch getaggte Wohnen-Bilder +
Eckschrank-Copy-Paste-Tags korrigiert.
- **W3 Farben (KI+UI):** Vertrag v0.5 `farben` (itemId→farbSlug, Cross-File-
`$ref` aufs Katalog-Enum – Codegen löst auf, kein Duplikat; 2 Testdateien
auf Registry-Validierung umgestellt); `placement.farbe` (Plan additiv);
eigener Validierungsschritt + Repair + Bereinigung (CURATOR_FARBEN_BEREINIGT);
Client `farben.ts` (MANUELL>KI>Default, Default = erste farbVariante);
Farb-Picker am selektierten Objekt + Flächen-Material-Picker; manuelle
Flächen laufen durch `POST /flaechen/pruefen` (Python bleibt Norm-Quelle).
Bewusst: Wand-Material gilt für alle Wände (keine Einzelwand-Selektion).
- **W4 Eval:** `kurator_eval.py` = 3 Räume × 3 Profile × 5 Seeds; Metriken
Überlebensrate/Auslastung/Palette-Treffer/Norm-Korrektur-Rate/Invariante;
deterministischer Report `services/engines/reports/kurator_eval.json`.
**Baseline-Messlatte: 0.867 / 0.886 / 0.855, 0 ❌ in 45 Läufen.** LLM-Lauf
mit FP_KURATOR_URL wiederholen → muss Baseline schlagen (Gate).
- **W5 K-Varianten:** `varianten.py` (K=3 Sub-Seeds deterministisch, bestes
lexikografisch: platziert → −knapp → Relations-/Stil-Score → Index);
`/solve` Flag `varianten` (Default false), App nutzt true (Bad/Wohnen;
Küche ausgenommen). **Fund:** `constraintReport.softScore` ist bei
bad/wohnen immer 0.0 (nur Küche patcht ergonomie) → knapp-Zähler ist das
echte Soft-Signal. Offen: LLM-Eval-Lauf; «3 Vorschläge zeigen» (UI-Bonus);
Einzelwand-Material-Selektion.
### CI-Fix + 2D-Wand-Semantik + 3D-Polish + Ladebildschirme (2026-07-13)
- **CI grün:** Prettier-Format der Codegen-Ausgabe gefixt; Merke: Gates immer
mit den ROOT-Kommandos fahren (`pnpm lint/typecheck/test` deckt alle
Workspaces — apps/web allein reicht nicht).
- **2D-Wand-Semantik korrigiert:** `floor.polygon` = INNENKANTE (nutzbare
Raumfläche, Solver-Realität) → Wand wächst jetzt nach AUSSEN
(`aussenPolygon`, Gehrung aussen); Boden = volles Polygon.
- **3D-Rendering-Polish:** prozedurale Environment (Lightformer-Softbox,
offline/CSP-fest), ACES-Tonemapping, ContactShadows; Chrom echt spiegelnd,
Glas reflektierend. Kein Post-Processing (bewusst schlank).
- **Ladebildschirme:** FP-Logo + Puls + Fortschrittsbalken an allen 4
Wartepunkten (v.a. Vorschlag/Kurator mit 3 KI-Calls); Piktogramme +20–30%.
### Kurator-Pipeline v2 + Licht + individuelle Flächen (2026-07-12)
- **Kurator v2 – 3 KI-Calls** (`kurator.py`, Vertrag v0.2 additiv): A Auswahl ·
B **Anordnung** (je Item wandIndex/relationen/prioritaet; Solver setzt sie
WEICH um – Ranking, nie NoFeasiblePlacement wegen Wandwunsch) · C **Flächen**
(materialSlug-Enum 10 Slugs als einzige Quelle, bereich voll/halbhoch/sockel,
hoeheM, akzent). Jeder Call validiert + 1 Repair + **Teil-Fallback**
(B→relationalRules, C→None). Prompts: `data/prompts/kurator-{rolle,anordnung,
flaechen}.md`. Norm-Filter/Parität unangetastet → 0 ❌ bleibt beweisbar.
- **Licht & Lampen** (Dressing): Platzierungsart `an_decke` (Schema-Enum
additiv); Pendelleuchte (wohnen+kueche, über Esstisch/Arbeitsfläche),
Tischleuchte (wohnen) – leuchten echt (Emissive + pointLight). Offener Punkt:
glTF-Upgrade einer Leuchte braucht das Punktlicht noch (additiv nachrüstbar).
- **3D-Flächen-Rendering**: Kurator-`flaechen` sichtbar – je Wand eigenes
Material (raumhoch/halbhoch/Sockel, Akzentwand), Boden aus Slug; ohne
`flaechen` exakt bisheriges stilabgeleitetes Verhalten. Offen: Zusammenspiel
Kurator-Flächen vs. manuelle Oberflächen-Variante definieren; 2D zeigt die
Flächen (bewusst) noch nicht.
- HF: /plan macht jetzt bis zu 3 Groq-Calls → wenige Sekunden länger; Fallback-
Marker je Call in der Begründung (`CURATOR_ANORDNUNG_FALLBACK` etc.).
- **Flächen-Normkontrolle (Nachtrag):** `data/rules/flaechen.json` (deklarativ,
getrennt vom Paritäts-Set) prüft jeden KI-Flächen-Output hart: Bad-Boden
wasserfest, Nasswände ≥2.0 m wasserfest (Heuristik: Fixpunkte ≤0.5 m),
Küchenboden abwaschbar. Ablauf: Regeln VORAB im Prompt → pruefe_flaechen →
1 Norm-Repair (`…NORMREPAIR`) → deterministische Korrektur
(`…NORMKORREKTUR`). Vokabular +tapete-hell/+taefer-holz (Wohnräume: Putz/
Tapete/Täfer statt Fliesen). Richtwerte «zu-verifizieren» (SIA 271/DIN 18534).
### Katalog-Ausbau: 13 neue Möbeltypen (2026-07-12)
- **26 neue Katalog-Items** (je 2 Stil-Varianten, alle P3): Bad waeschekorb/
badhocker/abfalleimer/midischrank · Wohnen pouf/vitrine/konsolentisch/
barwagen/recamiere · Küche barhocker/servierwagen/vorratsschrank/
weinkuehlschrank (elektro). relationalRules nutzen die neue Grammatik
(group:sitzgruppe, against-wall, near, corner).
- **Voll dargestellt:** 13 volumetrische 3D-Kits (`moebel3d.tsx`) + 13
2D-Plan-Symbole (`symbole2d.ts`) + MATERIAL_FARBE.
- **Baseline-Fix** (`kurator.py`): Rest-Slots nach priorityClass P1→P2→P3 statt
alphabetisch (latenter Bug: P3-Deko konnte Kernmöbeln die Flächen-Daumenregel
aufbrauchen). Solvability-Regression: alle 10 Raum-Fixtures × Seeds 0–5 →
0 ❌, kein NoFeasiblePlacement (kleine Räume lassen P3 erwartungsgemäss weg).
- **Offen:** weitere Wunschlisten-Typen (z.B. Dusch-WC, Raumteiler, Kücheninsel
als eigenes Möbel) bei Bedarf nach demselben Muster.
### 3D-Volumetrie + Scene-Dressing für ALLE Raumtypen komplett (2026-07-09)
- **Modelle** in höherer Volumetrie (rundbox/lathe/torus + Glas/Chrom) für
**Bad, Wohnen UND Küche** – alle `moebel3d`-Bauer klar erkennbar, nicht mehr
schematisch; Ampel-/Solver-Logik unberührt, bbox-Invariante bleibt.
- **Scene-Dresser** für alle drei Raumtypen: `data/dressing/{bad,wohnen,kueche}.json`
+ Kits in `dressing3d-kits.ts`, generischer Loader/Engine, „Deko"-Toggle.
Sparsam, „frisch gebaut". Verändert Plan/`constraintReport`/LV NICHT (0 ❌).
- **Offen/Backlog:** komplett neue Möbeltypen aus Bryans Wunschliste (Recamiere,
Weinkühler, Doppelwaschtisch, Barwagen …) = eigener Schritt (Katalog+Regeln+
Solver). Deko-Sonderfall „an Gerätegriff hängen" bräuchte neue Platzierungsart.
Echtes Spiegel-Chrom bräuchte Environment-Map. Bryans echte `.glb` ersetzen
Platzhalter über `apps/web/public/assets/dressing/`.
### 3D-Modelle Bad – höhere Volumetrie (2026-07-09)
- **Primitiv-System erweitert** (`apps/web/src/moebel3d.tsx`): neben box/zyl/kugel
neu **`rundbox`** (RoundedBox), **`lathe`** (Rotationskörper), **`torus`** –
plus Material-Rollen **`glas`** (echt transparent) und **`chrom`** (metallisch).
Ampel-Farblogik unangetastet (Rolle steuert nur Oberflächen-Physik).
- **Alle Bad-Objekte + 5 Deko-Kits** auf klar erkennbare, plastische Volumetrie
gehoben (WC/Lavabo/Dusche als Flagship, dann Fan-out: Badewanne, Spiegel,
Badmöbel, Hochschrank, Regal, Handtuchstange/-heizung, Urinal, Bidet,
Badteppich, Wandleuchte, Pflanze; Deko: Seifenspender, Zahnputzbecher,
gefaltete Handtücher, Tray, Pflanze). `dressing3d.tsx`-Renderer auf die neuen
Primitive erweitert. bbox-Invariante (`passtInBbox`/`clampTeil`) bleibt.
- **Offen:** Fan-out Wohnen/Küche-Modelle im gleichen Detailgrad; echtes
Spiegel-Chrom bräuchte eine Environment-Map (aktuell mattes Metall).
### Scene Dressing – Bad-Durchstich (2026-07-09)
- **Neue visuelle Deko-Ebene**, client-seitig & getrennt vom Plan (Konzept:
Brain `vault/50_Umsetzung/Scene-Dressing-Konzept.md`). `sceneDressing()` in
`apps/web/src/dressing.ts` (Seed/Anker/Stil/Klasse-B-Kollision), gerendert als
`DressingLayer3D` mit **„Deko"-Toggle** im 3D-Viewer. Verändert
`placements`/`constraintReport` NICHT → 0 ❌ unberührt (Regressions-Test).
- **Ästhetik „frisch gebaut, nicht bewohnt":** 5 sparsame Bad-Objekte
(Seifenspender, Zahnputzbecher, Tray, gefaltete Handtücher, Pflanze).
- **Asset-Drop-in:** `apps/web/public/assets/dressing/<gltfRef>.glb` (README dort);
`assetStatus:"modeled"`+`gltfRef` → ersetzt Platzhalter automatisch. Bryan
modelliert + lädt hoch, Rest macht die App.
- Bugfix: Ecken-Footprint (rotiert) stach durch die Wand → inkrementell nach
innen rücken. Details: `vault/10_Learnings/Learning-Scene-Dressing-Bad-Durchstich.md`.
- **Offen:** Fan-out Wohnen/Küche; Klasse C (`an_wand`) in Engine da, ungenutzt.
### Zuletzt (2026-07-08, diese Session)
- **UI-Feinschliff:** Schein nach innen stärker & überall, mehr CI-Grün,
Piktogramme überall grösser (`fp.css`/`theme.ts`/`Piktogramm.tsx` + Ansichten).
- **Solver/Kurator «Stufe 1»:** reiche Anordnungs-Grammatik (`near`,
`against-wall`, `corner`, `facing:<typ>`, `opposite:<typ>`, `group:<id>`,
`pair-with:<itemId>`) als weiche Constraints in P2/P3 + **stil-abhängige
Platzierung** (v0: Achse `raumgefuehl` → Wand-Nähe, kleines Gewicht). Harter
Norm-Filter/Interpreter/Parität unangetastet → 0 ❌ bleibt beweisbar. Neu:
`services/engines/src/fp_engines/relationen.py`; `solve()` um `style_profile`
additiv erweitert; `kurator-rolle.md` lehrt die Grammatik; `wohnen.json` demo.
**Offen (Kalibrierung):** welche Stilachsen welchen Platzierungs-Bias steuern →
Brain-Learning `vault/10_Learnings/Learning-Solver-Stufe1-Anordnung-Stil.md`.
- **2D-Grundriss CAD-Look:** Wand-Aussenkante liegt auf der Raumlinie, Stärke
wächst nach innen, Gehrungs-Ecken (`innenPolygon`/`wandBaender` in `plan2d.ts`);
Öffnungen sitzen im Band.
- **Objekt-Panel erweitert:** `ObjektInfoPanel.tsx` – Kosten + Wechsel-Alternativen
(Thumbnail = 2D-Symbol, Preis, Masse, Stil-Match). Produktfotos je Item fehlen
noch (Asset-Pipeline-Thema) → Thumbnail als Austauschpunkt vorbereitet.
- **(läuft)** mehr Beispielräume (`packages/shared/fixtures/artefakte/raummodell.*`).
## Meilensteine
| M | Inhalt | Status |
|---|---|---|
| **M0** Repo-Setup | Monorepo-Gerüst, Pins, Setup-Scripts, CI, CLAUDE.md/README | 🟢 fertig |
| **M1** Verträge & Regel-Kern | JSON-Schemas (7 Verträge) + Codegen + Regel-Interpreter TS & Python + Paritätstest | 🟢 fertig |
| **M2** Scan-Spike | Eval-Notebook, Messung R1–R3 (parallel, blockiert nichts) | 🟡 gestartet |
| **M3** Durchstich BAD ⭐ | Sample-Bad → Baseline → Solver P1–P3 → Viewer → Report → Mengen/KV-PDF | 🟢 DoD erfüllt |
| **M4** Auswertung voll + Kurator | LV, Bauzeitenplan, Offert-Paket, DXF · Kurator + Mini-Eval · Stil-UI | 🟢 DoD erfüllt |
| **M5** Durchstich WOHNEN | Sample-Wohnzimmer → Regelsatz/Katalog/Bilder wohnen → Solver mit freier Boden-Platzierung → LV/Bauzeit/Dokumente | 🟢 fertig |
| **M6** Durchstich KÜCHE | Formwahl + lineare Baugruppe + API + Frontend; Grossraum über Zone geplant | 🟢 DoD erfüllt |
| **M7** Scan-Integration (+AR) | Adapter · Space-Deploy · scan-worker · Upload/`/scan` · Korrektur-Modus fertig; offen: R1-Gate auf Colab (Schritt 5) | 🟡 gestartet |
## Was konkret existiert
- **M0:** pnpm-Workspace (Node 22 LTS, pnpm gepinnt) · `apps/web`
(React+Vite+TS, r3f-Smoke-Szene) · `services/engines` (uv, Python 3.12,
FastAPI-Skeleton mit `/health`) · `packages/shared` (Schemas/Fixtures/
TS-Quellcode) · `data/`-Ordnerstruktur mit READMEs · Setup-Scripts
(`scripts/setup.ps1` für Windows = Bryans Dev-System, `setup.sh` für CI) ·
GitHub-Actions-CI (Lint → Typecheck → Tests → Schema-Check) · `LICENSES.md`.
- **M1:** 9 JSON-Schemas in `packages/shared/schemas/` (7 Verträge laut
Schema-Spezifikation + Projekt-Hülle + Taxonomie) · Codegen beidseitig
(`pnpm codegen` → TS-Typen + pydantic-v2-Modelle, generiert & eingecheckt) ·
**Regel-Interpreter doppelt** (`packages/shared/src/rules/` ↔
`services/engines/src/fp_engines/rules/`, Semantik-Doku im rules-README) ·
Stammdaten `data/rules/basis.json` + `bad.json` (Norm-Regelsatz-v0,
Richtwerte «zu-verifizieren») + `data/taxonomy/stilachsen.json` (8 Achsen,
ADR-0006) · goldene Fixtures (Sample-Bad 3.0×2.4 m, Plan ok/verletzt) +
**Paritätstest grün** in vitest UND pytest · `POST /validate` liefert den
`constraintReport` über die API (inkl. Fehler-Envelope).
- **M2 (gestartet 2026-06-11):** `notebooks/scan-spike/` mit Colab-Notebook
`spike_eval.ipynb` (P0–P5-Skelett: Frames+Gate, Tiefe DA-V2-Small,
Grounding DINO; P1 Layout / P4 Spiegel als TODO bis zum ersten GPU-Lauf) ·
`capture_check.py` (lokaler Vorabcheck) · **R1-Testvideos von Bryan**
(Bad, normal + weitwinkel) in `testdata/r1/` · Vorabcheck-Report mit
**Aufnahme-Guideline v1** in `reports/r1-vorabcheck.md` (Befund: Material
zu unscharf/texturarm für die Kette → R1 nach Guideline wiederholen;
Weitwinkel ~3× besser) · **Ground Truth R1 erfasst** (Bryan 2026-06-11):
L-förmig, 1.8×1.0 m mit Versatz 0.3 m (Nische 0.7×0.8 m), 1.56 m² →
`ground-truth/r1.json` + reales Fixture
`packages/shared/fixtures/artefakte/raummodell.r1-wc.json` (6 Wände,
erster nicht-rechteckiger Grundriss). Offen: Raumhöhe, Türbreite,
Objektmasse, Neuaufnahme nach Guideline.
- **M2 (2026-06-14, getesteter Metrik-Kern):** `notebooks/scan-spike/`
`eval_metrics.py` – **GPU-freier, abhängigkeitsfreier** Mess-Kern
(Wandlängen-Fehler Median/Max, Fläche %, Rechtwinkligkeit, Objekt-Recall) +
`gate()` (Go/Anpassen/Pivot aus den Spike-Schwellen). **`test_eval_metrics.py`
grün** (5 Tests gegen R1-Ground-Truth: perfekte Schätzung = 0 Fehler, 1.56 m²/
5.6 m reproduziert, 5-cm-Störung erkannt, Eckenzahl-Mismatch gemeldet, Recall).
Notebook nutzt jetzt diesen Kern und kennt die **Ecken-Antippen-Variante**
(`corners/<raum>.json`, Wandgenauigkeit GPU-frei). Die schweren ML-Schritte
(Depth Anything V2, Grounding DINO, SAM2) brauchen GPU+Modell-Download →
laufen auf **Colab (T4)**, nicht in dieser Umgebung (kein GPU, HF 403).
- **M3 (2026-06-11):** kompletter End-to-End-Klickpfad: Raum wählen
(Sample-Bad ODER echtes R1-WC) → «Plan vorschlagen» (Baseline-Kurator +
Feasibility-first-Solver P1–P3, `data/catalog/bad.json`) → 3D-Viewer
(r3f: Hülle + Box-Platzhalter, Auswahl, Pfeiltasten/«r» verschieben/
rotieren, sperren) mit **Live-Norm-Ampel** (TS-Interpreter, dieselben
Regel-JSONs) → «Variante würfeln» (Seed) → Auswertung (Mengen + KV mit
Bandbreite + Next-Steps) → **KV-PDF** (ReportLab, CI-Look).
**Solver-Invariante ⭐ als Property-Test grün** (12 Seeds × 2 Räume, 0 ❌;
Plan validiert gegen Plan-Schema; Determinismus + Varianten getestet).
API: /samples/rooms · /catalog/{rt} · /rules/{rt} · /solve · /validate ·
/evaluate · /export/kv-pdf (Fehler-Envelope, 422 NO_FEASIBLE_PLACEMENT).
- **M4 (2026-06-11, DoD erfüllt):**
- **Dokumente:** LV (deklarativer Positionskatalog `data/positions/bad.json`,
Trigger→Template, rückverfolgbar via herkunft) · Bauzeitenplan
(Sequenz-DAG `data/sequence/bad.json` mit Trocknungs-Wartekanten) ·
Offertanfrage-Paket je Gewerk (ohne Preise + Rückgabeblatt) · DXF-
Grundriss (ezdxf) · alles als API-Exporte (+PDF) und getestet.
- **Kurator-Port** (`fp_engines/kurator.py`): Vorfilter → Prompt
(`data/prompts/kurator-rolle.md`) → Validierung → Repair → Fallback;
Ports baseline | llm-api/llm-local (FP_KURATOR_URL). **Mini-Eval**
`scripts/kurator_eval.py`; **Gate-Stand: Baseline aktiv** (kein LLM
konfiguriert – mit FP_KURATOR_URL erneut messen).
- **Stil-UI:** 8 Sample-Bilder (SVG-Platzhalter) + Bild-Katalog
`data/images/bad.json` · Swipe-Overlay + Preset-Klick · Smart Spider
(SVG-Radar, Achsen aus /taxonomy) · POST /style/profile · Klickpfad
Stil → /curate → /solve im Viewer verdrahtet (Begründung wird gezeigt).
- **Alle MVP-Dokumente generierbar (DoD):** KV · Mengen · LV ·
Bauzeitenplan · Offertanfrage-Paket · Gewerke-Übersicht · Einkaufsliste ·
2D-Plan (PDF **und** DXF) · 3D-Export (glTF 2.0, handgebauter Writer,
deckungsgleich mit dem Box-Viewer) · Next-Steps (im KV). Im Viewer als
«📄 Dokumente…»-Menü. **Eval-Gate entschieden: Baseline aktiv** (LLM darf
sie per FP_KURATOR_URL herausfordern).
- **Nicht code-seitig (bei Bryan):** Mini-Eval mit echtem LLM messen +
Mensch-Rating; echte Bad-Fotos taggen (ersetzt SVG-Platzhalter).
- **M5 (2026-06-12, Durchstich WOHNEN):**
- **Sample-Raum:** `raummodell.wohnen-sample` (Wohnzimmer 4.5×3.6 m, 16.2 m²,
4 massive Wände, Tür 0.9 m an kurzer Wand, Fenster als Öffnung, KEINE
Fixpunkte = Trockenraum). In `/samples/rooms` automatisch eingehängt.
- **Stammdaten wohnen:** `data/rules/wohnen.json` (host-binding Regal/
Wandbild, soft object-distance Sofa↔Couchtisch, hard clearance Esstisch
≥1.0 m + Sideboard ≥0.6 m; circulation bleibt Basis-Stub) ·
`data/catalog/wohnen.json` (28 Items: sofa/esstisch/tvmoebel P1,
couchtisch/regal/sideboard/stehleuchte P2, teppich/pflanze/wandbild/
beistelltisch P3 – je 2–3 Stil-Varianten, Präfix `bbbbbbbb-…`) ·
`data/images/wohnen.json` + 8 SVG-Platzhalter (2 Presets) ·
`data/positions/wohnen.json` (Trockenraum-Gewerke: Maler Wände+Decke,
Bodenleger, Elektro Leuchten-Anschluss, Möbel-/Wandmontage) ·
`data/sequence/wohnen.json` (Ausräumen → Maler[Trocknung] → Boden →
Elektro → Montage → Endreinigung).
- **Solver – freie Boden-Platzierung:** neu `_floor_candidates` (0.25-m-
Raster über der Bodenpolygon-BBox, Yaw 0/90/180/270, nur mount=boden) +
`_candidates` (Wand- ⊕ Boden-Kandidaten). Vorfilter/Caps unverändert
(P2 300 / P3 200), neu Cap P1-Backtracking 400. Determinismus gewahrt
(Kandidatenreihenfolge deterministisch vor dem seed-shuffle). Solve
wohnen ~0.05–0.11 s, Testsuite 109 grün in ~12 s.
- **Beweis freie Platzierung:** Property-Test + dedizierter Test (Couchtisch
steht frei vor dem Sofa, Wandabstand >0.3 m). Alle Dokument-Exporte
(LV/Bauzeit/KV/DXF/glTF/Plan-PDF) laufen für wohnen E2E.
- **M6 Phase A (2026-06-12, Durchstich KÜCHE – Stammdaten/Zonen):**
- **Sample-Räume:** `raummodell.kueche-sample` (geschlossene Küche 3.2×2.6 m,
4 massive Wände, Tür 0.9 m, Fenster, Fixpunkte wasser/abwasser/starkstrom/
elektro/lueftung an der Anschluss-Längswand) · `raummodell.grossraum-sample`
(eine Hülle 7.0×4.5 m, `zones[]` = Küche 2.8×4.5 + Wohnen; Zonengrenze als
`wall.kind:"offen"` mitten im Raum; Fixpunkte mit `zone`-Referenz an der
massiven Stirnwand der Küchenzone). Beide automatisch in `/samples/rooms`.
- **Stammdaten küche:** `data/rules/kueche.json` (connection spüle→wasser/
abwasser, kochfeld→starkstrom, GS→wasser/abwasser, kühlschrank→elektro je
hard, dunstabzug→lueftung soft · object-distance kochfeld↔spüle 0.3 hard ·
clearance ≥1.0 m je Haupttyp · host-binding Hängeschrank/Dunstabzug an Wand)
· `data/catalog/kueche.json` (37 Items, `normProfileVariante` ch55/eu60 für
Korpusse+Geräte, Präfix `cccccccc-…`) · `data/images/kueche.json` + 8 SVG
(2 Presets) · `data/positions/kueche.json` (Gewerke Küchenbauer/Sanitär/
Elektro/Lüftung/Maler/Bodenleger) · `data/sequence/kueche.json` (Demontage →
Roh-Installation → Maler[Trocknung] → Boden → Küchenmontage →
Fein-Installation → Endreinigung).
- **Zonen-Ableitung:** `services/engines/src/fp_engines/zonen.py` –
`zone_room(room, zone_id)` projiziert eine Zone auf ein eigenständiges,
schema-valides Teilraum-Raummodell (floor=Zonen-Polygon via Shoelace, Wände
auf die Zonenkanten geclippt: deckende Hüllenwand erbt kind massiv/offen,
sonst synthetisch `virtuell`; Öffnungen + Offset umgehängt; nur Fixpunkte
der Zone). Solver/Interpreter laufen unverändert darauf (nutzen nur
`kind=="massiv"`). v0-Annahme: achsparallele Rechteck-Zonen.
- **Tests:** `test_zonen.py` (Teilraum schema-valide, floor=Zonen-Polygon,
nur Küchen-Fixpunkte, virtuelle Kante bei fehlender Hüllenwand, offene
Zonengrenze nicht montierbar, massive Stirnwand erhalten, reine Funktion) ·
Schema-Check + beide Schema-Tests (TS+Py) um die 3 neuen Raum-Fixtures und
die Regelsätze wohnen/kueche erweitert. Alles grün (Python 122, vitest 19).
- **M6 Phase B (2026-06-12, Durchstich KÜCHE – Formwahl + Baugruppe):**
- **Küchen-Solver** `services/engines/src/fp_engines/kueche.py`:
- `formwahl(room, style_profile, norm_profile)` – Wandzüge (massiv,
achsparallel) + Anschlusswand (Wasser/Abwasser-Fixpunkte am nächsten);
Kandidaten-Formen nach Tabelle 1a hart gefiltert (I ≥2.4 m + Querbreite
≥1.6; Galley 2 parallel + Breite ≥2.4; L ≥1.8+≥1.2; U 3 Züge + Innenbreite
≥1.2; Insel Breite ≥3.4 UND Boden-Fixpunkt); Soft-Score
0.35·stil+0.30·ergo+0.20·arbeitsplatte+0.15·stauraum mit Stil→Form-
Heuristik (`_FORM_STILZIEL` auf den Stilachsen). Rückgabe Top-3 DISTINKTE
Formen (Begründung, Score, anchorWallIds, Nutzlänge).
- `solve_kueche(...)` – lineare Baugruppe: Raster ch55/eu60, Slots ab
Wandzug-Anfang (türfreies Teilstück), AMK-Füllung **P1** Spüle@Wasser →
GS direkt daneben → Kochfeld ≥1 Slot Abstand + nahe Starkstrom → Kühl an
Zeilenende → Dunstabzug als Wand-Placement über dem Kochfeld; **P2**
Hochschrank an Enden, Unterschränke in Restslots, Hängeschrank-Ebene (nicht
über Kochfeld, nicht vor Fenster), Füllstücke (0.05/0.15) ans Zeilenende;
**P3** 1–2 Deko über `_floor_candidates`. Jede Platzierung gegen
`_zulaessig` (evaluate_rules) – Solver-Invariante **verletzt==0** gewahrt,
sonst `NoFeasiblePlacement`. `plan.assemblies` = eine kuechenzeile-UUID je
Zeile, `meta.normProfile`. Stil-Auswahl reuse über `kurator._cos`.
Regel-Interpreter **nicht angefasst** (Paritäts-Gesetz). Solve-Zeiten:
geschlossene Küche ~0.09–0.12 s, Grossraum-Zone ~0.13 s (< 2 s).
- **API:** `POST /kueche/formen` (Top-3) · `/solve` erweitert um
`normProfile`/`form`/`zoneId`, Routing über `_effektiver_raum` (zoneId ODER
Auto-Küchenzone im Grossraum → `zone_room`), Küchen-Pfad → `solve_kueche`;
Response liefert zusätzlich `room` (effektiv geplanter Teilraum) für Viewer +
Live-Ampel. Alle `/export/*` laufen für Küchen-Pläne (placements-basiert).
- **Frontend:** Küchen-Erkennung (roomType kueche ODER Küchen-Zone);
Normprofil-Toggle CH/EU, Formwahl-Karten (Name, Begründung, Score-Balken),
Grossraum-Hinweis «geplant wird die Zone Küche»; Viewer + Ampel nutzen den
von `/solve` zurückgegebenen effektiven Raum; «Variante würfeln» behält
Form + Normprofil. Bad/Wohnen-Flows unverändert.
- **Tests:** `test_kueche.py` (Formwahl: Insel ausgeschlossen, Anschlusswand
in jeder Top-Form, Stil verschiebt Ranking · Baugruppe Property-Test
{kueche, grossraum+zone}×{ch,eu}×6 Seeds = 0 ❌ + schema-valide +
deterministisch · GS neben Spüle · Kochfeld↔Spüle ≥0.6 m · eine Assembly-ID
je Zeile · Rasterpositionen · ch55/eu60-Varianten · Grossraum-Placements in
Zone · Seed-Variation) + Endpoint-Tests (/kueche/formen, /solve kueche,
/solve Grossraum-Zone, 1 Export-Smoke). **Gesamt: Python 163 grün (~9 s),
vitest 19 grün, mypy/ruff sauber, Schema-Check 11 Files.**
- **M3-Politur (2026-06-13, Verkehrsweg-Freiraumanalyse `circulation`):**
Der seit M3 offene `circulation`-Stub ist jetzt ein **echter Evaluator** in
BEIDEN Interpretern (1:1, ganzzahlige Raster/Erosion-Analyse: Bodenraster →
freie Zellen → Manhattan-Distanztransform → Bottleneck via Union-Find über
fallende Clearance-Schwellen; Anker = Türmünder, bei 1 Tür zusätzlich der
offenste Punkt). **Marge = 2·Bottleneck − minWidth** (0.05-m-Raster). Die
Live-Ampel und alle Reports zeigen jetzt ein Verkehrsweg-Urteil.
- **Bewusst SOFT in v0** (Norm-Regelsatz lässt hard/soft offen): informiert,
ohne die Solver-Invariante zu berühren. Der Solver ist noch nicht
verkehrsweg-optimierend → seine Pläne zeigen oft eine **soft**
circulation-Verletzung (bad knapp, wohnen/kueche verletzt) – ehrlich.
- **Performance:** circulation läuft NICHT im Solver-Hot-Path – `_zulaessig`
wertet dort per `nur_hart=True` nur harte Regeln (verhaltensneutral, da
soft nie in `hard.summary` zählt); voller Report inkl. circulation nur 1×.
- **Parität:** neuer goldener Fall `flur-circulation-verletzt` (Trennwand
pincht Korridor) lockt den Verletzungs-Pfad TS↔Python; +4 Unit-Tests.
- **Gesamt grün:** Python 171 (~20 s, 0.05-Raster), vitest 23, Parität
beidseitig, mypy/ruff/Prettier/Schema sauber.
- **M3-Politur (2026-06-13, 2D-Grundriss-Ansicht):** Neuer **Aufriss von oben**
als SVG (`apps/web/src/Viewer2D.tsx`) – Header-Umschalter 3D↔2D, **2D ist
Default** (normgerecht beurteilbar). Wände (massiv/offen), Öffnungen mit
**Türschwenk**, Footprints **nach der Norm-Ampel eingefärbt** (verletzt/knapp/
ok/gesperrt), wandmontierte Objekte gestrichelt+transparent, klick-auswählbar,
Legende. Geometrie-Kern `plan2d.ts` nutzt **`footprint()` aus @fp/shared/rules**
→ exakt deckungsgleich mit Solver/Interpreter/3D-Viewer (kein zweites
Rotations-Mathe). **Test-Infra für `apps/web` neu aufgesetzt** (vitest 4.1.8):
`plan2d.test.ts` (Transform/Footprint/Normale) + `Viewer2D.test.tsx`
(Render-Smoke via renderToStaticMarkup). apps/web 8 Tests grün, Build ok.
- **Drag&Drop (2026-06-13):** Footprints im 2D-Grundriss per Maus/Touch
ziehen (unverriegelte Items), Live-Ampel rechnet sofort mit. Pointer→Welt
über die SVG-CTM + `toWorld` (Umkehrung von `toScreen`, Roundtrip-Test).
Pfeiltasten/«r»/sperren bleiben.
- **«austauschen» (2026-06-13):** Im Auswahl-Panel das Item gegen eine
Katalog-Alternative tauschen (gleicher Funktionstyp + gleiche
normProfileVariante → Küchenzeile bleibt konsistent); Pose bleibt, Ampel
rechnet die neuen Masse sofort durch.
- **Verkehrsweg-aware Solver, Schritt 1 (2026-06-14, Tür-Korridor-Freihaltung):**
Optionale (P2/P3) Objekte werden NICHT mehr in einen Tür-Zugangsstreifen
(Türbreite × 0.9 m ins Rauminnere) platziert – reiner Geometrie-Filter in
`solver.py` (`_tuer_korridore`/`_blockiert_korridor`), billig, paritätsneutral
(kein Interpreter/Goldens, kein Grid im Hot-Path), P1 unberührt. **Effekt: Bad
von knapp/verletzt auf circulation ok (+60 cm)** – genau «Korridor in P2/P3
freihalten». Test `test_tuer_korridor_frei_von_optionalen`. Suite 172 grün.
- **Verkehrsweg-aware Solver, Schritt 2 (2026-06-14, Metrik-Fix Tür-Anker):**
Die in Schritt 1 gefundene «Metrik-Fragilität» war der **Tür-Anker am Türmund**
(1.5 Zellen) – dadurch war faktisch die fixe **Türbreite** der Engpass statt
des durch Möbel beeinflussbaren Innenraum-Korridors. **Fix (1:1 TS & Python):
Anker ~minWidth/2 HINTER die Tür** (ins Korridor-Innere). Der grosse Refactor
(Chamfer/Wände-als-Hindernis) war NICHT nötig. **Effekt (echter Evaluator):
wohnen −70 → ok +10, kueche −70 → knapp 0, bad bleibt ok +60, flur (echte
Sperre) bleibt verletzt −30.** Goldens neu (Parität bit-identisch), 172 pytest
+ 23 vitest grün. Die Metrik urteilt jetzt sinnvoll & verteidigbar.
- **hard-Hochstufung jetzt plausibel, aber bewusst noch soft:** Solverpläne
sind jetzt circulation ok/knapp (kein verletzt in der Praxis). Hürde für hard
ist nicht mehr die Metrik, sondern die **Hot-Path-Performance** (circulation
ist teuer ~0.4 s/Auswertung; als hard liefe sie je Kandidat). Der billige
Tür-Korridor-Filter (Schritt 1) verhindert die häufigsten Verletzungen schon
ohne Grid – ein finaler Hard-Check + Keep-out könnte reichen. Mit Bryan
abstimmen, ob/wann hard. Details: Brain `Learning-Circulation-Metrik-Fragilitaet`.
- **Küchen-Politur (2026-06-14, Arbeitsdreieck als echter Score):** Der bisher
fix auf 0.0 gesetzte `softScore.ergonomie` wird für Küchenpläne mit dem
**gemessenen AMK-Arbeitsdreieck** (Spüle–Kochfeld–Kühlschrank) befüllt: drei
Seitenlängen + Summe → Trapez-Score [0,1] (Seite ideal 1.2–2.7 m, Summe
4–8 m = effizient). Berechnet **nach** der Platzierung (`arbeitsdreieck()` in
`kueche.py`) – **ohne Regel-Interpreter, Schema oder Goldens zu berühren**: das
Feld existierte bereits im Plan-Schema, der Solver post-prozessiert nur den
fertigen `constraintReport` (Paritäts-Gesetz unberührt, Invariante bleibt 0 ❌).
`/solve` liefert für Küchen zusätzlich ein `arbeitsdreieck`-Detail (Seiten/
Summe/Bewertung); der Viewer zeigt es als Panel (effizient/akzeptabel/beengt/
weitläufig). Ehrliche Werte (echter Evaluator): Sample-Küche 3.2×2.6 m =
**beengt** (0.26, Summe 2.2 m), Grossraum-Zone = **akzeptabel** (0.67, Summe
3.3 m) – differenziert nach Raumgrösse. `formwahl` (Pre-Placement-Ranking)
bleibt bewusst beim Proxy (das echte Dreieck ist vor der Platzierung unbekannt).
Suite: 187 pytest + 23 vitest + 8 apps/web-vitest grün, mypy/ruff/Prettier/
Schema sauber. Brain: Learning-Arbeitsdreieck-Ergonomie-Score.
- **M7 Schritt 1 (2026-07-05, Scan-Vertrags-Naht):** Neues Paket
`fp_engines/scan/` – **SpatialLM-Layout-Parser** (`spatiallm.py`, z-up-
Textformat Wall/Door/Window/Bbox) + **Adapter** (`adapter.py`: z-up→y-up
**ohne Spiegelung** (x,y,z)→(x,z,−y), Normierung auf min=0, mm-Rundung,
Wand-Ketten→Boden-Polygon mit 2-cm-Toleranz, Öffnungen mit hostWall/offset/
sill, Objekt-Boxen mit needsReview, **deterministische uuid5-IDs**, ehrlicher
`AdapterFehler` bei offener Hülle) + **Scan-Bundle-Posen v0** (`poses.py`:
kanonisches `poses.json` – Meter/Sekunden/Quaternion xyzw + gravity; App-
Exporte werden am Rand dorthin konvertiert). **Kernbeweis:** handgebautes
R1-WC-Layout (`fixtures/scan/r1-wc.layout.txt`) ergibt nach dem Adapter
**exakt** das Ground-Truth-Polygon von `raummodell.r1-wc.json` (1.56 m²,
6 Wände). 11 neue Tests (Schema+pydantic-valide, Determinismus, Segment-
Reihenfolge egal, Posen-Validierung). **Suite 198 grün**, mypy/ruff/Schema ok.
Kontext: Brain ADR-0012 (Scan-Kette fix: AR-App-Aufnahme → known-pose Fusion
→ SpatialLM), Code-Fahrplan in `M2-M7-Scan-Pipeline-Fahrplan`.
**Offen im Adapter-Umfeld:** Yaw-Vorzeichen + Winkel/Scale-Konvention gegen
echte SpatialLM-Ausgabe verifizieren (R1-Lauf, Schritt 5); Konverter
App-Export→poses.json, sobald Voxelio-/ARCore-Exportformat fixiert ist.
## Nächste Schritte (für die nächste Session)
1. **Scan-Code-Fahrplan (Brain: `M2-M7-Scan-Pipeline-Fahrplan`, von Bryan
freigegeben 2026-07):** ✅ Schritt 1 Adapter fertig (s.o.) · ✅ **Schritt 2
Space-Deploy-Gerüst (2026-07-05):** `fp_engines/space.py` (EIN Origin:
`/api` → Engines, `/` → gebautes Frontend – exakt die Dev-Proxy-Semantik,
kein CORS) + `Dockerfile` (multi-stage Node-Build → Python/uv, Port 7860,
User 1000) + `.dockerignore` + `deploy-space.yml` (pusht bei jedem
main-Push einen frischen Einzel-Commit OHNE notebooks/.git – die 50 MB
R1-Videos überschreiten HFs 10-MB-Grenze – mit HF-Frontmatter-README in
den Space `Bryan-HSLU/FP_POC`; Secret `HF_TOKEN` liegt in GitHub).
2 neue Tests (test_space.py), Suite 200 grün, Smoke lokal ok (API +
index.html). **Erster echter Space-Build läuft mit diesem Push** –
Ergebnis auf huggingface.co/spaces/Bryan-HSLU/FP_POC prüfen; Groq-Secrets
(`FP_KURATOR_URL/_MODEL/_API_KEY`) später in den Space-Settings setzen.
· ✅ **Schritt 3 scan-worker (2026-07-05):** neues Paket
`services/scan-worker` (eigenes uv-Projekt, Base-Dep nur numpy; gradio als
Extra `worker`; torch/spatiallm/open3d NIE als Deps – nur Colab, NC!).
**CPU-getesteter Geometrie-Kern:** `kamera.py` (OpenCV-Pinhole-unproject,
Quaternion→Pose) · `fusion.py` (Voxel-Zentroid-Downsampling, deterministisch)
· `ausrichtung.py` (z-up aus gravity via Rodrigues + `boden_ransac`-Fallback,
Boden→z=0) · `skalierung.py` (Wandhöhen-Fallback; Posen metrisch ⇒ Skala 1)
· `ply.py` (binary PLY XYZ+RGB = SpatialLM-Contract) · `pipeline.py`
(Keyframe-Sampling = Laufzeit-Hebel, TiefenProvider injizierbar) ·
`worker.py` (Gradio-App, GPU-Teile guarded) · `notebooks/colab_worker.ipynb`
(Starter: PAT-Clone, EINE Setup-Zelle mit Drive-Wheel-Cache-TODO, share-URL
→ manuell als `FP_SCAN_WORKER_URL` in den Space = Zeiger v0).
**Kamera-Konvention festgezurrt** (poses.py-Docstring: OpenCV-Pinhole,
T_wc; ARKit-Konverter = R·diag(1,−1,−1)). CI erweitert (lint/typecheck/
test + sync). **Suite: 200 engines + 22 scan-worker = 222 grün.**
· ✅ **Schritt 5 vorbereitet (2026-07-06):** `_lauf_spatiallm` **verdrahtet** –
ruft das offizielle SpatialLM-`inference.py` als Subprozess
(`-p scene.ply -o layout.txt -m manycore-research/SpatialLM1.1-Qwen-0.5B`),
dessen `to_language_string()`-Ausgabe = unser `layout.txt`-Vertrag (robust
gegen SpatialLM-API-Änderungen, kein Nachbau der Vorverarbeitung). Repo-Pfad
+ Modell über `FP_SPATIALLM_DIR`/`FP_SPATIALLM_MODELL`. Notebook-Setup-Zelle
klont SpatialLM, installiert den 1.1-Stack (Poetry + `poe install-sonata`)
mit **flash-attn-Wheel-Cache auf Drive**. **Korrektur:** SpatialLM 1.1 nutzt
**Sonata + flash-attn** als Backbone, **nicht** TorchSparse (das war nur 1.0)
→ Wheel-Cache zielt jetzt auf flash-attn.
**Offen (= der eigentliche R1-Gate, Schritt 5):** erster echter Colab-Lauf –
torch/CUDA-Pins an die Session anpassen (SpatialLM will 2.4.1/CUDA 12.4),
Genauigkeit gegen R1 messen, Intrinsics aus AR-Metadaten statt Schätzung.
Braucht Bryans AR-App-Aufnahmen (R1/R2/R3).
· ✅ **Schritt 4 Upload-UI + `/scan` (2026-07-05):** `POST /scan`
(multipart) nimmt ein **vorberechnetes Scan-Bundle** (`layout.txt` einzeln
ODER `.zip` mit `layout.txt`/`poses.json`) → `_bundle_dateien` entpackt →
`parse_layout` + `layout_to_raummodell` → schema-valides Raummodell +
`warnungen` (needsReview-Objekte, fehlende Anschlüsse). `poses.json` wird,
falls vorhanden, validiert (Warnung statt Abbruch). Fehler-Envelope:
SCAN_INVALID (roomType/Layout kaputt) · SCAN_NO_LAYOUT (nur Video → Live-Weg
folgt Schritt 5). Frontend: Header-Bedienung «📷 Scan laden» + Raumtyp-Select
→ `api.scan` (FormData) → Raum wird in die Liste gehängt und über
`raumWaehlen` in den bestehenden Klickpfad eingespeist. **v0 = Vorberechnen**
(Demo-Regel); Live-Weiterleitung an `FP_SCAN_WORKER_URL` bleibt Schritt-5-
TODO. 6 neue Endpoint-Tests + Live-Smoke (R1-Layout → 1.56 m², 6 Wände,
2 Öffnungen). Suite: **206 engines + 22 scan-worker grün**, Frontend
lint/typecheck/build sauber.
· ✅ **3D-Objekte statt Box-Platzhalter (2026-07-05, Bryan-Wunsch):**
`moebel3d.tsx` – alle 31 Katalog-funktionsTypen als Primitiv-Kompositionen
(2–7 Teile; WC/Lavabo/Dusche mit Glaswänden/Sofa/Kochfeld …), Fallback =
alte Box. **bbox-Treue als Test-Invariante** (`passtInBbox` über 5 Grössen):
kein Primitiv verlässt w×d×h, Gruppen-Transform + Klick 1:1 wie vorher →
Norm-Ampel-Farben/Logik unangetastet. Hinweis: glTF-Export bleibt bewusst
Boxen (Viewer detaillierter als Export – POC-ok).
· ✅ **Manueller Raum-Editor (2026-07-05, Bryan-Wunsch, Brain:
Raum-Editor-Manuell):** dritte Erstellungsvariante «✏️ Raum erstellen» –
`raumbau.ts` (pure, getestet: Rechteck/L-Form-Polygone, Shoelace, Öffnungs-
Validierung, R1-L-Form = 6 Wände/1.56 m²) + `RaumEditor.tsx` (SVG-Draufsicht,
Werkzeuge Tür/Fenster/Anschluss per Wand-Klick, Pflicht-Anschluss-Hinweis je
Raumtyp, nutzt `plan2d`-Transform) → schema-konformes Raummodell
(source manuell, `geometryConfirmed true` → keine Unsicherheits-Marge),
geht wie Scan/Sample durch `raumWaehlen` in den Klickpfad.
apps/web: 30 Tests grün, tsc/eslint/prettier/build sauber.
· ✅ **Corporate-Identity-Theme (2026-07-05, Brain: Corporate-Identity):**
`theme.ts` mit den 5 CI-Tokens (Dunkelgrün #243D35 primär, Orange #D6914F
CTA, Off-White #F0F7F2 Grund, Dunkelblau, Salbeigrau) · Header-Wortmarke
FUTURE (weiss) / PLANNING (orange, gesperrt) + Claim «Meet. Match. Build.»
· alle alten Marken-Hexwerte in App/Stil/RaumEditor/Viewer2D/Viewer3D auf
Tokens · **Ampel-/Statusfarben bewusst unangetastet** (verletzt/knapp/
gesperrt + P2/P3; P1 war schon CI-Grün, jetzt via Token + Klarstellungs-
Kommentar) · CI-Fonts NICHT eingebettet (Lizenz offen lt. Brain, Kommentar
im Code) · index.html-Titel mit Claim.
· ✅ **Wizard-UI + Pitch-Grafikstil + Logos + Möbel-Materialfarben
(2026-07-06, Bryan-Vorgaben aus CD-Sheet/Flyer/App-Mockups – Brain:
Corporate-Identity §Layout- & Grafiksprache):**
- **Schritt-für-Schritt-Wizard** statt alles auf einem Screen: 1 Projekt
(Sample-Karten/Scan/Raum erstellen) → 2 Stil → 3 Vorschlag (Küche:
Formwahl) → 4 Anpassen (Viewer/Ampel/würfeln) → 5 Auswertung & Export.
Auto-Weiter nach Raumwahl und Plan; Badge-Navigation; keine Funktion
entfernt (Checkliste im Commit).
- **Pitch-Grafikstil als Tokens:** harter Schlagschatten OHNE Blur
(8px 8px 0) + weisser Innen-Schein auf allen Karten; orange Pill-Buttons;
dünne Versal-Titel; heller Verlauf-Grund (dunkler Flyer-Look = Marketing).
- **Echte Logos:** `public/FP-Logo-horizontal.png` im hellen Header +
Signet als Favicon (aus Brain-assets).
- **Möbel-Materialfarben:** MATERIAL_FARBE je funktionsTyp (Keramik/Holz/
Salbei/Edelstahl/Terracotta/Grün) – greift NUR bei Ampel-Status ok;
verletzt/knapp/gesperrt behalten Statusfarben (Ampel dominiert). Dafür
Viewer3D jetzt mit statusById → **Norm-Ampel auch in 3D sichtbar**.
apps/web 32 Tests grün, alle Gates sauber. CI-Fonts weiter bewusst nicht
eingebettet (Lizenz offen). Bryan liefert eigene Piktogramme nach →
gleicher Stil (inner glow + harter Schatten).
· ✅ **Schritt 6 Scan-Korrektur-Modus (2026-07-06, Brain: ADR-0003 /
M2-M7-Scan-Pipeline-Fahrplan):** nutzergeführte Korrektur gescannter Räume
(~8.5 cm Unsicherheit, kein LiDAR). Reine Geometrie `korrektur.ts` (getestet,
DOM-frei): `ecken` (eindeutige Ecken via Endpunkt-Matching mit Epsilon –
Wand-Array NICHT umlauf-sortiert), `verschiebeEcke` (angrenzende Wände
wandern mit), `snappe` (Achsen-Snap auf Nachbar-x/z < 7 cm → rechtwinklig =
das Anti-8.5-cm-Werkzeug, dann 5-cm-Raster), `kettePolygon` (Umlauf schliessen
analog `_kette` in adapter.py, offene Hülle = Fehler-Objekt statt Wurf →
UI blockt «Übernehmen»), `wendeAn` (Öffnungs-offset-Clamp, Fixpunkt behält
relatives t auf der Wandachse, Fläche/Polygon neu, Objekte
bestätigt→needsReview:false / entfernt raus, geometryConfirmed→true, IDs
stabil, 2-Dezimal-Rundung). UI `ScanKorrektur.tsx` (Muster RaumEditor):
SVG-Draufsicht, Ecken als ziehbare Griffe (Pointer = Maus **und** Touch/
Antippen, live-Snap + Längen), Werkzeuge Tür/Fenster/Anschluss per Wand-Klick,
Scan-Objekte als gestrichelte rotierte bbox (needsReview orange), rechte
Spalte mit Scan-Warnungen + fehlenden Pflicht-Anschlüssen + Öffnungs-/
Anschluss-/Objekt-Listen, «Übernehmen» gesperrt bei Öffnungs-Fehler oder
offener Hülle. App.tsx: `scanLaden` öffnet jetzt das Korrektur-Modal (State
`korrektur`) statt direkt zu wählen; «Übernehmen» → korrigierter Raum in den
Klickpfad, «Abbrechen» → Scan unkorrigiert (geometryConfirmed bleibt false);
«📐 Korrigieren»-Knopf im Schritt «Projekt» für gescannte Räume
(`meta.captureMethod === "ar"`, defensiv). 16 neue Tests (korrektur.test.ts,
inkl. Schema-Validierung des korrigierten Raums). apps/web **52 Tests grün**,
tsc/eslint/prettier/build sauber.
· ✅ **Bild-Katalog aus Bryans Metadaten + echte Fotos (2026-07-06):**
`data/images/{bad,wohnen,kueche}.json` von je 8 Platzhaltern auf **je 30 real
getaggte Bilder** (aus Bryans ZIP-Metadaten generiert; alle 8 Achsen + reiche
`attributTags` + Palette + Lizenz; schema-sauber, ungültiger `roomType:`-Tag
entfernt). **Die echten PNG-Fotos hat Bryan direkt auf `main` hochgeladen**
(`data/images/{roomType}/…png`, 90 Dateien ~2 MB) – bildRef ↔ Datei matcht
1:1 (0 fehlend). `BildKachel` zeigt als Fallback die Farbpalette, falls ein
Foto mal fehlt. Tests robust: Bildzahl-Assert dynamisch, Preset-Test skippt
(Bryans Bilder alle istPreset=false). Bryans neue detailliertere `moebel3d`-
Objekte via `clampTeil`-Sicherheitsnetz bbox-treu gehalten (Norm-Ampel).
· ✅ **Möbel-3D-Varianten-Bibliothek (2026-07-06, Brain:
Moebel-3D-Varianten-Bibliothek):** neues optionales Katalog-Feld
`modell3d` (Schema additiv + Codegen TS/pydantic) → Viewer-Lookup
`modell3d → funktionsTyp-Standard → Box` via `bausatzSchluessel()`.
5 erste Varianten-Bausätze (wc-wandhaengend, lavabo-aufsatz, lavabo-doppel,
sofa-l, dusche-eck) + 7 Katalog-Items zugeordnet. **Stil entscheidet die
Variante** (Kurator-Score über achsenTags – Beleg: Profil warm/natürlich →
Wc Klassik + Naturstein-Aufsatzbecken; Profil kühl/modern → wandhängendes
Design-WC + Eckdusche). Erweitern = Katalog-Item mit achsenTags (+ optional
neuer Bausatz), 3-Schritte-Anleitung im moebel3d-Docstring. Suite: Web 36 +
engines 205/1 skip + scan-worker 22 grün.
· ✅ **3D-Viewer: Türen/Fenster + stilgetriebene Oberflächen (2026-07-07,
Bryan-Wunsch):** Wände jetzt aus massiven Segment-Quadern statt einer Voll-Box
→ Türen/Fenster sind **echte Löcher** (Vollsegmente + Sturz + Brüstung, ohne
CSG). `raum3d.ts` (rein, getestet: `wandSegmente`/`clipZone`/`oeffnungsPose`) +
`raum3d.tsx` (`Tuer3D` mit ~20°-Türblatt/Griff, `Fenster3D` mit getöntem Glas +
Sprosse, `WandMitOeffnungen`, prozedurale CanvasTexture für Boden/Wand). Boden-
& Wandoptik werden aus dem Stilprofil abgeleitet (`oberflaechen.ts`, rein/
deterministisch, **kein Schema-Eingriff** – Konvention wie `MATERIAL_FARBE`):
Bad→Fliesen+Wandsockel 1.2 m, Wohnen→Parkett, Küche→Stein/Fliesen je
Materialität; Achsen temperatur/helligkeit/materialitaet/farbigkeit steuern die
Farbe (HSL-Herleitung). Ohne Stilprofil bleibt die heutige neutrale Optik;
Türen/Fenster erscheinen immer. Abweichung dokumentiert: Wand-Fliesenzone per
Höhen-Clip (2 gestapelte Zonen) statt CSG-Zonenschnitt – robust & dependency-
frei. Suite: **Web +21 (raum3d 12 · oberflaechen 9)**, Gates lint/typecheck/
test(73)/build grün.
· ✅ **3D-Viewer v2: Ansichts-Presets, Orbit+Begehung, 3D-Drag/Rotate,
Ebenen, waehlbare Oberflaechen (2026-07-07, Bryan):** `Viewer3D.tsx`
generalüberholt. **Ansichts-Presets** (Perspektive/Draufsicht/Front/Seite)
als Leiste über dem Canvas – Kamera-Posen aus der Raum-BBox skaliert
(`presetKamera`), OrbitControls-Ziel = Raumzentrum, Draufsicht mit winzigem
z-Versatz gegen die Gimbal-Entartung. **Zwei Begehungsmodi**: 🌀 Orbit (wie
bisher) und 🚶 Begehung = First-Person (Augenhöhe 1.6 m, WASD/Pfeile gehen,
Drag umsehen, Doppeltipp = Schritt vorwärts – Maus **und** Touch, ohne
PointerLock; **keine** Wandkollision, POC-bewusst). **Direkte Objekt-
Interaktion wie im 2D**: gewähltes Möbel per Drag auf der y=0-Ebene
verschieben (Raycast → 5-cm-Raster, `verschiebeNach`-Constraints), Doppelklick
= +90° (`rotiereNach`); Klick öffnet weiterhin die Elementkarte. Nur in
Schritt 4 (`interaktiv`), Vorschau read-only. **Ebenen-Toggles** Ampel
(Statusfarben an/aus) + Wände (ausblendbar für freie Sicht von aussen).
**Oberflächen als wählbare Objekte**: Klick auf Boden/Wand (kein Möbel
getroffen) wählt die Fläche → Elementkarte-Ersatz «Oberfläche» in
`SchrittAnpassen.tsx` mit 3 vordefinierten Boden-/Wandvarianten je Raumtyp
(`variantenFuer`); die Wahl überschreibt lokal die stilabgeleitete Spez
(`wendeVariantenAn`, **rein visuell, kein Schema-/API-Eingriff**) und **bleibt
beim «Variante würfeln» erhalten** (eigener State, `loesen` fasst ihn nicht
an; nur Raumwechsel setzt zurück). Reine Logik in `viewer3d-logik.ts`
(getestet: BBox, Preset-Posen, Begehungs-Schrittvektor, Blickrichtung). Im
Begehungsmodus ruhen die Möbel-Pfeiltasten (`viewer3dModus` in App, Konflikt
vermeiden). Bewusst weggelassen: Wandkollision (POC), 3D-Labels/Beschriftungs-
Ebene (der 3D-Viewer trägt keine Text-Labels), PointerLock (Drag-Look ist
robuster + mobiltauglich). Suite: **Web +19 (viewer3d-logik 13 · oberflaechen
+6) → 120**, Gates lint/typecheck/test/build grün.
· ✅ **Viewer-Feinschliff (2026-07-07, Bryan):** (1) **3D-Bearbeitungsmodus
entfernt** – Möbel werden im 3D NICHT mehr verschoben/gedreht (Drag/Rotate +
DragEbene raus); 3D = reine Ansicht/Begehung, Klick wählt weiterhin aus
(Produktkarte) und Flächen/Oberflächen bleiben wählbar. Bearbeiten läuft nur
noch im 2D-Grundriss. (2) **2D-Planlook bei Ampel-aus**: ist die Ampel-Ebene
aus, zeichnen die Objektsymbole mit schwarzem Stift (`FARBE_PLAN`) und
**weisser Schraffur-Füllung** (SVG-`<pattern>` 45°), Boden wird reinweiss –
Architektenplan-Optik; Ampel an = farbig wie bisher. (3) **Zurück zur
Titelseite**: «Zurück» auf Schritt 1 führt zurück ins Startmenü
(`setPhase("menue")`, Label «← Zur Startseite»); AppRahmen um `zurueckLabel`
erweitert. Gates grün, 120 Tests.
· ✅ **Space-Deploy entschlackt + HF-Build-Fix (2026-07-07, Bryan):** Der HF-
Space bekam bei jedem Deploy ~233 MB Bilder (wahrscheinliche Build-Fehler-
Ursache). Fotos `data/images` von **PNG 941×1672 (1.7–2.8 MB)** → **JPEG q82,
max 1400px** (197 MB → 19 MB), `bildRef` in bad/kueche/wohnen.json auf `.jpg`
(StaticFiles serviert weiter). 43 ungenutzte Piktogramm-Originale (35 MB, teils
Umlaut-Dateinamen) entfernt – die App lädt nur die 25 ASCII-Registry-Kopien.
Deploy-Bilder gesamt **233 → 37 MB**. Engines 205 + Web 120 Tests grün.
· ✅ **2D Layer-System v2 + Wand-/Fenster-Look (2026-07-07, Bryan):** Die vier
«Ebenen»-Checkboxen ersetzt durch ein **Layer-Panel** (`layer2d.ts`): 8
**Darstellungs-Layer** (Wände/Öffnungen/Objekte/Bounding-Boxen/**Platzbedarf**/
Beschriftung/Masse/Ampel, Defaults: Boxen/Platzbedarf/Masse aus) + **Objekt-
Layer** (jedes Möbel einzeln ein-/ausblendbar, CAD-artig, `versteckteObjekte`).
**Platzbedarf-Layer**: Clearance-Zonen aus den `type:"clearance"`-Regeln
(`clearanceRect` in plan2d) als Bewegungsflächen vor den Objekten (halbtransp.
+ gestrichelt, Tooltip = `hinweis`). **Fenster jetzt IN der Wandstärke** (echte
Aussparung via `wandLuecken`, Glas über volle Wandstärke) statt Linie obenauf;
**Wand-Look nach Referenzbild** (helle graue Füllung `#d3d3d3` + dünne Kante).
`rules` von App an Viewer2D durchgereicht; Planlook (Ampel aus) bleibt.
+11 Tests (plan2d clearance/wandLuecken, layer2d) → **Web 131**, Gates grün.
· ✅ **UI-Redesign Etappe A: Designsystem + App-Rahmen (2026-07-07,
Bryan-Konzept):** Fundament, das die 5 Schritt-Ansichten (Etappe B) nur noch
konsumieren. **Designsystem** – globales Stylesheet `apps/web/src/fp.css`
(in `main.tsx` importiert): `:root`-Farbrollen als CSS-Variablen
(`--fp-primary/-dark/-accent/-muted/-soft/-background/-text`), Typografie-Vars
(`--fp-font-display/-body`, Schriftdateien BEWUSST nicht eingebettet – Lizenz
offen), vereinheitlichter Kartenstil `.fp-card` (+ `.fp-card-flach`
flach, `.fp-card-aktiv` oranger Rand) mit hartem Schlagschatten OHNE Blur
(7px 7px 0) + Innenschein, `.fp-button` (Press/Hover-Animation + `:active`
scale), `.fp-schritt-neu` (einmalige Einblendung), `.fp-piktogramm-puls`,
Responsive-Helfer `.fp-two-column`/`.fp-drei-karten` (@media 820px) + reduce-
motion. **Hintergrund reinweiss** (index.html body + `--fp-background`);
Off-White nur für Sekundärflächen. `theme.ts` **v2** – Rückwärtskompatibel
(THEME/karte/pill/titel/schlagschatten/innerGlow behalten, zeigen auf die
neuen Werte) + neu `FP_VAR` (CSS-Var-Strings) & `CSS` (Klassennamen-Konstanten).
**App-Rahmen** `AppRahmen.tsx` (Header Logo/Projektname/Menü-Platzhalter ·
Fortschrittsweg · Inhalt zentriert max-width 1400 · Footer Zurück/Weiter).
**Fortschrittsweg** `Fortschrittsweg.tsx` (Kreis+Label+Linie, aktiv=orange /
erledigt=grün+✓ / gesperrt=salbei, mobil scrollbar) auf getesteter Logik
`fortschritt.ts` (`schrittZustand`, 6 neue Tests). **Piktogramm-System**
`Piktogramm.tsx` – 25 Inline-SVGs + **Drop-in** (`<img /piktogramme/<name>.png>`
→ onError-Fallback aufs SVG; Ordner `public/piktogramme/` mit README). **Lade-
zustände** `Ladezustand.tsx` (pulsierendes Piktogramm + 4 Presets stil/
vorschlag/scan/dokumente – Etappe B verdrahtet). App.tsx: nur Rahmen-
Präsentation ersetzt (weisser Grund, alter Header/Stepper/Footer → AppRahmen),
Schritt-Logik/State unangetastet. **Web 79 Tests grün**, lint/typecheck/build
sauber.
· ✅ **UI-Redesign Etappe B: die 5 Ansichten (2026-07-07, Bryan-Konzept):**
Schritt-Inhalte aus App.tsx in eigene Komponenten gezogen –
`SchrittProjekt.tsx` («WAS MÖCHTEST DU PLANEN?», 3 Raumtyp-Karten mit
Piktogramm + Auswahlzustand, darunter die 3 Wege Beispiel/Scan/Manuell;
Raumtyp-Vorwahl steuert Scan-Raumtyp + filtert Beispiele) ·
`SchrittStil.tsx` (bildzentriertes Bewerten inline statt Overlay,
Fortschritt «n von m», Like/Dislike/Überspringen mit Piktogrammen,
Stilprofil-Karte mit Kurzworten/Spider/Top-3-%-Balken via fp-two-column,
«Dein Stilprofil wird genauer.» alle 5 Bewertungen) · `SchrittVorschlag.tsx`
(Zustand A Auslöser inkl. Küche-Normprofil/Formwahl; Zustand B «DEIN
RAUMVORSCHLAG IST BEREIT» mit 2D-Vorschau, Stilbegründung, Haupt-
materialien, Variante/Seed, einfacher Normstatus; EINE orange Hauptaktion
«Ansehen & anpassen») · `SchrittAnpassen.tsx` (fp-two-column: Viewer links,
Elementkarte rechts mit Normstatus passt/knapp/anpassen als Text+Piktogramm,
Tausch-Alternativen, Sperren; Hilfe-Karte ohne Auswahl) ·
`SchrittAuswertung.tsx` («DEIN PROJEKT»-Zusammenfassung mit Kosten-Spanne/
Gewerke-Zahl/Status + Dokument-Karten-Raster mit Piktogrammen, bestehende
api.dokument-Downloads). Ladezustände (stil/vorschlag/scan/dokumente)
verdrahtet. Logik/State/API unverändert in App.tsx. Hinweis: Etappe-B-Agent
brach am Session-Limit ab – Splice-Reparatur (verwaistes </div>, TS-Narrowing
Stil-Bild, Prettier) durch die Hauptsession. **Web 79 Tests grün**,
lint/typecheck/build sauber.
· ✅ **Stil-Schritt v2: Live-Profil pro Swipe, Swipe-Animation,
Konvergenz-Stopp, Bildhöhe fix (2026-07-07, Bryan):** Swipe-Bild auf
`min(46vh,420px)` begrenzt (kein Scrollen) · Stil-Analyse steht jetzt IMMER
neben dem Bild (fp-two-column immer aktiv; Neutralzustand «Bewerte Bilder,
dein Profil entsteht live.») und aktualisiert LIVE nach jeder Bewertung
(neuer Prop `onZwischenProfil` → `api.styleProfile`, nur Profil-State, kein
Schrittwechsel/Ladezustand; Balken re-animieren via `fp-schritt-neu`) ·
Swipe-Animation (Like gleitet rechts + oranger Akzent, Dislike links +
dunkel, ~220 ms, reduce-motion respektiert, Überspringen ohne Animation) ·
Konvergenz-Stopp: neues getestetes Modul `stilkonvergenz.ts`
(`istStabil(verlauf, minBewertungen=8, delta=0.06, fenster=2)`) stoppt das
Bewerten sobald das Profil stabil ist («Weiter mit diesem Profil» / «Weiter
verfeinern»), statt alle 30 Bilder zu erzwingen. Kleine, dokumentierte
Abweichung: finales `onProfil` springt nun automatisch zu Schritt 3
(bestätigtes Profil → «weiter»). **Web 86 Tests grün** (79 + 7 neue),
lint/typecheck/build sauber.
· ✅ **2D-Viewer v2: Wandstärke, Objektsymbole, Ebenen, Messen, Drag&Rotate,
Produkt-Panel (2026-07-07, Bryan):** Der 2D-Grundriss zeigt jetzt echte
**Objektsymbole** (Architekten-Draufsicht) statt nur beschrifteter Boxen –
neues reines Modul `symbole2d.ts`: je funktionsTyp eine Signatur (WC
Schüssel+Spülkasten, Lavabo-Becken, Dusche Quadrat+Diagonale+Abfluss,
Badewanne, Sofa Sitz/Rücken/Armlehnen, Ess-/Couchtisch, Bett Kissen+Decke,
Regal/Sideboard/TV Tiefenlinien, Kochfeld 4 Kreise, Spüle Becken+Armatur,
Kühlschrank Türlinie, Leuchten Kreis+Strahlen, Pflanze, Teppich gestrichelt,
Schränke, …) – **alle 31 Katalog-funktionsTypen abgedeckt** (Test), Fallback =
bisherige Box. Die Symbole nutzen die vorhandene Footprint-Transformation
(Position/Yaw/bbox über `rotateY`+`toScreen`, deckungsgleich mit `footprint()`)
und die **Ampel-/Materialfarbe** als Strichfarbe. **Wände mit echter
`thickness`** als gefüllte Polygone (`wallQuad`, Fallback 0.1 m); Türen mit
Aufschlag-Viertelkreis + Fenster als Doppellinie bleiben. **Ebenen-Leiste**
(Beschriftung/Boxen/Masse/Ampel, Default alles an ausser Boxen).
**Messwerkzeug** «📏 Messen» (zwei Klicks → Linie + Distanz in m, Snap auf
Wand-Ecken < 15 cm, Escape/Moduswechsel löscht). **Direkte Interaktion**:
gewähltes Objekt per Pointer-Drag verschieben (5-cm-Raster, bestehende
`verschiebeNach`-Constraints) + **Rotations-Griff** (Drag rastet 15°,
Doppelklick +90° via neues `rotiereNach`); Live-Ampel läuft wie beim Keyboard
weiter. **Detail-Panel (Schritt 4)** erweitert: funktionsTyp, Prioritätsklassen-
Badge, Materialfarbe-Punkt + Masse; Tausch-Alternativen als kleine Produkt-
karten (Name + Farbpunkt + Masse) statt Select. Schritt 3 (Vorschau) bleibt
read-only (`interaktiv=false`): Symbole/Wandstärke automatisch, keine
Editier-Interaktion. Neue reine, getestete Geometrie in `plan2d.ts`
(`wallQuad`, `distanz`, `wandEcken`, `naechsteEcke`, `yawAusZeiger`, `rasten`).
App.tsx nur minimal angefasst (Props + `rotiereNach`); Keyboard-Steuerung
unangetastet. **Web 101 Tests grün** (86 + 15 neue), lint/typecheck/build
sauber. Bewusst weggelassen: typübergreifender Tausch (wie schon dokumentiert),
Symbole als Fill (bewusst nur Strichkontur = Architektenlook, Ampel bleibt als
Tint-Füllung + Rahmen lesbar).
**Als Nächstes:** Presets optional kuratieren · ⚠️ 90 PNG (~180 MB) blähen
Repo + Space-Deploy – ggf. Bildgrösse reduzieren/auslagern ·
Schritt 5 E2E gegen R1 auf Colab (braucht AR-App-Aufnahmen) ·
Bryans Piktogramme. Alt-Punkte unverändert offen: circulation
auf `hard` hochstufen (Hot-Path-Performance, mit Bryan abstimmen); Küche
post-POC: mehr Slot-Breiten (30/45/90), triangle-aware Slot-Optimierung.
2. **M2 Scan-Spike weiterführen:** Metrik-Kern + Ecken-Antippen-Pfad stehen &
sind getestet (2026-06-14). **Offen – Bryan:** R1-Restmasse (Raumhöhe,
Türbreite, Objektmasse) + Neuaufnahme nach Guideline + R2/R3; dann
`spike_eval.ipynb` einmal auf **Colab (T4)** laufen lassen (GPU-Schritte gehen
hier nicht). **Offen – Code:** P1 automatischer Layout-Fit (GPU) + P4
Spiegel/Glas-Maskierung; danach Gate-Entscheid → Learning ins Brain.
3. Goldens bewusst aktualisieren: `uv run python scripts/update_goldens.py`
(aus `services/engines/`), nur zusammen mit Interpreter-Änderung committen.
## Bewusste Abweichungen / Engineering-Entscheide
| Entscheid | Grund | Wo dokumentiert |
|---|---|---|
| Eigener Code proprietär (kein LICENSE-File) | Entscheid Bryan 2026-06-11 | README, CLAUDE.md |
| `circulation` v0 **soft** statt hard (Norm-Regelsatz nennt hard) | Norm-Regelsatz lässt unter «Offene Fragen» hard/soft explizit offen; soft informiert ohne die Solver-Invariante zu brechen (Solver ist noch nicht verkehrsweg-aware). Hochstufung auf hard = bewusster Folgeschritt mit Solver-Support | rules/basis.json, rules-README, Learning |
| `circulation`-Marge grob/konservativ (0.05-m-Raster, bbox-Blocker, Türmund-Pessimismus) | v0-Freiraumanalyse: ganzzahlig (paritätssicher), Manhattan-Distanztransform, achsparallele Bounding-Box je bodenstehendem Objekt; Türbreite cappt den Bottleneck. Tuning (Euklid, feiner, türbewusst) post-POC | interpreter.py/.ts (Docstring), Learning |
| Kollision prüft vertikale Überlappung (Höhenintervalle) | Spiegel ÜBER Lavabo ist keine Kollision – nötig für P2-Wandobjekte | Interpreter beidseitig, Learning im Brain |
| Editor: Drag&Drop nur im 2D-Grundriss (3D bleibt Pfeiltasten) | 2D hat eine eindeutige Screen→Welt-Umkehrung (toWorld); 3D-Drag bräuchte Raycasting – später | Viewer2D, plan2d.ts |
| «austauschen» bietet nur gleichen Funktionstyp + gleiche normProfileVariante | hält Plan/Küchenzeile konsistent; typübergreifender Tausch (z.B. Dusche→Badewanne) wäre eine eigene Re-Solve-Logik – post-POC | App.tsx (alternativen) |
| `door-swing` v0 als Rechteck (Breite×Radius) statt Viertelkreis | identische, einfache Geometrie in TS & Python; konservative Näherung | Code-Kommentar + Fixtures |
| M5: Bett-Regel «Zugang ≥1 Längsseite» nicht umgesetzt → Wohnzimmer-Fokus | Mit den aktuellen Regel-Typen (clearance/object-distance/host-binding) nicht ausdrückbar; Schlafen-Spezifika (Bett, beidseitiger Zugang) auf später verschoben statt Interpreter zu ändern (Paritäts-Gesetz) | STATUS, wohnen.json |
| M5: Esstisch inkl. Stühlen als EIN Footprint | POC-Vereinfachung – Stuhl-Einzelplatzierung wäre eigene Solver-Stufe; Footprint deckt Tisch + ausgezogene Stühle ab, clearance-Regel zusätzlich ≥1.0 m | catalog/wohnen.json (Item-Beschreibung) |
| M5: relationalRules-Distanzen wohnen als Zentrum-zu-Zentrum kalibriert (z.B. `near:sofa:1.3` statt 0.45) | Der Solver-Relationsfilter misst Zentrumsabstand; bei grossen Ankern (Sofa 2.1×0.95) wäre der ergonomische Kantenabstand 0.40–0.45 m als Zentrumswert kollidierend. Die ergonomische Norm bleibt als **soft** object-distance-Regel (Kantenmass 0.40 m) erhalten | catalog/wohnen.json, rules/wohnen.json |
| M6-A: Grossraum-Sample mit echter `wall.kind:"offen"`-Kante mitten im Raum (statt nur Zonen-Polygone) | Code verträgt innenliegende Segmente problemlos (kein Hüllen-Closure-Validator; Solver/Interpreter filtern auf `kind=="massiv"`). Die offene Kante modelliert die reale offene Zonengrenze explizit; `zone_room` clippt sie auf die Zonenkante und erhält `kind:"offen"`. Die synthetische `virtuell`-Erzeugung greift nur, wenn KEINE Hüllenwand die Zonenkante deckt (separat getestet) | grossraum-sample, zonen.py, test_zonen.py |
| M6-A: GS↔Spüle-«maxDist»-Regel NICHT als `object-distance` umgesetzt | Der Regel-Interpreter kennt bei `object-distance` nur `minDist` (kein maxDist). «Geschirrspüler direkt neben Spüle» ist Baugruppen-/Slot-Logik → kommt in Phase B; Interpreter wird (Paritäts-Gesetz) nicht erweitert. Nähe bleibt v0 über `relationalRules:["near:spuele:0.9"]` am Katalog-Item | rules/kueche.json, catalog/kueche.json, STATUS |
| M6-A: clearance je Küchen-Haupttyp (spuele/kochfeld/geschirrspueler/unterschrank) statt einer Sammelregel | `appliesTo` matcht auf `funktionsTyp`; eine Regel pro Typ ist die mit den bestehenden Regel-Typen ausdrückbare Form des «Gang vor Zeile ≥ 1.0 m». Echte Zeilen-/Gang-Geometrie (circulation) kommt mit Phase B | rules/kueche.json |
| M6-B: Ecke bei L/U → **Eckschrank** (Politur 2026-06-14, behebt den früheren Totraum) | grid×grid-Eckquadrat am Wand-Stoss wird aus den Schenkel-Slots reserviert, ein Eckschrank (Karussell, ch55/eu60) gesetzt – VOR den Füllstücken, damit sie ihm ausweichen. Additiv (nur wenn zulässig), Invariante bleibt 0 ❌. L = 1 Ecke, U = 2 | kueche.py (`_eck_zonen`/`_platziere_eckschraenke`), catalog/kueche.json, test_kueche.py |
| M6-B: Fenster blockieren Unterschränke NICHT, nur die Hängeschrank-Ebene meidet Fenster | Brüstungsfenster (sill ≥ 0.9 m) erlauben darunter eine Arbeitsfläche/Unterschrank; nur Hängeschränke dürfen nicht vor ein Fenster. Türen blockieren dagegen die ganze Zeile (Nutzlänge = türfreies Teilstück) | kueche.py (`_fuelle_haengeschraenke`, `_freies_teilstueck_intervall`) |
| M6-B: Hängeschrank-Heuristik = Ebene über allen Unterschränken ausser Kochfeld | Über dem Kochfeld hängt der Dunstabzug; sonst wird jede freie Wandposition mit Hängeschrank belegt (vertikal getrennt von den Korpussen → keine Kollision dank Höhenintervall-Prüfung des Interpreters). Einfach + deckt den Stauraum-Score | kueche.py (`_fuelle_haengeschraenke`) |
| M6-B: GS-«direkt neben Spüle» als Slot-Nachbarschaft (nicht als Regel) | Der Interpreter kennt kein `maxDist` bei object-distance (M6-A-Entscheid); die Baugruppe setzt den GS deterministisch in den Nachbarslot der Spüle. Damit ist die Forderung konstruktiv erfüllt, ohne den Interpreter zu ändern | kueche.py (`_platziere_geschirrspueler`) |
| M6-B (Review-Fix): P1-Geräte sind Pflicht – fehlt Spüle/GS/Kochfeld/Kühlschrank → 422 NO_FEASIBLE_PLACEMENT (nur Dunstabzug best-effort, lueftung-connection ist soft) | Vorher entstand still ein Rumpf-Plan ohne Kühlschrank (constraintReport 0 ❌, aber fachlich unvollständig) – ehrliches Scheitern statt stiller Degradation | kueche.py (solve_kueche) |
| M6-B (Review-Fix): Solver-Vorfilter toleriert Berührung (Überlappung ≤ 0.05 cm = halbe Rundungsquante) | Exakt anliegende Korpusse (Küchenzeile!) erzeugen Float-Krümel-Überlappungen ~1e-16; der Vorfilter war strenger als das Interpreter-Urteil (0.1-cm-Rundung) und verwarf gültige Slots – deshalb fehlte der Kühlschrank | solver.py (_schnell_unzulaessig, _BERUEHRUNGS_EPS) |
| M6-B: `formwahl`-Score nutzt für Ergonomie weiterhin einen Proxy (echtes Dreieck erst NACH der Platzierung) | Formwahl läuft VOR der Platzierung → das echte Arbeitsdreieck ist dort unbekannt; der Proxy (kompakte I-Zeile, L/U-Bonus) ist richtungsrichtig. Das **gemessene** Dreieck füllt seit 2026-06-14 `softScore.ergonomie` des fertigen Plans (AMK-Trapez-Score, server-berechnet) – kein Eingriff in Interpreter/Schema/Goldens | kueche.py (`formwahl` Proxy · `arbeitsdreieck` Messung) |
| M6-B: /solve-Response um `room` (effektiver Raum) erweitert | Response ist kein Schema-Vertrag (Plan-Schema bleibt unberührt). Der Grossraum-Fall braucht den abgeleiteten Teilraum für Viewer + Live-Ampel; additiv am pydantic-Response-Modell | api.py (`/solve`), App.tsx (`planRoom`) |
## Offene Fragen an Bryan
- AR-Vorschau (Stretch) bestätigen → `vault/50_Umsetzung/AR-Vorschau-Konzept.md`.
- Erste Katalog-Quelle für echte Items/Preise (POC-Fallback: Sample-Daten).
- Spike-Testräume R1–R3 aufnehmen (sobald Eval-Notebook steht).