Files
cluster-knaster/docs/reference/KNOW-befunde.md
rorappandClaude Opus 5.5 9442cc49d1
ci / test (push) Successful in 27s
ontology: Ontologie als Quelle im Repo, Abgleich mit n8n/KNOW in der Service-Routine
- ontology/know.yaml: 49 Klassen, 66 Beziehungen, 12 Vokabulare (72 Konzepte);
  erzeugt: know.ttl (OWL + SKOS), Ansicht aus viewer.html.tmpl
- knaster.ontology: Modell + Prüfung, Export, Momentaufnahme der n8n-Workflows
  (nur GET; KNOW-Spezifikation aus dem Workflow-Code, Aufrufe inkl. Laufzeit-
  Operationen, direktes SQL), Abgleich, Übernahme von Entscheidungen
- Service-Routine Schritt 8: Verstöße als Befunde, Ontologie-Änderungen als
  Vorschläge ontology_change (immer Freigabe); „ordne P-x <konzept> zu“;
  Entscheidungen in ontology_decisions, `knaster ontology apply` schreibt sie
  in die YAML (Version +1)
- CLI: knaster ontology validate|export|snapshot|check|apply; knaster validate
  prüft Modell und Aktualität der Turtle-Datei
- Gegen echten n8n-Export geprüft: neue Befunde B31–B33 (verified/confirmed,
  KAN ruft unbekannte Operation, MPA ordnet Projektereignisse nie ein)
- SPEC-ontology, BETRIEB §6, Router-Image enthält die Ontologie

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:54:49 +02:00

28 KiB
Raw Permalink Blame History

KNOW: Befunde zur Datenhaltung (Analyse vom 13.10.2026)

Grundlage sind die Workflow-Definitionen auf n8n.mindboost.app, nur lesend über die n8n-API abgerufen. Ausgewertet wurde der Code von KNOW, KGW und allen aufrufenden Workflows. B11–B30 stammen aus der Vollanalyse in vier Ebenen:

  1. Schema: Schlüssel, Indizes, Typen, nachträgliche Spalten.
  2. SQL der 80 Operationen: Was wird geschrieben, was bei Konflikten nicht aktualisiert, was nie befüllt.
  3. Abgleich mit den Payloads der Aufrufer.
  4. Datenablagen außerhalb von KNOW: staticData, direkte Verbindungen, LLM-Aufrufe.

Jeder Befund wurde am Code nachgeprüft, Artefakte der automatischen Auswertung sind aussortiert. Inhalte der Datenbank wurden nicht gelesen. Für CANCEL - Unsubscribe Link und KAN-Learn - Kanbau Pattern Learning liefert die API keine gespeicherten Ausführungen, ein Nachweis aus der Laufzeit fehlt deshalb.

Status aller Befunde: offen. In n8n wurde nichts geändert.

