Der ursprüngliche Plan:

# Plan: CI-Pipeline fuer den Knowledge Manager

## 1. Aufgabenstellung

Der **Knowledge Manager** (`DCCT/knowledge_manager`) ist ein Python-Projekt, das Inhalte aus
Confluence extrahiert, transformiert und in Cognigy Knowledge Stores hochlaedt.
Das Projekt verfuegt ueber eine umfangreiche pytest-Testsuite, aber bisher gibt es
**keine automatisierte CI-Pipeline**, die diese Tests ausfuehrt.

### Anforderungen

| Nr. | Anforderung | Ausloeser |
|-----|-------------|-----------|
| 1 | Tests laufen automatisch bei **jedem Code-Push** auf einen beliebigen Branch (z.B. Feature-Branch) | `git push origin feature/xyz` |
| 2 | Tests laufen automatisch wenn ein **PR gegen `master` geoeffnet** wird | PR-Erstellung in Azure DevOps |
| 3 | Tests laufen automatisch wenn ein **PR in `master` gemerged** wird | Merge/Push auf `master` |

Zusaetzlich soll **Linting mit `ruff`** als separater Schritt ausgefuehrt werden.
Wenn Linting fehlschlaegt, sollen die Tests trotzdem weiterlaufen.

### Abgrenzung

- Kein Deployment, kein Packaging, keine Artefakte
- Keine SAST-Scans oder Dependency-Checks (nur fuer Deployment-Pipelines relevant)
- Keine Benachrichtigungen in der Pipeline (werden zentral in Azure DevOps konfiguriert)

---

## 2. Technischer Kontext

### Projekt-Steckbrief Knowledge Manager

| Eigenschaft | Wert |
|-------------|------|
| Repository | `DCCT/knowledge_manager` (Azure DevOps) |
| Sprache | Python 3.13 (`pyproject.toml`: `requires-python = ">=3.13"`) |
| Package Manager | UV (Installation per `curl` in Pipeline) |
| Test-Framework | pytest + pytest-cov + pytest-mock |
| Linter | ruff (in `[dependency-groups] dev`) |
| Lock-File | `uv.lock` vorhanden |
| Test-Verzeichnis | `tests/` mit Unterordnern pro Modul |
| Externe Abhaengigkeiten fuer Tests | Keine - alle Tests sind gemockt |

### Test-Struktur

tests/ ├── confluence_extractor/ # Extractor-Modul Tests │ ├── conftest.py # Shared Fixtures (Mock-Client, Testdaten) │ ├── test_base_extractor.py │ ├── test_batch_processor.py │ ├── test_converter.py │ ├── test_hierarchy_handler.py │ ├── test_sync_manager.py │ └── test_integration.py ├── markdown_transformer/ # Transformer-Modul Tests ├── cognigy_uploader/ # Uploader-Modul Tests ├── shared/ # Shared-Utils Tests ├── streamlit_components/ # UI-Komponenten Tests └── test_full_pipeline.py # Pipeline-Integrationstest


---

## 3. Rahmenbedingungen (aus Abstimmung mit CI/CD-Experten)

| Thema | Vorgabe | Auswirkung |
|-------|---------|------------|
| **CDOS-Template** | Pflicht: `extends: pipeline.yml@Templates` | Pipeline muss das CDOS-Template nutzen, mit `securityZone: dev` |
| **Build Service Rechte** | Build Service muss Reader in `CDOS/azure-devops-pipeline` sein | Sonst schlaegt `extends: template` fehl |
| **UV** | Nicht vorinstalliert, muss per `curl` installiert werden | Installation via `curl -LsSf <https://astral.sh/uv/install.sh> \\| sh` |
| **Internetzugang** | Agents haben Zugang zu `astral.sh` | UV-Installation und Python-Download funktionieren |
| **Python 3.13** | Via `uv python install 3.13` | UV laedt Python automatisch herunter |
| **Agent Pool** | `$(AGENT_POOL)` (Self-Hosted, persistenter Workspace) | Kein explizites Caching noetig |
| **Dateipfad** | `.azdo/ci.yaml` | Entspricht TeamBank-Konvention (semantischer Name fuer CI-Pipeline) |
| **Branch Policy** | Team richtet Build Validation selbst ein (manuell oder via Terraform) | Siehe Abschnitt 5.2 und 5.3 |
| **Linting** | `ruff check` als separater Step, Tests laufen trotzdem weiter | `continueOnError: true` auf Linting-Step |
| **Benachrichtigungen** | Zentral konfiguriert | Nichts in Pipeline noetig |
| **Compliance** | Keine fuer Test-Pipelines | Kein SAST/Dependency-Check noetig |

