Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/AGENTS.md
T

46 KiB
Raw Blame History

HiveGrid 2.0 — Agent-Regeln

Zentrale Regel-Datei für AI-Agents (Coding-Agents, HiveGrid-Worker), die an HiveGrid arbeiten.

Letzte Aktualisierung: 2026-08-02 Architektur-Vertrag: Diese Datei enthält die operativen Regeln; .planning/ENTITY-MODEL.md besitzt das Datenmodell. docs/ARCHITECTURE-DECISIONS.md ist das kompakte Entscheidungsregister mit IDs, Gründen und Status, aber kein zweites Regelwerk.

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 entfällt nach AD-26 (kein v1-KI-Layer); v2-Neuschnitt ist VR-MCP/VD-MCP.
  • 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".

Arbeitsmodus: Wir arbeiten ausschließlich ticket-getrieben über Plane und die zugehörigen Ticket-Docs.

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. Interim-UI-Regel (AD-28): UI-lastige Tickets bleiben nicht bis zu Davids Delta auf „On Hold" — der Dev baut eine Interim-UI (Vorschau-Screen) aus ~/ui (VM als Prop, kein handgeschriebenes Tailwind) und stellt das Ticket damit fertig; die finale Subframe-Optik wird ein getrackter Nachzug (Label design-nachzug), nicht Done-blockierend. Details: .planning/INTERIM-UI-PLAYBOOK.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". Die Arbeit während der Umsetzung an atomaren Checkpoints committen → siehe §„Atomare Commit-Checkpoints". Vor jedem Commit die passenden fokussierten Checks, vor jedem Push die vollständige operative Checkliste docs/AGENT-CHECKLIST.md abarbeiten (lokal typecheck+lint+test grün, passende verify:*, Footguns).

Wo finde ich was:

Was Wo
Bau-Spec je Ticket (EINSTIEG) .planning/ticket-docs/<ID>.md (157 Tickets, je 1 verlinkte Datei)
Ticket ohne Codeänderung planen .agents/skills/plan-ticket/SKILL.md
Subframe-Seiten/-Deltas integrieren .agents/skills/subframe-einbinden/SKILL.md
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
Reihenfolge (Ticket an Start-Tag) .planning/SPRINT-DAYPLAN.md
Requirements je Modul .planning/REQUIREMENTS.md
Screen-Briefs (Designer) .planning/designer-briefs/PXX.md
Architektur-Vertrag (operativ) diese AGENTS.md + .planning/ENTITY-MODEL.md
Entscheidungsregister / Warum docs/ARCHITECTURE-DECISIONS.md
Pre-Push/Pre-Merge-Checkliste docs/AGENT-CHECKLIST.md (lokal grün: typecheck+lint+test · verify:* · Footguns)
Live-Tickets/Status/Cycles Plane: project.tool-ctrl.de, Projekt HiveGrid

Agent-Skills: Der kanonische Workflow liegt unter .agents/skills/. Claude Code entdeckt Projekt-Skills ausschließlich unter .claude/skills/; dort liegen deshalb nur kleine, getrackte Discovery-Shims, die auf den kanonischen Workflow verweisen. Workflow-Inhalte nie doppelt pflegen.

Datenmodell-Vertrag: .planning/ENTITY-MODEL.md ist alleinige Quelle für das Projekt-zentrische Modell (AD-20: ein Projekt-Kern, Pipelines=Views, „Deal"/„Auftrag"=Typen, Lead→Projekt, kein Won-Handoff).

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.

Session-Isolation mit Worktree und Branch (verbindlich)

Jede neue mutierende Dev-/Agent-Session arbeitet in einem neuen Worktree auf einem neuen Branch. Ein bestehender Worktree oder Branch wird nicht für ein anderes Ticket, einen neuen Auftrag oder einen Folge-Fix wiederverwendet. Rein lesende Diagnose darf in einem vorhandenen Worktree laufen; vor der ersten Dateiänderung muss die Session isoliert werden.

Start aus einem vorhandenen Clone:

pnpm session:start -- <type>/<kurzer-slug>

Der Helfer fetcht den aktuellen origin/main, verweigert bereits vorhandene lokale oder Remote-Branches, legt einen neuen Geschwister-Worktree mit neuem Branch an, entschlüsselt die .env atomar und installiert mit pnpm install --frozen-lockfile. Danach arbeitet die Session ausschließlich im ausgegebenen Worktree. Ein optionaler Zielpfad ist mit --path <absoluter-pfad> möglich.

