Skip to content

⚠️ Veraltet (Stand 2026-04-30)

Diese Dokumentation beschreibt das alte System vor dem Process-Engine-Refactor (Phase 4.7). Die zugrundeliegenden Tabellen (cascade_*, concept_instances/bausteine, crisis_scenarios/events/tasks, workflow_*) wurden gedroppt. Die hier dokumentierten Routen und Models existieren so nicht mehr.

Aktuelle Architektur: Process-Engine API — eine Engine, mehrere Apps (flow.*, concept.*, crisis.*).

Diese Datei bleibt als historische Referenz erhalten.

Kaskaden-Modul — Technische Dokumentation

Ueberblick

Das Kaskaden-Modul ist eine visuelle Kommandozentrale fuer koordinierte Kommunikation. Ein 2D-Topologie-Graph zeigt Knoten (Gruppen/Spaces) und Kanten (Informationsfluss). Jede Kante hat konfigurierbare Linsen: Filter, Form und Gate — plus einen Automatisch/Manuell-Schalter.

Das Modul lebt als eigenstaendiger Hub in der Navigation.


Das Linsen-Prinzip

Jede Verbindung zwischen zwei Knoten hat drei Regler plus einen Auto-Forward-Schalter:

Information kommt rein
        |
   [AUTO/MANUELL]  Automatisch oder per Klick?
        |
   ┌────┴────┐
   | FILTER  |  Was darf durch?
   └────┬────┘
   ┌────┴────┐
   |  FORM   |  Wie wird es formuliert?
   └────┬────┘
   ┌────┴────┐
   |  GATE   |  Wann darf es durch?
   └────┬────┘
        |
Information geht raus

Auto-Forward

ModusVerhalten
autoForward: trueNachrichten fliessen automatisch durch die Kante (via Matrix-Connector Hook)
autoForward: falseManuell per "Weiter"-Button im Chat (Standard)

Filter

ModusVerhalten
allJede Nachricht fliesst weiter (Standard)
markedNur Nachrichten die explizit als "weiterleiten" markiert wurden (nur bei manuell)
keywordNur wenn bestimmte Stichwoerter im Text vorkommen

Form

ModusVerhalten
verbatimExakt wie im Original (Standard)
briefErster Satz + "Weitere Informationen folgen."
formalUmformulierung in offizielle Sprache mit Anrede und Grussformel
clearanceFeste Entwarnungs-Vorlage
customFreitext-Template mit {nachricht} als Platzhalter

Gate

ModusVerhalten
openSofort, automatisch (Standard)
manualJemand muss in der Spalte auf "Weiterleiten" klicken
delayedAutomatisch nach X Minuten (5, 10, 15, 30, 60)
approvalBestimmte Person muss die Nachricht freigeben — mit Bearbeitungsmoeglichkeit

Datenmodell

CascadeBoard

FeldTypBeschreibung
idcuidPrimary Key
tenantIdstringTenant-Zuordnung
spaceIdstringBesitzer-Space
namestringAnzeigename
descriptionstring?Optionale Beschreibung
createdBystringMatrix-User-ID des Erstellers

CascadeColumn (Knoten)

FeldTypBeschreibung
idcuidPrimary Key
boardIdstringZuordnung zum Board
titlestringKnotenname
colorstring?Farbcode
posXintHorizontale Position im Graph (Grid-Einheit)
posYintVertikale Position (0 = oben, hoeher = abhaengig)
filterModestringFilter-Linse
filterKeywordsstring?Komma-getrennte Stichwoerter
formModestringForm-Linse
formTemplatestring?Eigene Vorlage mit
gateModestringGate-Linse
gateDelayMinintVerzoegerung in Minuten
gateApproverstring?Freigabe-Person

CascadeEdge (Kante)

FeldTypBeschreibung
idcuidPrimary Key
boardIdstringZuordnung zum Board
sourceColumnIdstringVon welchem Knoten
targetColumnIdstringZu welchem Knoten
directionstring'one-way', 'two-way', 'confirm'
autoForwardbooleantrue = automatisch, false = manuell (Standard)

CascadeColumn.nodeConfig (JSON)

