333 lines
26 KiB
Markdown
333 lines
26 KiB
Markdown
# HiveGrid 2.0 — Agent-Regeln
|
||
|
||
Zentrale Regel-Datei für AI-Agents (Coding-Agents, ToolController-Worker), die an HiveGrid arbeiten.
|
||
|
||
**Letzte Aktualisierung:** 2026-06-19
|
||
**Architektur-Quelle:** Party-Mode-Diskussion mit Sally/Winston/Amelia, dokumentiert in `docs/milestones/README.md`.
|
||
|
||
## Arbeitsmodus & Wo-finde-ich-was (Stand 16.06.)
|
||
|
||
**Team — 2 Devs bauen PARALLEL** (+ David = Screens/Subframe, Dejan = Website, Haris = Vertrieb):
|
||
- **Raffael** — kritische Kern-Kette, SEQUENZIELL: P1 → P2 → P5 → P6 → P7 → P8 → P9 → P11 → P12 → P13.
|
||
- **Akeshan** — unabhängige Trabanten parallel dazu: Infra, P3, P4, P17, P19, dann (nach P5) P14, P15, P16, P20, P21.
|
||
- **Sync-Punkt:** P14/P15/P16 brauchen Raffaels P5 (Apps-Engine). **Parallel-Regel (AD-21):** jedes Modul bekommt eine eigene `prisma/schema/<modul>.prisma`; auf dem Feature-Branch `prisma db push`, **Migration erst beim Merge** — sonst Konflikte im Schema/in der Migrations-Historie. `core.prisma`/`accounts.prisma` nur für Relationen zu Tenant/User anfassen. Siehe §„Prisma-Schema & Migrationen".
|
||
|
||
**GSD = nur Infrastruktur, NICHT die Slash-Commands.** Wir nutzen die `.planning/`-Struktur + Codebase-Konventionen, arbeiten aber **ticket-getrieben** — KEINE `/gsd-plan-phase` / `/gsd-execute-phase`-Befehle.
|
||
|
||
**Ein Ticket bauen:** Du bekommst eine Ticket-ID → lies `.planning/ticket-docs/<ID>.md` (self-contained Bau-Spec: Was/Screens/DoD/Technische-Bausteine/Quellen/Abhängigkeiten) **und** diese AGENTS.md. Voller Einstiegs-Prompt + Designer-first-Regel (UI wartet auf „Davids Delta" im Screen-Ticket): **`.planning/ticket-docs/README.md`**. Vor dem Bauen die `blocked-by` aus der Doc prüfen. **Strikt im Ticket-/Ticket-Doc-Scope bleiben, Verbesserungen nur explizit ausgewiesen vorschlagen, Fortschritt als % der DoD melden** → siehe §„Ticket-Treue, Verbesserungs-Vorschläge & Fortschritt".
|
||
|
||
**Wo finde ich was:**
|
||
|
||
| Was | Wo |
|
||
|---|---|
|
||
| **Bau-Spec je Ticket (EINSTIEG)** | `.planning/ticket-docs/<ID>.md` (157 Tickets, je 1 verlinkte Datei) |
|
||
| Datenmodell — LOCKED, **Vorrang bei P6–P9** | `.planning/ENTITY-MODEL.md` (+ `entity-model.dbml`) |
|
||
| Phasen v1/v2 + Ziele/SC | `.planning/ROADMAP.md` · `.planning/ROADMAP-V2.md` |
|
||
| Phase → Milestone-Mapping | `.planning/PHASE-SOURCES.md` |
|
||
| Reihenfolge (Ticket an Start-Tag) | `.planning/SPRINT-DAYPLAN.md` |
|
||
| Requirements je Modul | `.planning/REQUIREMENTS.md` |
|
||
| Screen-Briefs (Designer) | `.planning/designer-briefs/PXX.md` |
|
||
| Architektur-Entscheidungen | `docs/ARCHITECTURE-DECISIONS.md` (u.a. AD-1 RLS · AD-17 PWA-only · AD-20 Projekt-zentrisch · AD-21 Prisma) |
|
||
| Live-Tickets/Status/Cycles | Plane: `project.tool-ctrl.de`, Projekt HiveGrid |
|
||
|
||
**Pivot-Vorrang:** Bei Widerspruch zwischen `ENTITY-MODEL.md` und den `_bmad-output/specs/M0x` (teils pre-pivot) gilt **immer das ENTITY-MODEL** (AD-20: ein `Projekt`-Kern, Pipelines=Views, „Deal"/„Auftrag"=Typen, Lead→Projekt, kein Won-Handoff). Die betroffenen Specs tragen oben einen 🔴 Pivot-Block.
|
||
|
||
## Plane-Tracking (Ticket-System — gleicher Standard für beide Devs)
|
||
|
||
**Wo:** self-hosted Plane `https://project.tool-ctrl.de`, Workspace `toolctrl`, **Projekt HiveGrid** = `35d0d968-8ab3-40cc-97b8-b81d917dc5df` (NICHT PowerLinked!). REST: `{BASE}/api/v1/workspaces/{WORKSPACE}/projects/{PROJECT}/…`, Auth-Header `X-API-Key`.
|
||
|
||
**Zugang — kanonisch über Environment-Variablen (identische Namen bei Raffael + Akeshan):**
|
||
`PLANE_API_KEY` (persönlich je Dev — **nie ins Repo/Git**) · `PLANE_BASE_URL=https://project.tool-ctrl.de` · `PLANE_WORKSPACE=toolctrl` · `PLANE_PROJECT=35d0d968-…`.
|
||
Fallback, wenn Env leer: dieselben 4 Keys aus `~/.cli-tokens/plane` (Zeilen `KEY=VALUE`).
|
||
|
||
**Wer wird imitiert? (vor jeder Schreibaktion prüfen):** Der `PLANE_API_KEY` ist persönlich → Kommentare/Assignee erscheinen unter *diesem* User. Identität verlässlich über `GET {BASE}/api/v1/users/me/` (liefert den User des Keys). **Nicht** an der git-Mail festmachen (Maschine kann `node1@…` sein, Plane-User aber `raffael@…`). Ergebnis muss zum Dev der Maschine passen → bei Mismatch STOP + melden.
|
||
**Member-IDs:** raffael `8e34a88c…` · akeshan `8bf43e86…` · david `6c1fc63f…` · dejan `a5883f5e…` · haris `48367c1f…`.
|
||
|
||
**Skripte:** `.planning/plane-sync/` — `dump_snapshot.py` (read-only Snapshot), Pflege idempotent via `scrub_plane.py`. Module=Tracks, Cycles=Wochen, blocked-by=JIT.
|
||
|
||
**Ticket abschließen (verbindliche Reihenfolge):** 1) `.planning/ticket-docs/<ID>.md`: Umsetzungs-Notiz mit **Datum + kurzem Proof** (Commits/Tests/Deploy-Run) + DoD abhaken + **%-Done-Stand** (siehe §„Ticket-Treue, Verbesserungs-Vorschläge & Fortschritt"). 2) Plane-Kommentar — **schlicht, 1. Person (= imitierter User), kein AI-Sprech, keine Em-Dashes (—)**. 3) Ticket auf **Done** — nur bei **100 % DoD**.
|
||
|
||
**Übersichten:** Done-Tickets ausblenden, nur offene Arbeit zeigen.
|
||
|
||
## Ticket-Änderungen dependency-aware (verbindlich)
|
||
|
||
Vor jeder maßgeblichen Ticket-Änderung (Start-/Zieldatum, Scope, Assignee/Owner) **oder** Code, der `blocks`-Tickets betrifft (Contracts aus `R-P1a`, `core.prisma`, Querschnitt-Services M001–M017):
|
||
1. **Beide Richtungen lesen:** `blocked-by` UND `blocks` — in Plane *und* in den `.planning/ticket-docs/`-Docs der Abhängigen.
|
||
2. **Abhängige nicht brechen:** Änderung so wählen, dass blockierte/folgende Tickets konsistent bleiben (z.B. Start-Datum nie hinter Ziel-/Vorgänger-Datum schieben; keine Contract-/Schema-Änderung ohne Nachzug der Konsumenten).
|
||
3. **Auswirkung dokumentieren** — in der eigenen Ticket-Doc *und* im betroffenen abhängigen Ticket (Muster: AD-23 → Eintrag in `AK-P4a`).
|
||
4. **„Fertig" heißt:** Abhängige bleiben grün (Tests/Typecheck) — proaktiv prüfen, nicht erst bei Bruch reagieren.
|
||
|
||
## Lokale Dev-Secrets (SOPS + age)
|
||
|
||
Geteilte Dev-`.env`-Werte liegen **verschlüsselt** in `secrets/dev.sops.env` (SOPS+age, beide Devs als Recipients). `pnpm env:pull` entschlüsselt nach `./.env`, `pnpm env:edit` ändert sie. Onboarding/Regeln: `secrets/README.md`. Der age-Private-Key bleibt lokal (`~/.config/sops/age/keys.txt`), **nie ins Repo**. Nur geteilte API-Keys liegen im Blob; lokale DB/Redis = Defaults aus `.env.example`.
|
||
|
||
## Die Boilerplate IST deine Codebase — kritisch
|
||
|
||
**Du baust HiveGrid NICHT from scratch.** Das `HiveGrid-Cloud/`-Repo enthält die EU-sterilisierte Boilerplate als Fundament. Das ist DEIN Code, du editierst, erweiterst und löschst ihn — du klonst nichts, du importierst nichts „von außen".
|
||
|
||
Konkret:
|
||
- `apps/web/`, `apps/api/`, `apps/worker/`, `packages/db/`, `packages/mail/`, `packages/queue/`, `packages/ui/`, alles weitere → das ist deine Arbeitsbasis
|
||
- `package.json` mit pnpm-Workspace, Prisma, BullMQ, MailerSend, shadcn/Radix → bestehender Stack, den du nutzt
|
||
- Du erweiterst, was fehlt (z. B. neue `packages/view-models/`, `packages/ui-app/`, neue Routes)
|
||
- Du löschst, was nicht zu HiveGrid gehört (Affiliate-Reste, Marketing-Demo-Inhalte aus dem ToolCTRL-Boilerplate, falls noch vorhanden)
|
||
- Du baust neue Module **in** dieser Codebase, nicht **neben** ihr
|
||
|
||
**Pre-Flight-Check beim Start (jedes Mal):**
|
||
1. `package.json` im Repo-Root existiert mit pnpm-Workspace-Konfig → wenn nein: STOP, melde „Boilerplate-Inhalt fehlt"
|
||
2. `apps/`, `packages/`, `prisma/` existieren → wenn nein: STOP
|
||
3. `pnpm typecheck && pnpm test` läuft grün → wenn nein: STOP
|
||
4. Keine US-Provider-Imports mehr (`grep -rE "@ai-sdk/openai[^-]|@ai-sdk/anthropic|remix-auth-google" --include="*.ts"` leer) → wenn nein: STOP, EU-Sterilisation nicht abgeschlossen. (Das `[^-]` lässt `@ai-sdk/openai-compatible` durch — das ist der EU-taugliche provider-agnostische SDK für Mistral/Scaleway, KEIN US-Import.)
|
||
|
||
Erst danach beginnst du mit dem ersten Milestone.
|
||
|
||
## Naming-Konvention (Stand 2026-06-04, siehe `docs/ARCHITECTURE-DECISIONS.md` AD-10)
|
||
|
||
**Wichtige Korrektur zu „benenne frei um":** Dieses Repo ist ein **git-Downstream** der Boilerplate
|
||
(`upstream` = `git@git.tool-ctrl.de:toolctrl-labs/boilerplate.git`). Boilerplate-Verbesserungen werden
|
||
via `git merge upstream/main` eingespielt. Deshalb wird **NICHT** umbenannt:
|
||
|
||
- **Geteilte Boilerplate-Pakete behalten `@toolctrl/*`** (z.B. `@toolctrl/foundation`,
|
||
`@toolctrl/view-models`, `@toolctrl/db`, `@toolctrl/search`) — mergebar mit upstream.
|
||
- **UI-Pakete heißen real `packages/ui-app`, `packages/ui-subframe`, `packages/ui-mock`** —
|
||
NICHT `packages/ui-hivegrid`. Wo Docs/PLANs „`packages/ui-hivegrid`" sagen, ist **`packages/ui-app`** gemeint.
|
||
- **`@hivegrid/*` nur für genuin NEUE HiveGrid-Pakete**, die upstream nie existieren (z.B. das
|
||
neue Activity-Paket). Diese kollidieren beim Merge nicht.
|
||
- Pfad-Korrekturen ggü. älteren Docs: Sidebar liegt in `apps/web/app/application/sidebar/`
|
||
(nicht `sidebar.config.ts`); Such-Index/`indexedEntities` gehört nach `packages/search/`
|
||
(nicht `packages/db/src/search/indexed-entities.ts`).
|
||
|
||
Die DoD-, Anti-Pattern- und Workflow-Pfade unten nennen ab hier bereits die realen Pfade (`packages/ui-app`, `apps/web/app/application/sidebar/`, `packages/search/`); nur Punkt oben behält `ui-hivegrid` als Mapping-Hinweis für ältere Docs/PLANs.
|
||
|
||
## Pflichtenheft als Quelle der Wahrheit
|
||
|
||
Verbindliches Pflichtenheft: `_bmad-output/pflichtenheft/` v1.1 (Stand 2026-05-09).
|
||
|
||
Master-Milestone-Liste in `docs/milestones/` ist die ausführungsreife Übersetzung in 176 Milestones. Pro Milestone ein Verzeichnis mit `PLAN.md`.
|
||
|
||
**Progress-Tracking:** `docs/PROGRESS-TRACKER.md` ist die Live-Übersicht. Du updatest sie nach JEDEM Status-Wechsel:
|
||
- Milestone-Start: Status auf `🔵 IN-ARBEIT`, Detail-Sektion gemäß Vorlage anlegen
|
||
- Slices abschließen: Slice-Liste abhaken mit Commit-Hashes
|
||
- Alle DoD-Punkte grün: Status auf `🟡 READY-TO-VERIFY`, warten auf User-Sign-Off
|
||
- User trägt `🟢 SIGNED-OFF` ein → erst DANN nächstes Milestone starten
|
||
|
||
Bei Konflikt zwischen Detail-Spec (`_bmad-output/specs/M*.md`) und Pflichtenheft gilt **das Pflichtenheft v1.1**.
|
||
|
||
## Definition of Done — verbindlich für JEDES Milestone
|
||
|
||
Ein Milestone gilt **nur dann als erledigt**, wenn ALLE folgenden Punkte wahr sind:
|
||
|
||
### A. Code-Layer-Trennung (PR-Block bei Verstoß)
|
||
|
||
1. Loader/Action in `app/routes/**` baut View-Model. KEIN Direkt-Import von `~/db` oder `@prisma/client` in `packages/ui-app/**`.
|
||
2. Domain-Logik in `app/domain/**` importiert NIE React oder UI.
|
||
3. View-Models in `packages/view-models/**` sind pure Funktionen ohne React/Prisma-Imports.
|
||
4. Subframe-Output in `packages/ui-subframe/**` wird NIE direkt von `app/routes/**` importiert — nur via Adapter in `packages/ui-app/src/adapters/`.
|
||
5. UI-Components rufen NIE `useLoaderData` / `useFetcher` auf — Routes injizieren VM und Action-Callbacks.
|
||
|
||
### B. Erreichbarkeit (Reachability — siehe `docs/INTEGRATION-CONTRACTS.md`)
|
||
|
||
6. **Sidebar-Eintrag** in `apps/web/app/application/sidebar/` (AppSidebar/AdminSidebar + SidebarItem) — oder Inline-Verlinkung aus bestehendem Modul, wenn das semantisch korrekt ist.
|
||
7. **Permission-Gate** auf dem Sidebar-Eintrag — Eintrag NUR sichtbar bei berechtigter Rolle.
|
||
8. **Route registriert** unter dem korrekten URL-Pattern (siehe INTEGRATION-CONTRACTS.md §3).
|
||
9. **Breadcrumbs** für Detail-Routes (siehe §4).
|
||
10. **i18n-Keys** für ALLE UI-Texte (Pflicht via M003-Build-Lint).
|
||
11. **Mobile-Tab-Bar-Eintrag**, falls das Modul mobile-relevant ist (§13).
|
||
12. **Customer-Portal-Konfiguration**, falls Endkunden das sehen sollen (§14, Default unsichtbar).
|
||
|
||
### C. Querschnitt-Wiring (Pflicht-Service-Aufrufe)
|
||
|
||
13. **Schreib-Aktion** ist auditiert (auto via M001-Prisma-Middleware oder manuell via `auditEvent`).
|
||
14. **Activity-Stream-Eintrag** auf relevanter Entität (Kontakt/Firma/Lead/Verkaufsfall/Auftrag) bei jeder relevanten Aktion via `logActivity`.
|
||
15. **Notifications** via M011-Service (`notify({...})`), niemals direkt Email/Push.
|
||
16. **Search-Index** registriert in `packages/search/` (indexed-entities-Registry — M00-Neubau), falls suchbar.
|
||
17. **Permission-Filter** via `withTenantScope()` + `withPermission()` (M004) — kein manueller Query-Filter im Modul-Code.
|
||
|
||
### D. Tests (PR-Block ohne)
|
||
|
||
18. **Unit-Tests** für View-Model-Builder (pure-function-test mit Fixture).
|
||
19. **Smoke-Test** in `e2e/smoke/<modul>.spec.ts` — Login → Route öffnen → Hauptaktion klicken → erwartetes Ergebnis sichtbar.
|
||
20. **Hot-Swap-Drill** (M017): Smoke-Tests müssen mit `packages/ui-mock` als Drop-in-Replacement laufen.
|
||
21. **Visual-Regression-Snapshot** (M016) für die Hauptansicht.
|
||
|
||
### E. Klartext-Hinweise (User-facing)
|
||
|
||
22. ALLE Fehler-Texte aus M002-Catalog (kein nackter HTTP-Code, keine kryptische Stack-Trace im UI).
|
||
23. **Empty-State** mit Klartext + nächster Aktion.
|
||
24. **Loading-State** mit Klartext (kein leerer Spinner).
|
||
25. **Permission-Denied** zeigt 404, nicht 403 (siehe Bauprinzip A.5.3).
|
||
|
||
### F. Dokumentation
|
||
|
||
26. PLAN.md des Milestones aktualisiert mit Lessons Learned, falls relevant.
|
||
27. **Rollback-Pfad dokumentiert** im Slice-Plan (siehe `docs/MID-STREAM-CHANGES.md`).
|
||
28. **Forward-Only-DB-Migration** mit Inverse-SQL als Kommentar im Migration-File.
|
||
|
||
---
|
||
|
||
## Drei Anti-Patterns — sofortiger PR-Block
|
||
|
||
1. `@prisma/client` oder `~/db` in `packages/ui-app/**`
|
||
2. Direktimport aus `packages/ui-subframe` in `app/routes/**`
|
||
3. `useLoaderData<typeof loader>()` innerhalb von `packages/ui-app/**`
|
||
|
||
## Daten-Fluss (verbindlich)
|
||
|
||
**Synchron im Loader.** Server bereitet das View-Model einmal auf, UI rendert. KEIN client-seitiges Re-Compute.
|
||
|
||
```ts
|
||
// app/routes/kontakte.$id.tsx
|
||
export async function loader({ params, context }: LoaderArgs) {
|
||
const entity = await context.db.kontakt.findUniqueOrThrow({ where: { id: params.id! } });
|
||
return json<KontaktDetailVM>(toKontaktDetailVM(entity, context.user));
|
||
}
|
||
export default function KontaktRoute() {
|
||
const vm = useLoaderData<typeof loader>();
|
||
return <KontaktDetailPage vm={vm} actions={{ onEdit, onArchive }} />;
|
||
}
|
||
```
|
||
|
||
## Prisma-Schema & Migrationen (verbindlich — AD-21)
|
||
|
||
Das Schema ist ein **Ordner** (`prisma/schema/`, Prisma 7), nicht eine Datei. Bei parallelen Branches gilt:
|
||
|
||
- **Ein Modul = eine eigene `prisma/schema/<modul>.prisma`** (viele existieren: `appsEngine`, `email`, `workflows`, `notifications`, `permissions`, `marketingLeads` …). Neue Modelle **NIE** in `core.prisma`/`accounts.prisma` — das ist der geteilte Kern (Tenant/User/Audit/File/ApiKey/Credential). Den nur für Relationen zu `Tenant`/`User` berühren, Rück-Relationsfelder minimal halten.
|
||
- **Feature-Branch: `prisma db push`** (kein Migrations-File). Die **eine** Migration pro Phase wird beim Merge auf `main` erzeugt — Forward-only + Inverse-SQL als Kommentar (DoD 28). Verhindert divergierende Timestamp-Historie bei parallelen Branches.
|
||
- Vor Merge: `prisma validate` + `prisma migrate status` grün. URLs liegen in `prisma.config.ts` (`DIRECT_URL` Migrate, `DATABASE_URL`/PgBouncer Runtime).
|
||
- **Migration muss aus dem Nichts reproduzierbar sein** (`infra/LESSONS.md §9`): CI testet `prisma migrate deploy` gegen eine **leere** DB — nur inkrementell auf Prod angewandte Migrations können bei Staging/DR/neuem Dev-Setup in falscher Reihenfolge brechen (gleicher Datums-Prefix sortiert lexikografisch, nicht nach Dependency → P3018). Order-abhängige DDL/DML in `DO $$ … IF [NOT] EXISTS … $$`-Blöcke guarden. Eine bereits auf Prod angewandte Migration **nie** umbenennen, ohne `_prisma_migrations.migration_name` nachzuziehen.
|
||
|
||
## EU-Stack (nicht verhandelbar — Bauprinzip A.12)
|
||
|
||
- **KEIN** `@ai-sdk/anthropic`, `@ai-sdk/openai`, `openai`
|
||
- **KEIN** `remix-auth-google`
|
||
- **KEIN** Apple-/Google-/Microsoft-Login (Phase 1)
|
||
- **JA** Mistral primär, Scaleway sekundär, AlephAlpha tertiär (M083, M084)
|
||
- **JA** Email+Passwort + Magic-Link + TOTP (M020, M021, M022)
|
||
- Hosting: Hetzner + Coolify (EU); Datenbank: in EU; Storage: Hetzner Object Storage / S3 (EU) — vgl. ADR-009 (löst den früheren Scaleway-Hosting-Plan ab)
|
||
|
||
## Querschnitt-Hülle nutzen, nicht nachbauen
|
||
|
||
| Concern | Querschnitt-Milestone | Service-Import |
|
||
|---|---|---|
|
||
| Audit-Log default | M001 | auto via Prisma-Middleware; manuell `auditEvent` |
|
||
| Klartext-Diagnostik | M002 | `errorCatalog`, `ErrorBoundary` |
|
||
| i18n | M003 | `t('<modul>.<bereich>.<key>')` |
|
||
| Tenant-Scope | M004 | `withTenantScope()`, `withPermission()` |
|
||
| Feature-Flag | M006 | `featureFlag('<key>', tenant)` |
|
||
| Idempotenz (BullMQ) | M007 | `withIdempotency()` |
|
||
| Apps-Engine-Adapter | M008-M010 | `defineAdapter()` |
|
||
| Notifications | M011 | `notify({...})` |
|
||
| View-Model-Layer | M013 | Type + Zod-Schema in `packages/view-models/src/<domain>/*.vm.ts` |
|
||
| Action-Callback-Contract | M014 | Standard-Shape `{ onX: (input) => Promise<...> | void }` |
|
||
| Subframe-Adapter | M015 | Adapter in `packages/ui-app/src/adapters/` |
|
||
| Visual-Regression | M016 | Playwright Component-Test mit Fixture-VM |
|
||
| Hot-Swap-Drill | M017 | `pnpm test:hot-swap` |
|
||
|
||
Wenn ein Modul Cross-Cutting-Concern selbst nachbaut → PR abgelehnt.
|
||
|
||
## Estate-Infrastruktur — verbindliche Regeln (Quelle: `infra/`)
|
||
|
||
HiveGrid ist **ein** Workload in einem geteilten Estate (neben mITapp + Marketing-Sites). **Quelle der Wahrheit ist das `infra/`-Repo** (eine Ebene über diesem Repo): `infra/README.md` (Bestand), `infra/STANDARDS.md` (der „so wird jeder SaaS aufgesetzt"-Vertrag), `infra/decisions/` (ADRs), `infra/LESSONS.md` (Footguns). Bei Widerspruch gewinnt `infra/STANDARDS.md`. Übersichts-Diagramm + Steckbriefe: `local/infra-diagrams/06-estate-infrastructure.{mmd,md}`.
|
||
|
||
**Goldene Regel: geteilte Estate-Services NUTZEN, nie pro-Produkt nachbauen** (analog zur Querschnitt-Hülle oben, nur eine Ebene tiefer — STANDARDS §1–§5/§13/§14). HiveGrid bekommt den Slug **`hivegrid`** (Sentry-Team, Meili-Index-Prefix, Cloudflare-Zone, Coolify-/Hetzner-Projekt `HiveGrid`).
|
||
|
||
| Concern | Zentral (mesh-only, wenn nicht anders genannt) | App-Env / Vertrag |
|
||
|---|---|---|
|
||
| Errors (STANDARDS §1) | Sentry `sentry.saas-ctrl.com` — 1 Team `hivegrid`, 1 Projekt je Codebase (`web`/`api`/`worker`) | `SENTRY_DSN_<APP>` · `SENTRY_ENVIRONMENT=staging\|production` · `SENTRY_RELEASE=$GIT_SHA`. Staging↔Prod = **Tag**, nicht eigenes Projekt. Browser-DSN als `VITE_SENTRY_*` **Build-Arg** (Coolify-Env ist nur Runtime). |
|
||
| FTS (STANDARDS §4, AD-5) | Meilisearch `10.42.0.4:7700` | `MEILI_URL=http://10.42.0.4:7700` · `MEILI_API_KEY=<hivegrid-scoped>`. Indizes **`hivegrid-*`**-prefixed, **nie** geteilt, **nie** Master-Key in der App. |
|
||
| Upload-AV (STANDARDS §13) | ClamAV `10.42.0.4:3310`, **INSTREAM** (kein Path-Scan, kein shared FS) | `CLAMD_HOST=10.42.0.4` · `CLAMD_PORT=3310`. Failure-Policy explizit: **fail-closed** für untrusted Uploads. >100 MB vorab ablehnen. |
|
||
| NSFW-Bilder (STANDARDS §14) | nsfw-api `10.42.0.4:3000`, `POST /classify` raw bytes | `NSFW_URL=http://10.42.0.4:3000`. Failure-Policy: **fail-open** (Gegenteil von AV). |
|
||
| Logs (STANDARDS §2) | Loki `10.42.0.4:3100` via Promtail (per Host) | App loggt **strukturiertes JSON nach stdout/stderr**, keine Secrets (Loki-Labels sind global queryable). |
|
||
| Metrics (STANDARDS §2/§3) | Prometheus+Grafana auf `control` (`grafana.saas-ctrl.com`) | App-`/metrics` optional, aber **bearer-token-geschützt**. Pflicht-Labels `project/environment/server/service`. |
|
||
| Admin-SSO (STANDARDS §5, AD-3) | Authentik `auth.tool-ctrl.de` (OIDC) | Nur **Operator/Admin**-Logins über Authentik. SaaS-Endkunden bleiben in der eigenen User-Tabelle. |
|
||
|
||
> Mesh-only-Services (`10.42.0.4`) haben **keine** Auth — Sicherheit = WireGuard-Mesh-Bind + UFW. Erreichbar nur als Mesh-Peer; **nie** einen Public-Port dafür fordern.
|
||
|
||
**Deploy & Repo (STANDARDS §6–§9, ADR-002/005/010):**
|
||
- **Coolify** ist das Deploy-Target (kein K8s/Terraform). Hierarchie: **Projekt = Produkt** (`HiveGrid`, TitleCase) · **Environment = Phase** (`production`/`staging`, lowercase) · **Resource = Service** (`hivegrid-web`/`-api`/`-worker`, kebab-case). Ein zentrales Coolify auf `control` steuert alle Hosts.
|
||
- **Build-once / deploy-via-pull:** CI (Gitea-Runner `toolctrl-ci`) baut **ein** Image, pusht in die **Gitea Package Registry** (`git.tool-ctrl.de/toolctrl-labs/<image>:<sha>` immutable + Alias-Tags); Coolify **pullt** nur — **kein** Build in Coolify. Rollback = altes `:<sha>` neu deployen.
|
||
- **CI triggert Coolify mit `POST /api/v1/deploy?uuid=…&force=true`** — NICHT `/restart` (das reuset das gecachte Image und shipped still alten Code, `infra/LESSONS.md §10`).
|
||
- Repo unter `toolctrl-labs/<name>` in **Gitea**, **private by default**, mit `Dockerfile` + `.gitea/workflows/`. Clone von Cloud-Hosts via **HTTPS+PAT** (Gitea-SSH:2222 ist von Hetzner unerreichbar, ADR-003).
|
||
- **DNS in Cloudflare** (SSL-Mode strict): A-Record `proxied=false` bis das LE-Cert steht, danach `true`. Mail: **OUT = Mailjet**, **IN = mailbox.org**.
|
||
|
||
**Secrets & Backups (STANDARDS §9/§10):**
|
||
- Secrets **nie** ins Repo / in Chat / in CI-Logs. Per-User-Tokens in `~/.cli-tokens/` (chmod 600); geteilte Dev-`.env` verschlüsselt via SOPS+age (`secrets/dev.sops.env`, `pnpm env:pull`).
|
||
- Ein **stateful** Service (Postgres/Redis-Persistenz/Index) darf **nicht** Prod-Traffic bekommen, bevor das Backup in `infra/runbooks/` dokumentiert ist (wohin, wie oft, Restore, letztes Restore-Drill-Datum).
|
||
|
||
Konkret tun (Host hinzufügen, Recovery, Secret-Rotation, neue Marketing-Site) → `infra/runbooks/*.md`.
|
||
|
||
## Subframe-Workflow
|
||
|
||
1. Designer baut Screen in Subframe
|
||
2. User pulled Subframe-Code → landet in `packages/ui-subframe/src/**`
|
||
3. AI-Coding-Agent integriert via Adapter-Layer in `packages/ui-app/src/adapters/**`
|
||
4. Routes importieren nur `packages/ui-app`, nie `ui-subframe` direkt
|
||
5. Visual-Regression-CI (M016) prüft auf unbeabsichtigte Diffs
|
||
6. Hot-Swap-Drill (M017) bestätigt: Tausch gegen `ui-mock` bricht nichts
|
||
|
||
## Anti-Liste respektieren — Bauprinzip A.9
|
||
|
||
Was nicht im Pflichtenheft v1.1 steht, ist nicht beauftragt. Vollständige Anti-Liste in `_bmad-output/pflichtenheft/02-vision.md` V.3.
|
||
|
||
Versucht ein Code-Vorschlag, einen Anti-Listen-Punkt umzusetzen → **PR abgelehnt**. Erweiterung nur über Change-Request-Workflow (`docs/CHANGE-REQUESTS.md`, M176).
|
||
|
||
## Ticket-Treue, Verbesserungs-Vorschläge & Fortschritt (verbindlich)
|
||
|
||
**1. Strikt am Ticket bauen — nichts erfinden.** Gebaut wird **ausschließlich**, was in der Ticket-Doc
|
||
`.planning/ticket-docs/<ID>.md` (Was / Screens / **DoD** / Technische-Bausteine) **und** im Pflichtenheft v1.1
|
||
steht. Verboten sind: erfundene Tasks, „während ich hier bin"-Scope, ungefragte Refactors und **vorgezogene
|
||
Arbeit aus Folge-Tickets/-Phasen** (z.B. flächiger Roll-out, wo das Ticket nur den Nachweis verlangt). Ist die
|
||
Ticket-Doc lückenhaft, mehrdeutig oder widersprüchlich → **Stop + Rückfrage** (siehe §„Bei Unklarheit"), kein
|
||
Auto-Ausbau. Alles, was über das Ticket hinausginge, läuft über den Change-Request-Workflow
|
||
(`docs/CHANGE-REQUESTS.md`, M176). Maßstab ist die **DoD der Ticket-Doc** — nicht das, was technisch „auch noch
|
||
ginge".
|
||
|
||
**2. Verbesserungen sind willkommen — aber IMMER explizit ausgewiesen, nie still gebaut.** Du **darfst** (am
|
||
Ende und gerne auch mittendrin) vorschlagen, was man besser/sicherer/sauberer machen könnte. Solche Hinweise
|
||
**müssen klar als Vorschlag außerhalb des Ticket-Scopes markiert** sein — Format: eine Zeile/Block mit Präfix
|
||
**„💡 Vorschlag (außerhalb Ticket-Scope): …"** + kurzer Begründung (Nutzen/Aufwand/Risiko). Erst nach
|
||
**explizitem OK** des Users wird daraus Arbeit (i.d.R. neues Ticket / CR). **Niemals** einen Vorschlag direkt in
|
||
Code/Commit ziehen, nur weil er sinnvoll wirkt — das ist ein PR-Block wie ein Anti-Listen-Verstoß.
|
||
|
||
**3. Fortschritt in % ausweisen — bis das Ticket auf 100 % steht.** Bei **jeder** Status- oder Abschluss-Meldung
|
||
zu einem Ticket den Stand als **% done** angeben, abgeleitet aus den DoD-Punkten der Ticket-Doc:
|
||
`% = abgehakte DoD-Punkte / DoD-Punkte gesamt`. Format z.B.: **„R-P2a: 6/7 DoD = ~86 % done — offen: <Punkt>"**.
|
||
DoD-Punkte, die **extern blockiert** sind (David-Screens, Partner-Input, Folge-Phase), als **`blockiert`**
|
||
kennzeichnen und vom **selbst machbaren** Rest trennen (z.B. „86 % done · 1 Punkt offen, davon 1 blockiert →
|
||
0 selbst machbar"). Den %-Stand zusätzlich in die **Umsetzungs-Notiz der Ticket-Doc** schreiben. Erst bei
|
||
**100 % (alle DoD grün, nichts mehr selbst machbar offen)** ist das Ticket abschlussreif → §„Ticket abschließen".
|
||
|
||
## Klartext-Diagnostik — Bauprinzip A.1
|
||
|
||
- Kein `throw new Error("401 Unauthorized")` — stattdessen Catalog-Fehler aus M002
|
||
- Kein hartkodierter UI-Text — stattdessen i18n-Key aus M003
|
||
- Jede Fehler-Meldung beschreibt: was passiert ist, was zu tun ist, welcher Knopf hilft
|
||
|
||
## Kommentar-Sprache (Code = Englisch, Dokus = Deutsch)
|
||
|
||
**Code-Kommentare werden auf Englisch geschrieben** — passe dich der umgebenden Datei an, und
|
||
der Code-Bestand ist praktisch durchgaengig englisch kommentiert (Messung 2026-06-19: nur ~1% der
|
||
Quelldateien mit Kommentaren enthalten Deutsch, meist Alt-Bestand). Das gilt fuer `*.ts`/`*.tsx`,
|
||
`*.prisma` sowie SQL/Migrations in `prisma/**`. Grund: dies ist ein git-Downstream der
|
||
`@toolctrl`-Boilerplate (siehe Naming-Konvention) — englische Kommentare bleiben mergebar und
|
||
konsistent mit upstream.
|
||
|
||
**Deutsch bleibt nur in den Projekt-Dokus:** `.planning/**` (inkl. `ticket-docs/`),
|
||
`docs/**`, `_bmad-output/**`, `local/**`-Planungsartefakte sowie die Umsetzungs-Notizen und
|
||
Plane-Kommentare. Diese sind bewusst deutsch und bleiben deutsch.
|
||
|
||
Faustregel: liegt die Datei im ausfuehrbaren Code (`apps/**`, `packages/**`, `prisma/**`) →
|
||
Kommentare Englisch. Liegt sie in einem Doku-/Planungs-Ordner → Sprache der Doku (meist Deutsch).
|
||
|
||
## RR7-Spezifika
|
||
|
||
- **`.server.ts`-Re-Export ist verboten** (User-Memory). Nutze stattdessen Component-Extraktion.
|
||
- Loader/Action sind die einzigen Pfade in `app/domain/**`. Keine Direkt-DB-Calls aus Components.
|
||
|
||
## Mid-Stream-Korrektur
|
||
|
||
Bei falschem Code: siehe `docs/MID-STREAM-CHANGES.md` für Eskalations-Stufen und Rollback-Pfad.
|
||
|
||
## Bei Unklarheit
|
||
|
||
Stop, Frage an User, kein Auto-Refactor. Dokumentiere offene Frage in der PR-Beschreibung.
|