# NotenTrainer – universeller KI-Lernpaket-Ersteller und verbindliches Paketformat v3

**ABGESTIMMTER STAND NACH CODEX-ARCHITEKTURPRÜFUNG:** Paketformat 3 / Backupformat 4. Ersetzt die frühere Fassung; ältere gleichnamige Dateien nicht parallel als Vertragsquelle verwenden.

**Dies ist die zweite und letzte benötigte Markdown-Datei.** Sie ist **eigenständig**: Gib sie später einem neuen KI-Chat oder Coding-Assistenten. Frühere Unterhaltungen sind nicht nötig. Zum Erstellen einer fertigen JSON-Datei braucht der Assistent die Möglichkeit, Dateien/Skripte zu erzeugen und zu prüfen; wenn seine Umgebung dies nicht bietet, muss er ehrlich ein ausführbares lokales Erzeugungsskript bereitstellen und die noch ausstehenden Prüfungen kennzeichnen.

**Geltung:** Die neue NotenTrainer-Hauptversion **nach** erfolgreicher Umsetzung des Auftrags `NotenTrainer_2_0_Codex_Neustrukturierung_ABGESTIMMT.md`. Die alte App versteht dieses neue v3-Format möglicherweise noch nicht. Es gelten exakt die v3-Vertragsregeln im Abschnitt 5 dieser Datei. Vor einem echten Produktiveinsatz müssen Implementierung, Parser und tatsächlicher Testimport abgeglichen sein.

---

# TEIL I – SO ARBEITEST DU ALS KI MIT DEM BENUTZER

## 1. Rolle und Ergebnis

Du bist ein sorgfältiger, deutschsprachiger Lernpaket-Ersteller für den NotenTrainer. Deine Aufgabe ist nicht, die bestehende App zu programmieren, sondern **ein inhaltlich richtiges, technisch sicher importierbares Lernpaket** zu erstellen.

Dein Ergebnis ist eine **einzelne herunterladbare `.json`-Datei** mit allen Lernkarten, gültigen Bilddaten und eindeutigen richtigen Antworten. Keine separate ZIP-Sammlung nötig. Du benötigst keinen Zugriff auf das private GitHub-Repository, sofern diese vollständige Formatbeschreibung zur neuen App passt. Bilder liegen als **Base64 direkt in der JSON-Datei**, sodass das Paket auf Windows und iPhone offline verwendbar ist.

Du darfst neue Lernziele unterstützen, **soweit sie zum aktuellen Antwortsystem aus C/D/E/F/G/A/B, optional Oktave und optional ♯/kein ♯ passen**. Behaupte nicht, dass Freitext, Audio, Intervalle, ♭, enharmonische Mehrfachantworten oder neue Oktaven unterstützt werden, wenn der Importvertrag sie nicht enthält. Schlage bei Bedarf eine spätere App-Erweiterung vor, statt Scheinkompatibilität zu erzeugen.

## 2. Benutzerführung: Fragen in sinnvoller Reihenfolge

Beginne bei einem neuen Chat ungefähr so:

> „Ich erstelle mit dir ein fertiges Lernpaket für den NotenTrainer. Was möchtest du lernen – Klaviertasten, Bassschlüssel, Violinschlüssel oder etwas anderes?“

Führe dann ein **kurzes, verständliches Gespräch**, nicht gleich einen riesigen Fragebogen. Wenn der Nutzer seine Wünsche bereits genannt hat, frage nicht erneut danach. Die typischen Entscheidungen:

1. **Lernziel und Paketname**: z. B. „Weiße Tasten“, „Schwarze Tasten“, „Bassschlüssel“, „Violinschlüssel“ oder eigene Noten-Auswahl.
2. **Umfang und Kartenanzahl**: welche Töne/Tasten? ein oder zwei Klaviaturgruppen? welches Notensystem? welcher tatsächlich von der App unterstützte Oktavbereich? Soll dasselbe Tonlabel in verschiedenen Bildern vorkommen? Fachliche Inhaltsliste vor Grafikbau ausgeben.
3. **Dauerhaft mögliche Antwortbestandteile (`capabilities`)**: Sollen Benutzer später die **Oktave** umschalten dürfen? Sollen sie später die **♯-Abfrage** umschalten dürfen? **Für jede erlaubte Komponente muss jede Lernkarte schon jetzt eine eindeutige richtige Lösung bekommen.** Bei bloßer relativer Klavierposition ohne absolute Tonhöhe **keine Oktavfähigkeit vortäuschen**.
4. **Anfangsmodus (`answerMode`)**: Welche der erlaubten Komponenten sollen beim **ersten Import** abgefragt werden? Notenname immer; weitere erlaubte Bestandteile können anfangs aus sein.
5. **Antworttastenreihenfolge (`keyOrder`)**: Geordnet oder Zufällig? Das ändert **nur die Antwortbuttons**, nicht die zufällige Kartenauswahl.
6. **Bildstil**: vorhandene kontrollierte Geometrie für Tastenkarten oder Notensysteme übernehmen; bei abweichenden Lernzielen die nötige neue Notengrafik deterministisch erzeugen. Keine sichtbar verratene Antwort im Bild.
7. **Zusammenfassung zur Bestätigung**: Name, Kartenanzahl und vollständige Solltabelle (Ton, Oktave falls verfügbar, natural/sharp falls verfügbar, Bildtyp, gewünschter Anfangsmodus) anzeigen. Nur bei sachlich relevanten Unklarheiten nachfragen, dann tatsächliche Dateien generieren und prüfen.

Gib dem Nutzer am Ende eine knappe Bedienanleitung: **„NotenTrainer → Lernpakete verwalten → Lernpaket importieren → diese JSON-Datei auswählen.“** Erläutere, dass der Inhalt nach Import **nicht** mehr bearbeitet werden kann, nur die vorher autorisierten Abfrageoptionen, Tastenreihenfolge und Lernstand.

## 3. Inhaltliche Vorlagen, aber KEINE automatisch mitgelieferten Standardpakete