# Befund Schwere
B1 CANCEL protokolliert über eine Operation, die es nicht gibt mittel
B2 KAN-Learn speichert über eine Operation, die es nicht gibt mittel
B3 IN-Sync ruft zwei nicht vorhandene Operationen auf (inaktiv) niedrig
B4 CAT schreibt per eigenem SQL an KNOW vorbei niedrig
B5 12 Tabellen ohne aktiven Schreibweg, mit drei Ursachen hoch (Datenbasis unklar)
B6 KAF-Bestätigung führt bei jedem Aufruf DDL aus und setzt einen festen Bestätiger mittel
B7 Anhang-Extraktionen kommen nur von außen niedrig
B8 KGW erlaubt externen Aufrufern eine Lösch-Operation niedrig
B9 Keine Löschfristen für Klartext-Felder mittel (Datenschutz)
B10 Absender-Report zeigt eingefrorene Statistik mittel
B11 Kein einziger Fremdschlüssel; alle Verweise sind freier Text hoch
B12 Zwei Schlüsselschemata für Kontakte → Dubletten, Join findet nichts hoch
B13 Zwei Projekt-Kennungen, projects leer, Projekte kommen live aus KanBau hoch
B14 Zu enge Eindeutigkeit: Ereignisse und Prüfungen werden überschrieben bzw. verworfen hoch
B15 Fehlende Eindeutigkeit: jede Extraktion erzeugt eine neue Zeile mittel
B16 Drei Tabellen ohne Primärschlüssel, doppelte Indizes niedrig
B17 Upserts frieren Fachdaten ein (Kontakte, Ankaufsfälle, Falldokumente) hoch
B18 Gespeicherte abgeleitete Werte veralten (deadline_is_overdue u. a.) mittel
B19 Felder, die nie geschrieben werden (confidence, confirmed_by, verified_at) mittel
B20 Kategorien in mehreren Vokabularen; Safety-Floor-Kategorien über CAT unerreichbar hoch
B21 Postfach und Absender uneinheitlich normalisiert mittel
B22 Zwei Listen für Dokumenttypen niedrig
B23 Sechs Statusfelder rund um den Ankaufsfall mittel
B24 Schema-Drift: 45 nachgezogene Spalten, Laufzeit-DDL, keine Versionierung mittel
B25 Uneinheitliche Typen und Benennung (Geld als Text, Cent vs. Euro, deutsch/englisch) niedrig
B26 Fehlende Indizes auf Such- und Join-Spalten niedrig
B27 Ein Datenbankzugang für alles, geteilt mit dem Chat-Gedächtnis mittel
B28 Zustand in staticData von 9 Workflows, außerhalb von KNOW mittel
B29 Browser-Sessions (Cookies, Header) unverschlüsselt gespeichert hoch (Sicherheit)
B30 LLM-Aufrufe am PSE-LiteLLM vorbei, u. a. Kaufvertragstexte hoch (Datenschutz)
B31 Zwei Wörter für „bestätigt“: verified vs. confirmed niedrig
B32 KAN-Gateway ruft get_kanbau_project_patterns auf, die es nicht gibt hoch
B33 MPA speichert jedes Projektereignis als info/normal hoch

B1 – CANCEL protokolliert ins Leere

  • Beleg: CANCEL - Unsubscribe Link (aktiv) ruft in 4 Knoten (KNOW: Log Erfolg, … Fehler, … Duplikat-Erfolg, … Dry Run) log_workflow_decision auf. Diese Operation steht nicht in der Liste von KNOW, KNOW wirft Nicht erlaubte KNOW operation. Alle 4 Knoten haben „Continue on Fail“.
  • Folge: Der Workflow läuft durch, aber kein Abmeldevorgang wird protokolliert, und niemand merkt es.
  • Vorschlag: auf die vorhandene Operation record_workflow_decision umstellen. Sie schreibt in workflow_decisions, das heute leer bleibt (B5b). Vorher die Felder des Payloads abgleichen.

B2 – KAN-Learn speichert nicht

  • Beleg: KAN-Learn - Kanbau Pattern Learning (aktiv) ruft im Knoten KNOW: Save Patterns upsert_kanbau_project_patterns auf. Die Operation gibt es in KNOW nicht, der Knoten hat keine Fehlerbehandlung.
  • Folge: Jede Ausführung bricht an dieser Stelle ab. KAN-Learn wird von KAN - Kanbau API Gateway aufgerufen.
  • Vorschlag: In der Ausführungsliste von n8n prüfen, ob und wie oft der Workflow läuft. Dann entweder die Operation in KNOW ergänzen (Zieltabelle festlegen) oder den Aufruf entfernen.

B3 – IN-Sync ruft Operationen auf, die fehlen

  • Beleg: IN - Sicherheitsregeln Source Sync (inaktiv) ruft upsert_input_validation_rule und upsert_input_validation_source_snapshot auf. Beide fehlen in KNOW, ebenso eine passende Tabelle.
  • Vorschlag: vor einer Aktivierung in KNOW ergänzen.

B4 – CAT schreibt direkt in workflow_hints

  • Beleg: Ein Postgres-Knoten in CAT - Postfach Pipeline führt UPDATE knowledge.workflow_hints SET notified_at = now(), pending_notification = false WHERE hint_key = '{{ $json.hint_key }}' aus.
  • Folge:
    • KNOW ist nicht mehr der einzige Schreibweg.
    • Der Wert wird ohne Escaping direkt in das SQL eingesetzt. Ein Hochkomma in hint_key bricht die Abfrage.
  • Vorschlag: eine KNOW-Operation dafür anlegen, z. B. mark_hint_notified.

