diff --git a/.claude/skills/java/SKILL.md b/.claude/skills/java/SKILL.md index 9785366..8775293 100644 --- a/.claude/skills/java/SKILL.md +++ b/.claude/skills/java/SKILL.md @@ -3,7 +3,7 @@ name: java description: Umfassende Java-Entwicklungsrichtlinien für Backend-Projekte. Enthält Konventionen, Integrationen (PostgreSQL, Javalin, Jackson, Log4j2), Patterns (Clean Architecture, Repository) und Build-Konfiguration (Maven). Triggers auf Java-Code, REST APIs, Datenbank, jOOQ, Flyway, DTOs, Records, Logging, Properties, Identifiers, Security. # GENERIERT aus personal/skills-ref/java/ — nicht hier editieren; Aenderungen gehoeren nach ~/.claude/skills-ref/java/. source: personal-provider-ref -ref-hash: sha256:8a4c909743dc64a6c65624290ae533a0c33027f19ad593362f2e0606491304d9 +ref-hash: sha256:c9b1fe65dbfd6413221ddb5a5bea9c6c7dea36cd7b40623b0c2c4fc1efad50d0 --- # Java Development Guide diff --git a/.claude/skills/java/rules/build-maven.md b/.claude/skills/java/rules/build-maven.md index 1e91d7f..3f6db3e 100644 --- a/.claude/skills/java/rules/build-maven.md +++ b/.claude/skills/java/rules/build-maven.md @@ -93,32 +93,64 @@ mvn wrapper:wrapper -Dmaven=3.9.12 --- -### Assembly Plugin (Fat JAR) +### Assembly (Anwendungs-JAR plus `lib/`) + +Eine Anwendung wird **nie** als Fat JAR gebaut — weder mit `maven-shade-plugin` noch mit dem +`jar-with-dependencies`-Deskriptor des Assembly-Plugins. Beide entpacken die Abhaengigkeiten in ein +einziges Archiv, und dabei ueberschreiben sich Dateien, die mehrere Jars an derselben Stelle +mitbringen: `META-INF/services`, `reference.conf`, Signaturdateien. Der Shade kann das mit +Transformern reparieren, `jar-with-dependencies` kann es gar nicht — und eine Reparatur, die still +scheitert, faellt erst beim Start auf, nicht im gruenen Build. + +Stattdessen bleibt das Anwendungs-JAR fuer sich, die Abhaengigkeiten liegen als eigene Jars daneben, +und der Klassenpfad steht im Manifest. ```xml org.apache.maven.plugins - maven-assembly-plugin - 3.8.0 + maven-dependency-plugin + ${maven.dependency.plugin.version} + + + copy-dependencies + package + copy-dependencies + + ${project.build.directory}/lib + runtime + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} - - jar-with-dependencies - net.example.Application + true + lib/ - - - package - single - - ``` +`java -jar target/app.jar` funktioniert damit unveraendert: `Class-Path` im Manifest wird relativ zum +Jar aufgeloest. Zusaetzlich bleibt lesbar, welche Version welcher Abhaengigkeit ausgeliefert wird — +genau das verliert ein Fat JAR, und mit ihm die Grundlage fuer CVE-Scans und das Nachziehen einer +einzelnen Abhaengigkeit. + +Ein verteilbares Archiv (`tar.gz`, `zip`) baut `maven-assembly-plugin` mit einem eigenen Deskriptor +darauf auf — erst dann, wenn ein Deployment eines braucht. + +Bei einem `module-info.java` kommt hinzu, dass ein Fat JAR den Moduldeskriptor verwirft: `shade` +fuehrt ihn nicht zusammen, und die Anwendung laeuft danach aus dem unbenannten Modul. Die Assembly +erhaelt ihn. + --- ### Befehle @@ -144,4 +176,4 @@ mvn wrapper:wrapper -Dmaven=3.9.12 - [ ] pom.xml konfiguriert - [ ] JDK Version gesetzt - [ ] JUnit hinzugefuegt -- [ ] Assembly Plugin (optional) +- [ ] Abhaengigkeiten als eigene Jars plus Manifest-Klassenpfad (nie ein Fat JAR) diff --git a/bin/traceability/traceability.py b/bin/traceability/traceability.py index 334a80e..02a2ef0 100644 --- a/bin/traceability/traceability.py +++ b/bin/traceability/traceability.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # GENERIERT aus personal/tools-ref/traceability/ — nicht hier editieren; Aenderungen gehoeren nach ~/.claude/tools-ref/traceability/. # source: personal-provider-ref -# ref-hash: sha256:e76cfbc5dcf2fb98d5403fbe8552b57c92abcdd461245c305280e08ca30c8843 +# ref-hash: sha256:a2541ffdcf3e5f58f8644626143ebe99d85fe1dc5b3d7900c597667b84f3b757 """ traceability.py — prueft die Verkettung zwischen Anforderungen und Tests. @@ -159,20 +159,32 @@ def read_spec(path: Path, is_delta: bool): def collect_requirements(spec_root: Path): - """Ist-Spec zuerst, Delta-Specs ueberschreiben — so gewinnt der laufende Change.""" + """Ist-Spec zuerst, Delta-Specs ueberschreiben — so gewinnt der laufende Change. + + Definieren mehrere offene Delta-Specs denselben Kurznamen, gewinnt die + hoechste Revision, nicht die zuletzt in Sortierreihenfolge gelesene Datei. + """ definitions, duplicates = {}, [] removed, tombstones = {}, {} + delta_defined = set() def record(defs, source_is_delta): for a in defs: previous = definitions.get(a.base) - if previous is not None and not source_is_delta: - duplicates.append(Finding( - "doppelter Kurzname", - f"req~{a.base} steht auch in {previous.file}:{previous.line}", - a.file, a.line)) - continue + if previous is not None: + if not source_is_delta: + duplicates.append(Finding( + "doppelter Kurzname", + f"req~{a.base} steht auch in {previous.file}:{previous.line}", + a.file, a.line)) + continue + # Revisionen werden der Reihe nach vergeben, die hoehere ist + # immer das spaetere Wort — Dateireihenfolge entscheidet nicht. + if a.base in delta_defined and a.revision < previous.revision: + continue definitions[a.base] = a + if source_is_delta: + delta_defined.add(a.base) specs_dir = spec_root / "specs" if specs_dir.is_dir(): @@ -565,6 +577,20 @@ def self_check(): "a_test.go": "// [impl->req~zugang.ganz-neu~1]\n", }, []), + # Zwei offene Delta-Specs auf demselben Kurznamen: "b" sortiert vor "g", + # die niedrigere Revision wird also NACH der hoeheren gelesen. Gewaenne + # die zuletzt gelesene Datei (der fruehere Fehler), stuende die Anforderung + # auf ~2 und der Verweis auf ~3 gaelte faelschlich als vorgegriffen. + "delta ueberholt delta: hoehere revision gewinnt trotz dateireihenfolge": ({ + "docs/changes/2026-09-21-bot-name-patterns/specs/pulls/spec.md": + "## MODIFIED Requirements\n\n### Requirement: Bot-Filter\n" + "`req~pulls.bot-filter~3`\n", + "docs/changes/2026-09-21-gitea-bot-logins/specs/pulls/spec.md": + "## MODIFIED Requirements\n\n### Requirement: Bot-Filter\n" + "`req~pulls.bot-filter~2`\n", + "a_test.go": "// [impl->req~pulls.bot-filter~3]\n", + }, []), + "doppelter kurzname": ({ "docs/specs/zugang/spec.md": "### Requirement: Eins\n`req~zugang.doppelt~1`\n"