ZWEI VORGÄNGE, ZWEI ROUTEN. Die AUSKUNFT (`GET …/export`) stellt alles zusammen, was der Betrieb über eine Person gespeichert hat: Stammdaten, Einwilligungen, Reservierungen, Wartelisteneinträge und die Metadaten der KI-Sitzungen. Das LÖSCHBEGEHREN (`POST …/anonymize`) leert den Personenbezug überall dort — unumkehrbar.
DIE AUSKUNFT IST DIE DICHTESTE ZUSAMMENSTELLUNG PERSONENBEZOGENER DATEN, die dieses Haus erzeugen kann. Sie verlangt deshalb `guests:read` UND die Handlung `action:guests.personal` (sie enthält Notizen, Sperrgrund und No-Show-Zähler — ohne die Handlung wäre sie der Umgehungsweg um genau diese Schranke) und zieht vom Stundenkontingent des Schlüssels ab.
WAS NICHT DRINSTEHT: die Nachrichteninhalte der KI-Sitzungen. Sie enthalten regelmässig Aussagen über DRITTE („meine Frau isst kein Gluten") — die Auskunft an eine Person darf keine Auskunft über eine andere sein. Deshalb nur die Metadaten der Sitzungen, genau wie im Dashboard-Export.
`complete` SAGT, OB DIE AUSKUNFT VOLLSTÄNDIG IST. Je Teilliste gibt es eine Obergrenze (1 000 Reservierungen, 500 Wartelisteneinträge, 500 KI-Sitzungen). Wird eine erreicht, steht `complete: false` — und dann ist die Auskunft ausdrücklich unvollständig und darf nicht als vollständig weitergegeben werden.
ANONYMISIERT WIRD, NICHT GELÖSCHT, und das ist die wichtigste Entscheidung dieses Bereichs. Der Weg im Dashboard löscht die Gastzeile; weil die Verknüpfungen auf „leeren" stehen, verlieren damit ALLE Reservierungen ihre Zuordnung. Berichte, Auslastungsstatistik und Besuchszählung bekommen Löcher, die niemand mehr schliesst. Hier bleibt die Zeile stehen und wird GELEERT: der Personenbezug ist weg, die Zählung bleibt.
DREI SCHRANKEN, WEIL ES KEIN ZURÜCK GIBT. Erstens `guests:write` UND `action:guests.personal`. Zweitens das ausgeschriebene Wort `{"confirm":"anonymisieren"}` im Rumpf — eine Adresse allein löst nichts aus, damit ein Vorschauabruf, ein verirrter Wiederholversuch oder eine falsch gebaute Schleife ins Leere treffen. Drittens ist `Idempotency-Key` PFLICHT: die Wiederholung nach einem Netzabbruch darf nicht die zweite Hälfte einer halben Löschung sein — und die Idempotenzzeile ist derzeit der einzige dauerhafte Nachweis, DASS gelöscht wurde. Dazu ein eigenes Stundenkontingent von zwanzig Vorgängen: mehr sind kein Betrieb, sondern ein Zwischenfall.
WAS DIE ANONYMISIERUNG ANFASST: die Gastzeile (Name, Mail, Telefon, Firma, Notizen, Merkmale, Sperre, Einwilligungen, Newsletter-Token), die Reservierungen (Name, Mail, Telefon, Gastwunsch; `anonymizedAt` wird gesetzt), die Wartelisteneinträge, die Notizen der Tischbelegungen und die KI-Sitzungen samt ihrer Nachrichteninhalte. Der Vorgang schreibt über Modulgrenzen hinweg, OHNE die Rechte dieser Module zu verlangen — das ist bewusst: eine „Löschung", die Name und Mailadresse an jeder Buchung stehen lässt, meldet Erfolg und hat nichts gelöscht. Er kann dabei in keiner Richtung etwas offenlegen oder anlegen; er entfernt ausschliesslich.
ZÄHLER UND DURCHSCHNITTE BLEIBEN STEHEN. `totalVisits`, `cancelCount`, `noShowCount` und `averagePartySize` sagen nach der Anonymisierung nichts mehr über einen Menschen aus, tragen aber die Auslastungsrechnung des Hauses. Sie zu nullen hiesse, die Statistik rückwirkend zu fälschen — und das verlangt Art. 17 nicht.
EIN ZWEITES LÖSCHBEGEHREN IST EIN ERFOLG, KEIN KONFLIKT. Ist die Zeile bereits geleert, antwortet die Route mit 200 und `anonymized.alreadyAnonymized: true`. Ein zweites Ticket zum selben Menschen darf nicht wie ein Fehler aussehen.