42 KiB
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.mdund 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 unddocs/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-Pfadtenants/{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 ~49EntitiesSingleton-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 denCrmService-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 vonRow-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.
AdminAuditLogbraucht kein neues Schema: vorhandenerAuditEvent(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-subframeundpackages/ui-mockbehalten ihre realen Pfade. Ein fiktivespackages/ui-hivegridwird nicht eingeführt.- Estate-Slugs bleiben dort erhalten, wo sie externe Infrastruktur beschreiben
(
git.tool-ctrl.de,toolctrl-labs,toolctrl-ci, Plane-Workspacetoolctrl). - HiveGrid-Doku (
AGENTS.md,docs/INTEGRATION-CONTRACTS.md, PLANs, Reports) wird auf diese Realität gezogen; ältereui-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;
Statusist 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:
Kontaktist Oberbegriff mitKontakttyp(Privatperson/Organisation),Kontaktstatus(Kontakt/Kunde/Bestandskunde) undKontaktart(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:
- Ein Modul = eine eigene
prisma/schema/<modul>.prisma. Neue Modelle nie incore.prisma/accounts.prisma. Diese nur für Relationen zuTenant/Useranfassen, Rück-Relationsfelder dort minimal halten. - Migrationen erst beim Merge. Auf dem Feature-Branch mit
prisma db pushiterieren (kein Migrations-File). Die eine Migration pro Phase erzeugt der Dev, der zuerst aufmainmerged — Forward-only mit Inverse-SQL als Kommentar (AGENTS.md DoD 28). Tägliche Integration hält Diffs klein. - CI-Gate vor Merge:
prisma validate+prisma migrate statusgrü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.ipspeichernHMAC-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/longitudebleiben 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). publicUrlin 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:
- Codebase lauffähig halten (pnpm-Workspace, Apps, Packages, Prisma).
- Naming-Drift beheben:
@hivegrid/*,hivegrid-cli,HIVEGRID_*,hivegrid_*,x-hivegrid-*,hivegrid.*, Sidebar-Konvention, falsche AGENTS.md-Pfade (indexed-entities.ts,sidebar.config.ts). - EU-Sterilisation: GitHub/Google-OAuth-Reste +
User.githubId/googleIdentfernen. - 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. - 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):
- Touch-Signatur vor Ort (Canvas → PDF) + Fern-Link-Signatur (Angebot remote annehmen)
- OTP-Identifizierung (E-Mail; SMS als stärkere Zwei-Kanal-Variante)
- PDF-Versiegelung (PAdES mit HiveGrid-Organisations-Zertifikat → jede Änderung bricht die Signatur)
- Zeitstempel (TSA, RFC 3161; nicht-qualifiziert genügt für SES/AES)
- Audit-Trail über M001 (Link/Öffnen/OTP/Signatur: wer, wann, IP, Gerät, Geo, PDF-Hash; append-only)
- Completion-Certificate als PDF-Anhang (sichtbare Beweis-Doku)
- 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:
- HiveGrid bleibt Produkt- und Zugriffsschicht: Tenants, Seats, Plan-Status, Feature-Gates, Read-only/Frozen-State.
- Lago self-hosted wird Billing-Engine: Plans, Seats, Subscriptions, Invoices und Billing-Status.
- 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 aufnode1-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.