ontology: Ontologie als Quelle im Repo, Abgleich mit n8n/KNOW in der Service-Routine
ci / test (push) Successful in 27s

- 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>
This commit is contained in:
2026-10-13 02:54:49 +02:00
co-authored by Claude Opus 5.5
parent 525b29aa6d
commit 9442cc49d1
29 changed files with 3675 additions and 96 deletions
+32
View File
@@ -144,6 +144,18 @@ healer_state = Table(
Column("updated_at", Float),
)
ontology_decisions = Table(
"ontology_decisions",
metadata,
Column("key", String(300), primary_key=True),
Column("decision", String(20), nullable=False), # accept | map | reject
Column("params", JSON),
Column("decided_by", String(100)),
Column("decided_at", Float),
Column("applied_at", Float),
Column("updated_at", Float),
)
events = Table(
"events",
metadata,
@@ -339,6 +351,26 @@ class Store:
with self.engine.connect() as conn:
return [dict(r._mapping) for r in conn.execute(q)]
# --- Ontologie-Entscheidungen ----------------------------------------------------------
def set_ontology_decision(self, key: str, decision: str, params: dict[str, Any], decided_by: str) -> None:
self._upsert(ontology_decisions, "key", key, {"decision": decision, "params": params,
"decided_by": decided_by, "decided_at": now(),
"applied_at": None})
def list_ontology_decisions(self, *, applied: bool | None = None) -> list[dict[str, Any]]:
q = select(ontology_decisions).order_by(ontology_decisions.c.decided_at)
if applied is True:
q = q.where(ontology_decisions.c.applied_at.is_not(None))
elif applied is False:
q = q.where(ontology_decisions.c.applied_at.is_(None))
with self.engine.connect() as conn:
return [dict(r._mapping) for r in conn.execute(q)]
def mark_ontology_applied(self, keys: list[str]) -> None:
with self.engine.begin() as conn:
conn.execute(update(ontology_decisions).where(ontology_decisions.c.key.in_(keys))
.values(applied_at=now()))
# --- Healer-Zustand --------------------------------------------------------------------
def get_healer_state(self, key: str) -> dict[str, Any] | None:
row = self._get(healer_state, "key", key)