B5 – Tabellen ohne aktiven Schreibweg

Kein aktiver Workflow schreibt diese 12 Tabellen über KNOW. Die Ursachen sind verschieden:

a) KNOW hat gar keine Schreib-Operation. Gefüllt werden können diese Tabellen nur mit direktem Zugang zur Datenbank:

Tabelle Wer liest Bemerkung
projects NEX - Teams Chat Agent, extern über KGW (find_project_candidates, get_sender_profile) Ist die Tabelle leer, findet die Projektsuche nur Treffer über project_aliases. Zuerst prüfen: SELECT count(*), max(last_synced_at) FROM knowledge.projects;
contact_aliases extern über KGW (find_contact_candidates)
contact_project_relations niemand
agent_capabilities, agent_context_documents, agent_execution_log niemand vermutlich Altlast oder außerhalb von n8n genutzt

b) Die Operation gibt es, aber kein Workflow ruft sie auf:

Tabelle vorhandene Operationen Wer liest
property_development_cases upsert_property_development_case, sync_property_development_folder_snapshot, store_kaf_confirmation_snapshot, claim_/complete_kaf_confirmation_create extern über KGW (6 Abfragen für Fristen, Angebote, fehlende Unterlagen)
property_development_documents upsert_property_development_document extern über KGW (summarize_property_development_case)
project_folders upsert_project_folder niemand
place_enrichments upsert_place_enrichment niemand (MAP - Maps/Geo Gateway nutzt KNOW nicht)
workflow_decisions record_workflow_decision niemand (siehe B1)

Die Abfragen zu Ankaufsfällen über KGW (Hermes) liefern also nur Daten, die einmal anders hineingekommen sind. Über KGW selbst dürfen diese Tabellen nicht beschrieben werden.

c) Gewollt: workflow_hint_config wird von setup_schema mit 9 Kategorien befüllt (u. a. mahnung, zahlungsaufforderung, newsletter_werbung) und danach nur noch gelesen.

Nächster Schritt: einmal die Zeilenzahlen aller Tabellen und das jüngste updated_at abfragen. Erst dann ist klar, ob die Tabellen leer sind oder von außerhalb von n8n befüllt werden.

B6 – KAF-Bestätigung: DDL im Anfragepfad, fester Bestätiger

  • Beleg: Ruft jemand upsert_property_development_case mit quality_status: confirmed auf, führt KNOW einen Sonderweg aus (Build KAF Case Confirmation SQL). Dieser Sonderweg macht Folgendes:
    • Er führt bei jedem Aufruf ALTER TABLE … ADD COLUMN IF NOT EXISTS confirmed_by / confirmed_at aus.
    • Er setzt confirmed_by ohne Angabe auf r.rapp@mygodel.de.
  • Folge:
    • Die beiden Spalten fehlen in setup_schema.
    • DDL im Anfragepfad braucht eine exklusive Tabellensperre.
    • Bestätigungen ohne Absender werden einer bestimmten Person zugeschrieben.
    • Heute ruft kein Workflow diesen Weg auf (B5b).
  • Vorschlag: die Spalten in setup_schema aufnehmen, das ALTER TABLE entfernen und confirmed_by zur Pflichtangabe machen.

B7 – document_extractions nur von außen

  • Beleg: store_document_extraction ruft kein n8n-Workflow auf, auch DOC - Dokumenten-Analyse nicht. Geschrieben wird nur extern über KGW.
  • Vorschlag: klären, ob DOC die Extraktionen speichern soll. Sie wären eine gute Quelle für Fälle bzw. Trainingsdaten in knaster.

B8 – Lösch-Operation über KGW

  • Beleg: Die Freigabeliste von KGW enthält purge_spf_aliases, ein DELETE auf project_aliases.
  • Vorschlag: Prüfen, ob externe Aufrufer (Hermes, Skripte) das wirklich brauchen. Sonst streichen.

B9 – Löschfristen

  • Beleg: Folgende Felder speichern Klartext, eine Bereinigung ist nicht erkennbar:

    • llm_jobs.prompt und .result
    • document_extractions.extracted_text
    • property_development_cases.meeting_transcripts
    • agent_context_documents.content
    • mail_events (Absender, Betreff)

    purge_* gibt es nur für Absender-Beobachtungen und Aliase. browser_sessions.session_data ist unverschlüsselt, siehe B29.

  • Vorschlag: Aufbewahrungsfristen festlegen, z. B. llm_jobs nach Abschluss nach 30 Tagen leeren. Zu browser_sessions siehe B29.