Die .env-Hydrierung setzt auf macOS/Linux automatisch den kanonischen Age-Key ~/.config/sops/age/keys.txt als SOPS_AGE_KEY_FILE, schreibt erst nach erfolgreicher Entschlüsselung und ersetzt .env atomar mit Modus 0600. Ein SOPS-/Key-Fehler darf weder eine leere .env noch einen halb eingerichteten Session-Worktree hinterlassen.

Ein Session-Worktree wird erst nach dem PR-Abschluss aufgeräumt:

pnpm session:finish -- <type>/<kurzer-slug>

Der Cleanup ist fail-closed und darf nur fortfahren, wenn der Worktree sauber ist, der lokale und gegebenenfalls Remote-Head identisch sind, der Feature-Head Vorfahr des aktuellen origin/main ist und ein normaler Merge-Commit den Feature-Head als Nicht-First-Parent aufgenommen hat. Squash-, Rebase-only- und Fast-Forward-Merges zählen nicht. Vor einer Remote-Löschung müssen persönliche Gitea-Identität, active=true und ein geschlossener PR bestätigt sein. Danach werden Remote-Branch, Worktree und lokaler Branch verifiziert entfernt. Kein manueller rm -rf, kein git branch -D und kein Löschen eines dirty, ungemergten oder weitergelaufenen Branches.

Atomare Commit-Checkpoints (verbindlich)

Agents committen während der Umsetzung, nicht erst auf Nachfrage oder gesammelt am Ende. Lokale Commits im aktiven Session-Branch gehören zum normalen Arbeitsablauf; ein Push braucht weiterhin einen eigenen, vollständig grünen Abschluss-Checkpoint.

Ein Commit enthält genau eine fachlich benennbare, separat revertierbare Änderung einschließlich ihrer direkt gekoppelten Tests, Typen, Übersetzungen und generierten Artefakte. Bevor ein zweites unabhängiges Thema begonnen wird, muss das erste Thema fokussiert geprüft und committed sein. Unfertige Zwischenzustände, die allein nicht bauen oder den Contract brechen, werden nicht künstlich getrennt.

Verbindliche Commit-Grenzen:

  1. Ein eigenständig beschreibbarer Bug oder ein sichtbares Verhalten ist fertig und fokussiert getestet.
  2. Eine Route bzw. eine zusammengehörige Routenfamilie ist verschoben, inklusive Navigation, Redirects und Routentests.
  3. Ein Feature, Modell oder Alt-Modul ist vollständig entfernt, inklusive direkt abhängiger Artefakte.
  4. Eine Schema-/Migrationsänderung samt zwingend gekoppeltem Client-/RLS-Nachzug ist konsistent.
  5. Während der Arbeit taucht ein unabhängiger Refactor, Tooling-Fix, Test-Fix oder Doku-Nachzug auf. Dieser wird nicht in den laufenden Fach-Commit gemischt, sondern erhält einen eigenen Commit.
  6. Vor einem Kontextwechsel, einer längeren Diagnose oder dem Beginn des nächsten Plan-/DoD-Punkts.

Größenbremse vor jedem Commit:

  • git diff --cached --name-only und git diff --cached --stat prüfen; nie blind git add -A, wenn mehr als ein Checkpoint offen ist.
  • Ab 25 Dateien oder 800 geänderten Zeilen ohne reine Generierung, Migration oder Massendeletion muss der Agent stoppen und den Staging-Inhalt in kleinere fachliche Einheiten zerlegen. Eine Überschreitung ist nur zulässig, wenn alle Dateien nachweislich zu derselben untrennbaren Änderung gehören; der Grund wird in der Abschlussmeldung genannt.
  • Ein Sammelcommit mit mehreren unabhängigen Themen wird nicht erstellt, auch nicht mit dem Vorsatz, ihn später noch zu splitten. Falls das versehentlich passiert, wird die lokale Historie vor Übergabe korrigiert.

Pro Checkpoint laufen mindestens git diff --cached --check, die fokussierten Tests und der Typecheck der betroffenen Pakete. Die vollständigen pnpm typecheck && pnpm lint && pnpm test sowie passende verify:*-Gates laufen spätestens vor Push/PR; Details stehen in docs/AGENT-CHECKLIST.md.

Staging-Credentials: keine Rotation ohne Auftrag (verbindlich)

