Architektur-Regeln vor dem Merge prüfen statt erst im Review #120

Closed
opened 2026-10-20 09:56:43 +00:00 by claude · 7 comments
Collaborator

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.:

  • eine Schichtungsverletzung: TeamMatchResource.delete prüft Fachregeln und löscht selbst, statt das dem Service zu überlassen,
  • drei Request/Response-Felder, die null sein dürfen, im Schema aber als Pflichtfeld stehen (PromotionRelegationRequest, CupPositionResponse, ResultCorrectionRequest),
  • ein Messartefakt.

Warum das passiert

  1. Geprüft wird erst hinterher. metrics.py checks braucht 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.
  2. Die bauende Session kennt die Regeln nicht. „Fachregeln gehören in den Service“ und „ein Feld, das null sein darf, braucht @Schema(nullable = true)“ stehen nur in der Review-Rubrik, nicht in CLAUDE.md.
  3. Der Bestand ist eine falsche Vorlage. Wenn 29 Resources es schon so machen, sieht Code, der zum Nachbarcode passt, genauso aus.

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ält FreezingArchRule in einer Datei im Repo fest; anschlagen tut es nur bei neuen Verstössen.

Kandidaten:

  • Eine Resource hängt nie von einem Repository oder vom EntityManager ab, weder lesend noch schreibend (Entscheidung unten). Der Altbestand (27 von 41 Resources) wird eingefroren.
  • Modulgrenzen cms / competition / shared.
  • Jede Resource trägt @RolesAllowed/@PermitAll/@Authenticated.

ArchUnit erfasst nicht: die Ablauflogik im Methodenrumpf („wenn die Abfrage X liefert, dann Y“), ob ein Wert zur Laufzeit null ist, 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 checks gegen main vor dem Merge