B10 – Absender-Report zeigt eingefrorene Statistik

  • Beleg:
    • ASP - Absender-Profil Backfill schreibt die Statistik eines Absenders (mail_count, dominant_category, avg_confidence …) per upsert_contact in contacts.raw_evidence_json.
    • upsert_contact aktualisiert bei einem vorhandenen Kontakt nur display_name, normalized_name, last_seen_at und updated_at, nicht raw_evidence_json.
    • ASP - Absender-Report liest Kategorie und Mail-Anzahl per eigenem SQL aus genau diesem Feld.
  • Folge: Der Report zeigt für jeden Absender die Werte vom ersten Backfill, auch wenn sich die Lage seitdem geändert hat. Die aktuellen Werte stehen in sender_observations, dort schreibt derselbe Backfill per upsert_sender_observation mit.
  • Ursache (ontologisch): Das Absender-Profil gehört zur Klasse Absender, der Kontakt ist das fachliche Gegenüber. Beide sind über die E-Mail-Adresse verbunden. Liegt die Statistik zusätzlich im Kontakt, entsteht eine zweite Quelle, und die veraltet.
  • Vorschlag: Den Report auf sender_observations umstellen und den Kontakt nur noch über email dazuholen. Die Statistik aus upsert_contact herausnehmen.

Vollanalyse: B11–B30

A. Integrität und Schlüssel

B11 – Keine Fremdschlüssel

  • Beleg: setup_schema enthält kein einziges REFERENCES.
    • Alle Verweise (project_key, contact_key, case_key, message_id, kuerzel, mac …) sind freier Text ohne Prüfung.
    • Auch Löschungen (purge_*, reject_project_alias) räumen abhängige Zeilen nicht auf.
  • Folge: Verwaiste und ungültige Verweise sind jederzeit möglich, und es fällt nicht auf. Besonders betroffen ist project_key: Es wird in 7 Tabellen benutzt, aber projects wird nie befüllt (B13). Kein einziger Projektverweis lässt sich gegen eine Projekttabelle prüfen.
  • Vorschlag:
    • Für die Kernbezüge Fremdschlüssel einführen, zuerst NOT VALID, dann nach einer Bereinigung validieren: Falldokument → Ankaufsfall, Alias → Kontakt, Beteiligung → Kontakt und Projekt.
    • Für project_key erst B13 lösen.

B12 – Zwei Schlüsselschemata für Kontakte

  • Beleg:
    • upsert_contact bildet den Schlüssel standardmäßig als normalisierter Name|E-Mail. Kein Aufrufer übergibt einen eigenen Schlüssel.
    • update_sender_relevance legt Kontakte mit dem Schlüssel E-Mail an, mit der E-Mail-Adresse als Anzeigename.
    • find_workflow_hints verknüpft workflow_hints.pattern_key (Absenderadresse) mit contacts.contact_key.
  • Folge:
    • Ein Absender kann zweimal als Kontakt existieren.
    • Der Relevanz-Score landet auf dem Schattenkontakt mit dem E-Mail-Schlüssel.
    • Die vom Backfill angelegten Kontakte („Name|E-Mail“) findet der Join in find_workflow_hints nie.
    • Ändert sich der Anzeigename, entsteht ein neuer Kontakt.
  • Vorschlag: einen Schlüssel festlegen, z. B. die kleingeschriebene E-Mail für Personen und Absender, Namen nur noch als Attribut bzw. Alias. Bestehende Dubletten über email zusammenführen.

B13 – Zwei Projekt-Kennungen, projects leer

  • Beleg:
    • Verweise nutzen project_key.
    • Der Ankaufsfall speichert dagegen selected_project_id, eine project_id aus der KAF-Bestätigung.
    • projects und project_aliases führen zusätzlich kanbau_project_id.
    • Keine KNOW-Operation schreibt projects (B5a).
    • PRO - Project Assignment fragt Projekte live bei mygodel.kanbau.com ab.
  • Folge: Es gibt keine Stelle, an der project_key und die KanBau-ID verlässlich zusammengehören. Lesende Abfragen auf projects (NEX-Teams-Agent, find_project_candidates) finden vermutlich nur Treffer über Aliase.
  • Vorschlag: einen Abgleich KanBau → projects anlegen (z. B. nachts, über das vorhandene KAN-Gateway) mit kanbau_project_id als festem Schlüssel. project_key daraus ableiten oder ersetzen.

