---
name: cpq-verbau-architektur
description: "adesso-cpq Verbau/Modell-Architektur (Product/Modell/Graph, BLAST-Validierung) + nächster Schritt für morgen früh"
metadata: 
  node_type: memory
  type: project
  originSessionId: d789290f-6631-426e-9639-e7ea7a60e8f9
---

Stand der Architektur-Diskussion adesso-cpq (Schutzzaun-Konfigurator). Repos: ~/projects/adesso-cpq (frontend/CPQ), ~/projects/geko-core (Speedmaxx-Backend). Constraint-Typen folgen noch; jetzt erst Infrastruktur.

## Kernmodell (erarbeitet, trägt)
- **Product = zweigeteilt:** (1) Graph mit PMIs/Collidern/Transforms = der „wertlose" Graph (keine Autorität, nur Darstellung + Approximationsdaten); (2) Variablen = Config + Constraints.
- **Product = Entity** (BaseEntity, BLAST-validiert). **Konstrukt aus Produkten = auch Entity = Modell.** Rekursiv: Modell = Produkt (Blatt) | Konstrukt aus Modellen. Aus den Entities wird am Ende der wertlose Graph projiziert.
- **Quellen (mehrere!):** Speedmaxx ist führend (~60%), daneben eigene Modelle (optimaler gestaltbar) + weitere Lieferanten. Darum muss BLAST gegen ein **normalisiertes** Modell validieren, nicht gegen Quell-Spezifika oder den rohen Graphen. Im Zweifel ein **Converter**, der Speedmaxx & whatever normalisiert.

## Speedmaxx-Realität (Quelle der Wahrheit, NICHT der cpq-Mock)
- Branch geko-core `feature/get-scene-from-speedmaxx`: `executeCommand` → `ExecuteCommandResponse { ui: Record<string,SpeedmaxxVariable>, viewing, error }`.
- `ui` = Config+Constraints: `domain.{min,max,step,valuelist,default}`, `required/visible/editable/multivalue/datatype/unit/...`. Hier stecken „≤6m / ≤10 Teile".
- `viewing` = die Szene (wertloser Graph). Controller `loadScene` ruft `combo1_cmd`, ProductCategory "Schutzzaun", Variables wizard → gibt `result.viewing`.
- Format des Graphen (= Alttool = `assets/scenes/zaun.json`): `sceneGraph_entity { handles[] }`; Hierarchie flach über `component_transform.key/parentKey` (root → asm_root → produkt-node → ~50 Blatt-Teile); `component_pmi` (z.B. CS_SVS/CS_BASIS am Produkt-Knoten) = Andockpunkte; `component_collider`; Länge teils in `scale.z` eingebacken.
- **FALSCH in cpq:** `packages/frontend/src/util/speedMaxx2Visual.ts` erfindet `{environmentId, nodes:[{id,label}]}` — gibt es bei Speedmaxx nicht. Muss korrigiert werden.

## Validierung / BLAST (entschieden)
- Variablen → BLAST direkt (per-Produkt + Querregeln).
- Geometrie-Constraints (≤10 Teile, Gesamtlänge, PMI-Kompatibilität, Platz/Kollision) stehen NICHT in Variablen → ergeben sich aus PMIs/Collidern/Transforms.
- Geometrie-RECHNUNG läuft in der Engine (nicht im BLAST-Kern, der ist geometrie-agnostisch). Aber: **Engine wird AUS BLAST getriggert.** BLAST ist der Orchestrator und reaktiv (Änderung → sofort wirksam), Cache sparse pro Entity-ID → Engine läuft nur für den betroffenen Bauzug.
- Zwei Topos-Ebenen: per Einzelteil + konsolidiert (Bauzug). Konsolidierter Topos ruft die Engine, holt Verdikt, validiert.
- Hebel = Abhängigkeitsschärfe (welcher Topos hängt woran). Wird über Constraint-Typen geregelt (kommt noch).
- Roher Graph geht NIE nach BLAST; nur Variablen + destillierte Graph-Fakten/Verdikte.

