---
description: Mandanten & Governance — TenantContext, Sichtbarkeit, keine Schnellpfade
globs: backend/routers/*.py
alwaysApply: false
---

# Zugriffsschicht (Shinkan)

Vor neuen oder geänderten Endpoints in `backend/routers/` kurz prüfen:

1. **Pflichtlektüre** (bei inhaltsbezogenen APIs): `.claude/docs/technical/ACCESS_LAYER_AND_GOVERNANCE_PLAN.md` und `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md`.

2. **Auth**: kein geschützter Endpoint ohne `Depends(require_auth)` bzw. eingebettet in `Depends(get_tenant_context)` (der holt die Session bereits).

3. **Verein / Sichtbarkeit** (`visibility`, `club_id`, Mitglieder-Inhalte):  
   `tenant: TenantContext = Depends(get_tenant_context)` verwenden; Profile-ID aus `tenant.profile_id`, Rolle aus `tenant.global_role`.

4. **Bibliothekslisten** (Übungen, Vorlagen, Rahmenprogramme, Progressionsgraphen, gleiche Semantik): Filter über `library_content_visibility_sql(...)` bzw. gleiche Leseregel wie bestehende Module — nicht „alle Zeilen aus SELECT“.

5. **Schreiben mit Governance**: bei `visibility`/`club_id`-Änderungen `assert_valid_governance_visibility`; bei `club` ohne `club_id` im Body → `tenant.effective_club_id` oder klare 400-Hinweise (wie bei Übungen).

6. **Ausnahmen** nur mit kurzem Kommentar im Code: `# ACCESS_LAYER exempt: …` und Eintrag im Audit oder in `backend/scripts/check_access_layer_hints.py` (EXEMPT-Liste).

7. **Nach Merge**: eine Zeile `.claude/docs/working/ACCESS_LAYER_ENDPOINT_AUDIT.md` anpassen, wenn sich Tenant oder Governance ändert.

```python
# ❌ Schnellpfad: nur Session, obwohl Vereinsdaten betroffen
@router.get("/foo")
def foo(session=Depends(require_auth)):
    ...

# ✅ Konsistent zu clubs/exercises/planning
@router.get("/foo")
def foo(tenant: TenantContext = Depends(get_tenant_context)):
    ...
```