B14 – Zu enge Eindeutigkeit

  • project_events: UNIQUE(message_id) mit ON CONFLICT (message_id) DO UPDATE SET project_key, category, summary ….
    • Meldet eine Mail zwei Ereignisse oder betrifft sie zwei Projekte, überschreibt das zweite das erste.
    • Vorschlag: Schlüssel (message_id, project_key, category).
  • kaf_review_snapshots: UNIQUE(case_key) mit Aktualisierung nur bei review_status='pending'.
    • Nach einer Freigabe kann für denselben Fall kein neuer Vertragsstand gespeichert werden. Der Insert läuft ohne Fehler ins Leere.
    • Vorschlag: eine Version pro Prüfung (case_key, version).
  • mail_events: UNIQUE(message_id) ohne Postfach.
    • Das ist unkritisch, solange die Graph-IDs pro Postfach verschieden sind. Bei Internet-Message-IDs würde eine Mail an zwei Postfächer zu einer Zeile.
    • Prüfen, welche ID CAT und MPA übergeben.

B15 – Fehlende Eindeutigkeit bei Extraktionen

  • Beleg: document_extractions hat keinen eindeutigen Schlüssel, store_document_extraction macht ein reines INSERT.
  • Folge: Jede erneute Verarbeitung desselben Anhangs erzeugt eine weitere Zeile, und es ist nicht festgelegt, welche gilt.
  • Vorschlag: UNIQUE(message_id, attachment_id) mit Upsert.

B16 – Tabellen ohne Primärschlüssel

  • Beleg: workflow_hints, workflow_hint_config und kanbau_method_examples haben nur UNIQUE, keinen PRIMARY KEY. Auf hint_key und method_key liegt zusätzlich ein zweiter, überflüssiger Index.
  • Vorschlag: den eindeutigen Schlüssel zum Primärschlüssel machen und die doppelten Indizes entfernen.

B. Aktualität

B17 – Upserts frieren Fachdaten ein

Bei einem vorhandenen Datensatz aktualisieren mehrere Upserts nur wenige Spalten. Alles andere behält den Wert vom ersten Insert:

  • upsert_contact: aktualisiert nur Name, last_seen_at und updated_at. E-Mail, Telefon, Website, Steuer-Nr., USt-IdNr., IBAN-Hash, Adresse, Kontakttyp, Qualitätsstatus und Belege werden nie korrigiert. B10 ist ein Spezialfall davon.
  • upsert_property_development_case: aktualisiert nur Name, die drei Statusfelder, sharepoint_json und raw_evidence_json. Rund 50 Fachfelder bleiben eingefroren, darunter Angebotsfrist, Kaufpreis, Fläche, next_action, next_action_due_at, Risiko, Unterlagen-Flags und Mitschriften.
  • upsert_property_development_document: is_current_version und supersedes_document_key werden nie aktualisiert. Ein altes Dokument bleibt „aktuell“, die Versionierung funktioniert nicht. Ebenso bleiben Zusammenfassung, Absender und Dokumentdatum stehen.
  • Vorschlag: In den Upserts alle fachlichen Felder mit COALESCE(EXCLUDED.x, tabelle.x) aktualisieren. Felder, die bewusst nur einmal gesetzt werden, im Code kennzeichnen.

B18 – Gespeicherte abgeleitete Werte veralten

  • Beleg:
    • deadline_is_overdue ist eine gespeicherte Spalte. find_cases_by_deadline liest sie, sie lässt sich aber aus offer_deadline und dem heutigen Datum berechnen.
    • document_count, missing_documents und detected_documents werden nur von sync_property_development_folder_snapshot gepflegt, und das ruft niemand auf (B5b).
  • Folge: „Überfällig“ und „fehlende Unterlagen“ zeigen den Stand vom Einfügen.
  • Vorschlag: deadline_is_overdue zur Laufzeit berechnen (View oder Ausdruck in der Abfrage). Die Zähler aus property_development_documents ableiten.

