Files
Orchestrator/bahn/wissensdatenbank/CHANGELOG.md

21 KiB
Raw Permalink Blame History

Changelog

Alle nennenswerten Aenderungen an dieser Wissensdatenbank werden hier dokumentiert.

Format orientiert sich an Keep a Changelog, Versionierung nach Semantic Versioning (MAJOR.MINOR.PATCH):

  • MAJOR inkompatible Aenderung an Datenmodell/Output-Struktur oder Pipeline-Verhalten.
  • MINOR neue Funktion/Strategie/Quelle, abwaertskompatibel.
  • PATCH Bugfix, Doku, kleine Korrektur.

Die aktuelle Version steht in der Datei VERSION und wird auf der GitLab-Pages-Seite („Changelog") angezeigt.

[Unreleased]

[2.3.0] - 2026-06-30

Added

  • FAQ-Chunking: ein Chunk je Frage/Antwort. Die pathOS-FAQ-Seite wird jetzt pro Q&A in einen eigenen Chunk zerlegt (chunk: faq, chunk_min_chars: 0 -> auch kurze Q&A bleiben eigenstaendig), statt mehrere Q&A in einen gierigen Chunk zu packen. Externe Systeme bekommen damit jede Frage als einzeln abrufbaren Chunk (~90 Chunks).
  • TAF/TAP-TSI Fachliche Dokumentation als Quelle (Confluence-Tree, scope intern) unter dem Tool pathOS.
  • Mehrere Chunk-Konfigurationen pro Ablage-Ort. collect_locations sammelt jetzt je Ort eine Liste von Konfigurationen; chunk_location waehlt pro Dokument die erste passende (_match_opts). Neuer Filter chunk_component (analog chunk_kind): so chunken am selben Ort intern/pathos die Anhang-PDFs weiter via chunk_kind: attachment (headings) UND die FAQ-Seite via chunk_component: faq, ohne sich zu ueberschreiben. Bestehende Chunks ohne chunk_component behalten ihren Fingerprint (kein Re-Chunk-Churn).

[2.2.0] - 2026-06-30

Added

  • PDF-Discovery ueber die Sitemap (Strategie pdf). Eine pdf-Quelle kann statt einer festen .pdf-URL eine Sitemap + Regex bekommen (sitemap_url + url_pattern). Der Connector liest die (gz-)Sitemap, filtert die passenden PDF-URLs und waehlt die neueste Version (hoechste resource/blob/<id>). So kommt z.B. immer die aktuelle Handbuch-Version automatisch rein, ohne die Config bei jeder Neuveroeffentlichung anzupassen. max_pdfs begrenzt die Anzahl.
  • pathOS-Handbuch (Webportal) als Quelle (scope intern+extern): ueber die Sitemap mit Pattern Handbuch-pathOS-Webportal -> aktuell Version 1.01 (Stand 13.03.2026).

[2.1.0] - 2026-06-29

Fixed

  • FAQ-Extraktion (confluence_faq) ordnet Spalten jetzt ueber die Kopfzeile zu. Bisher wurde Spalte 0 als Frage und ALLE weiteren Spalten zusammengeklatscht als Antwort genommen - dadurch landeten Ansprechpartner/Cluster/Thema im Antworttext und die Kopfzeile als Pseudo-FAQ. Neu: benannte Spalten (Frage, Antwort, optional Ansprechpartner/Cluster/Thema) werden korrekt erkannt, die Kopfzeile uebersprungen, Metadaten separat unter der Antwort gefuehrt und Listen/Zeilenumbrueche erhalten - so passen Ueberschrift (### Frage) und Inhalt je Block sauber zusammen (auch fuer das FAQ-Chunking). Faellt ohne benannte Kopfzeile auf Spalte0=Frage/Rest=Antwort zurueck.

Added

  • exclude_pages (Option fuer confluence_tree/page): einzelne Seiten-IDs aus einem Tree ausschliessen, damit sie gezielt anders aufbereitet werden koennen (z.B. als confluence_faq statt als generische Seite).
  • Komponenten-bewusstes inkrementelles Skip: eine confluence_faq-Quelle ueberspringt eine Seite nur dann, wenn bereits ein FAQ-Dokument existiert - nicht, wenn lediglich eine Seiten-Version (aus einem Tree) vorliegt. Zusammen mit exclude_pages wird die pathOS-FAQ (Seite 355496791) damit als Q/A aufbereitet statt als rohe Tabelle.

[2.0.1] - 2026-06-29

Fixed

  • _index.json zaehlte Chunks von gechunkten Anhang-PDFs nicht (chunk_kind: attachment, z.B. pathOS): build_index fuehrte die Chunk-Erkennung nur fuer Voll-Dokumente aus. Anhang-Eintraege bekamen keinen chunks-Block und chunks_total war zu niedrig (Undercount). Jetzt erhalten auch gechunkte Anhaenge ihren chunks-Block und zaehlen in chunks_total sowie by_domain[...].chunks mit. Wirkung auf den echten Bestand: chunks_total 2410 -> 2604 (+194 pathOS-Anhang-Chunks). Regressionstest ergaenzt.

Changed

  • TODO.md: ETL-Sichtung nach v2.0.0-Merge als offenen Punkt ergaenzt (Dateigroesse, Lauf-Check nach output/staging-Umstellung).

[2.0.0] - 2026-06-29

Changed (BREAKING - Output-Struktur)

  • Saubere Trennung in zwei Top-Level-Ordner. Der bisher mit Konsumenten- und internen Artefakten gemischte data/-Ordner ist aufgeteilt:
    • output/ = Konsumenten-Feed. Eine Regel: „lies aus output/, ignoriere alles andere". Enthaelt processed/<scope>/... (Markdown-Feed), chunks/<scope>/... (Chunks), _index.json (Katalog), _meta.json (Status), run_log.jsonl (Lauf-Historie).
    • staging/ = intern, NICHT fuer Konsumenten. Enthaelt pending/<domain>/... (wartet auf manuelle Sichtung) und review_report.json (Audit-Log letzter Lauf).
  • CLI: --data zeigt jetzt auf den Output-Pfad (Default output); neuer --staging-Parameter (Default staging). src.main, src.chunk und src.site unterstuetzen beide.
  • ReviewGate(output_dir, staging_dir): zwei Pfade. Test-Convenience-Form ReviewGate(base) (ein Pfad) leitet staging_dir = base/staging ab.
  • .gitlab-ci.yml: alle Pfade angepasst; knowledge-etl-Artefakt enthaelt nur noch output/processed/. Bot pusht output/, staging/ und docs/.
  • Pfade im _index.json sind weiterhin relativ zum Output-Root (processed/..., chunks/...); Anschliesser lesen sie als output/<path>.
  • PDF-Connector: keep_raw Default True -> False. Aktiviert man es per Quelle, landet das Roh-PDF in staging/raw/<domaene>/ (intern). Wegen incremental: true werden Roh-PDFs ohnehin nur einmal geladen, nicht pro Lauf.
  • Cleanup: 78 Roh-PDFs (~35 MB) aus dem Repo entfernt (INB, KOMBau, pathOS), explizite keep_raw-Zeilen aus config/general.yaml entfernt (Default greift).

Changed (CI-Artefakte schlanker)

  • knowledge-etl-Artefakt: expire_in von 30 auf 7 Tage verkuerzt - der Bot pusht eh nach main, das Artefakt ist nur Fallback ohne GIT_PUSH_TOKEN.
  • pages-Artefakt: expire_in: 7 days ergaenzt (vorher unbegrenzt). Pages nutzt nur das neueste; alte hingen unnoetig herum.
  • Erwartete Wirkung: kumulativer Job-Artifact-Storage ~70-80% weniger.

Fixed

  • Roh-PDFs landeten im Konsumenten-Feed statt intern: set_data_dir setzte das PDF-raw_dir aus dem Output-Pfad ab (output/raw) und ueberschrieb so den korrekten Default staging/raw. Bei keep_raw: true waeren Roh-PDFs in output/ gewandert. set_data_dir(data_dir, staging_dir) trennt jetzt sauber: Roh-PDFs nach staging/raw, die Feed-Wurzel (output) dient nur dem inkrementellen Auffinden bereits verarbeiteter Dokumente (eigenes PdfConnector.data_dir).
  • Defekter „Datei im Repo"-Link auf der Pages-Seite: Der Pfad bekam doppelt ein Prefix (data/output/processed/...), da store den Pfad bereits mit output/ bzw. staging/ liefert. Prefix entfernt -> Links zeigen wieder korrekt ins Repo.
  • Pfad-Prefix bei verschachteltem Layout: staging/-Pfade wurden im Report/Katalog korrekt erkannt, auch wenn staging unter output liegt (Test-Convenience-Form). Erkennung prueft jetzt staging zuerst.

Migration fuer Anschliesser

Aktuell gibt es keinen produktiven Anschliesser. Falls ein Repo-Clone alte data/-Pfade referenziert, einmalig auf output/ umstellen. Frontmatter und Inhalte sind unveraendert.

[1.5.1] - 2026-06-29

Fixed

  • CI-Robustheit gegen transiente Mirror-Ausfaelle: PIP_RETRIES=10 und PIP_DEFAULT_TIMEOUT=90 in .gitlab-ci.yml variables: ergaenzt (gilt fuer ALLE pip-Aufrufe). Hintergrund: am 2026-06-29 12:02 UTC scheiterte der ETL-Lauf an einem HTTP-504 vom Artifactory-PyPI-Mirror beim PyYAML-Download - pip brach nach dem Default-Retry-Budget ab. Mit den neuen Werten werden solche transienten Spikes abgefangen, ohne dass der ETL-Lauf scheitert.

[1.5.0] - 2026-06-29

Added

  • Confluence-Anhaenge: eingebundene PDFs werden geparst (Option A): bei confluence_page/confluence_tree werden im Seitentext eingebundene PDF-Anhaenge (view-file/viewpdf-Makros) ueber den vorhandenen PDF-Parser zu Markdown verarbeitet und als eigene Dokumente im selben Feed-Ordner abgelegt (Frontmatter kind: attachment, parent_url -> Elternseite, attachment_name mit Dateiname; scope/domain/owner werden geerbt). Inkrementell ueber die Attachment-Version, Default an (pro Quelle via options: { attachments: false } abschaltbar). Damit kommt das bisher verlorene Anhang-Wissen rein (z.B. nur „Architekturskizze" + pathOS-Schnittstellen-Doku).
  • Bilder Variante A im Markdown-Konverter: statt Bilder komplett zu verwerfen, bleibt der alt-Text/Dateiname als [Bild: ...] erhalten - gibt dem RAG Kontext ohne Binaerdaten. Datei-Embeds ohne Parsing-Pfad als [Anhang: ...]-Marker.
  • Katalog _index.json mit attachments-Liste pro Seite + Aggregat attachments_total/by_domain.attachments; Dokumente tragen kind im Index.
  • Feinsteuerung Chunking: zwei neue Optionen in config/chunking.yaml (auch pro Quelle ueberschreibbar):
    • chunk_min_doc_chars: Dokumente unter dieser Zeichenzahl bleiben ganz (kleine FAQ-/How-to-Seiten muessen nicht zwingend gechunkt werden).
    • chunk_kind: chunkt nur Dokumente mit passendem kind (z.B. attachment -> nur Anhang-PDFs chunken, Seiten der gleichen Quelle bleiben unangetastet). Bei pathOS eingestellt: chunk: headings, chunk_kind: attachment, chunk_min_doc_chars: 3000 an der Tool-Tree-Quelle - damit werden die grossen Anhang-PDFs (z.B. EVU-Schnittstellen-Doku, 2,8 MB) gechunkt, die Seiten + FAQs bleiben ganz. _CHUNKER_VERSION 3 -> 4 (Defaults im Fingerprint -> sauberer Rebuild betroffener Quellen beim ersten Lauf).

Fixed

  • Connector-Robustheit: UnboundLocalError bei nicht-inkrementellen Confluence-Quellen behoben (entry wird jetzt vor dem Skip-Pfad initialisiert).
  • chunk_kind wirkt jetzt End-to-End: _doc_from_md traegt kind/parent_url/ attachment_name aus dem Frontmatter mit, sodass der chunk_kind-Filter im Offline-Pipeline-Pfad greift (vorher blieb kind="document" als Klassen-Default).
  • Determinismus Anhaenge: bei reinen Tag-/Owner-Aenderungen werden auch Anhang-Dateien re-getagged (vorher driftete deren Frontmatter); last_updated="unknown" als letzter Fallback, wenn weder version.when noch version.number geliefert werden.

Changed

  • CI-Daten-Commit-Message: chore(data): Wissensaktualisierung (automatischer Lauf) (vorher „naechtliche Wissensaktualisierung" - der Job laeuft stuendlich, Mo-Fr 8-17).

[1.4.0] - 2026-06-28

Added

  • Dedup im Review-Gate: Dieselbe Quelle-Seite (per page_identity, i.d.R. Confluence-Page-ID) erzeugt je scope/domaene nur noch ein Dokument im Feed - auch wenn mehrere Quellen sie liefern (z.B. ein breiter confluence_tree UND eine dedizierte confluence_faq-Quelle, oder mehrere ueberlappende Trees). Bei Konkurrenz gewinnt die spezifischere Aufbereitung (FAQ/Q-A schlaegt rohe Seite/Tabelle), sonst die zuerst verarbeitete Quelle. Verhindert die frueheren <slug>-<hash>.md-Duplikate.
  • store.prune_duplicate_files: raeumt Alt-Duplikate (<slug>-<hash>.md, sofern die Basisdatei dieselbe Seite referenziert) auf; laeuft am Ende jedes Voll-ETL-Laufs.

Changed

  • Einmalige Bereinigung: 42 vorhandene Duplikate entfernt (v.a. nur: 3 ueberlappende confluence_tree-Quellen hatten „How to:"-Seiten mehrfach abgelegt; 95 -> 54 Dateien). Verschiedene Seiten mit zufaellig gleichem Titel (andere URL) bleiben erhalten.

[1.3.1] - 2026-06-28

Fixed

  • pathOS „FAQ PathOS Extern" wird wieder erfasst: Die Quelle war als confluence_faq konfiguriert (extrahiert Q/A nur aus Tabellen), die Seite ist aber mit Ueberschriften (h2) + Absaetzen aufgebaut. Dadurch landete das komplette externe FAQ leer in pending. Strategie auf confluence_page umgestellt -> der volle Inhalt kommt in den Feed. (Die interne pathOS-FAQ ist eine echte Q/A-Tabelle und bleibt confluence_faq.) Bekannte Restluecke (TODO): in Confluence angehaengte PDFs/Diagramme werden noch nicht erfasst.

[1.3.0] - 2026-06-28

Added

  • Lauf-Log output/run_log.jsonl (append-only Historie, 1 Zeile je ETL-Lauf): Zeitstempel, verarbeitete Dokumente, Status-Counts und Fehler je Quelle (z.B. fehlgeschlagene Confluence-Abrufe). Gekappt auf die letzten 500 Laeufe. So ist
    • auch historisch - sichtbar, ob ein Lauf sauber durchlief, ohne die fluechtigen GitLab-Job-Logs zu durchsuchen. src/main.py sammelt Fehler je Quelle und schreibt den Eintrag ueber store.append_run_log. Die Uebersichtsseite zeigt in der Fusszeile eine kompakte Health-Zeile („N Dok verarbeitet · M Quellen mit Fehler"), die Chatbot-Anschluss-Seite listet run_log.jsonl in den Manifest-Dateien.

Changed

  • Einfachere Erklaerung auf Uebersicht, Hilfe-Seite und im README: „Was ist ETL?" in drei Schritten (Sammeln → Aufbereiten → Ablegen) und die klare Botschaft, dass dieses Repo die Single Source of Truth ist (hier wird das Wissen gesammelt).

[1.2.0] - 2026-06-28

Added

  • Bestands-Katalog output/_index.json: dokument-genaues Manifest ueber den gesamten freigegebenen Bestand. Pro Voll-Dokument domain/tool/scope, url, path, content_hash, last_updated und - falls vorhanden - die zugehoerigen chunks (Anzahl + Pfad), dazu Aggregate by_domain/by_scope. So sieht ein Anschliesser auf einen Blick, was pro Domaene/Tool wo liegt und welche Dokumente zusaetzlich als Chunks vorliegen (z.B. INB = Voll-Dokument und Chunks). Deterministisch (kein Zeitstempel) -> aendert sich nur bei echten Bestandsaenderungen, kein Git-Churn. Wird am Ende jedes ETL-Laufs (src.main/src.chunk) erzeugt. Doku auf der Chatbot-Anschluss-Seite, in README und architecture.md.

Fixed

  • Robustheit ETL-Lauf: ein einzelnes defektes Dokument bricht den Lauf nicht mehr ab (per-Dokument abgefangen); Chunking ist im ETL ebenfalls gekapselt (nicht kritisch).
  • Ungueltiges Redaction-Regex in filter_rules.json crasht nicht mehr, sondern wird mit Notiz uebersprungen.
  • Deterministische Sitemap-Auswahl: Tiebreaker nach URL bei gleicher Trailing-ID (verhindert zufaellige Reihenfolge/Git-Churn bei gesetztem limit).
  • Chunk-Linking sauber: parent_hash eines Chunks entspricht jetzt exakt dem content_hash des Voll-Dokuments; Chunk-Dateinamen behalten garantiert die .md-Endung.
  • chunk_overlap jetzt wirksam: war zuvor dokumentiert, aber ohne Effekt. Ab jetzt wird bei chunk_overlap > 0 der Tail des Vorgaenger-Chunks vorangestellt (Default 0 -> INB unveraendert). _is_off matcht zudem nicht mehr versehentlich die Zahl 0.

[1.1.0] - 2026-06-28

Added

  • Chunking fuer grosse Dokumente (Default aus, zunaechst INB): Zusaetzlich zur Voll-Datei koennen Dokumente entlang der Markdown-Ueberschriften in Chunks zerlegt werden ein abgeleitetes, jederzeit neu erzeugbares Artefakt unter output/chunks/<scope>/<domaene>/<docslug>/. Das Voll-Dokument in output/processed/ bleibt unangetastet (Parent-Document-Muster). Aktivierung pro Quelle ueber options.chunk (off | headings | faq | recursive); gemeinsame Defaults in config/chunking.yaml, pro Quelle ueberschreibbar. Jeder Chunk traegt im Frontmatter kind: "chunk", parent_url, parent_hash, section, ziffer und einen kurzen Contextual-Retrieval-Vorspann (> Kontext: <Dokument> > <Abschnitt>, deterministisch, ohne Embedding/LLM). Lauf offline via python -m src.chunk --data data; laeuft zudem am Ende jedes ETL-Laufs (src.main) automatisch mit. Inkrementell: pro Dokument wird nur neu gechunkt, wenn sich der Inhalt (parent_hash) ODER die wirksamen Optionen/Strategie (chunk_fingerprint) geaendert haben; unveraenderte Dokumente werden uebersprungen (kein Git-Churn), deaktivierte Orte und verwaiste Chunks automatisch entfernt. Neu: src/transformers/chunker.py, src/chunk.py, config/chunking.yaml. INB 2026 + 2027 auf chunk: headings gestellt. Doku in Hilfe-Seite, README und architecture.md. Robustheit: ueberlange Bloecke ohne Absatztrenner (z.B. OCR-„picture text", grosse Tabellen) werden hart auf chunk_max_tokens begrenzt; eine _CHUNKER_VERSION im Fingerprint erzwingt bei Logik-Aenderungen einen sauberen Rebuild.
  • Inkrementelles Re-Tagging: Jedes Dokument bekommt im Frontmatter einen meta_fingerprint (Hash aus tags/owners/contact). Aendert sich eine dieser Metadaten in tools.yaml/general.yaml, gleicht der naechste ETL-Lauf die bereits vorhandenen Dateien der betroffenen Quelle guenstig in-place an (ohne erneuten Download/API-Call); inhaltlich geaenderte Seiten werden weiterhin neu geladen.
  • Scope-Wechsel inkl. Aufraeumen: Aenderst du den scope einer Quelle, wird die Seite an die neue Ablage (output/processed/<scope>/...) geschrieben und die alte Ablage automatisch entfernt (domaenen-intern, kollisionssicher, erst nach erfolgreichem Schreiben). Domain-Umbenennungen erzeugen die neue Ablage; die alte Domain bleibt bewusst stehen (siehe README) und ist manuell zu entfernen.
  • Globale Metadatei output/_meta.json fuer nachgelagerte Systeme: last_run (Zeitpunkt des letzten ETL-Laufs), last_change (wann sich der Bestand zuletzt inhaltlich/metadatenseitig geaendert hat), documents, by_scope und eine content_signature. So weiss ein RAG-/Index-Consumer, ob/seit wann sich etwas getan hat.
  • Issue-/MR-Template: Hinweis, dass die Domaene final ist (steuert den Ablagepfad, spaetere Umbenennung vermeiden).
  • Uebersicht: dezente Fusszeile mit „Stand: (letzte Aenderung: ...)" aus output/_meta.json.
  • Neue Domaene web: oeffentliche Schienennetz-Seiten via Sitemap (Pattern /web/schienennetz/, ~230 Seiten). Erster Test: Median-Quality 90.
  • Wissensquellen-Seite: pro Quelle ein ✏️-Link (vorbefuelltes Issue zum Anpassen/Loeschen, inkl. Direkt-Edit-Link zur Config-Datei).
  • Hilfe-Seite: Abschnitt „Wissen entfernen / aktualisieren" (per MR oder Schedule).
  • Quality-Score in jeder Listenzeile der Uebersicht sichtbar (farbiges Badge: gruen >=70, orange >=40, rot darunter); im Detail-Modal die transparente Aufschluesselung (Laenge/Woerter/Ueberschriften/Listen/Tabellen).
  • Platzhalter-Tools fuer alle betreuten Anwendungen ohne Quellen (erscheinen auf der Wissensquellen-Seite als „noch keine Quellen").
  • Hilfe-Seite: Transparenz „Filter-Regeln" (aus der Uebersicht hierher ausgelagert).
  • Chatbot-Anschluss-Seite: „Aktualitaet: letzter ETL-Lauf" + Schedule (aus der Uebersicht hierher ausgelagert).

Changed

  • Vereinfachung: pending und rejected zu einem einzigen Status pending zusammengefuehrt. Es gibt kein separates „rejected" mehr alles was nicht approved ist, ist pending (mit Grund + Scope klar sichtbar im Frontmatter, auf der Pages-Seite und im Detail-Modal). Filter-Chip „rejected" entfernt; Statistik-Karte vereinfacht.
  • Scope im Detail-Modal bei pending-Dokumenten prominent sichtbar (eigenes Badge neben dem pending-Badge).
  • Uebersichtsseite entschlackt: Zielbanner ohne ETL-/Link-Zeile; Filter-Regeln und ETL-Lauf-Box ausgelagert (s.o.).
  • Bindestrich-Vereinheitlichung auf den Pages: En-Dash () durch normalen - ersetzt.
  • Sitemap-Strategie: limit ist jetzt optional fehlt es, werden ALLE Treffer des url_pattern geholt (Kundeninfo ohne Limit, vorher 5000).
  • Uebersicht: redundanter CTA-Block „Wissen fehlt?" entfernt (Button im Menu reicht).

[1.0.0] - 2026-06-26

Erste produktive Version: die Pipeline laeuft im CI (Schedule), committet Wissen nach main und veroeffentlicht die GitLab-Pages-Seite.

Added

  • ETL-Grundgeruest: Extract -> Transform -> Auto-Filter -> Freigabe (Merge Request) -> output/processed/<scope>/<domaene>[/<tool>].
  • Strategien: confluence_page, confluence_tree, confluence_faq, crawler, sitemap, pdf, gitlab_md, file; automatische Strategie-Erkennung pro URL.
  • Inkrementelles Laden: Confluence (Versions-Check) und Sitemap/PDF (bekannte URLs).
  • PDF-Feintuning via pymupdf4llm (echte Markdown-Tabellen), z.B. INB 2026/2027.
  • Scope-Modell intern | extern | allgemein | mixed (+ Source-Scope "intern,extern").
  • owners (intern Verantwortliche) und contact (herausgebbare Kontaktadresse, Default einfachbahn@deutschebahn.com) im Frontmatter.
  • Auto-Filter (Sicherheitsnetz): Blacklist, Redaction (pro Quelle abschaltbar), Mindestlaenge; config/approvals.yaml zum nachtraeglichen Freigeben.
  • GitLab Pages (DB-UX): Uebersicht mit Filter (Scope + Domaene) und Suche, Hilfe, Chatbot-Anschluss (inkl. Gesamtbild-Skizze), Wissensquellen-Transparenz.
  • Changelog-Seite + Versionsanzeige in der Navigation (aus CHANGELOG.md/VERSION).
  • Issue-/MR-Vorlagen (Strategie pro Link), CODEOWNERS, docs/SETUP.md, scm-info.yaml, Architektur-Steering mit Diagrammen.

Changed

  • CI: PyPI-Mirror (PIP_INDEX_URL) statt eigenem Deps-Image; ETL-Job nutzt das volle python:3.14-Image (enthaelt git) und den DB-Web-Proxy fuer oeffentliche Quellen.
  • Lint im CI laeuft mit ruff.

Fixed

  • Confluence-Quell-URL aufloesbar (/spaces/<KEY>/pages/<id>/<Titel> statt /pages/<id>).
  • Pages-JS-SyntaxError (Zeilenumbrueche im erzeugten JavaScript) behoben.