# Activity History System — Detailed Plan ## Anforderungsanalyse **Ziel:** Ein lueckenloses Aenderungsprotokoll, das jede Mutation im System erfasst und ueber UI und AI Assistant abfragbar macht. Nutzer sollen fragen koennen: "Wer hat die Buchung von Person X geaendert?" oder "Was ist in den letzten Tagen bei Projekt Y passiert?" **Ist-Zustand:** - AuditLog-Modell existiert (entityType, entityId, action, userId, changes JSONB, createdAt) - 10 von 36 Routern loggen Aenderungen (44% Abdeckung) - userId wird nur in ~60% der Faelle erfasst - Kein Query-Endpoint (write-only) - Keine UI zum Anzeigen der Historie - AI Assistant kann keine Historie abfragen - Inkonsistente before/after Snapshots **Soll-Zustand:** - 100% Mutation-Abdeckung ueber alle Router - Konsistente before/after Snapshots mit User-Attribution - Query-API mit Filtern (entityType, entityId, userId, dateRange, action) - Admin-UI: `/admin/activity-log` mit suchbarer, filterbarer Timeline - Entity-Detail-Seiten: "History"-Tab/-Drawer auf Project/Resource/Allocation - AI Assistant Tool: `query_change_history` fuer natuerlichsprachliche Abfragen - Change-Source Tracking: UI vs API vs AI vs Import --- ## Architektur-Entscheidungen ### 1. Audit Middleware statt manuelle Calls **Entscheidung:** tRPC Middleware die automatisch vor/nach jeder Mutation auditiert **Grund:** Eliminiert vergessene `auditLog.create()` Calls, garantiert 100% Abdeckung **Umsetzung:** Middleware auf `protectedProcedure` die: - Vor der Mutation: Entity-Snapshot speichert (before) - Nach der Mutation: Neuen Snapshot speichert (after) - Diff berechnet und AuditLog-Entry erstellt ### 2. Standardisiertes Changes-Format ```typescript interface AuditChanges { before?: Record; // Snapshot vor der Aenderung after?: Record; // Snapshot nach der Aenderung diff?: Record; // Nur geaenderte Felder metadata?: { source: "ui" | "api" | "ai" | "import" | "cron"; // Wer hat die Aenderung ausgeloest reason?: string; // Optionaler Kommentar ip?: string; // Request IP (optional) batchId?: string; // Fuer Bulk-Operationen }; } ``` ### 3. Schema-Erweiterungen ```prisma model AuditLog { // Existierende Felder behalten id String @id @default(cuid()) entityType String entityId String action AuditAction userId String? user User? @relation(fields: [userId], references: [id]) changes Json @db.JsonB createdAt DateTime @default(now()) // NEU: Zusaetzliche Felder source String? // "ui" | "api" | "ai" | "import" | "cron" entityName String? // Menschenlesbarer Name (z.B. "Porsche Taycan Project") summary String? // Einzeiler: "Changed status from DRAFT to ACTIVE" @@index([entityType, entityId]) @@index([userId]) @@index([createdAt]) @@index([entityType, createdAt]) // NEU: Fuer sortierte Timeline-Queries } ``` --- ## Betroffene Pakete & Dateien | Paket | Dateien | Art der Aenderung | |-------|---------|------------------| | `packages/db` | `prisma/schema.prisma` | **edit** — AuditLog um source, entityName, summary erweitern | | `packages/api` | `src/lib/audit.ts` | **create** — `createAuditEntry()` Helper + `auditMiddleware` | | `packages/api` | `src/router/audit-log.ts` | **create** — Query-Router (list, getByEntity, getTimeline) | | `packages/api` | `src/router/index.ts` | **edit** — auditLog Router registrieren | | `packages/api` | `src/router/assistant-tools.ts` | **edit** — `query_change_history` Tool hinzufuegen | | `packages/api` | 26 Router-Dateien | **edit** — fehlende audit Calls nachruesten | | `apps/web` | `src/app/(app)/admin/activity-log/page.tsx` | **create** — Activity Log Seite | | `apps/web` | `src/components/admin/ActivityLogClient.tsx` | **create** — Suchbare Timeline | | `apps/web` | `src/components/ui/EntityHistory.tsx` | **create** — Wiederverwendbare History-Komponente | | `apps/web` | `src/components/layout/AppShell.tsx` | **edit** — Nav-Link fuer Activity Log | --- ## Task-Liste (atomare Schritte) ### Phase 1: Infrastruktur (Basis) - [ ] **Task 1:** Schema erweitern → `packages/db/prisma/schema.prisma` - `source String?`, `entityName String?`, `summary String?` hinzufuegen - Index `@@index([entityType, createdAt])` hinzufuegen - `prisma db push` + `prisma generate` - [ ] **Task 2:** Audit Helper erstellen → `packages/api/src/lib/audit.ts` - `createAuditEntry(db, params)` — standardisierter Audit-Entry-Creator - Params: `{ entityType, entityId, entityName, action, userId, before?, after?, source?, summary? }` - Automatische Diff-Berechnung wenn before + after vorhanden - Automatische Summary-Generierung aus Diff (z.B. "Updated name, status, budgetCents") - `computeDiff(before, after)` — gibt nur geaenderte Felder zurueck - [ ] **Task 3:** Query Router erstellen → `packages/api/src/router/audit-log.ts` - `list` query (controllerProcedure): paginiert, filterbar nach entityType, entityId, userId, action, dateRange, source - `getByEntity` query: alle Entries fuer eine Entity, chronologisch - `getTimeline` query: globale Timeline aller Aenderungen, gruppierbar nach Tag - `getActivitySummary` query: Zusammenfassung (counts pro entityType, pro action, pro User) fuer einen Zeitraum - Registrieren in `router/index.ts` ### Phase 2: Audit-Abdeckung erweitern - [ ] **Task 4:** Kritische Router nachruesteen (Parallel-fähig, 4 Agents) - **Agent A:** `vacation.ts` (8 Mutations), `entitlement.ts` (2), `user.ts` (9) - **Agent B:** `client.ts` (5), `org-unit.ts` (3), `country.ts` (5), `management-level.ts` (5) - **Agent C:** `rate-card.ts` (7), `blueprint.ts` (6), `settings.ts` (3), `calculation-rules.ts` (3) - **Agent D:** `webhook.ts` (4), `comment.ts` (3), `notification.ts` (nur create/task), `dispo.ts` (4) - Jeder Agent: `import { createAuditEntry } from "../lib/audit.js"` verwenden - userId immer aus `ctx.dbUser?.id` nehmen - [ ] **Task 5:** Bestehende Audit-Calls standardisieren - Alle 37 existierenden `auditLog.create` Calls auf `createAuditEntry()` Helper umstellen - userId konsistent aus Context nehmen - before/after Snapshots wo fehlend ergaenzen - `source: "ui"` als Default setzen ### Phase 3: UI - [ ] **Task 6:** Activity Log Admin-Seite → `ActivityLogClient.tsx` - Globale, suchbare Timeline aller Aenderungen - Filter: Entity-Typ (Project/Resource/Allocation/...), User, Action, Datum - Jeder Eintrag zeigt: Zeitstempel, User (Avatar + Name), Entity (verlinkt), Action-Badge, Summary - Expandierbares Detail: before/after Diff-View (JSON oder tabellarisch) - Pagination (50 pro Seite) - Sidebar Nav-Link unter Admin: "Activity Log" - [ ] **Task 7:** Entity History Komponente → `EntityHistory.tsx` - Wiederverwendbar fuer Project/Resource/Allocation Detail-Seiten - Props: `entityType: string, entityId: string` - Chronologische Liste der Aenderungen fuer diese Entity - Kompakte Darstellung: User, Action, Summary, Zeitstempel - Optional: als Tab oder Drawer auf Detail-Seiten einbinden - [ ] **Task 8:** History-Tab auf Detail-Seiten integrieren - `/projects/[id]` → "History" Tab mit `` - `/resources/[id]` → "History" Tab - Optional spaeter: Allocation Detail, Estimate Detail ### Phase 4: AI Assistant Integration - [ ] **Task 9:** AI Tool erstellen → `assistant-tools.ts` - `query_change_history` Tool: - Input: `{ entityType?, entityId?, userId?, search?, daysBack?, limit? }` - Ruft `auditLog.list` mit Filtern auf - Formatiert Ergebnis menschenlesbar: ``` [2026-03-22 14:30] admin@planarchy.dev UPDATED Project "Porsche Taycan" → Changed status from DRAFT to ACTIVE → Changed budgetCents from 500000 to 750000 ``` - `get_entity_timeline` Tool: - Input: `{ entityType, entityId, limit? }` - Gibt chronologische History fuer eine Entity zurueck - Beide Tools mit Permission `VIEW_PROJECTS` oder `VIEW_RESOURCES` je nach entityType --- ## Abhaengigkeiten ``` Task 1 (Schema) ──► Task 2 (Helper) ──► Task 3 (Query Router) └──► Task 4a-d (Parallel: 26 Router) └──► Task 5 (Bestehende Calls) Task 3 ──► Task 6 (UI: Activity Log) ──► Task 7 (UI: Entity History) ──► Task 9 (AI Tools) Task 7 ──► Task 8 (Integration in Detail-Seiten) ``` - Tasks 4a-d koennen **parallel** ausgefuehrt werden (unterschiedliche Dateien) - Tasks 6, 7, 9 koennen **parallel** nach Task 3 - Task 8 benoetigt Task 7 --- ## Akzeptanzkriterien - [ ] `pnpm --filter @planarchy/web exec tsc --noEmit` — keine neuen Errors - [ ] `pnpm test:unit` — alle Tests gruen - [ ] **100% Mutation-Abdeckung:** Jede Mutation in jedem Router erzeugt einen AuditLog-Entry - [ ] **Konsistente userId:** Jeder Entry hat den ausfuehrenden User - [ ] **before/after:** UPDATE-Actions haben immer before + after Snapshots - [ ] **Query-API:** `trpc.auditLog.list` liefert paginierte, filterbare Ergebnisse - [ ] **Admin UI:** `/admin/activity-log` zeigt globale Timeline mit Filtern - [ ] **Entity History:** Project/Resource Detail-Seiten zeigen Aenderungs-Historie - [ ] **AI Assistant:** "Wer hat die Buchung von Person X geaendert?" wird korrekt beantwortet - [ ] **AI Assistant:** "Was ist bei Projekt Y in den letzten Tagen passiert?" liefert Ergebnis --- ## Risiken & offene Fragen ### Risiken - **Performance:** Audit-Middleware auf jeder Mutation koennte Latenz erhoehen → Mitigation: Audit-Writes fire-and-forget (non-blocking), oder nach Response - **Storage:** JSONB Snapshots koennen gross werden → Mitigation: Nur geaenderte Felder in `diff` speichern, nicht volle Snapshots - **Migration:** 37 bestehende Calls umstellen birgt Regressions-Risiko → Mitigation: Schrittweise, mit Tests pro Router ### Offene Fragen 1. **Retention:** Wie lange sollen Audit-Logs aufbewahrt werden? (Vorschlag: 2 Jahre) 2. **Granularitaet:** Sollen READ-Zugriffe geloggt werden? (Vorschlag: Nein, nur Mutations) 3. **DSGVO:** Muessen Audit-Logs bei User-Loeschung anonymisiert werden? 4. **Notifications:** Sollen bestimmte Aenderungen (z.B. Projekt-Status) automatisch Notifications ausloesen? 5. **Middleware vs Manual:** Soll der Audit-Helper manuell oder als tRPC-Middleware eingebaut werden? → Empfehlung: Manuell mit Helper-Funktion, da Middleware die Entity-Snapshots nicht automatisch kennt --- ## Geschaetzter Aufwand | Phase | Aufwand | Parallelisierbar | |-------|---------|-----------------| | Phase 1: Infrastruktur | 1 Tag | Nein (sequenziell) | | Phase 2: Audit-Abdeckung | 1 Tag | Ja (4 Agents parallel) | | Phase 3: UI | 1 Tag | Ja (2 Agents parallel) | | Phase 4: AI Integration | 0.5 Tag | Ja (mit Phase 3) | | **Gesamt** | **~3.5 Tage** | |