<!-- Generated by scripts/build-prompts.mjs — do not edit by hand. -->
# Bau mir dieses Werkzeug: Verarbeitungsverzeichnis

*Alles Folgende ist die fachliche Anforderung. Halte dich daran; wo sie schweigt, entscheide selbst und sage, was du angenommen hast.*

## Ausgangspunkt

Nutze die Vorlage **openToolbox**: https://github.com/m-dohmen/openToolbox. Lies dort zuerst `AGENTS.md` — sie ist maßgeblich für Schemaform, Feldtypen und die Regeln, an denen ein Einzeldatei-Build zerbricht. Alles Fachliche kommt in eine einzige Datei, `src/domain.js`.

> Ist der openToolbox-Skill installiert (`claude plugin install opentoolbox@opentoolbox`), genügt es, diese Datei einzufügen — er holt sich die Vorlage selbst.

## Das Problem dahinter

Nach Art. 30 DSGVO muss fast jedes Unternehmen ein Verzeichnis von Verarbeitungstätigkeiten führen. Fast keines führt eines, das den Namen verdient, und der Grund ist selten Unwille — es ist die Erhebung. Die Antworten liegen bei zwölf verschiedenen Leuten: die Bewerbungsmappen bei der Personalstelle, das Newsletter-Werkzeug im Marketing, die Videoanlage beim Hausmeister. Wer das zentral ausfüllen will, schreibt zwölf Mails und bekommt neun Antworten. Das Werkzeug muss deshalb als etwas funktionieren, das man verschickt: jeder Bereich meldet seine eigene Verarbeitung, speichert, schickt zurück.

## Was am Ende dastehen muss

Eine einzelne, in sich geschlossene HTML-Datei, per Doppelklick zu öffnen, ohne Server und ohne Installation. Die Datei ist zugleich die Datenbank: Speichern schreibt eine neue HTML-Datei mit den eingebetteten Datensätzen.

## Der Datensatz

Ein Datensatz ist **Verarbeitung**, mehrere sind **Verarbeitungen**.

**Felder**

| Schlüssel | Beschriftung | Typ | Näheres |
| --- | --- | --- | --- |
| `name` | Verarbeitungstätigkeit | text | Pflicht |
| `area` | Bereich | enum | einer von: Personal · Vertrieb · Marketing · IT · Buchhaltung · Empfang |
| `contact` | Ansprechperson | text | Tabellenkopf `Kontakt` |
| `purpose` | Zweck der Verarbeitung | text | mehrzeilig, Tabellenkopf `Zweck` |
| `subjects` | Betroffene | enum | einer von: Beschäftigte · Bewerberinnen und Bewerber · Kundinnen und Kunden · Interessenten · Lieferanten · Besucher |
| `categories` | Datenkategorien | text | mehrzeilig, Tabellenkopf `Daten` |
| `basis` | Rechtsgrundlage | enum | einer von: Vertrag (Art. 6 I b) · Rechtliche Pflicht (Art. 6 I c) · Berechtigtes Interesse (Art. 6 I f) · Einwilligung (Art. 6 I a) · noch zu klären, Tabellenkopf `Grundlage` |
| `system` | System / Ablage | text | Tabellenkopf `System` |
| `processor` | Auftragsverarbeiter | text | Tabellenkopf `AV` |
| `thirdCountry` | Drittlandtransfer | enum | einer von: nein · ja · unklar, Tabellenkopf `Drittland` |
| `retention` | Löschfrist | text | Tabellenkopf `Frist` |
| `risk` | Risiko | enum | einer von: gering · mittel · hoch |
| `status` | Status | enum | einer von: gemeldet · in Prüfung · freigegeben · nachzubessern |
| `reviewed` | Zuletzt geprüft | date | Tabellenkopf `Geprüft` |
| `note` | Notiz | text | mehrzeilig |
| `age` | Tage seit Prüfung | computed | berechnet, nie gespeichert, Tabellenkopf `Alter` |

**Darstellung**

