Eine Person, die nicht aus dem Verzeichnis kommt, konnte keinen Vorgesetzten haben. {{vorgesetzter_sam}} blieb dort immer leer — genau im Onboarding-Befehl, wo er gebraucht wird.
Die Entscheidung
Das Issue stellte zwei Wege zur Wahl. Gewählt: eine eigene Selbstbeziehung vorgesetzterId — und die befürchteten „zwei Wege zur selben Auskunft" entstehen nicht, weil der Abgleich sie mitschreibt. vorgesetzterExterneId ist damit wie abteilung nur noch der rohe Wert des Verzeichnisses; gelesen wird allein vorgesetzterId, an beiden Stellen.
Die Alternative hätte manuellen Personen eine erfundene externeId geben müssen. externeId heißt „objectGUID aus dem Verzeichnis" — das wäre eine Lüge im Datenmodell gewesen, und @@unique([quelleId, externeId]) hätte danach auch manuelle Zeilen eingeschränkt.
Drei Grenzen an drei Stellen
Mandantengrenze: zusammengesetzter Fremdschlüssel (vorgesetzterId, kundeId) → (id, kundeId), wie bei Lauf und Geraet. Der Dienst prüft dasselbe noch einmal, nur mit besserer Meldung.
Kürzeste Schleife: ein CHECK vorgesetzterId <> id. Niemand ist sein eigener Vorgesetzter.
Längere Schleifen: der Dienst läuft die Kette hinauf, mit Obergrenze — wäre die Kette schon beschädigt, liefe die Suche sonst ewig.
Was das Verzeichnis darf und was nicht
Liefert es einen manager, bestimmt es — beide Felder. Liefert es keinen, bleibt eine von Hand gesetzte Zuordnung stehen: geleert wird nur, was das Verzeichnis selbst gesetzt hatte (erkennbar daran, dass dort eine Kennung stand). Gegenprobe gefahren: ohne diese Einschränkung wird der Test rot.
Der Umstiegspfad
Die Migration führt bestehende Daten nach. Ohne sie wäre die Auskunft nach dem Einspielen bei jeder Verzeichnis-Person leer gewesen, bis der nächste Abgleich läuft — und zwar still. Dieselbe Falle wie der Seed-Schlüssel in #32: ein Umstieg, der die vorhandenen Daten nicht mitnimmt. An einem nachgebauten Vor-Zustand in der Entwicklungsdatenbank gefahren: m-probe trug danach die Zuordnung auf Sonja Brand.
Seed
Petra Klein ist jetzt Vorgesetzte von Maria Huber — die Zuordnung aus dem Auftrag, die sich vorher nirgends speichern ließ. Gesetzt wird sie nur für Personen, die derselbe Lauf angelegt hat: eine leere Zuordnung sagt nicht, ob sie noch nie gesetzt oder im Betrieb entfernt wurde, und ein zweiter Seed-Lauf darf nichts zurückdrehen.
Oberfläche
Auswahlfeld und Knopf auf der Personenseite, kein eigener Bearbeiten-Modus. Die leere Option heißt ausdrücklich „niemand" und nicht „—": sie ist eine Angabe. Angeboten werden nur aktive Personen desselben Kunden, ohne die Person selbst — eine Auswahlliste mit unmöglichen Einträgen ist eine Falle.
Im gebauten Stand durchgeklickt, nicht nur gebaut: anmelden, Person öffnen, speichern, neu laden (Wert steht), Kopfzeile zeigt den Namen, eigene Id wird nicht angeboten, entfernen geht, und bei 390 px kein horizontales Scrollen (scrollWidth 390 = clientWidth 390). Das Protokoll trug danach beide Aktionen.
Tests
Dreizehn neue (1156 statt 1143), darunter alle drei Kriterien des Issues, beide Schleifenlängen, die Mandantengrenze auf Dienst- und Datenbankebene, und der Seed-Fall in beide Richtungen.
Alle Tore grün: lint, typecheck, format:pruefen, pruefe:doku, 1156 Vitest-Tests in 52 Dateien.
Eine Person, die nicht aus dem Verzeichnis kommt, konnte keinen Vorgesetzten haben. `{{vorgesetzter_sam}}` blieb dort immer leer — genau im Onboarding-Befehl, wo er gebraucht wird.
## Die Entscheidung
Das Issue stellte zwei Wege zur Wahl. Gewählt: **eine eigene Selbstbeziehung `vorgesetzterId`** — und die befürchteten „zwei Wege zur selben Auskunft" entstehen nicht, weil **der Abgleich sie mitschreibt**. `vorgesetzterExterneId` ist damit wie `abteilung` nur noch der rohe Wert des Verzeichnisses; gelesen wird allein `vorgesetzterId`, an beiden Stellen.
Die Alternative hätte manuellen Personen eine erfundene `externeId` geben müssen. `externeId` heißt „objectGUID aus dem Verzeichnis" — das wäre eine Lüge im Datenmodell gewesen, und `@@unique([quelleId, externeId])` hätte danach auch manuelle Zeilen eingeschränkt.
## Drei Grenzen an drei Stellen
- **Mandantengrenze:** zusammengesetzter Fremdschlüssel `(vorgesetzterId, kundeId) → (id, kundeId)`, wie bei `Lauf` und `Geraet`. Der Dienst prüft dasselbe noch einmal, nur mit besserer Meldung.
- **Kürzeste Schleife:** ein CHECK `vorgesetzterId <> id`. Niemand ist sein eigener Vorgesetzter.
- **Längere Schleifen:** der Dienst läuft die Kette hinauf, mit Obergrenze — wäre die Kette schon beschädigt, liefe die Suche sonst ewig.
## Was das Verzeichnis darf und was nicht
Liefert es einen `manager`, bestimmt es — beide Felder. Liefert es **keinen**, bleibt eine von Hand gesetzte Zuordnung stehen: geleert wird nur, was das Verzeichnis selbst gesetzt hatte (erkennbar daran, dass dort eine Kennung stand). **Gegenprobe gefahren:** ohne diese Einschränkung wird der Test rot.
## Der Umstiegspfad
**Die Migration führt bestehende Daten nach.** Ohne sie wäre die Auskunft nach dem Einspielen bei *jeder* Verzeichnis-Person leer gewesen, bis der nächste Abgleich läuft — und zwar still. Dieselbe Falle wie der Seed-Schlüssel in #32: ein Umstieg, der die vorhandenen Daten nicht mitnimmt. An einem nachgebauten Vor-Zustand in der Entwicklungsdatenbank gefahren: `m-probe` trug danach die Zuordnung auf Sonja Brand.
## Seed
Petra Klein ist jetzt Vorgesetzte von Maria Huber — die Zuordnung aus dem Auftrag, die sich vorher nirgends speichern ließ. Gesetzt wird sie nur für Personen, die **derselbe Lauf** angelegt hat: eine leere Zuordnung sagt nicht, ob sie noch nie gesetzt oder im Betrieb entfernt wurde, und ein zweiter Seed-Lauf darf nichts zurückdrehen.
## Oberfläche
Auswahlfeld und Knopf auf der Personenseite, kein eigener Bearbeiten-Modus. Die leere Option heißt ausdrücklich **„niemand"** und nicht „—": sie ist eine Angabe. Angeboten werden nur aktive Personen desselben Kunden, ohne die Person selbst — eine Auswahlliste mit unmöglichen Einträgen ist eine Falle.
**Im gebauten Stand durchgeklickt**, nicht nur gebaut: anmelden, Person öffnen, speichern, **neu laden** (Wert steht), Kopfzeile zeigt den Namen, eigene Id wird nicht angeboten, entfernen geht, und bei 390 px kein horizontales Scrollen (`scrollWidth 390 = clientWidth 390`). Das Protokoll trug danach beide Aktionen.
## Tests
Dreizehn neue (1156 statt 1143), darunter alle drei Kriterien des Issues, beide Schleifenlängen, die Mandantengrenze auf Dienst- **und** Datenbankebene, und der Seed-Fall in beide Richtungen.
Alle Tore grün: `lint`, `typecheck`, `format:pruefen`, `pruefe:doku`, **1156 Vitest-Tests** in 52 Dateien.
Closes #31
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Die Zuordnung lief allein ueber vorgesetzterExterneId, und die ist nur
innerhalb einer Verzeichnisquelle eindeutig: eine von Hand angelegte Person
konnte keinen Vorgesetzten haben, und {{vorgesetzter_sam}} blieb im
Onboarding-Befehl leer -- genau dort, wo er gebraucht wird.
Entschieden fuer eine eigene Selbstbeziehung vorgesetzterId. Die befuerchteten
"zwei Wege zur selben Auskunft" entstehen nicht, weil der Abgleich sie
mitschreibt: vorgesetzterExterneId ist jetzt wie abteilung nur der rohe Wert
des Verzeichnisses, gelesen wird allein vorgesetzterId. Die Alternative haette
manuellen Personen eine erfundene externeId geben muessen, und externeId heisst
"objectGUID aus dem Verzeichnis".
Liefert das Verzeichnis keinen manager, bleibt eine von Hand gesetzte Zuordnung
stehen -- geleert wird nur, was das Verzeichnis selbst gesetzt hatte.
Die Migration fuehrt bestehende Daten nach. Ohne das waere die Auskunft nach
dem Einspielen bei jeder Verzeichnis-Person still leer gewesen, bis der
naechste Abgleich laeuft. An einem nachgebauten Vor-Zustand gefahren.
Drei Grenzen, drei Stellen: der zusammengesetzte Fremdschluessel haelt die
Mandantengrenze, ein CHECK die kuerzeste Schleife, und der Dienst laeuft die
Kette hinauf fuer laengere.
Im gebauten Stand durchgeklickt: speichern, neu laden, Wert steht, entfernen,
390 px ohne horizontales Scrollen. Gegenprobe am Abgleich gefahren.
Closes#31
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Eine Person, die nicht aus dem Verzeichnis kommt, konnte keinen Vorgesetzten haben.
{{vorgesetzter_sam}}blieb dort immer leer — genau im Onboarding-Befehl, wo er gebraucht wird.Die Entscheidung
Das Issue stellte zwei Wege zur Wahl. Gewählt: eine eigene Selbstbeziehung
vorgesetzterId— und die befürchteten „zwei Wege zur selben Auskunft" entstehen nicht, weil der Abgleich sie mitschreibt.vorgesetzterExterneIdist damit wieabteilungnur noch der rohe Wert des Verzeichnisses; gelesen wird alleinvorgesetzterId, an beiden Stellen.Die Alternative hätte manuellen Personen eine erfundene
externeIdgeben müssen.externeIdheißt „objectGUID aus dem Verzeichnis" — das wäre eine Lüge im Datenmodell gewesen, und@@unique([quelleId, externeId])hätte danach auch manuelle Zeilen eingeschränkt.Drei Grenzen an drei Stellen
(vorgesetzterId, kundeId) → (id, kundeId), wie beiLaufundGeraet. Der Dienst prüft dasselbe noch einmal, nur mit besserer Meldung.vorgesetzterId <> id. Niemand ist sein eigener Vorgesetzter.Was das Verzeichnis darf und was nicht
Liefert es einen
manager, bestimmt es — beide Felder. Liefert es keinen, bleibt eine von Hand gesetzte Zuordnung stehen: geleert wird nur, was das Verzeichnis selbst gesetzt hatte (erkennbar daran, dass dort eine Kennung stand). Gegenprobe gefahren: ohne diese Einschränkung wird der Test rot.Der Umstiegspfad
Die Migration führt bestehende Daten nach. Ohne sie wäre die Auskunft nach dem Einspielen bei jeder Verzeichnis-Person leer gewesen, bis der nächste Abgleich läuft — und zwar still. Dieselbe Falle wie der Seed-Schlüssel in #32: ein Umstieg, der die vorhandenen Daten nicht mitnimmt. An einem nachgebauten Vor-Zustand in der Entwicklungsdatenbank gefahren:
m-probetrug danach die Zuordnung auf Sonja Brand.Seed
Petra Klein ist jetzt Vorgesetzte von Maria Huber — die Zuordnung aus dem Auftrag, die sich vorher nirgends speichern ließ. Gesetzt wird sie nur für Personen, die derselbe Lauf angelegt hat: eine leere Zuordnung sagt nicht, ob sie noch nie gesetzt oder im Betrieb entfernt wurde, und ein zweiter Seed-Lauf darf nichts zurückdrehen.
Oberfläche
Auswahlfeld und Knopf auf der Personenseite, kein eigener Bearbeiten-Modus. Die leere Option heißt ausdrücklich „niemand" und nicht „—": sie ist eine Angabe. Angeboten werden nur aktive Personen desselben Kunden, ohne die Person selbst — eine Auswahlliste mit unmöglichen Einträgen ist eine Falle.
Im gebauten Stand durchgeklickt, nicht nur gebaut: anmelden, Person öffnen, speichern, neu laden (Wert steht), Kopfzeile zeigt den Namen, eigene Id wird nicht angeboten, entfernen geht, und bei 390 px kein horizontales Scrollen (
scrollWidth 390 = clientWidth 390). Das Protokoll trug danach beide Aktionen.Tests
Dreizehn neue (1156 statt 1143), darunter alle drei Kriterien des Issues, beide Schleifenlängen, die Mandantengrenze auf Dienst- und Datenbankebene, und der Seed-Fall in beide Richtungen.
Alle Tore grün:
lint,typecheck,format:pruefen,pruefe:doku, 1156 Vitest-Tests in 52 Dateien.Closes #31
🤖 Generated with Claude Code
Die Zuordnung lief allein ueber vorgesetzterExterneId, und die ist nur innerhalb einer Verzeichnisquelle eindeutig: eine von Hand angelegte Person konnte keinen Vorgesetzten haben, und {{vorgesetzter_sam}} blieb im Onboarding-Befehl leer -- genau dort, wo er gebraucht wird. Entschieden fuer eine eigene Selbstbeziehung vorgesetzterId. Die befuerchteten "zwei Wege zur selben Auskunft" entstehen nicht, weil der Abgleich sie mitschreibt: vorgesetzterExterneId ist jetzt wie abteilung nur der rohe Wert des Verzeichnisses, gelesen wird allein vorgesetzterId. Die Alternative haette manuellen Personen eine erfundene externeId geben muessen, und externeId heisst "objectGUID aus dem Verzeichnis". Liefert das Verzeichnis keinen manager, bleibt eine von Hand gesetzte Zuordnung stehen -- geleert wird nur, was das Verzeichnis selbst gesetzt hatte. Die Migration fuehrt bestehende Daten nach. Ohne das waere die Auskunft nach dem Einspielen bei jeder Verzeichnis-Person still leer gewesen, bis der naechste Abgleich laeuft. An einem nachgebauten Vor-Zustand gefahren. Drei Grenzen, drei Stellen: der zusammengesetzte Fremdschluessel haelt die Mandantengrenze, ein CHECK die kuerzeste Schleife, und der Dienst laeuft die Kette hinauf fuer laengere. Im gebauten Stand durchgeklickt: speichern, neu laden, Wert steht, entfernen, 390 px ohne horizontales Scrollen. Gegenprobe am Abgleich gefahren. Closes #31