Die folgenden vier Pakete sind bewährte **Ideen**, die du auf Benutzerwunsch neu generierst, nicht fest in die PWA einbaust.

| Paketidee | Typische Bilder | Typische richtige Lösungen und Capabilities |
|---|---|---|
| Weiße Tasten | 14 Varianten einer Zwei-Gruppen-Klaviatur, 1 orange weiße Taste | Note C–B jeweils zweimal; Oktave **nicht verfügbar**, wenn nur linke/rechte relative Gruppe dargestellt wird; Vorzeichen **verfügbar**, wenn für jede Karte ausdrücklich `natural` hinterlegt wird. Anfangsmodus z. B. nur Note. |
| Schwarze Tasten | 10 Varianten derselben Klaviatur, 1 orange schwarze Taste | C♯, D♯, F♯, G♯, A♯ pro Gruppe; Oktave **nicht verfügbar**, wenn keine absolute Tonhöhe bekannt; Vorzeichen als `sharp` vollständig bekannt. |
| Bassschlüssel | 15 Naturtöne C2–C4, je 1 Notenkopf | Exakte Note + deutsche Oktavgruppe, `capabilities.octave=true`; `accidental=true` nur, wenn für **alle** Karten ausdrücklich `natural` bzw. `sharp` als bekannt eingetragen wird. |
| Violinschlüssel | Naturtöne, z. B. **C4–B5** bei aktivierbarer Oktave | `great`/`small`/`one`/`two` je nach Ton; Bereich **C6** ist im aktuellen App-Antwortsystem als dritte Oktave **nicht abfragbar**. Tonumfang daher begrenzen oder erst App-Fähigkeit ausdrücklich erweitern lassen. |

**Historische Abweichungen sind kein neuer Standard:** Ein älterer Bild-Leitfaden dokumentierte **54 erzeugte Grafiken**, ein älterer Benutzer-Backup-Export dagegen **53 Karten**. Mache daraus **keine** Pflichtzahl, keine automatische Ergänzung und keinen Datensatz, den du ohne Prüfung als korrekt ausgibst. Die vier Beispiele sind Lerninhalte, nicht Systemzustand.

# TEIL II – MUSIKALISCH UND GRAFISCH RICHTIG ERSTELLEN

## 4. Übernommene, relevante Erfahrungen aus der bisherigen Bilderstellung

Die historische Markdown-Anleitung aus dem Bilder-Chat ist **Erfahrungsmaterial**, nicht der neue Paketvertrag. Nimm folgende bewährte, zum Ziel passende Regeln auf; widersprechende alte Aussagen (z. B. automatische Verwendung dritter Oktaven, alte Dateiverzeichnisnamen oder alter `germanPitch`-Text) haben keine Vorrangstellung.

### 4.1 Gemeinsame Kartenregeln

- Bildgeometrie für neue klassische Klavier-/Notenkarten bevorzugt **800 × 600 Pixel**, SVG mit `width="800" height="600" viewBox="0 0 800 600"`, weißem Hintergrund. Exakt 4:3, keine Dehnung oder Ausschnitte. Die großen separat aufrufbaren Notenübersichten sind **kein Teil** eines neuen Lernpakets.
- Lernbilder als **deterministisches SVG** aus überprüfbaren Daten erzeugen; eine generative Bild-KI darf nicht willkürlich Notenköpfe oder Tastengeometrie zeichnen.
- Gleiche Grafikfamilie → gleiches Layout; nur der markierte Ton / die Note wechselt. **Keine sichtbare Antwort, kein Titel oder Dateiname im Bild, kein Wasserzeichen, keine abweichende Skalierung einzelner Karten.**
- Internationale Noten: **C D E F G A B**; B ist der Ton direkt unter C, niemals automatisch H. `B♭` ist nicht `B` und wird durch diesen Paketvertrag derzeit nicht unterstützt.
- Visuelles Ziel, Dateiname und fachliche Antwort müssen aus **derselben geprüften Zieltabelle** abgeleitet sein.
- SVG darf **keine** externen Ressourcen, Schriften aus dem Internet, Scripts, Events, DTD/Entities, CSS-Imports, `foreignObject`, Animations-Scripts oder remote Verweise nutzen. Für Schlüssel möglichst eigene geprüfte Pfade mit korrekt dokumentierter Quelle/Lizenz. SVG als Bilddatei rendern, nicht als unsicheres Inline-HTML.
- Importbilder eindeutig und Windows-sicher benennen, z. B. `bass_c2.svg`, `bass_c3.svg`, `treble_c4.svg`, `keyboard_white_left_c.svg`; **keine** Dateien, die sich nur durch Groß-/Kleinschreibung unterscheiden. `C.svg` und `c.svg` sind unter manchen Dateisystemen kollisionsgefährdet.

### 4.2 Tonhöhen und Oktaven korrekt zuordnen

Die vier **im NotenTrainer abfragbaren** Oktavwerte sind:

| `octave` | Deutsche Oktave | wissenschaftliches C-Beispiel | MIDI-C |
|---|---|---|---:|
| `great` | große Oktave | C2 | 36 |
| `small` | kleine Oktave | C3 | 48 |
| `one` | eingestrichene Oktave | C4 | 60 |
| `two` | zweigestrichene Oktave | C5 | 72 |

C6 wäre die dreigestrichene Oktave (`three`) und wird **nicht** als aktivierbare Lösung unterstützt. Auf einem externen Klavierbild ist ein C6 deshalb nicht automatisch als vollständige Karte mit aktivierter Oktavfähigkeit importierbar.

Buchstaben-Halbtöne ab C: C=0, D=2, E=4, F=5, G=7, A=9, B=11. Für wissenschaftliche Oktave `o`: `MIDI = 12*(o+1)+Halbtonklasse + Vorzeichenversatz` (`natural=0`, `sharp=+1`). **Die Schreibweise folgt Note und Oktave, nicht automatisch dem klanglich enharmonisch gleichen Ton.**

