chore: install refactoring guardrails (Phase 0) [guardrail-change]

- scripts/check-loc.sh: LOC budget checker (500 LOC hard cap)
- .claude/rules/architecture.md: split triggers, patterns per language
- .claude/rules/loc-exceptions.txt: documented escape hatches
- AGENTS.python.md: FastAPI conventions (routes thin, service layer)
- AGENTS.go.md: Go/Gin conventions (handler ≤40 LOC)
- AGENTS.typescript.md: Next.js conventions (page.tsx ≤250 LOC, colocation)
- CLAUDE.md extended with guardrail section + commit markers

273 files currently exceed 500 LOC — to be addressed phase by phase.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-23 12:25:36 +02:00
parent 9b0e310978
commit 5abdfa202e
8 changed files with 314 additions and 0 deletions

View File

@@ -256,3 +256,45 @@ ssh macmini "cd /Users/benjaminadmin/Projekte/breakpilot-lehrer && git push all
| `website/app/admin/klausur-korrektur/` | Korrektur-Workspace |
| `backend-lehrer/classroom_api.py` | Classroom Engine |
| `backend-lehrer/state_engine_api.py` | State Engine |
---
## Code-Qualitaet Guardrails (NON-NEGOTIABLE)
> Vollstaendige Details: `.claude/rules/architecture.md`
> Ausnahmen: `.claude/rules/loc-exceptions.txt`
### File Size Budget
- **Hard Cap: 500 LOC** pro Datei
- Wenn eine Aenderung eine Datei ueber 500 LOC bringen wuerde: **erst splitten, dann aendern**
- Ausnahmen nur mit Begruendung in `loc-exceptions.txt` + `[guardrail-change]` Commit-Marker
### Architektur
- **Python:** Routes duenn → Business Logic in Services → Persistenz in Repositories
- **Go:** Handler ≤40 LOC → Service-Layer → Repository-Pattern
- **TypeScript/Next.js:** page.tsx duenn → Server Actions, Queries, Components auslagern
- **Types:** Monolithische types.ts frueh splitten, types.ts + types/ Shadowing vermeiden
### Workflow (bei jeder Aenderung)
1. Datei lesen + LOC pruefen
2. Wenn nahe am Budget → erst splitten
3. Minimale kohaerente Aenderung
4. Verifikation (Tests + Lint)
5. Zusammenfassung: Was geaendert, was verifiziert, Restrisiko
### Commit-Marker
- `[migration-approved]` — Schema-/Migrations-Aenderungen
- `[guardrail-change]` — Aenderungen an .claude/**, scripts/check-loc.sh
- `[split-required]` — Aenderung beginnt mit Datei-Split
- `[interface-change]` — Public API Contracts geaendert
### LOC-Check ausfuehren
```bash
bash scripts/check-loc.sh --changed # nur geaenderte Dateien
bash scripts/check-loc.sh --all # alle Dateien (zeigt alle Violations)
```

View File

@@ -0,0 +1,46 @@
# Architecture Rule — BreakPilot Lehrer
## File Size Budget
Hard default: **500 LOC max** per file.
Soft targets:
- Handler/Router/Service: 300-400 LOC
- Models/Schemas/Types: 200-300 LOC
- Utilities: 100-200 LOC
Ausnahmen nur in `.claude/rules/loc-exceptions.txt` mit Begruendung.
## Split-Trigger
Sofort splitten wenn:
- Datei ueberschreitet 500 LOC
- Datei wuerde nach Aenderung 500 LOC ueberschreiten
- Datei mischt Transport + Business Logic + Persistence
- Datei enthaelt mehrere unabhaengig testbare Verantwortlichkeiten
## Python (backend-lehrer, klausur-service, voice-service)
- Routes duenn halten — Business Logic in Services
- Persistenz in Repositories/Data-Access-Module
- Pydantic Schemas nach Domain splitten
- Zirkulaere Imports vermeiden
## Go (school-service, edu-search-service)
- Handler duenn halten (≤40 LOC)
- Business Logic in Services/Use-Cases
- Transport/Request-Decoding getrennt von Domain-Logik
## TypeScript / Next.js (admin-lehrer, studio-v2, website)
- page.tsx duenn halten — Server Actions, Queries, Forms auslagern
- Monolithische types.ts frueh splitten
- types.ts + types/ Shadowing vermeiden
- Shared Client/Server Types explizit trennen
## Entscheidungsreihenfolge
1. Bestehendes kleines kohaeesives Modul wiederverwenden
2. Neues Modul in der Naehe erstellen
3. Ueberfuellte Datei splitten, neues Verhalten in richtiges Split-Modul
4. Nur als letzter Ausweg: Grosse bestehende Datei erweitern

View File

@@ -0,0 +1,20 @@
# LOC Exceptions — BreakPilot Lehrer
# Format: <glob> | owner=<person> | reason=<why> | review=<date>
#
# Jede Ausnahme braucht Begruendung und Review-Datum.
# Temporaere Ausnahmen muessen mit [guardrail-change] Commit-Marker versehen werden.
# Generated / Build Artifacts
**/node_modules/** | owner=infra | reason=npm packages | review=permanent
**/.next/** | owner=infra | reason=Next.js build output | review=permanent
**/__pycache__/** | owner=infra | reason=Python bytecode | review=permanent
**/venv/** | owner=infra | reason=Python virtualenv | review=permanent
# Test-Dateien (duerfen groesser sein fuer Table-Driven Tests)
**/tests/test_cv_vocab_pipeline.py | owner=klausur | reason=umfangreiche OCR Pipeline Tests | review=2026-07-01
**/tests/test_rbac.py | owner=klausur | reason=RBAC Test-Matrix | review=2026-07-01
**/tests/test_grid_editor_api.py | owner=klausur | reason=Grid Editor Integrationstests | review=2026-07-01
# Legacy — TEMPORAER bis Refactoring abgeschlossen
# Dateien hier werden Phase fuer Phase abgearbeitet und entfernt.
# KEINE neuen Ausnahmen ohne [guardrail-change] Commit-Marker!

