6 Commits

Author SHA1 Message Date
stefan 0817d49dfc ```markdown
fix: Entferne veraltete Gradle-Eigenschaft und aktualisiere Dokumentation

- **gradle.properties**: Veraltete Eigenschaft `kotlin.mpp.androidSourceSetLayoutVersion` entfernt.
- **Dokumentation**: Journal-Eintrag für die Gradle-Konfigurationsbereinigung hinzugefügt.

Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
```
Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
2026-06-09 19:46:41 +02:00
stefan 44cf2b3edc ### fix: Aktualisiere Gradle und Kotlin Versionen
- **gradle-wrapper.properties:** Upgegrade von Gradle 9.4.1 zu 9.5.1.
- **libs.versions.toml:** Upgegrade von Kotlin 2.3.20 zu 2.4.0.
- **gradle.properties:** Entferne veraltete `kotlin.mpp.androidSourceSetLayoutVersion`.
- **.aiassistant/rules/Meldestelle-Guidelines.md:** Füge neue Guidelines hinzu.

Signed-off-by: StefanMoCoAt <stefan.mo.co@gmail.com>
2026-06-09 19:43:13 +02:00
stefan 4acbd6b0b2 chore(ai): add provider overlays and ignore dist directory 2026-06-03 12:48:22 +02:00
stefan 843bd145a8 chore(ai): establish single source of truth and cleanup legacy rules 2026-06-03 12:12:17 +02:00
stefan 98425b8fa8 chore/ai-guardrails-centralization
PR Guard / no-hardcoded-versions (pull_request) Failing after 12m14s
Desktop CI — Headless Tests & Build / Compose Desktop — Tests (headless) & Build (push) Has been skipped
Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
2026-06-02 14:04:17 +02:00
stefan 5b6459a041 chore(ai): centralize guardrail scripts under .ai; add resolve_repo_root; add shims for .junie/.nolik; fix gitea contexts in release.yml
Co-authored-by: Junie <junie@jetbrains.com>
2026-06-02 14:01:26 +02:00
29 changed files with 440 additions and 201 deletions
+41
View File
@@ -0,0 +1,41 @@
## 🚀 Identität & Arbeitsmodus (Chamäleon-Modus)
Du bist ein hochqualifizierter KI-Assistent für das Softwareprojekt "Meldestelle" von Stefan.
Ich weise dir in meinen Prompts Aufgaben zu. Nimm sofort die entsprechende Rolle an, beginne deine Antwort zwingend mit dem passenden Badge und passe dein Vokabular an:
* 🏗️ **[Lead Architect]:** System-Design, Gradle-Build-Logik, Modulstruktur.
* 📜 **[Rulebook Expert]:** Validiert Business-Rules gegen das ÖTO/FEI Regelwerk.
* 👷 **[Backend Developer]:** Kotlin & Spring Boot Experte.
* 🎨 **[Frontend Expert]:** KMP & Compose Desktop Spezialist.
* 🐧 **[DevOps Engineer]:** Infrastruktur (Docker, CI/CD, Proxmox).
**Arbeitsanweisung:** Bearbeite pro Antwort immer nur EINE fachliche Aufgabe.
## 🏗️ Projekt-Strategie (Reality-Reset)
1. **Desktop-First & Offline-First:** Das Primärziel ist eine autarke Compose Desktop App (KMP). Sie muss auf Turnieren ohne Internet funktionieren (lokale Persistenz).
2. **Optionales Backend:** Ein Spring Boot Stack (PostgreSQL, Valkey, Keycloak) wird nur für Multi-Tenant-Verwaltung, Registrierung und P2P-Sync genutzt.
3. **Domain-Driven Design (DDD):** Die absolute Business-Hierarchie lautet: Veranstaltung -> Turnier -> Bewerb/Abteilung.
4. **Der System-Akteur:** Der primäre "Actor" in allen Use-Cases ist *nicht* der Veranstalter, sondern zwingend die Person, die die Meldestelle betreut (Actor = Meldestelle).
## 🛠️ Der verbindliche Tech-Stack
Generiere Code ausschließlich für diese exakten Versionen und Paradigmen:
* **Frontend (KMP):** Kotlin 2.3.21, Compose Multiplatform 1.10.3, Ktor Client 3.4.1, SQLDelight.
* **Backend:** Spring Boot 3.5.9 (JDK 25), Ktor Server (wo spezifiziert), Exposed 1.1.1.
* **Infrastruktur:** Gitea (CI/CD), Docker, Pangolin Tunnel. (KEIN GitHub, KEIN Cloudflare).
## 👁️ Anti-Halluzinations-Protokoll
Du bist an strikte, evidenzbasierte Entwicklung gebunden:
1. **Kein "Erledigt" ohne Beweis:** Ein Task ist erst abgeschlossen, wenn Test-Logs oder ein Build vorliegen.
2. **Verifikation ausstehend:** Generierter, ungetesteter Code muss diesen Vermerk zwingend tragen.
3. **Fakten-Check:** Wenn du den Code nicht im Kontext hast (z.B. eine spezifische Gradle-Datei), fordere sie aktiv vom User an, anstatt blind zu raten.
## 🛡️ DSGVO & Lokale Ausführung (Nolik-Spezifika)
* Dein Name ist "Nolik". Du bist ein lokal gehosteter, datenschutzkonformer Senior-Architekt auf dem Server "Simka" (Proxmox VM 101).
* **Datensouveränität:** Du bist der Hüter der lokalen Daten. Generiere niemals Code, der Telemetrie, Tracking oder Logging an externe Cloud-Anbieter sendet.
+11
View File
@@ -0,0 +1,11 @@
## 🚀 Identität & Arbeitsmodus (Chamäleon-Modus)
Du bist ein hochqualifizierter KI-Assistent für das Softwareprojekt "Meldestelle" von Stefan.
Ich weise dir in meinen Prompts Aufgaben zu. Nimm sofort die entsprechende Rolle an, beginne deine Antwort zwingend mit dem passenden Badge und passe dein Vokabular an:
* 🏗️ **[Lead Architect]:** System-Design, Gradle-Build-Logik, Modulstruktur.
* 📜 **[Rulebook Expert]:** Validiert Business-Rules gegen das ÖTO/FEI Regelwerk.
* 👷 **[Backend Developer]:** Kotlin & Spring Boot Experte.
* 🎨 **[Frontend Expert]:** KMP & Compose Desktop Spezialist.
* 🐧 **[DevOps Engineer]:** Infrastruktur (Docker, CI/CD, Proxmox).
**Arbeitsanweisung:** Bearbeite pro Antwort immer nur EINE fachliche Aufgabe.
+3
View File
@@ -0,0 +1,3 @@
## ⚙️ Provider-Spezifika (Google Gemini / Web-Meta-Modus)
* Du agierst als "Gemini" über die Web-Oberfläche. Deine primäre Aufgabe ist die strategische Meta-Ebene, Architektur-Analyse, Review von CI/CD-Pipelines und das Sparring bei komplexen Refactoring-Plänen.
* **Antwort-Stil:** Antworte prägnant, strukturiert und nutze das bereitgestellte Formatierungstoolkit (Markdown, klare Hierarchien, Code-Blöcke). Vermeide unnötige Floskeln und komm direkt auf den technischen Punkt.
+4
View File
@@ -0,0 +1,4 @@
## ⚙️ Provider-Spezifika (JetBrains Junie / IDE-Modus)
* Dein Name ist "Junie". Du arbeitest als hochintegrierter KI-Assistent direkt innerhalb von IntelliJ IDEA.
* **Kontext-Fokus:** Nutze die lokalen Projektdateien, Indizes und das Git-Log intensiv. Wenn Refactorings oder Code-Generierungen anstehen, achte penibel darauf, dass bestehende Datei-Imports (Kotlin-Packages) nicht zerschossen werden.
* **Generierungs-Gate:** Halte dich strikt an die im Projekt hinterlegten Formatierungsregeln für Detekt und Ktlint.
+3
View File
@@ -0,0 +1,3 @@
## 🛡️ DSGVO & Lokale Ausführung (Nolik-Spezifika)
* Dein Name ist "Nolik". Du bist ein lokal gehosteter, datenschutzkonformer Senior-Architekt auf dem Server "Simka" (Proxmox VM 101).
* **Datensouveränität:** Du bist der Hüter der lokalen Daten. Generiere niemals Code, der Telemetrie, Tracking oder Logging an externe Cloud-Anbieter sendet.
+5
View File
@@ -0,0 +1,5 @@
## 🏗️ Projekt-Strategie (Reality-Reset)
1. **Desktop-First & Offline-First:** Das Primärziel ist eine autarke Compose Desktop App (KMP). Sie muss auf Turnieren ohne Internet funktionieren (lokale Persistenz).
2. **Optionales Backend:** Ein Spring Boot Stack (PostgreSQL, Valkey, Keycloak) wird nur für Multi-Tenant-Verwaltung, Registrierung und P2P-Sync genutzt.
3. **Domain-Driven Design (DDD):** Die absolute Business-Hierarchie lautet: Veranstaltung -> Turnier -> Bewerb/Abteilung.
4. **Der System-Akteur:** Der primäre "Actor" in allen Use-Cases ist *nicht* der Veranstalter, sondern zwingend die Person, die die Meldestelle betreut (Actor = Meldestelle).
+5
View File
@@ -0,0 +1,5 @@
## 🛠️ Der verbindliche Tech-Stack
Generiere Code ausschließlich für diese exakten Versionen und Paradigmen:
* **Frontend (KMP):** Kotlin 2.3.21, Compose Multiplatform 1.10.3, Ktor Client 3.4.1, SQLDelight.
* **Backend:** Spring Boot 3.5.9 (JDK 25), Ktor Server (wo spezifiziert), Exposed 1.1.1.
* **Infrastruktur:** Gitea (CI/CD), Docker, Pangolin Tunnel. (KEIN GitHub, KEIN Cloudflare).
+5
View File
@@ -0,0 +1,5 @@
## 👁️ Anti-Halluzinations-Protokoll
Du bist an strikte, evidenzbasierte Entwicklung gebunden:
1. **Kein "Erledigt" ohne Beweis:** Ein Task ist erst abgeschlossen, wenn Test-Logs oder ein Build vorliegen.
2. **Verifikation ausstehend:** Generierter, ungetesteter Code muss diesen Vermerk zwingend tragen.
3. **Fakten-Check:** Wenn du den Code nicht im Kontext hast (z.B. eine spezifische Gradle-Datei), fordere sie aktiv vom User an, anstatt blind zu raten.
+42
View File
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail
# Nutze Junies robuste Pfad-Ermittlung
source "$(dirname "${BASH_SOURCE[0]}")/lib/common.sh"
REPO_ROOT="$(resolve_repo_root)"
cd "$REPO_ROOT"
AI_DIR=".ai"
DIST_DIR="$AI_DIR/dist"
mkdir -p "$DIST_DIR"
echo "[INFO] Generiere System-Prompts aus den Core-Rules..."
for PROVIDER_DIR in "$AI_DIR/providers/"*; do
if [ -d "$PROVIDER_DIR" ]; then
PROVIDER_NAME=$(basename "$PROVIDER_DIR")
OUTPUT_FILE="$DIST_DIR/${PROVIDER_NAME}-system-prompt.md"
echo "-> Baue Prompt für: $PROVIDER_NAME"
# 1. Basis-Identität schreiben
cat "$AI_DIR/prompts/system/base.md" > "$OUTPUT_FILE"
echo -e "\n\n" >> "$OUTPUT_FILE"
# 2. Alle globalen Regeln anhängen
for RULE_FILE in "$AI_DIR/rules/"*.md; do
if [ -f "$RULE_FILE" ]; then
cat "$RULE_FILE" >> "$OUTPUT_FILE"
echo -e "\n\n" >> "$OUTPUT_FILE"
fi
done
# 3. Provider-Spezifika anhängen
if [ -f "$PROVIDER_DIR/overlay.md" ]; then
cat "$PROVIDER_DIR/overlay.md" >> "$OUTPUT_FILE"
fi
echo "[OK] $OUTPUT_FILE erfolgreich erstellt."
fi
done
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/lib/common.sh"
REPO_ROOT="$(resolve_repo_root)"
cd "$REPO_ROOT"
# check-docs-drift.sh
# Zweck: sehr schlanke Drift-Checks gegen die neue Doku-Struktur.
# - Kein Guidelines-System mehr.
# - Single Source of Truth: `docs/`
err=0
has() { grep -q "$2" "$1" || { echo "[DRIFT] '$2' fehlt in $1"; err=1; }; }
miss() { grep -q "$2" "$1" && { echo "[DRIFT] Veralteter Begriff '$2' in $1"; err=1; }; }
# Harte Altlast-Pfade dürfen nicht mehr vorkommen
if git grep -n "docs/00_Domain/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/00_Domain/' in docs/* gefunden"
err=1
fi
if git grep -n "docs/adr/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/adr/' in docs/* gefunden"
err=1
fi
if git grep -n "docs/c4/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/c4/' in docs/* gefunden"
err=1
fi
if git grep -n "docs/how-to/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/how-to/' in docs/* gefunden"
err=1
fi
if git grep -n "docs/reference/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/reference/' in docs/* gefunden"
err=1
fi
# Quelle der Wahrheit: Gateway-Technologie (sollte in Architektur/ADRs/C4 konsistent sein)
has docs/01_Architecture/ARCHITECTURE.md "Spring Cloud Gateway"
has docs/01_Architecture/adr/0007-api-gateway-pattern-de.md "Spring Cloud Gateway"
miss docs/01_Architecture/adr/0007-api-gateway-pattern-de.md "Ktor"
has docs/01_Architecture/c4/02-container-de.puml "Spring Cloud Gateway"
miss docs/01_Architecture/c4/02-container-de.puml "Ktor"
exit $err
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
# Common helpers for AI guardrail scripts
# Robustly resolve the repository root directory.
# Strategy: prefer Git; fallback to marker search upwards; last resort: current dir.
resolve_repo_root() {
local start
start="${1:-$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)}"
if command -v git >/dev/null 2>&1; then
if git -C "$start" rev-parse --show-toplevel >/dev/null 2>&1; then
git -C "$start" rev-parse --show-toplevel
return 0
fi
fi
local dir
dir="$(cd "$start" && pwd)"
while [ "$dir" != "/" ]; do
if [ -f "$dir/gradlew" ] || [ -f "$dir/settings.gradle.kts" ] || [ -d "$dir/.git" ]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
pwd
}
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/lib/common.sh"
REPO_ROOT="$(resolve_repo_root)"
cd "$REPO_ROOT"
mkdir -p build/diagrams
shopt -s nullglob
for f in docs/architecture/c4/*.puml; do
docker run --rm -v "$PWD":/data plantuml/plantuml -tsvg "/data/$f" -o "/data/build/diagrams"
echo "Rendered build/diagrams/$(basename "${f%.puml}").svg"
done
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
# shellcheck disable=SC1091
source "$SCRIPT_DIR/lib/common.sh"
REPO_ROOT="$(resolve_repo_root)"
cd "$REPO_ROOT"
QUICK_MODE=false
while [[ $# -gt 0 ]]; do
case $1 in
--quick)
QUICK_MODE=true
shift
;;
--help|-h)
cat << 'EOF'
Docs Link-Validierung
USAGE:
./.ai/scripts/validate-links.sh [--quick]
BESCHREIBUNG:
Prüft Markdown-Links in `docs/**/*.md` auf gebrochene relative Pfade.
Ignoriert externe Links (http/https/mailto) sowie reine Anchors (#...).
OPTIONEN:
--quick Führt nur eine Teilmenge der Prüfungen durch (aktuell nicht implementiert).
EOF
exit 0
;;
*)
echo "[ERROR] Unbekannter Parameter: $1" >&2
exit 2
;;
esac
done
python3 - <<'PY'
import re
import sys
from pathlib import Path
from urllib.parse import unquote
root = Path.cwd()
docs_dir = root / "docs"
if not docs_dir.is_dir():
print(f"[ERROR] docs-Verzeichnis nicht gefunden: {docs_dir}", file=sys.stderr)
sys.exit(2)
# Veraltete Pfad-Prüfungen wurden entfernt; Fokus auf Link-Integrität.
FORBIDDEN_SUBSTRINGS = []
md_files = sorted(docs_dir.rglob("*.md"))
link_pattern = re.compile(r"\]\(([^)]+)\)")
errors = 0
def is_external(target: str) -> bool:
t = target.lower()
return t.startswith("http://") or t.startswith("https://") or t.startswith("mailto:")
def strip_fragment_and_query(target: str) -> str:
target = target.split("#", 1)[0]
target = target.split("?", 1)[0]
return target
for f in md_files:
text = f.read_text(encoding="utf-8", errors="replace")
for forbidden in FORBIDDEN_SUBSTRINGS:
if forbidden in text:
print(f"[ERROR] Veralteter Pfad '{forbidden}' in {f}")
errors += 1
for match in link_pattern.finditer(text):
target = match.group(1).strip()
if not target:
continue
if is_external(target):
continue
if target.startswith("#"):
continue
if target.startswith("<") and target.endswith(">"):
target = target[1:-1]
target = unquote(strip_fragment_and_query(target))
if target.startswith("/"):
continue
if ":" in target.split("/", 1)[0]:
# z.B. "vscode:..."
continue
resolved = (f.parent / target).resolve()
try:
resolved.relative_to(root.resolve())
except ValueError:
print(f"[ERROR] Link zeigt außerhalb des Repos: {f} -> {target}")
errors += 1
continue
if resolved.is_dir():
if not (resolved / "README.md").is_file():
print(f"[ERROR] Verlinktes Verzeichnis ohne README.md: {f} -> {target}")
errors += 1
continue
if not resolved.exists():
print(f"[ERROR] Broken link: {f} -> {target}")
errors += 1
if errors:
print(f"[ERROR] Link-Validierung fehlgeschlagen: {errors} Fehler")
sys.exit(1)
print(f"[OK] Link-Validierung erfolgreich: {len(md_files)} Markdown-Dateien geprüft")
PY
@@ -0,0 +1,4 @@
---
apply: always
---
+1 -1
View File
@@ -193,7 +193,7 @@ secrets/
# =================================================================== # ===================================================================
TODO*.md TODO*.md
NOTES*.md NOTES*.md
**/.junie/ .junie/
# =================================================================== # ===================================================================
# Keep essential files (override exclusions) # Keep essential files (override exclusions)
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
desktop-tests: desktop-tests:
# Komplett deaktivierbar über Repo-Variable: Settings → Variables → DESKTOP_CI_ENABLED=true # Komplett deaktivierbar über Repo-Variable: Settings → Variables → DESKTOP_CI_ENABLED=true
# Zusätzlich: Für PlanBBuilds überspringen, wenn Commit-Message [planb] enthält # Zusätzlich: Für PlanBBuilds überspringen, wenn Commit-Message [planb] enthält
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(github.event.head_commit.message, '[planb]') }} if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(gitea.event.head_commit.message, '[planb]') }}
name: Compose Desktop — Tests (headless) & Build name: Compose Desktop — Tests (headless) & Build
runs-on: ubuntu-latest runs-on: ubuntu-latest
+1 -1
View File
@@ -5,7 +5,7 @@ on:
jobs: jobs:
no-hardcoded-versions: no-hardcoded-versions:
# Für Plan-B-Builds überspringen: Commit-Message enthält [planb] # Für Plan-B-Builds überspringen: Commit-Message enthält [planb]
if: ${{ !contains(github.event.head_commit.message, '[planb]') }} if: ${{ !contains(gitea.event.head_commit.message, '[planb]') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
+16 -16
View File
@@ -23,7 +23,7 @@ jobs:
tag-release: tag-release:
name: 🏷️ Git-Tag setzen name: 🏷️ Git-Tag setzen
# Für Plan-B-Builds überspringen: Commit-Message enthält [planb] # Für Plan-B-Builds überspringen: Commit-Message enthält [planb]
if: ${{ !contains(github.event.head_commit.message, '[planb]') }} if: ${{ !contains(gitea.event.head_commit.message, '[planb]') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
version: ${{ steps.read-version.outputs.version }} version: ${{ steps.read-version.outputs.version }}
@@ -64,7 +64,7 @@ jobs:
fi fi
- name: Git-Tag erstellen & pushen - name: Git-Tag erstellen & pushen
if: steps.check-tag.outputs.already_tagged == 'false' && github.event.inputs.dry_run != 'true' if: steps.check-tag.outputs.already_tagged == 'false' && gitea.event.inputs.dry_run != 'true'
run: | run: |
TAG="${{ steps.read-version.outputs.tag }}" TAG="${{ steps.read-version.outputs.tag }}"
VERSION="${{ steps.read-version.outputs.version }}" VERSION="${{ steps.read-version.outputs.version }}"
@@ -80,7 +80,7 @@ jobs:
package-linux: package-linux:
name: 📦 Linux .deb Packaging name: 📦 Linux .deb Packaging
# Nur ausführen, wenn Desktop-CI explizit aktiviert ist UND kein PlanB Commit # Nur ausführen, wenn Desktop-CI explizit aktiviert ist UND kein PlanB Commit
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(github.event.head_commit.message, '[planb]') }} if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(gitea.event.head_commit.message, '[planb]') }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: tag-release needs: tag-release
@@ -88,11 +88,11 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup JDK 21 (Temurin) - name: Setup JDK 25 (Temurin)
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: temurin distribution: temurin
java-version: '21' java-version: '25'
- name: Gradle cache - name: Gradle cache
uses: actions/cache@v4 uses: actions/cache@v4
@@ -128,7 +128,7 @@ jobs:
package-windows: package-windows:
name: 📦 Windows .msi Packaging name: 📦 Windows .msi Packaging
# Nur ausführen, wenn Desktop-CI explizit aktiviert ist UND kein PlanB Commit # Nur ausführen, wenn Desktop-CI explizit aktiviert ist UND kein PlanB Commit
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(github.event.head_commit.message, '[planb]') }} if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(gitea.event.head_commit.message, '[planb]') }}
runs-on: windows-latest runs-on: windows-latest
needs: tag-release needs: tag-release
@@ -136,11 +136,11 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup JDK 21 (Temurin) - name: Setup JDK 25 (Temurin)
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
distribution: temurin distribution: temurin
java-version: '21' java-version: '25'
- name: Gradle cache - name: Gradle cache
uses: actions/cache@v4 uses: actions/cache@v4
@@ -179,11 +179,11 @@ jobs:
steps: steps:
- name: Summary ausgeben - name: Summary ausgeben
run: | run: |
echo "## 🚀 Release ${{ needs.tag-release.outputs.version }}" >> $GITHUB_STEP_SUMMARY echo "## 🚀 Release ${{ needs.tag-release.outputs.version }}" >> $GITEA_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITEA_STEP_SUMMARY
echo "| Artefakt | Status |" >> $GITHUB_STEP_SUMMARY echo "| Artefakt | Status |" >> $GITEA_STEP_SUMMARY
echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY echo "|----------|--------|" >> $GITEA_STEP_SUMMARY
echo "| Linux .deb | ${{ needs.package-linux.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Linux .deb | ${{ needs.package-linux.result }} |" >> $GITEA_STEP_SUMMARY
echo "| Windows .msi | ${{ needs.package-windows.result }} |" >> $GITHUB_STEP_SUMMARY echo "| Windows .msi | ${{ needs.package-windows.result }} |" >> $GITEA_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITEA_STEP_SUMMARY
echo "**Git-Tag:** \`${{ needs.tag-release.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY echo "**Git-Tag:** \`${{ needs.tag-release.outputs.tag }}\`" >> $GITEA_STEP_SUMMARY
+3
View File
@@ -1,5 +1,8 @@
# 🐧 [DevOps Engineer] Optimierte .gitignore für Meldestelle (KMP / Gradle / Docker) # 🐧 [DevOps Engineer] Optimierte .gitignore für Meldestelle (KMP / Gradle / Docker)
# --- AI ---
.ai/dist/
# --- IDE & Editor --- # --- IDE & Editor ---
.idea/ .idea/
*.iml *.iml
+4 -40
View File
@@ -1,43 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# check-docs-drift.sh # Shim: Weiterleitung auf zentrale Guardrail in .ai/
# Zweck: sehr schlanke Drift-Checks gegen die neue Doku-Struktur. SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
# - Kein Guidelines-System mehr. ROOT_DIR="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")"
# - Single Source of Truth: `docs/` exec "$ROOT_DIR/.ai/scripts/check-docs-drift.sh" "$@"
err=0
has() { grep -q "$2" "$1" || { echo "[DRIFT] '$2' fehlt in $1"; err=1; }; }
miss() { grep -q "$2" "$1" && { echo "[DRIFT] Veralteter Begriff '$2' in $1"; err=1; }; }
# Harte Altlast-Pfade dürfen nicht mehr vorkommen
if git grep -n "docs/00_Domain/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/00_Domain/' in docs/* gefunden"
err=1
fi
if git grep -n "docs/adr/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/adr/' in docs/* gefunden"
err=1
fi
if git grep -n "docs/c4/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/c4/' in docs/* gefunden"
err=1
fi
if git grep -n "docs/how-to/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/how-to/' in docs/* gefunden"
err=1
fi
if git grep -n "docs/reference/" -- docs >/dev/null 2>&1; then
echo "[DRIFT] Veralteter Pfad 'docs/reference/' in docs/* gefunden"
err=1
fi
# Quelle der Wahrheit: Gateway-Technologie (sollte in Architektur/ADRs/C4 konsistent sein)
has docs/01_Architecture/ARCHITECTURE.md "Spring Cloud Gateway"
has docs/01_Architecture/adr/0007-api-gateway-pattern-de.md "Spring Cloud Gateway"
miss docs/01_Architecture/adr/0007-api-gateway-pattern-de.md "Ktor"
has docs/01_Architecture/c4/02-container-de.puml "Spring Cloud Gateway"
miss docs/01_Architecture/c4/02-container-de.puml "Ktor"
exit $err
+4 -6
View File
@@ -1,9 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
mkdir -p build/diagrams # Shim: Weiterleitung auf zentrale Guardrail in .ai/
shopt -s nullglob SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
for f in docs/architecture/c4/*.puml; do ROOT_DIR="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")"
docker run --rm -v "$PWD":/data plantuml/plantuml -tsvg "/data/$f" -o "/data/build/diagrams" exec "$ROOT_DIR/.ai/scripts/render-plantuml.sh" "$@"
echo "Rendered build/diagrams/$(basename "${f%.puml}").svg"
done
+4 -133
View File
@@ -1,136 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# validate-links.sh - Link-Validierung für Projektdokumentation (`docs/**`). # Shim: Weiterleitung auf zentrale Guardrail in .ai/
# Zweck: Guardrail für die "Docs-as-Code"-Strategie. SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
ROOT_DIR="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" exec "$ROOT_DIR/.ai/scripts/validate-links.sh" "$@"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$PROJECT_ROOT"
QUICK_MODE=false
while [[ $# -gt 0 ]]; do
case $1 in
--quick)
QUICK_MODE=true
shift
;;
--help|-h)
cat << 'EOF'
Docs Link-Validierung
USAGE:
./.junie/scripts/validate-links.sh [--quick]
BESCHREIBUNG:
Prüft Markdown-Links in `docs/**/*.md` auf gebrochene relative Pfade.
Ignoriert externe Links (http/https/mailto) sowie reine Anchors (#...).
OPTIONEN:
--quick Führt nur eine Teilmenge der Prüfungen durch (aktuell nicht implementiert).
EOF
exit 0
;;
*)
echo "[ERROR] Unbekannter Parameter: $1" >&2
exit 2
;;
esac
done
python3 - <<'PY'
import os
import re
import sys
from pathlib import Path
from urllib.parse import unquote
root = Path.cwd()
docs_dir = root / "docs"
if not docs_dir.is_dir():
print(f"[ERROR] docs-Verzeichnis nicht gefunden: {docs_dir}", file=sys.stderr)
sys.exit(2)
# Veraltete Pfad-Prüfungen wurden entfernt, da sie zu wartungsintensiv waren.
# Das Skript konzentriert sich nun auf die Validierung der Link-Integrität.
FORBIDDEN_SUBSTRINGS = []
md_files = sorted(docs_dir.rglob("*.md"))
link_pattern = re.compile(r"\]\(([^)]+)\)")
errors = 0
def is_external(target: str) -> bool:
t = target.lower()
return t.startswith("http://") or t.startswith("https://") or t.startswith("mailto:")
def strip_fragment_and_query(target: str) -> str:
# remove fragment and query parts
target = target.split("#", 1)[0]
target = target.split("?", 1)[0]
return target
for f in md_files:
text = f.read_text(encoding="utf-8", errors="replace")
for forbidden in FORBIDDEN_SUBSTRINGS:
if forbidden in text:
print(f"[ERROR] Veralteter Pfad '{forbidden}' in {f}")
errors += 1
for match in link_pattern.finditer(text):
target = match.group(1).strip()
if not target:
continue
if is_external(target):
continue
if target.startswith("#"):
continue
# drop angle brackets <...> used in markdown for urls with spaces
if target.startswith("<") and target.endswith(">"):
target = target[1:-1]
target = unquote(strip_fragment_and_query(target))
# ignore absolute paths in the repo (we treat them as doc-style links; validate only if relative)
if target.startswith("/"):
continue
# ignore non-file targets (e.g. empty or protocol-less anchors)
if ":" in target.split("/", 1)[0]:
# things like "vscode:..." etc.
continue
# treat as file path relative to markdown file
resolved = (f.parent / target).resolve()
# keep validation within repo
try:
resolved.relative_to(root.resolve())
except ValueError:
print(f"[ERROR] Link zeigt außerhalb des Repos: {f} -> {target}")
errors += 1
continue
# allow directories if they contain README.md
if resolved.is_dir():
if not (resolved / "README.md").is_file():
print(f"[ERROR] Verlinktes Verzeichnis ohne README.md: {f} -> {target}")
errors += 1
continue
if not resolved.exists():
print(f"[ERROR] Broken link: {f} -> {target}")
errors += 1
if errors:
print(f"[ERROR] Link-Validierung fehlgeschlagen: {errors} Fehler")
sys.exit(1)
print(f"[OK] Link-Validierung erfolgreich: {len(md_files)} Markdown-Dateien geprüft")
PY
+21
View File
@@ -0,0 +1,21 @@
# .aiignore - Verhindert Token-Waste für Nolik
# Abhängigkeiten & Binaries
build/
.gradle/
*.jar
*.deb
*.msi
# Sensible Daten (auch lokal!)
.env
.env.*
config/docker/certs/
*.pem
*.jks
postgres-data/
valkey-data/
# Doku-Builds (Nolik soll die Source-Files in docs/ lesen, nicht die HTML-Exporte)
build/dokka/
docs/Neumarkt2026/*.pdf
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
# Shim: Weiterleitung auf zentrale Guardrail in .ai/
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
ROOT_DIR="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")"
exec "$ROOT_DIR/.ai/scripts/check-docs-drift.sh" "$@"
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
# Shim: Weiterleitung auf zentrale Guardrail in .ai/
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
ROOT_DIR="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")"
exec "$ROOT_DIR/.ai/scripts/validate-links.sh" "$@"
@@ -0,0 +1,26 @@
# Journal Eintrag: 2026-06-09 - Gradle Configuration Cleanup
## 🏗️ [Lead Architect] & 🧹 [Curator]
### 🎯 Ziel
Entfernung veralteter Gradle-Properties, um Build-Warnungen zu reduzieren und die Kompatibilität mit zukünftigen
Kotlin-Versionen sicherzustellen.
### 🛠️ Änderungen
- **`gradle.properties`**: Die Eigenschaft `kotlin.mpp.androidSourceSetLayoutVersion=2` wurde entfernt.
- **Grund**: Die Warnung `w: ⚠️ Deprecated Gradle Property 'kotlin.mpp.androidSourceSetLayoutVersion' Used` im Modul
`:contracts:ping-api` wies darauf hin, dass dieses Layout nun Standard ist und die explizite Setzung nicht mehr
unterstützt wird.
### ✅ Verifikation
- `./gradlew :contracts:ping-api:help` wurde erfolgreich ohne die besagte Warnung ausgeführt.
- Projektstruktur und andere Module wurden stichprobenartig auf ähnliche veraltete Einträge geprüft (keine weiteren
Funde).
### 📝 Notizen
- Es sind noch diverse Compose-bezogene Deprecation-Warnungen in den Frontend-Modulen vorhanden. Diese sollten in einer
separaten Session durch den **🎨 Frontend Expert** adressiert werden.
-1
View File
@@ -40,7 +40,6 @@ org.gradle.dependency.locking.enabled=true
io.ktor.development=true io.ktor.development=true
# IDE Configuration # IDE Configuration
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.mpp.enableCInteropCommonization=true kotlin.mpp.enableCInteropCommonization=true
org.jetbrains.kotlin.wasm.check.wasm.binary.format=false org.jetbrains.kotlin.wasm.check.wasm.binary.format=false
kotlin.native.ignoreDisabledTargets=true kotlin.native.ignoreDisabledTargets=true
+1 -1
View File
@@ -7,7 +7,7 @@
# === FRONTEND & KMP CORE === # === FRONTEND & KMP CORE ===
# ============================================================================== # ==============================================================================
# Kotlin & Tooling # Kotlin & Tooling
kotlin = "2.3.20" kotlin = "2.4.0"
ksp = "2.3.4" ksp = "2.3.4"
# KotlinX (Core Libraries) # KotlinX (Core Libraries)
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME