2 Commits

37 changed files with 806 additions and 294 deletions
-50
View File
@@ -1,50 +0,0 @@
#!/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
@@ -1,27 +0,0 @@
#!/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
@@ -1,16 +0,0 @@
#!/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
@@ -1,127 +0,0 @@
#!/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
+1 -1
View File
@@ -193,7 +193,7 @@ secrets/
# ===================================================================
TODO*.md
NOTES*.md
.junie/
**/.junie/
# ===================================================================
# Keep essential files (override exclusions)
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
desktop-tests:
# Komplett deaktivierbar über Repo-Variable: Settings → Variables → DESKTOP_CI_ENABLED=true
# Zusätzlich: Für PlanBBuilds überspringen, wenn Commit-Message [planb] enthält
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(gitea.event.head_commit.message, '[planb]') }}
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(github.event.head_commit.message, '[planb]') }}
name: Compose Desktop — Tests (headless) & Build
runs-on: ubuntu-latest
+5 -2
View File
@@ -4,6 +4,8 @@ run-name: Build & Publish by @${{ github.actor }}
on:
push:
branches: [ "main" ]
tags:
- 'v*'
paths:
- 'backend/**'
- 'platform/**'
@@ -114,8 +116,9 @@ jobs:
with:
images: ${{ env.REGISTRY_INTERNAL }}/${{ env.IMAGE_PREFIX }}/${{ matrix.image }}
tags: |
type=raw,value=latest
type=sha,format=long
type=ref,event=tag
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
type=sha,format=long,enable=${{ github.ref == 'refs/heads/main' }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
+1 -1
View File
@@ -5,7 +5,7 @@ on:
jobs:
no-hardcoded-versions:
# Für Plan-B-Builds überspringen: Commit-Message enthält [planb]
if: ${{ !contains(gitea.event.head_commit.message, '[planb]') }}
if: ${{ !contains(github.event.head_commit.message, '[planb]') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+16 -16
View File
@@ -23,7 +23,7 @@ jobs:
tag-release:
name: 🏷️ Git-Tag setzen
# Für Plan-B-Builds überspringen: Commit-Message enthält [planb]
if: ${{ !contains(gitea.event.head_commit.message, '[planb]') }}
if: ${{ !contains(github.event.head_commit.message, '[planb]') }}
runs-on: ubuntu-latest
outputs:
version: ${{ steps.read-version.outputs.version }}
@@ -64,7 +64,7 @@ jobs:
fi
- name: Git-Tag erstellen & pushen
if: steps.check-tag.outputs.already_tagged == 'false' && gitea.event.inputs.dry_run != 'true'
if: steps.check-tag.outputs.already_tagged == 'false' && github.event.inputs.dry_run != 'true'
run: |
TAG="${{ steps.read-version.outputs.tag }}"
VERSION="${{ steps.read-version.outputs.version }}"
@@ -80,7 +80,7 @@ jobs:
package-linux:
name: 📦 Linux .deb Packaging
# Nur ausführen, wenn Desktop-CI explizit aktiviert ist UND kein PlanB Commit
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(gitea.event.head_commit.message, '[planb]') }}
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(github.event.head_commit.message, '[planb]') }}
runs-on: ubuntu-latest
needs: tag-release
@@ -88,11 +88,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Setup JDK 25 (Temurin)
- name: Setup JDK 21 (Temurin)
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '25'
java-version: '21'
- name: Gradle cache
uses: actions/cache@v4
@@ -128,7 +128,7 @@ jobs:
package-windows:
name: 📦 Windows .msi Packaging
# Nur ausführen, wenn Desktop-CI explizit aktiviert ist UND kein PlanB Commit
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(gitea.event.head_commit.message, '[planb]') }}
if: ${{ vars.DESKTOP_CI_ENABLED == 'true' && !contains(github.event.head_commit.message, '[planb]') }}
runs-on: windows-latest
needs: tag-release
@@ -136,11 +136,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Setup JDK 25 (Temurin)
- name: Setup JDK 21 (Temurin)
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '25'
java-version: '21'
- name: Gradle cache
uses: actions/cache@v4
@@ -179,11 +179,11 @@ jobs:
steps:
- name: Summary ausgeben
run: |
echo "## 🚀 Release ${{ needs.tag-release.outputs.version }}" >> $GITEA_STEP_SUMMARY
echo "" >> $GITEA_STEP_SUMMARY
echo "| Artefakt | Status |" >> $GITEA_STEP_SUMMARY
echo "|----------|--------|" >> $GITEA_STEP_SUMMARY
echo "| Linux .deb | ${{ needs.package-linux.result }} |" >> $GITEA_STEP_SUMMARY
echo "| Windows .msi | ${{ needs.package-windows.result }} |" >> $GITEA_STEP_SUMMARY
echo "" >> $GITEA_STEP_SUMMARY
echo "**Git-Tag:** \`${{ needs.tag-release.outputs.tag }}\`" >> $GITEA_STEP_SUMMARY
echo "## 🚀 Release ${{ needs.tag-release.outputs.version }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Artefakt | Status |" >> $GITHUB_STEP_SUMMARY
echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Linux .deb | ${{ needs.package-linux.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Windows .msi | ${{ needs.package-windows.result }} |" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Git-Tag:** \`${{ needs.tag-release.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
+40 -4
View File
@@ -1,7 +1,43 @@
#!/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" "$@"
# 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
+6 -4
View File
@@ -1,7 +1,9 @@
#!/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/render-plantuml.sh" "$@"
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
+133 -4
View File
@@ -1,7 +1,136 @@
#!/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" "$@"
# validate-links.sh - Link-Validierung für Projektdokumentation (`docs/**`).
# Zweck: Guardrail für die "Docs-as-Code"-Strategie.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
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
@@ -1,21 +0,0 @@
# .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
@@ -1,7 +0,0 @@
#!/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
@@ -1,7 +0,0 @@
#!/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" "$@"
+58
View File
@@ -0,0 +1,58 @@
---
type: Guide
status: ACTIVE
owner: Lead Architect
last_update: 2026-04-28
---
# Git Branching & Deployment Strategy (Meldestelle)
Um parallele Weiterentwicklung und stabile Feld-Tests zu ermöglichen, nutzen wir einen vereinfachten **GitHub Flow** mit Release-Tags. Da wir ein kleines Team (bzw. Solo-Entwickler mit KI-Agents) sind, verzichten wir auf übermäßig komplexe Git-Flow-Modelle (wie `develop`, `release/*`, `hotfix/*`), stellen aber Stabilität für Deployments sicher.
## 1. Branching-Struktur
### `main` (Source of Truth / Production)
* **Zweck:** Enthält *immer* den aktuellen, stabilen und im Feld getesteten/auslieferbaren Code.
* **Regel:** Direkte Commits auf `main` sind tabu (außer Notfall-Hotfixes).
* **Deployment:** Ein Push/Merge auf `main` bedeutet **nicht** zwingend ein sofortiges Deployment auf Zora, aber der Code ist *bereit* dafür.
### Feature Branches (`feature/*` oder `fix/*`)
* **Zweck:** Hier findet die eigentliche Entwicklung statt (z.B. neue Bounded Contexts, Wizards).
* **Namenskonvention:** `feature/event-wizard-neu`, `fix/zns-import-bug`
* **Lebensdauer:** So kurz wie möglich. Sobald ein Feature/Fix *in sich geschlossen* und lokal getestet ist, wird ein Pull Request (PR) auf `main` erstellt.
### Release Tags (`v1.x.x`)
* **Zweck:** Markiert einen spezifischen, stabilen Punkt auf dem `main`-Branch, der tatsächlich für ein Turnier (Feld-Test) deployed wurde.
* **Szenario:** Du hast Version `v1.2.0` (Plan-B) für ein Turnier deployed. Du entwickelst weiter auf `feature/*` und mergest in `main`. Das nächste Turnier bekommt dann Tag `v1.3.0`.
## 2. Der Workflow im Alltag
1. **Start:** `git checkout main` -> `git pull` -> `git checkout -b feature/mein-neues-feature`
2. **Entwicklung:** Arbeiten, KI-Agents nutzen, Commits machen.
3. **Abschluss:** Feature ist fertig.
4. **Merge:** Pull Request in Gitea erstellen (oder lokal: `git checkout main`, `git merge feature/mein-neues-feature`, `git push`).
5. **Aufräumen:** `git branch -d feature/mein-neues-feature`
## 3. Strategie für Feld-Tests (Turnier-Einsatz)
Wenn ein Turnier ansteht und ein stabiler Stand eingefroren werden muss:
1. Stelle sicher, dass `main` den gewünschten Zustand hat.
2. Setze einen Tag in Git: `git tag -a v1.2.0 -m "Release für Turnier in Neumarkt"`
3. Pushe den Tag: `git push origin v1.2.0`
4. **Deployment:** Das Deployment-Skript zieht sich *diesen* Tag auf Zora (oder baut den Docker-Container aus diesem Tag).
### Was passiert, wenn während des Turniers ein Bug auftritt (Hotfix)?
*Szenario: Das Turnier läuft auf `v1.2.0`. Auf `main` gibt es schon neuere Features (unfertig).*
1. Checkout des stabilen Tags: `git checkout -b hotfix/turnier-fix v1.2.0`
2. Bug fixen, committen.
3. Neuen Tag für das Deployment setzen: `git tag -a v1.2.1 -m "Hotfix ZNS Import"`
4. `git push origin v1.2.1` -> Fix wird auf Zora deployed.
5. **WICHTIG (Backport):** Damit der Fix nicht verloren geht, den Hotfix-Branch danach in `main` mergen: `git checkout main`, `git merge hotfix/turnier-fix`.
## 4. Gitea Actions (CI/CD)
* **Pushes auf `feature/*`:** Führen Code-Checks/Tests aus.
* **Pushes auf `main`:** Führen erweiterte Tests aus und bauen Docker-Images mit dem Tag `latest` sowie dem Git-SHA in die interne Registry (`10.0.0.22:3000`).
* **Erstellung eines Tags (`v*`):** Triggert automatisch den Build und Push von Docker-Images in die interne Registry. Das Image erhält den Namen des Tags (z.B. `:v1.2.0`). Dies ist die Basis für stabile Deployments auf Zora.
+2
View File
@@ -32,6 +32,8 @@ Deine Aufgaben:
6. **Handover:** Stelle Architekturentscheidungen nicht nur als Text, sondern auch als Diagramm (Mermaid/PlantUML) bereit.
7. Erstelle und pflege die MASTER ROADMAP. Du bist der "Hüter des Plans". Du delegierst Aufgaben an die spezialisierten Agenten (Backend, Frontend, DevOps, QA), führst sie aber nicht selbst aus, es sei denn, es betrifft direkt die Architektur oder das Build-System.
8. **Bounded Context Awareness:** Stelle sicher, dass Änderungen immer einem der 6 SCS (Self-Contained Systems) zugeordnet sind und die Grenzen gewahrt bleiben.
9. **Active Task Manifest:** Nutze die Datei `docs/ACTIVE_TASK.md`, um den aktuellen Arbeitsstand zu dokumentieren und für die nächste Session/KI bereitzustellen.
10. **Scout-Prinzip:** Wenn eine Aufgabe unklar ist, delegiere zuerst an Junie als "Scout", um Code-Snippets in `docs/04_Agents/Research_Snippet.md` zu sammeln, bevor architektonische Entscheidungen getroffen werden.
Don't:
- Implementiere keine Business-Logik in Backend-Services (→ Backend Developer).
+14 -2
View File
@@ -23,6 +23,7 @@ Ziel:
- Jede Session endet mit genau einem Artefakt in `docs/`.
- Veraltetes Wissen wird sauber archiviert.
- Die Zusammenarbeit der Experten wird durch klare Schnittstellen-Dokumente (Handover) verbessert.
- **Context-Handover:** Am Ende jeder Session wird ein standardisierter `🔄 NEXT SESSION CONTEXT` Block ausgegeben und die Datei `docs/ACTIVE_TASK.md` aktualisiert.
Regeln:
1. Single Source of Truth ist `docs/`.
@@ -31,11 +32,22 @@ Regeln:
- Reference / technische Wahrheit pro System (z.B. `docs/05_Backend/Services/<service>.md`)
- How-to / Runbook (passender Bereich)
- Journal Entry (`docs/99_Journal/`)
3. **Quality Gate:** Prüfe, ob die Artefakte den Standards entsprechen:
3. **Session-Abschluss Checkliste:**
- [ ] Wurden alle geänderten/neuen Dateien im Journal/Artefakt mit absolutem Pfad erwähnt?
- [ ] Wurde ein "Warum" dokumentiert (nicht nur das "Was")?
- [ ] Wurde die Datei `docs/ACTIVE_TASK.md` auf den neuesten Stand gebracht?
- [ ] Enthält die finale Antwort den `🔄 NEXT SESSION CONTEXT` Block?
4. **🔄 NEXT SESSION CONTEXT Struktur:**
- **Focus:** [SCS / Feature-Name]
- **Last State:** [Kurz-Zusammenfassung des aktuellen Stands]
- **Critical Files:** [Liste der wichtigsten Dateien für die nächste Session]
- **Open Threads:** [Offene Fragen oder nächste konkrete Schritte]
- **Agent-Handover:** [Spezifische Anweisungen für die nächste KI-Rolle]
5. **Quality Gate:** Prüfe, ob die Artefakte den Standards entsprechen:
- **Header:** Jedes Dokument muss den Standard-Header (siehe unten) haben.
- **Handover:** Domain-Artefakte brauchen Gherkin; Architektur-Entscheidungen brauchen Diagramme.
- **ADR-Pflicht:** Bei größeren Entscheidungen (z.B. Tech-Stack-Änderungen) muss ein ADR eingefordert werden.
4. **Lifecycle & Archivierung:**
6. **Lifecycle & Archivierung:**
- Veraltete Dokumente (z.B. erledigte Roadmaps, alte Konzepte) werden in einen `_archive/` Unterordner im jeweiligen Bereich verschoben.
- Dateiname bei Archivierung: `YYYY-MM-DD_OriginalName.md`.
- Status im Header auf `ARCHIVED` setzen.
+2
View File
@@ -17,6 +17,8 @@ Gemini wird genutzt für **Konzeptarbeit**: Varianten vergleichen, Argumente/Tra
* Immer 24 Optionen mit Vor-/Nachteilen liefern.
* Offene Fragen explizit als Liste zurückgeben.
* Formuliere Outputs so, dass sie **direkt** in ein `docs/*` Artefakt übernommen werden können.
* **Richter-Prinzip:** Nutze von Junie bereitgestellte Code-Snippets in `docs/04_Agents/Research_Snippet.md`, um fundierte Entscheidungen zu treffen, ohne den Code selbst im Detail lesen zu müssen.
* **Manifest-Pflicht:** Nutze die `docs/ACTIVE_TASK.md`, um den Kontext-Handover zwischen Sessions zu gewährleisten.
## Dont
* Keine Annahmen als Fakten verkaufen.
+2
View File
@@ -21,6 +21,8 @@ Junie wird genutzt für **Repo-nahe Arbeit**: Code lesen, reale Pfade/Module fin
## Dont
* Keine „zweite Wahrheit“ in `.junie/*` etablieren (Tooling bleibt Tooling).
* Keine Entscheidungen „im Chat verlieren“ am Ende muss ein Artefakt in `docs/` stehen.
* **Scout-Prinzip:** Agiere bei Bedarf als technischer Scout für Gemini. Sammele Code-Beweise und Snippets in `docs/04_Agents/Research_Snippet.md`, um architektonische Entscheidungen vorzubereiten.
* **Manifest-Pflicht:** Lies bei Session-Start immer zuerst die `MASTER_ROADMAP` und dann die `docs/ACTIVE_TASK.md`.
## Abschluss (Pflicht)
Am Ende der Session genau **ein** Artefakt gemäß `docs/03_Agents/README.md` erzeugen (oder aktualisieren).
@@ -0,0 +1,94 @@
---
type: How-to
status: ACTIVE
owner: DevOps Engineer
---
# Runbook: Caddy & Pangolin Deployment (Plan-B Setup)
Dieses Dokument sichert das Wissen über die Konfiguration von Caddy als Webserver/Reverse-Proxy in Kombination mit Pangolin-Tunneln, welches während der "Plan-B" Online-Nennung erarbeitet wurde.
## 1. Architektur-Übersicht
* **Pangolin:** Stellt den sicheren Tunnel vom lokalen Netzwerk (Zora) ins Internet her (ersetzt Cloudflare). Leitet Traffic auf spezifische lokale Ports weiter.
* **Caddy:** Agiert als Reverse-Proxy und TLS-Terminierungspunkt. Nimmt Traffic von Pangolin (und lokalem Netz) an und routet ihn zu den internen Docker-Services (z.B. Frontend-Web, API-Gateway).
## 2. Caddy Konfiguration (`Caddyfile`)
Die Konfiguration befindet sich in `config/docker/caddy/web-app/Caddyfile`.
### Wichtige Erkenntnisse / Fallstricke:
* **TLS/SSL:** Caddy wurde mit `auto_https off` konfiguriert, da die SSL-Terminierung extern (Pangolin/Edge) erfolgt. Caddy läuft intern auf Port 80.
* **Same-Origin Strategy:** Um CORS-Probleme im Browser zu vermeiden, werden alle API-Anfragen (`/api/*`) über Caddy an den `mail-service:8085` geproxt. Dies macht die App robuster gegen Browser-Security-Policies.
* **MIME-Types:** Explizite Setzung von `application/wasm` für `.wasm` Dateien ist für KMP-Web-Apps kritisch (siehe Snippet).
* **COOP/COEP Header:** Für WASM/KMP-Web-Apps sind `Cross-Origin-Embedder-Policy "require-corp"` und `Cross-Origin-Opener-Policy "same-origin"` essentiell, damit SharedArrayBuffer etc. funktionieren.
* **Caching:**
* Assets mit Hashes im Namen sind `immutable` (max-age 1 Jahr).
* `.wasm` und `.js` Dateien wurden während Plan-B auf `no-store, no-cache, must-revalidate` gesetzt, um sicherzustellen, dass Teilnehmer immer die aktuellste Logik erhalten.
* **Header-Weiterleitung:** Wichtige Header für das Backend: `X-Real-IP`, `X-Forwarded-For`, `X-Forwarded-Proto`.
### Aktuelles Plan-B Snippet:
```caddyfile
{
auto_https off
}
:80 {
root * /usr/share/caddy
header {
Cross-Origin-Embedder-Policy "require-corp"
Cross-Origin-Opener-Policy "same-origin"
}
# API Proxy (Same-Origin Strategy)
handle /api/* {
reverse_proxy mail-service:8085 {
header_up Host {upstream_hostport}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# Wasm MIME & Caching
@wasm path *.wasm
header @wasm Content-Type "application/wasm"
@wasm_js path *.wasm *.js
header @wasm_js Cache-Control "no-store, no-cache, must-revalidate"
# SPA Fallback
handle {
try_files {path} /index.html
file_server
}
}
```
## 3. Pangolin Konfiguration
### Erkenntnisse aus Plan-B:
* **Tunnel-Endpunkt:** Pangolin leitet den Traffic von der öffentlichen Domain (z.B. `meldestelle.mo-code.at`) auf den lokalen Port der Zora-Instanz weiter (standardmäßig Port 80, gemappt auf Host-Port via Docker).
* **Stabilität:** Der Pangolin-Client läuft als persistenter Dienst auf der Zora-Node. Er ist extrem stabil gegenüber IP-Wechseln des ISP (DSL-Reconnect).
* **Konfiguration:** Erfolgt primär über das Pangolin-Dashboard (Web-UI). Wichtig ist das Mapping der Resource auf die interne IP von Zora.
## 4. Deployment-Workflow (Erkenntnisse)
### SMTP-Härtung (Plan-B Mail-Service)
In `dc-planb.yaml` wurden folgende Einstellungen für World4You (SMTP) als stabil verifiziert:
```yaml
SPRING_MAIL_HOST: "smtp.world4you.com"
SPRING_MAIL_PORT: "587"
SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE: "true"
SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_REQUIRED: "true"
```
Wichtig: `STARTTLS_REQUIRED` verhindert den Versand, falls keine verschlüsselte Verbindung aufgebaut werden kann.
### Infrastruktur-Optimierung
* **Zero-Downtime:** `docker compose exec web-app caddy reload --config /etc/caddy/Caddyfile` ermöglicht Konfigurationsänderungen ohne Container-Neustart.
* **Health-Check:** Der `/health` Endpunkt in Caddy wurde genutzt, um die Erreichbarkeit des Containers zu prüfen.
* **In-Memory DB:** Für Plan-B wurde die H2-Datenbank (In-Memory) genutzt, da keine Persistenz über den Turnier-Zeitraum hinaus (außer E-Mail-Kopien) nötig war. Dies vereinfachte das Deployment massiv.
---
*Hinweis: Dieses Dokument basiert auf den erfolgreichen Feld-Tests vom April 2026.*
@@ -0,0 +1,45 @@
# Journal Entry: Prozess-Optimierung & TurnierAnlage Vorbereitung
---
type: Journal
status: ACTIVE
owner: Curator
last_update: 2026-04-28
---
## 📝 Zusammenfassung
In dieser Session haben wir die KI-Zusammenarbeit durch neue Protokolle geschärft und die Grundlage für den "TurnierAnlage"-Wizard in der Desktop-App gelegt.
## 🏗️ Architektur- & Prozess-Updates
- **Context-Handover Protokoll:** Einführung des `🔄 NEXT SESSION CONTEXT` Blocks zur nahtlosen Übergabe zwischen KI-Instanzen.
- **Active Task Manifest:** Erstellung von `docs/ACTIVE_TASK.md` als Single Source of Truth für den aktuellen Arbeitsstand.
- **Playbook Updates:**
- `Curator.md`: Neue Checkliste für den Session-Abschluss.
- `Architect.md`: Integration des "Scout-Prinzips" und Manifest-Pflicht.
- `Junie.md` & `Gemini.md`: Rollen-Schärfung (Scout vs. Richter).
## 🐎 TurnierAnlage (Event Management)
- **Status-Quo Analyse:**
- Backend: `Turnier.kt` ist bereits gut auf ÖTO-Validierungen vorbereitet.
- Frontend: `CreateBewerbWizardScreen.kt` existiert als Tab-UI, muss aber auf den `WizardOrchestrator` (ADR-0025) migriert werden.
- Flow: `EventWizardFlow.kt` ist noch ein Platzhalter.
- **Strategische Entscheidung:** Wir nutzen den neuen `WizardCore` für die TurnierAnlage, um komplexe ÖTO-Regelwerke (z.B. § 39 Abteilungstrennung) zustandsbasiert und mit klaren Guards abzubilden.
## 🛠️ CI/CD & Deployment (DevOps)
- **Gitea-Actions:** Erweiterung der `docker-publish.yaml`, um bei Git-Tags (`v*`) automatisch Docker-Images zu bauen.
- **Tagging-Logik:** Docker-Images erhalten nun dedizierte Tags aus Git, was stabile Rollbacks und Feld-Tests ermöglicht.
- **Dokumentation:** Update der `Git_Branching_Strategy.md` um die automatisierte Build-Logik.
## 🔗 Betroffene Dateien
- `docs/ACTIVE_TASK.md` (NEU)
- `docs/04_Agents/Playbooks/Curator.md` (Update)
- `docs/04_Agents/Playbooks/Architect.md` (Update)
- `docs/04_Agents/Playbooks/Junie.md` (Update)
- `docs/04_Agents/Playbooks/Gemini.md` (Update)
- `backend/services/events/events-domain/src/main/kotlin/at/mocode/events/domain/model/Turnier.kt` (Gelesen/Analyse)
## ✅ Session-Abschluss Checkliste
- [x] Dateipfade absolut erwähnt?
- [x] "Warum" dokumentiert?
- [x] `docs/ACTIVE_TASK.md` aktuell?
- [x] Handover-Block vorhanden?
+32
View File
@@ -0,0 +1,32 @@
# ⚡ ACTIVE TASK: Event- & TurnierAnlage-Wizard Migration
**Status:** 🏗️ In Arbeit
**SCS:** Event Management / Desktop App
**Branch:** `feature/turnier-anlage-wizard`
## 🎯 Aktuelles Ziel
1. **Event-Wizard Migration:** Migration des Veranstaltungs-Wizards auf den deklarativen Orchestrator (ADR-0025) abgeschlossen. ✓
2. **TurnierAnlage:** Implementierung des Wizards zur Anlage von Turnieren, Bewerben und Abteilungen nach ÖTO-Regeln in der Desktop-App.
3. **ÖTO-Validierung:** Integration der Abteilungs-Trennungs-Regeln (§ 39) als Warn-Logik im Wizard.
## 🛠️ Letzte Änderungen
- Event-Wizard: `EventFlowSample.kt` erfolgreich nach `EventWizardFlow.kt` migriert, umbenannt und um ÖTO-Schritte erweitert. ✓
- Wissens-Sicherung Plan-B: Caddy & Pangolin Runbook vervollständigt (MIME, COOP/COEP, SMTP-Härtung). ✓
- CI/CD: Gitea-Action für automatisierte Docker-Builds bei Git-Tags (`v*`) aktiviert. ✓
- TurnierAnlage: `TurnierAnlageFlow.kt` Skelett erstellt. ✓
## 📍 Fokus-Dateien
- `frontend/features/veranstaltung-feature/src/commonMain/kotlin/at/mocode/veranstaltung/feature/wizard/EventWizardFlow.kt`
- `frontend/features/turnier-feature/src/commonMain/kotlin/at/mocode/frontend/features/turnier/wizard/TurnierAnlageFlow.kt`
- `docs/03_Domain/02_Reference/OETO_Regelwerk/Abteilungs-Trennungs-Schwellenwerte.md`
- `frontend/features/turnier-feature/src/jvmMain/kotlin/at/mocode/frontend/features/turnier/presentation/CreateBewerbWizardScreen.kt`
## 🚧 Offene Punkte / Blocker
- [ ] Erstellung der Compose-Screens für `TurnierBasisdatenStep`.
- [ ] Erstellung der Compose-Screens für `TurnierKategorieStep`.
- [ ] Implementierung der ÖTO-Check Logik für Abteilungen.
- [ ] Sync-Logik zum Backend für die Web-Generierung vorbereiten.
## 🔄 Nächste Schritte
- [ ] Implementierung von `TurnierBasisdatenScreen` (Compose Desktop).
- [ ] Verknüpfung des `TurnierAnlageFlow` mit dem UI-Orchestrator.
+123
View File
@@ -0,0 +1,123 @@
A26128CSN-C-NEU CSNP-C-NEU NEUM2026042520260425CSN-C-Neu CSNP-C_Neu 2.2PSO v1.07
B010Stilspringprüfung - CSNP-C_N006000000001
C010001307002129000000000000000000000000000000000000021771000000
D001PG47Paddy's Nikita 170107Remplbauer Selina 00080000000 000000AUT*
D002PK06H-S Button 196040Gillinger Marlene 00067000000 000000AUT*
D003P824Pit 3 184759Krenn Eva 00055000000 000000AUT*
D004P814Balu 6 193244Remplbauer Sophia 00000000000 000000AUT
D004P901Daneder's Blitz 195501Weidinger Janina 00000000000 000000AUT
D004PB70Daneder's Caramello 163545Montgomery Helena 00000000000 000000AUT
B021Einlaufspringprüfung - CSN-C-Ne008000000002
C021001307002129000000000000000000000000000000000000021771000000
D0001781Ritual Do Vizo 126532Layr Bianca 00000000000 000000AUT*
D000P816Aldensfarm Breaking Dawn 159405Starzengruber Marie-Theres 00000000000 000000AUT*
D000P901Daneder's Blitz 195501Weidinger Janina 00000000000 000000AUT*
D000PB70Daneder's Caramello 163545Montgomery Helena 00000000000 000000AUT*
D000PE14SD Antonette 929451Mayrhofer Simon 00000000000 000000AUT*
D000PG47Paddy's Nikita 170107Remplbauer Selina 00000000000 000000AUT*
D000P824Pit 3 184759Krenn Eva 00003000000 000000AUT
D000P814Balu 6 193244Remplbauer Sophia 00000000000 000000AUT
B022Einlaufspringprüfung - CSN-C-Ne003000000002
C022001307002129000000000000000000000000000000000000021771000000
D000AR70Chocolate Kiss 2 147265Vanova Nina 00000000000 000000AUT*10258795
D000P561Ginger Bread Girl 153601Winter Maja Sophie 00000000000 000000AUT*
D997Z001Wildberry Gold RPZ 168660Zechmeister-Paster Diana A00000000000 000000AUT
B030Stilspringprüfung - CSNP-C_N006000000003
C030001307002129000000000000000000000000000000000000021771000000
D001PA53Rathcline Star 178474Schmidmayr Nena Sophie 00072000000 000000AUT*
D002P152Verena 3 170454Krenn Miriam 00070000000 000000AUT*
D003P816Aldensfarm Breaking Dawn 159405Starzengruber Marie-Theres 00068000000 000000AUT*
D004P561Ginger Bread Girl 153601Winter Maja Sophie 00067000000 000000AUT*
D997PE14SD Antonette 929451Mayrhofer Simon A00000000000 000000AUT
D997PK06H-S Button 196040Gillinger Marlene A00000000000 000000AUT
B041Einlaufspringprüfung - CSNP-C_N006000000004
C041001307002129000000000000000000000000000000000000021771000000
D0002M80Handsome 186927Lengauer Jelena 00000000000 000000AUT* 106KB09
D000AN19Exklusiv EM 187665Mück Hannah 00000000000 000000AUT*
D0001781Ritual Do Vizo 126532Layr Bianca 00040000000 000000AUT
D0004Y59Legolas 196 925183Schreiber Tamina 00047000000 000000GER
D000AB83HB Vijola 920327Reisinger Marlene 00056000000 000000AUT
D0003E99Quinet 906586Kapeller Emilia 00000000000 000000AUT
B042Einlaufspringprüfung - CSNP-C_N007000000004
C042001307002129000000000000000000000000000000000000021771000000
D0003K69Lillet 18 150620Reitetschläger Lena 00000000000 000000AUT*
D0005789Furiosa de la Bryere CE 140156Ehrentraut Carina 00000000000 000000AUT*
D000A099Quintessa 2 609548Aichinger Bianca 00000000000 000000AUT*
D0003M58Samantha 25 609771Karl Reinhard 00040000000 000000AUT
D000H606Moondancer 070156Alberer Manuela 00040000000 000000AUT
D000Z001Wildberry Gold RPZ 168660Zechmeister-Paster Diana 00092500000 000000AUT
D000AR70Chocolate Kiss 2 147265Vanova Nina 00129000000 000000AUT 10258795
B050Stilspringprüfung - CSNP-C_N003000000005
C050001307002129000000000000000000000000000000000000021771000000
D001P152Verena 3 170454Krenn Miriam 00074000000 000000AUT*
D002P985Taffy 2 193430Schartmüller Sarah 00072000000 000000AUT*
D003PA53Rathcline Star 906580Egger Julia 00065000000 000000AUT*
B061Stilspringprüfung - CSNP-C_N009000000006
C061001307002129000000000000000000000000000000000000021771000000
D0012B41Guccini 922710Simlinger Marlies 00075000000 000000AUT*
D002AN19Exklusiv EM 187665Mück Hannah 00072000000 000000AUT*
D0033E99Quinet 906586Kapeller Emilia 00071000000 000000AUT*
D0042M80Handsome 186927Lengauer Jelena 00070000000 000000AUT* 106KB09
D004AF41Cäsar 55 916541Dugandzic Sarah 00070000000 000000AUT*
D006PA53Rathcline Star 906580Egger Julia 00068000000 000000AUT
D0074Y59Legolas 196 925183Schreiber Tamina 00062000000 000000GER
D0083785Coeur 17 145963Obermüller Hannah 00061000000 000000AUT
D009AB83HB Vijola 920327Reisinger Marlene 00057000000 000000AUT
B062Stilspringprüfung - CSNP-C_N007000000006
C062001307002129000000000000000000000000000000000000021771000000
D001A099Quintessa 2 609548Aichinger Bianca 00082000000 000000AUT*
D0025789Furiosa de la Bryere CE 140156Ehrentraut Carina 00072000000 000000AUT*
D0033K69Lillet 18 150620Reitetschläger Lena 00067000000 000000AUT*
D004KSS1Charity Coke 053749Eichler Eva 00065000000 000000AUT*
D0053M58Samantha 25 609771Karl Reinhard 00060000000 000000AUT
D005H606Moondancer 070156Alberer Manuela 00060000000 000000AUT
D9971A11Gradan 102783Steyrer Anna A00000000000 000000AUT
B070Stilspringprüfung - CSNP-C_N002000000007
C070001307002129000000000000000000000000000000000000021771000000
D001Y001Bella Graziella 144315Gaugl Laura 00075000000 000000AUT*
D002P985Taffy 2 193430Schartmüller Sarah 00000000000 000000AUT
B080Springreiterbewerb - CSNP-C_N003000000008
C080001307002129000000000000000000000000000000000000021771000000
D0013785Coeur 17 145963Obermüller Hannah 00080000000 000000AUT*
D0022M80Handsome 186927Lengauer Jelena 00072000000 000000AUT* 106KB09
D9973E99Quinet 178474Schmidmayr Nena Sophie A00000000000 000000AUT
B091Standardspringprüfung - CSNP-C_N005000000009
C091001307002129000000000000000000000000000000000000021771000000
D0012062Grover 157407Pröll Leonie 00000005416 000000AUT*
D0022B41Guccini 160813Grubmüller Lea 00000005463 000000AUT*
D0031317Quality's Finest 612295Stroblmair Victoria 00000005492 000000AUT*
D0041A11Gradan 102783Steyrer Anna 00000005858 000000AUT*
D005KSS1Charity Coke 053749Eichler Eva 00040006428 000000AUT
B092Standardspringprüfung - CSNP-C_N007000000009
C092001307002129000000000000000000000000000000000000021771000000
D001A024D Day 075374Ambros Susanne 00000005940 000000AUT*10071068 108EH50
D0021G88Hamira 3 074007Beißmann Andreas 00000005991 000000AUT*
D0032G77S Mirrallas 605835Ellmer Kassandra 00000006298 000000AUT*
D0043966Capitaine 601366Madlmayr Carina 00040005862 000000AUT
D0051942Obora's Agnetha 601300Hofer Michaela 00040005966 000000AUT
D006Y001Bella Graziella 144315Gaugl Laura 00080005012 000000AUT
D9972785Herr Frodo 144315Gaugl Laura A00000000000 000000AUT
B100Springpferdeprüfung - CSN-C-Ne000000000010
C100001307002129000000000000000000000000000000000000021771000000
B110Stilspringprüfung - CSN-C-Ne002000000011
C110001307002129000000000000000000000000000000000000021771000000
D0012062Grover 157407Pröll Leonie 00085000000 000000AUT*
D0021317Quality's Finest 612295Stroblmair Victoria 00080000000 000000AUT*
B121Standardspringprüfung - CSN-C-Ne002000000012
C121001307002129000000000000000000000000000000000000021771000000
D0012062Grover 157407Pröll Leonie 00040005651 000000AUT*
D0022B41Guccini 160813Grubmüller Lea 00080005774 000000AUT
B122Standardspringprüfung - CSN-C-Ne004000000012
C122001307002129000000000000000000000000000000000000021771000000
D001AS94Landliebe 3 162776Höllmüller Anna 00000005557 000000AUT*10294537
D0022G77S Mirrallas 605835Ellmer Kassandra 00000006212 000000AUT*
D0031942Obora's Agnetha 601300Hofer Michaela 00000006723 000000AUT*
D004A024D Day 075374Ambros Susanne 00040005943 000000AUT 10071068 108EH50
B130Stilspringprüfung - CSN-C-Ne001000000013
C130001307002129000000000000000000000000000000000000021771000000
D0014258Casino East 601300Hofer Michaela 00075000000 000000AUT*
B140Standardspringprüfung - CSN-C-Ne003000000014
C140001307002129000000000000000000000000000000000000021771000000
D0012010Leonidas van de Zuuthoeve Z 145960Fischerlehner Leonie 00000005368 000000AUT*
D002AS94Landliebe 3 162776Höllmüller Anna 00000005745 000000AUT*10294537
D0034258Casino East 601300Hofer Michaela 00000006261 000000AUT*
Binary file not shown.
+96
View File
@@ -0,0 +1,96 @@
A26129CDN-C-NEU CDNP-C_NEU NEUM2026042620260426 2.2PSO v1.07
B010Dressurprüfung lzf CDN-C_Ne002000000001
C010000000038705000000000000000000000000000000000000000000000000
D001PB70Daneder's Caramello 163545Montgomery Helena 00062000000 000000AUT*
D0022892Amore 5 AUT Stadler Caroline 00060000000 000000AUT*
B020Dressurprüfung lzf CDN-C_Ne003000000002
C020000000038705000000000000000000000000000000000000000000000000
D0014208Sahib Silver G 195331Neuhauser Lara 00075000000 000000AUT*
D0022892Amore 5 AUT Stadler Caroline 00068000000 000000AUT*
D003PB70Daneder's Caramello 163545Montgomery Helena 00060000000 000000AUT*
B030Dressurreiterprüfung lzf CDN-C_Ne005000000003
C030035110000000000000000000000000000000000000000000000000000000
D001PC62Flora HP 917397Altendorfer Pia 00080000000 000000AUT*
D002Z002Abrakadabra S 107926Fürbäck Melanie 00077000000 000000AUT*
D0034Y59Legolas 196 184074Stöbich Enya 00068000000 000000AUT*
D004HKTBAcceptius FA 196261Salzinger Luisa Marie 00064000000 000000AUT
D005PA53Rathcline Star 922380Kropfreiter Ines 00062000000 000000AUT
B040Dressurreiterprüfung lzf CDN-C_Ne006000000004
C040000000038705000000000000000000000000000000000000000000000000
D001PC62Flora HP 917397Altendorfer Pia 00078000000 000000AUT*
D0024208Sahib Silver G 195331Neuhauser Lara 00076000000 000000AUT*
D003Z002Abrakadabra S 107926Fürbäck Melanie 00068000000 000000AUT*
D004HKTBAcceptius FA 196261Salzinger Luisa Marie 00064000000 000000AUT
D0054Y59Legolas 196 184074Stöbich Enya 00062000000 000000AUT
D006PA53Rathcline Star 922380Kropfreiter Ines 00060000000 000000AUT
B050Dressurreiterprüfung lzf CDN-C_Ne001000000005
C050035110000000000000000000000000000000000000000000000000000000
D001PF06Domino N AUT Stelzl Helena 00080000000 000000AUT*
B060Dressurreiterprüfung lzf CDN-C_Ne000000000006
C060035110000000000000000000000000000000000000000000000000000000
B070Pony Dressurprüfung A CSNP-C_N003000000007
C070000000038705000000000000000000000000000000000000000000000000
D001PT24Daneder's Captain 146663Steinmetz Sinah-Marie 00065000000 000000AUT*
D002P561Ginger Bread Girl 153601Winter Maja Sophie 00060000000 000000AUT*
D003PA53Rathcline Star 906592Emsenhuber Tanja 00058000000 000000AUT
B081Dressurreiterprüfung A CDN-C_Ne006000000008
C081035110038705000000000000000000000000000000000000000000000000
D0013888Ravasz 123156Scheiblechner Sonja 00072000000 000000AUT*
D0024307Makker 146066Gstöttenbauer Olivia 00064000000 000000AUT*
D003P561Ginger Bread Girl 153601Winter Maja Sophie 00062000000 000000AUT*
D0042083Light Blue 194297Hazoth Anna-Maria 00060000000 000000AUT*
D0053M58Samantha 25 609771Karl Reinhard 00058000000 000000AUT
D005KSS1Charity Coke 053749Eichler Eva 00058000000 000000AUT
B082Dressurreiterprüfung A CDN-C_Ne002000000008
C082035110038705000000000000000000000000000000000000000000000000
D001GIGIGigi D'Agostidinina 076742Klein Elisabeth 00068000000 000000AUT*10144403
D002A590Queeny 8 612592Panzirsch Anna 00064000000 000000AUT*
B091Dressurprüfung A CDN-C_Ne007000000009
C091035110038705000000000000000000000000000000000000000000000000
D0013888Ravasz 123156Scheiblechner Sonja 00070000000 000000AUT*
D002AN19Exklusiv EM 187665Mück Hannah 00064000000 000000AUT*
D0032083Light Blue 194297Hazoth Anna-Maria 00062000000 000000AUT*
D0044B66Vingino's Victory 616957Kiesenhofer Sarah 00058000000 000000AUT
D0053M58Samantha 25 609771Karl Reinhard 00055000000 000000AUT
D005KSS1Charity Coke 053749Eichler Eva 00055000000 000000AUT
D0074307Makker 146066Gstöttenbauer Olivia 00053000000 000000AUT
B092Dressurprüfung A CDN-C_Ne004000000009
C092035110038705000000000000000000000000000000000000000000000000
D001GIGIGigi D'Agostidinina 076742Klein Elisabeth 00068000000 000000AUT*10144403
D002AL46Superbunt 616836Lengauer Julia 00065000000 000000AUT*
D0032010Leonidas van de Zuuthoeve Z 145960Fischerlehner Leonie 00064000000 000000AUT*
D004A590Queeny 8 612592Panzirsch Anna 00058000000 000000AUT
B100Pony Dressurprüfung L CSNP-C_N001000000010
C100035110038705000000000000000000000000000000000000000000000000
D001P540Pieter V 153601Winter Maja Sophie 00056000000 000000AUT*
B110Dressurreiterprüfung L CDN-C_Ne003000000011
C110035110038705000000000000000000000000000000000000000000000000
D0011317Quality's Finest 612295Stroblmair Victoria 00074000000 000000AUT*
D0021F34Ferro Felicis 146066Gstöttenbauer Olivia 00064000000 000000AUT*
D003P540Pieter V 153601Winter Maja Sophie 00062000000 000000AUT*
B121Dressurprüfung L CDN-C_Ne003000000012
C121035110038705000000000000000000000000000000000000000000000000
D001AN19Exklusiv EM 187665Mück Hannah 00068000000 000000AUT*
D0021F34Ferro Felicis 146066Gstöttenbauer Olivia 00062000000 000000AUT*
D003AE11Merlin SH 061601Povacz Gisela 00060000000 000000AUT*
B122Dressurprüfung L CDN-C_Ne002000000012
C122035110038705000000000000000000000000000000000000000000000000
D001A024D Day 075374Ambros Susanne 00066000000 000000AUT*10071068 108EH50
D0023966Capitaine 601366Madlmayr Carina 00060000000 000000AUT*
B131Dressurpferdeprüfung A CDN-C_Ne003000000013
C131035110038705000000000000000000000000000000000000000000000000
D001AX99Bon Sai 102783Steyrer Anna 00073400000 000000AUT*
D0020214SHS Donna Verdi 169981Süss Sarah 00066000000 000000AUT*
D003PT24Daneder's Captain 146663Steinmetz Sinah-Marie 00061200000 000000AUT*
B132Dressurpferdeprüfung A CDN-C_Ne005000000013
C132035110038705000000000000000000000000000000000000000000000000
D001MAXIVerstappen 2 075374Ambros Susanne 00074600000 000000AUT*10071068
D0022H08SHS Weltmädel 169981Süss Sarah 00074200000 000000AUT*
D003P983Daneders Tornado 153601Winter Maja Sophie 00064800000 000000AUT*
D0044B03SHS Roubinjo 169981Süss Sarah 00064200000 000000AUT*
D0054B66Vingino's Victory 616957Kiesenhofer Sarah 00063000000 000000AUT*
B140Dressurpferdeprüfung L CDN-C_Ne003000000014
C140035110038705000000000000000000000000000000000000000000000000
D0012H08SHS Weltmädel 169981Süss Sarah 00067800000 000000AUT*
D002P983Daneders Tornado 153601Winter Maja Sophie 00064800000 000000AUT*
D0034B03SHS Roubinjo 169981Süss Sarah 00063000000 000000AUT*
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

+1
View File
@@ -24,6 +24,7 @@ kotlin {
sourceSets {
commonMain.dependencies {
api(projects.core.coreDomain)
implementation(libs.kotlinx.serialization.json)
}
@@ -35,6 +35,7 @@ kotlin {
implementation(projects.frontend.core.navigation)
implementation(projects.frontend.features.billingFeature)
implementation(projects.frontend.features.nennungFeature)
implementation(projects.frontend.core.wizard)
implementation(projects.core.znsParser)
implementation(compose.foundation)
@@ -0,0 +1,60 @@
package at.mocode.frontend.features.turnier.wizard
import at.mocode.core.domain.model.ReglementE
import at.mocode.core.domain.model.SparteE
import at.mocode.core.domain.model.TurnierkategorieE
import at.mocode.frontend.core.wizard.dsl.flow
import at.mocode.frontend.core.wizard.runtime.StepId
/**
* Definiert die Schritte für den Turnier-Anlage-Wizard.
* Orientiert sich an der ÖTO-Logik und dem SCS-Rahmen.
*/
sealed interface TurnierAnlageStep : StepId {
/** 1. Basisdaten (Name, Nummer, Datum, Sparte) */
data object Basisdaten : TurnierAnlageStep
/** 2. Kategorie & Reglement (CSN-C, CDN, etc. / ÖTO vs FEI) */
data object KategorieReglement : TurnierAnlageStep
/** 3. Funktionäre (TB, Parcoursbauer, etc.) */
data object Funktionaere : TurnierAnlageStep
/** 4. Nenn-Konfiguration (Nennschluss, Gebühren, Tauschbörse) */
data object NennKonfig : TurnierAnlageStep
/** 5. Zusammenfassung & Validierung (ÖTO-Warnungen prüfen) */
data object Summary : TurnierAnlageStep
}
/**
* Accumulator für den Turnier-Wizard.
* Sammelt die Daten, bevor sie als [DomTurnier] ans Backend gesendet werden.
*/
data class TurnierAnlageAcc(
val name: String = "",
val turnierNummer: String = "",
val sparte: SparteE = SparteE.SPRINGEN,
val kategorie: TurnierkategorieE = TurnierkategorieE.C,
val reglement: ReglementE = ReglementE.OETO,
val datum: String? = null, // ISO LocalDate
val tbId: String? = null,
val pbId: String? = null,
val nennschluss: String? = null, // ISO Instant
val nachnenngebuehrVerlangt: Boolean = false,
val nenntauschboerseAktiv: Boolean = false
)
/**
* Der Wizard-Flow für die Turnier-Anlage.
*/
val TurnierAnlageFlow = flow<TurnierAnlageStep, TurnierAnlageAcc>(start = TurnierAnlageStep.Basisdaten) {
step(TurnierAnlageStep.Basisdaten) {
otherwise(TurnierAnlageStep.KategorieReglement)
}
step(TurnierAnlageStep.KategorieReglement) {
otherwise(TurnierAnlageStep.Funktionaere)
}
step(TurnierAnlageStep.Funktionaere) {
otherwise(TurnierAnlageStep.NennKonfig)
}
step(TurnierAnlageStep.NennKonfig) {
otherwise(TurnierAnlageStep.Summary)
}
}
@@ -1,7 +1,76 @@
package at.mocode.veranstaltung.feature.wizard
// Platzhalter für den Event-Flow.
// Hinweis: Der echte Flow lebt zunächst als Demo in :frontend:core:wizard (samples),
// bis die VM-Delegation hinter dem Feature-Flag integriert wird.
import at.mocode.frontend.core.navigation.AppScreen
import at.mocode.frontend.core.wizard.dsl.flow
import at.mocode.frontend.core.wizard.runtime.Guard
import at.mocode.frontend.core.wizard.runtime.StepId
import at.mocode.frontend.core.wizard.runtime.WizardContext
import at.mocode.frontend.core.wizard.runtime.WizardState
object EventWizardPlaceholder
sealed interface EventWizardStep : StepId {
data object ZnsCheck : EventWizardStep
data object VeranstalterSelection : EventWizardStep
data object AnsprechpersonMapping : EventWizardStep
data object MetaData : EventWizardStep
data object TurnierKonfiguration : EventWizardStep
data object BewerbKonfiguration : EventWizardStep
data object AbteilungKonfiguration : EventWizardStep
data object Summary : EventWizardStep
}
data class EventWizardAcc(
val veranstalterId: String? = null,
val veranstalterNr: String = ""
)
object EventWizardGuards {
val hasZns: Guard<EventWizardStep, EventWizardAcc> = { ctx, _ ->
val stats = ctx.stats
if (stats == null) false
else {
val hasData = stats.vereinCount > 0
hasData && !stats.lastImport.isNullOrBlank()
}
}
val needsContactPerson: Guard<EventWizardStep, EventWizardAcc> = { _, acc ->
acc.veranstalterId == null || acc.veranstalterNr.startsWith("ORG-")
}
val hasSelectedVeranstalter: Guard<EventWizardStep, EventWizardAcc> = { _, acc ->
!acc.veranstalterId.isNullOrBlank()
}
}
val EventWizardFlow = flow<EventWizardStep, EventWizardAcc>(start = EventWizardStep.ZnsCheck) {
step(EventWizardStep.ZnsCheck) {
whenGuard("hasZns", EventWizardGuards.hasZns, go = EventWizardStep.VeranstalterSelection)
otherwise(EventWizardStep.VeranstalterSelection)
}
step(EventWizardStep.VeranstalterSelection) {
whenGuard("notSelected", { ctx, acc -> !EventWizardGuards.hasSelectedVeranstalter(ctx, acc) }, go = EventWizardStep.VeranstalterSelection)
whenGuard("needsContactPerson", EventWizardGuards.needsContactPerson, go = EventWizardStep.AnsprechpersonMapping)
otherwise(EventWizardStep.MetaData)
}
step(EventWizardStep.AnsprechpersonMapping) {
otherwise(EventWizardStep.MetaData)
}
step(EventWizardStep.MetaData) {
otherwise(EventWizardStep.TurnierKonfiguration)
}
step(EventWizardStep.TurnierKonfiguration) {
otherwise(EventWizardStep.BewerbKonfiguration)
}
step(EventWizardStep.BewerbKonfiguration) {
otherwise(EventWizardStep.AbteilungKonfiguration)
}
step(EventWizardStep.AbteilungKonfiguration) {
otherwise(EventWizardStep.Summary)
}
step(EventWizardStep.Summary) {
// End-Step
}
}
fun eventWizardStartState(origin: AppScreen, acc: EventWizardAcc = EventWizardAcc()): WizardState<EventWizardStep, EventWizardAcc> =
WizardState(current = EventWizardStep.ZnsCheck, acc = acc)