- Führende Spalte: `name`
- Zweite Zeile darunter: `area`
- Tabellenspalten, in dieser Reihenfolge: `name`, `area`, `basis`, `subjects`, `retention`, `age`, `status`
- Filter in der Seitenleiste: `area`, `status`, `risk`
- Zählt nicht mehr als offen, wenn: `r.status === 'freigegeben'`
- Rot markiert, wenn: `!r.reviewed || r.reviewed < iso(-365)`

**Berechnete Felder**

Sie werden bei jeder Anzeige gerechnet und nie in den Datensatz geschrieben — eine gespeicherte Ableitung ist in dem Moment falsch, in dem sich eine ihrer Quellen ändert.

- `age` (Tage seit Prüfung):

  ```js
  const reviewed = localDateFromIso(r.reviewed)
  if (!reviewed) return ''
  /* Ganze lokale Tage über die Date.UTC-Differenz der Tageskompo-
     nenten - exakt ganzzahlig, ohne Rundungsrettung und ohne den
     Mix aus UTC-Mitternacht und Lokal-Mitternacht. */
  const now = new Date()
  return (
    (Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) -
      Date.UTC(reviewed.getFullYear(), reviewed.getMonth(), reviewed.getDate())) /
    86400000
  )
  ```

**Prüfregeln**

Bedingungen zwischen Feldern. Sie müssen an einer Stelle greifen, damit Formular, CSV-Import und die Vorschläge der KI durch dieselbe Prüfung laufen.

- **Wenn** `r.basis === 'Einwilligung (Art. 6 I a)'` → **Dann** `note`
  **Meldung:** „Bei einer Einwilligung gehört in die Notiz, wie und wo sie eingeholt wird.“
- **Wenn** `r.thirdCountry === 'ja'` → **Dann** `processor`
  **Meldung:** „Bei einem Drittlandtransfer muss stehen, wer dort verarbeitet.“
- **Wenn** `r.status === 'freigegeben'` → **Dann** `purpose`, `categories`, `retention`, `reviewed`
  **Meldung:** „Freigeben lässt sich nur, was Zweck, Datenkategorien, Löschfrist und Prüfdatum hat.“
- **Wenn** `r.risk === 'hoch'` → **Dann** `note`
  **Meldung:** „Ein hohes Risiko braucht eine Begründung — es kann eine Folgenabschätzung auslösen.“

## Dashboard

Kacheln über den gesamten Bestand, nicht über die gefilterte Ansicht.

- Eine Zahl: **Verarbeitungen** — der Anzahl · „im Verzeichnis“
- Eine Zahl: **Prüfung überfällig** — der Anzahl (nur Datensätze, die einem Filter entsprechen): `isOverdue(r)` · „nie oder vor über einem Jahr“
- Eine Zahl: **Offene Fragen** — der Anzahl (nur Datensätze, die einem Filter entsprechen): `r.basis === 'noch zu klären' || r.thirdCountry === 'unklar'` · „Rechtsgrundlage oder Drittland unklar“
- Ein Ring je Ausprägung von `status`
- Balken je Ausprägung von `area`, gemessen an der Anzahl — **Verarbeitungen je Bereich**
- Balken je Ausprägung von `risk`, gemessen an der Anzahl — **Verteilung nach Risiko**

## Geführte Erfassung

Eine kurze Schrittfolge für jemanden, der eine Sache melden soll und das Werkzeug nicht kennt. Geschrieben wird nichts, bevor der letzte Schritt bestätigt ist — ein Abbruch darf nichts hinterlassen.

Titel: **Eine Verarbeitung melden**

> Fünf kurze Schritte. Wenn Sie etwas nicht wissen, lassen Sie es leer — ein unvollständiger Eintrag ist besser als keiner. Gespeichert wird erst am Ende.

1. **Schritt 1** — Was und wer: Felder: `name`, `area`, `contact`
2. **Schritt 2** — Zweck und Daten: Felder: `purpose`, `subjects`, `categories`
3. **Schritt 3** — Rechtsgrundlage: Felder: `basis`, `note`
4. **Schritt 4** — Wo liegt es: Felder: `system`, `processor`, `thirdCountry`, `retention`
5. **Schritt 5** — Risiko: Felder: `risk`
   nur wenn: `drafts.records?.subjects === 'Beschäftigte' || drafts.records?.subjects === 'Bewerberinnen und Bewerber' || drafts.records?.thirdCountry === 'ja'`