B19 – Felder, die nie geschrieben werden

  • workflow_hints.confidence: Keine Operation schreibt das Feld.
    • CAT sendet die Konfidenz, aber innerhalb von suggested_action.
    • find_workflow_hints sortiert nach der Spalte, die immer 0 ist. Die Reihenfolge der Hinweise ist damit zufällig.
  • workflow_hints.confirmed_by: promote_workflow_hint setzt Status und Zeit, aber nicht die Person. Wer einen Hinweis über den Bestätigungslink freigegeben hat, wird nicht gespeichert.
  • contacts.verified_at, project_aliases.verified_at: Keine Operation setzt sie. Das Bestätigungsmodell (quality_status + verified_at) ist nur halb umgesetzt.
  • Vorschlag: confidence aus suggested_action in die Spalte übernehmen (auch rückwirkend). confirmed_by aus dem Bestätigungs-Webhook mitgeben.

C. Vokabular und Normalisierung

B20 – Kategorien in mehreren Vokabularen

  • Beleg: Dieselbe Idee „Kategorie einer Mail“ existiert in mindestens sechs Listen:
    • CAT-Zuordnung: 8 Werte, darunter bauvorhaben und projekt.
    • workflow_hint_config.config_key: 9 Werte, darunter mahnung, zahlungsaufforderung und default.
    • outlook_category: Anzeigenamen in Outlook.
    • sender_observations.dominant_category: vom Absender-Backfill vergeben.
    • project_events.category: 5 feste Werte.
    • mail_events.category: frei. MPA schreibt hier den Workflow-Namen "MPA".
  • Folge:
    • CAT erzeugt bauvorhaben und projekt, die Konfiguration kennt sie nicht, also greift default.
    • Umgekehrt erzeugt CAT nie mahnung oder zahlungsaufforderung. Die Regeln mit Safety-Floor („immer manuell prüfen“) greifen bei Hinweisen aus CAT nie.
    • Auswertungen nach Kategorie mischen fachliche Kategorien mit dem Workflow-Namen „MPA“.
  • Vorschlag:
    • Ein Vokabular als Tabelle bzw. SKOS-Konzeptschema in der Ontologie, mit Abbildung der Outlook-Namen.
    • CAT um Mahnung und Zahlungsaufforderung erweitern.
    • MPA schreibt seinen Status in workflow_status, nicht in category.

B21 – Postfach und Absender uneinheitlich normalisiert

  • Postfach: CAT setzt in store_mail_event fest info@mygodel.de, im CAT-Code kommt aber auch r.rapp@mygodel.de vor. MPA übergibt den grant_key. Der Absender-Backfill schreibt das Postfach kleingeschrieben.
  • Absender: CAT schreibt ihn klein, MPA übergibt ihn unverändert. sender_observations.email ist immer klein.
  • Folge: Die weichen Bezüge Mail ↔ Absender ↔ Kontakt (Ontologie) treffen nur teilweise. Steht ein falsches Postfach fest im Code, werden Mails dem falschen Postfach zugeordnet.
  • Vorschlag: E-Mail-Adressen und Postfächer in KNOW normalisieren (kleinschreiben, Anzeigename abtrennen), nicht in jedem Aufrufer. In CAT das Postfach aus dem Trigger übernehmen.

B22 – Zwei Listen für Dokumenttypen

  • Beleg: property_development_documents.document_type hat 19 feste Werte. document_extractions.document_type ist frei, mit Standardwert unknown.
  • Vorschlag: eine gemeinsame Liste (Ontologie-Klasse Dokumenttyp).

B23 – Sechs Statusfelder rund um den Ankaufsfall

  • Beleg: status (alt), pipeline_status, outcome_status, review_status, confirmation_status und quality_status. Dazu kommt die zweite Bestätigungslogik aus B6, die quality_status='confirmed' setzt, während der Token-Ablauf confirmation_status nutzt.
  • Folge: Es ist nicht festgelegt, welches Feld „bestätigt“ bzw. „abgeschlossen“ bedeutet.
  • Vorschlag: status (alt) aus pipeline_status und outcome_status ableiten oder entfernen. Eine Bestätigungslogik festlegen.