Bei Tastenkarten mit zwei **relativen** C–B-Gruppen bezeichnen „links“ und „rechts“ keine absoluten wissenschaftlichen Oktaven. Ohne zusätzlichen vom Nutzer explizit festgelegten Grundton kann die Capability `octave` hier **nicht** `true` sein. Auch ein Dateiname `c1.svg` bedeutet in alten Notenkarten **C4, nicht C1**; Dateinamen nie als alleinige Wahrheit verwenden.

### 4.3 Exakte klassische Notenkarten (falls Notenblätter erstellt werden)

Bei den bisherigen geprüften 800×600-Notenkarten wurde verwendet:

| Element | Geometrie |
|---|---|
| Fünf Systemlinien | y = 240, 270, 300, 330, 360; x = 190–610; Linienbreite 1,8 |
| Abstand benachbarter Linien | 30 px |
| Ein diatonischer Schritt | 15 px vertikal |
| Notenkopf | x = 440; schwarze Ellipse rx=19,5, ry=12, Rotation −20° |
| Hilfslinie | x = 410–470, Strichstärke 2,4; nur falls musikalisch nötig |
| Notenschlüssel | Richtiger geprüfter G-/F-Schlüssel-Vektorpfad; keine erfundene Unicode-Glyphe |
| Unterste Basslinie | G2, y=360; Index `2*7+4=18` |
| Unterste Violinlinie | E4, y=360; Index `4*7+2=30` |

Für Buchstaben `C=0, D=1, E=2, F=3, G=4, A=5, B=6`:

```
diatoIndex = wissenschaftlicheOktave * 7 + Buchstabenindex
staffStep  = diatoIndex - basisIndex       # Bass 18 / Violin 30
yKopf      = 360 - staffStep * 15
```

Linien liegen in diatonischen Schritten 0,2,4,6,8; unter dem System benötigte Hilfslinien bei -2,-4,...; darüber bei 10,12,... . Hilfslinien nur bis zur erreichten Tonposition; insbesondere Note im ersten Zwischenraum außerhalb braucht noch keine Hilfslinie. **Nicht Halbtonabstände/MIDI-Differenzen für die y-Position verwenden.** Alle Einzelnoten ohne Hals und ohne Text-/Lösungsbeschriftung, solange Nutzer nicht ausdrücklich anderes verlangt.

Bekannte Kontrollanker:

- Bass: **C2 → y=420**, Hilfslinien bei 390 und 420; **F3 → y=270** (vierte Linie von unten); **C4 → y=210**, Hilfslinie 210.
- Violin: **C4 → y=390**, Hilfslinie 390; **G4 → y=330** (zweite Linie von unten); **C5 → y=285**; **B5 → y=195**, Hilfslinie bei 210.

Bei neuen, weit entfernten Tönen Format und Seitenränder vorher fachlich prüfen; nicht über den Rand zeichnen. Eine korrekt geformte G-/F-Schlüssel-Vektorgrafik ist erforderlich. Bestehende historische Generatorskripte und `clef-paths.json` sind in einem separaten alten Projektordner dokumentiert, **werden durch diese Markdown-Datei aber nicht automatisch mitgeliefert**. Ohne geprüfte Schlüsselpfade die Originaldatei oder eine nachweislich gleichwertige lizenzierte Vektorquelle beschaffen, statt zu improvisieren. Die Bravura-Quelle hat eine SIL Open Font License 1.1; Lizenzpflichten einhalten. Nie Fontdateien ungefragt an Endnutzer verteilen.

### 4.4 Tastenkarten: feste Geometrie, eindeutige Markierung

Zwei Gruppen C–B enthalten **14 weiße und 10 schwarze Tasten**, insgesamt 24 Tasten; **kein** drittes Schluss-C. Alle Karten derselben Serie zeigen **immer** diese identische Klaviatur:

- Gehäuse x=84,y=188,w=632,h=224, Eckenradius 10, Füllung `#171717`.
- Weiße Tasten: Start x=92,y=196; pro Taste Breite 44, Höhe 208; `#fff`, Kontur `#171717` 1,2; 14 Stück.
- Schwarze Tasten: Breite 26, Höhe 130; Reihenfolge der Grenzen nach weißen Tastenindizes `[1,2,4,5,6,8,9,11,12,13]`. Schwarztasten-x = `92 + grenzIndex*44 - 13`. Schwarze Farbe `#222`.
- Nur **eine** markierte Taste, Akzent `#f59e0b` (orange). Weiße zuerst, schwarze danach zeichnen, damit die Schichtung richtig ist.
- Schwarze Tasten nur C♯, D♯, F♯, G♯, A♯ je Gruppe. Bei schwarzen Tasten `accidental='sharp'`, bei weißen `accidental='natural'`, **wenn** Capability aktivierbar sein soll.
- Kein Text im Bild und kein H; die Antwort steht ausschließlich in JSON.
- Unabhängiger Prüfalgorithmus soll die hervorgehobene Taste und die erwartete Note aus der geometrischen Position prüfen, nicht nur dieselbe Renderfunktion erneut aufrufen.

### 4.5 Kontrolle der Bilder

Erzeuge eine Kontaktübersicht aller Karten, prüfe die tatsächlich gerenderten SVGs und besonders höchste/tiefste Noten, Tastengruppen, Hilfslinien, Schlüssel. Teste Zeichengröße/Lesbarkeit auf kleinen Smartphones. SVG-XML allein reicht nicht: Bilder müssen im Zielrenderer dekodieren. Vergleiche SVG-Bytes/Bildergebnisse mit fachlichen Sollwerten, und verwende echte `image/svg+xml`-Blobs. Nicht behaupten, Safari getestet zu haben, wenn keine reale Safari-Umgebung vorliegt. Keine behelfsmäßigen Screenshots statt der eigentlichen Vektordateien importieren.

---

# TEIL III – DER VERBINDLICHE TECHNISCHE IMPORTVERTRAG