9
.claude/settings.json Normal file
View File

@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash",
"Write",
"Read"
]
}
}

36
AGENTS.go.md Normal file
View File

@@ -0,0 +1,36 @@
# AGENTS.go.md — Go/Gin Konventionen
## Architektur
- `handlers/`: HTTP Transport nur — Decode, Validate, Call Service, Encode Response
- `service/` oder `usecase/`: Business Logic
- `repo/`: Storage/Integration
- `model/` oder `domain/`: Domain Entities
- `tests/`: Table-driven Tests bevorzugen
## Regeln
1. Handler ≤40 LOC — nur Decode → Service → Encode
2. Business Logic NICHT in Handlers verstecken
3. Grosse Handler nach Resource/Verb splitten
4. Request/Response DTOs nah am Transport halten
5. Interfaces nur an echten Boundaries (nicht ueberall fuer Mocks)
6. Keine Giant-Utility-Dateien
7. Generated Files nicht manuell editieren
## Split-Trigger
- Handler-Datei ueberschreitet 400-500 LOC
- Unrelated Endpoints zusammengruppiert
- Encoding/Decoding dominiert die Handler-Datei
- Service-Logik und Transport-Logik gemischt
## Verifikation
```bash
gofmt -l . | grep -q . && exit 1
go vet ./...
golangci-lint run --timeout=5m
go test -race ./...
go build ./...
```

36
AGENTS.python.md Normal file
View File

@@ -0,0 +1,36 @@
# AGENTS.python.md — Python/FastAPI Konventionen
## Architektur
- `routes/` oder `api/`: Request/Response nur — kein Business Logic
- `services/`: Business Logic
- `repositories/`: Persistenz/Data Access
- `schemas/`: Pydantic Models, nach Domain gesplittet
- `tests/`: Spiegelt Produktions-Layout
## Regeln
1. Route-Dateien duenn halten (≤300 LOC)
2. Wenn eine Route-Datei 300-400 LOC erreicht → nach Resource/Operation splitten
3. Schema-Dateien nach Domain splitten wenn sie wachsen
4. Modul-Level Singleton-Kopplung vermeiden (Tests patchen falsches Symbol)
5. Patch immer das Symbol das vom getesteten Modul importiert wird
6. Dependency Injection bevorzugen statt versteckte Imports
7. Pydantic v2: `from __future__ import annotations` NICHT verwenden (bricht Pydantic)
8. Migrationen getrennt von Refactorings halten
## Split-Trigger
- Datei naehert sich oder ueberschreitet 500 LOC
- Zirkulaere Imports erscheinen
- Tests brauchen tiefes Patching
- API-Schemas mischen verschiedene Domains
- Service-Datei macht Transport UND DB-Logik
## Verifikation
```bash
ruff check .
mypy . --ignore-missing-imports --no-error-summary
pytest tests/ -x -q --no-header
```