D. Schema-Pflege

B24 – Schema-Drift

  • Beleg:
    • 45 Spalten werden per ALTER TABLE … ADD COLUMN IF NOT EXISTS nachgezogen, davon 41 in property_development_cases. Bei kanzlei_field_patterns doppelt mit unterschiedlichem Standardwert ('[]' bzw. '[]'::jsonb).
    • Dazu kommt Laufzeit-DDL in einer Anfrage (B6).
    • Es gibt keine Schema-Version und keine Migrationshistorie.
  • Folge: Welche Spalten eine Instanz hat, hängt davon ab, wann setup_schema zuletzt lief und ob der KAF-Sonderweg je ausgeführt wurde.
  • Vorschlag: versionierte Migrationen (Tabelle schema_migrations), setup_schema nur noch als Migrationslauf.

B25 – Uneinheitliche Typen und Benennung

  • Geld: als Text (commission_including_vat, godel_offer), Beträge mal in Cent (amount_cents BIGINT), mal in Euro (*_eur NUMERIC).
  • Konfidenz: mal NUMERIC, mal DOUBLE PRECISION.
  • Spaltennamen: deutsch und englisch gemischt (kuerzel, gesellschaft, quelle, bestaetigt, baustelle, absender_typ, mail_art).
  • Zeitstempel: updated_at fehlt u. a. in project_aliases, contact_aliases, document_extractions und mail_events.
  • Vorschlag: bei der nächsten Migration vereinheitlichen. Niedrige Priorität, aber Voraussetzung für generierte Schemas (Ontologie → JSON-Schema).

B26 – Fehlende Indizes

  • Beleg: Ohne Index sind u. a.:
    • contacts.email (Abgleich mit Absendern)
    • mail_events.sender, received_at, category, assigned_project_key (find_mail_events filtert und sortiert danach)
    • document_extractions.message_id und project_key
    • sender_observations.email (get_sender_profile)
    • contact_project_relations
  • Vorschlag: Indizes auf diese Spalten. Unkritisch, solange die Tabellen klein sind.

E. Ablage und Datenfluss außerhalb von KNOW

B27 – Ein Datenbankzugang für alles

  • Beleg: KNOW, die direkten SQL-Knoten in CAT und ASP, der Postgres-Healthcheck und der Credentials-Watchdog nutzen dasselbe Credential „n8n Postgres memory account“. Darüber läuft auch das Chat-Gedächtnis.
  • Folge: Jeder Workflow mit diesem Credential kann jede Tabelle lesen, ändern und löschen. Die Freigabeliste in KNOW und KGW lässt sich so umgehen (B4 ist ein Beispiel).
  • Vorschlag: eigene DB-Rolle für KNOW mit Rechten nur auf knowledge, getrennt vom Chat-Gedächtnis. Direkte SQL-Knoten auf eine Rolle nur mit Leserechten umstellen oder durch KNOW-Operationen ersetzen.

B28 – Zustand in staticData

  • Beleg: 9 Workflows speichern Zustand in n8n-staticData:
    • CANCEL (successful_unsubscribes, wegen B1 die einzige Aufzeichnung der Abmeldungen)
    • BEL (bel_alerts, bel_setup_sent)
    • KJW (nextLink)
    • TDO (manual_enrichment, etag)
    • MCR (last_reported_sha)
    • MPA (Token-Cache, verschlüsselt, in Ordnung)
    • ASP-Scheduler, KBT und KBJ (inaktiv)
  • Folge: Dieser Zustand ist in KNOW unsichtbar, hat keine Historie und geht bei einem Neuimport des Workflows verloren.
  • Vorschlag: Fachlich relevante Zustände (CANCEL, BEL) nach KNOW verlagern. Technische Cursor (KJW, MCR) dürfen bleiben.

B29 – Browser-Sessions unverschlüsselt

  • Beleg: BSB - Execute Login speichert session_data: { cookies, headers } über upsert_browser_session als JSON im Klartext.
  • Folge: Gültige Sitzungs-Cookies (u. a. für KanBau) liegen lesbar in der Datenbank. Jeder mit dem gemeinsamen Credential (B27) kann sie übernehmen. Zum Vergleich: Die Postfach-Tokens sind mit AES-256-GCM verschlüsselt, auch im Cache.
  • Vorschlag: session_data genauso verschlüsseln wie mailbox_grants. Die Lebensdauer kurz halten (Standard 60 Min. ist gut).