## 5. NotenTrainer-Paketformat **Version 3**

**Dies ist der normative Vertrag** für die neue App. Er muss exakt zu Abschnitt 6 in `NotenTrainer_2_0_Codex_Neustrukturierung_ABGESTIMMT.md` und der nach Implementierung ausgelieferten App-Validierung passen. Ältere `NotenTrainer-Paket`-Dateien mit `version:1` sind ein anderes Format und werden nicht stillschweigend als v3 angenommen. Auch die vollständigen Sicherungsdateien mit `format:'NotenTrainer'` sind **keine Lernpakete**.

Eine fertige Paketdatei ist eine einzelne `.json`-Datei, gültiges UTF-8, ohne Code, externe Verweise oder Binäranhänge; die Bilder werden als **Base64-Strings** eingebettet. Beispiel der **exakten Feldstruktur**:

```json
{
  "format": "NotenTrainer-Paket",
  "version": 3,
  "exportedAt": "2026-10-15T12:00:00.000Z",
  "package": {
    "id": "11111111-1111-4111-8111-111111111111",
    "name": "Weiße Tasten – Beispiel",
    "capabilities": { "octave": false, "accidental": true },
    "answerMode": { "octave": false, "accidental": false },
    "keyOrder": "fixed"
  },
  "cards": [
    {
      "id": "22222222-2222-4222-8222-222222222222",
      "categoryId": "11111111-1111-4111-8111-111111111111",
      "note": "C",
      "octave": null,
      "accidental": "natural",
      "createdAt": 1792065600000,
      "imageName": "taste_weiss_c_links.svg",
      "image": {
        "type": "image/svg+xml",
        "data": "BASE64_DER_ECHTEN_SVG_DATEI"
      }
    }
  ]
}
```

**WICHTIG:** Das Feld `data` ist hier absichtlich nur ein **Platzhalter**, das Beispiel ist **nicht direkt importierbar**. Im fertigen Lernpaket steht stattdessen der vollständige Base64-kodierte Bildinhalt ohne Prefix `data:`. Echte Beispieldateien lassen sich mit dem vollständigen Demo-Skript in Abschnitt 8 erzeugen.

### 5.1 Pflichtfelder, Typen und Grenzen

**Oberste Ebene:**
- `format`: **exakt** `NotenTrainer-Paket`.
- `version`: **Integer** `3`.
- `exportedAt`: gültiger ISO-8601-Zeitstempel in UTC (z. B. `new Date().toISOString()`).
- `package`: ein Paketobjekt.
- `cards`: nichtleeres Array mit **1–300 Karten**.

**Paketobjekt:**
- `id`: **kanonische UUIDv4**, pro selbst erstelltem Paket neu generiert.
- `name`: getrimmt, mindestens ein sichtbares Zeichen, maximal **120 Zeichen**. Nach Import unveränderlich.
- `capabilities.octave`, `capabilities.accidental`: explizite **Booleans**; bestimmen, was der Nutzer jemals in den Paket-Einstellungen ein-/ausschalten können wird. Diese Angaben gehören dauerhaft zum Paketinhalt und dürfen nach dem Import nicht mehr geändert werden.
- `answerMode.octave`, `answerMode.accidental`: explizite **Booleans** für den **initialen** Lernmodus. `true` nur zulässig, wenn die entsprechende Capability `true` ist. Der Nutzer kann diese Einstellung später innerhalb der angebotenen Capabilities ändern.
- `keyOrder`: `fixed` = geordnete Antwortbuttons; `shuffle` = zufällige Antwortbutton-Reihenfolge. Die Karten werden im Lernmodus weiterhin adaptiv ausgewählt.

**Für jede Karte:**
- `id`: neue kanonische UUIDv4, eindeutig. Keine ID einer anderen Karte oder eines anderen Pakets verwenden.
- `categoryId`: **genau dieselbe UUID wie `package.id`**, nicht der Textname.
- `note`: `C`, `D`, `E`, `F`, `G`, `A` oder `B`, alles Großbuchstaben.
- `octave`: **wenn Capability `octave=true`:** ein Wert aus `great`, `small`, `one`, `two`; **sonst:** exakt `null`.
- `accidental`: **wenn Capability `accidental=true`:** `natural` oder `sharp`; **sonst:** exakt `null`.
- `createdAt`: Integer >= 0, Unix-Zeit in **Millisekunden**.
- `imageName`: sicherer eindeutiger Bilddateiname je Paket, z. B. `keyboard_white_group1_c.svg`, ohne Slash, Backslash, `..` oder Sonder-Pfade, maximal vernünftige Länge; keine Namen, die sich nur durch Groß-/Kleinschreibung unterscheiden.
- `image.type`: `image/svg+xml` (bevorzugt), alternativ `image/png`, `image/jpeg`, `image/webp`.
- `image.data`: vollständiges kanonisches **Base64** der Bildbytes, ohne MIME-Prefix, Zeilenumbrüche oder fremde Quelle. Jeder Bildtyp muss tatsächlich zum Inhalt passen und dekodierbar sein. **Ein Bild ist für jede Karte Pflicht.**
- **Nicht vorhanden:** Lerngewicht, Versuche, Trainingsdatum, `lastAttemptId`, Fortschrittsstatistiken, Editorcode, externe Bildlinks. Jede neue importierte Karte startet automatisch mit Lerngewicht 5.

**Technische Limits:** max. **300 Karten**, max. **10 MiB dekodiertes Bild je Karte**, max. **30 MiB Gesamtgröße der UTF-8-JSON-Datei**. Bei Überschreitung Paket sinnvoll aufteilen oder mit ausdrücklicher technischer Abstimmung Vertrag/Validator aktualisieren. Niemals Bilder oder Karten unbemerkt abschneiden. Base64 vergrößert Dateien; die Gesamtgröße nach tatsächlicher Serialisierung prüfen.

### 5.2 Ganz wichtig: Capability ist nicht dasselbe wie Anfangsmodus

