Files
dejanandClaude Opus 4.7 4f33509a0f
ci / quality (push) Has been cancelled
ci / build (push) Has been cancelled
Initial commit: everythingbutwork MVP
Internes Project-Management-Tool für ToolCTRL — ergänzend zu Plane (Tickets)
und Outline (Doku). Umfasst Puls-Übersicht, Tagesrhythmus (Lock-in + Reflexion),
Bereichs-Detail-Views (Entwicklung/Vertrieb/Marketing), Pflicht-Minimum-Check
und Blocker-Lifecycle gemäß Projektmanagement-Leitfaden v3.0.

Stack: pnpm-Monorepo · Vite + React 18 + Tailwind · Fastify + tRPC v11 +
Drizzle ORM · Postgres 16 · Authentik OIDC (Phase 2, Dev-Auto-Login aktiv).

Planungs-Artefakte unter _bmad-output/planning-artifacts/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 22:47:26 +02:00

5.0 KiB

CONVENTIONS — ToolCTRL everythingbutwork

Bindend für alle Code-erzeugenden Agenten und Menschen. Spiegelt Architektur-Spec § Implementation Patterns. Pflicht-Lesen vor jeder Story.

Naming

Datenbank (Drizzle + Postgres)

  • Tabellen: Singular, snake_case (englisch) — user, project, day_entry, todo. Domain-Begriffe in Spalten dürfen deutsch sein (abwesend_bis, arbeitsmodus).
  • Primärschlüssel: immer id (UUID v7).
  • Fremdschlüssel: <entity>_id — user_id, project_id.
  • Indizes: idx_<table>_<column>; unique: uniq_<table>_<column>.
  • Enums: TypeScript-Union in Drizzle + text mit check-Constraint in Postgres.
  • Zeitstempel: created_at, updated_at, <verb>_at als timestamptz, Default now().

tRPC Backend

  • Router-Datei: apps/api/src/routers/<feature>.ts — day-entry.ts, project.ts.
  • Router-Variable: <feature>Router (camelCase) — dayEntryRouter.
  • Procedure-Naming: <feature>.<verbNoun> (camelCase) — dayEntry.checkIn, dayEntry.submitReflection, project.archive, blocker.resolve.
  • Reads: list, byId, mine, forProject.
  • Mutations: create, update, archive (statt delete), resolve, checkIn, submitReflection.
  • Procedure-Inputs: immer Zod-Schema aus @cockpit/shared/schemas/<feature>.

Frontend (React)

  • Komponenten: PascalCase. Dateiname matched (DayEntryForm.tsx).
  • Hooks: use<Name> camelCase.
  • Konstanten: SCREAMING_SNAKE_CASE.
  • Route-Pfade: kebab-case, deutsch wo Domain — /puls, /mein-heute, /bereich/entwicklung.
  • Komponenten-Organisation: nach Feature, nicht nach Typ.

Struktur

  • Tests neben dem Code: Foo.tsx ↔ Foo.test.tsx (Backend: foo.ts ↔ foo.test.ts).
  • E2E: zentral in apps/web/e2e/<journey>.spec.ts.
  • Shared zwischen Frontend + Backend → packages/shared/. Backend-only Helper → apps/api/src/lib/.
  • Konfig-Dateien im Repo-Root, pro App nur Overrides.

Format

  • JSON-Felder über die Wire: camelCase (Drizzle mapt DB-snake_case → TS-camelCase).
  • Datumsformat: ISO 8601 mit Zeitzone. Reine Werktage YYYY-MM-DD (Typ date).
  • Geldbeträge: Cent als integer in DB; Frontend formatiert via Intl.NumberFormat('de-DE').
  • Booleans: echtes boolean. Nie 0/1 oder "yes"/"no".
  • Null vs. Undefined: DB nutzt null. Code: null = explizit nicht gesetzt, undefined = existiert nicht.
  • IDs in URL: UUIDs (/projekt/:id), keine numerischen IDs öffentlich.

Errors

  • Backend: TRPCError mit klaren Codes — UNAUTHORIZED, FORBIDDEN, NOT_FOUND, BAD_REQUEST, INTERNAL_SERVER_ERROR.
  • Message: immer deutsch, menschenlesbar. Nicht „NULL constraint violated" sondern „Bitte wähle einen Verantwortlichen aus".
  • Frontend: zentraler onError-Hook in tRPC-Provider zeigt error.message als Toast.
  • Logging: INTERNAL_SERVER_ERROR mit Stack → pino.error. BAD_REQUEST → pino.info.

State (Frontend)

  • Server-State: ausschließlich React Query via tRPC (isPending, isError, data). Nie eigene useState-Booleans für Server-Loading.
  • Loading: Skeleton-Komponenten, keine Spinner-Only.
  • Optimistic Updates für Reflexion, Todo-Toggle, Blocker-Auflösung. Sonst nicht.
  • Lokaler UI-State: useState/useReducer in der Komponente. Hochziehen, wenn Sibling-Sharing nötig — kein Context.

Audit & Authorization

  • Mutations: withAudit()-Middleware um jede schreibende Procedure. Schreibt audit_event in derselben Transaktion.
  • AuditEvent-Naming: <entity>.<verb> (lowercase, dot-separated) — day_entry.reflection_submitted, blocker.resolved.
  • Procedure-Typen: publicProcedure, protectedProcedure, dayEntryVisibleProcedure, projectOwnerProcedure.

Werktag / Zeit

  • Server-Zeit: Europe/Berlin.
  • Werktage: Mo-Fr (MVP, ohne Feiertage).
  • Alle Datumslogik im Backend — Frontend bekommt fertige Felder (isWorkday, nextWorkday, streakDays).

Voice & Tone

  • UI-Strings zentral in apps/web/src/lib/voice.ts.
  • Tonalität: locker, direkt, deutsch, kurze Imperative — „Stark.", „Wie läuft es?", „Du hast heute noch nicht gepingt.".
  • Keine Corporate-Sprech, keine englischen Floskeln (Fachbegriffe aus Leitfaden erlaubt).
  • Error-Messages im Backend ebenfalls deutsch.

Tests

  • Unit & Procedures: Vitest.
  • E2E: Playwright auf UJ-1, UJ-2, UJ-5 (CI nur auf Tag/Release).
  • A11y: axe-core in Vitest pro Komponente, @axe-core/playwright für E2E, Lighthouse-A11y ≥ 95 im CI.
  • Tests neben Code (*.test.ts(x)).

DB-Änderungen

  • Immer Drizzle-Migration committen — kein Schema-Drift.
  • Migration entstehen je Story-Bedarf, nicht upfront.
  • TypeScript strict, kein any. Bei unklarer Form: unknown + Zod-Validation.

Don'ts

  • ❌ Inline-Zod im Procedure (gehört nach @cockpit/shared/schemas).
  • ❌ Englisches Error-Message.
  • ❌ snake_case-Komponentennamen.
  • ❌ Server-Loading via useState.
  • ❌ Drag-and-Drop außerhalb des Sales-Kanban.
  • ❌ Pulsing-/Spin-Animationen auf dem Puls.
  • ❌ Auto-Refresh / Background-Polling.