Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/docs/ARCHITECTURE-DECISIONS.md
T
2026-07-08 16:15:37 +02:00

42 KiB
Raw Blame History

HiveGrid 2.0 — Architektur-Entscheidungen

Entscheidungen aus dem Boilerplate-Abgleich, den späteren HiveGrid-Pivots und der Bespoke-Planung. Diese ADR-Liste ergänzt AGENTS.md und ist verbindlich für die Umsetzung. Die historischen Boilerplate-Mapping-Ordner wurden im Bespoke-Cleanup entfernt; aktuelle Quellen sind .planning/ENTITY-MODEL.md, .planning/PHASE-SOURCES.md, die Ticket-Docs und docs/milestones/. Legende: ✅ entschieden · 🅿️ Backlog (Entscheidung später).


AD-1 — Mandanten-Trennung: echtes Postgres-RLS ✅

Entscheidung: Row-Level-Security in Postgres als bauliche Vorgabe (nicht nur App-Level-Scoping).

  • RLS-Policies (USING/WITH CHECK) als Forward-Migration mit Inverse-SQL-Kommentar
  • Request-Middleware setzt SET LOCAL app.tenant_id
  • App-DB-Rolle ohne BYPASSRLS
  • Worker-/Job-Tenant-Context-Wrapper
  • Cache-Präfix tenant:{id}:, S3-Pfad tenants/{id}/
  • Ausnahmen ohne RLS: users, subscription_*, feature_flags, Branchen-Templates

Warum: Spec A.4 + M019-Erfolgskriterium (Pen-Test). Defense-in-depth — ein vergessener Query-Filter kann keine Fremddaten mehr leaken. Boilerplate hat nur App-Level-Scoping. Betrifft: M019 (+ jedes tenant-scoped Modul).

AD-2 — Daten-Modell: typisierte Tabellen, Entity-Builder raus ✅

Entscheidung: HiveGrid-Fachentitäten (CRM, Deals, Aufträge, Tasks, …) als dedizierte, typisierte First-Class-Prisma-Tabellen. Das Entity-Builder-CRM der Boilerplate (Entity+Row+RowValue, apps/web/app/modules/crm) wird entfernt, nicht angepasst. Custom-Felder über purpose-built CustomFieldDefinition + CustomFieldValue (JSONB-Wert), nicht über die polymorphe Row-Engine.

Warum: Spec M01 verlangt typisierte Tabellen mit Constraints, Indizes (pg_trgm, tenant_id, archived_at, last_activity_at), ContactEmail/ContactPhone, M:N-Relationen, zentrale Activity-Tabelle. Polymorphes RowValue ist damit unvereinbar (Performance bei 10k+, Indizes, Permissions, RLS). Zwei Architekturen parallel = Fremd-Architektur sickert ein. Betrifft: M01, M02, M03, M04, M26 (+ alle Fachmodule).