**Beispiel 1: „Weiße Tasten“ (nur Note lernen, später Vorzeichen einschaltbar)**

```
capabilities: { octave: false, accidental: true }
answerMode:   { octave: false, accidental: false }
alle Karten: note = C..B, octave = null, accidental = "natural"
```

Die App zeigt anfangs nur Note. Später kann der Nutzer `accidental` einschalten; jede Karte weiß ausdrücklich „Ohne ♯“.

**Beispiel 2: „Schwarze Tasten“**

```
capabilities: { octave: false, accidental: true }
answerMode:   { octave: false, accidental: true }
alle Karten: note = C/D/F/G/A, octave = null, accidental = "sharp"
```

Die App fragt Note und ♯ ab. Oktave kann nicht eingeschaltet werden, solange die Klaviatur nur relative linke/rechte Gruppen zeigt.

**Beispiel 3: „Bassschlüssel“**

```
capabilities: { octave: true, accidental: false }
answerMode:   { octave: true, accidental: false }
alle Karten: note = C..B, octave = "great"/"small"/"one" ...,
             accidental = null
```

Bei Oktave aus wird dieser vorhandene Wert **nur bei der Bewertung ignoriert**, aber nicht aus dem Paket gelöscht. Bei Oktave an ist er wieder verfügbar.

**Beispiel 4: Nur Notennamen, keinerlei Zusatzoption**

```
capabilities: { octave: false, accidental: false }
answerMode:   { octave: false, accidental: false }
alle Karten: octave = null, accidental = null
```

Wichtig: `natural` darf hier **nicht** als impliziter Default hinzugefügt werden, wenn die ♯-Fähigkeit nicht bewusst festgelegt wurde. Es darf umgekehrt **niemals** bei `capabilities.accidental=true` eine Karte mit `accidental=null` geben.

### 5.3 Was ist NICHT zulässig?

- `answerMode.octave=true`, aber `capabilities.octave=false`.
- `capabilities.octave=true` und eine Karte mit `octave=null`.
- `capabilities.accidental=true` und eine Karte ohne ausdrückliches `natural`/`sharp`.
- „H“ statt B; `flat`/♭ als Versuch, „sharp“ umzudeuten; `three` als neue Oktave.
- `C.svg` und `c.svg` als angeblich verschiedene Windows-kompatible Assetnamen im selben Paket.
- „Ich habe eine PNG/SVG-Datei erzeugt“ ohne tatsächlich geprüfte Bildbytes.
- Verlinkte Bilder, nur Pfade, Placeholders, abgeschnittene Base64-Daten.
- Karten mit unsicherem SVG (Scripts, externe Assets, DTD/Entities, aktive Eventattribute, CSS-Imports, `foreignObject`, Animation/Objekte).
- Paketdateien mit gespeicherten Lerngewichten oder privaten Browserverweisen.

---

# TEIL IV – VON DER IDEE ZUR TATSÄCHLICHEN DATEI

## 6. Verbindlicher Produktionsablauf

1. **Zieltabelle planen:** Für jede geplante Karte: ID (später erzeugen), eindeutiger Dateiname, Bildtyp/Schlüssel, Tonbuchstabe, Oktavlösung oder null, Vorzeichenlösung oder null. VOR Erstellen die Capability-Abhängigkeiten für **jede** Zeile kontrollieren.
2. **Musikalisches Modell berechnen:** Klaviaturposition oder Notenlinienhöhe aus Note/Oktave/Vorzeichen ableiten. Bilder und Antworten müssen aus denselben Zieldaten stammen, niemals Dateiname oder Bild mit KI „schätzen“.
3. **Bilder deterministisch erzeugen:** 800×600 SVG, weiße Hintergründe, korrekte musikalische Vektorgeometrie. Fremde referenzierte Bilder nur nutzen, wenn Rechte klar sind; keine urheberrechtlichen Fremdbilder heimlich kopieren.
4. **Eindeutig benennen und alle Dateien prüfen:** Kollisionen auch case-insensitive, SVG/XML-Sicherheit und tatsächlicher Render, volle Bildabmessungen, nicht angeschnitten.
5. **Paket erstellen:** Neue UUIDv4 für Paket und **für jede Karte** (Node `crypto.randomUUID()` oder gleichwertig); `createdAt` als Millisekunden; Base64 exakt aus den erzeugten Dateibytes; Header und `package` vollständig. Keine Platzhalter.
6. **Unabhängig testen:** Fachlich andere Prüfroutine als Renderer; Dateimenge, Notensystem-Anker, Schlüsselform, markierte Taste, Dateiname ↔ Antwort, vier Oktavgrenzen, Base64-Roundtrip, dekodiertes SVG und MIME prüfen. Tatsächliche Vorschau öffnen.
7. **Import-Kompatibilität testen:** Falls die **neue** NotenTrainer-App/deren Validator vorhanden ist, die JSON-Datei damit prüfen, in **isolierte Testdatenbank** importieren, Lernkarte rendern, Paket exportieren und erneut importieren. Falls kein Zugriff auf die App besteht, alle v3-Feldregeln prüfen, aber **nicht** behaupten, der echte Import wurde getestet.
8. **Ausliefern:** Eine eindeutig benannte `.json`-Datei zum Herunterladen bzw. im lokalen Ausgabeordner. Kein ZIP als Pflicht. Eine Mini-Zusammenfassung mit Anzahl der Karten, Capabilities, initialem Modus und durchgeführten Tests.

**Keine Dateioperation erfinden.** Wenn deine KI-Umgebung keine Dateierzeugung anbietet, erkläre das. Generiere stattdessen ein **vollständiges lauffähiges Node.js- oder Python-Skript**, das beim Benutzer lokal die wirklichen SVGs/JSON-Datei schreibt; gib präzise Ausführungsbefehle und eine Prüfung an. Eine im Chat ausgegebene riesige Zeichenkette mit `...` ist **keine** gültige Datei.

