Brainversum · tuxametrics Graph Admin

konzept vertraulich owner: matus

Prozess-Cockpit wieder anschließen — was am 2026-08-13 stillgelegt wurde und was es kostet

Prozess-Cockpit wieder anschließen — was am 2026-08-13 stillgelegt wurde und was es kostet

Status: ERLEDIGT (2026-08-15) — Weg A gebaut, nachdem Zwirn den generischen Hub bekommen hat. · Art: Korrektur einer von außen zugelaufenen Änderung, keine Scope-Entscheidung · Ursache: Zwirn / DOT-ADR-0081

Nachtrag 2026-08-15 — was tatsächlich passiert ist

Die Empfehlung unten lautete „keiner von beiden Wegen jetzt", mit einer klar benannten Bedingung: „Wenn die Authority-Entscheidung fällt, ist Weg A der richtige nächste Schritt." Beide Voraussetzungen sind seither eingetreten, und zwar in dieser Reihenfolge:

  1. Die Authority ist entschieden (2026-08-10, ADR-0005) — damit fiel die Blockade, die Weg A „nicht sinnvoll startbar" machte.
  2. Zwirn hat den generischen Aggregator gebaut (dev.zwirn:zwirn-process-hub-router-spring, Zwirn-ADR-0042). Damit ist der Kern von Weg B ebenfalls da — nur eben in Zwirn, wo er hingehört, und nicht produktlokal hier.

Umgesetzt ist deshalb Weg A auf Basis von Weg B: tuxametrics-process-hub-cockpit ist jetzt zugleich Prozessmodul (zwirn-process-api + zwirn-process-flowable) und Hub (zwirn-process-hub-router-spring), dessen Modul-Registry genau einen Eintrag hat und auf sich selbst zeigt. Der Entartungsfall N=1 wird also gefahren, aber nicht nachgebaut.

Was das konkret geändert hat:

Stelle Änderung
tuxametrics-process-hub-cockpit/pom.xml zwirn-process-hub-flowable-spring raus (brachte Flowables rohe REST-Fassade mit), stattdessen flowable-spring-boot-starter-process (nur Engine) + die drei Zwirn-Prozess-Artefakte + postgresql (war vorher transitiv)
.../config/ProcessModulKonfiguration.java die zwei Pflicht-Ports: OwnedProcessDefinitions (tuxametrics-auswertung) und ProcessModuleAuthorization (reicht das Aufrufer-Token durch, keine Rechteverstärkung)
.../capability/ PlatformCapabilityCheckClient (Laufzeit-Check, Token-Passthrough), PlatformCapabilityAdminClient (Registrierung, Service-Account) und CapabilityRegistrar — der erste Capability-Code dieses Moduls überhaupt
application.yaml Modul-Key auswertung, Capability-Präfix txm.process, Modul-Registry auf http://127.0.0.1:8124
tuxametrics-ui/src/App.tsx, nav/navConfig.ts createProcessHubRoutes({}) gemountet (ohne die zwei Aufgaben-Routen), Sidebar-Section „Prozesse" mit zwei Punkten zurück
vite.config.ts, nginx-locations.conf /flowable-api/process-api/ raus, /api/process-hub/ rein (kein Rewrite nötig)
domains/testfaelle/api/flowableApi.tsprozessApi.ts musste mit — siehe „Der Fund" unten

Vier neue Capability-Schlüssel, alle rein lesend und für Agenten offen ([USER, SERVICE, AGENT]): txm.process.process-module.read, txm.process.definition.read, txm.process.instance.read, txm.process.job.read. Hub- und Modul-Seite teilen sich bewusst ein Präfix — hier sind beide derselbe Prozess, und der Hub reicht das Token des Aufrufers durch; zwei Präfixe hätten acht Katalogeinträge und acht Grants je Praxis für vier Fragen erzeugt, die nie verschieden beantwortet werden können. Begründung im Javadoc von capability/CapabilityRegistrar.

