ADR-0069: Organisation und Projekt als getrennte Rollen-Ebenen
- Status: accepted
- Datum: 2026-10-09
- Phase: Multi-Tenant / Prozessmanagement
- Bezug: ADR-0032, ADR-0052, ADR-0053, ADR-0060, ADR-0061, ADR-0068
- Ersetzt: die Projektrollen aus ADR-0052 (
owner/editor/reader, Übernahme von Altprojekten) sowieproject_contactsund die org-weite Kommunikationsrolle aus ADR-0060 / ADR-0061
Kontext
Rechte und Kommunikation hingen an drei unabhängigen Stellen: Projektmitgliedschaft mit owner / editor / reader für Web-Konten (ADR-0052), Feldkontakte in project_contacts für Personen ohne Konto (ADR-0060) und eine org-weite Kommunikationsrolle persons.role_name (field / office / leadership). Wer „Leitung“ war, bekam Alarme aller Projekte; ein Feldmitarbeiter in zwei Projekten schrieb in die Unterhaltung, in die zuletzt geschrieben wurde. Mitarbeiter ohne Web-Login (nur Telegram/WhatsApp) konnten nicht Projektmitglied sein, Web-Konten nicht Feldkontakt. Die Frage „wer darf in diesem Projekt was, und wer bekommt welche Nachricht?“ hatte keine einzige Antwort.
Entscheidung
- Zwei Ebenen.
- Organisation (= Mandant): Mitarbeiter sind
app_users, mit oder ohne Web-Login. Ohne Login ist z. B. der Feldmitarbeiter, der nur über Telegram oder WhatsApp erreichbar ist. Die Org-Rollen bleiben: Admin (org_admin), Staff (staff), Gast (customer) (ADR-0053). - Projekt: Mitglieder sind Mitarbeiter der Organisation mit der Projektrolle Leitung (
lead), Büro (office) oder Feld (field).
- Organisation (= Mandant): Mitarbeiter sind
- Tabelle
project_members(project_id, user_id, person_id, role),user_idundperson_idbeideNOT NULL, eindeutig je(project_id, person_id)und(project_id, user_id). Mehrere Leitungen sind erlaubt. Wer ein Projekt anlegt, wird Leitung. Die letzte Leitung kann nur ein Org-Admin entfernen oder herabstufen. -
Rechte = Schnittmenge aus Org-Recht und Projektrolle (
app/projects/access.py):Recht Leitung Büro Feld Projekt sehen ✓ ✓ ✓ Mitglieder verwalten, Projekteinstellungen (Briefing-Zeit, assistant_policy), Projekt löschen✓ – – GIS/Datensätze bearbeiten (QGIS edit), Freigaben bearbeiten, Aufgaben zuweisen, Projekt-Dokumente ✓ ✓ – Leitungs-Alarm bei Stillstand ✓ – – Statusabfrage per Messenger ✓ ✓ – Briefings und Aufgaben per Messenger, Rückmeldungen – – ✓ Ein Gast (ohne Org-Recht
edit_records) bleibt unabhängig von der Projektrolle nur lesend. Der Org-Admin sieht und verwaltet jedes Projekt ohne Mitgliedschaft (er handelt als Leitung), ist aber nur Empfänger von Nachrichten, wenn er Mitglied ist. Staff und Gast sehen nur Projekte, in denen sie Mitglied sind. Das Übernehmen von Projekten ohne Mitglieder („unclaimed“) und der Kundenfilter über die Stammdaten-Organisation entfallen. 4. Stammdaten bleiben Stammdaten. Die Stammdaten-Organisationen (Unternehmen, Gemeinden inorganizations),projects.project_managerundmetadata_json.actor_assignmentsbeschreiben externe Beteiligte und vergeben keine Rechte (ADR-0032). 5. Entfernt: Tabelleproject_contactsund die Endpunkte/projects/{id}/contacts; die org-weite Kommunikationsrolle (persons.role_name=field/office/leadership,communication_rolean/users). Die Mitarbeiterliste zeigt stattdessenproject_count. 6. Kommunikation je Projekt (app/messaging/context.py,app/assistant/inbound.py). Ein Telegram-Bot bzw. eine WhatsApp-Nummer je Mandant, Identitäten je Person. Eine eingehende Nachricht wird zuerst vorläufig abgelegt (WhatsApp jetzt nach Person gefiltert), dann wird das Projekt in dieser Reihenfolge bestimmt: 1. Antwort auf eine offene Frage „Zu welchem Projekt gehört Deine Nachricht?“ (Nummer, Name oder Projektcode); 2. Projektname oder Projektcode im Text; 3. das einzige aktive Projekt des Absenders; 4. das Projekt, in das Linetra innerhalb von 12 Stunden geschrieben hat (REPLY_WINDOW, z. B. ein Briefing); 5. sonst schickt Linetra eine nummerierte Liste; die Nachricht wartet in einer Unterhaltung ohne Projekt (Zustandawaiting_project) und wird verarbeitet, sobald der Absender gewählt hat.Ist der Absender nur Leitung oder Büro, bekommt er die Statusantwort seiner Projekte. Feld-Nachrichten laufen im aufgelösten Projekt durch den Feld-Assistenten (ADR-0061); ist der Absender im aufgelösten Projekt Leitung oder Büro, gibt es eine Statusantwort. Absender ohne Mitgliedschaft bekommen „Du bist noch in keinem Projekt eingetragen …“. Unterhaltungen ohne Projekt gibt es nur noch für die Projektfrage. 7. Briefings gehen je Projekt an die Mitglieder mit Rolle Feld und nennen das Projekt. Der Leitungs-Alarm geht an die Leitungs-Mitglieder des betroffenen Projekts, in die Unterhaltung dieses Projekts. Folgeaufgaben (Chef/Büro) bleiben ohne Zuständigen. 8. Betrieb arbeitet je Projekt.
GET /api/v1/operations/inbox,/inbox/messages,/review,/agent-runsund/reportsverlangenproject_idund prüfen den Zugriff;/operations/status,/tasks/summaryund/alerts/summary(project_idoptional) umfassen nur zugängliche Projekte. Die Aufgabenliste liefertassignee_nameundassignee_is_member. 9. Migration (Major-Release): Tenant0038_project_roles(owner→lead,editor→office,reader→field;project_contacts→ Mitglieder; Personen mitrole_name = 'leadership'→ Leitung in allen aktiven Projekten; leertrole_namefürfield/office/leadership), danachpython -m app.projects.backfill_members(verknüpft jede Mitgliedschaft mit Mitarbeiter und Person, legt für Kontakte Staff-Mitarbeiter ohne Login an, verwirft Mitgliedschaften mandantenfremder Konten wie Systemadmins; idempotent), danach0039_project_members_not_null(NOT NULL, löschtproject_contacts; bricht laut ab, wenn unverknüpfte Zeilen übrig sind). Control0050_project_member_rolesspiegelt das für die gemeinsame pytest-DB../scripts/alembic_upgrade_all.shführt alle drei Schritte aus.
Begründung
Eine Organisation hat Mitarbeiter, ein Projekt hat Mitglieder mit einer Aufgabe darin. Wer in Projekt A Leitung und in Projekt B Feld ist, lässt sich mit einer org-weiten Rolle nicht abbilden. Die Projektrolle beschreibt die Aufgabe im Projekt, die Org-Rolle bleibt die Obergrenze; die Schnittmenge verhindert, dass eine Projektrolle einem Gast Schreibrechte gibt. Weil jedes Mitglied ein Mitarbeiter ist, gelten Rechte, Telegram-Verknüpfung und Briefings für Personen mit und ohne Login gleich. Die Projektauflösung je Nachricht ersetzt das Raten über „die letzte Unterhaltung“: Eindeutige Fälle laufen ohne Rückfrage, mehrdeutige werden gefragt statt falsch zugeordnet.
Verworfene Alternativen
| Alternative | Warum verworfen |
|---|---|
| Kommunikationsrolle org-weit behalten, Projektrollen nur für Rechte | Zwei Rollenmodelle für dieselbe Frage; Leitung bekäme weiter Alarme fremder Projekte |
project_contacts neben project_members behalten |
Zwei Listen je Projekt, Konten ohne Login nicht als Mitglied abbildbar |
| Bot bzw. Nummer je Projekt | Kosten und Verwaltung; der Mitarbeiter müsste wissen, wem er schreibt (ADR-0060) |
| Projekt immer per LLM aus der Nachricht bestimmen | Nicht deterministisch; die Projektauflösung bleibt Code (ADR-0061) |
Stammdaten-Projektleiter (project_manager) als Leitung |
Personen in Stammdaten sind externe Beteiligte, keine Mitarbeiter (ADR-0032) |
Folgen
- Breaking: Projektrollen heißen
lead/office/field;/projects/{id}/contactsundcommunication_rolesind entfernt; die Betrieb-Listen verlangenproject_id.my_rolean/projectsliefert die neuen Werte. - Die Oberfläche für Mitglieder liegt unter Stammdaten → Projekte → Projekt und Kontomenü → Projekte → Projekt: jeder Mitarbeiter (auch ohne Web-Login) wählbar, Rolle je Mitglied, Spalten Telefon und Messenger.
- Betrieb (Inbox, Freigaben, Berichte, Agent-Log) zeigt das in der Kopfzeile gewählte Projekt; ohne Auswahl erscheint ein Hinweis.
- Ein Absender in mehreren Projekten bekommt gelegentlich die Projektfrage; eine Antwort mit Nummer, Name oder Code genügt.
- Org-Admins ohne Mitgliedschaft bekommen keine Alarme und keine Briefings; wer benachrichtigt werden soll, muss Mitglied sein.
- Postgres-Grants bleiben tenant-weit wie in ADR-0052; die Projektgrenze setzen API und Plugin.