AP0.3 Abschlussbericht v0.3: Prod-Deploy-Nacharbeit und DB-Passwort-Doku.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
39e8084b1a
commit
f8af47d1d5
|
|
@ -25,7 +25,17 @@ jobs:
|
||||||
fi
|
fi
|
||||||
docker compose build --no-cache backend frontend
|
docker compose build --no-cache backend frontend
|
||||||
echo "✓ Backend + Frontend gebaut (Frontend: npm run build im Dockerfile)"
|
echo "✓ Backend + Frontend gebaut (Frontend: npm run build im Dockerfile)"
|
||||||
docker compose up -d --wait
|
if ! docker compose up -d --wait; then
|
||||||
curl -sf http://localhost:8004/api/health && echo "✓ PROD API /api/health OK"
|
echo "✗ compose up --wait fehlgeschlagen — Backend-Logs:"
|
||||||
|
docker compose logs backend --tail 150 || true
|
||||||
|
docker compose ps || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! curl -sf http://localhost:8004/api/health; then
|
||||||
|
echo "✗ PROD API nicht erreichbar — Backend-Logs:"
|
||||||
|
docker compose logs backend --tail 150 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ PROD API /api/health OK"
|
||||||
curl -sf http://localhost:3004/api/health && echo "✓ PROD Frontend-Proxy /api/health OK"
|
curl -sf http://localhost:3004/api/health && echo "✓ PROD Frontend-Proxy /api/health OK"
|
||||||
echo "=== Kairo PROD Deploy complete ==="
|
echo "=== Kairo PROD Deploy complete ==="
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,24 @@ Aktuell keine Medien-Speicherung. Bei Bedarf: NAS-Mount + `docker-compose.overri
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Dev-Datenbank wechseln
|
## Datenbank-Passwort (Dev & Prod)
|
||||||
|
|
||||||
PostgreSQL im Compose-Stack initialisiert User/Passwort **nur beim ersten Start** des Volumes (`dev-kairo-db-data`).
|
PostgreSQL im Compose-Stack initialisiert User/Passwort **nur beim ersten Start** des Volumes.
|
||||||
|
Wenn `DB_PASSWORD` in `.env` nicht mehr zum Volume passt, scheitert das Backend mit
|
||||||
|
`password authentication failed for user "kairo_user"` — der Container bleibt `unhealthy`.
|
||||||
|
|
||||||
|
**Prod (Daten behalten):** Passwort in der laufenden DB an `.env` anpassen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/lars/docker/kairo
|
||||||
|
set -a && source .env && set +a
|
||||||
|
docker compose exec -T postgres psql -U kairo_user -d kairo \
|
||||||
|
-c "ALTER USER kairo_user WITH PASSWORD '${DB_PASSWORD}';"
|
||||||
|
docker compose restart backend
|
||||||
|
curl -sf http://localhost:8004/api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dev (Volume neu, inkl. Testdaten):** Volume löschen und neu starten (`dev-kairo-db-data`).
|
||||||
Wenn du `DB_NAME`, `DB_USER` oder `DB_PASSWORD` in `.env` änderst, muss das Volume neu angelegt werden:
|
Wenn du `DB_NAME`, `DB_USER` oder `DB_PASSWORD` in `.env` änderst, muss das Volume neu angelegt werden:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
322
docs/sprints/Sprint0_AP0_3_Completion_Report_v0.3.md
Normal file
322
docs/sprints/Sprint0_AP0_3_Completion_Report_v0.3.md
Normal file
|
|
@ -0,0 +1,322 @@
|
||||||
|
# AP0.3 – Abschlussbericht Capability / Rights Registry & Entitlements Snapshot
|
||||||
|
|
||||||
|
**Status:** abgeschlossen
|
||||||
|
**Stand:** 2026-07-04 (final inkl. Prod-Deploy-Nacharbeit)
|
||||||
|
**Branch:** `develop` · Dev-Deploy und Test Suite grün · **Prod verifiziert** (Schema `004`, Health ok)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Scope und Einordnung
|
||||||
|
|
||||||
|
AP0.3 liefert die **minimale Capability-/Rights-Grundlage** und den **Entitlements-Snapshot** für Sprint 0. Auth, TenantContext und Sessions stammen aus AP0.2; AP0.3 ergänzt Registry, DB-Sync, zentrale Capability-Prüfung und `/api/me/entitlements`.
|
||||||
|
|
||||||
|
| Anforderung (AP0.3) | Status |
|
||||||
|
|---------------------|--------|
|
||||||
|
| Migration Capabilities + Role-Grants | ✓ |
|
||||||
|
| Runtime Registry + Modul-Registrierungen | ✓ |
|
||||||
|
| Startup-Sync Registry → DB | ✓ |
|
||||||
|
| TenantContext.capabilities | ✓ |
|
||||||
|
| `require_capability()` Dependency | ✓ |
|
||||||
|
| `GET /api/me/entitlements` | ✓ |
|
||||||
|
| Beispiel-Endpoint mit Capability-Gate | ✓ |
|
||||||
|
| Tests (Registry, Sync, Auflösung, Enforce/Probe) | ✓ |
|
||||||
|
| Trennung Auth / Capability / Feature | ✓ |
|
||||||
|
|
||||||
|
**Bewusst nicht in AP0.3:** Feature-Limits, Billing, Usage-Zähler, Prompt Registry, produktive Admin-UI, Governance/Object-ACL.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Umgesetzte Dateien
|
||||||
|
|
||||||
|
| Datei | Zweck |
|
||||||
|
|-------|--------|
|
||||||
|
| `backend/migrations/004_capabilities_registry.sql` | Tabellen `capabilities`, `role_capability_grants` |
|
||||||
|
| `backend/rights_registry.py` | In-Memory-Registry, Validierung, DB-Sync, Grant-Lookup |
|
||||||
|
| `backend/rights_registrations/__init__.py` | Side-Effect-Import aller Modul-Registrierungen |
|
||||||
|
| `backend/rights_registrations/platform.py` | Platform-Capabilities |
|
||||||
|
| `backend/rights_registrations/tenant_ops.py` | Tenant-/Actor-Capabilities |
|
||||||
|
| `backend/capabilities.py` | Auflösung, `check_capability`, `require_capability` |
|
||||||
|
| `backend/entitlements.py` | Entitlements-Snapshot-Builder |
|
||||||
|
| `backend/sync_rights_registry.py` | CLI-Sync beim Container-Start |
|
||||||
|
| `backend/tenant_context.py` | `TenantContext.capabilities` |
|
||||||
|
| `backend/routers/me.py` | `/entitlements`, `/admin/demo` |
|
||||||
|
| `backend/entrypoint.sh` | Rights-Sync nach Seeds |
|
||||||
|
| `backend/version.py` | `0.3.0-ap0.3`, Schema `004` |
|
||||||
|
| `backend/tests/test_rights_registry.py` | Registry + Sync |
|
||||||
|
| `backend/tests/test_capabilities.py` | Grants, enforce/probe, Demo-Endpoint |
|
||||||
|
| `backend/tests/test_entitlements.py` | Snapshot-Form |
|
||||||
|
| `backend/tests/conftest.py` | Registry-Sync in Test-Session |
|
||||||
|
| `frontend/src/App.jsx` | Header AP0.3; `/api/me/context` zeigt Capabilities |
|
||||||
|
| `README.md` | API-Doku AP0.3 |
|
||||||
|
| `.gitea/workflows/deploy-prod.yml` | Backend-Logs bei `--wait`-Fehler (Parität zu Dev) |
|
||||||
|
| `docs/DEPLOYMENT.md` | Abschnitt DB-Passwort Dev/Prod inkl. `ALTER USER`-Fix |
|
||||||
|
|
||||||
|
**Wesentliche Commits:** `910b1d7` (Implementierung), `eaa0e25` (pytest-Fix), `4069cb0` (GUI-Sprint-Label), `39e8084` (Abschlussbericht v0.2).
|
||||||
|
|
||||||
|
**Nacharbeit (noch auf `develop`):** `deploy-prod.yml`, `DEPLOYMENT.md`, dieser Bericht v0.3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Neue Migrationen
|
||||||
|
|
||||||
|
**`004_capabilities_registry.sql`**
|
||||||
|
|
||||||
|
- **`capabilities`** — `capability_key`, `module`, `description`, `is_active`
|
||||||
|
- **`role_capability_grants`** — `role_scope` (`portal` \| `tenant`), `role_code`, `capability_key`
|
||||||
|
|
||||||
|
Kein Feature-/Limit-Schema (Vorbereitung AP0.4).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Registry-Struktur
|
||||||
|
|
||||||
|
```
|
||||||
|
backend/
|
||||||
|
├── rights_registry.py
|
||||||
|
├── rights_registrations/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── platform.py # admin, context, entitlements
|
||||||
|
│ └── tenant_ops.py # tenant.manage, actor.manage
|
||||||
|
└── sync_rights_registry.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Modell:** `@dataclass(frozen=True) CapabilityRegistration` mit `key`, `module`, `description`, `default_grants`.
|
||||||
|
|
||||||
|
**Startup:** `entrypoint.sh` → Migrationen → Seeds → `python sync_rights_registry.py` → Uvicorn (`SKIP_RIGHTS_SYNC=1` zum Überspringen).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Endpoints
|
||||||
|
|
||||||
|
| Endpoint | Methode | Auth / Gate | Status |
|
||||||
|
|----------|---------|-------------|--------|
|
||||||
|
| `/api/me/context` | GET | Session | geändert — `capabilities[]` |
|
||||||
|
| `/api/me/entitlements` | GET | Session | neu |
|
||||||
|
| `/api/me/admin/demo` | GET | Session + `kairo.admin.access` | neu (Beispiel `require_capability`) |
|
||||||
|
|
||||||
|
OpenAPI (Dev): `/api/docs`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Capability-Auflösungslogik
|
||||||
|
|
||||||
|
```
|
||||||
|
Session (portal_role, active_tenant_id)
|
||||||
|
→ tenant_role aus Membership (optional)
|
||||||
|
→ load_grants_for_roles(portal_role, tenant_role)
|
||||||
|
→ Union Portal-Grants + Tenant-Grants
|
||||||
|
→ TenantContext.capabilities (frozenset)
|
||||||
|
```
|
||||||
|
|
||||||
|
**`require_capability(key)`:** prüft Mitgliedschaft in `ctx.capabilities`.
|
||||||
|
|
||||||
|
| Modus | Env | Verhalten |
|
||||||
|
|-------|-----|-----------|
|
||||||
|
| probe | `CAPABILITY_ENFORCE=probe` (Default) | Zugriff erlaubt; Verweigerung → Audit `capability.denied` |
|
||||||
|
| enforce | `CAPABILITY_ENFORCE=enforce` | HTTP 403 ohne Grant |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Initiale Capabilities
|
||||||
|
|
||||||
|
| Capability | Portal admin | Portal user | Tenant owner | Tenant admin | Tenant member |
|
||||||
|
|------------|:------------:|:-----------:|:------------:|:------------:|:-------------:|
|
||||||
|
| `kairo.admin.access` | ✓ | | | | |
|
||||||
|
| `kairo.tenant.manage` | ✓ | | ✓ | ✓ | |
|
||||||
|
| `kairo.actor.manage` | ✓ | | ✓ | ✓ | |
|
||||||
|
| `kairo.context.read` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| `kairo.entitlements.read` | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Tests und Verifikation
|
||||||
|
|
||||||
|
### pytest (CI, Dev-Container)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose -f docker-compose.dev-env.yml exec backend python -m pytest tests -ra -vv
|
||||||
|
```
|
||||||
|
|
||||||
|
| Testdatei | Abdeckung | Status |
|
||||||
|
|-----------|-----------|--------|
|
||||||
|
| `test_rights_registry.py` | 5 Capabilities, Sync idempotent | ✓ |
|
||||||
|
| `test_capabilities.py` | Admin/Member enforce, Owner-Grants, probe | ✓ |
|
||||||
|
| `test_entitlements.py` | Snapshot-Struktur | ✓ |
|
||||||
|
| `test_migrations.py` | Migration 004 | ✓ |
|
||||||
|
| Gesamt Test Suite (Gitea) | nach Push `develop` | ✓ |
|
||||||
|
|
||||||
|
### Manuell (Dev-GUI)
|
||||||
|
|
||||||
|
| Prüfung | Ergebnis |
|
||||||
|
|---------|----------|
|
||||||
|
| Login `lars@stommer.com` | ✓ |
|
||||||
|
| `/api/me/context` → `capabilities` sichtbar | ✓ |
|
||||||
|
| API Health → `"schema": "004"` | ✓ |
|
||||||
|
| Header Sprint-Anzeige AP0.3 | ✓ |
|
||||||
|
|
||||||
|
**Hinweis:** Entitlements und Admin-Demo sind in der GUI noch nicht als eigene Karten — prüfbar über Swagger (`/api/docs`) oder DevTools.
|
||||||
|
|
||||||
|
### Production (nach Nacharbeit 2026-07-04)
|
||||||
|
|
||||||
|
| Prüfung | Ergebnis |
|
||||||
|
|---------|----------|
|
||||||
|
| `curl http://localhost:8004/api/health` | ✓ — `0.3.0-ap0.3`, Schema `004`, `db: ok` |
|
||||||
|
| `curl http://localhost:3004/api/health` | ✓ |
|
||||||
|
| Migration 004 auf Prod-Volume | ✓ (nach DB-Reconnect) |
|
||||||
|
| Rights-Sync beim Start | ✓ |
|
||||||
|
| Bootstrap-Admin (`.env`) | unverändert nutzbar |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Prod-Deploy-Nacharbeit (Gitea `deploy-prod` #7154)
|
||||||
|
|
||||||
|
### Symptom
|
||||||
|
|
||||||
|
Push auf `main` → Workflow `deploy-prod` schlug fehl:
|
||||||
|
|
||||||
|
```
|
||||||
|
dependency failed to start: container kairo-api is unhealthy
|
||||||
|
```
|
||||||
|
|
||||||
|
Backend-Logs (Pi):
|
||||||
|
|
||||||
|
```
|
||||||
|
[FAIL] ... FATAL: password authentication failed for user "kairo_user"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ursache
|
||||||
|
|
||||||
|
**Kein AP0.3-Codefehler.** PostgreSQL initialisiert User/Passwort nur beim **ersten Start** des Volumes (`kairo-db-data`). Das Passwort in `/home/lars/docker/kairo/.env` wich vom in der DB gespeicherten Passwort ab — identisches Muster wie beim Dev-Deploy (#7100).
|
||||||
|
|
||||||
|
Der Entrypoint bricht bei DB-Auth-Fehlern fail-fast ab (seit AP0.2/`f8b3ae3`); Uvicorn startet nicht → Healthcheck scheitert nach wenigen Sekunden.
|
||||||
|
|
||||||
|
### Behebung (Prod, ohne Datenverlust)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/lars/docker/kairo
|
||||||
|
set -a && source .env && set +a
|
||||||
|
docker compose exec -T postgres psql -U kairo_user -d kairo \
|
||||||
|
-c "ALTER USER kairo_user WITH PASSWORD '${DB_PASSWORD}';"
|
||||||
|
docker compose restart backend
|
||||||
|
docker compose up -d --wait
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach: alle Container `healthy`, API und Frontend-Proxy antworten.
|
||||||
|
|
||||||
|
### Prävention / Doku
|
||||||
|
|
||||||
|
| Maßnahme | Datei |
|
||||||
|
|----------|-------|
|
||||||
|
| Prod-Fix ohne Volume-Löschung dokumentiert | `docs/DEPLOYMENT.md` § Datenbank-Passwort |
|
||||||
|
| Backend-Logs bei `compose up --wait`-Fehler | `.gitea/workflows/deploy-prod.yml` (wie Dev) |
|
||||||
|
|
||||||
|
**Wichtig:** `docker compose down -v` auf Prod löscht alle Anwendungsdaten. Bei Passwort-Wechsel in `.env` bevorzugt `ALTER USER` verwenden.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Deployment & Betrieb
|
||||||
|
|
||||||
|
| | Development | Production |
|
||||||
|
|---|-------------|------------|
|
||||||
|
| Verzeichnis | `/home/lars/docker/kairo-dev` | `/home/lars/docker/kairo` |
|
||||||
|
| Ports | 3097 / 8097 | 3004 / 8004 |
|
||||||
|
| Postgres | eigener Container + Volume | eigener Container + Volume (`kairo-db-data`) |
|
||||||
|
| Dev-Seeds | ja | nein |
|
||||||
|
| Admin | `lars@stommer.com` (Seed) | `KAIRO_BOOTSTRAP_*` in `.env` |
|
||||||
|
| Schema nach AP0.3 | `004` | `004` |
|
||||||
|
|
||||||
|
**Startup-Reihenfolge:** Entrypoint → Schema-Migrationen → Data-Seeds → Rights-Sync → Uvicorn.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Übernommene Muster aus Shinkan
|
||||||
|
|
||||||
|
| Muster | Kairo-Umsetzung |
|
||||||
|
|--------|-----------------|
|
||||||
|
| Registry-first | `rights_registry.py` + `rights_registrations/` |
|
||||||
|
| `register_capability()` mit Validierung | Pflichtfelder `key`, `module`, `description` |
|
||||||
|
| Frozen Dataclass-Definitionen | `CapabilityRegistration` |
|
||||||
|
| Startup-Sync Registry → DB | `sync_rights_registry_to_db()` |
|
||||||
|
| Modul-Ownership | `platform`, `tenant` |
|
||||||
|
| Default Grants in Code | `default_grants` pro Capability |
|
||||||
|
| `require_capability` Dependency | `capabilities.py` |
|
||||||
|
| `/api/me/entitlements` Snapshot | `entitlements.py` |
|
||||||
|
| Probe vs. Enforce | `CAPABILITY_ENFORCE` |
|
||||||
|
|
||||||
|
**Nicht übernommen:** `club_id`, Vereinsrollen, Übungs-/Trainingsrechte, Club-Feature-Kontingente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Übernommene Muster aus Mitai
|
||||||
|
|
||||||
|
| Muster | Kairo-Umsetzung |
|
||||||
|
|--------|-----------------|
|
||||||
|
| Entitlements als zentrale Schicht | `build_entitlements_snapshot()` |
|
||||||
|
| Probe-vs-Enforce-Denken | Default `probe`, optional `enforce` |
|
||||||
|
| Feature-Registry vorbereitet | `features: {}` im Snapshot |
|
||||||
|
|
||||||
|
**Nicht übernommen:** Tier/Subscription, Usage-Zähler, Billing, Legacy `check_feature_access`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Bewusst nicht übernommen
|
||||||
|
|
||||||
|
- Shinkan-/Mitai-Domänenbegriffe (`club`, `profile`, Tier)
|
||||||
|
- Feature-Limits, Billing, Coupons
|
||||||
|
- Prompt Registry, Vorhaben-/Projektlogik
|
||||||
|
- MCP, produktive Admin-UI (nur Debug-JSON + Swagger)
|
||||||
|
- Capabilities nur in SQL pflegen
|
||||||
|
- Governance/Object-ACL
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Abweichungen von Designprinzipien
|
||||||
|
|
||||||
|
| Thema | Abweichung | Begründung |
|
||||||
|
|-------|------------|------------|
|
||||||
|
| Entitlements-Struktur | Account- + Tenant-Block | Kairo Hybrid laut Family Entitlement Model |
|
||||||
|
| Feature-Registry | leeres Objekt | AP0.4 |
|
||||||
|
| Grant-Overrides | Sync nur additive Grants | Sprint-0-Minimal |
|
||||||
|
| `linked_feature_id` | nicht modelliert | Keine Limits in AP0.3 |
|
||||||
|
| Frontend | kein Entitlements-Panel | Minimal-UI; AP0.4/UX optional |
|
||||||
|
|
||||||
|
**Eingehalten:** zentrale Capability-Prüfung; Portal- vs. Tenant-Rolle getrennt; TenantContext als Auflösungsschicht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Offene Entscheidungen
|
||||||
|
|
||||||
|
1. **Prod `CAPABILITY_ENFORCE=enforce`** — wann umstellen?
|
||||||
|
2. **Grant-Overrides in DB** vs. reiner Code-Kanon
|
||||||
|
3. **Prod-pytest-Cleanup** (`*@example.com`) — aus AP0.2 offen
|
||||||
|
4. **Frontend:** Entitlements-Karte + Admin-Demo-Button
|
||||||
|
5. **Feature-Registry** — AP0.4
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. Empfehlung für AP0.4
|
||||||
|
|
||||||
|
Laut Foundation **AP0.4 – Prompt, Feature, Config Registry**:
|
||||||
|
|
||||||
|
- Feature Registry (Metadaten, ohne Billing-Limits in Sprint 0)
|
||||||
|
- PromptTemplate / PromptVersion / Placeholder
|
||||||
|
- ConfigurationEntry
|
||||||
|
- Prompt-Rendering mit Placeholder-Validation
|
||||||
|
- `features`-Block in Entitlements mit Registry-Sync
|
||||||
|
|
||||||
|
Optional vor AP0.4: Prod-Cleanup-Seed; GUI-Entitlements; `CAPABILITY_ENFORCE=enforce` in Prod nach Kalibrierung.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Referenzen
|
||||||
|
|
||||||
|
- `docs/sprints/Jinkendo_Kairo_04_Sprint0_Foundation_v0.3.md` § AP0.3, §9
|
||||||
|
- `docs/reference/design-principles/shinkan/RIGHTS_REGISTRY_DESIGN_PRINCIPLES.md`
|
||||||
|
- `docs/reference/design-principles/shinkan/CAPABILITY_ENTITLEMENT_DESIGN_PRINCIPLES.md`
|
||||||
|
- `docs/reference/design-principles/alignment/FAMILY_ENTITLEMENT_MODEL.md`
|
||||||
|
- `docs/DEPLOYMENT.md` — DB-Passwort Dev/Prod
|
||||||
|
- Vorgänger: `docs/sprints/Sprint0_AP0_2_Completion_Report_v0.2.md`, `Sprint0_AP0_3_Completion_Report_v0.2.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Abgeschlossen im Rahmen Sprint 0 – AP0.3. Ersetzt `Sprint0_AP0_3_Completion_Report_v0.2.md`.*
|
||||||
Loading…
Reference in New Issue
Block a user