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:
@@ -256,3 +256,45 @@ ssh macmini "cd /Users/benjaminadmin/Projekte/breakpilot-lehrer && git push all
|
|||||||
| `website/app/admin/klausur-korrektur/` | Korrektur-Workspace |
|
| `website/app/admin/klausur-korrektur/` | Korrektur-Workspace |
|
||||||
| `backend-lehrer/classroom_api.py` | Classroom Engine |
|
| `backend-lehrer/classroom_api.py` | Classroom Engine |
|
||||||
| `backend-lehrer/state_engine_api.py` | State 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)
|
||||||
|
```
|
||||||
|
|||||||
46
.claude/rules/architecture.md
Normal file
46
.claude/rules/architecture.md
Normal 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
|
||||||
20
.claude/rules/loc-exceptions.txt
Normal file
20
.claude/rules/loc-exceptions.txt
Normal 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
9
.claude/settings.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash",
|
||||||
|
"Write",
|
||||||
|
"Read"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
36
AGENTS.go.md
Normal file
36
AGENTS.go.md
Normal 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
36
AGENTS.python.md
Normal 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
55
AGENTS.typescript.md
Normal 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
70
scripts/check-loc.sh
Executable 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 "$@"
|
||||||
Reference in New Issue
Block a user