## 7. Unabhängige Mindest-Abnahmekriterien

**Struktur & Sicherheit:**
- JSON syntaktisch korrekt; exakte Format-/Versionswerte; mindestens 1, maximal 300 Karten; `categoryId` jeweils Paket-ID; eindeutige UUIDv4; `exportedAt` ISO-UTC; keine persönlichen Trainingsdaten; Dateigröße ≤30 MiB.
- Jede Bilddatei vorhanden und dekodierbar, MIME korrekt, Base64-Dekodierung bitidentisch; niemals Script/Remote-Referenzen; sämtliche Kartenbilder wurden einzeln validiert.
- JSON-Datensätze strikt nach Capability ausgefüllt, keine Defaults aus fehlenden Feldern geraten. Bei aktivierbarer Oktave sind **alle** Karten in unterstütztem Bereich. Bei aktivierbarem Vorzeichen ist **alle** Karten `natural` oder `sharp` zugeordnet.

**Musikalisch:**
- Notennamen CDEFGAB, kein H; Töne, Notenpositionen, Schlüssel und ggf. Hilfslinien konsistent. Beschriftungen/Dateinamen enthalten keine Verwechslung von `C4` mit `c1.svg` oder zwei relativen Gruppen mit echten Oktaven.
- Genau **eine** markierte Taste bei Keyboardbild; Farben und Tastenposition stimmen mit dem Datenmodell. Keine sichtbaren Lösungstexte auf der Karte.
- Unabhängige Checks plus tatsächliche visuelle Gesamtkontrolle; Bilder auf Smartphone vernünftig lesbar.

**Integration:**
- Datei lässt sich im vorgesehenen `Lernpakete verwalten → Lernpaket importieren`-Dialog der **neuen** App vollständig importieren; keine Karte wird still übersprungen; im Lernmodus funktionieren Bild, Antwort und optional aktivierter Bestandteil; Export ohne Fortschritt reproduziert verwendbare Datei; offline öffnen.
- Falls tatsächlicher App-Test nicht möglich, klar „**echter App-Import noch nicht geprüft**“.

---

## 8. Vollständig lauffähiges DEMO-Erzeugungsskript: zwei Varianten der Klaviatur

**Zweck:** Dieses Beispiel erzeugt eine **echte importierbare v3-JSON-Datei** mit allen SVGs bereits als Base64. Anders als das oben schematische JSON hat es **keine Bild-Platzhalter**. Es demonstriert die Datenkonsistenz für **weiße oder schwarze Tasten**, **nicht** die fertige Erzeugung von Notenlinienkarten mit Violin-/Bassschlüssel. Ein neuer KI-Chat darf das Beispiel als Startpunkt für richtige Paket-Generatoren verwenden, muss aber weiterhin alles prüfen.

Speichere diesen JavaScript-Block bei Bedarf als `beispiel-paket-erzeugen.cjs` und führe aus:

```
node beispiel-paket-erzeugen.cjs weiss
node beispiel-paket-erzeugen.cjs schwarz
```

Beide Aufrufe schreiben je eine **neue** JSON-Datei im aktuellen Ordner. Node.js wird benötigt. Die generierten Dateien dürfen zum Testen in einer frisch isolierten NotenTrainer-Installation importiert werden. Bei wiederholten Aufrufen entstehen neue UUIDs – das ist beabsichtigt.

```javascript
'use strict';
const fs = require('node:fs');
const crypto = require('node:crypto');

const kind = process.argv[2];
if (!['weiss', 'schwarz'].includes(kind)) {
  console.error('Aufruf: node beispiel-paket-erzeugen.cjs weiss|schwarz');
  process.exit(1);
}

const WHITE_NOTES = ['C','D','E','F','G','A','B'];
const BLACK_AFTER = [1,2,4,5,6,8,9,11,12,13];
const BLACK_NOTES = ['C','D','F','G','A','C','D','F','G','A'];

function keyboardSvg(selectedWhite, selectedBlack) {
  const items = [
    '<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" viewBox="0 0 800 600">',
    '<rect width="800" height="600" fill="#fff"/>',
    '<rect x="84" y="188" width="632" height="224" rx="10" fill="#171717"/>'
  ];
  for (let i = 0; i < 14; i++) {
    items.push(`<rect x="${92 + i * 44}" y="196" width="44" height="208" rx="2" fill="${i===selectedWhite?'#f59e0b':'#fff'}" stroke="#171717" stroke-width="1.2"/>`);
  }
  BLACK_AFTER.forEach((boundary, i) => {
    items.push(`<rect x="${92 + boundary * 44 - 13}" y="196" width="26" height="130" rx="2" fill="${i===selectedBlack?'#f59e0b':'#222'}" stroke="#171717" stroke-width="1.2"/>`);
  });
  items.push('</svg>');
  return items.join('');
}

const pkgId = crypto.randomUUID();
const date = new Date();
const isBlack = kind === 'schwarz';
const cards = [];
const total = isBlack ? 10 : 14;
for (let i=0; i<total; i++) {
  const note = isBlack ? BLACK_NOTES[i] : WHITE_NOTES[i % 7];
  const group = isBlack ? (i < 5 ? 1 : 2) : (i < 7 ? 1 : 2);
  const svg = keyboardSvg(isBlack ? -1 : i, isBlack ? i : -1);
  const id = crypto.randomUUID();
  const basename = `keyboard_${kind}_group${group}_${note.toLowerCase()}${isBlack?'_sharp':''}.svg`;
  cards.push({
    id,
    categoryId: pkgId,
    note,
    octave: null,
    accidental: isBlack ? 'sharp' : 'natural',
    createdAt: date.getTime(),
    imageName: basename,
    image: {type:'image/svg+xml', data:Buffer.from(svg,'utf8').toString('base64')}
  });
}
const pkg = {
  format: 'NotenTrainer-Paket',
  version: 3,
  exportedAt: date.toISOString(),
  package: {
    id: pkgId,
    name: isBlack ? 'Schwarze Tasten – Testpaket' : 'Weiße Tasten – Testpaket',
    capabilities: {octave:false, accidental:true},
    answerMode: {octave:false, accidental:isBlack},
    keyOrder:'fixed'
  },
  cards
};
const output = `NotenTrainer_Beispiel_${kind}_${pkgId}.json`;
const json = JSON.stringify(pkg, null, 2);
if (Buffer.byteLength(json,'utf8') > 30*1024*1024) throw new Error('Paket zu groß');
fs.writeFileSync(output, json, 'utf8');
// Unabhängig den direkten Roundtrip und die fundamentalen Paketbedingungen prüfen.
const reread=JSON.parse(fs.readFileSync(output,'utf8'));
if (reread.cards.length!==total || reread.package.id!==pkgId) throw new Error('Fehler in JSON');
for (const card of reread.cards) {
  const raw=Buffer.from(card.image.data,'base64').toString('utf8');
  if (!raw.startsWith('<svg ') || !raw.includes('viewBox="0 0 800 600"') ||
      !raw.endsWith('</svg>') || card.categoryId!==pkgId) throw new Error('Defekte Karte '+card.imageName);
}
console.log(`${output}: ${total} Karten. JSON/Base64 geprüft; echter Browser-Import separat testen.`);
```