55
AGENTS.typescript.md Normal file
View File

@@ -0,0 +1,55 @@
# AGENTS.typescript.md — Next.js Konventionen
## Architektur
- `app/.../page.tsx`: Minimale Seiten-Komposition (≤250 LOC)
- `app/.../actions.ts`: Server Actions
- `app/.../queries.ts`: Data Loading
- `app/.../_components/`: View-Teile (Colocation)
- `app/.../_hooks/`: Seiten-spezifische Hooks (Colocation)
- `types/` oder `types/*.ts`: Domain-spezifische Types
- `schemas/`: Zod/Validierungs-Schemas
- `lib/`: Shared Utilities
## Regeln
1. page.tsx duenn halten (≤250 LOC)
2. Grosse Seiten frueh in Sections/Components splitten
3. KEINE einzelne types.ts als Catch-All
4. types.ts UND types/ Shadowing vermeiden (eines waehlen!)
5. Server/Client Module-Grenzen explizit halten
6. Pure Helpers und schmale Props bevorzugen
7. API-Client Types getrennt von handgeschriebenen Domain Types
## Colocation Pattern (bevorzugt)
```
app/(admin)/ai/rag/
page.tsx ← duenn, komponiert nur
_components/
SearchPanel.tsx
ResultsTable.tsx
FilterBar.tsx
_hooks/
useRagSearch.ts
actions.ts ← Server Actions
queries.ts ← Data Fetching
```
## Split-Trigger
- page.tsx ueberschreitet 250-350 LOC
- types.ts ueberschreitet 200-300 LOC
- Form-Logik, Server Actions und Rendering in einer Datei
- Mehrere unabhaengig testbare Sections vorhanden
- Imports werden broechig
## Verifikation
```bash
npx tsc --noEmit
npm run lint
npm run build
```
> `npm run build` ist PFLICHT — `tsc` allein reicht nicht.

70
scripts/check-loc.sh Executable file
View File

@@ -0,0 +1,70 @@
#!/usr/bin/env bash
set -euo pipefail
MAX_LOC="${MAX_LOC:-500}"
ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
EXCEPTIONS_FILE="${ROOT_DIR}/.claude/rules/loc-exceptions.txt"
red() { printf '\033[31m%s\033[0m\n' "$*"; }
green() { printf '\033[32m%s\033[0m\n' "$*"; }
is_exempt() {
local file="$1"
[[ -f "$EXCEPTIONS_FILE" ]] || return 1
while IFS= read -r line; do
[[ -z "$line" ]] && continue
[[ "$line" =~ ^# ]] && continue
local pattern="${line%%|*}"
pattern="$(echo "$pattern" | xargs)"
if [[ -n "$pattern" ]] && [[ "$file" == $pattern ]]; then
return 0
fi
done < "$EXCEPTIONS_FILE"
return 1
}
count_loc() {
[[ -f "$1" ]] && awk 'END { print NR }' "$1" || echo 0
}
collect_changed_files() {
{ git diff --name-only --cached; git diff --name-only; git ls-files --others --exclude-standard; } | awk 'NF' | sort -u
}
main() {
local mode="changed"
[[ "${1:-}" == "--changed" ]] && mode="changed"
[[ "${1:-}" == "--all" ]] && mode="all"
local files=()
if [[ "$mode" == "changed" ]]; then
IFS=$'\n' read -d '' -r -a files < <(collect_changed_files)
else
IFS=$'\n' read -d '' -r -a files < <(find "$ROOT_DIR" \( -name '*.py' -o -name '*.go' -o -name '*.ts' -o -name '*.tsx' \) -not -path '*/node_modules/*' -not -path '*/.next/*' -not -path '*/__pycache__/*' -not -path '*/venv/*')
fi
local failed=0
local violations=0
for file in "${files[@]}"; do
[[ -f "$file" ]] || continue
is_exempt "$file" && continue
local loc
loc="$(count_loc "$file")"
if (( loc > MAX_LOC )); then
red "VIOLATION: $file ($loc LOC > $MAX_LOC)"
violations=$((violations + 1))
failed=1
fi
done
if (( failed )); then
red ""
red "$violations file(s) exceed ${MAX_LOC} LOC budget."
exit 1
fi
green "LOC budget check passed."
}
main "$@"