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

104 lines
5.0 KiB
Markdown

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