17 KiB
Boilerplate-Abgleich — Modul M32 (Feature-Flags / Admin-UI)
- Modul-ID: M32
- Name: Feature-Flags (Plattform-Admin-UI)
- Milestones: M031 (Schalter-Definition + Lifecycle), M032 (Filter-Editor + Einzel-Ausnahme), M033 (Massen-Funktionen + Statistik + Audit-Historie)
- Block: Block 1 · Modul 32 (Pflichtenheft
block1.md§32.1-32.9) - LP: LP-9 (Spec-Tiefe), LP-10 (Klartext-Diagnostik); ist selbst die Umsetzung von AV #7 (Feature-Flag-Hülle) und AV #6 (Audit-Default)
- Reuse-Level: medium — sehr starke, getestete Querschnitt-/Engine-Basis (Paket
@toolctrl/feature-flags) vorhanden; das fachliche Datenmodell der Spec (Lifecycle, Tenant-Override, Filter mit Priority/Criteria/Value, Owner, Audit-Diff) und die konkrete Admin-UI müssen aber nennenswert ergänzt/neu gebaut werden.
Verifiziert direkt in
/Users/raffaelsheikh/Documents/GitHub/boilerplate(nicht nur Inventur). Hinweis: HiveGrid hat noch kein Prisma-Schema (/Users/raffaelsheikh/Documents/GitHub/HiveGrid-Cloud/prismaenthält nurinit-db.sql), keinepackages/ui-hivegrid, keinapp/routes. Die Boilerplate-Artefakte sind daher als Vorlage/zu portieren zu betrachten, nicht 1:1-Import in ein bestehendes HiveGrid-Paket.
Übernehmbar aus Boilerplate
| Boilerplate-Pfad | Was | Wie nutzbar |
|---|---|---|
packages/feature-flags/src/evaluate.ts |
evaluateFlag(name, ctx, opts) — DB-freie Eval-Engine via FlagLoader-Callback, mit Zeitfenster, Parent-Dependency (Zyklus-Schutz maxDepth=8), Variant-Bucketing. EvaluateFlagResult.reason (flag-not-found/disabled/…/matched) |
anpassen — Fundament für tenantHasFlag. Spec-Auswertungsreihenfolge (Override → Filter nach priority → default_value) ist NICHT die Paket-Logik; muss als eigene Resolver-Schicht oben drauf oder als neuer Rule-/Loader-Aufbau ergänzt werden. |
packages/feature-flags/src/targeting/evaluator.ts + targeting/types.ts |
Reiner Regel-Evaluator + Rule-Union (allowlist/percentage/plan-tier/role-based/time-based/composite) |
als-vorlage — Pattern für deterministische, I/O-freie, total-typsichere Regel-Auswertung. HiveGrid-Filter-Criteria (tenant_size, tenant_industry, tenant_status, subscription_product_id, tenant_created_*, tenant_id_in/not_in) sind andere Leaf-Typen → analog ergänzen. AND-Verknüpfung + Exhaustiveness-Check direkt übernehmbar. |
packages/feature-flags/src/targeting/percentage.ts |
computeBucket, isInRolloutBucket (sticky Hash-Bucketing) |
als-vorlage — in Phase 1 nicht benötigt (Prozent-Rollout = Phase 2), aber für M032/Phase-2 vorhanden. |
packages/feature-flags/src/cache.ts |
createFlagEvalCache, buildCacheKey, hashContext, FEATURE_FLAGS_INVALIDATION_CHANNEL |
anpassen — deckt das Spec-Caching (Block E: Request-scoped, kein wiederholtes DB-Roundtrip) ab. Cache-Key müsste um Resolver-Inputs (TenantOverride/Filter-Set) erweitert werden; Cross-Replica-Invalidierung bei Admin-Save bereits gelöst. |
packages/feature-flags/src/publisher.ts |
publishFeatureFlagInvalidationOnce/ViaStore (Cross-Process-Invalidierung via shared-store) |
1:1 — bei jeder Flag-/Filter-/Override-Mutation aus Admin-Action aufrufbar. |
packages/feature-flags/src/client/context.tsx |
FeatureFlagProvider, useFeatureFlag, useFeatureFlagEnabled, useFeatureFlagVariant (SSR-Hydration) |
1:1 / anpassen — Phase 1 hat kein Tenant-User-UI für Flags, aber das Hydration-Pattern ist für flag-gegatete UI (AV #7) generell nutzbar (deckt INTEGRATION-CONTRACTS §2 feature_flag?-Gate ab). |
packages/foundation/src/flags.ts |
defineFlag, featureFlag, selectFlagRecord (leichtgewichtige Auflösung user > tenant > global, override) |
als-vorlage — die selectFlagRecord-Präzedenz (User→Tenant→Global) ist konzeptuell nah an der Spec-Reihenfolge (Override→…→Default). Für ein einfaches tenantHasFlag ggf. direkt erweiterbar; berücksichtigt aber keine priority-sortierten Filter. |
packages/foundation/src/audit.ts |
auditEvent(input, client), withAuditMiddleware(handler, buildEvent, client) |
1:1 — erfüllt Block G (Audit-Logging mit Vorher/Nachher). Wrappt jede mutierende Flag-/Filter-/Override-Action. Schreibt nach AuditEvent (core.prisma). |
packages/foundation/src/permissions.ts |
requirePermission, withPermission, withTenantScope, withOwnerCheck |
1:1 — für Block H (admin.feature_flags.read/write/lifecycle). Liefert bei Fehlschlag CatalogError(notFound) → 404 statt 403 (deckt DoD „Permission-Denied → 404" / INTEGRATION-CONTRACTS A.5.3). |
packages/foundation/src/errors.ts |
CatalogError, foundationErrors, catalogRegistry, toPinoLogObject |
1:1 — Klartext-Fehler mit i18nKey für Block I (Lookup-auf-unbekannten-Key, Vergangenheits-expires_at usw.). |
packages/view-models/src/index.ts |
VMState, VMStatus, ActionCallbacks, RegistryGate, SidebarRegistryItem, ready/empty, assertI18nLabel |
1:1 — Loader-baut-VM-Disziplin (DoD „Loader baut VM; UI nur VM+Actions"), Empty-/Loading-/Error-States, Sidebar-Registry-Gate. |
packages/i18n/src/* |
useTranslate-Factory, Intl-Formatter, ICU, Namespaces, assertI18nKey |
1:1 — alle UI-Texte als i18n-Keys (DoD/INTEGRATION-CONTRACTS §10). |
packages/url/src/canonical.ts |
adminPath/adminUrl (host-aware) |
1:1 — alle Routen liegen auf admin.hivegrid.de (Block B). |
prisma/schema/core.prisma → AuditEvent (Z.55) |
Append-only Audit-Event mit actorType, action, targetType/Id, metadata, previousHash/eventHash (Hash-Chain), dataSensitivity, regulatoryScope[] |
1:1 — Ziel-Tabelle für Block G. Diff (oldValue/newValue) als metadata ablegbar. |
prisma/schema/core.prisma → IdempotencyKey (Z.84) |
@@unique([scope, key]) |
1:1 — für idempotente Bulk-Override-Actions (M033). |
apps/web/app/routes/admin/feature-flags/* (feature-flags.tsx, index.tsx, flags.tsx, flags/$id.tsx, flags/new.tsx, settings.tsx, engine.tsx) |
Bestehende Plattform-Admin-Routen-Struktur (Liste/Detail/New, Sidebar-Icon-Layout, Permission-Gate admin.featureFlags.view/manage) |
als-vorlage — Routen-Aufteilung und Admin-Gating als Blaupause. UI deckt jedoch nur einfache An/Aus + analytics-orientierte Filter ab, nicht Lifecycle/Tenant-Override/Priority/Statistik/Audit-Historie. |
apps/web/app/modules/featureFlags/db/featureFlags.db.server.ts |
getAllFeatureFlags, getFeatureFlag, create/update/deleteFeatureFlag, Cache-Invalidierung |
als-vorlage — DB-Server-Layer-Muster (.db.server.ts). Muss um Lifecycle/Owner/TargetModule/Archiv/Tenant-Override/Filter-Priority erweitert werden. |
apps/web/app/modules/featureFlags/components/FeatureFlagForm.tsx, FeatureFlagTracker.tsx |
Anlege-/Edit-Form + Analytics-Tracker-Component | als-vorlage — Form-Struktur wiederverwendbar; HiveGrid-Felder/UI (Lifecycle-Dropdown, Owner, Modul-Tag, Default-Toggle) ergänzen. |
apps/web/app/utils/services/.server/featureFlagEvaluator.server.ts, featureFlagCache.server.ts, featureFlagsRequest.server.ts |
Server-Bridge zwischen Prisma-FeatureFlag und Paket-evaluateFlag (OR-Lookup name/id, L2-Cache, Memo 60s) |
anpassen — Vorlage für die HiveGrid-tenantHasFlag-Server-Bridge; Resolution-Order/Loader an Spec-Modell anpassen. |
apps/web/app/components/layouts/icons/IconFeatureFlags.tsx |
Sidebar-Icon | 1:1 — Icon übernehmbar. |
apps/web/app/application/sidebar/AdminSidebar.tsx (Z.206-215) |
Admin-Sidebar-Registrierung der Feature-Flags-Gruppe inkl. Sub-Items + Permission-Gate | als-vorlage — Muster für die HiveGrid-Sidebar (packages/ui-hivegrid/src/app-shell/sidebar.config.ts, INTEGRATION-CONTRACTS §2). |
packages/shared-store/* |
createTwoTierCache, Pub/Sub |
1:1 — Infrastruktur, die cache.ts/publisher.ts benötigen. |
Vorhandene Daten-Modelle (konkret, mit Pfad)
Alle in /Users/raffaelsheikh/Documents/GitHub/boilerplate/prisma/schema/:
FeatureFlag—featureFlags.prisma:1Felder:id,createdAt,updatedAt,key String?,scope(defaultglobal),tenantId?,userId?,name,description,enabled, sowie v2-Targeting-Felderrules Json?,variants Json[],enabledFrom?,enabledUntil?,parentFlagId?(Self-RelationFeatureFlagDependency). Relationen:audits,events(Analytics),filters. Indizes:@@unique([name, description]),[key, scope],[tenantId, key],[userId, key],[parentFlagId].FeatureFlagFilter—featureFlags.prisma:32Felder:id,createdAt,featureFlagId,type String,value String?,action String?(onDelete: Cascade).FeatureFlagAudit—featureFlags.prisma:44Append-only:id,flagId,actorUserId?,action(create/update/delete/toggle),oldValue Json?,newValue Json?,createdAt. Indizes[flagId, createdAt],[actorUserId, createdAt].AuditEvent—core.prisma:55(Querschnitt-Audit, Hash-Chain, tenant-aware).IdempotencyKey—core.prisma:84.Tenant—accounts.prisma(für Filter-Criteria relevant:slug,name,active,createdAt,subscription/productConfiguration/featureOverrides). Achtung: Es gibt keintenant_size-,tenant_industry- odertenant_status(trial/active/past_due/frozen/suspended)-Feld am Boilerplate-Tenant— nuractive Boolean+deactivatedReason. Die Spec-Filter-Criteria brauchen also entweder zusätzliche Tenant-Felder (HiveGrid-Tenant-Modell, Modul 37) oder eine Ableitung (z.B. MA-Zahl aus HR-Hub).
Lücken / komplett neu zu bauen
Datenmodell (größte Lücke)
Das Boilerplate-Schema folgt einem anderen Konzept (An/Aus + Targeting-Rules-Tree pro Flag, Filter = analytics-/session-/percentage-Match). Die Spec verlangt ein dreiteiliges Modell mit Tenant-Override und priorisierten Filtern. Konkret fehlt / weicht ab:
FeatureFlag— fehlende Spec-Felder:label,default_value(es gibt nurenabled),lifecycle_stageEnum (beta|rollout|ga|deprecated),owner_admin_user_id,created_by_admin_user_id,target_module,archived_at(Soft-Delete).keyistString?und nur via[name, description]unique — Spec verlangtkeyglobal unique + non-null + nach Anlage nicht editierbar.FeatureFlagFilter— fehlende Spec-Felder komplett:name,priority Int,value Boolean,criteria JSONB(strukturiertes Schema),active Boolean,created_by_admin_user_id. Boilerplate-Filter (type+value String+action) ist semantisch ein anderes Konstrukt.TenantFeatureFlag(Tenant-Override) — existiert nicht. Komplett neu:feature_flag_id,tenant_id,value Boolean,set_by_admin_user_id,note?,expires_at?,@@unique([feature_flag_id, tenant_id]), Indizes[tenant_id, feature_flag_id]+expires_at.
Logik
- Auswertungs-Reihenfolge der Spec (Override → aktive Filter nach
prioritydesc, bei Gleichstand ältester →default_value) — neu zu implementieren. Die Paket-Engine (evaluate.ts) macht Zeitfenster + Parent-Dependency + Rule-Tree, nicht die priorisierte Override/Filter/Default-Kaskade. tenantHasFlag(tenantId, key)(Block E) — neue Convenience-API mit: wirft bei unbekanntemkeyin Dev/Staging (Schema-Drift-Schutz), loggt im Prod; Boolean-Only; Request-scoped-Cache. Vorlage:featureFlagEvaluator.server.ts, aber andere Resolution.- Filter-Criteria-Matcher für
tenant_size/industry/status/subscription_product_id/created_*/id_in/id_not_in— neue Leaf-Evaluatoren (Pattern austargeting/evaluator.ts). - Live-Vorschau „X Tenants matchen" (Block D, §32.5.4) — neue Query gegen Tenant-Tabelle (Spec: Read-Replica). Komplett neu.
- Effektive-Auswertungs-Statistik „X aktiv / Y nicht" (Block C, §32.5.3, M033) — neue Aggregation über alle Tenants. Komplett neu.
- Bulk-Override „12 Tenants gleichzeitig" (Block C, §32.5.6, M033) — neue idempotente Action (Vorlage
IdempotencyKey/withIdempotency). Komplett neu. - Lifecycle-Wechsel-Workflow mit Bestätigungs-Dialog + Edge-Case-Warnungen (Block F, §32.5.7) + Archivierung/Reaktivierung (§32.5.8). Komplett neu (kein Lifecycle-Konzept in BP).
- Expires-At-Cron (Tenant-Override-Auto-Reset) — neuer Worker-Job. Komplett neu.
- Audit-Diff-History pro Flag im Detail-View (Block G/§32.7) —
FeatureFlagAuditexistiert als Tabelle, aber UI + Befüllung über alle Spec-Aktionen (Filter-, Override-, Lifecycle-, Archiv-Änderungen mit Diff) neu zu verdrahten; alternativAuditEvent(Querschnitt M21) nutzen.
UI (HiveGrid-spezifisch, komplett neu in packages/ui-hivegrid + app/routes)
- Listen-View mit Spalten/Filter/Suche/Sortierung/„Archivierte anzeigen"/Quick-Actions (Lifecycle, Archiv) — BP-
flags.tsxdeckt nur Teil ab. - Detail-View mit Sektionen Stammdaten/Filter/Tenant-Overrides/Statistik/Audit, Inline-Edit, Filter-Editor mit Criteria-Builder + Live-Vorschau, Tenant-Override-Editor mit Tenant-Autocomplete + Expires-At, Bulk-Override-UI, Lifecycle-Confirm-Dialog.
- Alle als View-Model (Loader) + reine UI-Component (kein Prisma-Import in
packages/ui-hivegrid/**), Subframe-Output via Adapter (DoD). - Sidebar-Eintrag in
packages/ui-hivegrid/src/app-shell/sidebar.config.ts+ Breadcrumbs + Smoke-Test (e2e/smoke/) — Infrastruktur existiert in HiveGrid noch nicht.
Permissions
- BP nutzt
admin.featureFlags.view/admin.featureFlags.manage. Spec verlangt 3 atomare Permissions (admin.feature_flags.read/write/lifecycle) + 4 Bundles (none/viewer/operator/owner). Mapping/Definition neu (final mit Modul 37 abzustimmen — offener Spec-Punkt).
Plattform-Admin-Fundament (Vorbedingung, außerhalb M32)
admin.hivegrid.de-Subdomain-App-Shell, Plattform-Admin-Auth (M034), Admin-User-Modell,AdminAuditLog/Cross-Tenant-Audit (M037). In Boilerplate liegt Admin als Bereich innerhalbapps/web— HiveGrid plant separate Admin-App-Surface (INTEGRATION-CONTRACTS §1: „separate Sidebar-Struktur M034-M041"). Diese Vorbedingungen sind nicht Teil von M32 selbst, aber blockierend.
Empfohlenes Vorgehen (Reihenfolge)
- Querschnitt 1:1 übernehmen (kein Neubau):
@toolctrl/foundation(audit, permissions, errors),@toolctrl/view-models,@toolctrl/i18n,@toolctrl/url,@toolctrl/shared-store,AuditEvent/IdempotencyKey(core.prisma). Das ist das stabile Fundament für DoD-Wiring. - Engine-Paket als Basis ziehen, Resolver anpassen:
@toolctrl/feature-flags(cache, publisher, client, percentage, targeting-Pattern) übernehmen; darüber einen HiveGrid-Resolver bauen, der die Spec-Reihenfolge (Override → priorisierte Filter → default) implementiert.tenantHasFlagals neue Convenience-API mit Schema-Drift-Schutz + Request-Cache (Vorlage:featureFlagEvaluator.server.ts). - Datenmodell neu/erweitert (M031-Fundament): HiveGrid-Prisma-Schema anlegen.
FeatureFlagum Spec-Felder erweitern (label, default_value, lifecycle_stage, owner/created_by, target_module, archived_at, key unique non-null),FeatureFlagFilterum (name, priority, value Boolean, criteria JSONB, active, created_by),TenantFeatureFlagneu. Forward-Only-Migration mit Inverse-SQL-Kommentar. Entscheiden: bestehende BP-FeatureFlagAuditnutzen oder Querschnitt-AuditEvent(Empfehlung:AuditEventfür plattformweite Durchsuchbarkeit, §32.7.2). - M031 — Schalter-Definition + Lifecycle: DB-Server-Layer (
.db.server.ts, Vorlage BP), Permissions (3 atomar + 4 Bundles), Routen Liste/Detail/New/Edit aufadmin.hivegrid.de(Vorlage BP-Routen-Struktur), Sidebar-Registry, Lifecycle-Wechsel mit Confirm + Audit. Form-Component aus BPFeatureFlagForm.tsxals Vorlage. - M032 — Filter-Editor + Einzel-Ausnahme: Criteria-Builder-UI + neue Leaf-Matcher (Pattern
targeting/evaluator.ts), Live-Vorschau-Query gegen Tenant-Tabelle, Tenant-Override-Editor mit Autocomplete + Expires-At, priorisierte Auswertung + Edge-Case-Klartext (Block D). Vorab klären: welche Tenant-Felder (tenant_size/industry/status) das HiveGrid-Tenant-Modell (M37) bereitstellt — BP-Tenanthat sie nicht. - M033 — Massen + Statistik + Audit: Bulk-Override (idempotent via
withIdempotency), Wirkungs-Statistik-Aggregation, Audit-History-Sektion im Detail-View, Archiv-/Reaktivierungs-Flow, Expires-At-Cron-Worker, Klartext-Hinweise (§32.9 / Block I). - Querschnitt-Wiring + DoD:
publishFeatureFlagInvalidationOncebei jeder Mutation, i18n-Keys durchgängig, Smoke-Tests pro Route, VM-Unit-Tests mit Zod, Visual-Regression. Permission-Denied → 404.
Kurzfazit: Das Querschnitt- und Engine-Gerüst (Paket + foundation) ist exzellent und großteils 1:1/als-Vorlage nutzbar — daher nicht „low". Aber das fachliche Kernmodell der Spec (Lifecycle + Tenant-Override + priorisierte Criteria-Filter + Owner/Audit-Diff + Statistik/Bulk) und die komplette Admin-UI sind in der Boilerplate nicht vorhanden bzw. konzeptuell abweichend → medium.