254 lines
12 KiB
Markdown
254 lines
12 KiB
Markdown
<!-- status: superseded -->
|
|
<!-- lastUpdated: 2026-04-24 -->
|
|
<!-- lastReviewed: 2026-04-25 -->
|
|
<!-- archivedOn: 2026-04-25 -->
|
|
<!-- supersededBy: b-reference/gateway/workflow.md (Typed Action Architecture) -->
|
|
<!-- related: c-work/4-done/2026-04-typed-action-architecture.md, c-work/4-done/2026-04-typed-action-followups.md -->
|
|
|
|
# Node-Typisierungs-Audit (Stand 2026-04-24)
|
|
|
|
> **ARCHIVIERT 2026-04-25.** Die 4 Defekt-Muster sind durch die
|
|
> [Typed Action Architecture](../../b-reference/gateway/workflow.md)
|
|
> und ihre [Folge-Schritte](../c-work/2026-04-typed-action-followups.md)
|
|
> strukturell beseitigt. Bug-Restbestand (Trustee/Redmine-Adapter noch nicht
|
|
> migriert) wurde in `c-work/4-done/2026-04-feature-instance-ref-adapter-migration.md`
|
|
> erledigt. Diese Seite bleibt nur als historische Faktenbasis erhalten.
|
|
|
|
Quelle: `gateway/modules/features/graphicalEditor/nodeDefinitions/*.py` —
|
|
manuell ausgelesen aus 12 Node-Definition-Dateien (53 Nodes total).
|
|
|
|
## Frage des Audits
|
|
|
|
> Haben die Nodes eine klare Logik, welche Input-Attribute mit welchem
|
|
> Format-Typ (Str, Float, UserConnection, FeatureInstanceId,
|
|
> TrusteeDatabaseObject, etc.) erforderlich sind, und ist die Ausgabe
|
|
> pro Node klar definiert?
|
|
|
|
## Antwort
|
|
|
|
**Nein.** Nur **9 von 53 Nodes** sind sauber typisiert.
|
|
|
|
| Status | Anzahl | Bedeutung |
|
|
|---|---|---|
|
|
| ✓ Klar | 9 / 53 | Input-Typen + Output-Typ klar definiert ueber Katalog-Schemas |
|
|
| ⚠ Pseudo-Typen | 21 / 53 | Funktioniert, aber `type: "string"` mit Frontend-Magic statt typisierter Schema-Typ |
|
|
| ✗ Defekt | 23 / 53 | Pflicht-Param `hidden` ODER Output ist generisches `ActionResult` ODER beides |
|
|
|
|
## Score pro Modul
|
|
|
|
| Modul | Total | ✓ | ⚠ | ✗ | Haeufigster Defekt |
|
|
|---|---|---|---|---|---|
|
|
| `trustee.*` | 5 | 0 | 0 | 5 | `featureInstanceId:string,hidden` + Output `ActionResult` |
|
|
| `redmine.*` | 6 | 0 | 0 | 6 | `featureInstanceId:string,hidden` + Output `ActionResult` |
|
|
| `sharepoint.*` | 6 | 0 | 6 | 0 | Pseudo `connectionReference` + Folder-/File-Refs |
|
|
| `clickup.*` | 6 | 0 | 6 | 0 | Pseudo `connectionReference` + List-/Team-Refs |
|
|
| `email.*` | 3 | 0 | 2 | 1 | Pseudo `connectionReference`; `draftEmail` Output `ActionResult` (statt existierendem `EmailDraft`) |
|
|
| `ai.*` | 8 | 3 | 4 | 1 | `ai.prompt` mit `documentList:hidden` + `context:hidden`; Wildcard-`accepts` |
|
|
| `input.*` | 7 | 6 | 1 | 0 | sauber; `input.review` mit Pseudo-`contentRef` |
|
|
| `flow.*` | 4 | 1 | 3 | 0 | `condition`/`value`/`items` als Pseudo-strings |
|
|
| `triggers.*` | 3 | 2 | 1 | 0 | `trigger.schedule.cron:string` |
|
|
| `data.*` | 3 | 2 | 1 | 0 | `data.filter.condition` Pseudo + Wildcard-`accepts` |
|
|
| `file.*` | 1 | 0 | 0 | 1 | `context:string,hidden` Pflicht-Inhalt nicht pickbar |
|
|
| `context.*` | 1 | 1 | 0 | 0 | sauber |
|
|
| **Summe** | 53 | 9 | 21 | 23 | |
|
|
|
|
## Vier wiederkehrende Defekt-Muster
|
|
|
|
### Muster 1 — `featureInstanceId: string, hidden` als Pflicht-Param
|
|
|
|
**Vorkommen:** 11x (5 trustee + 6 redmine)
|
|
|
|
**Code heute:**
|
|
```python
|
|
{"name": "featureInstanceId", "type": "string", "required": True,
|
|
"frontendType": "hidden", "description": t("Trustee Feature-Instanz-ID")},
|
|
```
|
|
|
|
**Konsequenz:** User kann den Pflicht-Param nicht setzen. Engine raet
|
|
ihn aus `WorkflowFeatureContext` oder Session — bricht still bei
|
|
Mehr-Mandant-Setups.
|
|
|
|
**Soll:**
|
|
```python
|
|
{"name": "target", "type": "FeatureInstanceRef[trustee]", "required": True,
|
|
"ui": {"label": "Trustee-System"}},
|
|
```
|
|
|
|
mit neuem Katalog-Typ in `portTypes.py`:
|
|
```python
|
|
"FeatureInstanceRef": PortSchema(name="FeatureInstanceRef", fields=[
|
|
PortField(name="featureCode", type="str", description="trustee | redmine | clickup | ..."),
|
|
PortField(name="id", type="str", description="FeatureInstance.id"),
|
|
PortField(name="label", type="str", required=False),
|
|
]),
|
|
```
|
|
|
|
Picker-Optionen kommen vom neuen Endpoint `/options/featureInstance?featureCode=trustee&mandateId=…` (analog zu `user.connection`).
|
|
|
|
---
|
|
|
|
### Muster 2 — `connectionReference: string, frontendType: userConnection`
|
|
|
|
**Vorkommen:** 15x (6 sharepoint + 6 clickup + 3 email)
|
|
|
|
**Code heute:**
|
|
```python
|
|
{"name": "connectionReference", "type": "string", "required": True,
|
|
"frontendType": "userConnection",
|
|
"frontendOptions": {"authority": "msft"},
|
|
"description": t("SharePoint-Verbindung")},
|
|
```
|
|
|
|
**Konsequenz:** Type ist string — der Picker im Backend kennt das
|
|
Konzept "ConnectionRef" nicht. Adapter im Frontend macht einen
|
|
Spezialfall fuer `frontendType: "userConnection"`. Wenn ein Folge-Node
|
|
auch eine Connection braucht, kann der Output keiner Vorgaenger-Node
|
|
sie liefern, weil Connection nirgends als Output deklariert ist.
|
|
|
|
**Soll:**
|
|
```python
|
|
{"name": "connection", "type": "ConnectionRef[msft]", "required": True,
|
|
"ui": {"label": "SharePoint-Verbindung"}},
|
|
```
|
|
|
|
`ConnectionRef` existiert bereits im Katalog — wird heute aber nur
|
|
implizit benutzt (im `DocumentList.connection`-Subfeld). Mit
|
|
`ConnectionRef[authority]` als Discriminator wird die Authority
|
|
deterministisch gefiltert.
|
|
|
|
---
|
|
|
|
### Muster 3 — Output `ActionResult` statt typed Schema
|
|
|
|
**Vorkommen:** 17x
|
|
|
|
| Node | Heute | Soll-Schema |
|
|
|---|---|---|
|
|
| `trustee.refreshAccountingData` | `ActionResult` | `TrusteeRefreshResult { syncCounts, dateRange, errors }` |
|
|
| `trustee.processDocuments` | `ActionResult` | `TrusteeProcessResult { documents: List[Document], errors: List[ProcessError] }` |
|
|
| `trustee.syncToAccounting` | `ActionResult` | `TrusteeSyncResult { synced: int, failed: int, journalLines: List[JournalLine] }` |
|
|
| `trustee.queryData` | `ActionResult` | `QueryResult { rows, columns, count }` (Schema **existiert** im Katalog!) |
|
|
| `email.draftEmail` | `ActionResult` | `EmailDraft { subject, body, to, cc, attachments, connection }` (Schema **existiert** im Katalog!) |
|
|
| `redmine.readTicket` | `ActionResult` | `RedmineTicket { id, subject, status, tracker, assignee, ... }` (neu) |
|
|
| `redmine.listTickets` | `ActionResult` | `RedmineTicketList { tickets: List[RedmineTicket], count, filters }` (neu) |
|
|
| `redmine.createTicket` | `ActionResult` | `RedmineTicket` |
|
|
| `redmine.updateTicket` | `ActionResult` | `RedmineTicket` |
|
|
| `redmine.getStats` | `ActionResult` | `RedmineStats { kpis, throughput, statusDistribution, backlog }` (neu) |
|
|
| `redmine.runSync` | `ActionResult` | `TrusteeRefreshResult`-aehnlich oder bleibt `ActionResult` (fire-and-forget) |
|
|
| `sharepoint.uploadFile` | `ActionResult` | `SharePointFileRef` (Upload-Ziel als Ref) oder bleibt |
|
|
| `sharepoint.copyFile` | `ActionResult` | `SharePointFileRef` (neue Datei-Position) |
|
|
| `clickup.uploadAttachment` | `ActionResult` | `TaskAttachmentRef` (neu) oder bleibt |
|
|
| `file.create` | `DocumentList` | OK, bleibt (1 Document drin) |
|
|
|
|
**Konsequenz heute:** Folge-Nodes koennen keine spezifischen Felder
|
|
pickrn. Workflow-Bauer muss raten, was im `data: Dict`-Black-Box ist.
|
|
|
|
**Soll:** Pro Action ein typisiertes Schema im Katalog. `ActionResult`
|
|
bleibt nur fuer echte fire-and-forget-Aktionen (z.B. `runSync`).
|
|
|
|
---
|
|
|
|
### Muster 4 — Wildcard-`accepts` mit ≥ 4 Typen inkl. `Transit`
|
|
|
|
**Vorkommen:** 8x
|
|
|
|
| Node | Heute `accepts` | Problem |
|
|
|---|---|---|
|
|
| `trustee.extractFromFiles` | `[DocumentList, Transit, AiResult, LoopItem, ActionResult]` | Was wenn `AiResult` reinkommt? Heuristik im Engine raet, was wo befuellt wird |
|
|
| `ai.prompt` | `[DocumentList, AiResult, TextResult, Transit, LoopItem, ActionResult]` | Pflicht-Param `documentList:hidden` wird aus dem Wire heuristisch befuellt |
|
|
| `email.draftEmail` | `[EmailDraft, AiResult, Transit, ConsolidateResult, DocumentList]` | Wie wird `subject/body/to` aus `AiResult` oder `ConsolidateResult` abgeleitet? |
|
|
| `flow.loop` | `[Transit, UdmDocument, EmailList, DocumentList, FileList, TaskList, ActionResult]` | Loop ueber `ActionResult`? Was iteriert dann? |
|
|
| `data.filter` | `[AggregateResult, FileList, TaskList, EmailList, DocumentList, UdmDocument, UdmNodeList]` | OK — alle sind Listen-aehnlich, aber kein Pflicht-Output-Schema |
|
|
| `ai.summarize/translate/convert` | `[DocumentList, Transit]` | `Transit` als generisches "irgendwas" |
|
|
| `data.aggregate` | `[Transit, AiResult, LoopItem]` | Loop-Body Output kann alles sein |
|
|
| `data.consolidate` | `[AggregateResult, Transit]` | `Transit` |
|
|
|
|
**Konsequenz:** Wires bringen "irgendwas" vorbei, Backend muss
|
|
heuristisch zuordnen. Keine deterministische Validierung im Frontend
|
|
oder Backend moeglich.
|
|
|
|
**Soll:** `accepts` enthaelt **exakt** den Schema-Typ, der vom
|
|
Pflicht-Input-Param erwartet wird. Wenn mehrere Modi existieren (z.B.
|
|
`flow.loop` ueber Liste oder Dokument), wird das ueber ein typisiertes
|
|
**Union** im Katalog ausgedrueckt — nicht ueber eine Wildcard-Liste.
|
|
|
|
---
|
|
|
|
## Schemas die im Katalog existieren aber nirgends benutzt werden
|
|
|
|
Diese sind heute schon im `PORT_TYPE_CATALOG` deklariert, werden aber
|
|
von keiner Node-Definition als Output verwendet:
|
|
|
|
| Schema | Sollte Output sein von | Aktuell Output |
|
|
|---|---|---|
|
|
| `EmailDraft` | `email.draftEmail` | `ActionResult` |
|
|
| `QueryResult` | `trustee.queryData` | `ActionResult` |
|
|
| `TaskItem` (alleinstehend, nicht List) | `clickup.getTask` | `TaskResult` (OK, aber inkonsistent) |
|
|
| `UdmPage`, `UdmBlock`, `UdmNodeList` | sind nur `accepts`-Eintraege fuer `data.filter`, kein Producer | — |
|
|
|
|
**Konsequenz:** Schon das Aktivieren der existierenden Schemas wuerde
|
|
2 der ✗-Faelle ohne neue Katalog-Eintraege loesen.
|
|
|
|
---
|
|
|
|
## Schemas die fehlen und neu gebraucht werden
|
|
|
|
| Schema | Brauchen wir fuer |
|
|
|---|---|
|
|
| `FeatureInstanceRef` (mit Discriminator) | 11x — alle trustee + redmine Nodes |
|
|
| `ConnectionRef[authority]`-Discriminator | 15x — sharepoint + clickup + email |
|
|
| `TrusteeRefreshResult` | `trustee.refreshAccountingData` |
|
|
| `TrusteeProcessResult` | `trustee.processDocuments` |
|
|
| `TrusteeSyncResult` | `trustee.syncToAccounting` |
|
|
| `RedmineTicket` | `redmine.readTicket/createTicket/updateTicket` |
|
|
| `RedmineTicketList` | `redmine.listTickets` |
|
|
| `RedmineStats` | `redmine.getStats` |
|
|
| `JournalLine`, `Account`, `Tenant` | Drill-Down in Trustee-Outputs |
|
|
| `ProcessError` | Fehler-Listen in `*Result`-Schemas |
|
|
| `CronExpression` | `trigger.schedule` |
|
|
| `ConditionExpression` | `flow.ifElse`, `data.filter` |
|
|
| `PromptTemplateRef` | optional — wenn Prompt-Library kommt |
|
|
|
|
Total: **~13 neue Katalog-Schemas**, davon 2 mit Discriminator
|
|
(`FeatureInstanceRef`, `ConnectionRef`).
|
|
|
|
---
|
|
|
|
## Pflicht-Param-Konzepte die heute als `frontendType` versteckt sind
|
|
|
|
Diese laufen heute als Frontend-only-Magic mit `type: "string"` und
|
|
`frontendType: "<custom>"`. Sie sollten echte Katalog-Typen werden:
|
|
|
|
| `frontendType` | Vorkommen | Soll-Katalog-Typ |
|
|
|---|---|---|
|
|
| `userConnection` | 15x | `ConnectionRef[authority]` |
|
|
| `sharepointFolder` | 4x | `SharePointFolderRef` (existiert!) |
|
|
| `sharepointFile` | 3x | `SharePointFileRef` (existiert!) |
|
|
| `clickupList` | 3x | `ClickUpListRef` (neu) |
|
|
| `hidden` (als Pflicht) | 11x | meist `FeatureInstanceRef`, manchmal `DataRef`-Kontext-Param |
|
|
| `dataRef` | 2x (kuerzlich gefixt) | typed Param mit `accepts` matchend |
|
|
| `fieldBuilder` | 2x | OK — bleibt UI-Hint, Output-Schema dynamisch |
|
|
| `attachmentBuilder` | 1x | `List[AttachmentSpec]` (neu) |
|
|
| `condition`, `filterExpression`, `caseList` | 4x | `ConditionExpression` |
|
|
| `cron` | 1x | `CronExpression` |
|
|
|
|
---
|
|
|
|
## Folgerung fuer den Architektur-Plan
|
|
|
|
Diese Audit-Daten bestaetigen den Plan
|
|
[`2026-04-typed-action-architecture.md`](../3-validate/2026-04-typed-action-architecture.md):
|
|
|
|
1. **Schicht 1** (Katalog) muss zuerst um ~13 Schemas erweitert werden.
|
|
2. **Schicht 2** (Methods/Actions) muss `WorkflowActionParameter.type`
|
|
auf Katalog-Typen umstellen — beseitigt automatisch Muster 1
|
|
(`hidden`-Pflicht-Params).
|
|
3. **Schicht 3** (Adapter) ersetzt das parallele
|
|
`frontendType`-Schatten-System durch echte Typen — beseitigt
|
|
Muster 2 (Pseudo-`connectionReference`).
|
|
4. **Outputs typisieren** beseitigt Muster 3 (Output
|
|
`ActionResult`-Black-Box).
|
|
5. **`accepts` strikt** beseitigt Muster 4 (Wildcards).
|
|
|
|
Der Aufwand ist nicht trivial, aber er beseitigt **alle 4
|
|
Defekt-Muster** in einem Refactor — kein einzelner Workaround.
|