Für Staging werden Credentials, Secrets, API-Keys und OAuth-Client-Secrets grundsätzlich nicht automatisch rotiert, ersetzt oder widerrufen. Agents dürfen eine Rotation nur nach ausdrücklicher Anweisung des Users in der aktuellen Aufgabe ausführen oder als Voraussetzung für einen Staging-Test bzw. -Deploy behandeln. Ein möglicher Leak wird gemeldet und dokumentiert, löst aber keine eigenmächtige Rotation oder pauschale Staging-Sperre aus. Produktion bleibt davon unberührt. Die providerseitige, protokollbedingte Refresh-Token-Rotation eines OAuth-Flows ist keine manuelle Credential-Rotation und bleibt technisch aktiv.

Kanonische lokale Login-Daten (verbindlich)

Für ausschließlich lokale, wegwerfbare Dev-Datenbanken gilt: Der Plattform-Admin für http://admin.localhost:3000 ist immer admin@email.com; sein Passwort ist immer password. Abweichende lokale AdminUser-Identitäten werden demotet; nur der kanonische Admin erhält die globale SuperAdmin-Rolle. Alle lokalen Plattform- und Portal-Benutzer mit Passwort-Login erhalten ebenfalls password; absichtlich passwortlose Magic-Link-Identitäten bleiben passwortlos.

Der kanonische Startbefehl ist pnpm dev:local. Er lädt .env/.env.local, aktiviert den nur für admin@email.com erlaubten lokalen Admin-Fallback und stellt die Login-Daten vor jedem Start idempotent wieder her. Ein rohes pnpm dev startet nur den Web-Prozess und erfüllt diesen lokalen Credential-Vertrag nicht.

Diese Vereinfachung gilt niemals für CI, Staging oder Produktion. Die Normalisierung muss bei NODE_ENV !== "development", RLS_RUNTIME_MODE=required, gesetztem SENTRY_ENVIRONMENT, einem nicht über Loopback erreichbaren PostgreSQL-Ziel, Connection-String-Queryparametern oder einer DB ohne den Datenbank-Marker app.local_dev_credentials=enabled fail-closed abbrechen. Frische lokale Compose-Datenbanken erhalten den Marker beim Init; bestehende lokale Compose-Volumes werden einmalig und ausschließlich über pnpm dev:services:mark-local markiert. Der allgemeine Seed-Pfad darf diese Guards nicht umgehen. Die Production-/Staging-Gates bleiben unverändert scharf.

Bespoke-HiveGrid auf ehemaligem Boilerplate-Fundament — kritisch

HiveGrid wird bespoke gebaut. Das HiveGrid-Cloud/-Repo stammt historisch aus der EU-sterilisierten Boilerplate und nutzt weiterhin sinnvolle Fundament-Pakete, aber wir planen keine laufende Upstream-Produktmerge-Strategie mehr. Boilerplate-Reste sind Arbeitsmaterial, kein Produktziel. Du editierst, erweiterst und löschst sie, wenn HiveGrid dadurch kohärenter wird.

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, Mailjet, 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, Scaffolder, Demo-/Marketing-Boilerplate, Storybook-/Docs-Ballast, falls nicht mehr gebraucht)
  • Du baust neue Module in dieser Codebase, nicht neben ihr

Pre-Flight-Check beim Start (jedes Mal):

  1. Für die mutierende Session wurde mit pnpm session:start -- <branch> ein neuer Worktree/Branch vom aktuellen origin/main erstellt und als Arbeitsverzeichnis gewählt → wenn nein: STOP
  2. package.json im Repo-Root existiert mit pnpm-Workspace-Konfig → wenn nein: STOP, melde „Workspace-Inhalt fehlt"
  3. apps/, packages/, prisma/ existieren → wenn nein: STOP
  4. Kein redundanter Baseline-Typecheck/-Test auf frischem main: Wenn der Worktree sauber ist und nach einem frischen Fetch exakt auf origin/main basiert (direkt durch pnpm session:start oder konfliktfrei per --ff-only synchronisiert), werden beim Start weder pnpm typecheck noch pnpm test ausgeführt. Die grünen Merge-Gates von main sind für diesen unveränderten Stand maßgeblich; den Entwickler-Rechner nur zur Wiederholung derselben Baseline zu belasten ist verboten. Erst nach eigenen Codeänderungen laufen fokussierte Checks an den atomaren Checkpoints und die vollständigen Gates gemäß docs/AGENT-CHECKLIST.md vor Push/PR. Baseline-Checks sind nur zulässig, wenn der Worktree von origin/main abweicht oder eine konkrete Diagnose sie erfordert.
  5. 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. (@ai-sdk/openai-compatible ist zwar provider-neutral, führt nach AD-26 aber nicht zu neuem v1/v2-LLM-Scope.)

