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:
@@ -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"
|
||||
```
|
||||
@@ -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.
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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'
|
||||
);
|
||||
Reference in New Issue
Block a user