Migrate all repos into monorepo context folders

Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
This commit is contained in:
2026-06-30 20:39:52 +02:00
parent 2f2b295531
commit a5f8fb49ab
1717 changed files with 447332 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
# Knowledge Graph — pathOS Audit
Leichtgewichtige Graph-Datenbank (SQLite) fuer das pathOS Portfolio-Audit.
## Zweck
- Historische Daten und Analysen ausserhalb des Kontextfensters speichern
- Schnelle Kontextsuche ueber Zusammenfassungen
- Beziehungen zwischen Entitaeten (Personen, Teams, Systeme, Issues) abbilden
## Struktur
- `pathos.db` — SQLite-Datenbank mit Graph-Struktur
- `ingest.py` — Script zum Einlesen von Analyse-Dateien
- `query.py` — Kontextsuche-Interface (wird als Skill genutzt)
- `schema.sql` — Datenbankschema
## Tabellen
| Tabelle | Inhalt |
|---------|--------|
| nodes | Entitaeten (Person, Team, System, Dokument, Issue, etc.) |
| edges | Beziehungen zwischen Entitaeten |
| summaries | Kompakte Zusammenfassungen pro Dokument/Thema |
| facts | Einzelne Fakten mit Quelle und Datum |
## Nutzung
```bash
# Daten einlesen
python project-audit/knowledge/ingest.py
# Kontext suchen
python project-audit/knowledge/query.py "Bug-Rate Team 404"
```
+124
View File
@@ -0,0 +1,124 @@
"""
Knowledge Graph DB - Basis-Modul
SQLite-basierte Graph-Datenbank fuer pathOS Audit
"""
import sqlite3
import json
import os
from pathlib import Path
DB_PATH = Path(__file__).parent / "pathos.db"
SCHEMA_PATH = Path(__file__).parent / "schema.sql"
def get_db():
"""Verbindung zur DB herstellen, Schema anlegen falls noetig."""
db = sqlite3.connect(str(DB_PATH))
db.row_factory = sqlite3.Row
db.execute("PRAGMA journal_mode=WAL")
db.execute("PRAGMA foreign_keys=ON")
# Schema anlegen
with open(SCHEMA_PATH, "r", encoding="utf-8") as f:
db.executescript(f.read())
return db
def upsert_node(db, node_id, node_type, name, summary=None, properties=None, source=None):
"""Node einfuegen oder aktualisieren."""
props_json = json.dumps(properties, ensure_ascii=False) if properties else None
db.execute("""
INSERT INTO nodes (id, type, name, summary, properties, source, updated_at)
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(id) DO UPDATE SET
type=excluded.type, name=excluded.name, summary=excluded.summary,
properties=excluded.properties, source=excluded.source, updated_at=datetime('now')
""", (node_id, node_type, name, summary, props_json, source))
def add_edge(db, source_id, target_id, relation, properties=None, source=None):
"""Kante hinzufuegen (Duplikate werden vermieden)."""
props_json = json.dumps(properties, ensure_ascii=False) if properties else None
# Pruefen ob schon existiert
existing = db.execute(
"SELECT id FROM edges WHERE source_id=? AND target_id=? AND relation=?",
(source_id, target_id, relation)
).fetchone()
if existing:
db.execute(
"UPDATE edges SET properties=?, source=?, updated_at=datetime('now') WHERE id=?",
(props_json, source, existing["id"])
)
else:
db.execute(
"INSERT INTO edges (source_id, target_id, relation, properties, source, updated_at) VALUES (?, ?, ?, ?, ?, datetime('now'))",
(source_id, target_id, relation, props_json, source)
)
def upsert_summary(db, summary_id, topic, content, detail_path=None, tags=None):
"""Zusammenfassung einfuegen oder aktualisieren."""
db.execute("""
INSERT INTO summaries (id, topic, content, detail_path, tags, updated_at)
VALUES (?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(id) DO UPDATE SET
topic=excluded.topic, content=excluded.content,
detail_path=excluded.detail_path, tags=excluded.tags, updated_at=datetime('now')
""", (summary_id, topic, content, detail_path, tags))
# FTS aktualisieren
db.execute("INSERT INTO summaries_fts(summaries_fts) VALUES('rebuild')")
def add_fact(db, subject, predicate, value, confidence="confirmed", source=None, date=None):
"""Fakt hinzufuegen."""
db.execute(
"INSERT INTO facts (subject, predicate, value, confidence, source, date, updated_at) VALUES (?, ?, ?, ?, ?, ?, datetime('now'))",
(subject, predicate, value, confidence, source, date)
)
db.execute("INSERT INTO facts_fts(facts_fts) VALUES('rebuild')")
def search_summaries(db, query, limit=5):
"""Volltextsuche in Zusammenfassungen."""
# FTS5 Query escapen: Woerter mit Anfuehrungszeichen schuetzen
safe_query = " ".join(f'"{w}"' for w in query.split())
results = db.execute("""
SELECT s.id, s.topic, s.content, s.detail_path, s.tags
FROM summaries_fts fts
JOIN summaries s ON s.rowid = fts.rowid
WHERE summaries_fts MATCH ?
LIMIT ?
""", (safe_query, limit)).fetchall()
return [dict(r) for r in results]
def search_facts(db, query, limit=10):
"""Volltextsuche in Fakten."""
safe_query = " ".join(f'"{w}"' for w in query.split())
results = db.execute("""
SELECT f.subject, f.predicate, f.value, f.confidence, f.source, f.date
FROM facts_fts fts
JOIN facts f ON f.rowid = fts.rowid
WHERE facts_fts MATCH ?
LIMIT ?
""", (safe_query, limit)).fetchall()
return [dict(r) for r in results]
def get_node_context(db, node_id):
"""Alle Infos zu einem Node + seine Beziehungen."""
node = db.execute("SELECT * FROM nodes WHERE id=?", (node_id,)).fetchone()
if not node:
return None
edges_out = db.execute(
"SELECT e.relation, n.name, n.type, e.properties FROM edges e JOIN nodes n ON n.id=e.target_id WHERE e.source_id=?",
(node_id,)
).fetchall()
edges_in = db.execute(
"SELECT e.relation, n.name, n.type, e.properties FROM edges e JOIN nodes n ON n.id=e.source_id WHERE e.target_id=?",
(node_id,)
).fetchall()
return {
"node": dict(node),
"outgoing": [dict(e) for e in edges_out],
"incoming": [dict(e) for e in edges_in]
}
@@ -0,0 +1,89 @@
# Wissensfragment: BSSUPPORT Jira Dashboard
> Erstellt: 2026-04-30 | Kontext: Andre moechte ein Dashboard fuer gefixt Bugs aus Releases
## Zweck
Dashboard das zeigt welche Kunden-Bugs (BSSUPPORT) in den letzten 2 Kalenderwochen durch Releases auf KTU/Prod gefixt wurden.
## JQL-Abfragen
### Im ReTest (aktuelles Release)
```
project = BSSUPPORT AND issuetype = Bug AND status = ReTest ORDER BY fixVersions DESC, updated DESC
```
### Gefixt in letzten 2 Kalenderwochen
```
project = BSSUPPORT AND issuetype = Bug AND statusCategory = Done AND updated >= startOfWeek(-1w) ORDER BY updated DESC
```
### Bugs mit FixVersion (alle Status)
```
project = BSSUPPORT AND issuetype = Bug AND fixVersions is not EMPTY AND updated >= startOfWeek(-1w) ORDER BY fixVersions DESC, status ASC
```
### Kombiniert (Fertig + ReTest)
```
project = BSSUPPORT AND issuetype = Bug AND (status = ReTest OR (statusCategory = Done AND updated >= startOfWeek(-1w))) ORDER BY status ASC, fixVersions DESC
```
## Spalten-Konfiguration
| Spalte | Jira-Feld | Zweck |
|--------|-----------|-------|
| Key | key | Ticketnummer mit Link |
| Summary | summary | Titel |
| Created | created | Geoeffnet am |
| Resolved | resolved | Geschlossen am |
| Fix Version/s | fixVersions | Release (z.B. R37 04.05 KTU) |
| Status | status | Fertig / ReTest |
| Labels | labels | Team-Zuordnung |
| Assignee | assignee | Bearbeiter |
## Team-Erkennung ueber Labels
| Label | Team |
|-------|------|
| o2c, portal, Portal-UI | Team 404 |
| c2s, ifp | Team CIB |
| ci, CI, Stammdaten | Team Zero |
| ujBau | Uebergreifend (CIB + Zero) |
| Support_Kunde_DBCargo | Cargo-relevant |
| Support_Kunde_DBFernverkehr | FV-relevant |
| Support_Kunde_DBRegio | Regio-relevant |
## Dashboard-Gadgets
| Gadget | Typ | JQL |
|--------|-----|-----|
| Im ReTest | Filter Results | `status = ReTest` |
| Gefixt (2W) | Filter Results | `statusCategory = Done AND updated >= startOfWeek(-1w)` |
| Trend (4W) | Created vs Resolved | `resolved >= startOfWeek(-4w)` |
## Release-Namenskonvention
Format: `R{nr} {dd}.{mm} {Umgebung}`
- Beispiel: R37 04.05 KTU = Release 37, deployed am 04.05 auf KTU
- Umgebungen: KTU (Kundentestumgebung), Prod
## Verlinkung zu Umsetzungstickets
BSSUPPORT-Tickets sind oft verlinkt mit:
- O2CCIB-xxx (CIB Umsetzung)
- O2C404-xxx (404 Umsetzung)
- O2CZERO-xxx (Zero Umsetzung)
- TTTI-xxx (Integrationstest)
Falls ScriptRunner verfuegbar:
```
issueFunction in linkedIssuesOf("project = BSSUPPORT AND issuetype = Bug AND updated >= startOfWeek(-1w)", "is caused by")
```
## Issue-Typen im Projekt BSSUPPORT
- Bug (Fehler im System)
- Serviceanfrage (Kundenfrage/Konfiguration)
- Support (Allgemeine Unterstuetzung)
Nur `Bug` ist relevant fuer das Release-Dashboard.
+167
View File
@@ -0,0 +1,167 @@
"""
Ingest - Liest vorhandene Analyse-Dateien in die Knowledge-DB ein.
Erstellt Nodes, Edges, Summaries und Facts.
"""
import sys
import os
sys.path.insert(0, os.path.dirname(__file__))
from db import get_db, upsert_node, add_edge, upsert_summary, add_fact, DB_PATH
def ingest_teams(db):
"""Teams und Personen aus der Analyse einlesen."""
teams = [
("team-404", "Team 404", "Portal (UI + Middleware)", {"bug_rate_90d": "33%", "bug_rate_12m": "25%", "trend": "steigend", "issues_90d": 500}),
("team-cib", "Team CIB", "Prozesse und Backend (SV, AV)", {"bug_rate_90d": "20%", "bug_rate_12m": "23%", "trend": "sinkend", "issues_90d": 342}),
("team-zero", "Team Zero", "TAF/TAP Schnittstellen, Stammdaten", {"bug_rate_90d": "15%", "bug_rate_12m": "15%", "trend": "stabil", "issues_90d": 417}),
("team-ops", "OPs Squad", "Infrastruktur (seit PI 39, ersetzt STeam)", {"bug_rate_90d": "31%", "bug_rate_12m": "27%", "trend": "sinkend", "issues_90d": 379}),
("team-devops", "DevOps", "CI/CD, Deployment, Enabler", {"bug_rate_90d": "9%", "bug_rate_12m": "7%", "trend": "stabil", "issues_90d": 289}),
]
for tid, name, summary, props in teams:
upsert_node(db, tid, "team", name, summary, props, "jira-teams-deepdive.md")
# Personen
people = [
# 404
("p-ana-cvitkovic", "Ana Cvitkovic", "team-404", "Business Engineer", {"since": "2021-08", "issues_90d": 78, "bug_rate": "niedrig", "status": "aktiv", "bewertung": "TOP"}),
("p-jasmin-keskin", "Jasmin Keskin", "team-404", "Frontend Dev", {"since": "2021-08", "issues_90d": 45, "bug_rate": "42%", "status": "aktiv"}),
("p-emmanuel-kontcheu", "Emmanuel Kontcheu Tagne", "team-404", "Frontend Dev", {"since": "2021-08", "issues_90d": 42, "bug_rate": "69%", "status": "aktiv", "bewertung": "KRITISCH"}),
("p-diego-dacosta", "Diego Da Costa Souza", "team-404", "Frontend Dev", {"since": "2021-09", "issues_90d": 41, "status": "aktiv"}),
("p-leon-hoerpel", "Leon Hoerpel", "team-404", "Dev", {"issues_90d": 35, "bug_rate": "71%", "status": "aktiv", "bewertung": "KRITISCH"}),
("p-dominik-ruecker", "Dominik Ruecker", "team-404", "Backend Dev", {"issues_90d": 31, "status": "aktiv"}),
("p-annette-halbhuber", "Annette Halbhuber", "team-404", "Lead Dev", {"since": "2021-09", "issues_90d": 26, "bug_rate": "niedrig", "status": "aktiv"}),
("p-simon-reitinger", "Simon Reitinger", "team-404", "Dev", {"since": "2025-11", "issues_90d": 15, "bug_rate": "0%", "status": "aktiv", "bewertung": "exzellent"}),
("p-marcel-hufgard", "Marcel Hufgard", "team-404", "PO", {"since": "2021-12", "issues_90d": 8, "status": "aktiv"}),
("p-luca-caracciolo", "Luca Caracciolo", "team-404", "QA", {"since": "2021-09", "issues_90d": 5, "status": "wenig aktiv"}),
# CIB
("p-bishara-jaser", "Bishara Jaser", "team-cib", "Dev", {"since": "2022-08", "issues_90d": 73, "bug_rate": "niedrig", "done_rate": "93%", "status": "aktiv", "bewertung": "TOP"}),
("p-saurav-kumar", "Saurav Kumar", "team-cib", "Dev", {"since": "2022-01", "issues_90d": 48, "bug_rate": "8%", "done_rate": "94%", "status": "aktiv", "bewertung": "TOP"}),
("p-jonas-koehler", "Jonas Koehler", "team-cib", "Dev", {"issues_90d": 34, "done_rate": "100%", "status": "aktiv", "bewertung": "exzellent"}),
("p-dong-won-han", "Dong-Won Han", "team-cib", "Dev", {"since": "2021-09", "issues_90d": 20, "status": "aktiv"}),
("p-christian-meins", "Christian Meins", "team-cib", "PO", {"since": "2021-10", "issues_90d": 9, "status": "aktiv"}),
("p-jan-lubenow", "Jan Lubenow", "team-devops", "Lead Dev", {"since": "2022-09", "issues_90d": 114, "anteil": "39%", "status": "aktiv", "bewertung": "SPOF"}),
# Zero
("p-steven-meixner", "Steven Meixner", "team-zero", "Dev", {"since": "2022-07", "issues_90d": 91, "status": "aktiv", "bewertung": "TOP/SPOF"}),
("p-bing-shi", "Bing Shi", "team-zero", "Dev", {"since": "2022-04", "issues_90d": 63, "status": "aktiv"}),
("p-frank-lemke", "Frank Lemke", "team-zero", "Dev", {"since": "2022-12", "issues_90d": 49, "status": "aktiv"}),
("p-bernd-klebl", "Bernd Klebl", "team-zero", "PO", {"since": "2021-09", "issues_90d": 16, "status": "aktiv"}),
# Uebergreifend
("p-thorsten-volland", "Thorsten Volland", "uebergreifend", "Architekt", {"status": "aktiv"}),
("p-henrik-scholl", "Henrik Scholl", "team-ops", "Dev (fest)", {"since": "2024-10", "issues_90d": 29, "status": "aktiv"}),
]
for pid, name, team, role, props in people:
props["role"] = role
upsert_node(db, pid, "person", name, f"{role} in {team}", props, "personen-rollen.md")
# Edge nur wenn Team als Node existiert
if team.startswith("team-"):
add_edge(db, pid, team, "belongs_to", {"role": role}, "personen-rollen.md")
def ingest_systems(db):
"""Systeme/Komponenten einlesen."""
systems = [
("sys-portal-ui", "Portal UI", "Angular Frontend fuer EVU-Kunden und Kundenbetreuer", {"tech": "Angular, TypeScript", "team": "team-404"}),
("sys-portal-mw", "Portal Middleware", "Backend fuer Portal, Validierung, Autorisierung", {"tech": "Spring Boot", "team": "team-404"}),
("sys-sv", "Steuerung Vertrieb", "Camunda 8 Workflow-Engine, orchestriert Bestellprozess", {"tech": "Camunda 8, Spring Boot", "team": "team-cib", "smells": 2214}),
("sys-av", "Auftrags-Verwaltung", "Speicherung und Bereitstellung von Auftragsdaten", {"tech": "Spring Boot", "team": "team-cib"}),
("sys-ci", "Common Interface", "TAF/TAP TSI Schnittstelle (SOAP)", {"tech": "Spring Boot, SOAP", "team": "team-zero"}),
("sys-stb", "Stammdaten-Bereitstellung", "Import IM-Stammdaten", {"tech": "Spring Boot", "team": "team-zero"}),
("sys-vdv", "Vertragsdaten-Verteiler", "Kafka-basierte Weiterleitung", {"tech": "Spring Boot, Kafka", "team": "team-zero"}),
("sys-ifp-connector", "IFP-Connector", "Kafka-Adapter zu TPN/IFP", {"tech": "Spring Boot, Kafka", "team": "team-zero"}),
]
for sid, name, summary, props in systems:
upsert_node(db, sid, "system", name, summary, props, "runbook")
if "team" in props:
add_edge(db, props["team"], sid, "owns", source="runbook")
def ingest_summaries(db):
"""Zusammenfassungen der Analyse-Dokumente."""
docs = [
("sum-tech-deepdive", "Technischer Deep Dive",
"23 pom.xml analysiert. Spring Boot 3.5.13 (EOL Juni 2026), Upgrade auf 4.0.5 laeuft aktiv. Java 21 in Services, Parent POM noch 17. SV hat eigenen Parent POM. Camunda 8.8.22 aktuell. tadef-connector 0% Coverage (28K Lines).",
"project-audit/analysis/iteration-02-technical-deepdive.md", "technik,spring-boot,dependencies,sonarqube"),
("sum-team-workflow", "Team und Workflow Analyse",
"6 Teams: Zero, 404, CIB, OPs, BSSUPPORT, FbF. STeam aufgeloest in PI 39, ersetzt durch OPs Squad (2 fest + rotierend). 17+ Umgebungen. 5 Engpaesse. 9 Reorganisations-Empfehlungen. Jan Lubenow = 39% DevOps (SPOF).",
"project-audit/analysis/iteration-03-team-workflow.md", "teams,organisation,spof,ops"),
("sum-velocity", "Velocity 12 Monate",
"4875 Issues, ~400/Monat. Go-Live-Effekt: Bug-Rate 10% -> 29% (Feb Peak) -> 20% (Normalisierung). Team 404 Bug-Rate steigt (33% Maerz, KRITISCH). CIB sinkt (36% -> 9%, Erfolg). Zero stabil (15%).",
"project-audit/analysis/velocity-12m-analyse.md", "velocity,bugs,go-live,teams"),
("sum-jira-teams", "Jira Team-Boards",
"1949 Issues (90d). 404: 500 Issues/165 Bugs (33%). CIB: 342/67 (20%). Zero: 417/64 (15%). OPs: 379/116 (31%). DevOps: 289/27 (9%). Top-Contributor: Ana (404, 78), Bishara (CIB, 73), Steven (Zero, 64), Jan (DevOps, 114).",
"project-audit/analysis/jira-teams-deepdive.md", "jira,teams,bugs,personen"),
("sum-sonarqube", "SonarQube Code-Qualitaet",
"74 Projekte. Team Zero = beste Qualitaet (4.2 Smells/1K, 91.8% Coverage). CIB hoechste Schuld (23 Smells/1K, SV mit 2214 Smells). tadef-connector 0% Coverage. Top-Smells: S1192 (1078 String-Duplikate, 976 in SV), S3776 (55 Cognitive Complexity).",
"project-audit/data/sonarqube/", "sonarqube,qualitaet,smells,coverage"),
("sum-ttt-programm", "TTT-Programm und NEP2",
"Go-Live Dez 2025, Go/No-Go Sep 2025 positiv. pathOS seit ~4 Monaten produktiv. Verlaengerte Hypercare. NEP2 stabil (identisch zu NEP1, andere Priorisierung). 8 Programmrisiken (TTTSOL-42 bis -52). Abrechnung strategisches Thema.",
"project-audit/analysis/ttsi-programm-analyse.md", "ttt,go-live,nep2,hypercare,risiken"),
("sum-roadmap-po", "Produkt-Roadmap (PO-Input)",
"18 neue Themen nicht in Roadmap. Top: Vorpruefung Portal, Aenderungswesen, RouteUpdate, Expertenmodus, Fehlermeldungen-Konzept. BO: 6 strategische Fragen (Support, Monitoring, Axt schaerfen, Enablement, Regeln, Fachlichkeits-Loop).",
"project-audit/analysis/po-roadmap-input.md", "roadmap,po,features,bo"),
("sum-personal", "Personal- und Bug-Analyse",
"Tenure korreliert NICHT mit Bug-Rate bei 404 (Emmanuel 56M/69% Bugs vs Simon 5M/0%). CIB+Zero: Erfahrung+Prozesse wirken. SPOFs: Jan Lubenow (39% DevOps), Steven Meixner (91 Issues). Top 5 Massnahmen: Jan entlasten, Portal-Qualitaet, Steven entlasten, Inaktive klaeren, CIB Best Practices teilen.",
"project-audit/analysis/personal-bug-analyse.md", "personal,bugs,spof,tenure,risiko"),
]
for sid, topic, content, path, tags in docs:
upsert_summary(db, sid, topic, content, path, tags)
def ingest_facts(db):
"""Wichtige Einzelfakten."""
facts = [
("pathOS", "go-live", "Dezember 2025", "confirmed", "ttsi-programm-analyse.md", "2025-12"),
("pathOS", "go-no-go", "September 2025, positiv", "confirmed", "ttsi-programm-analyse.md", "2025-09"),
("pathOS", "status", "Produktiv seit ~5 Monaten, verlaengerte Hypercare + NEP2", "confirmed", None, "2026-04"),
("Spring Boot", "version", "3.5.13 (EOL Juni 2026)", "confirmed", "iteration-02-technical-deepdive.md", "2026-04"),
("Spring Boot", "upgrade-status", "4.0.5 Upgrade laeuft aktiv: Portal fertig, CI/SV/AV in Arbeit", "confirmed", "BO-Input", "2026-04"),
("Camunda", "version", "8.8.22", "confirmed", "iteration-02-technical-deepdive.md", "2026-04"),
("Team 404", "bug-rate-90d", "33% (steigend, KRITISCH)", "confirmed", "velocity-12m-analyse.md", "2026-04"),
("Team CIB", "bug-rate-90d", "20% (sinkend von 36%, Erfolgsgeschichte)", "confirmed", "velocity-12m-analyse.md", "2026-04"),
("Team Zero", "bug-rate-90d", "15% (stabil, beste Qualitaet)", "confirmed", "velocity-12m-analyse.md", "2026-04"),
("Jan Lubenow", "spof-risiko", "39% aller DevOps-Issues, kritischster SPOF", "confirmed", "jira-teams-deepdive.md", "2026-04"),
("Steven Meixner", "spof-risiko", "91 Issues (Zero+OPs), Allrounder aber ueberlastet", "confirmed", "jira-teams-deepdive.md", "2026-04"),
("STeam", "status", "Aufgeloest in PI 39, ersetzt durch OPs Squad", "confirmed", "BO-Input", "2025"),
("NEP2", "status", "Stabil, kein Risiko, identisch zu NEP1", "confirmed", "ttsi-programm-analyse.md", "2026-04"),
("SV (Steuerung Vertrieb)", "smells", "2214 SonarQube Smells (hoechste im Programm)", "confirmed", "sonarqube-export", "2026-04"),
("tadef-connector", "coverage", "0% Test-Coverage bei 28K Lines of Code", "confirmed", "sonarqube-export", "2026-04"),
("Gesamtkapazitaet", "throughput", "~400 Issues/Monat ueber alle Teams", "confirmed", "velocity-12m-analyse.md", "2026-04"),
("GitLab", "projekte-gesamt", "167 (109 aktiv, 32 archiviert)", "confirmed", "gitlab-inventory", "2026-04"),
("Confluence", "seiten-analysiert", "143+ im pathOS Space", "confirmed", "confluence-export", "2026-04"),
]
for subject, predicate, value, confidence, source, date in facts:
add_fact(db, subject, predicate, value, confidence, source, date)
def main():
print("=== Knowledge Graph Ingest ===")
db = get_db()
print(" DB initialisiert:", DB_PATH)
print(" Teams und Personen...")
ingest_teams(db)
print(" Systeme...")
ingest_systems(db)
print(" Zusammenfassungen...")
ingest_summaries(db)
print(" Fakten...")
ingest_facts(db)
db.commit()
# Stats
nodes = db.execute("SELECT COUNT(*) as c FROM nodes").fetchone()["c"]
edges = db.execute("SELECT COUNT(*) as c FROM edges").fetchone()["c"]
summaries = db.execute("SELECT COUNT(*) as c FROM summaries").fetchone()["c"]
facts = db.execute("SELECT COUNT(*) as c FROM facts").fetchone()["c"]
print(f"\n Ergebnis: {nodes} Nodes, {edges} Edges, {summaries} Summaries, {facts} Facts")
print(" FERTIG!")
db.close()
if __name__ == "__main__":
main()
+89
View File
@@ -0,0 +1,89 @@
"""
Query - Kontextsuche im Knowledge Graph
Wird als Skill genutzt um relevanten Kontext zu finden.
Usage:
python project-audit/knowledge/query.py "Suchbegriff"
python project-audit/knowledge/query.py --node "team-404"
python project-audit/knowledge/query.py --facts "Spring Boot"
"""
import sys
import os
import json
sys.path.insert(0, os.path.dirname(__file__))
from db import get_db, search_summaries, search_facts, get_node_context
def format_results(summaries, facts, node_context=None):
"""Formatiert Ergebnisse als kompakten Kontext-String."""
output = []
if node_context:
n = node_context["node"]
output.append(f"=== {n['name']} ({n['type']}) ===")
if n["summary"]:
output.append(f" {n['summary']}")
if n["properties"]:
props = json.loads(n["properties"]) if isinstance(n["properties"], str) else n["properties"]
for k, v in props.items():
output.append(f" {k}: {v}")
if node_context["outgoing"]:
output.append(" Beziehungen (ausgehend):")
for e in node_context["outgoing"]:
output.append(f" -> {e['relation']} -> {e['name']} ({e['type']})")
if node_context["incoming"]:
output.append(" Beziehungen (eingehend):")
for e in node_context["incoming"]:
output.append(f" <- {e['relation']} <- {e['name']} ({e['type']})")
output.append("")
if summaries:
output.append("=== Relevante Zusammenfassungen ===")
for s in summaries:
output.append(f"[{s['topic']}] {s['content']}")
if s.get("detail_path"):
output.append(f" -> Detail: {s['detail_path']}")
output.append("")
if facts:
output.append("=== Relevante Fakten ===")
for f in facts:
conf = f" ({f['confidence']})" if f["confidence"] != "confirmed" else ""
date = f" [{f['date']}]" if f.get("date") else ""
output.append(f" {f['subject']}.{f['predicate']} = {f['value']}{conf}{date}")
return "\n".join(output)
def main():
if len(sys.argv) < 2:
print("Usage: python query.py <suchbegriff>")
print(" python query.py --node <node-id>")
print(" python query.py --facts <subject>")
sys.exit(1)
db = get_db()
if sys.argv[1] == "--node" and len(sys.argv) > 2:
node_id = sys.argv[2]
ctx = get_node_context(db, node_id)
if ctx:
print(format_results([], [], ctx))
else:
print(f"Node '{node_id}' nicht gefunden.")
elif sys.argv[1] == "--facts" and len(sys.argv) > 2:
query = " ".join(sys.argv[2:])
facts = search_facts(db, query, limit=15)
print(format_results([], facts))
else:
query = " ".join(sys.argv[1:])
summaries = search_summaries(db, query, limit=5)
facts = search_facts(db, query, limit=10)
print(format_results(summaries, facts))
db.close()
if __name__ == "__main__":
main()
+62
View File
@@ -0,0 +1,62 @@
-- Knowledge Graph Schema fuer pathOS Audit
-- Leichtgewichtig: SQLite mit JSON-Feldern
CREATE TABLE IF NOT EXISTS nodes (
id TEXT PRIMARY KEY,
type TEXT NOT NULL, -- person, team, system, document, issue, metric, process
name TEXT NOT NULL,
summary TEXT, -- Kurzbeschreibung (1-2 Saetze)
properties TEXT, -- JSON mit zusaetzlichen Attributen
source TEXT, -- Woher stammt die Info (Dateiname, URL)
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS edges (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source_id TEXT NOT NULL,
target_id TEXT NOT NULL,
relation TEXT NOT NULL, -- belongs_to, works_on, causes, depends_on, etc.
properties TEXT, -- JSON mit Gewicht, Zeitraum, etc.
source TEXT,
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (source_id) REFERENCES nodes(id),
FOREIGN KEY (target_id) REFERENCES nodes(id)
);
CREATE TABLE IF NOT EXISTS summaries (
id TEXT PRIMARY KEY,
topic TEXT NOT NULL, -- Thema/Kategorie
content TEXT NOT NULL, -- Zusammenfassung (kompakt, fuer Kontextsuche)
detail_path TEXT, -- Pfad zur vollen Datei
tags TEXT, -- Komma-separierte Tags
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS facts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
subject TEXT NOT NULL, -- Worauf bezieht sich der Fakt
predicate TEXT NOT NULL, -- Was wird ausgesagt
value TEXT NOT NULL, -- Der Wert/die Aussage
confidence TEXT DEFAULT 'confirmed', -- confirmed, estimated, outdated
source TEXT,
date TEXT, -- Wann war das aktuell
updated_at TEXT DEFAULT (datetime('now'))
);
-- Indizes fuer schnelle Suche
CREATE INDEX IF NOT EXISTS idx_nodes_type ON nodes(type);
CREATE INDEX IF NOT EXISTS idx_nodes_name ON nodes(name);
CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_id);
CREATE INDEX IF NOT EXISTS idx_edges_target ON edges(target_id);
CREATE INDEX IF NOT EXISTS idx_edges_relation ON edges(relation);
CREATE INDEX IF NOT EXISTS idx_summaries_topic ON summaries(topic);
CREATE INDEX IF NOT EXISTS idx_facts_subject ON facts(subject);
-- FTS (Full-Text-Search) fuer Kontextsuche
CREATE VIRTUAL TABLE IF NOT EXISTS summaries_fts USING fts5(
topic, content, tags, content='summaries', content_rowid='rowid'
);
CREATE VIRTUAL TABLE IF NOT EXISTS facts_fts USING fts5(
subject, predicate, value, content='facts', content_rowid='rowid'
);