Files
HiveGrid-Wireframes/hivegrid-cloud/AGENTS.md
T

26 KiB
Raw Blame History

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)

  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/Verkaufsfall/Auftrag) 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.

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. PLAN.md des Milestones aktualisiert mit Lessons Learned, falls relevant.
  2. Rollback-Pfad dokumentiert im Slice-Plan (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/**

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.

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<...>
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: ". 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.