Jeder Knoten speichert seine Elemente und Design-Informationen als JSON:

typescript
{
  design?: {
    heading?: string;       // Ueberschrift im Player
    body?: string;          // Beschreibungstext
  };
  elements?: ElementConfig[];  // Array von Elementen
}

Jedes Element im Array:

typescript
{
  type: string;             // 'decision' | 'dropdown' | 'checklist' | ...
  question?: string;        // Fragetext / Bezeichnung
  label?: string;           // Label (bei checklist, radio, button, ...)
  options?: Array<{ id: string; label: string }>;  // Fuer dropdown/checklist/radio
  visibleWhen?: { elementIdx: number; branch: string };  // Bedingte Sichtbarkeit
  thenGoTo?: string;        // Knoten-ID bei "Weiter"
  thenGoToYes?: string;     // Knoten-ID bei "Ja" (nur decision)
  thenGoToNo?: string;      // Knoten-ID bei "Nein" (nur decision)
  sourceElement?: number;   // Referenz auf anderes Element (condition)
  sourceSpaceElement?: number;  // Referenz auf createSpace-Element (createTasks, space)
  autoExecute?: boolean;    // Automatisch ausfuehren ohne Klick
  // ... weitere typ-spezifische Felder
}

CascadeColumnSpace

FeldTypBeschreibung
idcuidPrimary Key
columnIdstringZuordnung zum Knoten
spaceIdstringDer verknuepfte Chat-Space

CascadeFlowLog

Protokolliert jeden Schritt jedes Player-Durchlaufs:

FeldTypBeschreibung
idcuidPrimary Key
boardIdstringZuordnung zum Board
sessionIdstringEindeutige Session-ID (pro Player-Durchlauf)
userIdstringMatrix-User-ID
userNamestringAnzeigename des Nutzers
columnIdstringAktueller Knoten
columnTitlestringKnoten-Titel (denormalisiert)
actionstring'start', 'navigate', 'answer_yes', 'answer_no', 'select_option', 'finish'
detailstring?Zusatz-Info (z.B. gewaehlte Option)
createdAtdatetimeZeitstempel

CascadePendingMessage

Nachrichten die am Gate warten (manual, delayed, approval).

CascadeNodeStatus

Live-Status jedes Knotens: idle, active, confirmed, pending.


Auto-Forward-System

Architektur

Nachricht in Space → Matrix Synapse → on_new_event (Python Connector)
    → POST /cascade-forward-hook (Backend)
    → roomId → spaceId aufloesen
    → Alle ausgehenden Kanten mit autoForward=true finden
    → Filter → Form → Gate → Senden an Ziel-Spaces

Matrix-Connector Integration

Der prilog-matrix-connector (Python Synapse-Modul) hat einen on_new_event Hook der bei jeder m.text Nachricht:

  1. Prüeft ob die Nachricht eine Kaskaden-Nachricht ist (org.prilog.cascade Metadata) → wenn ja, ignorieren (Endlosschleifen-Schutz)
  2. cascade_forward() am Backend aufruft (fire-and-forget)
  3. Das Backend loest die Matrix-Room-ID zum Prilog-Space auf
  4. Findet alle Kaskaden-Kanten mit autoForward: true
  5. Wendet Filter/Form/Gate an und sendet an die Ziel-Spaces

Shared Secret Auth

Der Connector authentifiziert sich mit X-Matrix-Connector-Secret Header. Das Secret muss in der Synapse-Homeserver-Config UND in der Backend-.env identisch sein.


Gemeinsamer Text (CollabDocument)

Ueberblick

Echtzeit-kollaborativer Texteditor basierend auf Y.js (CRDT) + Tiptap + WebSocket.

Datenmodell

FeldTypBeschreibung
idcuidPrimary Key
tenantIdstringTenant-Zuordnung
boardIdstring?Kaskaden-Board (optional)
columnIdstring?Kaskaden-Knoten (optional)
spaceIdstring?Space (fuer Space-basierte Dokumente)
titlestringAnzeigename
yjsStatebytes?Y.js Dokument-State als Binary
statusstring'draft', 'sent', 'archived'
createdBystringErsteller

