# 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/.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/.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/.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/.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/.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()` 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(toKontaktDetailVM(entity, context.user)); } export default function KontaktRoute() { const vm = useLoaderData(); return ; } ``` ## 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/.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('..')` | | Tenant-Scope | M004 | `withTenantScope()`, `withPermission()` | | Feature-Flag | M006 | `featureFlag('', 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//*.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_` · `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=`. 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/:` immutable + Alias-Tags); Coolify **pullt** nur — **kein** Build in Coolify. Rollback = altes `:` 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/` 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/.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: "**. 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.