Der Fund, der beinahe ein stiller Ausfall geworden wäre: der Abschnitt „Bewusst nicht angefasst" unten führt die /flowable-api/process-api/-Proxyregeln als konsumentenlos. Das stimmte am 2026-08-13 und nicht mehr am 2026-08-14: die Testfall-UI hatte inzwischen ein Gold-Path-Prozessdiagramm bekommen, das Flowables rohes REST direkt anrief (domains/testfaelle/api/flowableApi.ts). Das Entfernen der Regel hätte das Diagramm ohne Fehlermeldung leer gelassen. Es ist stattdessen auf den Prozess-Vertrag umgestellt worden (prozessApi.ts, fetchDefinitions/fetchDefinitionModel) — und liest damit nebenbei erstmals mandantengescoped und capability-geprüft.

Was der Umbau NICHT mitgemacht hat: den Aufgaben-Lebenszyklus (claim/complete/unclaim). Eigener Eintrag mit Begründung und Auslösebedingung: process-hub-task-lifecycle.md.

Was noch offen ist: die vier Schlüssel sind registriert, aber noch nicht gegrantet. scripts/erste-grants-bootstrap.sql kennt sie nicht — ohne Grant antwortet das Cockpit fail-closed mit 403 (no_grant). Das ist die richtige Fehlerrichtung, aber kein fertiger Zustand.

Der Rest dieses Dokuments bleibt als Herleitung stehen. Der Abschnitt „Strukturelle Ursache, die dieses Repo nicht lösen kann" (file:-Dependency ohne Versionsgrenze) ist von alldem unberührt und gilt unverändert.

Ursprünglicher Status (2026-08-13): Sofortmaßnahme umgesetzt · Priorität: niedrig — das Cockpit hat nie einen Gold-Path-Schritt getragen

Dieses Dokument ergänzt audit-feedback-processhub-integration.md (dort die ursprüngliche Anschluss-Entscheidung) und grenzt an flowable-passthrough-tenant-isolation.md (dort die Sicherheitsfrage desselben Backends).


Was passiert ist

@zwirn/bpmn ist am 2026-08-13 im Zwirn-Repo auf einen engine-agnostischen Prozess-Vertrag umgestellt worden (DOT-ADR-0081, umgesetzt von der Zwirn-Session claude-session-process-hub-router). Der Umbau ist inhaltlich richtig und keine Verschlechterung:

Für tuxametrics hat das eine Folge, die der Umbau selbst nicht auffangen konnte:

Die Gegenstelle, die dieses Repo betreibt, ist nicht mehr die Gegenstelle, die das Paket erwartet.