Für den Rest: Ablauflogik der Schichtung, Nullability im Vertrag, Frontend. Neue Treffer gegenüber main werden 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 findet metrics.py das Repo über seinen eigenen Ablageort (REPO aus __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 if dazu, dann eine zweite Abfrage, dann ein Schreibaufruf. Die strenge Regel prüft ArchUnit ohne Urteil.

  • Service-Methoden, die nur durchreichen, sind in Ordnung. Sie sind der eine Ort, an dem später die Regel dazukommt, kein Code auf Vorrat.
  • Neuer Code hält die Regel ein. Bestehende Resources werden umgestellt, wenn man sie ohnehin anfasst. Bis dahin hält FreezingArchRule sie fest. Ein eigener Umbau aller 27 Resources wäre eine eigene Entscheidung.
  • Dazu eine ADR und eine Zeile in CLAUDE.md (Teil 3).

Später, eigene Entscheidung

  • Den Altbestand gezielt abbauen statt nur beim Anfassen: 29 Schichtungs-Befunde, 27 Resources mit Repository-Zugriff.
  • Die Review-Rubrik liest für die ArchUnit-Regeln deren Ergebnis, statt dieselbe Regel ein zweites Mal zu prüfen. Das ist eine Rubrik-Änderung, also eine neue Version.
  • Nullability: Jedes Feld in 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 verify schlägt an, wenn eine Resource neu persist/delete aufruft. Der Gegentest mit einem absichtlich eingebauten Verstoss ist rot.
  • Der Altbestand blockiert den Build nicht.
  • Die Prüfung vor dem Merge steht in CLAUDE.md unter „Gemergt wird nach der technischen Abnahme“.
## 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.: - eine Schichtungsverletzung: `TeamMatchResource.delete` prüft Fachregeln und löscht selbst, statt das dem Service zu überlassen, - drei Request/Response-Felder, die `null` sein dürfen, im Schema aber als Pflichtfeld stehen (`PromotionRelegationRequest`, `CupPositionResponse`, `ResultCorrectionRequest`), - ein Messartefakt. ## Warum das passiert 1. **Geprüft wird erst hinterher.** `metrics.py checks` braucht 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. 2. **Die bauende Session kennt die Regeln nicht.** „Fachregeln gehören in den Service“ und „ein Feld, das null sein darf, braucht `@Schema(nullable = true)`“ stehen nur in der Review-Rubrik, nicht in CLAUDE.md. 3. **Der Bestand ist eine falsche Vorlage.** Wenn 29 Resources es schon so machen, sieht Code, der zum Nachbarcode passt, genauso aus. ## 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ält `FreezingArchRule` in einer Datei im Repo fest; anschlagen tut es nur bei neuen Verstössen. Kandidaten: - **Eine Resource hängt nie von einem Repository oder vom `EntityManager` ab**, weder lesend noch schreibend (Entscheidung unten). Der Altbestand (27 von 41 Resources) wird eingefroren. - Modulgrenzen `cms` / `competition` / `shared`. - Jede Resource trägt `@RolesAllowed`/`@PermitAll`/`@Authenticated`. **ArchUnit erfasst nicht:** die Ablauflogik im Methodenrumpf („wenn die Abfrage X liefert, dann Y“), ob ein Wert zur Laufzeit `null` ist, 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 checks` gegen `main` vor dem Merge Für den Rest: Ablauflogik der Schichtung, Nullability im Vertrag, Frontend. Neue Treffer gegenüber `main` werden 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 findet `metrics.py` das Repo über seinen eigenen Ablageort (`REPO` aus `__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 `if` dazu, dann eine zweite Abfrage, dann ein Schreibaufruf. Die strenge Regel prüft ArchUnit ohne Urteil. - Service-Methoden, die nur durchreichen, sind in Ordnung. Sie sind der eine Ort, an dem später die Regel dazukommt, kein Code auf Vorrat. - **Neuer Code** hält die Regel ein. **Bestehende Resources** werden umgestellt, wenn man sie ohnehin anfasst. Bis dahin hält `FreezingArchRule` sie fest. Ein eigener Umbau aller 27 Resources wäre eine eigene Entscheidung. - Dazu eine ADR und eine Zeile in CLAUDE.md (Teil 3). ## Später, eigene Entscheidung - Den Altbestand gezielt abbauen statt nur beim Anfassen: 29 Schichtungs-Befunde, 27 Resources mit Repository-Zugriff. - Die Review-Rubrik liest für die ArchUnit-Regeln deren Ergebnis, statt dieselbe Regel ein zweites Mal zu prüfen. Das ist eine Rubrik-Änderung, also eine neue Version. - Nullability: Jedes Feld in `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 verify` schlägt an, wenn eine Resource neu `persist`/`delete` aufruft. Der Gegentest mit einem absichtlich eingebauten Verstoss ist rot. - Der Altbestand blockiert den Build nicht. - Die Prüfung vor dem Merge steht in CLAUDE.md unter „Gemergt wird nach der technischen Abnahme“.
Author
Collaborator

Entscheidung von Olli am 20.10.: strenge Schichtung. Resources greifen auch lesend nicht mehr auf Repositories oder den EntityManager zu. 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.

Entscheidung von Olli am 20.10.: **strenge Schichtung.** Resources greifen auch lesend nicht mehr auf Repositories oder den `EntityManager` zu. 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.
Author
Collaborator

Weitere Regel aus #122 (20.10.): Nur der Benachrichtiger hängt von NotificationMailer ab. Fachservices melden Ereignisse, verschickt wird nach dem Commit.

Weitere Regel aus #122 (20.10.): **Nur der Benachrichtiger hängt von `NotificationMailer` ab.** Fachservices melden Ereignisse, verschickt wird nach dem Commit.
Author
Collaborator

Java-25-Check bestanden (20.10.2026). Wegwerf-Projekt mit ArchUnit 1.5.1 (neueste Version, Maven Central) gegen backend/target/classes von main 8ac1b58. Die Klassen haben Hauptversion 69, also Java 25.

  • Import: 502 Klassen, keine Warnung. Auch die Methodenaufrufe sind aufgelöst (20 Aufrufe aus CaptainReminderService.run). Der ganze Lauf dauert rund 2 s.
  • Erste Messung der Kandidaten-Regeln:
    • Resource hängt nicht von Repository/EntityManager ab: 246 Einzelverstösse in 28 Klassen. Das ist der Altbestand, der eingefroren wird.
    • Nur der Benachrichtiger kennt den Mailer (#122): Gegen NotificationMailer geformt, meldet die Regel 0. Der Versand läuft heute vollständig über CaptainNotificationService. Die Regel muss darum auf CaptainNotificationService zielen: 4 Klassen rufen ihn heute auf (CaptainReminderService, TeamMatchProposalService, TeamMatchResultService, TeamMatchResource). Genau die baut #122 um.
    • Modulgrenze shared → competition/cms: 0, wie im Review.

Noch nicht ausprobiert: FreezingArchRule (Speicherdatei im Repo) und die Einbindung in mvn -B clean verify. Beides hängt nicht an der Java-Version.

**Java-25-Check bestanden (20.10.2026).** Wegwerf-Projekt mit **ArchUnit 1.5.1** (neueste Version, Maven Central) gegen `backend/target/classes` von `main` 8ac1b58. Die Klassen haben Hauptversion 69, also Java 25. - Import: **502 Klassen, keine Warnung.** Auch die Methodenaufrufe sind aufgelöst (20 Aufrufe aus `CaptainReminderService.run`). Der ganze Lauf dauert rund 2 s. - Erste Messung der Kandidaten-Regeln: - **Resource hängt nicht von Repository/EntityManager ab:** 246 Einzelverstösse in **28 Klassen**. Das ist der Altbestand, der eingefroren wird. - **Nur der Benachrichtiger kennt den Mailer (#122):** Gegen `NotificationMailer` geformt, meldet die Regel **0**. Der Versand läuft heute vollständig über `CaptainNotificationService`. Die Regel muss darum auf `CaptainNotificationService` zielen: **4 Klassen** rufen ihn heute auf (`CaptainReminderService`, `TeamMatchProposalService`, `TeamMatchResultService`, `TeamMatchResource`). Genau die baut #122 um. - **Modulgrenze `shared` → `competition`/`cms`:** 0, wie im Review. Noch nicht ausprobiert: `FreezingArchRule` (Speicherdatei im Repo) und die Einbindung in `mvn -B clean verify`. Beides hängt nicht an der Java-Version.
Author
Collaborator

Teil 1 gebaut auf feature/archunit (Stand c565a53, noch nicht gepusht, nicht in main). ArchitectureTest prüft in jedem mvn -B clean verify die Tabelle aus ADR 0033 und den Modulschnitt aus ADR 0001; die Gegenprobe gegen absichtlich falsche Fixtures steht in ArchitectureRulesTest. mvn -B clean verify: 6353 Tests grün, openapi.json unverändert.

Eingefrorener Altbestand (backend/src/test/archunit_store, darf nur schrumpfen):

Regel Einträge
Schichten nach ADR 0033 1573 (rund 1110 Resource→Entity, 242 Resource→Repository, 150 Api→Entity, 4 Repository→Service)
Kein Active Record ausserhalb der Repositories 83 Aufrufstellen in 11 Klassen (statisch und an der Instanz; der ADR-Kontext nannte 67 in 9 Klassen)
Entitäten ohne Active-Record-Basisklasse 43 (41 direkt, TeamMatch und TournamentMatch über Match)
EntityManager nur in Repositories 10
Modulgrenzen nach ADR 0001 0
Fachkern ohne Technik 0
Mails nur über den NotificationMailer 0

Nicht eingefroren, weil ohne Altbestand: jede Klasse liegt in einem Paket der Tabelle. Dafür sind HealthResource, HealthResponse und Roles nach shared/resource bzw. shared/api umgezogen. 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 checks gegen main vor dem Merge) und Teil 3 (Nullability).

Teil 1 gebaut auf `feature/archunit` (Stand `c565a53`, noch nicht gepusht, nicht in `main`). `ArchitectureTest` prüft in jedem `mvn -B clean verify` die Tabelle aus ADR 0033 und den Modulschnitt aus ADR 0001; die Gegenprobe gegen absichtlich falsche Fixtures steht in `ArchitectureRulesTest`. `mvn -B clean verify`: 6353 Tests grün, `openapi.json` unverändert. Eingefrorener Altbestand (`backend/src/test/archunit_store`, darf nur schrumpfen): | Regel | Einträge | |---|---| | Schichten nach ADR 0033 | 1573 (rund 1110 Resource→Entity, 242 Resource→Repository, 150 Api→Entity, 4 Repository→Service) | | Kein Active Record ausserhalb der Repositories | 83 Aufrufstellen in 11 Klassen (statisch und an der Instanz; der ADR-Kontext nannte 67 in 9 Klassen) | | Entitäten ohne Active-Record-Basisklasse | 43 (41 direkt, `TeamMatch` und `TournamentMatch` über `Match`) | | EntityManager nur in Repositories | 10 | | Modulgrenzen nach ADR 0001 | 0 | | Fachkern ohne Technik | 0 | | Mails nur über den NotificationMailer | 0 | Nicht eingefroren, weil ohne Altbestand: jede Klasse liegt in einem Paket der Tabelle. Dafür sind `HealthResource`, `HealthResponse` und `Roles` nach `shared/resource` bzw. `shared/api` umgezogen. 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 checks` gegen `main` vor dem Merge) **und Teil 3** (Nullability).
Author
Collaborator

Teil 1 ist in main (0a5a18e, Code-Stand c565a53), gepusht am 20.10.2026. Vor dem Merge: mvn -B clean verify grün (6353 Tests), Frontend-Builds grün, E2E auf Staging gegen feature/archunit-c565a53 214/214. Branch und Worktree sind gelöscht.

Das Issue bleibt offen für Teil 2 (metrics.py checks gegen main vor dem Merge) und Teil 3 (Nullability). Aufgeschobene Review-Befunde stehen in #124.

Teil 1 ist in `main` (`0a5a18e`, Code-Stand `c565a53`), gepusht am 20.10.2026. Vor dem Merge: `mvn -B clean verify` grün (6353 Tests), Frontend-Builds grün, E2E auf Staging gegen `feature/archunit-c565a53` 214/214. Branch und Worktree sind gelöscht. Das Issue bleibt offen für **Teil 2** (`metrics.py checks` gegen `main` vor dem Merge) und **Teil 3** (Nullability). Aufgeschobene Review-Befunde stehen in #124.
Author
Collaborator

Teil 2 ist in main (01d0fad, noch nicht gepusht).

python3 .claude/skills/project-review/metrics.py diff misst den Arbeitsbaum und den Abzweigpunkt von main mit 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. Auf main gegen main meldet der Vergleich 0/0/0.

Der erste Lauf auf main gegen main fand einen Fehler im Messskript selbst: os.walk lieferte 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 2 ist in `main` (`01d0fad`, noch nicht gepusht). `python3 .claude/skills/project-review/metrics.py diff` misst den Arbeitsbaum und den Abzweigpunkt von `main` mit **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. Auf `main` gegen `main` meldet der Vergleich 0/0/0. Der erste Lauf auf `main` gegen `main` fand einen Fehler im Messskript selbst: `os.walk` lieferte 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).
Author
Collaborator

Teil 3 ist in main (9f3387e, gepusht 20.10.2026). CLAUDE.md enthält jetzt die zwei Regeln, die ArchUnit nicht prüft:

  • Unter „Architektur (ADR 0033)“: Eine Resource entscheidet nichts. Ein if auf ein Service-Ergebnis mit einem zweiten Aufruf dahinter oder ein Vergleich auf Status oder Phase gehört in den Service (architecture/2).
  • Unter „Java“: Ein nullbares Feld eines *Request/*Response-Records trägt @Schema(nullable = true). metrics.py diff meldet es, wenn der Code null hineingibt (Antwort) oder das Feld auf null prüft (Anfrage) (architecture/9).

Damit sind alle drei Teile erledigt. Den CI-Schritt für metrics.py diff haben wir bewusst verschoben, er steht jetzt in #125.

Prüfschritte (Olli):

  1. CLAUDE.md, Abschnitt „Architektur (ADR 0033)“: der Absatz „Eine Resource entscheidet nichts“.
  2. CLAUDE.md, Abschnitt „Java“: der letzte Punkt zu @Schema(nullable = true).
  3. CLAUDE.md, „Gemergt wird nach der technischen Abnahme“: metrics.py diff steht unter den Bedingungen.
  4. Auf main ausführen: python3 .claude/skills/project-review/metrics.py diff. Erwartet wird 0 neu, 0 veraendert, 0 weggefallen und Exit-Code 0.
Teil 3 ist in `main` (`9f3387e`, gepusht 20.10.2026). CLAUDE.md enthält jetzt die zwei Regeln, die ArchUnit nicht prüft: - **Unter „Architektur (ADR 0033)“: Eine Resource entscheidet nichts.** Ein `if` auf ein Service-Ergebnis mit einem zweiten Aufruf dahinter oder ein Vergleich auf Status oder Phase gehört in den Service (architecture/2). - **Unter „Java“: Ein nullbares Feld eines `*Request`/`*Response`-Records trägt `@Schema(nullable = true)`.** `metrics.py diff` meldet es, wenn der Code `null` hineingibt (Antwort) oder das Feld auf `null` prüft (Anfrage) (architecture/9). Damit sind alle drei Teile erledigt. Den CI-Schritt für `metrics.py diff` haben wir bewusst verschoben, er steht jetzt in #125. **Prüfschritte (Olli):** 1. CLAUDE.md, Abschnitt „Architektur (ADR 0033)“: der Absatz „Eine Resource entscheidet nichts“. 2. CLAUDE.md, Abschnitt „Java“: der letzte Punkt zu `@Schema(nullable = true)`. 3. CLAUDE.md, „Gemergt wird nach der technischen Abnahme“: `metrics.py diff` steht unter den Bedingungen. 4. Auf `main` ausführen: `python3 .claude/skills/project-review/metrics.py diff`. Erwartet wird `0 neu, 0 veraendert, 0 weggefallen` und Exit-Code 0.
olli closed this issue 2026-10-20 15:33:47 +00:00
Sign in to join this conversation.
No description provided.