6. **Schritt 6** — Prüfen: Zusammenfassung, aus dem Schema erzeugt

Abschluss: „Danke — das ist aufgenommen. Bitte die Datei speichern und zurücksenden.“

## Vorgaben

In `DEFAULT_SETTINGS`, `DEFAULT_COLORS` und `DEFAULT_HOME` in `src/app.jsx` setzen.

- Titel: **Verarbeitungsverzeichnis**
- Untertitel: Art. 30 DSGVO — gemeldet von den Fachbereichen
- Dateiname: `verarbeitungsverzeichnis`
- Version: `1.0`
- Oberflächensprache: `de`
- Öffnet als: geführte Erfassung (die Liste bleibt verborgen)
- Farben: `accent` #3a5a8c · `band` #1b2333 · `flag` #b0472e · `ok` #3a7a5e · `pending` #c08a12

## Startseite

Die Anwendung öffnet auf diesem Text. Er ist ein kleiner Markdown-Teilsatz — Überschriften, Listen, Zitate, fett, kursiv, Inline-Code und Verweise. Wörtlich übernehmen:

```markdown
# Verzeichnis von Verarbeitungstätigkeiten

Nach **Art. 30 DSGVO** muss fast jedes Unternehmen eines führen. Fast keines führt eines, das den
Namen verdient — und der Grund ist selten Unwille.

Es ist die Erhebung. Die Angaben liegen bei zwölf verschiedenen Leuten: die Bewerbungsmappen bei
der Personalstelle, das Newsletter-Werkzeug im Marketing, die Videoanlage beim Hausmeister. Wer das
zentral ausfüllen will, schreibt zwölf Mails und bekommt neun Antworten.

## Was diese Demo zeigt

Diese Datei ist im **Erfassungsmodus** ausgeliefert: sie öffnet direkt im Meldeformular und zeigt
die Liste gar nicht. Gedacht ist sie zum Weiterschicken — jeder Fachbereich meldet seine eigene
Verarbeitung, speichert und schickt zurück. Die Rückläufer führt man mit *Datei abgleichen*
zusammen.

Umschalten auf das vollständige Werkzeug: **Einstellungen → Anwendung → Öffnet als**.

> Erfundene Einträge, Veranschaulichung der Struktur. Keine Rechtsberatung.
```

## Beispieldaten

Lege 10 realistische Beispieldatensätze an, damit die Datei beim ersten Öffnen nicht leer ist. Erfinde sie im Stil der Felder oben; sie sind Anschauung, nicht die Daten des Nutzers. Sag ihm, dass seine eigenen Daten über **Import CSV → alle ersetzen** hineinkommen.

## Fertig, wenn

- `npm run build` eine einzelne, geschlossene `dist/index.html` erzeugt
- `npm test` durchläuft
- die Datei per Doppelklick öffnet und die Beispieldatensätze zeigt
- die berechneten Felder Werte zeigen und die Regeln einen verstoßenden Datensatz abweisen
- Einstellungen, Farben und Startseite der Vorgabe oben entsprechen

## Vor der Übergabe

Entscheide das selbst, statt es dem Empfänger zu überlassen: `copyright` auf den Eigentümer des Werkzeugs setzen, den Kopfzeilen-Verweis auf das openToolbox-Repository ersetzen und `examplePrompts` abschalten, wenn der Empfänger nur Daten pflegt. Den Aufrufzähler (Einstellungen → Sicherheit) bei der Übergabe ansprechen, statt ihn später in einem Netzwerkprotokoll entdecken zu lassen.

---

*Erzeugt aus `examples/gdpr-processing.domain.js` — dem laufenden Quelltext der [Live-Demo](https://m-dohmen.github.io/openToolbox/demos/gdpr-processing/). Neu erzeugen mit `npm run prompts`.*

*Alle Daten der Demo sind erfunden. Sie veranschaulicht die Struktur eines solchen Werkzeugs — sie ist keine Rechtsberatung und kein Nachweis von Konformität.*