> **Hinweis zu UV:** Die CDOS-Templates (`azure-devops-pipeline`) enthalten keine
> UV-spezifischen Steps - nur pip/venv/conda-basierte Steps (`Steps/python/create_venv.yml`,
> `Steps/python/requirements_install.yml`). Da das Knowledge Manager Projekt `pyproject.toml`
> und `uv.lock` nutzt, verwenden wir UV direkt.
>
> **Bestaetigt durch Senior-Entwickler:** UV kann per `curl` von `astral.sh` installiert
> werden. Dieses Pattern wird bereits in der `export.yaml` Pipeline verwendet
> (Zeilen 209-224 in `cognigy-pipelines/.azdo/knowledgebases/export.yaml`).

---

## 4. Loesungsweg

### 4.1 Neue Datei erstellen

**Pfad:** `DCCT/knowledge_manager/.azdo/ci.yaml`

> **Referenz zur Konvention:** Die bestehenden Pipelines im Workspace liegen ebenfalls
> unter `.azdo/` (siehe `demo-chatbot-cognigy-version/.azdo/snapshots/create.yaml`,
> `.azdo/knowledgebases/export.yaml`, etc.).

### 4.2 Trigger-Konfiguration

```yaml
trigger:
  branches:
    include:
      - '*'

pr:
  branches:
    include:
      - master

Erklaerung:

Trigger YAML-Schluessel Was passiert
Push auf beliebigen Branch trigger: branches: include: ['*'] Deckt Anforderung 1 (Feature-Branch Push) und Anforderung 3 (Merge in master) ab
PR gegen master pr: branches: include: [master] Deckt Anforderung 2 ab

Hinweis: In Azure DevOps ist trigger der sogenannte CI-Trigger - er reagiert auf jeden git push. Wenn ein PR in master gemerged wird, ist das technisch ein Push auf master, daher deckt trigger: include: ['*'] sowohl Feature-Branch-Pushes als auch Merges ab.

Der pr-Trigger ist der PR-Validation-Trigger - er reagiert speziell auf das Oeffnen/Aktualisieren eines Pull Requests.

4.3 CDOS-Template Einbindung

resources:
  repositories:
    - repository: Templates
      type: git
      name: CDOS/azure-devops-pipeline
      ref: refs/tags/v4

extends:
  template: pipeline.yml@Templates
  parameters:
    securityZone: dev
    stages:
      - stage: test
        ...

Referenz: Exakt dasselbe Pattern wird in der bestehenden Knowledge-Export-Pipeline verwendet: cognigy-pipelines/.azdo/knowledgebases/export.yaml (Zeilen 2-7 fuer resources, Zeilen 46-48 fuer extends).

Der einzige Unterschied: Die Export-Pipeline nutzt securityZone: ${{ parameters.securityZone }} (parametrisiert), wir verwenden fest securityZone: dev, da unsere Test-Pipeline immer in der Dev-Zone laeuft.

4.4 Setup-Step: Python & Dependencies

- bash: |
    set -eu
    set -o pipefail

    # UV installieren (nicht vorinstalliert auf Agents)
    curl -LsSf <https://astral.sh/uv/install.sh> | sh
    source "$HOME/.local/bin/env"
    uv --version

    # Python 3.13 sicherstellen
    uv python install 3.13
    python3 --version

    # Alle Dependencies installieren (inkl. dev-Gruppe fuer ruff)
    uv sync --frozen --python 3.13

    # Virtual Environment fuer nachfolgende Steps verfuegbar machen
    echo "##vso[task.prependpath]$(pwd)/.venv/bin"
    echo "##vso[task.setvariable variable=VIRTUAL_ENV]$(pwd)/.venv"
  displayName: Setup Python and install dependencies

Erklaerung der einzelnen Befehle:

Befehl Zweck
set -eu / set -o pipefail Script bricht bei Fehlern sofort ab (Best Practice)
curl -LsSf ... \| sh Laedt und installiert UV vom offiziellen Installer. -L folgt Redirects, -sS zeigt nur Fehler, -f bricht bei HTTP-Fehlern ab
source "$HOME/.local/bin/env" Laedt die UV-Umgebungsvariablen, damit uv im PATH ist
uv python install 3.13 Laedt Python 3.13 herunter und installiert es lokal
uv sync --frozen --python 3.13 Installiert alle Dependencies aus uv.lock - reproduzierbar und deterministisch. Ohne --no-dev, damit auch ruff (dev-Gruppe) installiert wird
##vso[task.prependpath] Azure DevOps Logging-Command: Fuegt .venv/bin zum PATH hinzu, damit nachfolgende Steps pytest und ruff direkt aufrufen koennen
##vso[task.setvariable] Setzt VIRTUAL_ENV Environment-Variable fuer nachfolgende Steps

Referenz: Exakt dasselbe Pattern findet sich in: cognigy-pipelines/.azdo/knowledgebases/export.yaml (Zeilen 204-224).

Einziger Unterschied zur Referenz: Die Export-Pipeline nutzt --no-dev (da sie kein ruff braucht). Wir verwenden uv sync --frozen --python 3.13 ohne --no-dev, damit ruff aus der dev-Gruppe mitinstalliert wird.

Bestaetigt durch Senior-Entwickler: Die UV-Installation per curl funktioniert auf den Build-Agents. Dies ist das etablierte Pattern bei DCCT.

4.5 Linting-Step: ruff

- bash: |
    set -eu
    set -o pipefail

    echo "Running ruff linter..."
    uv run ruff check src/ tests/
  displayName: Lint with ruff
  continueOnError: true

Wichtig: continueOnError: true sorgt dafuer, dass die Pipeline bei Linting-Fehlern nicht abbricht. Der Step wird als "Warning" (orange) angezeigt, aber die nachfolgenden Test-Steps laufen trotzdem weiter. Dies entspricht der Vorgabe des CI/CD-Experten.

4.6 Test-Step: pytest

- bash: |
    set -eu
    set -o pipefail

    uv run pytest \\
      --tb=short \\
      --junitxml=$(Build.ArtifactStagingDirectory)/test-results.xml \\
      --cov=src \\
      --cov-report=term-missing
  displayName: Run tests with pytest
Flag Zweck
--tb=short Kurze Tracebacks bei Fehlern (uebersichtlicher im Build-Log)
--junitxml=... Erzeugt JUnit-XML-Report fuer Azure DevOps Test-Tab
--cov=src Misst Code-Coverage ueber das gesamte src/-Verzeichnis
--cov-report=term-missing Zeigt im Log, welche Zeilen nicht durch Tests abgedeckt sind

4.7 Test-Ergebnisse veroeffentlichen

- task: PublishTestResults@2
  condition: succeededOrFailed()
  inputs:
    testResultsFormat: JUnit
    testResultsFiles: $(Build.ArtifactStagingDirectory)/test-results.xml
    testRunTitle: Knowledge Manager Tests

Warum condition: succeededOrFailed()? Standardmaessig werden nachfolgende Steps bei einem Fehler uebersprungen. Mit succeededOrFailed() wird der Test-Report auch bei fehlgeschlagenen Tests veroeffentlicht. So sieht man in Azure DevOps immer, welche Tests genau fehlgeschlagen sind - auch wenn der Build rot ist.

Der Test-Report erscheint dann im Azure DevOps Build unter dem "Tests"-Tab.

4.8 Vollstaendige Pipeline-Datei

---
trigger:
  branches:
    include:
      - '*'

pr:
  branches:
    include:
      - master

resources:
  repositories:
    - repository: Templates
      type: git
      name: CDOS/azure-devops-pipeline
      ref: refs/tags/v4

pool: $(AGENT_POOL)

name: $(Build.BuildId)

extends:
  template: pipeline.yml@Templates
  parameters:
    securityZone: dev
    stages:
      - stage: test
        displayName: Lint and Test
        jobs:
          - job: lint_and_test
            displayName: Lint and Test Knowledge Manager
            steps:
              - checkout: self
                fetchDepth: 1
                clean: true

              - bash: |
                  set -eu
                  set -o pipefail

                  # UV installieren
                  curl -LsSf <https://astral.sh/uv/install.sh> | sh
                  source "$HOME/.local/bin/env"
                  uv --version

                  # Python 3.13 installieren
                  uv python install 3.13
                  python3 --version

                  # Dependencies installieren (inkl. dev-Gruppe fuer ruff)
                  uv sync --frozen --python 3.13

                  # Virtual Environment fuer nachfolgende Steps verfuegbar machen
                  echo "##vso[task.prependpath]$(pwd)/.venv/bin"
                  echo "##vso[task.setvariable variable=VIRTUAL_ENV]$(pwd)/.venv"
                displayName: Setup Python and install dependencies

              - bash: |
                  set -eu
                  set -o pipefail

                  echo "Running ruff linter..."
                  uv run ruff check src/ tests/
                displayName: Lint with ruff
                continueOnError: true

              - bash: |
                  set -eu
                  set -o pipefail

                  uv run pytest \\
                    --tb=short \\
                    --junitxml=$(Build.ArtifactStagingDirectory)/test-results.xml \\
                    --cov=src \\
                    --cov-report=term-missing
                displayName: Run tests with pytest

              - task: PublishTestResults@2
                condition: succeededOrFailed()
                inputs:
                  testResultsFormat: JUnit
                  testResultsFiles: $(Build.ArtifactStagingDirectory)/test-results.xml
                  testRunTitle: Knowledge Manager Tests

5. Manuelle Schritte nach der Umsetzung

5.1 Pipeline in Azure DevOps registrieren

  1. Azure DevOps oeffnen -> Projekt DCCT -> Repository knowledge_manager
  2. Pipelines -> New Pipeline
  3. Azure Repos Git auswaehlen -> Repository knowledge_manager auswaehlen
  4. Existing Azure Pipelines YAML file auswaehlen
  5. Pfad .azdo/ci.yaml auswaehlen
  6. Run klicken

5.2 Branch Policy einrichten (Build Validation) - Manuell

Damit PRs nur gemerged werden koennen, wenn die Tests gruen sind:

  1. Azure DevOps -> Repos -> Branches
  2. Auf master das Kontextmenue oeffnen (drei Punkte) -> Branch Policies
  3. Unter Build Validation -> Add build policy
  4. Die soeben erstellte Pipeline auswaehlen
  5. Trigger: Automatic
  6. Policy requirement: Required
  7. Speichern

Dieser Schritt wird vom Entwicklungsteam selbst durchgefuehrt (Bestaetigung durch CI/CD-Experten).

5.3 Branch Policy einrichten (Build Validation) - Via Terraform (empfohlen)

Die Build Validation wird ueber das tf-azdo Repository in der Datei alm.tfvars konfiguriert. Dies ist der CDOS-konforme Weg und nutzt das Modul tf_module_azdo_build_validations.

Es sind zwei Aenderungen in alm.tfvars noetig:


Schritt 1: Neuen Pipeline-Eintrag in pipelines hinzufuegen.

Position: Am Ende des pipelines-Blocks (nach dem letzten bestehenden Eintrag).

  # --- BESTEHENDER LETZTER EINTRAG (nicht aendern) ---
  "partnerbank-chatbot-knowledge-stage" = {
    name            = "Stage Knowledge"
    path            = "\\\\Cognigy\\\\Partnerbank-Chatbot\\\\Knowledge"
    branch_name     = "refs/heads/main"
    yaml_file_path  = ".azdo/knowledgebases/stage.yaml"
    repository_name = "partnerbank-chatbot-cognigy-version"
  }

  # --- NEUER EINTRAG (hinzufuegen) ---
  "knowledge-manager-ci" = {
    name            = "CI Tests"
    path            = "\\\\DCCT\\\\Knowledge Manager"
    branch_name     = "refs/heads/master"
    yaml_file_path  = ".azdo/ci.yaml"
    repository_name = "knowledge_manager"
  }
Feld Wert Erklaerung
Key "knowledge-manager-ci" Eindeutiger Schluessel, wird in Schritt 2 referenziert
name "CI Tests" Anzeigename der Pipeline in Azure DevOps
path "\\\\DCCT\\\\Knowledge Manager" Ordnerstruktur in Azure DevOps Pipelines
branch_name "refs/heads/master" Default-Branch des Repositories (nicht main!)
yaml_file_path ".azdo/ci.yaml" Pfad zur Pipeline-Datei im Repository
repository_name "knowledge_manager" Name des Repositories in Azure DevOps

Schritt 2: build_validations zum bestehenden knowledge_manager Repository-Block hinzufuegen.

Position: Im repositories-Block gibt es bereits einen Eintrag "knowledge_manager". Dort muss build_validations ergaenzt werden.

Vorher:

  "knowledge_manager" = {
    reviewers = "dcct_contributors"
  }

Nachher:

  "knowledge_manager" = {
    reviewers = "dcct_contributors"
    build_validations = {
      knowledge-manager-ci = ["*"]
    }
  }
Feld Wert Erklaerung
Key knowledge-manager-ci Muss exakt dem Pipeline-Key aus Schritt 1 entsprechen
["*"] Alle Branches Build Validation gilt fuer PRs gegen alle Branches. Analog zum bestehenden Pattern bei cognai-bot-deployment

Schritt 3: PR in tf-azdo erstellen und mergen -> Terraform Apply

Ergebnis nach Terraform Apply: PRs gegen master im knowledge_manager Repository koennen nur noch gemerged werden, wenn die CI-Pipeline gruen ist.

Referenz: Das exakt gleiche Pattern wird bereits fuer cognai-bot-deployment verwendet (Pipeline-Key cognai-bot-deployment-test mit build_validations im Repository-Block).


6. Verifikation

Nach der Umsetzung koennen alle drei Anforderungen getestet werden:

Test Aktion Erwartetes Ergebnis
Feature-Branch Push Neuen Branch erstellen, Aenderung pushen Pipeline startet automatisch, Tests laufen
PR-Erstellung PR gegen master oeffnen Pipeline startet automatisch, Ergebnis erscheint im PR
Merge in master PR genehmigen und mergen Pipeline startet automatisch auf master
Linting-Fehler Code mit ruff-Verstoessen pushen Linting-Step zeigt Warning (orange), Tests laufen trotzdem
Test-Fehler Fehlerhaften Test pushen Build schlaegt fehl (rot), Test-Report ist trotzdem sichtbar im Tests-Tab

7. Referenzen

Referenz Pfad Was wird daraus uebernommen
Export-Pipeline (cognigy-pipelines) cognigy-pipelines/.azdo/knowledgebases/export.yaml CDOS-Template-Einbindung (Zeilen 2-7, 46-48), Python/UV-Setup (Zeilen 204-224)
Snapshot-Create-Pipeline demo-chatbot-cognigy-version/.azdo/snapshots/create.yaml Pool-Konfiguration $(AGENT_POOL), name: $(BuildID) Pattern
CDOS Pipeline Template CDOS/azure-devops-pipeline (ref: v4) Basis-Template pipeline.yml, securityZone Parameter
CDOS Python Steps azure-devops-pipeline/Steps/python/ Referenz fuer venv/pip-basierte Workflows (hier nicht genutzt, da UV verwendet wird)
CDOS Build Validations tf_module_azdo_build_validations Terraform-Modul fuer automatisierte Branch Policies
Azure-Artikel (Microsoft) DevOps Pipelines Baseline Architecture Konzeptionelle Grundlage fuer PR- und CI-Trigger
pyproject.toml knowledge_manager/pyproject.toml Python-Version (>=3.13), Dependencies (pytest, ruff)

8. Anhang: Verifizierung gegen CDOS-Standards

Dieser Plan wurde gegen die aktuellen CDOS-Templates und Best Practices verifiziert (Stand: Maerz 2026).

Geprueft und bestaetigt

Aspekt Status Beleg
CDOS-Template Nutzung (extends: pipeline.yml@Templates) CDOS-Empfehlung, ref: v4
securityZone: dev fuer Test-Pipelines Standard in allen DCCT-Pipelines
UV-basiertes Python-Setup Etabliertes Pattern in DCCT-Projekten
JUnit-Reporting mit PublishTestResults@2 CDOS-konform
continueOnError: true fuer Linting Keine CDOS-Policy dagegen
Build Validation via Terraform tf_module_azdo_build_validations

Abweichungen von CDOS-Defaults (dokumentiert)

Aspekt CDOS-Default Unsere Wahl Begruendung
Pipeline-Dateiname .azdo/azure-pipeline.yml .azdo/ci.yaml Semantisch beschreibend fuer CI-Pipeline; andere DCCT-Pipelines nutzen auch spezifische Namen (export.yaml, create.yaml)
Python-Setup Steps/python/create_venv.yml + requirements_install.yml UV direkt Projekt nutzt pyproject.toml + uv.lock; CDOS-Steps sind pip-basiert und unterstuetzen UV nicht

Offene Punkte / Voraussetzungen

Punkt Verantwortlich Status
Build Service als Reader in CDOS/azure-devops-pipeline DevOps-Admin Vor Pipeline-Registrierung pruefen
Internetzugang zu astral.sh Infrastruktur ✅ Bestaetigt durch Senior-Entwickler

### .azdo/ci.yaml:

<aside>
💡

In Repo tf_azdo wird die pipeline verlinkt, jedoch nicht konfiguriert! Normalerweise außerhalb TB wird tf_azdo nicht benötigt

</aside>

```yaml
# =============================================================================
# CI-Pipeline: Knowledge Manager - Lint & Test
# =============================================================================
#
# Zweck:
#   Automatisierte Code-Qualitaetspruefung und Testausfuehrung fuer den
#   Knowledge Manager. Laeuft bei jedem Push und bei PRs gegen master.
#
# Was passiert:
#   1. Python 3.13 + UV werden installiert (UV ist nicht vorinstalliert)
#   2. Alle Dependencies werden aus uv.lock installiert (reproduzierbar)
#   3. Linting mit ruff (Warnung bei Fehlern, kein Build-Abbruch)
#   4. Tests mit pytest (Build schlaegt fehl bei Test-Fehlern)
#   5. Test-Ergebnisse werden im Azure DevOps "Tests"-Tab angezeigt
#
# Voraussetzungen:
#   - Build Service muss Reader-Rechte in CDOS/azure-devops-pipeline haben
#   - Agents muessen Internetzugang zu astral.sh haben (fuer UV-Download)
#
# Referenzen:
#   - CDOS-Template: CDOS/azure-devops-pipeline (ref: v4)
#   - UV-Setup-Pattern: cognigy-pipelines/.azdo/knowledgebases/export.yaml
# =============================================================================