B30 – LLM-Aufrufe am PSE-LiteLLM vorbei

  • Beleg (direkte HTTP-Aufrufe, sicher am LiteLLM vorbei):
    • KAF - Kaufvertrag Erkennung und Extraktion (aktiv) schickt den Kaufvertragstext (pdf_text) direkt an openrouter.ai, ohne PSE-Aufruf.
    • Ebenso direkt gehen KAN - Kanbau API Gateway (Intent-Erkennung), BSB - Discover Session (Login-Seiten an api.anthropic.com), CDW (Prüfaufruf) und KSUM (inaktiv).
  • Nicht entscheidbar: Die OpenAI-Knoten in ASP, DOC, MAIL, CAT und OR nutzen das Credential openAiApi. Ob es auf den LiteLLM mit PSE zeigt, ist ohne Einsicht in das Credential nicht erkennbar. ASP ruft dabei kein PSE auf.
  • Folge: Widerspricht der Vorgabe im Aufbauplan („Starke Modelle nur über den LiteLLM mit PSE“). Kaufverträge enthalten Namen, Anschriften und Preise.
  • Vorschlag: KAF, KAN und BSB auf den LiteLLM mit PSE umstellen. Das Ziel des Credentials openAiApi prüfen.

Geprüft, ohne Befund

  • Postfach-Tokens (mailbox_grants) und der Token-Cache in MPA sind mit AES-256-GCM verschlüsselt.
  • KNOW maskiert alle Werte über sqlString/sqlJson. Bei den Operationen gibt es keine offene String-Verkettung. Die Ausnahme ist der direkte SQL-Knoten in CAT (B4).
  • KGW prüft HMAC und Zeitfenster und lässt nur eine feste Liste von Operationen zu.
  • Die vermeintlich fehlende Spalte workflow_decisions.decided_by existiert (per ALTER ergänzt).

Gefunden mit knaster ontology check (13.10.)

Diese Befunde hat der automatische Abgleich gefunden, nachdem die Ontologie im Repo als Quelle angelegt war (SPEC-ontology).

B31 – Zwei Wörter für „bestätigt“

  • Beleg: record_workflow_decision setzt quality_status auf verified. Alle anderen Tabellen und Operationen nutzen confirmed.
  • Folge: Auswertungen über quality_status = 'confirmed' übersehen bestätigte Entscheidungen.
  • Stand in der Ontologie: verified ist als alternative Schreibweise von confirmed im Vokabular Qualitätsstatus eingetragen. Vorschlag: in KNOW auf confirmed vereinheitlichen.

B32 – KAN-Gateway ruft eine Operation auf, die es nicht gibt

  • Beleg: KAN - Kanbau API Gateway (aktiv) ruft get_kanbau_project_patterns auf. Die Operation steht nicht in der Liste von KNOW. Das Gegenstück upsert_kanbau_project_patterns in KAN-Learn fehlt ebenso (B2).
  • Folge: Gelernte Projektmuster für KanBau werden weder gespeichert noch gelesen. Je nach Fehlerbehandlung des Knotens bricht das Gateway an dieser Stelle ab.
  • Vorschlag: die beiden Operationen samt Zieltabelle in KNOW anlegen (und dann in der Ontologie aufnehmen) oder die Aufrufe entfernen.

B33 – Projektereignisse ohne Einordnung

  • Beleg: MPA - Projekt-Ablage ruft upsert_project_event mit den festen Werten category: "info" und urgency: "normal" auf.
  • Folge: Die Ereignisarten verzoegerung, mangel, termin und kosten (CHECK-Constraint der Tabelle) kommen nie vor, ebenso keine Dringlichkeit high. Eine Auswertung „Mängel pro Projekt“ ist mit diesen Daten nicht möglich.
  • Vorschlag: Kategorie und Dringlichkeit aus der Mail-Analyse übernehmen (MAIL bzw. CAT kennen „Mangel“) oder ein kleines Modell bzw. einen knaster-Skill einordnen lassen.