wiki/z-archive/c-work/2026-04-node-typization-audit.md
ValueOn AG 24190f532a fixes
2026-04-26 08:36:33 +02:00

12 KiB

Node-Typisierungs-Audit (Stand 2026-04-24)

ARCHIVIERT 2026-04-25. Die 4 Defekt-Muster sind durch die Typed Action Architecture und ihre Folge-Schritte 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:

{"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:

{"name": "target", "type": "FeatureInstanceRef[trustee]", "required": True,
 "ui": {"label": "Trustee-System"}},

mit neuem Katalog-Typ in portTypes.py:

"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:

{"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:

{"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:

  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.