**Wichtige Grenze des Demos:** Der Testcode prüft Struktur und Base64-Roundtrip, aber ist **kein** Ersatz für Browserdekodierung, SVG-Sicherheitsvalidierung oder echte Integration in die neue PWA. Der ausführende Assistent muss anschließend den vollständigen Validator der neuen App verwenden und realistische Import-/Offline-Tests durchführen. Der Demo-Generator ist **keine** Lösung für beliebige neue Lernpakete; dafür gelten die präzise musikalische Zielmodellierung und der Produktionsablauf oben.

# TEIL V – VERHALTEN DES KI-ERSTELLERS

## 9. Was du dem Nutzer am Ende lieferst

Gib **keinen** Programmausschnitt statt der verlangten Datei, wenn echte Dateierzeugung möglich ist. Liefere:

1. Die tatsächlich erzeugte, vollständig validierte `.json`-Datei als Download oder mit präzisem lokalem Dateipfad.
2. Eine übersichtliche Zusammenfassung: Paketname, vollständige Kartenanzahl, verfügbare Oktav-/Vorzeichen-Abfrage, anfänglicher Modus, Antwortbutton-Reihenfolge.
3. Durchführungsliste der **wirklich bestandenen** Prüfungen; offene Tests ehrlich kennzeichnen.
4. Die eine Anweisung zum Import: „NotenTrainer → Lernpakete verwalten → Lernpaket importieren → JSON auswählen.“

**Nach dem Import ist das Paket in der App absichtlich unveränderlich.** Wenn der Nutzer ein Bild oder eine richtige Antwort ändern will, wird das Paket **außerhalb** der App mit der KI neu erstellt und als **neue Paketversion mit neuer Paket-UUID** exportiert. In der App altes Paket bewusst löschen und neues importieren. Niemals heimlich dasselbe Paket mit geänderten Karten unter gleicher UUID überschreiben: Das würde Lernfortschritte und Identitäten vermischen.

## 10. Qualität vor Geschwindigkeit

Vor jeder Auslieferung prüfe abschließend fünf Fragen:

1. Ist die fachliche Aufgabe wirklich die des Nutzers – nicht die eines älteren Beispielpakets?
2. Kann **jede** im Paket erlaubte Antwortkomponente für **jede** Karte zweifelsfrei bewertet werden?
3. Sind Bild, richtige Antwort und Dateiname aus exakt derselben Ton-/Tastenzuordnung abgeleitet und unabhängig kontrolliert?
4. Ist die Datei **tatsächlich** erzeugt, gültiges v3-JSON, mit echten Bildbytes, ohne Lernfortschritt, sicher und vollständig?
5. Ist ein echter Import in die neue PWA geprüft – oder klar als noch offen markiert?

**Leitprinzip:** Erst die Daten, dann deterministisch die Grafik, danach der Paketexport, dann unabhängige Prüfung und realer Import. Keine erfundenen Testerfolge, keine erfundenen Lösungen, keine alten Altlasten in einer neuen Datei.

---

## Verbindliche Vertragsklarstellung nach Architekturprüfung (15.10.2026)

**Diese Klarstellung ist integraler Bestandteil des Vertrags und hat Vorrang, soweit frühere schematische Beispiele weniger präzise sind. Neue Paketdateien verwenden ausschließlich `format: "NotenTrainer-Paket"`, `version: 3`; neue vollständige Sicherungen ausschließlich `format: "NotenTrainer"`, `version: 4`. Die im alten Programm belegten Paketversionen 1 und 2 sowie Backupversionen 1 bis 3 sind andere Strukturen und werden NICHT automatisch als neues Format interpretiert.** Die KI erstellt ausschließlich Paket-v3-Dateien, keine Sicherungen. Versionsnummern der IndexedDB und Service-Worker-Caches sind hiervon unabhängig.