🔄 REVIDIERT 2026-06-13 (Modell-Pivot, siehe AD-20): Deal + Project verschmelzen zu EINER typisierten Projekt-Tabelle (Superset der Felder) + CustomFieldDefinition/Value + einem neuen Pipeline-Feld-Config-Layer (welche Felder pro Pipeline+Status sichtbar/pflichtig sind; nicht sichtbare Felder bleiben im Hintergrund erhalten). Die Konfigurierbarkeit ist Metadaten über typisierten Spalten — der generische Entity-Builder (Entity/Row/RowValue) bleibt entfernt. AD-2-Prinzip („typisiert, kein Generikum") bleibt gewahrt. Anker: .planning/ENTITY-MODEL.md.

🔴 PRÄZISIERUNG & VERBINDLICHE REGEL 2026-06-29 (R-P6 / R-P6B): Das „✅" oben gilt bislang nur für den CRM-Pfad (R-P6 typisierte Kontakte + R-P6B-α + R-P6C). Der generische Entity-Builder läuft noch und wird weiter genutzt von EmailMarketing, Form-Submission (apps/worker/apps/api), WorkflowEngine, formulas und ~49 EntitiesSingleton-Konsumenten (Konsumenten-Matrix + Reihenfolge/Ownership: .planning/ticket-docs/R-P6B.md).

Regel (ab jetzt verbindlich): KEINE neuen Entity-Builder-Konsumenten. Jedes neue Fachmodul/Feature nutzt typisierte Prisma-Tabellen (wie CRM), niemals Entity/Row/RowValue, modules/rows, utils/db/entities, EntitiesSingleton, die *Api.server-Entity-Layer oder den CrmService-Legacy. Ein PR, der einen neuen Builder-Konsumenten einführt, wird abgelehnt (wie ein Anti-Listen-Verstoß).

Ablöse-Pflicht: Der generische Entity-Builder MUSS vollständig abgelöst werden (R-P6B-β; kein „optional"). Vorgehen organisch: jedes Builder-nutzende Modul wird typisiert, sobald es ohnehin angefasst wird (Migration durch Neubau < Refactor); neue Module sowieso typisiert. Ist der letzte Konsument weg, fällt entityBuilder.prisma (+ Tenant-Daten-Entscheidung). Spätlimit: vor produktivem Masseneinsatz von Row-Daten (sonst wird die Schema-Löschung zur Daten-Migration).

AD-3 — Plattform-Admin-Identität: getrennt, Email+PW+TOTP, optional Authentik-SSO ✅

Entscheidung: Vollständig getrennte Admin-Welt — eigene AdminUser-Tabelle (eigenes passwordHash/totpSecret/status), eigene AdminSession (server-side, IP-gebunden), eigene AdminRole/AdminPermission-RBAC. Primärer Login: Email + Passwort + TOTP (Magic-Link verboten). Zusätzlich optionaler Authentik-OIDC/SSO-Login, gemappt auf dieselbe getrennte Admin-Identität. Kein Generalschlüssel zwischen Plattform und Mandanten.

  • AdminAuditLog braucht kein neues Schema: vorhandener AuditEvent (Hash-Kette, tenantId=null = plattformweit) deckt das ab.
  • Boilerplate-Admin-Auth (Login-Flow, MFA-Step-up-Gate, Rate-Limit, Session-Cookie-Policy) dient als Vorlage, nicht 1:1 (BP hat AdminUser nur als User-Extension).

Warum: Spec 37.1 / M034 verbietet den geteilten Identity-/RBAC-Pfad explizit. Betrifft: M034–M041 (M37).

AD-4 — Sessions: Postgres als Source-of-Truth + Redis-Cache ✅

Entscheidung: Server-side Session-Tabelle in Postgres (Geräte-Liste mit Browser/IP/ Standort/letzter Aktivität, Revoke, „von allen Geräten abmelden", 4h-Inaktivität, 30-Tage-Sliding-Window, Auto-Logout bei Passwort-/2FA-/Email-Änderung) als Quelle der Wahrheit; Redis-Cache für schnelle Pro-Request-Validierung. Cookie hält nur die Session-ID-Referenz.

Warum: Spec B.5 braucht dauerhafte, abfragbare Geräte-Liste + verlässliches Revoke-all; reine Cookies (BP-Default) können nicht serverseitig widerrufen werden; reines Redis verliert Geräte-Historie bei Flush. Betrifft: M021 (Mandanten-User), M034 (AdminSession), M141 (Customer-Portal).

AD-5 — Such-Engine: Meilisearch (self-hosted) + pgvector-Hybrid ✅

Entscheidung: Keyword-Suche über das selbst gehostete Meilisearch (BP-@hivegrid/search weitgehend 1:1), semantische Suche über pgvector + EU-Embeddings (Scaleway 1024-dim, BP 1:1), kombiniert via RRF-Fusion. Embedding-Pipeline + Outbox + Indexer-Worker aus der BP übernehmen.

Warum: Meilisearch wird ohnehin selbst gehostet (EU-Datenhoheit + Ops bereits gelöst) → bessere Tippfehler-Toleranz/Relevanz + maximaler Reuse. Weicht bewusst von M056 „Postgres-FTS, kein Extra-Service" ab → Change-Request gegen M056 nötig. Betrifft: M24 / M056–M058.

AD-6 — EU-Email-Provider ✅

Entscheidung (2026-06-16, EXT-02): Mailjet (EU/FR) als EU-Email-Provider für transaktionale und Marketing-Mails. MailerSend (BP-Default) wird verworfen — MailerSend, Inc. ist US-Entität (Transfers via EU-US Data Privacy Framework), unter strikter LP-13-Lesart („keine US-Gesellschaft im Boot", wie bei OpenAI/Anthropic) problematisch.

Warum: EU-Datenhoheit ohne US-Gesellschaft im Boot; feature-reich (transaktional + Marketing

  • A/B in P14). Boilerplate ist bereits auf MAILJET_* verdrahtet.

Verifikationsstand (EXT-02): Sende-Domain hivegrid.de bei Mailjet verifiziert — SPF (include:spf.mailjet.com) + DKIM (Selector mailjet) Auto-Probe grün, Sender *@hivegrid.de aktiv; DMARC p=none (Monitoring während Warm-up). Cleanup-Follow-ups: (a) include:_spf.mailersend.net aus dem SPF-Record entfernen, sobald die Migration vollständig ist; (b) DMARC nach Warm-up von p=none auf p=quarantine ziehen.

Betrifft: M09, M10, M22, M102.

AD-7 — Captcha / Bot-Schutz 🅿️ (Backlog)

Status: Entscheidung später. Tendenz: EU — Friendly Captcha (DE, GDPR-first) oder self-hosted mCaptcha (passt zum Meilisearch-Self-Host-Ansatz).

  • Turnstile (BP) und hCaptcha (Spec) sind beide US-Firmen → bei strikter Linie Change-Request. Betrifft: Login (M020), Public-Forms (M05/M125), Booking (M06).

AD-8 — Verschlüsselung: Per-Tenant-DEK + Envelope ✅

Entscheidung: Pro Mandant ein Data-Encryption-Key (DEK), verschlüsselt mit einem Master-Key (Scaleway Key Manager o.ä.) — Envelope-Encryption. Crypto-Shredding bei Mandanten-Löschung (M165: Tenant-Key wegwerfen → alle verschlüsselten Daten unlesbar).

Warum: Spec verlangt Per-Tenant für Passwort-Safe (M134) + OAuth-Tokens; globaler Key (BP-Default) exponiert bei Kompromiss alle Mandanten und erlaubt kein Crypto-Shredding. Betrifft: M134 (HR-Passwort-Safe), OAuth-Tokens (M09/M25/M12), M165 (DSGVO-Löschung).

AD-9 — Realtime: WebSocket für Chat + SSE für Read-Updates ✅

Entscheidung: Generischer WebSocket-Room-Layer für bidirektionale Echtzeit (Team-Chat M13: Rooms, Presence, Typing, Read-Receipts). SSE (BP-Default) bleibt für einseitige Live-Updates (Kanban M03, Notifications M22). hocuspocus bleibt CRDT-only (Kollab-Dokumente).

Warum: SSE ist einseitig → für Chat ungeeignet; hocuspocus ist CRDT, kein Chat-Broadcast. Betrifft: M13 (M110–M114); SSE-Pfad: M03, M22.

AD-10 — Naming-Konvention: HiveGrid-Scope ohne Alt-Aliase ✅

Entscheidung: Alle Workspace-Pakete und technischen Produkt-Contracts nutzen HiveGrid-Namen: @hivegrid/*, hivegrid-cli, HIVEGRID_*, hivegrid_*-Cookies, x-hivegrid-*-Header und hivegrid.*-Queue-Namen. Es gibt keine Alt-Scope-Package-Aliase und keine parallel gepflegten Alt-Binaries.

  • packages/ui-app, packages/ui-subframe und packages/ui-mock behalten ihre realen Pfade. Ein fiktives packages/ui-hivegrid wird nicht eingeführt.
  • Estate-Slugs bleiben dort erhalten, wo sie externe Infrastruktur beschreiben (git.tool-ctrl.de, toolctrl-labs, toolctrl-ci, Plane-Workspace toolctrl).
  • HiveGrid-Doku (AGENTS.md, docs/INTEGRATION-CONTRACTS.md, PLANs, Reports) wird auf diese Realität gezogen; ältere ui-hivegrid- oder Alt-Scope-Erwähnungen sind Doku-Drift.

Warum: HiveGrid wird bespoke weitergeführt. Einheitliche Produktnamen reduzieren kognitive Last, vermeiden falsche Imports und verhindern, dass neue Arbeit versehentlich alte Contracts weiterträgt. Betrifft: Querschnitt (alle Module), Phase 0.


Delivery- & Scope-Entscheidungen

AD-11 — Bau-Strategie: Hybrid (Fundament-Kern, dann Geerkens-Kernpfad) ✅

Entscheidung: Nach Phase 0 zuerst der Fundament-Kern (M27 Auth + M33a Apps-Engine + M01 CRM-Datenmodell), dann sofort die Geerkens-Wertschöpfungskette Lead → Projekt (konfigurierbare Pipelines) → KWP, danach die Breite (HR, Portal, Marketing, Mobile, Billing-Feinheiten). Balance aus sauberem Fundament und früh sichtbarem Nutzen.

🔄 REVIDIERT 2026-06-13 (Modell-Pivot, AD-20): Kette ist nicht mehr „Lead → Sales(Deal) → Auftrag(Project)", sondern Lead → Projekt → KWP. Sales und Auftrag sind konfigurierbare Pipeline-Views auf denselben Projekt-Kern.

AD-12 — SHK-Domänen-Defaults: aus Spec als Seed, Noah validiert ✅

Entscheidung: Die SHK-Inhalte (10 Standard-Rollen + Permission-Bundles [27.7/N1], SHK-Sales-Pipeline-Stufen [M59], Standard-Custom-Felder + Tags [M44], Provisionierungs-Daten [M158]) werden aus Spec/Pflichtenheft als deklaratives, leicht änderbares Seed umgesetzt; Noah/Geerkens validieren iterativ im Klick-Pfad. Blockiert den Bau nicht.

🔄 AMENDMENT 2026-06-19 (Owner-Entscheidung Raffael, Partner-validiert): Die spekulativen „10 Standard-Rollen" aus 27.7/N1 sind auf die 6 vom Partner gelieferten Rollen festgelegt: Geschäftsführer, Projektleiter, Verwaltung, Vertriebler, Kundensupporter, Monteur (Seed apps/web/app/utils/services/shkRoles.server.ts, idempotent, is_demo_data). Das deklarative-Seed-Prinzip bleibt unverändert; nur die Anzahl/Liste ist fixiert. R-P2b-DoD + Planungs-Docs (.planning/, Milestone-Layer) entsprechend von 10 auf 6 nachgezogen.

AD-13 — Go-Live-Scope: breiter Launch ✅

Entscheidung: Der erste Geerkens-Go-Live umfasst nicht nur die Vertriebskette, sondern auch HR-Hub, Customer-Portal, Marketing und Mobile. Scope ≈ Großteil der 35 Module. (Sequenzierung trotzdem nach AD-11.)

AD-14 — Migration: sauberer Start + KWP-Sync füllt nach ✅

Entscheidung: Geerkens startet weitgehend leer; KWP-Sync (M089ff) bringt Auftrags-/Stammdaten automatisch. HubSpot/Quiply-Altdaten bleiben extern als Read-only-Archiv; nur punktuelle CSV-Importe via packages/imports. Keine vollen Migrations-Pipelines pro Quelle zum Launch.

AD-15 — Pricing/Billing: Engine bauen, Preise geparkt ✅

Entscheidung (revidiert 2026-07-04, siehe AD-27): Legacy-Provider wird nicht mehr als Billing-/Payment-Default gebaut. HiveGrid nutzt eine selbst gehostete Billing-Engine und einen EU-Payment-Processor. Für v1 sind nur Plans + Seats im Scope; kein Usage-Billing und keine konkreten öffentlichen Preisstufen. Geerkens läuft weiter auf manueller Sonderkondition/Trial. Self-Service-Checkout-Preise bleiben eine eigene spätere Pricing-Runde.

AD-16 — USP-Säulen (Automation + KI): volle Tiefe zum Launch ✅ (⚠ KI-Hälfte abgelöst von AD-26, 2026-06-22)

Entscheidung: Kompletter visueller Automation-Editor (M075–M082, alle Trigger/Aktionen, 24h-Undo, Guards) und voller KI-Layer (M083–M088, EU-Provider, RAG, Sprach-zu-Automation, Pay-as-you-go) sind day-1 dabei — nicht nur Fundament.

⚠ KI-Hälfte abgelöst durch [AD-26] (2026-06-22): Kein selbst-gehostetes LLM/RAG/HiveMind mehr — KI kommt via MCP-Server (externe Clients), M083–M088 gestrichen. Die Automation-Säule bleibt unverändert day-1.

AD-17 — Mobile: PWA-only (v1 + v2), kein nativer Client ✅ (revidiert 16.06.)

Entscheidung (revidiert 2026-06-16): Mobile läuft ausschließlich als installierbare PWA / responsive Web — in v1 und v2. Monteur kann auf der Baustelle offline Aufträge bearbeiten, Abnahmen erfassen, Plantafel sehen; alles wird über Service-Worker + IndexedDB-Offline-Queue + Background-Sync gequeued und bei Sync mit „KWP gewinnt" aufgelöst (KWP-Sync = v2). Kein Expo, kein Capacitor, keine native iOS/Android-App, keine App-Store-Einreichung. Push = Web-Push (VAPID) — aktiviert in v2 den in P1 ruhenden notify()-Push-Kanal. Native-App + App-Store → frühestens v3 (Backlog, nicht beauftragt).

Historie: 10.05. voller Offline-Betrieb (Capacitor, M167–M171) · 11.06. Capacitor→Expo + native Hülle nach v2 · 16.06. komplett auf PWA-only reduziert (kein nativer Client in v1/v2; M167–M171 entfallen, „Mobile-App"-Arbeit = PWA-Ausbau: Installierbarkeit, Service-Worker, Offline-Härtung, Web-Push).

AD-18 — Customer-Portal: voll inkl. Skalierungs-Sicht ✅

Entscheidung: Komplettes Portal day-1: Projekt-Sicht + Upload + Reklamation + Subunternehmer-Zugang + Skalierungs-Sicht (Service-Inbox + Routing + Pro-Kunden-Lebensdauer), M141–M146 inkl. [N19].

Scope-Muster (historisch): AD-13/16 wählen den maximalen Umfang → der erste Go-Live ist faktisch das vollständige Phase-1-Produkt in großer Tiefe. Sequenzierung nach AD-11. AD-17 (Mobile) und AD-18 (Portal) wurden später revidiert (AD-18 → v2; AD-17 → PWA-only v1/v2) und folgen diesem Maximal-Muster nicht mehr. Dies bleibt eine bewusst große Scope-Wahl (siehe Scope-Check in der Session + SCOPE-V1-CUTS.md).

AD-19 — Go-Live-Modus: Big-Bang ✅

Entscheidung: Geerkens geht erst live, wenn das vollständige Phase-1-Produkt steht — ein großer Launch, kein phasiertes Produktiv-Go-Live. Interne Bau-Reihenfolge bleibt AD-11 (Fundament → Kernpfad → Breite), aber es gibt kein inkrementelles Produktiv-Schalten einzelner Module für Geerkens.

Bewusst akzeptiert: längste Zeit bis zum ersten echten Nutzer-Feedback, höchstes „am-Alltag-vorbei"-Risiko. Gegenmaßnahme empfohlen: Noah/Geerkens-Validierung pro Modul über den PROGRESS-TRACKER-Klick-Pfad (READY-TO-VERIFY → Sign-Off) als Feedback-Ersatz vor dem Big-Bang.

AD-20 — Projekt-zentrisches CRM/Pipeline-Modell ✅ (NEU 2026-06-13)

Entscheidung: Das CRM/Pipeline-Modell wird Projekt-zentrisch (auf Basis von Davids UX-Briefing, voll adoptiert). Statt getrennter Deal- und Project-Entitäten gibt es EINEN Projekt-Kern, auf den frei konfigurierbare Pipelines als Views schauen. „Deal" und „Auftrag" sind nur Pipeline-Namen/-Typen, keine eigenen Objektarten.

  • Pipeline = View: Admin legt Pipelines an/benennt sie, steuert Zugriff (wer sieht), Feld-Sichtbarkeit, Pflichtfelder pro Pipeline/Status und die Statuswerte. Ein Projekt ist genau einer Pipeline zugeordnet; Status ist pipeline-intern (kein globaler Projektstatus). Nicht sichtbare Felder bleiben im Hintergrund erhalten.
  • Lead-Flow: Lead/LeadInput ist Vorstufe mit Datenpunkt Weiterverarbeitungsfreigabe (Ja/Nein/Unklar); bei Freigabe → Vererbung ins Projekt. Lead → Projekt (das frühere Lead→Contact-Promotions-Modal entfällt; Kontakt wird separat verknüpft).
  • Kontaktmodell: Kontakt ist Oberbegriff mit Kontakttyp (Privatperson/Organisation), Kontaktstatus (Kontakt/Kunde/Bestandskunde) und Kontaktart (Haupt-/Sekundärkontakt als Rolle/Relation).
  • Scope-Calls (13.06.): Abnahme bleibt v1 als Checklisten/Protokoll-Sub-Area am Projekt (Mängel = Checkliste); Material/Zeiterfassung/Leistungspositionen/Baustelle/Support → v2-Backlog. Rechnung = einfaches Sub-Entity am Projekt in v1. KWP-Felder bleiben v2-Sync, v1 nur Feld-Halter.

Warum: Davids konsolidiertes UX-Modell wird zur Bau-Grundlage gemacht; Raffael richtet die Planung daran aus (statt David einzunormen). Höhere Konfigurierbarkeit (SHK-Betriebe brauchen frei benennbare Pipelines) bei erhaltenem typisiertem Kern. Betrifft: AD-2 (revidiert), AD-11 (Kette revidiert), M01–M04, ENTITY-MODEL, ROADMAP P6–P9, REQUIREMENTS, SCREEN-MAP. Anker: .planning/ENTITY-MODEL.md.

AD-21 — Prisma: Schema-Folder + Migrations-Disziplin für parallele Branches ✅ (NEU 2026-06-16)

Kontext: 2 Devs (Raffael + Akeshan) bauen Modul-Phasen parallel in eigenen Branches. Risiko: Kollisionen im Datenmodell und in der Migrations-Historie.

Befund (16.06.): Prisma 7.8.0; das Schema ist bereits als Ordner organisiert (prisma/schema/ mit 29 Domänen-Dateien: core, accounts, permissions, appsEngine, email, workflows, notifications, marketingLeads, ai …). Die Datei-Ebene ist damit gelöst — Module in verschiedenen Domänen-Dateien kollidieren nicht. Es existieren bereits 153 Migrations (lineare Timestamp-Historie). Connection-URLs in prisma.config.ts (Prisma 7): DIRECT_URL für Migrate, DATABASE_URL via PgBouncer zur Laufzeit.

Verbleibende Risiken: (1) core.prisma/accounts.prisma sind der geteilte Kern (Tenant/User/Audit/File/ApiKey/Credential) — gleichzeitige Relations-Edits kollidieren in dieser Datei. (2) Zwei Branches mit je migrate dev erzeugen divergierende Migrations-Ordner → „applied out of order" beim Merge.

Entscheidung — drei verbindliche Konventionen:

  1. Ein Modul = eine eigene prisma/schema/<modul>.prisma. Neue Modelle nie in core.prisma/accounts.prisma. Diese nur für Relationen zu Tenant/User anfassen, Rück-Relationsfelder dort minimal halten.
  2. Migrationen erst beim Merge. Auf dem Feature-Branch mit prisma db push iterieren (kein Migrations-File). Die eine Migration pro Phase erzeugt der Dev, der zuerst auf main merged — Forward-only mit Inverse-SQL als Kommentar (AGENTS.md DoD 28). Tägliche Integration hält Diffs klein.
  3. CI-Gate vor Merge: prisma validate + prisma migrate status grün (pre-push-Hook schärfen).

Warum: Schema-Splittung erledigt die Datei-Konflikte bereits; was bleibt (core-Hotspot + lineare Migrations-Historie) löst Konvention/Disziplin, nicht neue Technik. Betrifft: alle parallel gebauten Modul-Phasen (P2–P21, v2), AGENTS.md §Prisma-Schema, lefthook pre-push. Anker: prisma/schema/, AGENTS.md.

AD-22 — GeoIP lokal + IP-Anonymisierung (EXT-12) ✅

Entscheidung: Geo-Auflösung von Login-/Session-IPs über eine lokale GeoIP-Datenbank (DB-IP Lite Country, CC-BY-4.0, MMDB-Format) statt eines externen Dienstes. Die gespeicherte IP wird pseudonymisiert (HMAC-SHA256 mit geheimem Pepper); persistiert wird nur das Land (countryCode/countryName). Kein EU-SaaS-Geo-Dienst, kein ipstack (US/apilayer, LP-13).

Warum:

  • Ein externer Geo-Dienst — auch ein EU-gehosteter — bekäme die Endnutzer-IP (personenbezogen) übermittelt → DPA + Subprozessor (M166) + Latenz + Abhängigkeit. Der Transfer selbst ist das Risiko.
  • Lokale MMDB: Lookup passiert auf unserem Server, die IP verlässt unsere Infra nie. Kein DPA, kein Subprozessor. Passt zur Self-Host-Linie (Meilisearch/mCaptcha).

Wofür Geo-IP (Zweck): „Login aus Land X" + Anomalie „Login aus neuem Land" in der Session-/ Security-Sicht (P2); optionale Analytics-Anreicherung nach Land (Default aus); Feature-Flag-Targeting nach Land. Der HIBP-Breach-Check (EXT-12, password-policy.server.ts) ist davon unabhängig und bereits aktiv.

Speicher-/Minimierungs-Regeln:

  • IP nie im Klartext: IpAddress.ip/IpAddressLog.ip speichern HMAC-SHA256(IP, IP_HASH_PEPPER). Der Pepper liegt in den Coolify-Secrets, nicht in der DB → ein DB-Leak allein ist wertlos; der Gleichheits-Vergleich („gleicher Client?") bleibt möglich (Pseudonymisierung statt Anonymisierung).
  • Geo nur Land: regionCode/city/zipCode/latitude/longitude bleiben leer/null.
  • Der Blacklist-Abgleich nutzt die Roh-IP nur zur Laufzeit (Security-Liste), persistiert sie nicht im Log.

Provisionierung: Die MMDB ist ein Deploy-Artefakt (nicht im Git), Pfad via GEOIP_DB_PATH (Default ./data/geoip/dbip-country-lite.mmdb); scripts/update-geoip-db.sh lädt die Monatsversion. Fehlt die Datei, ist die Land-Auflösung ein No-op (Stub) ohne Fehler. Reader: npm maxmind (MIT).

Env: IP_HASH_PEPPER (Secret), GEOIP_DB_PATH (optional).

Folge-Arbeit (offen): monatlicher Worker-Cron (BullMQ) statt Shell-Skript; IpAddress/IpAddressLog perspektivisch auf Land + Hash-IP eindampfen (Spalten entfernen, sobald Session-Filter/Feature-Flag- Konsumenten umgestellt sind); UI, die bisher die Roh-IP zeigte, auf Land umstellen. Betrifft: EXT-12, P2 (Sessions/Security), P21 (DSGVO/Retention), modules/ipAddress, services/auth/password-policy. Anker: apps/web/app/modules/ipAddress/, docs/ARCHITECTURE-DECISIONS.md.

AD-23 — Media-Auslieferung: privater Bucket + authentifizierter App-Proxy (kein public-read) ✅

Entscheidung (2026-06-17): Hochgeladene Medien werden nicht über public-read S3 ausgeliefert. Der Bucket bleibt privat; die App liefert Medien über eine authentifizierte Proxy-Route aus (Session + Tenant-Scope + Permission), die das Objekt aus dem privaten Bucket streamt bzw. auf eine kurzlebige signierte Download-URL weiterleitet. STORAGE_PUBLIC_BASE_URL bleibt leer; der publicUrl- Pfad (RowHelper/MediaHelper/ApiHelper, publicUrl ?? file) zeigt auf die Proxy-Route.

Warum: CRM-Medien enthalten Kundendaten/Dokumente — world-readable Buckets sind DSGVO-untragbar (jeder mit der URL käme ohne Auth/Tenant-Check ran). Weg A (public-read) ist damit verworfen.

Für P4 (Files, AK-P4a/b) umzusetzen:

  • Proxy-Route (z.B. /media/… bzw. /storage/…) mit Auth + withTenantScope() + withPermission(), streamt aus dem privaten Bucket oder redirected auf signierte Download-URL (kurze TTL).
  • publicUrl in den Media-Helpern auf die Proxy-Route verdrahten (statt null/rohem Key).
  • Genuin öffentliche Assets (E-Mail-Bilder, öffentliche Formular-/Booking-/Portal-Seiten, Branding-Logos) können keinen Auth-Proxy nutzen → signierte URLs mit TTL oder ein separater public/-Prefix mit eng begrenztem public-read. Pro Asset-Typ entscheiden.
  • DSGVO-Export-Artefakte nutzen bereits signierte URLs (unverändert).

Betrifft: P4 (Files), EXT-06 (nur Bucket/Infra), packages/storage, apps/web Media-Helper. Anker: AD-8 (Verschlüsselung), packages/storage, apps/web/app/utils/helpers/MediaHelper.ts.


Phase-0-Pflichten (kein „Entscheid", sondern Voraussetzung — siehe _SUMMARY.md)

Diese Punkte sind vor der ersten Fachmodul-Umsetzung abzuarbeiten:

  1. Codebase lauffähig halten (pnpm-Workspace, Apps, Packages, Prisma).
  2. Naming-Drift beheben: @hivegrid/*, hivegrid-cli, HIVEGRID_*, hivegrid_*, x-hivegrid-*, hivegrid.*, Sidebar-Konvention, falsche AGENTS.md-Pfade (indexed-entities.ts, sidebar.config.ts).
  3. EU-Sterilisation: GitHub/Google-OAuth-Reste + User.githubId/googleId entfernen.
  4. M00 aktivieren + 4 echte Neubauten: logActivity()/Activity-Stream, Search-indexed-entities-Registry, Subframe-Adapter-Code (Quer-Voraussetzung für jede UI-Route), Mass-Action-Schutz.
  5. Fehlende Dependencies ergänzen (PLAN.md teils falsch): signature_pad, browser-image-compression, react-grid-layout, @dnd-kit (statt react-beautiful-dnd), libphonenumber, ics/ical-generator, btree_gist/EXCLUDE-Constraint u.a.

AD-24 — Prod unter eigenem Hetzner-Account (dediziert, self-contained) ✅

Entscheidung (2026-06-19, Owner Raffael): Produktion läuft in einem eigenen, dedizierten Hetzner-Cloud-Account, vollständig getrennt von Staging/dev. Topologie — 5 VMs (fsn1, x86 cx-Linie):

  • control — eigenes Coolify + Orchestrierung/Bastion (cx33: 4 vCPU/8 GB)
  • web, api (je cx33)
  • worker + services — 5 Worker-Queue-Gruppen + self-hosted Meilisearch + ClamAV + mCaptcha (cx43: 8 vCPU/16 GB)
  • db — Postgres + PgBouncer + Redis + Daten-Volume (cx33 + Volume)

mCaptcha nutzt die Postgres/Redis der db-VM (eigene DB), keine separate VM. Self-contained: eigene Zentral-Dienste (kein Zugriff aufs Staging-Privatnetz), eigener DEK-Master-Key (AD-8) + eigener S3-Bucket (hivegrid-prod, fsn1), eigenes Coolify.

Ingress (Variante „Cloudflare-Proxy"): Cloudflare proxied vor app./api./captcha. (Edge-TLS, DDoS/WAF, Origin-IP versteckt) → Coolify/Traefik pro Host (Let's-Encrypt am Origin). admin. restriktiv (dns-only + Firewall bzw. Cloudflare Access). Kein Hetzner-Load-Balancer bis zur horizontalen Skalierung — die App ist über serverseitige Sessions in Postgres+Redis (AD-4) dafür bereit; LB + 2+ web/api-Instanzen kommen nach Anhebung des VPS-Limits.

Netzwerk: privates Hetzner-Network + Firewall — db/services nur privat erreichbar, öffentlich nur 80/443 (web/api/control), SSH eingeschränkt.

Warum: Blast-Radius/Isolation (getrennte Tokens/Billing, kein Cross-Deploy-Unfall Staging↔Prod), saubere DSGVO-/Audit-Grenze, Crypto-Shredding-Grenze pro Account. Revidiert/erweitert die Infra-ADR-009 (Hetzner+Coolify) um die Account-Trennung.

Grenzen: Hetzner-Default-Limit 5 VPS/Projekt → echtes HA (web1/web2 + api1/api2 hinter LB) erst nach Limit-Anhebung (Support). Provisionierungs-Schritte + Go-Gates: docs/ops/prod-rollout-hivegrid.md.

Betrifft: Prod-Deploy, Ops, alle Module.

AD-25 — E-Signatur: gehärtete SES-Eigenbau, kein externer Provider (v1); QES-Add-on via DE-QTSP (v2) ✅ (NEU 2026-06-21)

Entscheidung (2026-06-21, Owner Raffael): HiveGrid baut die elektronische Signatur (Angebote/Aufträge/Abnahmen) für v1 selbst als gehärtete SES und macht das zum Default für alle Tenants — kein externer Provider als Zwang. PandaDoc bleibt als optionale BYOK-Anbindung erhalten: Tenants mit eigenem PandaDoc-Account tragen ihren eigenen API-Key ein und signieren über ihren Account (Opt-in). Damit ist die Annahme „PandaDoc ist der zentrale/erzwungene Provider" abgelöst (Abweichung vom Pflichtenheft V.4.4), PandaDoc selbst aber nicht — es wird vom Default zum Opt-in-Adapter. Betrifft AK-P16d, EXT-09, EXT-17, VR-P16.

BYOK-Vorteil (CLOUD Act): Bei BYOK nutzt der Tenant bewusst seinen eigenen PandaDoc — die CLOUD-Act-/Drittland- Verantwortung liegt damit beim Tenant (dessen Account, dessen Entscheidung), nicht bei HiveGrid. Das „100 % EU"- Default (SES-Eigenbau) bleibt unangetastet; PandaDoc ist eine transparente Opt-in-Wahl (Subprozessor-Liste/AVV).

Begründung (dreifach belegt):

  • Rechtlich: SHK-Dokumente sind formfrei oder Textform (§§ 631, 650i Abs. 2, 640 BGB) → SES genügt (auch beim Verbraucherbauvertrag, der nur Textform verlangt — keine QES). Einziger zwingender QES-Fall im ganzen SHK: Kündigung eines Bauvertrags (§ 650h BGB) — kein Tagesgeschäft, selten.
  • Praxis: Marktstandard im DE-Handwerk ist die SES-Touch-Unterschrift vor Ort (+ Fern-Link). Alle etablierten Tools (HERO, plancraft, Craftnote, ToolTime …) bieten SES; AES/QES spielt im Tagesgeschäft keine Rolle, kein Marktdruck. Wettbewerbsfaktor ist der Prozess (papierlos → schneller zur Rechnung), nicht die Stufe.
  • Souveränität: PandaDoc/DocuSign = US-Firmen → CLOUD Act §2713, bricht das „100 % EU"-Versprechen (A.12.4). EU-Provider geprüft (Yousign FR am besten; Skribble CH hat US-Subprozessoren/kein echtes Embedding/per-Seat). OSS geprüft (Documenso = nur SES + AGPL). Eigenbau ist souveränster + günstigster + rechtlich ausreichend.

Umsetzung v1 (P16 / AK-P16d) — „gehärtete SES" (praktisch auf AES-Beweisniveau, ohne PKI-pro-Person/QTSP):

  1. Touch-Signatur vor Ort (Canvas → PDF) + Fern-Link-Signatur (Angebot remote annehmen)
  2. OTP-Identifizierung (E-Mail; SMS als stärkere Zwei-Kanal-Variante)
  3. PDF-Versiegelung (PAdES mit HiveGrid-Organisations-Zertifikat → jede Änderung bricht die Signatur)
  4. Zeitstempel (TSA, RFC 3161; nicht-qualifiziert genügt für SES/AES)
  5. Audit-Trail über M001 (Link/Öffnen/OTP/Signatur: wer, wann, IP, Gerät, Geo, PDF-Hash; append-only)
  6. Completion-Certificate als PDF-Anhang (sichtbare Beweis-Doku)
  7. Einmal-Link (personengebunden) + expliziter „Intent to sign"

Beweiskraft: gehärtete SES wie AES unter freier Beweiswürdigung (§ 286 ZPO) — der Audit-Trail trägt, nicht das Stufen-Etikett. Anscheinsbeweis (§ 371a) hat nur QES.

Architektur: Signatur über defineAdapter (Apps-Engine, provider-agnostisch). Adapter:

  • ses-selfhosted — Default (v1), die gehärtete Eigenbau-SES.
  • pandadoc-byok — optional (v1), Tenant bringt eigenen PandaDoc-API-Key (DEK-verschlüsselt im Credential-Store); Mandanten-Trennung via eigener Account + Webhook-Routing (pro-Tenant-URL + metadata.hivegrid_tenant_id). Anbindungs-Spec: EXT-17.
  • Spätere Optionen ohne Fachcode-Umbau: AES (Yousign managed oder EU-DSS-Library self-hosted, Java-Sidecar), QES (DE-QTSP D-Trust sign-me, Hash-only — Dokument bleibt im Perimeter) für § 650h-Bauvertragskündigung.

Betrifft: P16 (AK-P16d), EXT-09 (SES-Eigenbau-Default), EXT-17 (PandaDoc-BYOK-Adapter), VR-P16. PandaDoc bleibt als BYOK-Opt-in. Vor Produktivschaltung der Signatur: anwaltliche Bestätigung der Form-/Beweis-Einordnung (SES genügt, § 650h→QES).

Pflichtenheft-Status (Pivot-Vorrang): Abweichung von V.4.4 (PandaDoc als zentraler/erzwungener Signatur-Provider) sowie von den Phase-1-App-Listen in Block 4 (33a.1.1 / 33a.1.4 / 33a.2.3). Change-Request gegen das Pflichtenheft nötig (M176); bis dieser eingepflegt ist, gilt AD-25 vor dem Pflichtenheft (Pivot-Vorrang, analog AD-20/AD-26). PandaDoc bleibt in den Listen, wechselt aber vom Default zum Opt-in-Adapter. Offen (mit M176): Pivot-Hinweis an der V.4.4-/PandaDoc-Stelle im Pflichtenheft + Aufnahme in die CR-Liste.

AD-26 — KI-Pivot: MCP-only, kein selbst-gehostetes LLM/RAG (löst die KI-Hälfte von AD-16 ab) ✅ (NEU 2026-06-22)

Entscheidung (2026-06-22, Owner Raffael): HiveGrid hostet kein eigenes LLM/RAG/HiveMind mehr. Statt eine KI-App zu sein, wird HiveGrid KI-anschließbar über einen MCP-Server (Model Context Protocol): externe KI-Clients (Claude Desktop/Cursor/ChatGPT) docken an, Inferenz liegt beim Client. Damit entfällt der EU-LLM-Provider-/DSGVO- Zwang für HiveGrid selbst (kein Mistral/Scaleway/AlephAlpha-Hosting mehr). Löst die KI-Hälfte von AD-16 ab — die Automation-Säule bleibt.

Begründung: Kleines Team; der teure/harte Teil (LLM-Hosting, RAG-Infra, Promptfoo-Eval, AiCredits-Abrechnung, EU-Provider-Compliance) fällt weg. MCP nutzt dieselbe mandanten-gescopte Daten-/Aktions-Fläche wie die geplante Public-API (VR-P22a/VR-P22b) → keine Doppelarbeit.

v1 (KI komplett gestrichen): R-P13 (KI-Layer/Mistral-Gateway) + D-P13 entfernt, EXT-11 + Mistral-Key gelöscht, Milestones M083–M088 gelöscht, R-P12b verliert den Sprach→Automation-Teil (bleibt reiner visueller Flow-Editor). Neues letztes v1-Glied = R-P12b (~1 Tag Puffer → Härtung). Freeze/Launch 09./10.07. bleiben fix.

v2: VR-P25/VD-P25 (HiveMind/RAG/Whisper) gestrichen (Plane: Cancelled) → neu VR-MCP + VD-MCP im Slot 18.–20.07., blocked-by VR-P22a/b (Public-API, fertig 17.07. → kein Konflikt).

Public-API-/MCP-Surface (2026-07-03, verbindlich): Public API läuft ausschließlich auf api.hivegrid.de (staging: staging-api.hivegrid.de) mit Versionierungs-Prefix /v1/...; es gibt keine Public-API-Routen unter app.hivegrid.de/api. MCP läuft ausschließlich auf mcp.hivegrid.de (staging: staging-mcp.hivegrid.de) als eigener Coolify-Service/Container hivegrid-mcp mit eigener Runtime (apps/mcp), ohne zusätzlichen /mcp-Pfad. Auth/Scopes/Rate-Limit werden aus der Public-API-Schicht wiederverwendet, aber die Runtime-/Host-Surface bleibt getrennt.

MCP-Design (Best-Practice, MCP-Spec-Rev 2025-11-25): Remote-Server über Streamable HTTP (stateless Start), SDK @modelcontextprotocol/sdk. Auth v1 = OAuth 2.1 Authorization Code + PKCE mit Protected-Resource-Metadata (RFC 9728), Authorization-Server-Metadata (RFC 8414), Resource-/Audience-Bindung und Consent. API-Key-Bearer aus VR-P22 bleibt nur als Operator-/Fallback-Pfad, nicht als primärer User-Connect-Flow. Tenant aus dem Token (nie aus Tool-Argumenten) → withTenantScope() + RLS. Read-Tools + Write-Tools (withPermission() + Client-Confirmation + auditEvent/logActivity). Annotations (readOnlyHint/destructiveHint) nur als Hint — echte Guards serverseitig. Rate-Limit + Output-Limits + Pagination; Tenant-Daten sind Daten, nie Instruktionen (Prompt-Injection).

Code-Rip-out (Variante a): raus = packages/ai, packages/vector-store, Module ai/ai-gen/ai-cost/chatbot/ ai-elements, AI-Routen, Worker-AI-Jobs, ai.prisma — UND die Legacy-PromptFlow-Engine (promptBuilder/admin/prompts/ promptFlows.prisma). Bleiben: @modelcontextprotocol/sdk, modules/mcp/, der EU-Sterilisations-Guard.

Pflichtenheft: A.12.3 (EU-LLM-Provider) und V.2.2 („KI als Bedienoberfläche der Automation") widersprechen jetzt → Change-Request (M176) nötig; bis dahin gilt diese AD vor dem Pflichtenheft (Pivot-Vorrang). Anti-Liste V.3.2.7 (HiveMind autonom) bleibt erfüllt (wird gar nicht gebaut).

Betrifft: R-P13/D-P13/EXT-11/M083–M088 (entfernt), R-P12b (Trim), VR-P25/VD-P25 (gestrichen), VR-MCP/VD-MCP (neu), R-P11a/R-P11b/D-P3/R-P5/D-P5/D-P20 (KI-Andock umdeuten/trimmen), VA-P24b/VA-P29/VR-P31/VR-P33 (KI-Teile entfallen).

AD-27 — Billing-Recut: Lago self-hosted + Mollie Payments, kein Legacy-Provider-Default ✅ (NEU 2026-07-04)

Entscheidung (2026-07-04, Owner Raffael): HiveGrid nutzt kein Legacy-Provider als Billing- oder Payment-Default, weil kein relevantes CLOUD-Act-Risiko in der zentralen Billing-Abhängigkeit akzeptiert wird. Billing wird in drei Schichten getrennt:

  1. HiveGrid bleibt Produkt- und Zugriffsschicht: Tenants, Seats, Plan-Status, Feature-Gates, Read-only/Frozen-State.
  2. Lago self-hosted wird Billing-Engine: Plans, Seats, Subscriptions, Invoices und Billing-Status.
  3. Mollie wird v1-Payment-Processor: Checkout/Zahlung, Payment-Status, Refund-/Failure-Events. Mollie wird nicht zur Billing-Source-of-Truth und nicht fuer Invoicing genutzt.

v1-Scope: nur Plans + Seats. Kein Usage-Billing, keine Credits, keine Add-on-Metering-Logik, keine oeffentlichen Preisversprechen im Code. Bestehende Usage-/Quota-Felder duerfen als technische Altlast bleiben, werden aber nicht als neuer Billing-Scope erweitert.

Invoicing: Lago erzeugt die fachliche Rechnung/Invoice-Records. Rechnungssteller und finale Rechnungs-/Steuerlogik werden entschieden, wenn die rechtliche/gesellschaftliche Struktur feststeht. Bis dahin bleibt die Umsetzung auf technisches Invoicing + Testdaten begrenzt.

Infrastruktur:

  • Staging: eine geteilte lago-staging-Instanz im Estate-Coolify auf node1-estate, aktuell intern erreichbar. Mehrere Staging-Services koennen dieselbe Instanz nutzen, solange Testkunden/Plans sauber ueber Codes/Metadata getrennt werden.
  • Production: bekommt spaeter eine eigene lago-production-Instanz im HiveGrid-Prod-Setup. Staging und Prod werden nicht in derselben Lago-Instanz gemischt.
  • Lago bleibt als separater Service angebunden. Keine Lago-Core-Patches im HiveGrid-Repo. Falls Lago-Code angepasst werden muesste, ist wegen AGPL-3.0 vorher Legal-/Lizenz-Entscheidung oder Commercial-License-Pfad erforderlich.

Payment-Minimierung: Mollie erhaelt nur Billing-/Payment-Minimum: Customer-Referenz, Invoice-Referenz, Betrag, Zahlungsmethode und noetige Kontaktdaten. HiveGrid-Fachdaten, CRM-Inhalte, Projektinhalte und MCP-Daten werden niemals an Mollie oder Lago gesendet.

Abgeloeste Annahmen: Die aktiven Billing-Ticketdocs wurden in BILL-00 auf Lago/Mollie recuttet. Alte Legacy-Provider-Erwaehnungen in historischen Umsetzungsnotizen, Roadmap-/Milestone-Alttexten oder Legacy-Code sind Migrationsinventar und duerfen nicht als Zielarchitektur gelesen werden.

Betrifft: AK-P20, D-P20, VA-P24a/b, VD-P24, AK-P19, AK-P21, EXT-07, EXT-16, H-PAYLINK, R-P1b/Feature-Gates.

AD-28 — Interim-UI: Dev baut Vorschau-Screens aus ~/ui, finale Optik wird getrackter Nachzug ✅ (NEU 2026-07-04)

Entscheidung (2026-07-04, Owner Akeshan + Raffael): UI-lastige Dev-Tickets bleiben nicht bis zu Davids finalem Screen-Delta auf „On Hold". Stattdessen baut der Dev eine Interim-UI (Vorschau-Screen) aus den bereits im Repo gesyncten Subframe-Components (apps/web/app/ui/, Alias ~/ui/*) und stellt das Ticket damit fertig. Präzisierung der Done-Definition für solche Tickets: Ein Ticket ist Done-fähig, sobald Dev-DoD 100 % + Interim-Screen aus ~/ui (VM als Prop, kein handgeschriebenes Tailwind) + Tests grün sind. Die finale Subframe-Optik ist dann kein Done-Blocker mehr, sondern ein getrackter Nachzug (Plane-Label design-nachzug + „Dev-Nachzug"-Sektion der Ticket-Doc + Interim-Visual-Baseline, die beim Nachzug neu gezogen wird).

Goldene Regel: Interim-Screens werden aus ~/ui-Components + ~/ui/layouts/* komponiert und beziehen ihre Daten aus dem bestehenden View-Model (packages/view-models/**). Handgeschriebenes Tailwind statt ~/ui ist ein PR-Block — nur die ~/ui-Komposition hält den späteren Tausch gegen Davids Screen billig (Component-Swap hinter stabiler VM/Loader-Grenze, Hot-Swap-Gedanke M015/M017). Die drei Anti-Patterns und die Layer-Trennung (Abschnitt „Drei Anti-Patterns") bleiben unangetastet.

Warum: Davids Screens dauern noch lange, während die Component-Basis (79 ~/ui-Components + Layouts, gesynct via .subframe/sync.json) und die View-Models bereits stehen. Die Designer-first-Reihenfolge bleibt erhalten — David überschreibt/finalisiert weiterhin —, aber Backend-Tickets werden nicht länger formal von leeren D-*-Deltas blockiert. Begriff „Interim-UI" ist bewusst von packages/ui-mock (M017-Hot-Swap-Drop-in) abgegrenzt.

Betrifft: alle UI-lastigen Dev-Tickets v1+v2 (Akeshan + Raffael), u. a. R-P2/P8/P9/P11/P12, AK-P3/P4/P14/P16/P17/P19/P20/P21, VR-P22b, VR-MCP; sowie den plan-ticket-Command und die Designer-first- Regel in .planning/ticket-docs/README.md.

Anker: .planning/INTERIM-UI-PLAYBOOK.md (ausführliches Wie) · .planning/INTERIM-UI-DESIGN-STANDARDS.md (Davids Layout-/Komponenten-/State-Werte als In-Repo-Spiegel von ../HiveGrid-Wireframes/, damit die Interim-UI nah an Davids finalen Screens landet) · .claude/commands/plan-ticket.md (§4/§5/§8) · AGENTS.md (Ein Ticket bauen + DoD) · docs/INTEGRATION-CONTRACTS.md (§12) · .planning/ticket-docs/README.md (Konvention „Dev-Nachzug"/Interim-UI-Vermerk).

AD-29 — Rollen-Seed: Produktion startet admin-only, SHK-Seeds nur Staging/Dev ✅ (NEU 2026-07-07)

Entscheidung (2026-07-07, Owner Akeshan): Auf Produktion startet ein frischer Tenant nur mit der Admin-Systemrolle — die 6 SHK-Standard-Rollen (Geschäftsführer, Projektleiter, Verwaltung, Vertriebler, Kundensupporter, Monteur; Partner-Liste 2026-06-19) werden dort nicht mehr automatisch geseedet. Auf Staging/Dev bleiben die SHK-Seeds als Test-Komfort aktiv. Damit ist der Widerspruch zwischen dem R-P2b-Seed (Owner-Entscheidung Raffael) und Davids validierter D-P2-Design-Entscheidung („zum Start nur die Systemrolle Admin, keine vorgebauten Seed-Rollen") zugunsten des Designs aufgelöst; die SHK-Liste bleibt als Definition erhalten (perspektivisch Rollen-Vorlagen, Davids WIP-Konzept in M04A).

Mechanik: Gate shouldSeedShkRoles() in apps/web/app/utils/services/shkRoles.server.ts — SENTRY_ENVIRONMENT=production ⇒ kein Seed. Bewusst die deploy-scoped Variable (Estate-Standard §1), weil NODE_ENV auf Staging-Containern ebenfalls production ist; lokal (ohne SENTRY_ENVIRONMENT) entscheidet nodeEnv. Override in beide Richtungen per SEED_SHK_ROLES=true|false, falls der Partner die Seeds produktiv doch will. Die übrige Auto-SHK-Provisionierung (AK-P20: Pipelines, Custom-Felder, Form-/Projekt-Templates, Checkliste) ist nicht betroffen — nur der Rollen-Seed.

Betrifft: R-P2b (DoD „6 SHK-Rollen geseedet" gilt Staging/Dev), AK-P20 (provisionShkTenantDefaults ruft den Rollen-Seed weiter auf, er no-opt auf Prod), D-P2/S02 (Rollen-Liste zeigt auf Prod initial nur Systemrollen), REQUIREMENTS.md REQ-auth-rbac, ROADMAP.md Phase-2-SC 4.

KWP (separater Strang — siehe _bmad-output/specs/M19-kwp-integration.md und docs/milestones/M19-KWP-API-RECUT.md)

KWP bekommt eine offizielle API-Anbindung → M089 (Windows-Sync-Programm) entfällt, M090/M091/M092/M094/M095 vereinfachen sich, M093 bleibt. KWP als DataConnection type='rest-api' ins Apps-Engine-Muster (M33a). M089–M095 vor Umsetzung neu schneiden.