Fix: verify_ws_origin async + await callers
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
- auth.py: verify_ws_origin is now async def - kommunikation/routes.py: await verify_ws_origin - ai_ui_control/routes.py: await verify_ws_origin
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"mcpServers": {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"title": "LeoCRM",
|
||||
"description": "Mini-CRM mit Kontakten, Mail, DMS, Kalender, Tasks und Plugin-System. FastAPI Backend + React/TypeScript Frontend. Deployiert über Coolify auf Hetzner VPS.",
|
||||
"instructions": "Du arbeitest am LeoCRM-Projekt.\n\n## Projekt-Übersicht\n- **Repo**: /a0/usr/workdir/leocrm-fix (Git: Forgejo Leopoldadmin/leocrm)\n- **Frontend**: React + TypeScript + Vite + Tailwind, in frontend/\n- **Backend**: FastAPI + SQLAlchemy + PostgreSQL (pgvector), in app/\n- **Tests**: frontend/src/__tests__/ (Vitest), tests/ (pytest)\n- **Migrations**: alembic/versions/\n- **Plugins**: app/plugins/builtins/ (Mail, DMS, Tasks, Calendar, etc.)\n- **Plugin-Manifest-Schema**: app/plugins/manifest.py\n\n## Deploy\n### Frontend-only (~20s)\n```bash\nbash /a0/usr/workdir/leocrm-fix/scripts/fast-deploy.sh frontend\n```\nBaut lokal, kopiert dist/ direkt in den laufenden Container. Kein Coolify-Rebuild.\n\n### Full Deploy (~2min, fuer Backend-Aenderungen)\n```bash\nbash /a0/usr/workdir/leocrm-fix/scripts/fast-deploy.sh full\n```\nTriggert Coolify-Rebuild ueber deploy.py.\n\n### Wann was?\n- Nur Frontend (TSX, CSS): frontend\n- Backend (Python, Dockerfile, requirements): full\n\n## Git Workflow\n1. Aenderungen in /a0/usr/workdir/leocrm-fix\n2. git add -A && git commit -m '...' && git push origin main\n3. Dann deploy\n\n## Server & Container\n- Host: 46.225.91.159 (root, SSH Key: /a0/usr/workdir/.ssh/coolify-01-root)\n- Coolify: https://server.media-on.de\n- App UUID: stvabl4vaqru7jclx4ittzr3\n- Container-Name aendert sich bei jedem Coolify-Deploy (Suffix)\n- Frontend-Pfad im Container: /app/frontend/dist\n- Worker-Container: leocrm-worker\n- DB: crm-postgres (pgvector/pgvector:pg16)\n- Redis: crm-redis (redis:7-alpine)\n\n## Zugaenge\n- Web-UI: https://crm.media-on.de/login\n- Forgejo: https://forgejo.media-on.de/Leopoldadmin/leocrm\n- Sensible Credentials siehe leocrm-deploy.promptinclude.md im workdir\n\n## Wichtige Dateien\n- FIX-PLAN-V2.md — Aktueller Fix-Plan\n- architecture.md — Architektur-Dokumentation\n- DEPLOY.md — Deployment-Anleitung\n- COOLIFY_SETUP.md — Coolify-Konfiguration\n- .a0/ — Projekt-Status (current_status.md, next_steps.md, risks.md, worklog.md)\n\n## Regeln\n- Frontend-Style an bestehenden Komponenten orientieren (Mail-Plugin als Referenz)\n- Bei UI-Aenderungen immer Mail-Plugin als Referenz pruefen\n- Tests nicht editieren ausser explizit verlangt\n- Minimal focused changes, bestehenden Style beibehalten\n- Bei destruktiven Aenderungen: User fragen",
|
||||
"include_agents_md": true,
|
||||
"color": "#3b82f6",
|
||||
"git_url": "https://forgejo.media-on.de/Leopoldadmin/leocrm.git",
|
||||
"file_structure": {
|
||||
"enabled": true,
|
||||
"max_depth": 5,
|
||||
"max_files": 20,
|
||||
"max_folders": 20,
|
||||
"max_lines": 250,
|
||||
"gitignore": "node_modules\n__pycache__\n*.pyc\n.env\n.git\n.venv\ndist\n"
|
||||
}
|
||||
}
|
||||
@@ -1,571 +1,125 @@
|
||||
# LeoCRM — AGENTS.md
|
||||
|
||||
**Projekt:** leocrm
|
||||
**Erstellt:** 2026-06-28
|
||||
**Status:** Draft — ready for implementation
|
||||
**Projekt:** leocrm | **Stack:** FastAPI + SQLAlchemy + PostgreSQL 16 (pgvector) + React/TypeScript/Vite/Tailwind
|
||||
|
||||
---
|
||||
|
||||
## 1. Build & Test Commands
|
||||
|
||||
### Backend (Python / FastAPI)
|
||||
|
||||
#### Setup
|
||||
```bash
|
||||
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
#### Run Dev Server
|
||||
```bash
|
||||
|
||||
# Backend
|
||||
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
#### Database Migrations (Alembic)
|
||||
```bash
|
||||
|
||||
# Generate migration after model changes
|
||||
alembic revision --autogenerate -m "description"
|
||||
# Apply migrations
|
||||
alembic upgrade head
|
||||
# Rollback one migration
|
||||
alembic downgrade -1
|
||||
```
|
||||
|
||||
#### Run All Backend Tests
|
||||
```bash
|
||||
|
||||
python -m pytest -v --tb=short
|
||||
```
|
||||
|
||||
#### Run Specific Test File
|
||||
```bash
|
||||
|
||||
python -m pytest tests/test_auth.py -v --tb=short
|
||||
```
|
||||
alembic upgrade head
|
||||
alembic revision --autogenerate -m "description"
|
||||
|
||||
#### Run Tests with Coverage
|
||||
```bash
|
||||
# Frontend
|
||||
cd frontend && npm run dev
|
||||
cd frontend && npm run build
|
||||
cd frontend && npx vitest run --reporter=verbose
|
||||
cd frontend && npx tsc --noEmit
|
||||
|
||||
python -m pytest --cov=app --cov-report=term-missing --cov-report=html
|
||||
```
|
||||
|
||||
#### Run Tests with Grep Filter
|
||||
```bash
|
||||
|
||||
python -m pytest -k 'tenant or auth' -v
|
||||
```
|
||||
|
||||
#### Type Checking
|
||||
```bash
|
||||
|
||||
mypy app/ --ignore-missing-imports
|
||||
```
|
||||
|
||||
#### Linting
|
||||
```bash
|
||||
|
||||
ruff check app/
|
||||
ruff format app/
|
||||
```
|
||||
|
||||
### Frontend (React / Vite / TypeScript)
|
||||
|
||||
#### Setup
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
```
|
||||
|
||||
#### Run Dev Server
|
||||
```bash
|
||||
cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
#### Build Production
|
||||
```bash
|
||||
cd frontend
|
||||
npm run build
|
||||
```
|
||||
|
||||
#### Run All Frontend Tests
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest run --reporter=verbose
|
||||
```
|
||||
|
||||
#### Run Tests with Coverage
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest run --coverage
|
||||
```
|
||||
|
||||
#### Run Tests in Watch Mode (dev)
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest watch
|
||||
```
|
||||
|
||||
#### Type Checking
|
||||
```bash
|
||||
cd frontend
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
#### Linting
|
||||
```bash
|
||||
cd frontend
|
||||
npx eslint src/ --ext .ts,.tsx
|
||||
```
|
||||
|
||||
### Docker Compose (Full Stack)
|
||||
|
||||
#### Build All Services
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
#### Start All Services
|
||||
```bash
|
||||
# Docker
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
#### View Logs
|
||||
```bash
|
||||
docker compose logs -f backend
|
||||
```
|
||||
|
||||
#### Stop All Services
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
#### Validate Compose Config
|
||||
```bash
|
||||
docker compose config --quiet
|
||||
```
|
||||
|
||||
### E2E Tests (Playwright)
|
||||
|
||||
```bash
|
||||
cd e2e
|
||||
npx playwright install
|
||||
npx playwright test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Test Rules
|
||||
|
||||
### TDD (Test-Driven Development)
|
||||
|
||||
- **Red-Green-Refactor:** Write failing test first → implement minimum code to pass → refactor.
|
||||
- **Every new endpoint gets a test BEFORE implementation.**
|
||||
- **Every bug fix starts with a reproduction test.**
|
||||
|
||||
### Coverage Targets
|
||||
|
||||
| Layer | Coverage Target | Measured By |
|
||||
|-------|----------------|-------------|
|
||||
| Backend Core (app/core/) | 85% | pytest-cov |
|
||||
| Backend Models+Services | 85% | pytest-cov |
|
||||
| Backend Routes | 85% | pytest-cov |
|
||||
| Backend Plugins | 80% | pytest-cov |
|
||||
| Frontend Components | 75% | vitest coverage |
|
||||
| Frontend Plugin UI | 70% | vitest coverage |
|
||||
| E2E (critical paths) | 100% of defined specs | Playwright |
|
||||
|
||||
### Test File Structure
|
||||
|
||||
#### Backend
|
||||
```
|
||||
backend/tests/
|
||||
├── conftest.py — Fixtures: test client, test DB, auth helpers, seed data
|
||||
├── test_auth.py — Auth endpoints, RBAC, password reset
|
||||
├── test_tenant.py — Tenant isolation, cross-tenant access
|
||||
├── test_companies.py — Company CRUD, search, filter, pagination, soft-delete
|
||||
├── test_contacts.py — Contact CRUD, N:M links, GDPR delete
|
||||
├── test_import_export.py — CSV import/export, XLSX export, dry-run preview
|
||||
├── test_plugins.py — Plugin lifecycle, event bus, migrations
|
||||
├── test_dms.py — DMS folders, files, upload, shares, permissions
|
||||
├── test_calendar.py — Entries, recurrence, kanban, ICS, resources
|
||||
├── test_mail.py — Accounts, IMAP sync, send, threading, rules, PGP
|
||||
├── test_tags.py — Tag CRUD, assignment, bulk
|
||||
├── test_notifications.py — Notification CRUD, unread count
|
||||
├── test_health.py — Health endpoint
|
||||
├── test_ai_copilot.py — KI-Copilot API, RBAC enforcement, history
|
||||
├── test_workflows.py — Workflow CRUD, instances, approval/rejection, event triggers
|
||||
├── test_monitoring.py — Extended health, Prometheus metrics, alerting
|
||||
└── test_performance.py — 200k seed, list <500ms, FTS <500ms, streaming export
|
||||
```
|
||||
|
||||
#### Frontend
|
||||
```
|
||||
frontend/src/__tests__/
|
||||
├── components/ — UI component unit tests (Button, Input, Modal, Table, etc.)
|
||||
├── features/ — Feature integration tests (CompanyList, ContactForm, etc.)
|
||||
├── hooks/ — Custom hook tests (useDebounce, usePagination, etc.)
|
||||
├── plugins/ — Plugin UI tests (DMS, Calendar, Mail, Tags)
|
||||
└── search/ — Global search tests
|
||||
```
|
||||
|
||||
#### E2E
|
||||
```
|
||||
e2e/
|
||||
├── auth.spec.ts — Login → logout flow
|
||||
├── company-crud.spec.ts — Create → edit → delete company
|
||||
├── contact-crud.spec.ts — Create → link to company → delete
|
||||
├── search.spec.ts — Global search
|
||||
└── plugin-toggle.spec.ts — Activate/deactivate plugin
|
||||
```
|
||||
|
||||
### Test Conventions
|
||||
|
||||
- **Test names:** `test_<action>_<condition>_<expected_result>` (e.g., `test_login_with_invalid_credentials_returns_401`)
|
||||
- **Test structure:** Arrange → Act → Assert (AAA pattern)
|
||||
- **Fixtures:** Use `conftest.py` for shared fixtures. No fixture duplication across files.
|
||||
- **Test DB:** Use in-memory or ephemeral PostgreSQL (via testcontainers or pytest-postgresql). NEVER test against production DB.
|
||||
- **Mocking:** Mock external services (SMTP, IMAP, OnlyOffice) in tests. Use `unittest.mock.AsyncMock` for async mocks.
|
||||
- **Assertions:** Use pytest's native `assert` for backend, `expect()` from `@testing-library/jest-dom` for frontend.
|
||||
- **No flaky tests:** Tests must be deterministic. Use explicit waits, not sleeps.
|
||||
- **Test isolation:** Each test must be independent. No test depends on another test's side effects.
|
||||
|
||||
### Don't Modify Tests Rule
|
||||
|
||||
- **NEVER modify existing tests to make them pass.** If a test fails, fix the code, not the test.
|
||||
- **Exception:** If the test itself is wrong (testing incorrect behavior), document why and get approval before changing.
|
||||
- **Test files are owned by the QA process, not the implementer.**
|
||||
- TDD: failing test first → implement → refactor
|
||||
- NEVER modify tests to make them pass — fix the code
|
||||
- Test DB: ephemeral PostgreSQL, NEVER production DB
|
||||
- Mock external services (SMTP, IMAP, OnlyOffice) with AsyncMock
|
||||
- Tests must be deterministic and isolated
|
||||
|
||||
---
|
||||
|
||||
## 3. Conventions
|
||||
## 3. Code Conventions
|
||||
|
||||
### Backend Structure
|
||||
### Backend
|
||||
- Async first: all routes/services `async def`
|
||||
- UUID primary keys only, never integer auto-increment
|
||||
- TIMESTAMPTZ only, never naive datetime
|
||||
- Soft-delete via `deleted_at IS NULL`; hard-delete only with `?gdpr=true`
|
||||
- Pydantic schemas validate input, never validate in routes
|
||||
- All mutations create audit log entries
|
||||
- snake_case files/functions, PascalCase classes
|
||||
- Schemas: `<Entity>Create`, `<Entity>Update`, `<Entity>Read`
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── main.py — FastAPI app entry point, lifespan, middleware registration
|
||||
├── config.py — Pydantic Settings (reads from env vars)
|
||||
├── deps.py — FastAPI dependency injection (auth, db, tenant, permissions)
|
||||
├── core/ — Core infrastructure (cross-cutting concerns)
|
||||
│ ├── db/ — SQLAlchemy engine, session factory, base model
|
||||
│ ├── tenant.py — TenantMixin, ORM auto-filter, tenant context
|
||||
│ ├── auth.py — Session auth, password hashing (bcrypt), RBAC
|
||||
│ ├── event_bus.py — Async in-process event bus
|
||||
│ ├── service_container.py — DI container
|
||||
│ ├── storage.py — File storage (local/S3)
|
||||
│ ├── cache.py — Redis cache wrapper
|
||||
│ ├── jobs.py — ARQ job queue integration
|
||||
│ ├── notifications.py — Notification service
|
||||
│ └── audit.py — Audit log middleware
|
||||
├── models/ — SQLAlchemy ORM models (one file per domain)
|
||||
├── schemas/ — Pydantic schemas (request/response, one file per domain)
|
||||
├── services/ — Business logic (one file per domain)
|
||||
├── routes/ — FastAPI routers (one file per domain)
|
||||
├── plugins/ — Plugin system
|
||||
│ ├── registry.py — Plugin discovery, registration
|
||||
│ ├── manifest.py — Plugin manifest Pydantic schema
|
||||
│ ├── lifecycle.py — Install/activate/deactivate/uninstall
|
||||
│ ├── migrations.py — Plugin DB migration runner
|
||||
│ ├── ui_registry.py — Plugin UI component registration
|
||||
│ └── builtins/ — Built-in plugins
|
||||
│ ├── dms/ — DMS plugin
|
||||
│ ├── calendar/ — Calendar plugin
|
||||
│ ├── mail/ — Mail plugin
|
||||
│ └── tags/ — Tags plugin
|
||||
└── utils/ — Shared utilities (validation, export, import)
|
||||
```
|
||||
### Frontend
|
||||
- TypeScript strict, no `any`
|
||||
- Functional components only, no class components
|
||||
- TanStack Query for server state, Zustand for client state only
|
||||
- React Hook Form + Zod for all forms
|
||||
- Tailwind utility classes, no inline styles
|
||||
- i18n via `t()` from react-i18next, no hardcoded strings
|
||||
- ARIA attributes on all interactive elements, 44px touch targets
|
||||
- PascalCase.tsx for components, camelCase.ts for utilities
|
||||
|
||||
### Backend Naming Conventions
|
||||
|
||||
- **Files:** `snake_case.py` (e.g., `company_service.py`)
|
||||
- **Classes:** `PascalCase` (e.g., `CompanyService`, `CompanyModel`)
|
||||
- **Functions/Methods:** `snake_case` (e.g., `get_company_by_id`)
|
||||
- **Constants:** `UPPER_SNAKE_CASE` (e.g., `SESSION_TIMEOUT_HOURS`)
|
||||
- **Models:** `<Entity>Model` suffix or just `<Entity>` (e.g., `Company`, `Contact`)
|
||||
- **Schemas:** `<Entity>Create`, `<Entity>Update`, `<Entity>Read`, `<Entity>List` (Pydantic)
|
||||
- **Services:** `<Entity>Service` (e.g., `CompanyService`)
|
||||
- **Routers:** `<entity>_router` variable, file name `<entity>_router.py`
|
||||
- **Tests:** `test_<domain>.py` (e.g., `test_companies.py`)
|
||||
|
||||
### Backend Code Conventions
|
||||
|
||||
- **Async first:** All route handlers and service methods are `async def`.
|
||||
- **Type hints:** All function signatures have type hints (Python 3.12+ syntax).
|
||||
- **Docstrings:** All public functions/classes have docstrings (Google style).
|
||||
- **Error handling:** Use FastAPI `HTTPException` with proper status codes. Never raise generic `Exception`.
|
||||
- **Validation:** Pydantic schemas validate input. Never validate in routes directly.
|
||||
- **Tenant scoping:** Never query without tenant filter (ORM auto-filter handles this, but be aware).
|
||||
- **UUID:** All IDs are UUID. Never use integer auto-increment.
|
||||
- **Timestamps:** All datetime fields are `TIMESTAMPTZ`. Never use naive datetime.
|
||||
- **Soft-delete:** Use `deleted_at IS NULL` filter. Never hard-delete without explicit `gdpr=true` flag.
|
||||
- **Audit:** All mutations must create audit log entries. Use the audit middleware/decorator.
|
||||
|
||||
### Frontend Structure
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── main.tsx — React entry point
|
||||
├── App.tsx — Root component, router, providers
|
||||
├── api/ — API client (axios), interceptors, endpoint definitions
|
||||
├── components/ — Shared UI components
|
||||
│ ├── layout/ — Shell, Sidebar, TopBar, ContentArea
|
||||
│ ├── ui/ — Button, Input, Select, Modal, Toast, Table, Card, Badge, Avatar
|
||||
│ └── shared/ — EmptyState, LoadingState, ConfirmDialog, Pagination, Skeleton
|
||||
├── features/ — Feature modules (one folder per feature)
|
||||
│ ├── auth/ — Login, PasswordReset
|
||||
│ ├── companies/ — CompanyList, CompanyDetail, CompanyForm
|
||||
│ ├── contacts/ — ContactList, ContactDetail, ContactForm
|
||||
│ ├── settings/ — SettingsTree, ProfileSettings, RoleEditor
|
||||
│ ├── audit/ — AuditLog
|
||||
│ ├── dashboard/ — Dashboard
|
||||
│ └── search/ — GlobalSearch
|
||||
├── plugins/ — Plugin UI loading framework
|
||||
│ ├── PluginRegistry.tsx — Fetch manifests, register components
|
||||
│ └── PluginLoader.tsx — Dynamic lazy-loading of plugin components
|
||||
├── hooks/ — Custom React hooks (useDebounce, usePagination, useAuth, etc.)
|
||||
├── store/ — Zustand stores (useAuthStore, useUIStore, useTenantStore)
|
||||
├── i18n/ — react-i18next setup + locale files (de.json, en.json)
|
||||
├── styles/ — Global CSS, design tokens (Tailwind config), accessibility
|
||||
└── utils/ — Utilities (format, validation, export, constants)
|
||||
```
|
||||
|
||||
### Frontend Naming Conventions
|
||||
|
||||
- **Files:** `PascalCase.tsx` for components (e.g., `CompanyList.tsx`), `camelCase.ts` for utilities (e.g., `apiClient.ts`)
|
||||
- **Components:** `PascalCase` (e.g., `CompanyList`, `ContactForm`)
|
||||
- **Hooks:** `use<Feature>` (e.g., `useDebounce`, `useAuth`)
|
||||
- **Stores:** `use<Domain>Store` (e.g., `useAuthStore`, `useUIStore`)
|
||||
- **Types/Interfaces:** `PascalCase` (e.g., `CompanyData`, `ContactFormValues`)
|
||||
- **API functions:** `camelCase` (e.g., `getCompanies`, `createContact`)
|
||||
- **Test files:** `<Component>.test.tsx` next to component or in `__tests__/` mirror
|
||||
|
||||
### Frontend Code Conventions
|
||||
|
||||
- **TypeScript strict:** `strict: true` in tsconfig.json. No `any` types.
|
||||
- **Functional components:** Only function components, no class components.
|
||||
- **Hooks:** Custom hooks for reusable logic. No inline hooks in JSX.
|
||||
- **TanStack Query:** Server state via `useQuery` / `useMutation`. No manual fetch in components.
|
||||
- **Zustand:** Client state only (UI toggles, theme, active tenant). No server data in Zustand.
|
||||
- **React Hook Form + Zod:** All forms use `react-hook-form` with `zodResolver`.
|
||||
- **Tailwind CSS:** No custom CSS files (except global + accessibility). Use Tailwind utility classes.
|
||||
- **i18n:** All user-visible strings go through `t()` from `react-i18next`. No hardcoded strings.
|
||||
- **Accessibility:** ARIA attributes on all interactive elements. 44px touch targets. Keyboard navigation.
|
||||
- **Lazy loading:** Plugin components use `React.lazy()` with `Suspense` boundaries.
|
||||
|
||||
### Git Conventions
|
||||
|
||||
- **Branch naming:** `feature/T01-core-infrastructure`, `fix/auth-tenant-isolation`, `hotfix/critical-bug`
|
||||
- **Commit messages:** Conventional Commits format:
|
||||
- `feat(core): implement auth system with session-based login`
|
||||
- `fix(dms): resolve folder permission bypass on move`
|
||||
- `test(mail): add IMAP sync integration tests`
|
||||
- `refactor(calendar): extract recurrence engine to separate module`
|
||||
- `docs(architecture): update ADR-03 with plugin lifecycle details`
|
||||
- **PR titles:** `[T01] Core Infrastructure + Multi-Tenant + Auth System`
|
||||
- **Branch from:** `main` (or feature branch for sub-features)
|
||||
- **Merge strategy:** Squash merge to `main` after review + CI passes
|
||||
### Git
|
||||
- Conventional Commits: `feat(core): ...`, `fix(dms): ...`
|
||||
- Squash merge to main after review
|
||||
|
||||
---
|
||||
|
||||
## 4. Task-Zuweisung (Subagenten pro Task)
|
||||
## 4. Forbidden Patterns
|
||||
|
||||
### Phasen-Plan
|
||||
### Backend
|
||||
- ❌ SQLite — PostgreSQL 16 only
|
||||
- ❌ Jinja2/server-side HTML rendering — API-only backend
|
||||
- ❌ Cross-tenant data access — ORM auto-filter must not be bypassed
|
||||
- ❌ Plaintext passwords — bcrypt cost=12
|
||||
- ❌ JWT auth — session-based with HttpOnly cookies only
|
||||
- ❌ Naive datetime — TIMESTAMPTZ only
|
||||
- ❌ Integer IDs — UUID only
|
||||
- ❌ Hard-delete without `?gdpr=true`
|
||||
- ❌ Manual tenant filter — ORM auto-filter handles it
|
||||
- ❌ Sync I/O in routes — use asyncpg, aiofiles
|
||||
- ❌ Raw SQL without tenant_id check
|
||||
- ❌ Secrets in code — env vars only
|
||||
- ❌ Unvalidated input — Pydantic schemas required
|
||||
- ❌ Missing audit log on mutations
|
||||
- ❌ Plugin tables without tenant_id
|
||||
|
||||
#### v1 Core Phases (Phase 3 — Implementation)
|
||||
### Frontend
|
||||
- ❌ Class components
|
||||
- ❌ Inline styles — Tailwind only
|
||||
- ❌ Hardcoded strings — use `t()`
|
||||
- ❌ Manual fetch/axios in components — use TanStack Query
|
||||
- ❌ Server data in Zustand
|
||||
- ❌ `any` types
|
||||
- ❌ Missing ARIA attributes
|
||||
- ❌ Touch targets < 44px
|
||||
- ❌ Direct DOM manipulation — use React refs
|
||||
- ❌ `dangerouslySetInnerHTML` without sanitization
|
||||
|
||||
| Phase | Tasks | Parallel | Subagent Profile | Description |
|
||||
|-------|-------|----------|-------------------|-------------|
|
||||
| 1 | T01 | No | implementation_engineer | Foundation: Core, Auth, Multi-Tenant, RLS, Rate Limiting |
|
||||
| 2 | T02, T03 | Yes (2 agents) | implementation_engineer ×2 | Core entities + Plugin framework parallel |
|
||||
| 3 | T07a, T09 | Yes (2 agents) | implementation_engineer ×2 | Frontend Shell+Auth+UI Library + KI-Copilot/Workflow parallel |
|
||||
| 4 | T07b | No | implementation_engineer | Frontend Feature Pages (Companies, Contacts, Settings, Dashboard, Search) |
|
||||
| 5 | T10 | No | implementation_engineer | Monitoring, Performance, Doku, Environment Config |
|
||||
|
||||
#### v2 Plugin Phases (nach v1 Deployment)
|
||||
|
||||
| Phase | Tasks | Parallel | Subagent Profile | Description |
|
||||
|-------|-------|----------|-------------------|-------------|
|
||||
| 6 | T04, T05, T06, T11 | Yes (4 agents) | implementation_engineer ×4 | DMS, Calendar, Mail, Tags+Permissions backends parallel |
|
||||
| 7 | T08a, T08b, T08c | Yes (3 agents) | implementation_engineer ×3 | Frontend DMS+Tags, Calendar, Mail+Search parallel |
|
||||
|
||||
### Task-to-Subagent Mapping
|
||||
|
||||
| Task ID | Title | Subagent | Dependencies | Phase | Scope |
|
||||
|---------|-------|----------|--------------|-------|-------|
|
||||
| T01 | Core Infrastructure + Multi-Tenant + Auth | implementation_engineer | — | 1 | v1 |
|
||||
| T02 | Company + Contact + Import/Export | implementation_engineer | T01 | 2 | v1 |
|
||||
| T03 | Plugin System Framework | implementation_engineer | T01 | 2 | v1 |
|
||||
| T07a | Frontend SPA — Shell, Auth, Routing, i18n, UI Library | implementation_engineer | T01 | 3 | v1 |
|
||||
| T07b | Frontend SPA — Companies, Contacts, Settings, Dashboard, Search | implementation_engineer | T01, T02, T07a | 4 | v1 |
|
||||
| T09 | KI-Copilot + Workflow Engine | implementation_engineer | T01, T02 | 3 | v1 |
|
||||
| T10 | Monitoring + Performance + Doku + Env Config | implementation_engineer | T01, T02 | 5 | v1 |
|
||||
| T04 | DMS Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T05 | Calendar Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T06 | Mail Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T11 | Tags + Permissions + Entity Links Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T08a | Frontend DMS + Tags + Permissions UI | implementation_engineer | T04, T07b | 7 | v2 |
|
||||
| T08b | Frontend Calendar UI | implementation_engineer | T05, T07b | 7 | v2 |
|
||||
| T08c | Frontend Mail + Global Search UI | implementation_engineer | T06, T07b | 7 | v2 |
|
||||
|
||||
### Parallelization Notes
|
||||
|
||||
**v1 Phases:**
|
||||
- **Phase 2:** T02 (Company/Contact) and T03 (Plugin Framework) are independent after T01 — safe to run in parallel.
|
||||
- **Phase 3:** T07a (Frontend Shell+Auth+UI Library) depends only on T01. T09 (KI/Workflow) depends on T01+T02. Both can run in parallel if API contracts are frozen.
|
||||
- **Phase 4:** T07b (Frontend Feature Pages) depends on T07a (UI library, routing, auth) + T02 (company/contact API). Must run after T07a.
|
||||
- **Phase 5:** T10 (Monitoring+Doku) depends on T01+T02. Can run parallel with T07b.
|
||||
|
||||
**v2 Phases (after v1 deployment):**
|
||||
- **Phase 6:** T04 (DMS), T05 (Calendar), T06 (Mail), T11 (Tags+Perm) all depend on T01+T03 — safe to run in parallel.
|
||||
- **Phase 7:** T08a/T08b/T08c depend on T07b + respective backend (T04/T05/T06) — safe to run in parallel.
|
||||
|
||||
### Block Rules
|
||||
|
||||
- Block = max 3 Tasks per implementation block.
|
||||
- After each block: quality_reviewer review → block_compactor → context_compactor → User checkpoint.
|
||||
- quality_reviewer and release_auditor do NOT count toward the 3-task limit.
|
||||
- After 3 blocks (9 tasks): release_auditor runs full audit.
|
||||
- Token budget: ~3000 tokens per task. If tool result >5000 tokens: context_compactor.
|
||||
### Deployment
|
||||
- ❌ Running as root in container — use app:app
|
||||
- ❌ Exposed DB port in production
|
||||
- ❌ Missing Docker health checks
|
||||
- ❌ Ephemeral storage — use named volumes
|
||||
- ❌ Secrets in docker-compose.yml
|
||||
|
||||
---
|
||||
|
||||
## 5. Forbidden Patterns
|
||||
## 5. Quality Gates
|
||||
|
||||
### Backend Forbidden
|
||||
|
||||
- ❌ **SQLite:** No SQLite as database. PostgreSQL 16 only (ADR-01).
|
||||
- ❌ **Jinja2:** No server-side HTML rendering. API-only backend (ADR-03).
|
||||
- ❌ **Cross-Tenant Data Access:** No query without tenant_id filter. ORM auto-filter must not be bypassed.
|
||||
- ❌ **Plaintext Passwords:** Passwords must be bcrypt-hashed (cost=12). Never store or log plaintext.
|
||||
- ❌ **JWT Tokens:** No JWT auth in v1. Session-based auth with HttpOnly cookies only (ADR-05).
|
||||
- ❌ **Naive Datetime:** All datetime fields must be timezone-aware (TIMESTAMPTZ). Never use `datetime.now()` without tz.
|
||||
- ❌ **Integer IDs:** All primary keys are UUID. Never use auto-increment integer IDs.
|
||||
- ❌ **Hard-Delete without GDPR flag:** Companies/Contacts use soft-delete. Hard-delete only with explicit `?gdpr=true`.
|
||||
- ❌ **Manual Tenant Filter:** Never manually add `.filter(Tenant.id == x)` in services. The ORM auto-filter handles this.
|
||||
- ❌ **Sync I/O in Routes:** All route handlers are `async def`. Never use blocking I/O (use `asyncpg`, `aiofiles`, etc.).
|
||||
- ❌ **Raw SQL without Tenant Check:** Any raw SQL query must explicitly include `tenant_id` filter.
|
||||
- ❌ **Secrets in Code:** No hardcoded secrets. All secrets via environment variables.
|
||||
- ❌ **Unvalidated Input:** All request bodies validated by Pydantic schemas. Never trust raw request data.
|
||||
- ❌ **Missing Audit Log:** All create/update/delete operations must create audit log entries.
|
||||
- ❌ **Plugin Tables without tenant_id:** All plugin-created tables must include `tenant_id` column. The migration validator enforces this.
|
||||
|
||||
### Frontend Forbidden
|
||||
|
||||
- ❌ **Class Components:** No class components. Functional components with hooks only.
|
||||
- ❌ **Inline Styles:** No `style={{}}` props. Use Tailwind utility classes.
|
||||
- ❌ **Hardcoded Strings:** No user-visible hardcoded strings. Use `t()` from i18n.
|
||||
- ❌ **Manual Fetch in Components:** No `fetch()` or `axios` calls in components. Use TanStack Query hooks.
|
||||
- ❌ **Server Data in Zustand:** Zustand is for client state only. Server data goes in TanStack Query.
|
||||
- ❌ **`any` Types:** No `any` type. Use proper TypeScript types.
|
||||
- ❌ **Missing ARIA Attributes:** All interactive elements must have ARIA labels.
|
||||
- ❌ **Touch Targets < 44px:** All buttons/links must have minimum 44px touch target.
|
||||
- ❌ **Direct DOM Manipulation:** No `document.getElementById()` or `querySelector()` in components. Use React refs.
|
||||
- ❌ **Unsafe HTML Rendering:** No `dangerouslySetInnerHTML` without sanitization. Mail bodies must be sanitized (DOMPurify equivalent).
|
||||
|
||||
### Deployment Forbidden
|
||||
|
||||
- ❌ **Running as Root in Container:** Containers run as non-root user (app:app).
|
||||
- ❌ **Exposed DB Port in Production:** PostgreSQL port (5432) must not be exposed externally in production.
|
||||
- ❌ **No Health Check:** All services must have Docker health checks configured.
|
||||
- ❌ **No Volume for Storage:** File storage must use a named volume, not ephemeral container storage.
|
||||
- ❌ **Secrets in docker-compose.yml:** No secrets in compose file. Use `.env` file or Docker secrets.
|
||||
- Per-Task: tests pass, coverage met, tsc/ruff clean, build succeeds, no forbidden patterns
|
||||
- Phase: all tasks pass → quality_reviewer review → user checkpoint
|
||||
- Release: all tasks complete → release_auditor audit → Docker builds → health 200 → E2E pass
|
||||
|
||||
---
|
||||
|
||||
## 6. Quality Gates
|
||||
## 6. ADRs
|
||||
|
||||
### Per-Task Quality Gate
|
||||
|
||||
Before a task is marked complete:
|
||||
1. All test_spec commands must pass.
|
||||
2. Coverage target must be met (measured by pytest-cov / vitest coverage).
|
||||
3. TypeScript compiles without errors (`tsc --noEmit`).
|
||||
4. Linting passes (ruff for backend, eslint for frontend).
|
||||
5. Build succeeds (Vite build for frontend, no build step for backend).
|
||||
6. No forbidden patterns detected.
|
||||
7. All acceptance criteria verified as testable.
|
||||
|
||||
### Phase Gate (after each phase)
|
||||
|
||||
1. All tasks in the phase pass their quality gates.
|
||||
2. quality_reviewer subagent reviews the phase output.
|
||||
3. No critical issues from quality_reviewer.
|
||||
4. Block compactor saves progress.
|
||||
5. User checkpoint before next phase.
|
||||
|
||||
### Release Gate (before v1 deployment)
|
||||
|
||||
1. All 7 v1 tasks complete (T01, T02, T03, T07a, T07b, T09, T10).
|
||||
2. release_auditor runs full audit.
|
||||
3. Docker Compose builds and starts successfully.
|
||||
4. Health endpoint returns 200.
|
||||
5. E2E tests (Playwright) pass.
|
||||
6. All forbidden patterns checked.
|
||||
|
||||
### v2 Release Gate (before v2 plugin deployment)
|
||||
|
||||
1. All 7 v2 tasks complete (T04, T05, T06, T11, T08a, T08b, T08c).
|
||||
2. release_auditor runs full audit.
|
||||
3. All plugin backends + frontends pass quality gates.
|
||||
4. Plugin install/activate/deactivate lifecycle tested.
|
||||
5. All forbidden patterns checked.
|
||||
|
||||
---
|
||||
|
||||
## 7. Environment Setup
|
||||
|
||||
### Development Environment
|
||||
|
||||
| Variable | Value | Purpose |
|
||||
|----------|-------|---------|
|
||||
| `POSTGRES_HOST` | `localhost` (dev) / `postgres` (docker) | Database host |
|
||||
| `POSTGRES_PORT` | `5432` | Database port |
|
||||
| `POSTGRES_DB` | `leocrm` | Database name |
|
||||
| `POSTGRES_USER` | `leocrm` | Database user |
|
||||
| `POSTGRES_PASSWORD` | (from .env) | Database password |
|
||||
| `REDIS_URL` | `redis://localhost:6379/0` | Redis for cache + sessions + jobs |
|
||||
| `LEOCRM_SECRET_KEY` | (min 32 chars) | Session signing secret |
|
||||
| `SESSION_TIMEOUT_HOURS` | `8` | Session expiry |
|
||||
| `MAIL_ENCRYPTION_KEY` | (32-byte hex) | AES-256 key for mail credentials |
|
||||
| `STORAGE_BACKEND` | `local` (dev) / `s3` (prod) | File storage backend |
|
||||
| `STORAGE_PATH` | `/data/leocrm/storage` | Local storage path |
|
||||
| `ONLYOFFICE_URL` | `http://onlyoffice:80` | OnlyOffice document server |
|
||||
| `LOG_LEVEL` | `INFO` | Logging level |
|
||||
|
||||
### Test Environment
|
||||
|
||||
- Test DB: Ephemeral PostgreSQL (pytest-postgresql or testcontainers).
|
||||
- Test Redis: Ephemeral or fakeredis.
|
||||
- External services (IMAP, SMTP, OnlyOffice): Mocked via `unittest.mock.AsyncMock`.
|
||||
- Test fixtures in `conftest.py` provide: test client, authenticated client (per role), seeded data.
|
||||
|
||||
---
|
||||
|
||||
## 8. Architecture Reference
|
||||
|
||||
Full architecture details: `architecture.md`
|
||||
|
||||
Full task graph with test specs: `task_graph.json`
|
||||
|
||||
Key ADRs:
|
||||
- ADR-01: PostgreSQL 16 (not SQLite)
|
||||
- ADR-02: ARQ (not Celery)
|
||||
- ADR-03: Built-in plugins with manifest (not dynamic pip-install)
|
||||
- ADR-03: Built-in plugins with manifest (not pip-install)
|
||||
- ADR-04: TanStack Query (not Redux)
|
||||
- ADR-05: Session-based auth (not JWT)
|
||||
- ADR-06: Soft-delete with `deleted_at` column
|
||||
- ADR-06: Soft-delete with `deleted_at`
|
||||
|
||||
---
|
||||
|
||||
## Handoff
|
||||
|
||||
- **AGENTS.md status:** COMPLETE
|
||||
- **task_graph.json status:** COMPLETE (14 tasks: 7 v1 + 7 v2, all with test_spec, 143 features covered, v1/v2 separated, v2.1.0)
|
||||
- **architecture.md status:** COMPLETE (73/73 v1 features referenced, v2 sections marked)
|
||||
- **Ready for v1 implementation:** YES (pending quality_reviewer review + plan_mode transition to implementation_allowed)
|
||||
- **v2 implementation:** After v1 deployment, separate phase
|
||||
Full architecture: `architecture.md` | Full task graph: `task_graph.json`
|
||||
|
||||
+571
@@ -0,0 +1,571 @@
|
||||
# LeoCRM — AGENTS.md
|
||||
|
||||
**Projekt:** leocrm
|
||||
**Erstellt:** 2026-06-28
|
||||
**Status:** Draft — ready for implementation
|
||||
|
||||
---
|
||||
|
||||
## 1. Build & Test Commands
|
||||
|
||||
### Backend (Python / FastAPI)
|
||||
|
||||
#### Setup
|
||||
```bash
|
||||
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e ".[dev]"
|
||||
```
|
||||
|
||||
#### Run Dev Server
|
||||
```bash
|
||||
|
||||
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||
```
|
||||
|
||||
#### Database Migrations (Alembic)
|
||||
```bash
|
||||
|
||||
# Generate migration after model changes
|
||||
alembic revision --autogenerate -m "description"
|
||||
# Apply migrations
|
||||
alembic upgrade head
|
||||
# Rollback one migration
|
||||
alembic downgrade -1
|
||||
```
|
||||
|
||||
#### Run All Backend Tests
|
||||
```bash
|
||||
|
||||
python -m pytest -v --tb=short
|
||||
```
|
||||
|
||||
#### Run Specific Test File
|
||||
```bash
|
||||
|
||||
python -m pytest tests/test_auth.py -v --tb=short
|
||||
```
|
||||
|
||||
#### Run Tests with Coverage
|
||||
```bash
|
||||
|
||||
python -m pytest --cov=app --cov-report=term-missing --cov-report=html
|
||||
```
|
||||
|
||||
#### Run Tests with Grep Filter
|
||||
```bash
|
||||
|
||||
python -m pytest -k 'tenant or auth' -v
|
||||
```
|
||||
|
||||
#### Type Checking
|
||||
```bash
|
||||
|
||||
mypy app/ --ignore-missing-imports
|
||||
```
|
||||
|
||||
#### Linting
|
||||
```bash
|
||||
|
||||
ruff check app/
|
||||
ruff format app/
|
||||
```
|
||||
|
||||
### Frontend (React / Vite / TypeScript)
|
||||
|
||||
#### Setup
|
||||
```bash
|
||||
cd frontend
|
||||
npm install
|
||||
```
|
||||
|
||||
#### Run Dev Server
|
||||
```bash
|
||||
cd frontend
|
||||
npm run dev
|
||||
```
|
||||
|
||||
#### Build Production
|
||||
```bash
|
||||
cd frontend
|
||||
npm run build
|
||||
```
|
||||
|
||||
#### Run All Frontend Tests
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest run --reporter=verbose
|
||||
```
|
||||
|
||||
#### Run Tests with Coverage
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest run --coverage
|
||||
```
|
||||
|
||||
#### Run Tests in Watch Mode (dev)
|
||||
```bash
|
||||
cd frontend
|
||||
npx vitest watch
|
||||
```
|
||||
|
||||
#### Type Checking
|
||||
```bash
|
||||
cd frontend
|
||||
npx tsc --noEmit
|
||||
```
|
||||
|
||||
#### Linting
|
||||
```bash
|
||||
cd frontend
|
||||
npx eslint src/ --ext .ts,.tsx
|
||||
```
|
||||
|
||||
### Docker Compose (Full Stack)
|
||||
|
||||
#### Build All Services
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
#### Start All Services
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
#### View Logs
|
||||
```bash
|
||||
docker compose logs -f backend
|
||||
```
|
||||
|
||||
#### Stop All Services
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
#### Validate Compose Config
|
||||
```bash
|
||||
docker compose config --quiet
|
||||
```
|
||||
|
||||
### E2E Tests (Playwright)
|
||||
|
||||
```bash
|
||||
cd e2e
|
||||
npx playwright install
|
||||
npx playwright test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Test Rules
|
||||
|
||||
### TDD (Test-Driven Development)
|
||||
|
||||
- **Red-Green-Refactor:** Write failing test first → implement minimum code to pass → refactor.
|
||||
- **Every new endpoint gets a test BEFORE implementation.**
|
||||
- **Every bug fix starts with a reproduction test.**
|
||||
|
||||
### Coverage Targets
|
||||
|
||||
| Layer | Coverage Target | Measured By |
|
||||
|-------|----------------|-------------|
|
||||
| Backend Core (app/core/) | 85% | pytest-cov |
|
||||
| Backend Models+Services | 85% | pytest-cov |
|
||||
| Backend Routes | 85% | pytest-cov |
|
||||
| Backend Plugins | 80% | pytest-cov |
|
||||
| Frontend Components | 75% | vitest coverage |
|
||||
| Frontend Plugin UI | 70% | vitest coverage |
|
||||
| E2E (critical paths) | 100% of defined specs | Playwright |
|
||||
|
||||
### Test File Structure
|
||||
|
||||
#### Backend
|
||||
```
|
||||
backend/tests/
|
||||
├── conftest.py — Fixtures: test client, test DB, auth helpers, seed data
|
||||
├── test_auth.py — Auth endpoints, RBAC, password reset
|
||||
├── test_tenant.py — Tenant isolation, cross-tenant access
|
||||
├── test_companies.py — Company CRUD, search, filter, pagination, soft-delete
|
||||
├── test_contacts.py — Contact CRUD, N:M links, GDPR delete
|
||||
├── test_import_export.py — CSV import/export, XLSX export, dry-run preview
|
||||
├── test_plugins.py — Plugin lifecycle, event bus, migrations
|
||||
├── test_dms.py — DMS folders, files, upload, shares, permissions
|
||||
├── test_calendar.py — Entries, recurrence, kanban, ICS, resources
|
||||
├── test_mail.py — Accounts, IMAP sync, send, threading, rules, PGP
|
||||
├── test_tags.py — Tag CRUD, assignment, bulk
|
||||
├── test_notifications.py — Notification CRUD, unread count
|
||||
├── test_health.py — Health endpoint
|
||||
├── test_ai_copilot.py — KI-Copilot API, RBAC enforcement, history
|
||||
├── test_workflows.py — Workflow CRUD, instances, approval/rejection, event triggers
|
||||
├── test_monitoring.py — Extended health, Prometheus metrics, alerting
|
||||
└── test_performance.py — 200k seed, list <500ms, FTS <500ms, streaming export
|
||||
```
|
||||
|
||||
#### Frontend
|
||||
```
|
||||
frontend/src/__tests__/
|
||||
├── components/ — UI component unit tests (Button, Input, Modal, Table, etc.)
|
||||
├── features/ — Feature integration tests (CompanyList, ContactForm, etc.)
|
||||
├── hooks/ — Custom hook tests (useDebounce, usePagination, etc.)
|
||||
├── plugins/ — Plugin UI tests (DMS, Calendar, Mail, Tags)
|
||||
└── search/ — Global search tests
|
||||
```
|
||||
|
||||
#### E2E
|
||||
```
|
||||
e2e/
|
||||
├── auth.spec.ts — Login → logout flow
|
||||
├── company-crud.spec.ts — Create → edit → delete company
|
||||
├── contact-crud.spec.ts — Create → link to company → delete
|
||||
├── search.spec.ts — Global search
|
||||
└── plugin-toggle.spec.ts — Activate/deactivate plugin
|
||||
```
|
||||
|
||||
### Test Conventions
|
||||
|
||||
- **Test names:** `test_<action>_<condition>_<expected_result>` (e.g., `test_login_with_invalid_credentials_returns_401`)
|
||||
- **Test structure:** Arrange → Act → Assert (AAA pattern)
|
||||
- **Fixtures:** Use `conftest.py` for shared fixtures. No fixture duplication across files.
|
||||
- **Test DB:** Use in-memory or ephemeral PostgreSQL (via testcontainers or pytest-postgresql). NEVER test against production DB.
|
||||
- **Mocking:** Mock external services (SMTP, IMAP, OnlyOffice) in tests. Use `unittest.mock.AsyncMock` for async mocks.
|
||||
- **Assertions:** Use pytest's native `assert` for backend, `expect()` from `@testing-library/jest-dom` for frontend.
|
||||
- **No flaky tests:** Tests must be deterministic. Use explicit waits, not sleeps.
|
||||
- **Test isolation:** Each test must be independent. No test depends on another test's side effects.
|
||||
|
||||
### Don't Modify Tests Rule
|
||||
|
||||
- **NEVER modify existing tests to make them pass.** If a test fails, fix the code, not the test.
|
||||
- **Exception:** If the test itself is wrong (testing incorrect behavior), document why and get approval before changing.
|
||||
- **Test files are owned by the QA process, not the implementer.**
|
||||
|
||||
---
|
||||
|
||||
## 3. Conventions
|
||||
|
||||
### Backend Structure
|
||||
|
||||
```
|
||||
backend/app/
|
||||
├── main.py — FastAPI app entry point, lifespan, middleware registration
|
||||
├── config.py — Pydantic Settings (reads from env vars)
|
||||
├── deps.py — FastAPI dependency injection (auth, db, tenant, permissions)
|
||||
├── core/ — Core infrastructure (cross-cutting concerns)
|
||||
│ ├── db/ — SQLAlchemy engine, session factory, base model
|
||||
│ ├── tenant.py — TenantMixin, ORM auto-filter, tenant context
|
||||
│ ├── auth.py — Session auth, password hashing (bcrypt), RBAC
|
||||
│ ├── event_bus.py — Async in-process event bus
|
||||
│ ├── service_container.py — DI container
|
||||
│ ├── storage.py — File storage (local/S3)
|
||||
│ ├── cache.py — Redis cache wrapper
|
||||
│ ├── jobs.py — ARQ job queue integration
|
||||
│ ├── notifications.py — Notification service
|
||||
│ └── audit.py — Audit log middleware
|
||||
├── models/ — SQLAlchemy ORM models (one file per domain)
|
||||
├── schemas/ — Pydantic schemas (request/response, one file per domain)
|
||||
├── services/ — Business logic (one file per domain)
|
||||
├── routes/ — FastAPI routers (one file per domain)
|
||||
├── plugins/ — Plugin system
|
||||
│ ├── registry.py — Plugin discovery, registration
|
||||
│ ├── manifest.py — Plugin manifest Pydantic schema
|
||||
│ ├── lifecycle.py — Install/activate/deactivate/uninstall
|
||||
│ ├── migrations.py — Plugin DB migration runner
|
||||
│ ├── ui_registry.py — Plugin UI component registration
|
||||
│ └── builtins/ — Built-in plugins
|
||||
│ ├── dms/ — DMS plugin
|
||||
│ ├── calendar/ — Calendar plugin
|
||||
│ ├── mail/ — Mail plugin
|
||||
│ └── tags/ — Tags plugin
|
||||
└── utils/ — Shared utilities (validation, export, import)
|
||||
```
|
||||
|
||||
### Backend Naming Conventions
|
||||
|
||||
- **Files:** `snake_case.py` (e.g., `company_service.py`)
|
||||
- **Classes:** `PascalCase` (e.g., `CompanyService`, `CompanyModel`)
|
||||
- **Functions/Methods:** `snake_case` (e.g., `get_company_by_id`)
|
||||
- **Constants:** `UPPER_SNAKE_CASE` (e.g., `SESSION_TIMEOUT_HOURS`)
|
||||
- **Models:** `<Entity>Model` suffix or just `<Entity>` (e.g., `Company`, `Contact`)
|
||||
- **Schemas:** `<Entity>Create`, `<Entity>Update`, `<Entity>Read`, `<Entity>List` (Pydantic)
|
||||
- **Services:** `<Entity>Service` (e.g., `CompanyService`)
|
||||
- **Routers:** `<entity>_router` variable, file name `<entity>_router.py`
|
||||
- **Tests:** `test_<domain>.py` (e.g., `test_companies.py`)
|
||||
|
||||
### Backend Code Conventions
|
||||
|
||||
- **Async first:** All route handlers and service methods are `async def`.
|
||||
- **Type hints:** All function signatures have type hints (Python 3.12+ syntax).
|
||||
- **Docstrings:** All public functions/classes have docstrings (Google style).
|
||||
- **Error handling:** Use FastAPI `HTTPException` with proper status codes. Never raise generic `Exception`.
|
||||
- **Validation:** Pydantic schemas validate input. Never validate in routes directly.
|
||||
- **Tenant scoping:** Never query without tenant filter (ORM auto-filter handles this, but be aware).
|
||||
- **UUID:** All IDs are UUID. Never use integer auto-increment.
|
||||
- **Timestamps:** All datetime fields are `TIMESTAMPTZ`. Never use naive datetime.
|
||||
- **Soft-delete:** Use `deleted_at IS NULL` filter. Never hard-delete without explicit `gdpr=true` flag.
|
||||
- **Audit:** All mutations must create audit log entries. Use the audit middleware/decorator.
|
||||
|
||||
### Frontend Structure
|
||||
|
||||
```
|
||||
frontend/src/
|
||||
├── main.tsx — React entry point
|
||||
├── App.tsx — Root component, router, providers
|
||||
├── api/ — API client (axios), interceptors, endpoint definitions
|
||||
├── components/ — Shared UI components
|
||||
│ ├── layout/ — Shell, Sidebar, TopBar, ContentArea
|
||||
│ ├── ui/ — Button, Input, Select, Modal, Toast, Table, Card, Badge, Avatar
|
||||
│ └── shared/ — EmptyState, LoadingState, ConfirmDialog, Pagination, Skeleton
|
||||
├── features/ — Feature modules (one folder per feature)
|
||||
│ ├── auth/ — Login, PasswordReset
|
||||
│ ├── companies/ — CompanyList, CompanyDetail, CompanyForm
|
||||
│ ├── contacts/ — ContactList, ContactDetail, ContactForm
|
||||
│ ├── settings/ — SettingsTree, ProfileSettings, RoleEditor
|
||||
│ ├── audit/ — AuditLog
|
||||
│ ├── dashboard/ — Dashboard
|
||||
│ └── search/ — GlobalSearch
|
||||
├── plugins/ — Plugin UI loading framework
|
||||
│ ├── PluginRegistry.tsx — Fetch manifests, register components
|
||||
│ └── PluginLoader.tsx — Dynamic lazy-loading of plugin components
|
||||
├── hooks/ — Custom React hooks (useDebounce, usePagination, useAuth, etc.)
|
||||
├── store/ — Zustand stores (useAuthStore, useUIStore, useTenantStore)
|
||||
├── i18n/ — react-i18next setup + locale files (de.json, en.json)
|
||||
├── styles/ — Global CSS, design tokens (Tailwind config), accessibility
|
||||
└── utils/ — Utilities (format, validation, export, constants)
|
||||
```
|
||||
|
||||
### Frontend Naming Conventions
|
||||
|
||||
- **Files:** `PascalCase.tsx` for components (e.g., `CompanyList.tsx`), `camelCase.ts` for utilities (e.g., `apiClient.ts`)
|
||||
- **Components:** `PascalCase` (e.g., `CompanyList`, `ContactForm`)
|
||||
- **Hooks:** `use<Feature>` (e.g., `useDebounce`, `useAuth`)
|
||||
- **Stores:** `use<Domain>Store` (e.g., `useAuthStore`, `useUIStore`)
|
||||
- **Types/Interfaces:** `PascalCase` (e.g., `CompanyData`, `ContactFormValues`)
|
||||
- **API functions:** `camelCase` (e.g., `getCompanies`, `createContact`)
|
||||
- **Test files:** `<Component>.test.tsx` next to component or in `__tests__/` mirror
|
||||
|
||||
### Frontend Code Conventions
|
||||
|
||||
- **TypeScript strict:** `strict: true` in tsconfig.json. No `any` types.
|
||||
- **Functional components:** Only function components, no class components.
|
||||
- **Hooks:** Custom hooks for reusable logic. No inline hooks in JSX.
|
||||
- **TanStack Query:** Server state via `useQuery` / `useMutation`. No manual fetch in components.
|
||||
- **Zustand:** Client state only (UI toggles, theme, active tenant). No server data in Zustand.
|
||||
- **React Hook Form + Zod:** All forms use `react-hook-form` with `zodResolver`.
|
||||
- **Tailwind CSS:** No custom CSS files (except global + accessibility). Use Tailwind utility classes.
|
||||
- **i18n:** All user-visible strings go through `t()` from `react-i18next`. No hardcoded strings.
|
||||
- **Accessibility:** ARIA attributes on all interactive elements. 44px touch targets. Keyboard navigation.
|
||||
- **Lazy loading:** Plugin components use `React.lazy()` with `Suspense` boundaries.
|
||||
|
||||
### Git Conventions
|
||||
|
||||
- **Branch naming:** `feature/T01-core-infrastructure`, `fix/auth-tenant-isolation`, `hotfix/critical-bug`
|
||||
- **Commit messages:** Conventional Commits format:
|
||||
- `feat(core): implement auth system with session-based login`
|
||||
- `fix(dms): resolve folder permission bypass on move`
|
||||
- `test(mail): add IMAP sync integration tests`
|
||||
- `refactor(calendar): extract recurrence engine to separate module`
|
||||
- `docs(architecture): update ADR-03 with plugin lifecycle details`
|
||||
- **PR titles:** `[T01] Core Infrastructure + Multi-Tenant + Auth System`
|
||||
- **Branch from:** `main` (or feature branch for sub-features)
|
||||
- **Merge strategy:** Squash merge to `main` after review + CI passes
|
||||
|
||||
---
|
||||
|
||||
## 4. Task-Zuweisung (Subagenten pro Task)
|
||||
|
||||
### Phasen-Plan
|
||||
|
||||
#### v1 Core Phases (Phase 3 — Implementation)
|
||||
|
||||
| Phase | Tasks | Parallel | Subagent Profile | Description |
|
||||
|-------|-------|----------|-------------------|-------------|
|
||||
| 1 | T01 | No | implementation_engineer | Foundation: Core, Auth, Multi-Tenant, RLS, Rate Limiting |
|
||||
| 2 | T02, T03 | Yes (2 agents) | implementation_engineer ×2 | Core entities + Plugin framework parallel |
|
||||
| 3 | T07a, T09 | Yes (2 agents) | implementation_engineer ×2 | Frontend Shell+Auth+UI Library + KI-Copilot/Workflow parallel |
|
||||
| 4 | T07b | No | implementation_engineer | Frontend Feature Pages (Companies, Contacts, Settings, Dashboard, Search) |
|
||||
| 5 | T10 | No | implementation_engineer | Monitoring, Performance, Doku, Environment Config |
|
||||
|
||||
#### v2 Plugin Phases (nach v1 Deployment)
|
||||
|
||||
| Phase | Tasks | Parallel | Subagent Profile | Description |
|
||||
|-------|-------|----------|-------------------|-------------|
|
||||
| 6 | T04, T05, T06, T11 | Yes (4 agents) | implementation_engineer ×4 | DMS, Calendar, Mail, Tags+Permissions backends parallel |
|
||||
| 7 | T08a, T08b, T08c | Yes (3 agents) | implementation_engineer ×3 | Frontend DMS+Tags, Calendar, Mail+Search parallel |
|
||||
|
||||
### Task-to-Subagent Mapping
|
||||
|
||||
| Task ID | Title | Subagent | Dependencies | Phase | Scope |
|
||||
|---------|-------|----------|--------------|-------|-------|
|
||||
| T01 | Core Infrastructure + Multi-Tenant + Auth | implementation_engineer | — | 1 | v1 |
|
||||
| T02 | Company + Contact + Import/Export | implementation_engineer | T01 | 2 | v1 |
|
||||
| T03 | Plugin System Framework | implementation_engineer | T01 | 2 | v1 |
|
||||
| T07a | Frontend SPA — Shell, Auth, Routing, i18n, UI Library | implementation_engineer | T01 | 3 | v1 |
|
||||
| T07b | Frontend SPA — Companies, Contacts, Settings, Dashboard, Search | implementation_engineer | T01, T02, T07a | 4 | v1 |
|
||||
| T09 | KI-Copilot + Workflow Engine | implementation_engineer | T01, T02 | 3 | v1 |
|
||||
| T10 | Monitoring + Performance + Doku + Env Config | implementation_engineer | T01, T02 | 5 | v1 |
|
||||
| T04 | DMS Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T05 | Calendar Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T06 | Mail Plugin Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T11 | Tags + Permissions + Entity Links Backend | implementation_engineer | T01, T03 | 6 | v2 |
|
||||
| T08a | Frontend DMS + Tags + Permissions UI | implementation_engineer | T04, T07b | 7 | v2 |
|
||||
| T08b | Frontend Calendar UI | implementation_engineer | T05, T07b | 7 | v2 |
|
||||
| T08c | Frontend Mail + Global Search UI | implementation_engineer | T06, T07b | 7 | v2 |
|
||||
|
||||
### Parallelization Notes
|
||||
|
||||
**v1 Phases:**
|
||||
- **Phase 2:** T02 (Company/Contact) and T03 (Plugin Framework) are independent after T01 — safe to run in parallel.
|
||||
- **Phase 3:** T07a (Frontend Shell+Auth+UI Library) depends only on T01. T09 (KI/Workflow) depends on T01+T02. Both can run in parallel if API contracts are frozen.
|
||||
- **Phase 4:** T07b (Frontend Feature Pages) depends on T07a (UI library, routing, auth) + T02 (company/contact API). Must run after T07a.
|
||||
- **Phase 5:** T10 (Monitoring+Doku) depends on T01+T02. Can run parallel with T07b.
|
||||
|
||||
**v2 Phases (after v1 deployment):**
|
||||
- **Phase 6:** T04 (DMS), T05 (Calendar), T06 (Mail), T11 (Tags+Perm) all depend on T01+T03 — safe to run in parallel.
|
||||
- **Phase 7:** T08a/T08b/T08c depend on T07b + respective backend (T04/T05/T06) — safe to run in parallel.
|
||||
|
||||
### Block Rules
|
||||
|
||||
- Block = max 3 Tasks per implementation block.
|
||||
- After each block: quality_reviewer review → block_compactor → context_compactor → User checkpoint.
|
||||
- quality_reviewer and release_auditor do NOT count toward the 3-task limit.
|
||||
- After 3 blocks (9 tasks): release_auditor runs full audit.
|
||||
- Token budget: ~3000 tokens per task. If tool result >5000 tokens: context_compactor.
|
||||
|
||||
---
|
||||
|
||||
## 5. Forbidden Patterns
|
||||
|
||||
### Backend Forbidden
|
||||
|
||||
- ❌ **SQLite:** No SQLite as database. PostgreSQL 16 only (ADR-01).
|
||||
- ❌ **Jinja2:** No server-side HTML rendering. API-only backend (ADR-03).
|
||||
- ❌ **Cross-Tenant Data Access:** No query without tenant_id filter. ORM auto-filter must not be bypassed.
|
||||
- ❌ **Plaintext Passwords:** Passwords must be bcrypt-hashed (cost=12). Never store or log plaintext.
|
||||
- ❌ **JWT Tokens:** No JWT auth in v1. Session-based auth with HttpOnly cookies only (ADR-05).
|
||||
- ❌ **Naive Datetime:** All datetime fields must be timezone-aware (TIMESTAMPTZ). Never use `datetime.now()` without tz.
|
||||
- ❌ **Integer IDs:** All primary keys are UUID. Never use auto-increment integer IDs.
|
||||
- ❌ **Hard-Delete without GDPR flag:** Companies/Contacts use soft-delete. Hard-delete only with explicit `?gdpr=true`.
|
||||
- ❌ **Manual Tenant Filter:** Never manually add `.filter(Tenant.id == x)` in services. The ORM auto-filter handles this.
|
||||
- ❌ **Sync I/O in Routes:** All route handlers are `async def`. Never use blocking I/O (use `asyncpg`, `aiofiles`, etc.).
|
||||
- ❌ **Raw SQL without Tenant Check:** Any raw SQL query must explicitly include `tenant_id` filter.
|
||||
- ❌ **Secrets in Code:** No hardcoded secrets. All secrets via environment variables.
|
||||
- ❌ **Unvalidated Input:** All request bodies validated by Pydantic schemas. Never trust raw request data.
|
||||
- ❌ **Missing Audit Log:** All create/update/delete operations must create audit log entries.
|
||||
- ❌ **Plugin Tables without tenant_id:** All plugin-created tables must include `tenant_id` column. The migration validator enforces this.
|
||||
|
||||
### Frontend Forbidden
|
||||
|
||||
- ❌ **Class Components:** No class components. Functional components with hooks only.
|
||||
- ❌ **Inline Styles:** No `style={{}}` props. Use Tailwind utility classes.
|
||||
- ❌ **Hardcoded Strings:** No user-visible hardcoded strings. Use `t()` from i18n.
|
||||
- ❌ **Manual Fetch in Components:** No `fetch()` or `axios` calls in components. Use TanStack Query hooks.
|
||||
- ❌ **Server Data in Zustand:** Zustand is for client state only. Server data goes in TanStack Query.
|
||||
- ❌ **`any` Types:** No `any` type. Use proper TypeScript types.
|
||||
- ❌ **Missing ARIA Attributes:** All interactive elements must have ARIA labels.
|
||||
- ❌ **Touch Targets < 44px:** All buttons/links must have minimum 44px touch target.
|
||||
- ❌ **Direct DOM Manipulation:** No `document.getElementById()` or `querySelector()` in components. Use React refs.
|
||||
- ❌ **Unsafe HTML Rendering:** No `dangerouslySetInnerHTML` without sanitization. Mail bodies must be sanitized (DOMPurify equivalent).
|
||||
|
||||
### Deployment Forbidden
|
||||
|
||||
- ❌ **Running as Root in Container:** Containers run as non-root user (app:app).
|
||||
- ❌ **Exposed DB Port in Production:** PostgreSQL port (5432) must not be exposed externally in production.
|
||||
- ❌ **No Health Check:** All services must have Docker health checks configured.
|
||||
- ❌ **No Volume for Storage:** File storage must use a named volume, not ephemeral container storage.
|
||||
- ❌ **Secrets in docker-compose.yml:** No secrets in compose file. Use `.env` file or Docker secrets.
|
||||
|
||||
---
|
||||
|
||||
## 6. Quality Gates
|
||||
|
||||
### Per-Task Quality Gate
|
||||
|
||||
Before a task is marked complete:
|
||||
1. All test_spec commands must pass.
|
||||
2. Coverage target must be met (measured by pytest-cov / vitest coverage).
|
||||
3. TypeScript compiles without errors (`tsc --noEmit`).
|
||||
4. Linting passes (ruff for backend, eslint for frontend).
|
||||
5. Build succeeds (Vite build for frontend, no build step for backend).
|
||||
6. No forbidden patterns detected.
|
||||
7. All acceptance criteria verified as testable.
|
||||
|
||||
### Phase Gate (after each phase)
|
||||
|
||||
1. All tasks in the phase pass their quality gates.
|
||||
2. quality_reviewer subagent reviews the phase output.
|
||||
3. No critical issues from quality_reviewer.
|
||||
4. Block compactor saves progress.
|
||||
5. User checkpoint before next phase.
|
||||
|
||||
### Release Gate (before v1 deployment)
|
||||
|
||||
1. All 7 v1 tasks complete (T01, T02, T03, T07a, T07b, T09, T10).
|
||||
2. release_auditor runs full audit.
|
||||
3. Docker Compose builds and starts successfully.
|
||||
4. Health endpoint returns 200.
|
||||
5. E2E tests (Playwright) pass.
|
||||
6. All forbidden patterns checked.
|
||||
|
||||
### v2 Release Gate (before v2 plugin deployment)
|
||||
|
||||
1. All 7 v2 tasks complete (T04, T05, T06, T11, T08a, T08b, T08c).
|
||||
2. release_auditor runs full audit.
|
||||
3. All plugin backends + frontends pass quality gates.
|
||||
4. Plugin install/activate/deactivate lifecycle tested.
|
||||
5. All forbidden patterns checked.
|
||||
|
||||
---
|
||||
|
||||
## 7. Environment Setup
|
||||
|
||||
### Development Environment
|
||||
|
||||
| Variable | Value | Purpose |
|
||||
|----------|-------|---------|
|
||||
| `POSTGRES_HOST` | `localhost` (dev) / `postgres` (docker) | Database host |
|
||||
| `POSTGRES_PORT` | `5432` | Database port |
|
||||
| `POSTGRES_DB` | `leocrm` | Database name |
|
||||
| `POSTGRES_USER` | `leocrm` | Database user |
|
||||
| `POSTGRES_PASSWORD` | (from .env) | Database password |
|
||||
| `REDIS_URL` | `redis://localhost:6379/0` | Redis for cache + sessions + jobs |
|
||||
| `LEOCRM_SECRET_KEY` | (min 32 chars) | Session signing secret |
|
||||
| `SESSION_TIMEOUT_HOURS` | `8` | Session expiry |
|
||||
| `MAIL_ENCRYPTION_KEY` | (32-byte hex) | AES-256 key for mail credentials |
|
||||
| `STORAGE_BACKEND` | `local` (dev) / `s3` (prod) | File storage backend |
|
||||
| `STORAGE_PATH` | `/data/leocrm/storage` | Local storage path |
|
||||
| `ONLYOFFICE_URL` | `http://onlyoffice:80` | OnlyOffice document server |
|
||||
| `LOG_LEVEL` | `INFO` | Logging level |
|
||||
|
||||
### Test Environment
|
||||
|
||||
- Test DB: Ephemeral PostgreSQL (pytest-postgresql or testcontainers).
|
||||
- Test Redis: Ephemeral or fakeredis.
|
||||
- External services (IMAP, SMTP, OnlyOffice): Mocked via `unittest.mock.AsyncMock`.
|
||||
- Test fixtures in `conftest.py` provide: test client, authenticated client (per role), seeded data.
|
||||
|
||||
---
|
||||
|
||||
## 8. Architecture Reference
|
||||
|
||||
Full architecture details: `architecture.md`
|
||||
|
||||
Full task graph with test specs: `task_graph.json`
|
||||
|
||||
Key ADRs:
|
||||
- ADR-01: PostgreSQL 16 (not SQLite)
|
||||
- ADR-02: ARQ (not Celery)
|
||||
- ADR-03: Built-in plugins with manifest (not dynamic pip-install)
|
||||
- ADR-04: TanStack Query (not Redux)
|
||||
- ADR-05: Session-based auth (not JWT)
|
||||
- ADR-06: Soft-delete with `deleted_at` column
|
||||
|
||||
---
|
||||
|
||||
## Handoff
|
||||
|
||||
- **AGENTS.md status:** COMPLETE
|
||||
- **task_graph.json status:** COMPLETE (14 tasks: 7 v1 + 7 v2, all with test_spec, 143 features covered, v1/v2 separated, v2.1.0)
|
||||
- **architecture.md status:** COMPLETE (73/73 v1 features referenced, v2 sections marked)
|
||||
- **Ready for v1 implementation:** YES (pending quality_reviewer review + plan_mode transition to implementation_allowed)
|
||||
- **v2 implementation:** After v1 deployment, separate phase
|
||||
+1
-1
@@ -91,7 +91,7 @@ def hash_token(token: str) -> str:
|
||||
return hashlib.sha256(token.encode()).hexdigest()
|
||||
|
||||
|
||||
def verify_ws_origin(websocket) -> bool:
|
||||
async def verify_ws_origin(websocket) -> bool:
|
||||
"""Verify that the WebSocket upgrade request comes from an allowed origin.
|
||||
|
||||
Checks the Origin header against the configured CORS origins.
|
||||
|
||||
@@ -209,7 +209,7 @@ async def ai_ui_control_ws(websocket: WebSocket):
|
||||
from app.core.service_container import get_container
|
||||
|
||||
settings = get_settings()
|
||||
if not verify_ws_origin(websocket):
|
||||
if not await verify_ws_origin(websocket):
|
||||
await websocket.close(code=4003, reason="Origin not allowed")
|
||||
return
|
||||
|
||||
|
||||
@@ -477,7 +477,7 @@ async def websocket_endpoint(
|
||||
from app.core.auth import get_session_data, get_redis, verify_ws_origin
|
||||
|
||||
settings = get_settings()
|
||||
if not verify_ws_origin(websocket):
|
||||
if not await verify_ws_origin(websocket):
|
||||
await websocket.close(code=4003, reason="Origin not allowed")
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user