## Schichten (Zielbild)
- **UI** spricht nur mit dem **Scene/Model-Service** (Name noch offen), gibt dessen Ergebnis als Graph/Commands an fire.
- **Scene/Model-Service** (core): hält Modell/Bauzüge, PMI-Verbindungen, rechnet Constraints, projiziert Modell → Graph. Intern getrennt: Modell/Constraint (entscheidet, kennt Platz) vs. Projektion (stumpf, entscheidet nichts).
- **ProductService** (gehört nach **core**, FE-agnostisch): getProduct/updateProduct, liefert nur Einzelteile, **quelle-agnostisch** über die Asset-Abstraktion (datamanager-Connectors in environment.config.json: path+typeKey-Templates; dev=Assets, live=Server). Darf NICHT wissen, woher.
- **fire/Graph**: rendert, ist außerhalb unserer Engine interaktiv und arbeitet mit Näherungen (Collider). Wir haben das letzte Wort; nach jeder Interaktion **Vollaustausch** des Graphen.
- **Gemeinsame Constraint-Sprache nötig** (3x bestätigt): zwischen zwei gemeldeten Interaktionen handelt fire autonom; es braucht dieselben Constraints. Lösung: **deklarative Daten im Graph** (Collider/PMI/Grenzen), kein geteilter Code. Offen: diese deklarativen Constraints und die exakten BLAST-Regeln müssen aus EINER Quelle stammen, sonst Drift.

## Bestand an Typen (adesso-cpq)
- shared: `BaseEntity { id }`. core: nur `EntityStore`, keine Typen.
- blast: `ObjectSchema<T extends BaseEntity>`, `ToposKey`, `BehaviourResolver`, `HintResolver`, `Diagnostic`, `Severity('error'|'warn'|'info')`, `Behaviours`, `Hint`, `FieldState`, `ItemResult`, `BlastValidator`.
- visual-runtime: `Visual3D` (setScene/execute/subscribe/camera), `CameraTransformation{translation,rotation,scale}`, `ConnectorConfig`, `Visual3DConfig`, `Observable<T>`.
- **Szenegraph-Format-Typen (SceneGraph/Handle/Component, component_transform/pmi/collider) sind HEIMATLOS** — nur in `utils/sceneMerger.ts` (fliegt raus) und als `any` in Fire3D.vue/DefaultCommandProzessor/SceneReplayProcessor. Brauchen EINEN Besitzer (Fundament für Product/Scene/fire).
- `utils/sceneMerger.ts`: addProductInstance/mergeScene/generateUniqueIds/positionScene/calculateNextPosition — fliegt raus (falscher Ort `utils`, zu groß, vermischt Mechanik/Platzierung/Instanz). generateUniqueIds vom User als „Mist" markiert.

## Offene Baustelle aus dieser Session
- `packages/frontend/src/services/product-service.ts` habe ich angelegt (getProduct/updateProduct, AssetLoader, lokaler SceneGraph-Typ). User-Kritik: zu nah an UI (gehört nach core), zuviel vorab definiert (Interfaces/Typen ohne Plan). Morgen entscheiden: behalten/verschieben/löschen. NICHT eigenmächtig Typen streuen.

## MORGEN FRÜH ZUERST
1. GENAU auf die **Entities für BLAST** schauen: was ist exakt die normalisierte Entity (Product, Modell). Wahrscheinlich ein **Converter** rein, der Speedmaxx (ui+viewing) und andere Quellen normalisiert. Das MUSS sitzen, sonst Schmerzen.
2. Dann Infrastruktur/Schichten verorten (ProductService → core, Format-Typen-Heimat).
3. Constraint-Typen erst danach.

## Arbeitsregeln des Users (hart)
- Sehr knapp, exakt das Gefragte, KEINE ungefragten Optionen/Vorschläge/Typen. Eins nach dem anderen, langsam. Nicht 3 Schritte vorausbauen. Keine Antwort mit Rückfrage/Angebot beenden. Kein Claude-Hinweis in Commits. Vor verändernden Aktionen kurz Plan + Go abwarten.
- Branch+MR-Workflow (adesso-CLAUDE.md); frontend→core ist paketübergreifend → nur mit explizitem Go. Git-Remotes heron.at: HTTPS+Token (osxkeychain), SSH (Port 22) ist geblockt.