# ---------------------------------------------------------------------------
# Trigger-Konfiguration
# ---------------------------------------------------------------------------
#
# CI-Trigger (trigger):
#   Reagiert auf jeden 'git push' - egal auf welchen Branch.
#   Deckt ab:
#     - Push auf Feature-Branches
#     - Merge in master - ein Merge ist technisch ein Push
#
# PR-Trigger (pr):
#   Reagiert auf das Oeffnen/Aktualisieren eines Pull Requests gegen master.
#   Deckt ab:
#     - PR-Erstellung gegen master
# ---------------------------------------------------------------------------
trigger:
  branches:
    include:
      - '*'

pr:
  branches:
    include:
      - master

# ---------------------------------------------------------------------------
# CDOS-Template Einbindung
# ---------------------------------------------------------------------------
# Pflicht fuer alle Pipelines: Das CDOS-Template wird als Basis verwendet.
# ref: refs/tags/v4 pinnt auf eine stabile Version des Templates.
# ---------------------------------------------------------------------------
resources:
  repositories:
    - repository: Templates
      type: git
      name: CDOS/azure-devops-pipeline
      ref: refs/tags/v4

# Self-Hosted Agent Pool (wird zentral als Variable bereitgestellt)
pool: $(AGENT_POOL)

# Build-Nummer entspricht der Azure DevOps Build-ID
name: $(Build.BuildId)