WebSocket-Server

  • Endpoint: GET /api/platform/v1/collab/:documentId/ws?token=<jwt>
  • Protokoll: Y.js Sync + Awareness (MSG_SYNC=0, MSG_AWARENESS=1)
  • In-Memory: Y.Doc pro Dokument, mit Awareness fuer Cursor-Positionen
  • Persistence: Debounced (2s) in DB als yjsState Binary
  • Cleanup: 30s nach letztem Disconnect wird das Dokument aus dem Speicher entfernt

API-Endpoints

EndpointMethodeBeschreibung
/spaces/:spaceId/collab-docPOSTDraft erstellen oder existierenden zurueckgeben
/spaces/:spaceId/collab-docGETAktiven Draft fuer Space holen
/spaces/:spaceId/collab-docsGETAlle Dokumente eines Space listen
/spaces/:spaceId/cascade-targetsGETAusgehende Kaskaden-Kanten fuer diesen Space
/collab-docs/:docId/savePOSTText als .md Dokument im Space-DMS speichern
/collab-docs/:docId/send-to-targetsPOSTAn ausgewaehlte Kaskaden-Ziele senden
/collab-docs/:docId/send-to-spacePOSTDirekt in den eigenen Space senden
/collab-docs/from-document/:documentIdPOSTNeuen Draft aus bestehendem DMS-Dokument erstellen
/collab-docs/:docIdDELETEDraft loeschen

Senden an Kaskaden-Ziele

Der /send-to-targets Endpoint akzeptiert ein Array von Edge-IDs (oder __space__ fuer den eigenen Space). Fuer jede Kaskaden-Kante wird der Text durch die Linsen des Ziel-Knotens geschickt (Filter/Form/Gate). Der Status des Dokuments wechselt auf 'sent'.

Nginx WebSocket Config

Auf Kundenservern (die API ueber den zentralen Server proxyen) muss eine spezielle Location fuer den Collab-WebSocket konfiguriert sein:

nginx
location /api/platform/v1/collab/ {
    proxy_pass https://api.prilog.chat/api/platform/v1/collab/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 24h;
    proxy_send_timeout 24h;
}

Frontend

Hub (/kaskaden)

Eigenstaendiger Hub in der Navigation. Zeigt:

  • Alle Kaskaden-Boards als Karten
  • "Neue Kaskade" mit Vorlagen-Auswahl
  • Klick oeffnet Board in voller Breite

Graph-Ansicht (TopologyCanvas — React Flow)

Basiert auf @xyflow/react v12 (React Flow). Ersetzt den frueheren custom SVG-Canvas:

  • Zoom/Pan: Built-in via React Flow (Mausrad, Trackpad, Touch)
  • MiniMap + Controls: React Flow Controls-Widget unten rechts
  • Edge-Routing: smoothstep Edges mit pathOptions: { borderRadius: 12, offset: 20 }
  • Custom Nodes: CascadeNode Component mit foreignObject fuer volle React-Interaktivitaet
  • Handle-System: Top=Input (Kreis), Bottom=Output (Dreieck), Right=Ja/Nein (Dreiecke)
  • Edge-Interaktion: Klick → rot markiert → nochmal klicken → loescht
  • Undo-System: pushUndo() vor destruktiven Aktionen, Wiederherstellung ueber API

CSS-Hinweis: Tailwind v4 resettet SVG-Styles. Die React-Flow-Edge-Pfade benoetigen !important Overrides in styles.css:

css
.react-flow__edge-path { stroke-width: 2 !important; stroke: ... !important; }

Element-Registry (cascade-elements.tsx)

Alle interaktiven Elemente in Knoten werden ueber eine zentrale Registry definiert. Jeder ElementDef hat:

typescript
interface ElementDef {
  type: string;           // Eindeutiger Typname
  label: string;          // Anzeigename im + Menue
  icon: JSX.Element;      // Icon im + Menue
  color: string;          // Tailwind hover-class
  defaultConfig: () => ElementConfig;
  heightRows: (el: ElementConfig) => number; // Graph-Hoehe
  renderGraph: (props) => JSX.Element;       // Kleine Darstellung in der Box
  renderDesigner: (props) => JSX.Element;    // Editor-Felder im Designer
  renderPlayer: (props) => JSX.Element;      // Vollbild im Player
}

