Files
CapaKraken/plan.md
T
Hartmut 66878f18f4 feat: Activity History system — full audit coverage, UI, AI tools
Infrastructure (Phase 1):
- AuditLog schema: add source, entityName, summary fields + index
- createAuditEntry() helper: auto-diff, auto-summary, fire-and-forget
- auditLog query router: list, getByEntity, getTimeline, getActivitySummary

Audit Coverage (Phase 2 — 14 routers, 50+ mutations):
- vacation: create, approve, reject, cancel, batch ops (8 mutations)
- user: create, updateRole, setPermissions, resetPermissions (5 mutations)
- entitlement: set, bulkSet (3 mutations)
- client: create, update, delete, batchUpdateSortOrder
- org-unit: create, update, deactivate
- country: create, update, createCity, updateCity, deleteCity
- management-level: createGroup, updateGroup, createLevel, updateLevel, deleteLevel
- settings: updateSystemSettings (sensitive fields sanitized), testSmtp
- blueprint: create, update, updateRolePresets, delete, batchDelete, setGlobal
- rate-card: create, update, deactivate, addLine, updateLine, deleteLine, replaceLines
- calculation-rules: create, update, delete
- effort-rule: create, update, delete
- experience-multiplier: create, update, delete
- utilization-category: create, update

Admin UI (Phase 3):
- /admin/activity-log page with global searchable timeline
- Filters: entity type, action, user, date range, text search
- Expandable before/after diff view per entry
- Summary cards showing top entity types by change count
- EntityHistory reusable component for entity detail pages
- Sidebar nav link with clock icon

AI Assistant (Phase 4):
- query_change_history tool: "Who changed project X?"
- get_entity_timeline tool: "What happened to resource Y?"

Regression: 283 engine + 37 staffing tests pass. TypeScript clean.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-22 22:39:30 +01:00

11 KiB

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

interface AuditChanges {
  before?: Record<string, unknown>;  // Snapshot vor der Aenderung
  after?: Record<string, unknown>;   // Snapshot nach der Aenderung
  diff?: Record<string, { old: unknown; new: unknown }>;  // 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

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 createcreateAuditEntry() 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 editquery_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 <EntityHistory entityType="project" entityId={id} />
    • /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