# ---------------------------------------------------------------------------
# Pipeline-Struktur
# ---------------------------------------------------------------------------
# extends: pipeline.yml@Templates bindet das CDOS-Basis-Template ein.
# securityZone: dev - Test-Pipelines laufen immer in der Dev-Zone.
# ---------------------------------------------------------------------------
extends:
  template: pipeline.yml@Templates
  parameters:
    securityZone: dev
    stages:
      - stage: test
        displayName: Lint and Test
        jobs:
          - job: lint_and_test
            displayName: Lint and Test Knowledge Manager
            steps:

              # ---------------------------------------------------------------
              # Step 1: Repository auschecken
              # ---------------------------------------------------------------
              # fetchDepth: 1 - Nur den letzten Commit laden (schneller)
              # clean: true   - Workspace vorher bereinigen (keine Altlasten)
              # ---------------------------------------------------------------
              - checkout: self
                fetchDepth: 1
                clean: true

              # ---------------------------------------------------------------
              # Step 2: Python-Umgebung einrichten
              # ---------------------------------------------------------------
              # UV ist auf den Build-Agents nicht vorinstalliert und muss
              # per curl heruntergeladen werden. Dieses Pattern ist etabliert
              # und wird bereits in der Export-Pipeline verwendet.
              #
              # Ablauf:
              #   1. UV installieren (offizieller Installer von astral.sh)
              #   2. Python 3.13 ueber UV installieren
              #   3. Alle Dependencies aus uv.lock installieren
              #      (--frozen: exakt die Versionen aus dem Lock-File)
              #      (ohne --no-dev: damit ruff aus der dev-Gruppe dabei ist)
              #   4. .venv/bin zum PATH hinzufuegen, damit pytest und ruff
              #      in nachfolgenden Steps direkt verfuegbar sind
              # ---------------------------------------------------------------
              - bash: |
                  set -eu
                  set -o pipefail

                  # UV installieren (nicht vorinstalliert auf Agents)
                  # -L: Redirects folgen, -sS: nur Fehler anzeigen, -f: bei HTTP-Fehlern abbrechen
                  curl -LsSf <https://astral.sh/uv/install.sh> | sh
                  source "$HOME/.local/bin/env"
                  echo "UV Version: $(uv --version)"

                  # Python 3.13 sicherstellen (UV laedt es automatisch herunter)
                  uv python install 3.13
                  echo "Python Version: $(python3 --version)"

                  # Alle Dependencies installieren (inkl. dev-Gruppe fuer ruff)
                  # --frozen: Nutzt exakt die Versionen aus uv.lock (reproduzierbar)
                  uv sync --frozen --python 3.13

                  # Virtual Environment fuer nachfolgende Steps verfuegbar machen
                  # Ohne diese Zeilen wuerden pytest/ruff in den naechsten Steps nicht gefunden
                  echo "##vso[task.prependpath]$(pwd)/.venv/bin"
                  echo "##vso[task.setvariable variable=VIRTUAL_ENV]$(pwd)/.venv"
                displayName: Setup Python and install dependencies

              # ---------------------------------------------------------------
              # Step 3: Linting mit ruff
              # ---------------------------------------------------------------
              # Prueft Code-Stil und typische Fehler in src/ und tests/.
              #
              # continueOnError: true
              #   → Bei Linting-Fehlern wird der Step als "Warning" (orange)
              #     angezeigt, aber die Pipeline laeuft weiter.
              #   → Tests werden trotzdem ausgefuehrt - ein Linting-Fehler
              #     soll den Build nicht blockieren.
              # ---------------------------------------------------------------
              - bash: |
                  set -eu
                  set -o pipefail

                  echo "Running ruff linter..."
                  uv run ruff check src/ tests/
                displayName: Lint with ruff
                continueOnError: true

              # ---------------------------------------------------------------
              # Step 4: Tests mit pytest
              # ---------------------------------------------------------------
              # Fuehrt die komplette Test-Suite aus.
              #
              # Flags:
              #   --tb=short              Kurze Tracebacks (uebersichtlicher im Log)
              #   --junitxml=...          JUnit-XML fuer den Azure DevOps Tests-Tab
              #   --cov=src               Code-Coverage ueber das src/-Verzeichnis
              #   --cov-report=term-missing  Zeigt nicht abgedeckte Zeilen im Log
              #
              # Wenn Tests fehlschlagen, wird der Build rot.
              # Bei aktiver Branch Policy blockiert das den Merge des PRs.
              # ---------------------------------------------------------------
              - bash: |
                  set -eu
                  set -o pipefail

                  uv run pytest \\
                    --tb=short \\
                    --junitxml=$(Build.ArtifactStagingDirectory)/test-results.xml \\
                    --cov=src \\
                    --cov-report=term-missing
                displayName: Run tests with pytest

              # ---------------------------------------------------------------
              # Step 5: Test-Ergebnisse veroeffentlichen
              # ---------------------------------------------------------------
              # Macht die Test-Ergebnisse im Azure DevOps "Tests"-Tab sichtbar.
              #
              # condition: succeededOrFailed()
              #   → Wird auch bei fehlgeschlagenen Tests ausgefuehrt.
              #   → Ohne diese Condition wuerde der Step bei roten Tests
              #     uebersprungen - und man wuesste nicht, welche Tests
              #     genau fehlgeschlagen sind.
              # ---------------------------------------------------------------
              - task: PublishTestResults@2
                condition: succeededOrFailed()
                inputs:
                  testResultsFormat: JUnit
                  testResultsFiles: $(Build.ArtifactStagingDirectory)/test-results.xml
                  testRunTitle: Knowledge Manager Tests