Aktuell 11 Elementtypen: decision, dropdown, checklist, radio, condition, textfield, link, space (oeffnen), createSpace, createTasks, button.

Neuen Typ hinzufuegen: Nur eine Definition in cascade-elements.tsx ergaenzen und zu ELEMENT_TYPES hinzufuegen. Kein anderer Code muss angepasst werden.

Visibility-System: Elemente koennen eine visibleWhen-Eigenschaft haben:

typescript
{ elementIdx: number, branch: 'then' | 'else' | 'yes' | 'no' | optionId }

isElementVisible() prueft zur Laufzeit ob das Element sichtbar ist, basierend auf dem State des referenzierten Elements.

Cross-Element-Referenzen: Das createTasks-Element referenziert ein createSpace-Element ueber sourceSpaceElement (Index im Elements-Array), um die Space-ID des gerade erstellten Space als Ziel zu verwenden.

Designer + Player (cascade-designer.tsx)

Drei Komponenten in einer Datei:

CascadeDesigner: Bearbeitungsansicht fuer einzelne Knoten

  • Heading, Body-Text, Element-Editoren aus der Registry
  • Sichtbar-wenn-Dropdowns und Dann-weiter-zu-Dropdowns pro Element
  • Live-Vorschau des Player-Layouts oben

CascadePlayer: Fullscreen Mobile-First Ablauf-Ansicht

  • Navigiert durch den Graphen ueber navigateFlow()
  • Unterstuetzt thenGoTo / thenGoToYes / thenGoToNo Routing
  • Sendet jeden Schritt als Log-Eintrag an das Backend (fire-and-forget)
  • State wird pro Knoten zurueckgesetzt (setElState({}) bei Navigation)

CascadeFlowLog: Echtzeit-Protokoll aller Durchlaeufe

  • Pollt alle 3 Sekunden GET /cascade-boards/:id/flow-log
  • Gruppiert nach Session (jeder Player-Durchlauf = eine Session)
  • Zeigt Zeitstempel, User, Aktion, Knoten, Detail

Gemeinsamer Text (CollabPanel)

Rechtes Detailpanel mit zwei Modi:

  1. Listen-Ansicht: Gespeicherte Dokumente, "Neu"-Button
  2. Editor-Ansicht: Tiptap v2 + Y.js Collaboration + CollaborationCursor

Verfuegbar in jedem Mitarbeiter-Space (ohne Schueler/Eltern) mit >1 Mitglied. Zugang ueber das "+" Menue im Chat-Eingabefeld.

Wichtig: Tiptap Collaboration v2.27.2 (nicht v3). V3 mischt @tiptap/y-tiptap mit y-prosemirror → Crash. Beide Extensions muessen dieselbe Bridge (y-prosemirror) nutzen.


Dateien

Backend

DateiBeschreibung
src/modules/cascade/cascade-logic.tsFilter/Form/Gate Logik + 8 Krisen-Vorlagen
src/modules/cascade/cascade.router.tsBoard/Column/Edge API + Flow-Log Endpoints
src/modules/cascade/cascade-auto-forward.tsAuto-Forward Engine
src/modules/cascade/cascade-send-helper.tsMatrix-Sende-Helper
src/modules/cascade/collab-ws.router.tsY.js WebSocket Server
src/routes/platform-v1/collab.router.tsSpace-basierte Collab REST-API
src/modules/cascade/index.tsModul-Registrierung

Frontend

DateiBeschreibung
src/features/cascade/cascade-hub.tsxHub-Ansicht (Board-Liste)
src/features/cascade/cascade-panel.tsxBoard-Ansicht mit Graph + Chat + Undo
src/features/cascade/topology-canvas.tsxReact Flow Graph-Editor mit Custom Nodes
src/features/cascade/cascade-elements.tsxElement-Registry (11 Typen, renderGraph/Designer/Player)
src/features/cascade/cascade-designer.tsxDesigner + Player + FlowLog Komponenten
src/features/cascade/collab-editor.tsxCollabPanel + CollabEditor (Y.js + Tiptap v2)