Erst danach beginnst du mit dem Ticket.

Naming-Konvention (Stand 2026-07-01, Bespoke-Re-Cut)

Wichtige Korrektur: Dieses Repo hat einen Boilerplate-Ursprung, aber HiveGrid wird bespoke weitergeführt. Stabilität im laufenden Code zählt mehr als künstliche Upstream-Mergebarkeit.

  • Alle Workspace-Pakete nutzen den @hivegrid/*-Scope (z.B. @hivegrid/foundation, @hivegrid/view-models, @hivegrid/db, @hivegrid/search, @hivegrid/cli). Es gibt keine Alt-Scope-Aliase und keine parallel gepflegten Alt-Namen.
  • UI-Pakete heißen real packages/ui-app, packages/ui-subframe, packages/ui-mock — NICHT packages/ui-hivegrid. Alte ui-hivegrid-Erwähnungen sind Doku-Drift und zu korrigieren.
  • 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/).

Quellenverantwortung und Status

Es gibt keine globale Rangliste für unterschiedliche Dokumenttypen; jede Quelle besitzt einen klaren Verantwortungsbereich:

  1. Ticket-Doc .planning/ticket-docs/<ID>.md — ausführbarer Scope, Abhängigkeiten und DoD.
  2. Diese AGENTS.md — repo-weite Architektur-, Sicherheits- und Arbeitsregeln. Eine Ticket-Doc darf diese Grenzen nicht still lockern.
  3. Entity Model .planning/ENTITY-MODEL.md — alleinige Quelle für Datenmodell und Projekt-Pivot.
  4. Requirements/Roadmaps .planning/REQUIREMENTS.md, ROADMAP.md, ROADMAP-V2.md — Modulabdeckung und Reihenfolge.
  5. Entscheidungsregister docs/ARCHITECTURE-DECISIONS.md — IDs, Gründe und Status; keine zweite operative Regelsammlung.
  6. Ersetzte Produktplanungen sind ausschließlich über die Git-Historie verfügbar und keine Bauquelle.

Ein genehmigter Change-Request aktualisiert immer die zuständige operative Quelle und die betroffenen Ticket-Docs. Ein Eintrag nur im Entscheidungsregister genügt nicht.

Operativer Status, Owner, Cycles und Abhängigkeiten liegen ausschließlich in Plane. Umsetzung und Proof werden in der Ticket-Doc dokumentiert; es gibt keinen parallelen Milestone-Tracker.

Architektur-Baseline (operativ)

Diese Kurzfassung ist beim Bauen bindend. Die AD-Nummern dienen nur der Rückverfolgbarkeit ins Entscheidungsregister.

Bereich Verbindlicher Vertrag
Tenant-Sicherheit (AD-1) Postgres erzwingt RLS; App-/Worker-Rollen haben kein BYPASSRLS. Jeder DB-Pfad bindet User-/Tenant-Kontext, jede neue Tabelle erhält Policy und Contract-Gate.
Fachmodelle (AD-2/20) Neue Fachentitäten sind typisierte Prisma-Modelle. Kein neuer Entity-Builder-Konsument. CRM bleibt projekt-zentrisch: ein Projekt-Kern, Pipelines als Views, Lead → Projekt.
Admin und Sessions (AD-3/4) Plattform-Admins sind von Tenant-Identitäten getrennt und nutzen produktiv nur Authentik-OIDC. Sessions liegen in Postgres; Redis ist Cache, Cookies enthalten nur die Referenz.
Secrets und Medien (AD-8/22/23) Tenant-Credentials nutzen DEK-Envelope-Encryption; GeoIP bleibt lokal und minimal; Uploads bleiben privat und werden nur über geprüfte App-/Signaturpfade ausgeliefert.
Suche, Mail, Captcha (AD-5/6/7) Meilisearch ist keyword-only und SQL wird revalidiert; Mail läuft über Mailjet; Captcha ist self-hosted (mCaptcha oder Cap), nicht Google/Cloudflare/hCaptcha.
Realtime, Naming, Mobile (AD-9/10/17) Chat nutzt WebSocket, Read-Updates SSE; Produktcode heißt ausschließlich @hivegrid/*; Mobile bleibt in v1/v2 PWA-only.
Prisma (AD-21) Ein Modul = eine Schema-Datei; Shared-Core nur minimal ändern; Branch = db push, lineare Forward-only-Migration beim Merge.
Signatur (AD-25) Self-hosted gehärtete SES ist Default, PandaDoc nur Tenant-BYOK; QES bleibt späteres Add-on. Vor Produktivfreigabe juristisch bestätigen.
KI/MCP (AD-26) Kein eigenes LLM/RAG/Whisper in v1/v2. Externe KI-Clients nutzen den getrennten, tenant- und permission-gescopten MCP-Service.
Billing (AD-27) HiveGrid hält Entitlements, Lago Billing/Invoices, Mollie Zahlungen. Kein v1-Usage-Billing und keine Fachdaten beim Provider.
Interim-UI (AD-28) UI-lastige Tickets werden mit getesteter ~/ui-Interim-UI fertig; finale Subframe-Optik bleibt als design-nachzug getrackt.
Workspace-Rollen (AD-29) Neue Workspaces erhalten fünf versionierte SHK-Rollen; Owner bleibt ausschließlich globaler Admin; bestehende Tenants werden nicht automatisch verändert.
Customer-Portal (AD-30) Eigenständiger Host und hivegrid-portal-Runtime mit eigenen Sessions, minimalem DB-Login, Entitlement-Gates und verifizierter Host→Tenant-Auflösung.
Plattform-Leads (AD-31) HiveGrids eigene Leads liegen tenantlos in der Admin-Domäne. lead-inbox wird erst nach Backfill, Cutover und Drain stillgelegt.
KWP (AD-32) Initial fachlich read-only. Nur feste Read-Allowlist; Mutationen vor HTTP gesperrt. Writeback erst nach VEXT-6 plus erneuter ausdrücklicher Freigabe.

Definition of Done — verbindlicher Qualitätsrahmen für Tickets

Ein Ticket gilt nur dann als erledigt, wenn seine eigene DoD vollständig erfüllt ist. Soweit der Ticket-Scope die folgenden Flächen berührt, gelten zusätzlich diese repo-weiten Qualitätsanforderungen:

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)

  1. Sidebar-Eintrag in apps/web/app/application/sidebar/ (AppSidebar/AdminSidebar + SidebarItem) — oder Inline-Verlinkung aus bestehendem Modul, wenn das semantisch korrekt ist.
  2. Permission-Gate auf dem Sidebar-Eintrag — Eintrag NUR sichtbar bei berechtigter Rolle.
  3. Route registriert unter dem korrekten URL-Pattern (siehe INTEGRATION-CONTRACTS.md §3).
  4. Breadcrumbs für Detail-Routes (siehe §4).
  5. i18n-Keys für ALLE UI-Texte (Pflicht via M003-Build-Lint).
  6. Mobile-Tab-Bar-Eintrag, falls das Modul mobile-relevant ist (§13).
  7. Customer-Portal-Konfiguration, falls Endkunden das sehen sollen (§14, Default unsichtbar).

C. Querschnitt-Wiring (Pflicht-Service-Aufrufe)

  1. Schreib-Aktion ist auditiert (auto via M001-Prisma-Middleware oder manuell via auditEvent).
  2. Activity-Stream-Eintrag auf relevanter Entität (Kontakt/Firma/Lead/Projekt) bei jeder relevanten Aktion via logActivity.
  3. Notifications via M011-Service (notify({...})), niemals direkt Email/Push.
  4. Search-Index registriert in packages/search/ (indexed-entities-Registry — M00-Neubau), falls suchbar.
  5. Permission-Filter via withTenantScope() + withPermission() (M004) — kein manueller Query-Filter im Modul-Code.

D. Tests (PR-Block ohne)

  1. Unit-Tests für View-Model-Builder (pure-function-test mit Fixture).
  2. Smoke-Test in e2e/smoke/<modul>.spec.ts — Login → Route öffnen → Hauptaktion klicken → erwartetes Ergebnis sichtbar.
  3. Hot-Swap-Drill (M017): Smoke-Tests müssen mit packages/ui-mock als Drop-in-Replacement laufen.
  4. Visual-Regression-Snapshot (M016) für die Hauptansicht — bei Interim-UI (AD-28) gegen die Interim-Baseline, mit Re-Baseline-Pflicht beim David-Nachzug.

Interim-UI & Done (AD-28, .planning/INTERIM-UI-PLAYBOOK.md): Für UI-lastige Tickets zählt als Done: Dev-DoD 100 % + Interim-Screen aus ~/ui (VM als Prop, kein handgeschriebenes Tailwind) + Tests grün (B 6–10, D 18–21 gegen Interim-Baseline). Die finale Subframe-Optik ist dann kein Done-Blocker, sondern ein getrackter Nachzug. /planning bleibt Single Source of Truth: der 🎨 design-nachzug: offen-Tag unter der Header-Tabelle der Ticket-Doc + Interim-UI-Vermerk in der „Dev-Nachzug"-Sektion sind führend; das gleichnamige Plane-Label spiegelt nur. Die Layer-Trennung (Block A) und die drei Anti-Patterns bleiben unangetastet.

E. Klartext-Hinweise (User-facing)

  1. ALLE Fehler-Texte aus M002-Catalog (kein nackter HTTP-Code, keine kryptische Stack-Trace im UI).
  2. Empty-State mit Klartext + nächster Aktion.
  3. Loading-State mit Klartext (kein leerer Spinner).
  4. Permission-Denied zeigt 404, nicht 403 (siehe Bauprinzip A.5.3).

F. Dokumentation

  1. Ticket-Doc mit Umsetzungs-Notiz, Datum, Proof und %-Stand aktualisiert.
  2. Rollback-Pfad dokumentiert in der Ticket-Doc (siehe docs/MID-STREAM-CHANGES.md).
  3. 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/**

Entity-Builder: KEIN neuer Konsument + Ablöse-Pflicht (AD-2, verbindlich seit 2026-06-29)

Der generische Entity-Builder (Entity/Row/RowValue, modules/rows, utils/db/entities, EntitiesSingleton, die *Api.server-Entity-Layer, modules/crm-CrmService-Legacy) ist Alt-Last und wird abgelöst — bisher nur für den CRM-Pfad raus (R-P6/R-P6B-α/R-P6C), generisch läuft er noch.

  • PR-Block: Ein neues Fachmodul/Feature, das den Entity-Builder als Datenmodell nutzt (statt typisierter Prisma-Tabellen wie CRM), wird abgelehnt — wie ein Anti-Listen-Verstoß. Neue Entitäten = typisiert.
  • Pflicht (kein „optional"): Der generische Builder MUSS vollständig abgelöst werden. Die bereits gebauten Konsumenten — EmailMarketing (AK-P14, dringend), WorkflowEngine, formulas, codeGenerator, onboarding, CrmService-Legacy — werden typisiert abgelöst, sobald sie ohnehin angefasst werden („Migration durch Neubau", billiger als späterer Refactor). Noch nicht gebaute Module (AK-P14b/P15/P16c/d/DEMO1) sowieso direkt typisiert.
  • Konsumenten-Matrix, Reihenfolge und Ownership: .planning/ticket-docs/R-P6B.md §Migrationskette & Ownership / §Rückwirkungs-Audit.

Daten-Fluss (verbindlich)

Synchron im Loader. Server bereitet das View-Model einmal auf, UI rendert. KEIN client-seitiges Re-Compute.

// 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.

Row-Level-Security (RLS) — verbindlich, seit Staging-Cutover 14.07.2026 scharf

Seit 20260713190000_appwide_rls_cutover erzwingt Postgres selbst die Mandanten-Trennung (264 Tabellen, FORCE ROW LEVEL SECURITY). Die App verbindet als hivegrid_app bzw. hivegrid_worker — ohne Superuser, ohne BYPASSRLS. Details: docs/security/RLS-HARDENING-2026-07-13.md.

Die Falle, die dich sonst garantiert erwischt: Lokal läuft RLS_RUNTIME_MODE=off. Dein Code kann also lokal einwandfrei laufen und auf Staging/Prod null Zeilen liefern — kein Fehler, kein Log, nur leere Ergebnisse (bereits einmal passiert: eine neue Route lieferte in Prod jedes Profilbild als 404). Verlass dich für RLS-Korrektheit nie auf einen grünen lokalen Lauf.

  • Ohne gebundenen Kontext liefert eine RLS-Tabelle KEINE Zeile. Der Kontext (app.user_id / app.tenant_id) wird transaktionslokal gesetzt — siehe packages/db/src/rls-context.server.ts.
  • Gehe über die db-Layer-Helfer (app/utils/db/**): die binden den Kontext selbst (z.B. updateUserProfile via withUserRlsContext). Rufst du in einer Route/Service direkt db.<model> auf, musst du ihn selbst binden: runWithRlsContext({ userId, tenantId }, …) bzw. withTenantRLS(tenantId, …).
  • Cross-Tenant-Lesen gibt es nicht „mal eben". Eine Route ohne Tenant-Bezug (host-weit) muss die Mitgliedschaft Tenant für Tenant nachweisen, statt eine GUC in einer offenen Transaktion umzuschalten. Vorbild: apps/web/app/routes/avatars.$userId.tsx.
  • Pre-Auth-Pfade (Login, Registrierung, Magic-Link) laufen über SECURITY-DEFINER-Funktionen und binden den Kontext danach — nicht selbst nachbauen.
  • Neue Tabelle = neue Policy. pnpm run verify:tenant-policy-contract schlägt sonst an. Der Vertrag wird generiert, nicht handgeschrieben.
  • Tests, die den DB-Client mocken, beweisen nichts über RLS. Ein Mock, der auf jede Query antwortet, versteckt genau diesen Bruch. Bilde in solchen Tests die Policy nach (Sichtbarkeit abhängig vom gebundenen Kontext).

Runtime-Env: verbotene Schlüssel. Auf Runtime-Ressourcen (web/api/mcp/hocuspocus/worker) dürfen DIRECT_URL, MIGRATE_ON_BOOT, SEED_ON_BOOT, SEED_* und MIGRATOR_* nicht gesetzt sein — assert-runtime-db-env bricht den Deploy sonst ab. Migrationen laufen ausschließlich im Einmal-Container des Deploys. Konsequenz für Feature-Flags: Ein Laufzeit-Schalter darf niemals ein SEED_-Präfix tragen, auch wenn er thematisch nach Seed klingt — genau daran sind die Staging-Demo-Logins gestorben (siehe docs/deployment.md, STAGING_DEMO_LOGIN_ENABLED).

Seeding ist auf Runtime-Ressourcen abgeschaltet und unter erzwungenem RLS nicht freigegeben. Ein tenant-bewusster Operator-Seed-Workflow ist offener Nachzug.

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)
  • KEIN eigenes LLM/RAG/Whisper in v1/v2 (AD-26). HiveGrid wird stattdessen Remote-MCP-Server (VR-MCP/VD-MCP); externe KI-Clients bringen die Inferenz mit.
  • JA Email+Passwort + Magic-Link + TOTP (M020, M021, M022)
  • Hosting: Hetzner + Coolify (EU); Datenbank: in EU; Storage: Hetzner Object Storage / S3 (EU).

Querschnitt-Hülle nutzen, nicht nachbauen

Concern Querschnitt-Vertrag 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<...>
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 im ToolCtrl-Estate (neben mITapp + Marketing-Sites). Quelle der Wahrheit ist das Schwester-Repo ../infra/: ../infra/README.md (Bestand), ../infra/STANDARDS.md (Estate-Default), ../infra/decisions/ (Ausnahmen/ADRs), ../infra/LESSONS.md (Footguns). Eine akzeptierte Infra-ADR darf einen allgemeinen Standard ausdrücklich brechen. Übersichts-Diagramm + Steckbriefe: local/infra-diagrams/06-estate-infrastructure.{mmd,md}.

Estate-Default: geteilte Services nutzen, nie ungeplant pro Produkt nachbauen. HiveGrid bekommt den Slug hivegrid. Die Tabelle gilt für Staging und alle nicht ausdrücklich ausgenommenen Runtimes.

Produktions-Ausnahme (AD-24 / Infra-ADR 016): HiveGrid-Prod läuft im eigenen Hetzner-Account mit eigenem Coolify. Meilisearch, ClamAV und Captcha laufen dort innerhalb des Prod-Accounts; Prod darf dafür nicht auf die Staging-/Estate-Mesh-Adressen 10.42.0.4:* zeigen. Gitea Registry, Sentry und Cloudflare bleiben bewusst geteilte Dienste über authentifiziertes öffentliches TLS.

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) Staging: Meilisearch 10.42.0.4:7700; Prod: dedizierte AD-24-Instanz MEILI_API_KEY=<hivegrid-scoped>. Indizes hivegrid-*-prefixed, nie geteilt, nie Master-Key in der App.
Upload-AV (STANDARDS §13) Staging: ClamAV 10.42.0.4:3310; Prod: dedizierte AD-24-Instanz; immer INSTREAM Failure-Policy: fail-closed für untrusted Uploads. Kein Path-Scan/shared FS; >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):

  • Interaktive Gitea-Identität = persönlicher Dev-Account (verbindlich): Jeder Clone deklariert seinen erwarteten Gitea-Login lokal mit git config --local hivegrid.giteaUser <login> (raffael auf Raffaels Maschine, akeshan auf Akeshans Maschine). Vor jedem Git-Push und vor jeder schreibenden Gitea-API-Aktion (PR anlegen/schließen/mergen, Kommentar, Branch/Tag löschen, Release) muss die effektiv verwendete Credential über GET /api/v1/user exakt diesen Login und active=true liefern. Fehlende Deklaration, Lookup-Fehler oder Abweichung = STOP; keine Admin-Impersonation als Ersatz. Der kanonische lokale API-PAT liegt in ~/.cli-tokens/gitea-<login> (Mode 600); ~/.cli-tokens/gitea darf nur als Symlink auf den zum deklarierten Login passenden Token existieren. HTTPS-Credentials bleiben pfadspezifisch (credential.useHttpPath=true) und dürfen nie in Remote-URLs stehen. gitea-admin ist für normale Entwickler-/Agent-Arbeit verboten und nur als ausdrücklich autorisierter, separat auditierter Break-Glass-Zugang zulässig. Ausnahme: CI-/Deploy-/Registry-Automation nutzt weiterhin dedizierte Servicekonten/Bot-PATs; persönliche Entwickler-Credentials gehören nie in Gitea-Secrets oder Runner.
  • Merge-Strategie = normaler Merge-Commit, niemals Squash (verbindlich): PRs werden nach vollständig grüner CI mit der normalen Merge-Strategie übernommen. Die einzelnen Branch-Commits müssen mit ihren ursprünglichen SHAs Vorfahren von main bleiben; Gitea-API-Merges verwenden Do=merge, nie Do=squash. Vor dem Merge wird die erwartete Head-SHA geprüft, danach mit git merge-base --is-ancestor <feature-head> origin/main verifiziert, dass die Branch-Historie erhalten ist. Ein versehentlicher Squash wird nicht durch Umschreiben oder Force-Pushen des geteilten main repariert, sondern append-only durch einen bytegleichen History-Repair-PR, der den ursprünglichen Feature-Head per normalem Merge-Commit anbindet.
  • 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). Staging läuft über das zentrale Estate-Coolify; HiveGrid-Prod über das eigene AD-24-Coolify.
  • 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

Produktgrenzen / Anti-Liste — Bauprinzip A.9

Was nicht in der aktuellen Ticket-Doc beauftragt ist, wird nicht gebaut. Zusätzlich bleiben folgende repo-weite Grenzen ohne genehmigten Change-Request ausdrücklich außerhalb des Produkts:

  • keine Marketing-Webseiten-, CMS-, Blog- oder MySite-Engine in HiveGrid,
  • kein vollständiges Buchhaltungs-/ERP-Ersatzprodukt; der einfache Projekt-Rechnungsdatensatz und ausdrücklich beauftragte Adapter sind die Grenze,
  • kein Apple-/Google-/Microsoft-Login; Provider-OAuth für freigegebene Fachintegrationen ist davon getrennt,
  • keine native App, kein Expo/Capacitor und keine App-Store-Distribution in v1/v2,
  • kein eigenes LLM, RAG, Whisper, HiveMind oder autonom handelnder KI-Agent in v1/v2,
  • keine frei ausführbaren Custom-JS-Ausdrücke in Automationen,
  • keine GraphQL-Surface und keine zusätzlichen ERP-, Social-, Marketplace- oder Provider-Integrationen ohne eigenes Ticket und Produktfreigabe,
  • kein KWP-Writeback vor VEXT-6 und erneuter ausdrücklicher Freigabe (AD-32).

Eine Erweiterung läuft über docs/CHANGE-REQUESTS.md. Die dort genehmigte Änderung muss anschließend in AGENTS.md, Requirements und den betroffenen Ticket-Docs nachgezogen werden.

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) steht und mit den repo-weiten Verträgen aus AGENTS.md, Entity Model und Requirements vereinbar ist. 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: der ausführbare Code bleibt tool- und teamübergreifend konsistent; Projektkontext und Umsetzungsnotizen bleiben deutsch.

Deutsch bleibt nur in den Projekt-Dokus: .planning/** (inkl. ticket-docs/), docs/**, 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.