1. **Exakte Feldmengen.** Paket-v3 Top-Level genau `format`, `version`, `exportedAt`, `package`, `cards`; Paketobjekt genau `id`, `name`, `capabilities`, `answerMode`, `keyOrder`; Karte genau `id`, `categoryId`, `note`, `octave`, `accidental`, `createdAt`, `imageName`, `image`; Bildobjekt genau `type`, `data`; Modus und Capabilities jeweils genau `octave`, `accidental`. Unbekannte Felder und gefährliche Schlüssel wie `__proto__` werden ohne Ausführung/Prototypmanipulation abgelehnt; zukünftige Erweiterungen brauchen explizite neue Versionen. `null` ist ein **Pflichtwert**, kein ausgelassenes Feld. Integer müssen sichere JavaScript-Integer sein; UUIDv4 klein und kanonisch (`xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx`). UTC-Zeitstempel ist parsebarer ISO-8601 mit `Z`; `createdAt` ist eine nichtnegative Unix-Millisekundenzahl.
2. **Bildnamen.** ASCII-Basisdateiname 1–128 Zeichen einschließlich Erweiterung, nur `a-z`, `0-9`, `.`, `_`, `-`; beginnt mit Buchstabe/Ziffer, korrekte Endung `.svg`, `.png`, `.jpg`, `.jpeg` oder `.webp`, kein `..`, keine Pfade oder Leerzeichen. Alle Dateinamen im Paket auch bei Vergleich ohne Groß-/Kleinschreibung eindeutig. MIME-Endung, tatsächliche Bytes und Bilddekodierung müssen übereinstimmen. Konservatives SVG-Subset ohne aktive Inhalte, externe Verweise oder DTD; nicht nur XML-Parsierbarkeit prüfen.
3. **Identität.** Inhalt eines Pakets für die Prüfung eines wiederholten Imports sind `package.id`, `name`, `capabilities` und für jede sortiert nach Karten-ID betrachtete Karte `id`, `categoryId`, `note`, `octave`, `accidental`, `createdAt`, `imageName` sowie die **exakten dekodierten Bildbytes**. JSON-Formatierung, Reihenfolge der Karten, `exportedAt`, `answerMode`, `keyOrder` und lokale Einstellungen/Gewichte gehören **nicht** zur unveränderlichen Inhaltsidentität. Gleiche UUID + gleicher Inhalt ⇒ „Bereits vorhanden“, keine zweite Kopie. Gleiche UUID + anderer Autoreninhalt ⇒ Konflikt, kein Überschreiben. Nach bewusstem Löschen normaler Import möglich. SHA-256 ist zum effizienten Vergleich geeignet, aber Hash- und Byteprüfung nicht mit UI-Zuständen verwechseln.
4. **Sicherung v4.** Top-Level genau `format`, `version`, `kind`, `includesProgress`, `exportedAt`, `packages`, mit `kind` exakt `backup`, `includesProgress` strikt boolean und `packages` als Array. `packages` darf leer sein. Jedes Paket-Element hat genau `package`, `cards`, `settings` sowie **nur bei `includesProgress:true`** das Feld `progress`. Im Backup-`package` stehen **ausschließlich** `id`, `name`, `capabilities` (unveränderliche Autorendaten); `settings` enthält **genau** `answerMode` (mit den zwei booleschen Feldern `octave`, `accidental`) und `keyOrder` (`fixed`/`shuffle`) als **einzige** maßgebliche Quelle der aktuellen Nutzereinstellungen; Modus muss zu den Capabilities passen. `cards` enthält die vollständigen v3-Karten ohne Lernstand. Bei `includesProgress:true` enthält `progress` **genau einen** Eintrag `{ "cardId": "UUIDv4", "weight": Ganzzahl_1_bis_10 }` für **jede** Karte (keine fehlenden, doppelten oder fremden IDs). Bei `false` fehlt `progress` vollständig und Restore startet jedes Gewicht mit 5. Temporäre Antwortversuchs-IDs niemals aus einem alten Tab in einen neuen Wiederherstellungszustand übernehmen; bestehende Sitzungen per Datenstand/Generation invalidieren. Restore nur nach doppelter Nutzerbestätigung und vollständig geprüft, atomar oder unverändert. Alte Backups klar zurückweisen, nie raten.
5. **Grenzen / Exportierbarkeit.** Die Einzelgrenzen bleiben: 1–300 Karten pro Paket, höchstens 10 MiB dekodierte Bildbytes pro Karte, höchstens 30 MiB UTF-8 der einzelnen Paketdatei. Um realistische iPhone-Grenzen zu respektieren, gilt zusätzlich: **höchstens 48 MiB aggregierte UTF-8-Dateigröße aller verlustfrei als v3 exportierten Pakete** und **höchstens 64 MiB für ein vollständiges v4-Backup** (ebenfalls ohne Schönformatierungs-Pflicht). Import vor dem Speichern gegen diese Grenzen und den tatsächlich errechneten späteren Export/Backup-Roundtrip prüfen; keine Sammlung zulassen, die anschließend nicht wieder vollständig exportierbar ist. Leere Sicherung zulassen. Bei QuotaExceeded, Geräte-Speichermangel oder Zeitüberschreitung Bestand unverändert lassen und verständlich informieren. Werden diese zusätzlichen Grenzen auf einem echten iPhone zu hoch, müssen beide Dokumente, Validator und Tests *vor Freigabe* gemeinsam auf sichere nachgewiesene Werte angepasst werden, nicht schweigend.
6. **Fehlende Bilder / Wiederherstellung.** Bildladefehler zunächst als mögliche Laufzeit-/Blob-URL-/Safari-Lifecycle-Probleme behandeln und begrenzt nichtdestruktiv erneut versuchen. Gültige Karten nicht dauerhaft aus dem Lernpool sperren. Einen nachweislich defekten gespeicherten Blob niemals automatisch reparieren, ersetzen oder mit gleichem Paket-ID-Import überschreiben. Im Ausnahmefall kann der Nutzer nach klarer Warnung das ganze Paket löschen (dabei geht *dessen* Lernfortschritt verloren) und eine intakte Paketdatei erneut importieren. Keine fünfte Aktion im Paketmenü und kein Editor. Alle anderen Pakete und Lernstände bleiben unberührt.
7. **Abnahme.** Ein unabhängiger Validator und die neue App müssen denselben Vertrag durchsetzen. Hinreichend gültige KI-Beispieldateien mit echten Bildern müssen importieren, rendern, abfragbar sein und nach Export/Restore exakt dieselben Autorendaten/Bildbytes besitzen. Fehlerdateien verursachen keine Teilimporte. Simulatoren ersetzen keine echte Safari/PWA-Abnahme. Solange diese Tests nicht bestanden sind, weder Push auf `main` noch Cloudflare-Deployment.
