Architektur-Regeln vor dem Merge prüfen statt erst im Review #120
Labels
No labels
Kind
Bug
Kind
Enhancement
Kind
Maintenance
Priority
P1
Priority
P2
Priority
P3
Status
In Progress
Status
Need More Info
Status
Zur Abnahme
Änderungen Wettspielreglement
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
sffs-tt-basel/sffs-tt-issues#120
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Anlass
Die Architektur steht im Project-Review vom 20.10.2026 bei 8 Punkten (38 Befunde). Der grösste Teil ist Altbestand: 29 Schichtungs-Befunde machte die Prüfung, die es seit Rubrik v12 (11.10.) gibt, am 13.10. sichtbar. Neu gebaut kamen trotzdem fünf dazu, alle mit dem Termin-Paket zwischen 13.10. und 19.10.:
TeamMatchResource.deleteprüft Fachregeln und löscht selbst, statt das dem Service zu überlassen,nullsein dürfen, im Schema aber als Pflichtfeld stehen (PromotionRelegationRequest,CupPositionResponse,ResultCorrectionRequest),Warum das passiert
metrics.py checksbraucht 2,1 s (gemessen 20.10.), läuft aber nur bei/project-review, also nach dem Merge. Die CI prüft Tests und Schema-Drift, die technische Abnahme ist ein Lese-Review des Diffs.@Schema(nullable = true)“ stehen nur in der Review-Rubrik, nicht in CLAUDE.md.Vorschlag, drei Teile
1. ArchUnit für die harten Java-Regeln
Läuft in jedem
mvn -B clean verify, also lokal, in der CI und in jeder parallelen Session. Den Altbestand hältFreezingArchRulein einer Datei im Repo fest; anschlagen tut es nur bei neuen Verstössen.Kandidaten:
EntityManagerab, weder lesend noch schreibend (Entscheidung unten). Der Altbestand (27 von 41 Resources) wird eingefroren.cms/competition/shared.@RolesAllowed/@PermitAll/@Authenticated.ArchUnit erfasst nicht: die Ablauflogik im Methodenrumpf („wenn die Abfrage X liefert, dann Y“), ob ein Wert zur Laufzeit
nullist, und das Frontend.Zuerst prüfen: ob die aktuelle ArchUnit-Version Java 25 lesen kann. ArchUnit hängt an ASM und folgt neuen Java-Versionen manchmal verzögert.
2.
metrics.py checksgegenmainvor dem MergeFür den Rest: Ablauflogik der Schichtung, Nullability im Vertrag, Frontend. Neue Treffer gegenüber
mainwerden behoben oder begründet. Erst als Punkt der technischen Abnahme, danach als CI-Schritt.Voraussetzung: Die uncommitteten Änderungen an
.claude/skills/project-review/metrics.py(Allowlist-Einträge vom 19.10. und 20.10.) müssen erst committet sein. Am 20.10. lieferte ein Vergleich mit zwei verschiedenen Fassungen elf falsche „neue“ Treffer. Ausserdem findetmetrics.pydas Repo über seinen eigenen Ablageort (REPOaus__file__), nicht über das Arbeitsverzeichnis; aus einem Worktree heraus misst eine fremde Kopie also den falschen Baum.3. Die Regeln in CLAUDE.md
Je zwei bis drei Zeilen zu Schichtung und Nullability, damit die bauende Session sie kennt, bevor sie schreibt.
Entscheidung (Olli, 20.10.2026): strenge Schichtung
Resources reden nur mit Services, nie mit Repositories oder dem
EntityManager. Das gilt auch fürs Lesen, auch für Laden per id.Verworfen wurde die weichere Regel „Laden und Anzeigen in der Resource erlaubt, fachliche Abfragen und Schreiben im Service“. Sie hat eine Grauzone: Wo Anzeigen aufhört und Fachregel anfängt, muss jemand beurteilen, und eine Maschine kann es nicht prüfen. Genau so sind die 29 Schichtungs-Befunde entstanden. Die Resource hat das Repository ohnehin, also kommt erst ein
ifdazu, dann eine zweite Abfrage, dann ein Schreibaufruf. Die strenge Regel prüft ArchUnit ohne Urteil.FreezingArchRulesie fest. Ein eigener Umbau aller 27 Resources wäre eine eigene Entscheidung.Später, eigene Entscheidung
api/ausdrücklich als Pflicht oder nullbar markieren? Das wäre eine neue Konvention, die ArchUnit dann prüfen könnte.Abnahme
mvn -B clean verifyschlägt an, wenn eine Resource neupersist/deleteaufruft. Der Gegentest mit einem absichtlich eingebauten Verstoss ist rot.Entscheidung von Olli am 20.10.: strenge Schichtung. Resources greifen auch lesend nicht mehr auf Repositories oder den
EntityManagerzu. Die Beschreibung oben ist angepasst; die Begründung steht dort unter „Entscheidung“. Die weichere Regel (Lesen erlaubt) ist verworfen, weil ihre Grauzone nicht maschinell prüfbar ist.Weitere Regel aus #122 (20.10.): Nur der Benachrichtiger hängt von
NotificationMailerab. Fachservices melden Ereignisse, verschickt wird nach dem Commit.Java-25-Check bestanden (20.10.2026). Wegwerf-Projekt mit ArchUnit 1.5.1 (neueste Version, Maven Central) gegen
backend/target/classesvonmain8ac1b58. Die Klassen haben Hauptversion 69, also Java 25.CaptainReminderService.run). Der ganze Lauf dauert rund 2 s.NotificationMailergeformt, meldet die Regel 0. Der Versand läuft heute vollständig überCaptainNotificationService. Die Regel muss darum aufCaptainNotificationServicezielen: 4 Klassen rufen ihn heute auf (CaptainReminderService,TeamMatchProposalService,TeamMatchResultService,TeamMatchResource). Genau die baut #122 um.shared→competition/cms: 0, wie im Review.Noch nicht ausprobiert:
FreezingArchRule(Speicherdatei im Repo) und die Einbindung inmvn -B clean verify. Beides hängt nicht an der Java-Version.Teil 1 gebaut auf
feature/archunit(Standc565a53, noch nicht gepusht, nicht inmain).ArchitectureTestprüft in jedemmvn -B clean verifydie Tabelle aus ADR 0033 und den Modulschnitt aus ADR 0001; die Gegenprobe gegen absichtlich falsche Fixtures steht inArchitectureRulesTest.mvn -B clean verify: 6353 Tests grün,openapi.jsonunverändert.Eingefrorener Altbestand (
backend/src/test/archunit_store, darf nur schrumpfen):TeamMatchundTournamentMatchüberMatch)Nicht eingefroren, weil ohne Altbestand: jede Klasse liegt in einem Paket der Tabelle. Dafür sind
HealthResource,HealthResponseundRolesnachshared/resourcebzw.shared/apiumgezogen. Der CI-Schritt „Architektur-Store unveraendert“ wird rot, wenn der Testlauf den Store ändert.Aus dem Review aufgeschoben: #124. Offen bleiben hier Teil 2 (
metrics.py checksgegenmainvor dem Merge) und Teil 3 (Nullability).Teil 1 ist in
main(0a5a18e, Code-Standc565a53), gepusht am 20.10.2026. Vor dem Merge:mvn -B clean verifygrün (6353 Tests), Frontend-Builds grün, E2E auf Staging gegenfeature/archunit-c565a53214/214. Branch und Worktree sind gelöscht.Das Issue bleibt offen für Teil 2 (
metrics.py checksgegenmainvor dem Merge) und Teil 3 (Nullability). Aufgeschobene Review-Befunde stehen in #124.Teil 2 ist in
main(01d0fad, noch nicht gepusht).python3 .claude/skills/project-review/metrics.py diffmisst den Arbeitsbaum und den Abzweigpunkt vonmainmit derselben Fassung des Skripts, über alle Kriterien des Reviews (Entscheidung Olli, 20.10.). Ausgegeben wird, was neu ist und was sich verändert hat; Zeilennummern sind ausgeblendet. Bei beidem ist der Exit-Code 1. Laufzeit rund 6 s. Nicht verglichen wird der Backend-Teil von testCoverage/2, weil er einen JaCoCo-Bericht beider Stände braucht. CLAUDE.md nennt den Vergleich im Absatz „Gemergt wird nach der technischen Abnahme“.Gegenprobe in einem Wegwerf-Worktree: Ein toter Export, eine Datei ohne Test und ein Wildcard-Import erscheinen als NEU. Ein zusätzlicher Feld-Schreibzugriff in
BoardMemberResource.uploadPhoto(dort schon im Altbestand gemeldet) erscheint als VERÄNDERT. Drei Leerzeilen über der Klasse melden nichts. Aufmaingegenmainmeldet der Vergleich 0/0/0.Der erste Lauf auf
maingegenmainfand einen Fehler im Messskript selbst:os.walklieferte die Verzeichnisse in der Reihenfolge des Dateisystems. quality/6 hängt an der ersten Fundstelle eines Literals, Datei, Anker und ID hingen also am Checkout. Behoben als Rubrik v13 (quality/6 criterionVersion 3). Vier offene Befunde bekommen einen neuen Anker bei gleichem Inhalt. Das nächste Review ist darum ein Neu-Null-Lauf. Die geplanten Sweeps heissen jetzt v14.Den CI-Schritt bauen wir erst nach ein paar Merges, wenn klar ist, wie viele falsche Treffer kommen. Offen bleibt hier noch Teil 3 (die Regeln in CLAUDE.md).
Teil 3 ist in
main(9f3387e, gepusht 20.10.2026). CLAUDE.md enthält jetzt die zwei Regeln, die ArchUnit nicht prüft:ifauf ein Service-Ergebnis mit einem zweiten Aufruf dahinter oder ein Vergleich auf Status oder Phase gehört in den Service (architecture/2).*Request/*Response-Records trägt@Schema(nullable = true).metrics.py diffmeldet es, wenn der Codenullhineingibt (Antwort) oder das Feld aufnullprüft (Anfrage) (architecture/9).Damit sind alle drei Teile erledigt. Den CI-Schritt für
metrics.py diffhaben wir bewusst verschoben, er steht jetzt in #125.Prüfschritte (Olli):
@Schema(nullable = true).metrics.py diffsteht unter den Bedingungen.mainausführen:python3 .claude/skills/project-review/metrics.py diff. Erwartet wird0 neu, 0 veraendert, 0 weggefallenund Exit-Code 0.