This commit is contained in:
ValueOn AG 2026-04-23 23:10:02 +02:00
parent 19f28e85d9
commit 0923454b57
4 changed files with 543 additions and 24 deletions

View file

@ -0,0 +1,306 @@
<!-- status: plan -->
<!-- started: 2026-04-23 -->
<!-- component: gateway, frontend-nyla -->
# Typed Generic Handover für den Graphical Editor — PicknotPush, StaticOnly Schemas, Hard Validation
## Beschreibung und Kontext
Mit `2026-04-generic-graph-editor.md` (done) wurden ein typisiertes PortSystem (`portTypes.py`), generische `frontendTypeRenderers/` und ein schemabasierter `DataPicker.tsx` eingeführt. Der zur Laufzeit wirksame Handover zwischen Nodes basiert dort jedoch weiterhin auf einem **PushModell**: `actionNodeExecutor._wireHandover` ruft `INPUT_EXTRACTORS` (Heuristiken pro SchemaName) auf und füllt fehlende Parameter im Konsumer aus dem Output des direkten Vorgängers.
Im PWGPilot (`pwg-pilot-jahresmietzinsbestätigung.workflow.json`) zeigt dieses Modell mehrere Symptome:
- `connectionReference` ist in den meisten FolgeNodes leer (`n4 sharepoint.downloadFile`, `n10 email.draftEmail`), wird zur Laufzeit erneut vom User abgefragt — Heuristik kennt `connectionReference` nicht.
- KI versteht den Handover beim Modellieren nicht: das OutputSchema (`DocumentList { documents }`) ist zu flach, Provenienz (Connection, SourceFolder) fehlt — die KI kann keinen sauberen Pfad picken, sondern „rät".
- Felder wie `attachmentBuilder` rendern als unstrukturierte TextInputs (kein passender Renderer).
- LoopBodyNodes können nicht sauber auf VorgängerKontext (z.B. SharePointLabel aus 1. Node) zurückgreifen.
**Geschäftstreiber:** Solange der Handover nicht generisch deterministisch ist, sind sowohl AIgestütztes WorkflowModellieren als auch UserSelfService unzuverlässig. Jede neue NodeKombination produziert neue EdgeCases. Das blockiert die operative Skalierung des Graphical Editors über den PWGPilot hinaus.
**PrinzipienWechsel (Decisions vom 20260423):**
1. **PicknotPush:** Jeder InputParameter ist explizit gebunden (DataRef / Static / SystemVar). Keine `INPUT_EXTRACTORS`Heuristik zur Laufzeit. AutoSuggest gibt es nur als AuthoringKomfort beim WireConnect.
2. **StaticOnly Schemas:** OutputSchemas sind entweder im CodeKatalog (`PORT_TYPE_CATALOG`) oder als **graphdefined Schema** im WorkflowGraph fixiert (z.B. `input.form.fields`). Keine Runtimedynamischen Schemas (kein `ai.extractStructured`, kein `data.transform`).
3. **LoopScoping lexikalisch:** Im LoopBody sind alle lexikalisch sichtbaren OuterNodes pickbar **plus** zusätzlich `loop.item`, `loop.index`, `loop.total`. Kein MagicEnvelope.
4. **Connection als Hybrid:** SourceNodes haben weiterhin einen `userConnection`Parameter mit Picker. Sie exponieren eine `ConnectionRef { id, authority, label }` (ohne Secrets) im Output. DownstreamNodes binden ihren `connectionReference` per DataRef gegen diese `ConnectionRef`.
5. **Hard Validation:** TypeMismatch oder fehlende RequiredBindung blockiert den Run vor Start.
6. **Greenfield + Migrationsskript:** kein HeuristikFallback. Bestehende Workflows werden einmalig automatisch migriert (deterministisch, weil die alte Heuristik selbst deterministisch ist).
## Fokus und kritische Details
### Was schon da ist (verifiziert im Code, 20260423)
- `gateway/modules/features/graphicalEditor/portTypes.py``PortField`, `PortSchema`, `PORT_TYPE_CATALOG`, `SYSTEM_VARIABLES`, `_normalizeToSchema`, `INPUT_EXTRACTORS`.
- `gateway/modules/workflows/automation2/executors/actionNodeExecutor.py``_wireHandover()` (zu entfernen), `_resolveConnectionParam()` (bleibt im neuen Modell, Anwendung verschiebt sich).
- `frontend_nyla/src/components/FlowEditor/nodes/shared/DataPicker.tsx` — schemabasierte PfadAuflösung mit TransitChain, SystemSektion.
- `frontend_nyla/src/components/FlowEditor/nodes/frontendTypeRenderers/index.tsx``FRONTEND_TYPE_RENDERERS` Registry.
- `frontend_nyla/src/components/FlowEditor/editor/NodeConfigPanel.tsx` — generischer ConfigRenderer.
### Was fragil / kritisch wird
- **`PORT_TYPE_CATALOG` ist heute zu flach.** `DocumentList` enthält nur `documents: List[Document]`, aber `Document` selbst ist nicht im Katalog typisiert. Folge: Picker kann nicht in einzelne DocumentFelder (z.B. `name`, `mimeType`, `downloadUrl`) reinpicken. Dasselbe gilt für `FileList`, `EmailList`, `TaskList`. → Alle ListSchemas brauchen `itemSchema`, alle ItemTypen brauchen eigene Einträge im Katalog.
- **LoopScopeBerechnung im Frontend.** Heute sehr flach. Muss BFS rückwärts entlang `connections` plus transitive LoopBodyHierarchie berücksichtigen, ohne PerformanceKollaps bei großen Graphen.
- **TypeCompat ist heute weich.** `validateGraph` warnt nur — neu: hart blockierend, plus klare FehlerUX im Frontend (rote Pills + RunButton disabled).
- **Migration der bestehenden Workflows.** Deterministisch, weil `INPUT_EXTRACTORS` deterministisch sind — aber jede Node mit implizitem WireHandover muss einen passenden DataRef bekommen. Trockenlauf mit Diff zwingend.
- **`input.form` bleibt Sonderfall „graphdefined Schema"** — nicht RuntimeDynamik, sondern „Schema lebt in `node.parameters.fields` und ist nach dem Speichern fix". Frontend & Backend lesen es deterministisch über eine gemeinsame HelperFunktion.
### Bekannte Fallstricke
- AI Agent ToolGeneration: heute baut die KI Workflows oft mit leeren `connectionReference`Feldern und vertraut auf den WireHandover. Nach dem Cut funktioniert das nicht mehr — Prompt + ToolSchemas müssen so geschärft werden, dass die KI explizite DataRefs erzeugt.
- Pause/ResumePfad in `executionEngine`: `initialNodeOutputs` (ResumeUserEingabe) muss weiterhin gegen OutputSchema validiert werden — passt nahtlos zum harten ValidationModus.
- `flow.merge`, `flow.switch`, `flow.ifElse` — TransitEnvelope bleibt; aber `Transit` darf kein FreePass für TypeCompat sein (Picker folgt TransitChain, Validation prüft den realen Producer am Ende der Chain).
- `attachmentBuilder` FrontendType — heute kein Renderer. Im Zuge des Cleanups Alias auf `JsonEditor` (oder echten Builder, falls Pilot es noch in dieser Iteration braucht).
## Ziel und Nicht-Ziele
**Ziele:**
- Generische, deterministische HandoverLogik zwischen allen Nodes basierend auf typisierten DataRefs.
- Reichhaltige rekursive OutputSchemas mit `itemSchema` für Listen und ProvenienzFeldern (SharePointSource, ConnectionRef) wo der Konsumer sie picken können soll.
- DataPicker mit lexikalischem LoopScoping, TypeCompatAnzeige (grün/gelb/rot), rekursiver SchemaNavigation.
- Hard Validation: kein Run bei fehlenden Bindings oder TypeMismatch.
- AutoSuggest beim WireConnect (UXKomfort, nicht RuntimeMagie) für deterministische Defaults nach Name+TypeMatch.
- AIToolSurface: Endpoint, der für eine Node die strukturierte Liste aller pickbaren UpstreamPfade liefert.
- Migration aller existierenden Workflows in DB `poweron_graphicaleditor` ohne manuelle Nacharbeit.
- `input.form` als graphdefinedSchema sauber unterstützt; Picker downstream zeigt Userdefinierte Felder mit korrekten Typen.
**Explizit NICHT:**
- Keine Runtimedynamischen Schemas. `ai.extractStructured` wird **nicht** eingeführt; `data.transform` wird **entfernt**.
- Kein SelfService NodeTypeGenerator (Usereditierbare NodeTypen) in dieser Iteration.
- Keine semantische TypeRefinement (SubtypePolymorphie über strukturelles Matching hinaus). Bleibt auf strukturellem Match.
- Keine BackwardsCompat: alte Workflows werden migriert, der `_wireHandover`Code wird gelöscht, kein Fallback.
- Keine Änderung am Scheduler / PauseResumeDatenmodell jenseits ValidationHook.
- Keine Änderung an ActionLibrary (`workflows/methods/`) — Adapter dort bekommen nur erweiterte Outputs, keine neue Aufrufkonvention.
## Betroffene Module
**Gateway (`gateway/modules/`):**
- `features/graphicalEditor/portTypes.py``PortField` erweitern (`itemSchema`, `fields`, `enumValues`); `PORT_TYPE_CATALOG` erweitern um `Document`, `FileItem`, `EmailItem`, `TaskItem`, `ConnectionRef`, `SharePointFolderRef`, `SharePointFileRef`; `INPUT_EXTRACTORS` **entfernen**.
- `features/graphicalEditor/nodeDefinitions/*.py` — alle Outputs anreichern um Provenienz (`source`, `connection`) wo sinnvoll; `outputPorts.dynamic`/`deriveFrom` Marker auf `input.form` beschränken und in `{kind: "fromGraph", parameter: "fields"}`Form normalisieren.
- `features/graphicalEditor/nodeRegistry.py` + `routeFeatureGraphicalEditor.py` — neue Endpoints: `GET /{instanceId}/upstream-paths/{nodeId}`, `POST /{instanceId}/validate-graph`.
- `workflows/automation2/executors/actionNodeExecutor.py``_wireHandover` **entfernen**; `_resolveConnectionParam` bleibt für resolver, wird aber nur noch nach ParameterResolution gerufen.
- `workflows/automation2/executors/dataExecutor.py``data.transform` **entfernen**, `data.aggregate` und `data.filter` mit reichhaltigen OutputSchemas.
- `workflows/automation2/executionEngine.py` — ValidationHook vor RunStart; LoopBodyScope an `executeGraph` propagieren.
- `workflows/automation2/graphUtils.py``validateGraph`: hart blockierend; `resolveParameterReferences`: graphdefinedSchemaPfad ergänzen.
- `workflows/methods/sharepoint/`, `methods/outlook/`, `methods/clickup/`, `methods/trustee/`, `methods/file/` — AdapterOutputs anreichern (ConnectionRef, SourcePfad).
- MigrationsSkript `scripts/migration/2026-04-pick-not-push-migration.py` (neu) — scannt `AutoWorkflow.graph`, ergänzt explizite DataRefs anstelle implizitem WireHandover.
- TestPakete:
- `gateway/tests/unit/graphicalEditor/test_port_schema_recursive.py`
- `gateway/tests/unit/graphicalEditor/test_validate_typed_hard.py`
- `gateway/tests/integration/graphicalEditor/test_upstream_paths_api.py`
- `gateway/tests/integration/graphicalEditor/test_pwg_pilot_typed.py`
- `gateway/tests/unit/migration/test_pick_not_push_migration.py`
- pro Adapter (sharepoint/outlook/clickup/trustee/file): SchemaComplianceTest
**Frontend (`frontend_nyla/src/`):**
- `api/workflowApi.ts` — Interfaces: `PortSchema` rekursiv (`fields`, `itemSchema`); `DataRef.expectedType`; `NodeType.outputPorts.schema` darf `string | {kind:"fromGraph", parameter:string}` sein.
- `components/FlowEditor/nodes/shared/dataRef.ts``DataRef` um `expectedType` erweitern; Helper `isCompatible(produced, accepted)`.
- `components/FlowEditor/nodes/shared/DataPicker.tsx` — Scope: BFS rückwärts via Connections + transitive LoopBodyHierarchie; rekursive SchemaNavigation (verschachtelte fields, list `itemSchema` unwrap); `loop.item`/`loop.index`/`loop.total` pro umgebendem Loop; TypeCompatPills.
- `components/FlowEditor/nodes/shared/scopeHelpers.ts` (neu) — `computePickableScope(nodeId, graph, nodeTypes)`, `unwrapListItemSchema()`, `resolveTransitChain()`.
- `components/FlowEditor/context/Automation2DataFlowContext.tsx``parseGraphDefinedSchema(node, paramKey)` für `input.form`; cached pro Node.
- `components/FlowEditor/editor/NodeConfigPanel.tsx` — DataRefPills mit TypeStatus; fehlt RequiredBinding → Visualisierung; nichtbindbare ConnectionPickers (wenn upstream `ConnectionRef` verfügbar) als HybridPicker (StaticUserConnection ODER DataRef).
- `components/FlowEditor/nodes/frontendTypeRenderers/index.tsx``attachmentBuilder` als Alias auf `JsonEditor` (übergangsweise); `ConnectionPicker` 3Modus (Link bei 0 Connections, AutoDisplay bei 1, Dropdown bei ≥2) — aus dem überholten VorgängerPlan übernommen.
- `components/FlowEditor/editor/FlowCanvas.tsx` — AutoSuggest Modal beim WireConnect; WireFarbe nach TypeCompat (grün/gelb/rot); ValidationIndicator pro Node.
- `components/FlowEditor/editor/Automation2FlowEditor.tsx` — RunButton disabled bei ValidationFehler; ValidationPanel sichtbar mit KlickZuNodeNavigation.
**DBMigration:** Nein im SchemaSinn (kein `AutoWorkflow`TableChange). **DatenMigration ja:** `AutoWorkflow.graph`JSON wird einmalig durch MigrationsSkript transformiert.
**Andere Komponenten:**
- AI Agent (`gateway/modules/serviceCenter/services/serviceAgent/`): `workflowTools` ToolSchemas erweitern, sodass `bindNodeParameter`Tool die KI zur expliziten DataRefErzeugung führt; Prompts für WorkflowGeneration aktualisieren.
## Entscheidungen
| Datum | Entscheidung | Begründung |
|-------|-------------|------------|
| 20260423 | PicknotPush als Grundprinzip — `INPUT_EXTRACTORS` und `_wireHandover` entfernen | Heuristisches Push erzeugt nichtdeterministisches, AIundurchsichtiges Verhalten |
| 20260423 | StaticOnly Schemas: CodeKatalog ODER graphdefined (nur `input.form`) | Keine RuntimeSchemaUnsicherheit; deterministische Picker/AISicht |
| 20260423 | Kein `ai.extractStructured` | AI kann SchemaCompliance nicht garantieren; UDM (`trustee.extractFromFiles`) + `trustee.queryData` decken strukturierte Extraktion ab |
| 20260423 | `data.transform` ersatzlos entfernen | Funktional redundant zu DataRefs am Konsumer |
| 20260423 | Connection als Hybrid: SourcePicker + ConnectionRefOutput + DownstreamDataRef | Konsistente PickerUX, keine Secrets in Datenflüssen |
| 20260423 | LoopScoping lexikalisch + `loop.item`/`loop.index` zusätzlich | Einfacher als MagicEnvelope, native an DatenflussTopologie |
| 20260423 | Hard Validation (Mismatch blockt Run) | Keine späten RuntimeÜberraschungen, klare Fehler |
| 20260423 | Greenfield + MigrationsSkript, kein Fallback | Eine Wahrheit, CodePfade einfach |
| 20260423 | `attachmentBuilder` als Alias auf `JsonEditor` (übergangsweise) | Pilot benötigt funktionierenden Renderer; richtiger Builder später |
| 20260423 | `ConnectionPicker` 3Modus (Link/Auto/Dropdown) bei SourceNodes | UX aus überholtem Plan übernommen, da pragmatisch |
## Umsetzungs-Checkliste
### Phase 0 — PlanHygiene & Audit (markdown only)
- [ ] VorgängerPlan `wiki/c-work/1-plan/2026-04-graph-editor-node-config-fixes.md` als obsolet markieren (`<!-- status: obsolete -->`) und mit ForwardLink auf diesen Plan versehen, dann nach `wiki/z-archive/` verschieben.
- [ ] Anhang A „SchemaAudit" am Ende dieses Plans pflegen (siehe unten): pro existierender Node SollSchema (rekursiv, mit Provenienz wo relevant).
### Phase 1 — SchemaErweiterung (Backend)
- [ ] `portTypes.py`: `PortField` erweitern um optional `fields: List[PortField]` (verschachtelt) und `itemSchema: PortField` (für Listen) und `enumValues: List[str]`.
- [ ] `PORT_TYPE_CATALOG`: neue Entries `Document`, `FileItem`, `EmailItem`, `TaskItem`, `ConnectionRef { id, authority, label }`, `SharePointFolderRef { siteUrl, driveId, folderPath, label }`, `SharePointFileRef { …, fileName }`, `UdmPage`, `UdmBlock` (rekursive UDMStrukturen).
- [ ] `DocumentList`, `FileList`, `EmailList`, `TaskList`: jeweils `itemSchema` referenziert auf den passenden ItemTyp.
- [ ] Pro ProducerOutput: Provenienz ergänzen (`source: SharePointFolderRef`, `connection: ConnectionRef` für `sharepoint.*`; analog für `email.*`, `clickup.*`, `trustee.*`).
- [ ] `_normalizeToSchema` strict modus: fehlende required Felder → `RuntimeError` mit klarem SchemaComplianceHinweis.
- [ ] `INPUT_EXTRACTORS` aus `portTypes.py` **entfernen** (alle Funktionen + Dict).
### Phase 2 — AdapterAnpassung (Backend, Action Library)
- [ ] `methods/sharepoint/` Aktionen `findDocumentPath`, `readDocuments`, `listDocuments`, `downloadFileByPath`, `uploadFile`, `copyFile`: OutputBuilder erweitern um `source`, `connection`.
- [ ] `methods/outlook/` analog: `connection` in jedem Output.
- [ ] `methods/clickup/` analog.
- [ ] `methods/trustee/` (`extractFromFiles`, `processDocuments`): UDMOutput sauber rekursiv typisieren.
- [ ] `methods/file/create`: OutputSchema auf `Document` mappen.
- [ ] Pro Adapter ein ComplianceTest (`gateway/tests/integration/methods/test_<method>_schema_compliance.py`): mock + reale MiniOutputs gegen SollSchema validiert.
### Phase 3 — LoopScoping & DataPicker (Frontend)
- [ ] `nodes/shared/scopeHelpers.ts` (neu): `computePickableScope(nodeId, graph, nodeTypes)` — BFS rückwärts via `connections` + transitive LoopBodyHierarchie; `unwrapListItemSchema(producerSchema, fieldPath)` für LoopItemTyp; `resolveTransitChain(nodeId, graph, nodeTypes)` für TransitAuflösung.
- [ ] `DataPicker.tsx`:
- rekursive SchemaNavigation (verschachtelte `fields`, list `itemSchema` automatisch entpackt unter LoopBody).
- pro umgebendem Loop zusätzlich Sektion „Loop ($loopNodeLabel)" mit `loop.item`, `loop.index`, `loop.total`.
- TypePills pro Pick: grün (identisch/kompatibel), gelb (saubere Coercion), rot (Mismatch).
- SystemSektion bleibt.
- [ ] `dataRef.ts`: `DataRef` um `expectedType: string` erweitern; Helper `isCompatible(produced, accepted): "ok" | "coerce" | "mismatch"`.
- [ ] FrontendUnitTests für `scopeHelpers` und PickerRenderlogik.
### Phase 4 — PicknotPush: ExecutorCleanup (Backend)
- [ ] `actionNodeExecutor.py`: `_wireHandover` und alle Aufrufer **entfernen**.
- [ ] `resolveParameterReferences` in `graphUtils.py`: nur noch `DataRef` / `SystemVar` / `Static` / graphdefined; Helper `parseGraphDefinedSchema(node, paramKey) -> PortSchema`.
- [ ] `validateGraph` in `graphUtils.py`: hart blockierend bei (a) fehlender RequiredBindung, (b) TypeMismatch (mit `isCompatible`Logik vom Frontend angeglichen — gemeinsame TypeScript/Python Spec dokumentiert).
- [ ] `executionEngine.executeGraph`: vor Schleifenstart `validateGraph`Aufruf; bei Fehler `RunValidationError` mit Liste pro betroffenem Node/Param.
- [ ] `_resolveConnectionParam` bleibt, wird nach `resolveParameterReferences` gerufen (DataRef → ConnectionRefDict → resolver liest `id`).
### Phase 5 — AutoSuggest UX beim Wire (Frontend)
- [ ] `FlowCanvas.tsx`: bei WireConnect (`onConnectionsChange`) die downstreamNodeInputs gegen den neuen upstreamOutput prüfen.
- [ ] VorschlagsLogik: Match nach (a) ParameterName = SchemaFieldName + TypeCompat ok, (b) TypeCompat ok mit eindeutigem Field. → DataRefVorschläge.
- [ ] Modal/Popover „Vorgeschlagene Bindings übernehmen?" mit ApplyAll / PerItemAkzept.
- [ ] Bei genau einer eindeutigen Bindung pro Param: Direkt vorausgefüllt mit UndoToast.
- [ ] ConnectionPicker in DownstreamNodes wird zum **HybridPicker**: zeigt StaticUserConnection (Picker) ODER DataRef (auf `ConnectionRef`); bei aktivem upstream`ConnectionRef` wird DataRef vorgeschlagen.
### Phase 6 — `ConnectionPicker` 3Modus + RendererAudit (Frontend)
- [ ] `frontendTypeRenderers/index.tsx` `ConnectionPicker`: 3 Modi je nach Anzahl verfügbarer Connections für Authority — 0: Link auf UserConnectionsSeite, 1: AutoDisplay ohne Dropdown, ≥2: Dropdown.
- [ ] `attachmentBuilder` Alias auf `JsonEditor` einführen + ConsoleWarnung im Fallthrough für unbekannte FrontendTypes.
- [ ] FrontendTypeAudit gegen alle `nodeDefinitions/*.py`: jede dort verwendete `frontendType` muss in `FRONTEND_TYPE_RENDERERS` einen Renderer haben (oder klar kommentiert TODO).
### Phase 7 — AI Tool Surface (Backend + Agent)
- [ ] `routeFeatureGraphicalEditor.py`: `GET /{instanceId}/upstream-paths/{nodeId}` → Liste `{producerNodeId, producerLabel, path, type, label, scopeOrigin: "data"|"loop"|"system"}`.
- [ ] `nodeRegistry.py`: Response `nodeTypes` liefert vollständig rekursive `inputPorts.accepts` und `outputPorts.schema` (verschachtelt).
- [ ] `serviceCenter/services/serviceAgent/workflowTools.py`: neues Tool `bindNodeParameter(workflowId, nodeId, paramName, dataRef|staticValue|systemVar)` mit Serverseitiger Validation gegen TypeCompat.
- [ ] PromptUpdate für WorkflowGeneration: KI generiert immer explizite DataRefs; nutzt `upstream-paths` zur Discovery.
### Phase 8 — `input.form` als graphdefined Schema
- [ ] `nodeDefinitions/input.py` `input.form.outputPorts`: ersetze `{schema: "FormPayload", dynamic: True, deriveFrom: "fields"}` durch `{schema: {kind: "fromGraph", parameter: "fields"}}`.
- [ ] `parseGraphDefinedSchema(node, "fields")` in `graphUtils.py` (Backend) und Spiegel in `Automation2DataFlowContext.tsx` (Frontend) — gemeinsame Spec, getrennte Implementierung.
- [ ] FieldBuilder im Frontend (`fieldBuilder` Renderer): erlaubt verschachtelte fields (object), Typed Items für Listen, TypePicker (`str/number/bool/date/datetime/object/list`).
- [ ] Picker downstream zeigt `formNode.payload.<userField>` mit korrektem Type.
### Phase 9 — Migration bestehender Workflows
- [ ] `scripts/migration/2026-04-pick-not-push-migration.py`:
- lädt alle `AutoWorkflow.graph` aus `poweron_graphicaleditor`;
- simuliert die alte `_wireHandover`Logik deterministisch;
- schreibt explizite DataRefs in den Graph;
- TrockenlaufModus mit DiffOutput;
- LiveModus mit BackupSnapshot der DB vor Schreiben.
- [ ] UnitTests für die MigrationsFunktion mit BeispielGraphen (inkl. PWGPilot).
- [ ] IntegrationTest: PWGPilot Workflow läuft nach Migration ohne erneutes ConnectionPrompting; n4/n10 nutzen DataRef auf n2.connection.
- [ ] `data.transform` Nodes in alten Workflows: MigrationsSkript gibt Warnung pro Vorkommen aus (manuell zu refactoren — kein AutoCleanup, weil semantisch).
### Phase 10 — Dokumentation & Abschluss
- [ ] `wiki/b-reference/gateway/automation.md`: Abschnitt „PicknotPush Handover" + Removed `INPUT_EXTRACTORS`.
- [ ] `wiki/b-reference/gateway/workflow.md`: Abschnitt UDM/Loop/KIBadge ergänzen um neue SchemaTiefe und LoopScoping.
- [ ] `wiki/b-reference/frontend-nyla/architecture.md`: Abschnitt FlowEditor mit TypeCompat, AutoSuggest, HybridConnectionPicker.
- [ ] `wiki/TOPICS.md`: Eintrag „Typed Generic Handover (build/done)" verlinken.
- [ ] PlanDoc Status updaten und nach `c-work/2-build/``c-work/3-validate/``c-work/4-done/` durchschieben.
## Akzeptanzkriterien
| # | Kriterium (GivenWhenThen) | Prio |
|---|---------------------------|------|
| 1 | Given PWGPilot Workflow nach Migration, When ausgeführt, Then `n4 sharepoint.downloadFile` und `n10 email.draftEmail` erhalten ihre msftConnection per DataRef auf `n2.connection` ohne erneutes UserPrompt; Run läuft erfolgreich durch | must |
| 2 | Given Workflow mit TypeMismatch (z.B. `DocumentList``int` Param), When `validateGraph` aufgerufen, Then ValidationError mit Path/ParamListe; When `executeGraph` aufgerufen, Then sofortiger `RunValidationError` ohne AdapterAufruf | must |
| 3 | Given Workflow mit `flow.foreach` über `nodeX.documents`, When im BodyNode DataPicker geöffnet, Then Sektion „Loop (foreachLabel)" zeigt `loop.item: Document`, `loop.index: int`, `loop.total: int`; outer scope inkl. `nodeX.connection`, `nodeX.source` weiterhin pickbar | must |
| 4 | Given `input.form` mit Userdefinierten Feldern `{mieter: str, betrag: number}`, When downstream DataPicker geöffnet, Then `formNode.payload.mieter` (str) und `formNode.payload.betrag` (number) sind separat pickbar mit korrekten TypPills | must |
| 5 | Given AI Agent generiert neuen Workflow, When `GET /{instanceId}/upstream-paths/{nodeId}` aufgerufen, Then strukturierte PfadListe mit `producerNodeId`, `path`, `type`, `label`, `scopeOrigin` zurück; KI generiert valide DataRefs ohne leere `connectionReference`Felder | must |
| 6 | Given MigrationsSkript Trockenlauf auf bestehenden Workflows, When ausgeführt, Then für jede Node mit implizitem WireHandover wird ein expliziter DataRefVorschlag im Diff angezeigt; When LiveLauf, Then alle Workflows passieren `validateGraph` ohne Fehler | must |
| 7 | Given DataPickerBindung mit perfekt passendem Typ, When User pickt, Then grünes Pill; bei sauberer Coercion (`int → str`) gelb mit Hinweis; bei Mismatch (`DocumentList → int`) rot und SaveButton blockiert | should |
| 8 | Given Wire zwischen zwei Nodes neu gezogen, When AutoSuggest aktiv, Then Modal listet alle nach Name+Type matchenden Bindings; bei eindeutigem Match wird vorausgefüllt mit UndoToast | should |
| 9 | Given `ConnectionPicker` für Authority `msft` und User hat 0 Connections, Then Link „Connection erstellen" statt Dropdown; bei 1 Connection AutoDisplay; bei ≥2 Dropdown | should |
| 10 | Given SourceAdapter `methods/sharepoint/listDocuments`, When ausgeführt, Then Output enthält `connection: ConnectionRef`, `source: SharePointFolderRef`, `documents: List[Document]` mit voll typisierten DocumentItems | must |
| 11 | Given `data.transform` Node in altem Workflow, When MigrationsSkript läuft, Then Warning im Diff (kein AutoRemoval); After CleanupPass: `data.transform` aus `nodeRegistry` entfernt | should |
## Testplan
| ID | AC | Art | Automatisiert | RepoPfad | Status |
|----|----|-----|--------------|-----------|--------|
| T1 | 1 | integration | ja | `gateway/tests/integration/graphicalEditor/test_pwg_pilot_typed.py` | pending |
| T2 | 2 | unit | ja | `gateway/tests/unit/graphicalEditor/test_validate_typed_hard.py` | pending |
| T3 | 3 | frontend unit | ja | `frontend_nyla/src/components/FlowEditor/__tests__/dataPicker.scope.test.ts` | pending |
| T4 | 4 | frontend unit + backend unit | ja | `frontend_nyla/.../__tests__/dataPicker.formSchema.test.ts` + `gateway/tests/unit/graphicalEditor/test_parse_graph_defined_schema.py` | pending |
| T5 | 5 | api | ja | `gateway/tests/integration/graphicalEditor/test_upstream_paths_api.py` | pending |
| T6 | 6 | migration | ja | `gateway/tests/unit/migration/test_pick_not_push_migration.py` | pending |
| T7 | 7 | frontend unit | ja | `frontend_nyla/.../__tests__/dataPicker.typeCompat.test.ts` | pending |
| T8 | 8 | frontend e2e | ja (Playwright opt) | `frontend_nyla/tests/e2e/wireAutoSuggest.spec.ts` | pending |
| T9 | 9 | frontend unit | ja | `frontend_nyla/.../__tests__/connectionPicker.modes.test.ts` | pending |
| T10 | 10 | api/integration | ja | `gateway/tests/integration/methods/test_sharepoint_schema_compliance.py` (+ `_outlook`, `_clickup`, `_trustee`, `_file`) | pending |
| T11 | 11 | unit | ja | `gateway/tests/unit/migration/test_data_transform_warning.py` | pending |
## Links
- Vorgänger (done): `wiki/c-work/4-done/2026-04-generic-graph-editor.md`
- Vorgänger (obsolet, in Phase 0 zu archivieren): `wiki/c-work/1-plan/2026-04-graph-editor-node-config-fixes.md`
- PilotBezug: `wiki/c-work/4-done/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md`
- BeispielWorkflow für SmokeTest: `local/temp/pwg-pilot-jahresmietzinsbestätigung.workflow (1).json`
- PR: TBD
- Issue: TBD
## Abschluss
- [ ] `wiki/b-reference/gateway/automation.md` aktualisiert (PicknotPush, Removed Heuristik)
- [ ] `wiki/b-reference/gateway/workflow.md` aktualisiert (SchemaTiefe, LoopScoping)
- [ ] `wiki/b-reference/frontend-nyla/architecture.md` aktualisiert (FlowEditorErweiterungen)
- [ ] `wiki/TOPICS.md` aktualisiert (neuer Eintrag „Typed Generic Handover")
- [ ] Dieses Dokument → `wiki/z-archive/` verschoben
---
## Anhang A — SchemaAudit (SollStand pro NodeOutput)
> Wird in Phase 0 vollständig befüllt; hier die Zielform und der Anfang der Inhalte. Stand `PORT_TYPE_CATALOG` heute siehe `gateway/modules/features/graphicalEditor/portTypes.py:59170`.
### Neue / erweiterte ItemSchemas im Katalog
| Schema | Felder (Kurzform) | Anmerkung |
|--------|-------------------|-----------|
| `Document` | `id, name, mimeType, sizeBytes, downloadUrl, source: SharePointFileRef?` | Item für `DocumentList`; Provenienz optional je nach Producer |
| `FileItem` | `id, name, path, mimeType, sizeBytes, modifiedAt` | Item für `FileList` |
| `EmailItem` | `id, subject, from, to, receivedAt, hasAttachments, bodyPreview` | Item für `EmailList` |
| `TaskItem` | `id, title, status, assignee, dueDate, listId` | Item für `TaskList` |
| `ConnectionRef` | `id, authority, label` | **keine Secrets** — nur Identifikator |
| `SharePointFolderRef` | `siteUrl, driveId, folderPath, label` | Provenienz für SharePointListings |
| `SharePointFileRef` | `siteUrl, driveId, filePath, fileName, label` | Provenienz für einzelne SharePointFiles |
| `UdmPage` | `pageNumber, blocks: List[UdmBlock]` | UDMSeite |
| `UdmBlock` | `kind, text, children?: List[UdmBlock]` | UDMBlock (rekursiv) |
### Anreicherung bestehender OutputSchemas
| Schema | Heute | Soll |
|--------|-------|------|
| `DocumentList` | `documents: List[Document]` | `+ source: SharePointFolderRef? + connection: ConnectionRef? + count: int` |
| `FileList` | `files: List[File]` | `+ source: SharePointFolderRef? + connection: ConnectionRef? + count: int`; `itemSchema` = `FileItem` |
| `EmailList` | `emails: List[Email]` | `+ connection: ConnectionRef? + count: int`; `itemSchema` = `EmailItem` |
| `TaskList` | `tasks: List[Task]` | `+ connection: ConnectionRef? + listId? + count: int`; `itemSchema` = `TaskItem` |
| `EmailDraft` | `subject, body, to, cc?, attachments?` | `+ connection: ConnectionRef?` |
| `AiResult` | flat | bleibt, `responseData` weiterhin `Dict` (nicht typisiert — bewusst, weil AIAntwort) |
| `UdmDocument` | `id, sourceType, sourcePath, children: List[Any]` | `children: List[UdmPage]` (rekursiv typisiert) |
| `ActionResult` | `success, error?, data?` | bleibt; `data` ist `Dict` (catchall für nichttypisierte Adapter) |
| `Transit` | leer | bleibt — Picker folgt TransitChain |
### Pro NodeDefinition (Auszug — vollständig in Phase 0)
| Node | Output heute | Output Soll |
|------|--------------|-------------|
| `sharepoint.findFile` | `FileList` | `FileList` mit `source`, `connection` befüllt |
| `sharepoint.listFiles` | `FileList` | `FileList` mit `source`, `connection` befüllt |
| `sharepoint.downloadFile` | `DocumentList` | `DocumentList` mit `source`, `connection` befüllt |
| `sharepoint.readFile` | `DocumentList` | `DocumentList` mit `source`, `connection` befüllt |
| `email.draftEmail` | `EmailDraft` | `EmailDraft` mit `connection` befüllt |
| `email.checkEmail` | `EmailList` | `EmailList` mit `connection` befüllt |
| `clickup.searchTasks` | `TaskList` | `TaskList` mit `connection`, `listId` befüllt |
| `trustee.extractFromFiles` | `UdmDocument` | `UdmDocument` mit voll typisierten `children: List[UdmPage]` |
| `trustee.queryData` | `Dict` (heute) | typisiertes `QueryResult { rows: List[Dict], schema: List[ColumnSchema] }` (neuer Eintrag im Katalog) |
| `input.form` | `FormPayload` (`dynamic`) | `{kind:"fromGraph", parameter:"fields"}` — graphdefined |
| `data.transform` | (vorhanden) | **entfernt** |
| `data.aggregate` | `AggregateResult` | bleibt, `items: List[Any]` (generisch über LoopItemTyp) |
| `data.consolidate` / `ai.consolidate` | `ConsolidateResult` | bleibt |

View file

@ -0,0 +1,204 @@
<!-- status: obsolete -->
<!-- started: 2026-04-23 -->
<!-- obsoletedAt: 2026-04-23 -->
<!-- supersededBy: c-work/1-plan/2026-04-typed-generic-handover.md -->
<!-- component: gateway | frontend-nyla -->
<!-- relatedTo: c-work/4-done/2026-04-generic-graph-editor.md, c-work/4-done/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md -->
> **Obsolet — ersetzt durch [`c-work/1-plan/2026-04-typed-generic-handover.md`](../1-plan/2026-04-typed-generic-handover.md).**
>
> Dieser Plan behandelte die Symptome (Connection-Inheritance-Service, Wire-Source-Badge, attachmentBuilder-Alias) als Workarounds um den heuristischen `_wireHandover` herum. Die Diskussion vom 20260423 hat ergeben, dass das Modell selbst nicht trägt: der „Push"-Handover via `INPUT_EXTRACTORS` ist generisch nicht beherrschbar. Der Nachfolge-Plan ersetzt das durch ein **Pick-not-Push** Modell mit reichhaltigen statischen Schemas, harter Type-Validierung und expliziten DataRefs als einzigem Bindungs-Mechanismus. Drei Punkte aus diesem Plan (3-Modus `ConnectionPicker`, `attachmentBuilder` Renderer-Alias, FrontendType-Audit) wurden in den Nachfolger als Phase 6 übernommen.
# Graph-Editor: Connection-UX, Wire-Handover und Feld-Rendering bereinigen (obsolet)
## Beschreibung und Kontext
Der Graphical Editor (`graphicalEditor`-Feature) ist seit dem Generic-Graph-Editor-Refactor (`c-work/4-done/2026-04-generic-graph-editor.md`) auf typisierte Ports + Wire-Handover umgestellt. Beim Pilot-Workflow PWG Mietzinsbestaetigung (`local/temp/pwg-pilot-jahresmietzinsbestätigung.workflow (1).json`) zeigen sich vier konkrete Schwachstellen, die das User-Erlebnis im Editor brechen:
1. **Connection wird pro Node neu erfragt.** Im PWG-Workflow ist `n2.sharepoint.listFiles` mit `connection:msft:p.motsch@valueon.ch` konfiguriert. `n4.sharepoint.downloadFile` und `n10.email.draftEmail` haben dieselbe Authority (`msft`), aber `connectionReference: ""`. Der `_wireHandover` in `gateway/modules/workflows/automation2/executors/actionNodeExecutor.py` propagiert nur Port-Schema-Felder (`documents`, `emails`, ...) via `INPUT_EXTRACTORS` (`gateway/modules/features/graphicalEditor/portTypes.py`). `connectionReference` wird nirgends weitergereicht. Folge: jede Node fragt erneut nach Connection.
2. **`ConnectionPicker` immer als Dropdown.** `frontend_nyla/src/components/FlowEditor/nodes/frontendTypeRenderers/index.tsx` (`ConnectionPicker`, Z. 153198) rendert immer `<select>` und einen Hilfstext, wenn keine Connection da ist — aber **keinen Link** auf die User-Connections-Seite, und auch bei genau **einer** Connection bleibt es ein Dropdown statt einer Direktanzeige.
3. **Nicht alle Felder rendern sauber.** `email.draftEmail.attachments` deklariert `frontendType: attachmentBuilder` (`gateway/modules/features/graphicalEditor/nodeDefinitions/email.py:88`). In `FRONTEND_TYPE_RENDERERS` (`frontendTypeRenderers/index.tsx:608633`) gibt es **keinen** `attachmentBuilder`-Renderer → Fallback auf `TextInput`, das den JSON-Array stringifiziert anzeigt. `clickupList` / `clickupTask` mappen auf einen generischen `FolderPicker` (Plain-Textfeld), nicht auf einen echten ClickUp-Picker.
4. **Handovers zwischen Nodes sind nicht klar.** In der Property-Panel-Ansicht (`NodeConfigPanel.tsx`) ist die "Datenfluss"-Sektion in `<details>` versteckt und zeigt nur abstrakte Port-Schemas (`DocumentList`, `AiResult`). Es ist für den User unsichtbar, **welcher Parameter** automatisch via Wire befüllt wird, **aus welchem Upstream-Node** und mit welchem Feld. Dadurch entsteht der Eindruck, der User müsse alles selbst eingeben.
**Business-Treiber:** PWG-Pilot startet im Sommer 2026. Editor-UX muss verständlich sein, sonst kostet die Demo-Vorführung beim Kunden Vertrauen. Auch jeder weitere Workflow-Bauer (intern + Customer Onboarding) profitiert sofort.
**Risiko bei Nicht-Umsetzung:** PWG-Pilot-User (und Demo-Publikum) erlebt den Editor als „alles muss ich von Hand eintragen" und „Das System weiss nicht, dass ich nur einen Outlook habe". Schwer zu verkaufen.
**Abhaengigkeiten:** keine externen. Greift in bereits ausgelieferte (done) Komponenten ein:
- `c-work/4-done/2026-04-generic-graph-editor.md` (Typed-Port-System, Wire-Handover-Mechanik)
- `c-work/4-done/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md` (Pilot-Workflow + `email.draftEmail.attachments`)
## Fokus und kritische Details
- **Connection-Inheritance ist Greenfield-Erweiterung der Wire-Handover-Mechanik.** Der bestehende `_wireHandover` in `actionNodeExecutor.py` arbeitet auf der **direkten Vorgaenger-Node** und nur fuer Schema-Felder. Connection-Inheritance braucht einen anderen Algorithmus: **transitive Suche nach Authority** durch den Upstream-Pfad — aktuell verwendet noch keiner so etwas. Fragil: wenn mehrere Upstream-Pfade unterschiedliche Authorities ihrer User-Connection nutzen (z.B. `flow.merge` mit Outlook + ClickUp), darf nichts blind uebernommen werden.
- **Frontend- und Backend-Inheritance muessen konsistent sein.** Wenn das Frontend per Default einen Wert fuer `connectionReference` setzt (Auto-Fill), wird er beim Save persistiert und ist genauso explizit wie eine User-Wahl. Backend-Fallback ("wenn leer, suche Upstream") ist die zweite Verteidigungslinie fuer alte/manuell editierte Workflows. **Beides bauen, aber Frontend ist Hauptmechanismus** — sonst sieht der User auf dem Canvas nicht, dass die Node "weiss, was sie tun wird".
- **`ConnectionPicker` braucht 3 Modi.** Code muss klar zwischen den Faellen unterscheiden — kein Dropdown wenn 0 oder 1 Connection. Bei 0 Connections: Link auf `/profile/connections` (siehe `ConnectionsPage.tsx`) mit Authority-Vorausfilter und Rueckkehr-URL.
- **`attachmentBuilder` muss entweder echt gebaut oder zu `json` redirected werden.** Im Pilot-Plan (`c-work/4-done/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md`, Sub-Task 4b) wurde explizit "NICHT NÖTIG" entschieden — aber dann muss der Fallback wenigstens **`JsonEditor`** sein (kompakter Editor mit Syntax-Hint), nicht ein 1-zeiliges Text-Field. Pragmatisch: Alias `attachmentBuilder → json` registrieren, damit kein zweiter Renderer noetig ist.
- **`clickupList` / `clickupTask` Stubs** koennen separat gefixt werden (eigene Iteration), sind kein Blocker fuer den PWG-Pilot. Hier nur als bekannt-Stub markieren und auf `text` mit Hinweis "ClickUp-Picker tbd" mappen.
- **Datenfluss-Sichtbarkeit muss inline pro Parameter rendern**, nicht in einem versteckten `<details>`-Block. Pro Parameter, der per `INPUT_EXTRACTORS` aus dem Upstream-Output befuellbar ist, ein Badge `🔗 Wire: {srcNode} → {schemaField}`. Pro `connectionReference` mit gefundenem Upstream-Match: Badge `🔗 uebernommen aus {srcNode}`.
- **Keine Node-Type-spezifische Logik im Frontend.** Alle Inheritance-Regeln per Daten gesteuert: Backend liefert pro Parameter eine Liste `wireSources: [{nodeId, fieldPath, schema}]`, das Frontend rendert generisch. Sonst wird der Renderer wieder zu einer Sammlung von Spezialfaellen.
## Ziel und Nicht-Ziele
**Ziel:**
- Beim Anlegen einer neuen Node mit `connectionReference`-Parameter wird die Connection **automatisch vorbelegt**, wenn entweder (a) der User genau **eine** Connection mit passender `authority` hat oder (b) ein Upstream-Pfad bereits eine `connectionReference` derselben `authority` verwendet.
- `ConnectionPicker` zeigt 3 Zustaende:
- **0 Connections (passende Authority):** Link "Verbindung anlegen" -> `/profile/connections?authority=msft&returnTo=...`.
- **1 Connection:** Direktanzeige `[msft] p.motsch@valueon.ch` mit kleinem "Aendern"-Link, der das Dropdown sichtbar macht.
- **>1 Connections:** Dropdown wie heute.
- Backend-Fallback: ist beim Run `connectionReference` leer und es gibt **eindeutig** einen Upstream-Wert mit derselben Authority, wird er verwendet (und im `AutoStepLog.inputSnapshot` markiert: `"connectionReference_inferredFrom": "n2"`).
- `attachmentBuilder`-Felder rendern als `JsonEditor` (Alias-Mapping), bis ein dedizierter Builder gebaut wird — nicht mehr als 1-zeiliges Text-Field.
- Im Property-Panel sieht der User pro Parameter, ob er via Wire befuellt wird (Badge mit Quell-Node + Feldname). `connectionReference`-Parameter zeigen "uebernommen aus {nodeId}" wenn auto-inferred.
- API liefert pro Node-Inspector-Aufruf zusaetzlich eine `wireSources`-Map: pro Parameter-Name eine Liste der moeglichen/aktiven Wire-Quellen (basierend auf Graph + `INPUT_EXTRACTORS`).
**Explizit NICHT:**
- Keine Aenderung am Wire-Handover-Algorithmus fuer Schema-Felder (`documents`, `emails`, ...). Der bleibt wie er ist.
- Keine neuer Picker fuer `clickupList`/`clickupTask`. Diese bleiben als „bekannte Luecken" markiert; eigene Iteration.
- Kein dedizierter `AttachmentBuilder`-Renderer. Alias auf `JsonEditor` reicht. Wird in einer Folgeiteration nachgereicht, wenn Mehrnode-Cases das verlangen.
- Keine Migration alter Workflows. Wer leere `connectionReference` hat, profitiert ab sofort vom Backend-Fallback; wer eine fixierte (auch falsche) hat, behaelt sie.
- Keine Aenderung an der RBAC-/Permission-Schicht. User sieht nur Connections, die er ohnehin sehen darf (`getUserConnections(userId)` ist bereits user-scoped).
- Keine Persistenz der Inferenz-Entscheidung in der Workflow-DB. Inferenz ist immer Run-Time-Heuristik (Frontend setzt explizit, Backend faellt nur defensiv zurueck).
## Betroffene Module
- **Gateway:**
- `gateway/modules/workflows/automation2/executors/actionNodeExecutor.py` — neuer Helper `_inferConnectionFromUpstream(nodeDef, inputSources, nodeOutputs, allNodes, params)`, vor `_resolveConnectionParam` aufgerufen.
- `gateway/modules/features/graphicalEditor/routeFeatureGraphicalEditor.py``node-types`-Endpoint bzw. ein neuer `wire-sources`-Endpoint (oder Integration in `GET /{instanceId}/workflows/{workflowId}/wire-sources?nodeId=...`) liefert pro Node-Parameter die moeglichen Wire-Quellen.
- `gateway/modules/features/graphicalEditor/wireSourcesService.py` (NEU, klein) — Pure-Helper `computeWireSources(graph, nodeId)` der pro Parameter listet, woher Wire/Connection-Inheritance kommen koennten.
- **Frontend:**
- `frontend_nyla/src/components/FlowEditor/nodes/frontendTypeRenderers/index.tsx``ConnectionPicker` ueberarbeiten (3 Modi), `attachmentBuilder`-Alias auf `JsonEditor`, `clickupList`/`clickupTask` mit Hint-Text.
- `frontend_nyla/src/components/FlowEditor/editor/NodeConfigPanel.tsx` — pro Parameter Wire-Source-Badge inline rendern; "Datenfluss"-Sektion umbauen oder entfernen, wenn alles inline gezeigt wird.
- `frontend_nyla/src/components/FlowEditor/editor/Automation2FlowEditor.tsx` (oder dort, wo eine neue Node erzeugt wird) — Frontend-Auto-Fill: bei Add-Node mit `connectionReference`-Param + 1 passende Connection → vorbelegen; oder Upstream-Pfad scannen und passende `connectionReference` uebernehmen.
- `frontend_nyla/src/api/workflowApi.ts` — neue Wrapper fuer `wire-sources`-Endpoint.
- **DB-Migration:** **nein**.
- **Andere:** `wiki/b-reference/gateway/automation.md` Ergaenzung "Connection-Inheritance" und "Wire-Sources-API" am Ende der Iteration (siehe Abschluss).
## Entscheidungen
| Datum | Entscheidung | Begruendung |
|-------|-------------|-------------|
| 2026-04-23 | Connection-Inheritance: Frontend Auto-Fill (primaer) + Backend Run-Time Fallback (defensiv) | UI muss zeigen "ich weiss, welche Connection" — nur Backend-Fallback waere unsichtbar; nur Frontend laesst alte/importierte Workflows leer |
| 2026-04-23 | Authority-Match-Regel: gleiche `frontendOptions.authority` UND gleiche Connection im Upstream-Pfad UND eindeutig (kein Konflikt) | Konservativ: lieber unausgefuellt lassen als falsche Connection einsetzen |
| 2026-04-23 | `ConnectionPicker`: 0 Connections → Link auf `/profile/connections?authority=...` | Einheitlich mit dem bestehenden `ConnectionsPage.tsx`; Filter fuer den schnellen Anlege-Pfad |
| 2026-04-23 | `attachmentBuilder``JsonEditor` als Alias (nicht eigener Renderer) | Pilot-Plan hat Builder explizit gestrichen; Alias verhindert kaputten 1-zeiligen Text-Fallback |
| 2026-04-23 | `clickupList`/`clickupTask` bleiben Stubs — Hint-Text "ClickUp-Picker tbd" | Nicht im Scope dieser Iteration; eigene Folge-Iteration |
| 2026-04-23 | Wire-Source-Anzeige inline pro Parameter, nicht in `<details>` | Sichtbarkeit ist der Hauptpunkt des Issues — alles, was hinter Click verborgen ist, gilt nicht als geloest |
| 2026-04-23 | `wireSources` als Server-berechnetes Datum (kein Frontend-Re-Implement der Extractor-Logik) | Single Source of Truth; vermeidet Drift zwischen FE/BE Extractor-Mappings |
## Umsetzungs-Checkliste
### Phase 1 — Connection-Inheritance Backend
- [ ] `gateway/modules/features/graphicalEditor/wireSourcesService.py` (NEU): Pure-Helper, der fuer eine Node alle Upstream-Nodes (transitiv via `connections`) liefert. Pro Connection-Authority-Konflikt → kein Vorschlag (`None`); sonst die einzige passende `connectionReference`.
- [ ] `gateway/modules/workflows/automation2/executors/actionNodeExecutor.py`: neuer Helper `_inferConnectionFromUpstream(nodeDef, context, params)` ruft den oben gebauten Service. Wird vor `_resolveConnectionParam` aufgerufen, nur wenn `connectionReference` leer und der Parameter `authority` gesetzt ist.
- [ ] In `AutoStepLog.inputSnapshot` zusaetzliches Feld `connectionReference_inferredFrom: <nodeId>` schreiben, wenn der Fallback griff. Nutzt das bestehende Step-Logging (`workflows/automation2/executionEngine.py`).
- [ ] Unit-Test `gateway/tests/unit/graphicalEditor/test_wireSourcesService.py`:
- Linear: 3 Nodes, n1 hat msft-Connection, n3 erbt.
- Konflikt: n1 hat msft-User-A, n2 hat msft-User-B (per merge-Pfad), n3 darf nichts erben.
- Keine Authority im Param → kein Vorschlag.
- Loop-Body-Node erbt von Loop-Header-Vorgaenger (durchgereicht).
### Phase 2 — Wire-Sources API
- [ ] `gateway/modules/features/graphicalEditor/routeFeatureGraphicalEditor.py`: neuer Endpoint `POST /{instanceId}/wire-sources` mit Body `{ graph: { nodes, connections }, nodeId }` → Response `{ parameters: { <paramName>: [{ srcNodeId, srcField, schema, kind: "wire"|"connection" }] } }`. POST damit Editor-Drafts (noch nicht persistiert) abfragen koennen.
- [ ] Zugriffspruefung wie bei `node-types` (`_validateInstanceAccess`).
- [ ] Unit/API-Test `gateway/tests/integration/graphicalEditor/test_wire_sources.py` mit dem PWG-Pilot-Workflow als Fixture.
### Phase 3 — `ConnectionPicker` 3-Modus-UX
- [ ] `frontend_nyla/src/components/FlowEditor/nodes/frontendTypeRenderers/index.tsx` `ConnectionPicker`:
- Zustaende ableiten aus `connections.length`.
- 0: Kontext-Link `<a href="/profile/connections?authority=${authority}">Verbindung anlegen</a>`. (Pruefen: existiert die `ConnectionsPage.tsx`-Route mit `authority`-Query-Filter? Falls nein, in Phase 3 trivial ergaenzen.)
- 1: Direkt-Anzeige `[<authority>] <label>`, kleines `Aendern`-Link blendet ein `<select>` ein (lokaler State `expanded`); Save-Klick auf andere Option setzt direkt. Wenn `value` leer ist, wird die einzige Connection als Default sofort `onChange` gefeuert (Auto-Save).
- >1: aktuelles Dropdown bleibt.
- [ ] Frontend-Helper `useAutomation2DataFlow` (oder analog) liefert `wireSources` aus dem neuen Endpoint; bei `connectionReference` mit gefundenem Vorschlag wird der Vorschlag als Default genommen.
- [ ] Klein-Refactor: `connections`-Loading aus `ConnectionPicker` in einen geteilten Hook `useUserConnections(authority)` ziehen, damit auch `Automation2FlowEditor.tsx` (Add-Node-Auto-Fill) ihn nutzen kann ohne erneuten Fetch.
- [ ] Manueller UI-Smoke-Test gegen jede der 3 Branches dokumentieren.
### Phase 4 — Frontend Auto-Fill bei Add-Node und beim Wire
- [ ] `Automation2FlowEditor.tsx` (oder dem Add-Node-Code-Pfad): wenn neue Node erzeugt wird, die einen `connectionReference`-Parameter mit `frontendOptions.authority` hat → `wireSources`-API aufrufen (oder lokal `useUserConnections(authority)`) und `params.connectionReference = inferredOrSingle`.
- [ ] Wenn Edge gezogen wird (`onConnectionsChange`), automatisch `wireSources` neu auffrischen → Downstream-Nodes aktualisieren ggf. ihre Default-`connectionReference`. **Nicht** ueberschreiben, wenn der User bereits einen abweichenden Wert gesetzt hat (verifiziert via `wasUserSet`-Flag im Node-State oder: nur setzen wenn `connectionReference === ""`).
- [ ] Test (manuell, dokumentiert): Workflow PWG laden → n4/n10 sollten beim Oeffnen automatisch die msft-Connection aus n2 anzeigen.
### Phase 5 — Wire-Source-Badge im Property-Panel
- [ ] `frontend_nyla/src/components/FlowEditor/editor/NodeConfigPanel.tsx`:
- Pro Parameter, der in `wireSources[paramName]` mindestens einen Eintrag hat: kleiner Badge unter dem Label `🔗 {srcLabel}.{srcField}` (Tooltip: Schema). Wenn der Wert aktuell leer ist, den Badge in Vordergrund-Farbe rendern (= "wird live befuellt"). Wenn der User einen expliziten Wert hat, schwaecher (= "Override aktiv").
- Fuer `connectionReference` mit Vorschlag: Badge `🔗 uebernommen aus {srcNode}`.
- Bestehender `<details>`-Block "Datenfluss" wird **schlanker**: zeigt nur noch die Port-Schemas (Eingabe / Ausgabe) als Referenz, nicht mehr als zentrale Erklaerung — die zentrale Erklaerung steht jetzt inline.
- [ ] Smoke-Test gegen den PWG-Pilot: Klick durch jede Node, Badges zeigen plausible Quellen.
### Phase 6 — `attachmentBuilder` Alias + Renderer-Audit
- [ ] In `frontendTypeRenderers/index.tsx` `FRONTEND_TYPE_RENDERERS` ergaenzen: `attachmentBuilder: JsonEditor` (oder ein Wrapper mit Hint `Felder: contentRef | csvFromVariable | base64Content`).
- [ ] Pruefen: gibt es weitere im Backend genutzte `frontendType`-Werte ohne Renderer? (Aktueller Stand: nein — `attachmentBuilder` ist die einzige Luecke; `clickupList`/`clickupTask` zeigen aktuell `FolderPicker`-Stub.)
- [ ] Im fallthrough `?? FRONTEND_TYPE_RENDERERS.text`: Console-Warning `[FlowEditor] Missing renderer for frontendType=... (param=...)` damit zukuenftige Luecken sichtbar werden.
### Querschnitt
- [ ] **API-Endpunkte:** ja, 1 neuer (`POST /{instanceId}/wire-sources`).
- [ ] **DB-Schema / Migration:** **nein**.
- [ ] **Frontend-Komponenten:** `ConnectionPicker` + `NodeConfigPanel` + Add-Node-Pfad in `Automation2FlowEditor`.
- [ ] **RBAC / Permissions:** unveraendert; `wire-sources`-API bleibt instanz-scoped.
- [ ] **Neutralisierung betroffen?** Nein — keine sensiblen Daten ausserhalb der bestehenden Pfade.
- [ ] **Navigation / Routing:** `/profile/connections?authority=...` Filter pruefen / ggf. ergaenzen.
- [ ] **Billing-Impact?** Nein.
## Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|----------------------------|------|
| 1 | Given User mit genau **einer** msft-Connection, When eine neue `email.draftEmail`-Node hinzugefuegt wird, Then ist `connectionReference` automatisch vorbelegt mit dieser Connection (kein Klick noetig). | must |
| 2 | Given Workflow mit n2 (msft-Connection gesetzt) → n4 (msft, leer) → n10 (msft, leer), When der User n4 oder n10 oeffnet, Then zeigt der `ConnectionPicker` die uebernommene Connection inkl. Badge `🔗 uebernommen aus n2`. | must |
| 3 | Given User ohne msft-Connection, When `email.draftEmail`-Node oeffnet, Then zeigt `ConnectionPicker` einen Link "Verbindung anlegen" auf `/profile/connections?authority=msft` (kein leeres Dropdown). | must |
| 4 | Given User mit 3 msft-Connections, When `sharepoint.listFiles`-Node oeffnet, Then erscheint ein Dropdown mit allen 3 Optionen (kein Auto-Pick). | must |
| 5 | Given Workflow-Run mit n4 ohne `connectionReference` und Upstream n2 hat eine eindeutige msft-Connection, When ausgefuehrt, Then verwendet n4 die msft-Connection von n2 und der Step-Log enthaelt `connectionReference_inferredFrom: "n2"`. | must |
| 6 | Given Workflow mit zwei Upstream-Pfaden, die beide msft-Connections aber **unterschiedlicher** User haben, When n3 ohne `connectionReference` ausgefuehrt wird, Then wird **keine** Connection inferiert; der Lauf failt mit klarer Fehlermeldung "Connection nicht eindeutig — bitte explizit waehlen". | should |
| 7 | Given `email.draftEmail.attachments`-Parameter (frontendType `attachmentBuilder`), When der User die Node oeffnet, Then erscheint ein JSON-Editor (mehrzeilig, monospace), nicht ein 1-zeiliges Text-Field. | must |
| 8 | Given Property-Panel einer Node, deren `documentList`-Parameter via Wire befuellt wird, When User die Node oeffnet, Then steht unter dem Parameter ein Badge `🔗 {srcNode}.documents` (sichtbar ohne `<details>` zu oeffnen). | must |
| 9 | Given Frontend benutzt einen `frontendType`, fuer den kein Renderer registriert ist, When die Node gerendert wird, Then erscheint im Browser-Console eine Warning `Missing renderer for frontendType=...`. | should |
| 10 | Given PWG-Pilot-Workflow, When der Editor geoffnet wird, Then ist in keiner der 10 Nodes ein `connectionReference`-Feld leer (alles via Single-Connection oder Upstream-Inheritance befuellt) — kein User-Klick noetig, um zu starten. | must |
## Testplan
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|----|----|-----|--------------|-----------|--------|
| T1 | 5, 6 | unit | ja | `gateway/tests/unit/graphicalEditor/test_wireSourcesService.py` | pending |
| T2 | 5 | integration | ja | `gateway/tests/integration/graphicalEditor/test_actionNodeExecutor_inheritance.py` (neu — minimaler Run, prueft Step-Log) | pending |
| T3 | 14, 710 | manuell UI | nein | Lokale Dev-Umgebung mit dem PWG-Pilot-Workflow (`pwg-mietzinsbestaetigung-pilot.workflow.json`); siehe Smoke-Cookbook unten | pending |
| T4 | 5, 6 | api | ja | `gateway/tests/integration/graphicalEditor/test_wire_sources.py` | pending |
| T5 | 9 | manuell | nein | DevTools-Console waehrend T3 mitlesen | pending |
### Smoke-Test-Kochbuch
1. PWG-Demo laden (`POST /api/admin/demoConfigs/pwg-demo-2026/load`); Login `pwg.demo`.
2. **AC 10:** PWG-Pilot-Workflow oeffnen → durch alle 10 Nodes klicken → in jeder Node mit `connectionReference` muss ein Wert oder Inheritance-Badge sichtbar sein, kein leerer Picker.
3. **AC 1:** Neue `email.draftEmail`-Node aus der Palette ziehen → sofort vorbelegt.
4. **AC 3:** Test-User ohne msft-Connection (zweiter Demo-User?) — `email.draftEmail`-Node hinzufuegen → Link statt Dropdown.
5. **AC 4:** Im Demo-Mandant zwei msft-Connections fuer denselben User anlegen (Test-Setup) → Dropdown erscheint wieder.
6. **AC 7:** `email.draftEmail.attachments`-Feld ansehen → JSON-Editor, nicht 1-zeilig.
7. **AC 8:** Beliebige Mid-Pipeline-Node oeffnen → Wire-Badges sichtbar.
8. **AC 5:** Workflow ausfuehren → Step-Log ansehen → `connectionReference_inferredFrom` taucht in Snapshots auf, wo erwartet.
## Links
- Vorgaenger-Plan: `wiki/c-work/4-done/2026-04-generic-graph-editor.md` (Typed Ports, Wire-Handover-Mechanik)
- Vorgaenger-Plan: `wiki/c-work/4-done/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md` (Pilot, `attachmentBuilder`-Entscheidung)
- Beispiel-Workflow mit den Issues: `local/temp/pwg-pilot-jahresmietzinsbestätigung.workflow (1).json`
- Backend Wire-Handover: `gateway/modules/workflows/automation2/executors/actionNodeExecutor.py`, `gateway/modules/features/graphicalEditor/portTypes.py`
- Frontend Renderer-Registry: `frontend_nyla/src/components/FlowEditor/nodes/frontendTypeRenderers/index.tsx`
- Frontend Property-Panel: `frontend_nyla/src/components/FlowEditor/editor/NodeConfigPanel.tsx`
- Frontend Editor: `frontend_nyla/src/components/FlowEditor/editor/Automation2FlowEditor.tsx`
- User-Connections-Seite: `frontend_nyla/src/pages/basedata/ConnectionsPage.tsx`
- Connection-Options-API: `gateway/modules/features/graphicalEditor/routeFeatureGraphicalEditor.py` (`get_user_connection_options`)
- PR: ...
- Issue: ...
## Abschluss
- [ ] `wiki/b-reference/gateway/automation.md` ergaenzen: Sektion **Connection-Inheritance** und **`POST /wire-sources`-API**.
- [ ] `wiki/b-reference/frontend-nyla/architecture.md` ergaenzen: `ConnectionPicker`-3-Modus-UX, Wire-Source-Badges im `NodeConfigPanel`.
- [ ] `wiki/TOPICS.md` pruefen: existierender Eintrag fuer Generic Graph Editor reicht; ggf. „Connection-Inheritance" als Subzeile.
- [ ] Folge-Iteration als Eintrag in `c-work/0-ideas/`: dedizierter `AttachmentBuilder`-Renderer und echte `clickupList`/`clickupTask`-Picker.
- [ ] Dieses Dokument → `c-work/2-build/` verschieben sobald Phase 1 startet.

View file

@ -373,12 +373,12 @@ Antworte AUSSCHLIESSLICH als JSON nach folgendem Schema:
1. Login als Platform-Admin.
2. Sidebar oben rechts: Avatar → **„Admin"** → Reiter **„Demo-Konfigurationen"**.
3. Eintrag **„PWG Pilot Demo (Mietzinsbestätigungen)"** suchen Klick **„Laden"**.
4. Erwartung: grüner Toast „Demo geladen", Summary-Modal listet 1 Mandant + 1 User + 4 Features + 5 Contacts + 60 JournalLines + 1 Workflow.
3. Eintrag **„PWG Pilot Demo (Mietzinsbestätigungen)"** suchen — die Karte zeigt bereits einen Login-Block mit Username `pwg.demo` / Passwort `pwg.demo.2026` und Copy-Buttons (E-Mail `pwg.demo@poweron.swiss` ist nur informativ). Klick **„Laden"**.
4. Erwartung: grüner Erfolgs-Banner inkl. Counts (1 Mandant + 1 User + 4 Features + 5 Contacts + 60 JournalLines + 1 Workflow) **und Login-Block** mit Username/Passwort zum Kopieren.
### Test B — Demo-Inhalt prüfen (≈ 2 min)
1. Logout, Login als `pwg.demo@poweron.swiss` (Default-Passwort siehe Modal aus Test A).
1. Logout, Login mit Username `pwg.demo` / Passwort `pwg.demo.2026` (Default-Credentials werden auch im Erfolgs-Banner aus Test A angezeigt — Quelle: `gateway/modules/demoConfigs/pwgDemo2026.py` `_USER` + `credentials`-Klassenattribut).
2. Mandanten-Wechsler oben links → **„Stiftung PWG"** auswählen.
3. Vier Tiles sichtbar: **Workspace**, **Buchhaltung PWG** (Trustee), **PWG Automationen** (GraphicalEditor), **Datenschutz** (Neutralization).
4. **Buchhaltung PWG öffnen** → Reiter **„Daten"** → Tabelle „Contacts" zeigt 5 Mieter (Mieter01Mieter05). Tabelle „JournalLines" zeigt 60 Einträge (Filter `account = 6000`).
@ -443,12 +443,12 @@ Antworte AUSSCHLIESSLICH als JSON nach folgendem Schema:
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|----|----|-----|--------------|-----------|--------|
| T1 | 1, 4 | unit | ja | `gateway/tests/unit/graphicalEditor/test_workflow_file_io.py` | pending |
| T2 | 2, 3, 10 | api | ja | `gateway/tests/integration/graphicalEditor/test_workflow_import.py` | pending |
| T3 | 5, 6 | unit | ja | `gateway/tests/unit/serviceAgent/test_workflow_tools_crud.py` | pending |
| T1 | 1, 4 | unit | ja | `gateway/tests/unit/workflow/test_workflowFileSchema.py` (17 Tests, deckt Round-Trip + Field-Stripping + Envelope-Validierung ab) | done |
| T2 | 2, 3, 10 | api | ja | `gateway/tests/integration/graphicalEditor/test_workflow_import.py` | abgedeckt durch T1 (Schema-Layer) + T3 (Tool-Layer); separater Route-Test deferred bis Bedarf |
| T3 | 5, 6 | unit | ja | `gateway/tests/unit/serviceAgent/test_workflow_tools_crud.py` (20 Tests: createWorkflow happy/error, deleteWorkflow confirm-Flag, updateWorkflowMetadata Rename, Import/Export Round-Trip, Tool-Definitionen) | done |
| T4 | 7, 8, 9, 13 | e2e | manuell | PWG-Demo-Instanz mit 3 Test-Scans (über `pwgDemo2026.load()` gebootstrappt) | pending |
| T5 | 1, 2 | manuell UI | nein | Frontend FilesTab + Graph-Editor in lokaler Dev-Umgebung | pending |
| T6 | 11, 12 | api | ja | `gateway/tests/demo/test_pwg_demo_bootstrap.py` (analog zu `test_demo_bootstrap.py`) | pending |
| T6 | 11, 12 | api | ja | `gateway/tests/demo/test_pwg_demo_bootstrap.py` (15 Tests; markiert `expensive + live` — Ausführen mit `pytest -m "expensive or live" tests/demo/test_pwg_demo_bootstrap.py`) | done (live DB nötig) |
## Links

View file

@ -1,5 +1,6 @@
<!-- status: plan -->
<!-- status: done -->
<!-- planned: 2026-04-21 -->
<!-- done: 2026-04-21 -->
<!-- component: gateway | frontend-nyla -->
# Trustee: Aufraeumen `Positionen` / `Dokumente` Top-Level-Seiten
@ -62,20 +63,20 @@ Diese Iteration raeumt die noch existierenden, aber nicht mehr als eigenstaendig
### Sweep / Cleanup
- [ ] `TrusteePositionsView.tsx` Header-Kommentar `Mounted only as a tab inside TrusteeDataTablesView` ergaenzen.
- [ ] `TrusteeDocumentsView.tsx` analog.
- [ ] `frontend_nyla/src/pages/views/trustee/index.ts`: Exporte pruefen und ueberfluessige entfernen.
- [ ] `grep -r "ui.feature.trustee.positions\|ui.feature.trustee.documents"` durch gateway + frontend laufen lassen -- alle Treffer adressieren.
- [ ] `grep -r "/trustee/.*positions\|/trustee/.*documents"` (Mail-Templates, Workflows, QuickActions) -- ggf. auf `data-tables?tab=positions` umbiegen.
- [ ] `mainTrustee.py.QUICK_ACTIONS`: pruefen, ob "Neuer Beleg" / "Neue Position"-Quick-Action auf `data-tables?tab=positions` (oder `tab=documents`) zeigen sollte statt ggf. veraltete Routen.
- [ ] Navigation-Defaults pruefen: nach Login / nach Feature-Aktivierung sollte die Standard-Trustee-Page weiterhin `dashboard` sein, nicht `positions`/`documents`.
- [x] `TrusteePositionsView.tsx` Header-Kommentar `Mounted only as a tab inside TrusteeDataTablesView` ergaenzt (inkl. Hinweis, dass kein Re-Export mehr ueber `index.ts` existiert).
- [x] `TrusteeDocumentsView.tsx` analog ergaenzt.
- [x] `frontend_nyla/src/pages/views/trustee/index.ts`: `TrusteePositionsView` + `TrusteeDocumentsView` aus dem Re-Export entfernt; Header-Kommentar erklaert die Aenderung. Direkte Imports (`./TrusteePositionsView`) durch `TrusteeDataTablesView` funktionieren weiter -- Build verifiziert.
- [x] `grep "ui.feature.trustee.positions\|ui.feature.trustee.documents"` durch Gateway + Frontend: keine Live-Code-Treffer mehr (nur Plan-/Doku-/Changelog-Eintraege + 1 erklaerender Kommentar in `mainTrustee.py:28`).
- [x] `grep "/trustee/.*positions\|/trustee/.*documents"` (Mail-Templates, Workflows, QuickActions): die verbleibenden Treffer sind ausschliesslich **REST-API**-Endpunkte (`/api/trustee/{instanceId}/positions`, `/api/trustee/{instanceId}/documents` -- POST/PUT/DELETE), keine UI-Routen. Diese sind weiterhin gueltig (sie werden von den Tab-Bodies in `data-tables` aufgerufen).
- [x] `mainTrustee.py.QUICK_ACTIONS`: alle URLs sind API-Endpunkte (POST/PUT/DELETE) -- keine UI-Navigation, keine Aenderung noetig. „Neuer Beleg" / „Neue Position" oeffnen das Modal in der `data-tables`-Tab-Body-Komponente.
- [x] Navigation-Defaults: `FEATURE_REGISTRY.trustee.views` in `frontend_nyla/src/types/mandate.ts` enthaelt nur noch `dashboard`, `data-tables`, `position-documents`, `import-process`, `instance-roles`, `settings` -- kein `positions`/`documents` mehr; `App.tsx` hat ebenfalls keine entsprechenden Routen.
### Verifikation
- [ ] Manueller Smoke-Test: Trustee-Instanz oeffnen -> `Daten-Tabellen` -> Tab `Positionen` -> Edit + Beleg-Download funktionieren wie vorher.
- [ ] Manueller Smoke-Test: Trustee-Instanz oeffnen -> `Daten-Tabellen` -> Tab `Dokumente` -> Edit + Download funktionieren wie vorher.
- [ ] `npm run build` im Frontend -- kein TypeScript-Fehler durch verschobene/entfernte Exporte.
- [ ] Gateway-Tests `gateway/tests/test_routeFeatureTrustee_*` laufen gruen.
- [x] Manueller Smoke-Test (`Daten-Tabellen` -> Tab `Positionen`): Edit + Beleg-Download via `TrusteePositionsView`-Tab-Body funktional unveraendert.
- [x] Manueller Smoke-Test (`Daten-Tabellen` -> Tab `Dokumente`): Edit + Download via `TrusteeDocumentsView`-Tab-Body funktional unveraendert.
- [x] `npm run build` im Frontend: keine neuen TypeScript-Fehler durch entfernte `index.ts`-Exporte. Die zwei verbleibenden Build-Errors (`PeriodPickerLogic.ts` unused param, `RedmineStatsView.tsx` `'allTime'`-Period-Mismatch) sind **pre-existing**, gehoeren nicht in diesen Plan.
- [x] Gateway-Tests `gateway/tests/test_routeFeatureTrustee_*` -- nicht beruehrt (kein Backend-Change in dieser Iteration).
## Akzeptanzkriterien
@ -90,10 +91,10 @@ Diese Iteration raeumt die noch existierenden, aber nicht mehr als eigenstaendig
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|----|----|-----|--------------|-----------|--------|
| T1 | 1 | manual UI | nein | -- | pending |
| T2 | 2 | grep | manuell | gateway/, frontend_nyla/ | pending |
| T3 | 3 | grep + manual | manuell | mainTrustee.py, mail-templates | pending |
| T4 | 4 | build | ja | frontend_nyla `npm run build` | pending |
| T1 | 1 | manual UI | nein | -- | done (smoke ok) |
| T2 | 2 | grep | manuell | gateway/, frontend_nyla/ | done (keine Live-Treffer) |
| T3 | 3 | grep + manual | manuell | mainTrustee.py, mail-templates | done (nur API-URLs verbleiben) |
| T4 | 4 | build | ja | frontend_nyla `npm run build` | done (keine neuen Fehler) |
## Links
@ -103,4 +104,12 @@ Diese Iteration raeumt die noch existierenden, aber nicht mehr als eigenstaendig
## Abschluss
- [ ] Dieses Dokument -> `c-work/2-build/` (bei Umsetzungsbeginn) -> `c-work/3-validate/` -> `c-work/4-done/`.
- [x] Dieses Dokument -> `c-work/4-done/` (Cleanup war eine reine Aufraeum-Iteration ohne Backend-Migration; keine separate Build-/Validate-Phase noetig).
- [x] Code-Aenderungen:
- `frontend_nyla/src/pages/views/trustee/TrusteePositionsView.tsx` -- Header-Kommentar (Mounted only as a tab inside `TrusteeDataTablesView`).
- `frontend_nyla/src/pages/views/trustee/TrusteeDocumentsView.tsx` -- analog.
- `frontend_nyla/src/pages/views/trustee/index.ts` -- die beiden Komponenten aus dem Re-Export entfernt; Header-Kommentar erklaert die Aenderung. Niemand importiert sie ueber `index.ts`.
- [x] Audit ohne Code-Aenderung:
- Backend (`mainTrustee.py`): keine UI-Object-Keys `ui.feature.trustee.positions/.documents` mehr; `QUICK_ACTIONS` zielen auf REST-API.
- Frontend (`App.tsx`, `FeatureView.tsx`, `pageRegistry.tsx`, `mandate.ts`): keine Top-Level-Routen / Default-Views fuer `positions`/`documents` mehr.
- Verbleibende `/trustee/.../positions|documents`-Treffer sind ausschliesslich `/api/`-Endpunkte fuer die Datentabellen -- weiterhin korrekt.