Fix: verify_ws_origin async + await callers
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:
Agent Zero
2026-08-04 11:41:18 +02:00
parent 7d3007b6c4
commit 51c9b467b2
7 changed files with 672 additions and 528 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"mcpServers": {}
}
+16
View File
@@ -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"
}
}
+79 -525
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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