tuxametrics-process-hub-cockpit serviert Flowables eigene REST-API unter /process-api/**. Einen Process-Hub-Aggregator unter /api/process-hub/v1/tenants gibt es hier nicht — und, das ist der Punkt, auch in Zwirn nicht. Zwirn liefert nur die Modul-Seite des Vertrags (dev.zwirn:zwirn-process-api/api/v1/process-module, /api/v1/tenants/{t}/process-*). Die Hub-Seite existiert bisher als produktlokaler Code in genau einem Repo (dot-process-hub).

Was das für die Routen bedeutet

Seite vor 2026-08-13 seit dem Umbau
ProcessCockpitPage funktionierte (flowableClient + auditClient) kein Backend
ProcessDefinitionDetailPage funktionierte kein Backend
ProcessInstanceDetailPage funktionierte kein Backend
ProcessInstancesPage, ProcessTasksPage, TaskDetailPage nie gemountet (brauchten schon immer den Aggregator) unverändert nicht gemountet

Die ersten drei waren also nicht die Seiten mit dem bekannten Aggregator-Problem — sie liefen gegen ein real vorhandenes Backend. Sie sind erst durch den Umbau dazugekommen.

Was am 2026-08-13 getan wurde

Nach der bereits in diesem Repo etablierten Haltung — „Eine Route, die garantiert 404 liefert, ist schlechter als eine Route, die es nicht gibt" (siehe audit-feedback-processhub-integration.md, Abschnitt „Was das Prozess-Cockpit hier zeigt") — sind die verbliebenen drei Seiten stillgelegt, nicht repariert:

Datei Änderung
tuxametrics-ui/src/App.tsx die 7 <Route>-Elemente der drei Seiten, ihre lazy()-Importe und die PROZESSMODULE-Konstante entfernt; Begründung als Kommentarblock an derselben Stelle
tuxametrics-ui/src/nav/navConfig.ts Sidebar-Section „Prozesse" entfernt — ein Menüpunkt auf eine nicht existierende Route behauptet eine Fähigkeit, das ist dieselbe Vortäuschung eine Ebene höher

Bewusst nicht angefasst:

Ergebnis: npx tsc --noEmit sauber, npx vite build grün. Sichtbare Nebenwirkung: @xyflow/react ist nicht mehr im Bundle — die Dependency, wegen der @zwirn/bpmn überhaupt ein eigenes npm-Paket ist (Zwirn-ADR-0012).


Nebenbefund: zwei Dokumente sind dadurch veraltet

  1. audit-feedback-processhub-integration.md sagt „ProcessModuleInfo[] bleibt leer. Ein Eintrag dort behauptete ein Prozessmodul […] für tuxametrics gäbe es dafür keine Fundstelle." Das stimmte bis 2026-08-12; danach stand dort ein Eintrag (tuxametrics-auswertung), weil inzwischen eine BPMN-Definition deployt wurde. Mit der Stilllegung ist die Aussage wieder wahr, aber aus einem anderen Grund.
  2. flowable-passthrough-tenant-isolation.md stützt sein „heute nicht schlimm" auf zwei Punkte, von denen einer nicht mehr gilt und einer sich verschoben hat:
    • Die Engine ist leer. find . -name "*.bpmn*": null Treffer" — nicht mehr wahr, seit 2026-08-12 liegt tuxametrics-process-hub-cockpit/src/main/resources/processes/tuxametrics-auswertung.bpmn20.xml im Deployment.
    • Der einzige Aufrufer ist das externe Paket @zwirn/bpmn" — dieser Aufrufer ist jetzt weg. Damit hat der schreibfähige, nicht tenant-geprüfte Passthrough /process-api/** null legitime Konsumenten und ist reine Angriffsfläche. Das macht das Entfernen der beiden Proxy-Regeln (Vite + nginx) zur naheliegenden nächsten Sofortmaßnahme — sie ist hier bewusst nicht mitgemacht worden, weil sie die Betriebsform des Service berührt und in jenes Dokument gehört.

Der Wiederanschluss: zwei Wege

Weg A — ein produkteigener Hub in tuxametrics-process-hub-cockpit

tuxametrics hat genau ein Prozessmodul, und es steckt im selben Service, der auch der „Hub" sein soll. Der Aggregations-Teil eines Hubs (Fan-out über N Module, Merge, Paginierung) ist hier also der Entartungsfall N = 1: reines 1:1-Routing.

Schritte:

  1. dev.zwirn:zwirn-process-api + dev.zwirn:zwirn-process-flowable als Dependencies aufnehmen. Der Service hat bereits eine eingebettete Flowable-Engine — er wird damit ein regulärer Prozessmodul-Anbieter (/api/v1/process-module, /api/v1/tenants/{t}/process-*).
  2. Einen dünnen Router davor, der /api/process-hub/v1/tenants/{t}/… auf die eigene Modul-API abbildet und Instanz-/Aufgaben-Ids mit dem Routing-Key präfixt ({moduleKey}::{id}).

Das ist ausdrücklich kein 20-Zeilen-Platzhalter. Der ehrliche Aufwand liegt nicht im Routing, sondern hier: zwirn-process-api schützt jeden Endpunkt mit Zwirns CapabilityGuard, und der ist fail-closed. tuxametrics hat keine entschiedene Authority (capability-authority.md) — ohne registrierte Capabilities ({prefix}.process-module.read, .definition.read, .instance.read, .job.read) antwortet jeder Aufruf mit 403 statt 404. Ein Cockpit, das statt „nicht da" nun „verboten" sagt, ist keine Verbesserung.

Weg A ist damit an dieselbe eine Entscheidung gekettet wie schon vier andere Services. Er wird billig, sobald die Authority entschieden ist — und ist bis dahin nicht sinnvoll startbar.

Weg B — warten auf einen geteilten Hub in Zwirn (zwirn-process-hub-spring)

Der Hub-Baustein liegt heute nur produktlokal in dot-process-hub (~750 Zeilen Kern: Router, Instanz-/Aufgaben-Aggregator, Service-Token-Client). Ihn als wiederverwendbaren Spring-Starter nach Zwirn zu ziehen wäre die strukturelle Lösung.

Einschätzung: sinnvoll, aber noch nicht jetzt — und nicht von hier aus zu entscheiden.

Dafür spricht: Der Hub hat keine Entities und keine eigene Datenbank (dot-process-hubs pom sagt das ausdrücklich). Er besteht damit genau den Aufnahme-Lackmustest, an dem Zwirn-ADR-0019 die Testfallverwaltung als Bibliothek hat scheitern lassen. Und die Vorgeschichte des Nachbarmoduls zwirn-process-hub-flowable-spring ist die Warnung: derselbe Fund wurde erst in DOT gemacht und dann von Hand nach evepop kopiert — exakt der Drift, den ein geteiltes Artefakt verhindert.

Dagegen spricht — und das wiegt heute schwerer: Es gibt eine einzige lauffähige Implementierung. Ihre drei produktspezifischen Nähte sind bekannt (die dot-eigenen Capability-Key-Namen, der dot-platform-service-eigene Token-Broker POST /api/platform/v1/tenants/{t}/service-clients/token, die hub.*-Property-Namen), aber welche davon echte Variationspunkte sind, lässt sich aus einem Sample nicht ablesen. Eine Bibliothek aus einer Implementierung zu extrahieren, friert geratene Nähte ein — und Zwirn hat mit ADR-0018 → ADR-0019 bereits einmal eine zu früh gebaute Bibliothek zurücknehmen müssen.

Und der entscheidende Punkt für tuxametrics: Der eine Konsument, der einen zweiten Beleg liefern könnte, ist dieses Repo — und tuxametrics braucht ausgerechnet den Entartungsfall N = 1, also die Variante, in der der Aggregator gar nichts aggregiert. Die allgemeine Bibliothek am Sonderfall zu entwerfen ist die falsche Reihenfolge.

Auslösebedingung, ab der Weg B gebaut werden sollte: sobald ein zweiter Hub mit echtem Fan-out (≥ 2 Prozessmodule) entsteht — dkfz-prozesswerkstatt führt bereits einen prozesswerkstatt-process-hub im Compose. Dann liegen zwei unabhängige Implementierungen vor und die Nähte sind belegt statt geraten. Die Entscheidung gehört ins Zwirn-Repo (ADR-Charakter) und dort zur Session, die den Zwirn-Teil von DOT-ADR-0081 gebaut hat — nicht hierher.

Empfehlung

Keiner von beiden jetzt. Das Cockpit hat nie einen Gold-Path-Schritt getragen; der Ablauf Patient → Befund → Auswertung → Freigabe läuft weiterhin als linearer REST-Ablauf über den Laborauswertungs-Service. Es gibt keinen fachlichen Verlust, der Dringlichkeit begründet.

Wenn die Authority-Entscheidung fällt, ist Weg A der richtige nächste Schritt — er ist dann klein, er macht tuxametrics-process-hub-cockpit nebenbei vom Sicherheits-Sonderfall (roher Flowable-Passthrough) zu einem regulären, capability-geschützten Prozessmodul, und er liefert Zwirn genau den zweiten Beleg, den Weg B braucht.


Strukturelle Ursache, die dieses Repo nicht lösen kann

Der Bruch kam ohne Vorwarnung an, und das ist kein Versehen der Zwirn-Session:

@zwirn/bpmn wird als file:-Dependency konsumiert (Zwirn-ADR-0009), also direkt aus dem Arbeitsverzeichnis des Nachbar-Repos. Es gibt damit keine Versionsgrenze: eine noch nicht einmal committete Änderung in ../zwirn bricht hier sofort den Build. DOT-ADR-0081 nennt als Gegenmittel ausdrücklich, „das Paket muss die Version mitziehen" — genau das ist bei einer file:-Dependency nicht möglich, das version-Feld wird bei der Auflösung ignoriert.

Das ist eine Zwirn-Entscheidung, nicht hier zu treffen. Es ist aber der Grund, warum ein Konsument solche Umbauten nur nachträglich bemerken kann — und ein Argument dafür, tsc --noEmit über die file:-Pakete weiterhin mitlaufen zu lassen (siehe oben: Dependency bleibt bewusst stehen).