AP0 Quick Wins: Chart.js lokal, Scaffolding weg, Umlaute, FinalProtocol-Titel

- Chart.js von jsDelivr-CDN lokal vendored (static/js/vendor/chart.umd.min.js
  v4.4.7), progress.html darauf umgestellt -> schliesst das einzige externe
  Asset / DSGVO-Datenleck.
- Scaffolding entfernt: DummyController.java, dummy.html, static/hello.txt.
- Umlaute umfassend korrigiert (wort-/morphemgenau, Slugs + Code-Identifier wie
  ZahlungPruefung/GEPRUEFT/Daemon/Boehm bewusst geschont): usv.json,
  devlab/techtacular/bughunter content.json, techtacular.js, zahlensysteme.html,
  style.css-Mojibake. V21-Migration reseedet geseedete DBs aus korrigiertem JSON.
- FinalProtocol: Titel "AP1" entfernt, Hero deckt jetzt AP1 und AP2 ab.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WFxcjtc9HNP4VECT6Ur1tE
This commit is contained in:
AliceDarkest
2026-07-20 23:37:55 +02:00
parent c5009d1718
commit e52cada445
14 changed files with 300 additions and 318 deletions
+5 -5
View File
@@ -15,8 +15,8 @@
{
"slug": "menue-fallt-durch", "title": "Fall #3: Das fallende Menü", "language": "Java", "difficulty": "junior", "examScope": "AP1",
"description": "Der Punkt ‚Status‘ öffnet unerwartet den Abmelde-Dialog.", "briefing": "Ein Azubi meldet einen Fehler im Konsolenmenü. Prüfe die Verzweigungen.", "consoleOutput": "Eingabe: 2\nStatus: OK\nAbmeldung gestartet",
"files": [{"filename":"Menue.java","content":"switch (auswahl) {\n case 1:\n System.out.println(\"Start\");\n break;\n case 2:\n System.out.println(\"Status: OK\");\n case 3:\n System.out.println(\"Abmeldung gestartet\");\n break;\n default:\n System.out.println(\"Ungültig\");\n}"}],
"bugs": [{"points":100,"file":"Menue.java","line":6,"category":"logik","evidence":"Nach dem Status-Fall wird der nächste case ebenfalls ausgeführt.","fixOptions":[" break;"," continue;"," return false;"," case 2: break;"],"fixIndex":0,"fixedLine":" break;","explanation":"Ohne break fällt ein switch in den nächsten Fall durch (fall-through)."}]
"files": [{"filename":"Menü.java","content":"switch (auswahl) {\n case 1:\n System.out.println(\"Start\");\n break;\n case 2:\n System.out.println(\"Status: OK\");\n case 3:\n System.out.println(\"Abmeldung gestartet\");\n break;\n default:\n System.out.println(\"Ungültig\");\n}"}],
"bugs": [{"points":100,"file":"Menü.java","line":6,"category":"logik","evidence":"Nach dem Status-Fall wird der nächste case ebenfalls ausgeführt.","fixOptions":[" break;"," continue;"," return false;"," case 2: break;"],"fixIndex":0,"fixedLine":" break;","explanation":"Ohne break fällt ein switch in den nächsten Fall durch (fall-through)."}]
},
{
"slug": "absturz-um-mitternacht", "title": "Fall #4: Der Absturz um Mitternacht", "language": "Java", "difficulty": "senior", "examScope": "AP2_FIAE",
@@ -43,11 +43,11 @@
"slug": "archiv-unter-druck", "title": "Fall #7: Archiv unter Druck", "language": "Java", "difficulty": "senior", "examScope": "AP2_FIAE",
"description": "Der tägliche Audit-Export bricht bei einem leeren Archiv ab und die CSV-Route stürzt nur in der Nachtverarbeitung ab.", "briefing": "Das Compliance-Team benötigt den Export vor 06:00 Uhr. Zwei Fehler liegen in getrennten Dateien; der Stacktrace nennt nur den Controller.", "consoleOutput": "java.lang.NullPointerException: Cannot invoke \"String.equals(Object)\"\n at ExportController.ausliefern(ExportController.java:4)\n[warn] Audit-Archiv enthält heute keine Einträge",
"files": [
{"filename":"AuditExport.java","content":"import java.util.List;\n\npublic class AuditExport {\n public String export(List<String> eintraege) {\n if (eintraege == null && eintraege.isEmpty()) {\n return \"Keine Einträge\";\n }\n return String.join(\"\\n\", eintraege);\n }\n}"},
{"filename":"ExportController.java","content":"public class ExportController {\n public String ausliefern(String format, AuditExport export, java.util.List<String> eintraege) {\n if (format.equals(\"csv\")) {\n return export.export(eintraege);\n }\n return \"Format nicht unterstützt\";\n }\n}"}
{"filename":"AuditExport.java","content":"import java.util.List;\n\npublic class AuditExport {\n public String export(List<String> einträge) {\n if (einträge == null && einträge.isEmpty()) {\n return \"Keine Einträge\";\n }\n return String.join(\"\\n\", einträge);\n }\n}"},
{"filename":"ExportController.java","content":"public class ExportController {\n public String ausliefern(String format, AuditExport export, java.util.List<String> einträge) {\n if (format.equals(\"csv\")) {\n return export.export(einträge);\n }\n return \"Format nicht unterstützt\";\n }\n}"}
],
"bugs": [
{"points":150,"file":"AuditExport.java","line":5,"category":"laufzeit","evidence":"Bei null wird rechts von && trotzdem ein Methodenaufruf ausgeführt.","fixOptions":[" if (eintraege == null || eintraege.isEmpty()) {"," if (eintraege != null && eintraege.isEmpty()) {"," if (eintraege.isEmpty() || eintraege == null) {"," if (eintraege == null & eintraege.isEmpty()) {"],"fixIndex":0,"fixedLine":" if (eintraege == null || eintraege.isEmpty()) {","explanation":"Die ODER-Verknüpfung beendet die Auswertung sofort, wenn die Liste null ist. Danach kann isEmpty sicher geprüft werden."},
{"points":150,"file":"AuditExport.java","line":5,"category":"laufzeit","evidence":"Bei null wird rechts von && trotzdem ein Methodenaufruf ausgeführt.","fixOptions":[" if (einträge == null || einträge.isEmpty()) {"," if (einträge != null && einträge.isEmpty()) {"," if (einträge.isEmpty() || einträge == null) {"," if (einträge == null & einträge.isEmpty()) {"],"fixIndex":0,"fixedLine":" if (einträge == null || einträge.isEmpty()) {","explanation":"Die ODER-Verknüpfung beendet die Auswertung sofort, wenn die Liste null ist. Danach kann isEmpty sicher geprüft werden."},
{"points":150,"file":"ExportController.java","line":3,"category":"laufzeit","evidence":"Der Stacktrace zeigt auf den Methodenaufruf equals; format kann aus einem optionalen Jobparameter stammen.","fixOptions":[" if (\"csv\".equals(format)) {"," if (format == \"csv\") {"," if (format.equalsIgnoreCase == \"csv\") {"," if (format != \"csv\") {"],"fixIndex":0,"fixedLine":" if (\"csv\".equals(format)) {","explanation":"Wird die feste Zeichenkette links verwendet, liefert equals bei einem null-Wert einfach false statt eine NullPointerException auszulösen."}
]
},