Files
HiveGrid-Wireframes/hivegrid-cloud/docs/improvement-plan/02-observability/VERIFICATION.md
T

399 lines
27 KiB
Markdown

# Phase 02 — Verification Report
**Reviewer:** Verifier-Subagent
**Datum:** 2026-05-16
**Scope:** README.md + 8 Detail-MDs (29 → 36) in `docs/improvement-plan/02-observability/`
**Verdict:** **APPROVED WITH MINOR FIXES** — Plan ist ausführungsreif; einige Pfad-/Konsistenz-Fixes vor Phase-Start nötig (siehe §11).
---
## 1. Template-Konformität (15 Sections, korrekte Reihenfolge)
Pflicht-Reihenfolge laut `DETAIL-MD-TEMPLATE.md`:
1. Frontmatter (Status/Task-ID/Phase/Effort/Owner)
2. Problem · 3. Depends on · 4. Blocks · 5. Approach · 6. Implementation Steps
7. Test Requirements · 8. Acceptance Criteria · 9. Rollback Strategy · 10. Security Notes
11. Subagent Recommendations · 12. Staging-Deployment-Steps · 13. Deviations from Plan
14. Progress Log · 15. Lessons Learned (optional)
| Datei | 1-5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | OK? |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| `sentry-selfhost-setup.md` | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| `sentry-wiring-api-worker.md` | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| `sentry-wiring-web.md` | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| `sentry-ci-integration.md` | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| `opentelemetry.md` | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| `logging-migration.md` | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| `metrics-grafana.md` | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
| `performance-quickwins.md` | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK | OK |
Alle 8 Files folgen der Pflicht-Reihenfolge. Keine fehlenden Sections, keine Reihenfolge-Verstöße.
---
## 2. Dependencies-Konsistenz (Depends on / Blocks vs. execution-order.md)
### 2.1 Phase-01-Verweise
Alle Detail-MDs verweisen auf `01-tech-debt-foundation/dep-updates.md`. Diese Datei **existiert** (verifiziert: `/docs/improvement-plan/01-tech-debt-foundation/dep-updates.md`). OK.
### 2.2 Intra-Phase-2-Graph (lt. README.md)
```
#29 sentry-selfhost ──► #30 wiring-api-worker ─┐
└─► #31 wiring-web ────────┤
▼
#32 sentry-ci
│
▼
┌────────────────────┼────────────────────┐
▼ ▼ ▼
#33 opentelemetry #34 logging-migration #35 metrics-grafana
│
▼
#36 performance-quickwins
```
Cross-Check der Detail-MDs:
| Task | Depends on (declared) | Blocks (declared) | Konsistenz |
|---|---|---|---|
| #29 selfhost | `dep-updates` | #30, #31, #32, #33, scaling-*, authentik | OK |
| #30 wiring-api-worker | #29, `dep-updates` | #32, #33, scaling-* | OK |
| #31 wiring-web | #29, `dep-updates` | #32, capacitor | OK |
| #32 sentry-ci | #29, #30, #31 | "Keine harten Blocks" | OK — konsistent mit "kein harter Block, aber Phase-03+ profitiert" |
| #33 opentelemetry | #29, #30, #31 | #35 (kein harter Block) + scaling/read-write-split | **Abweichung**: README ordnet #33 hinter #32 ein, aber #33 deklariert keine Dependency auf #32. Das ist OK weil #32 nur Release-Tagging ist und keine Wiring-Voraussetzung für OTel — der README-Graph ist Workflow-Reihenfolge, nicht harte Dependency. Kein Fix nötig, aber **Hinweis** an ausführenden Agent: parallel zu #32 startbar wenn #30/#31 grün. |
| #34 logging-migration | #30, #31 | "Keine harten Blocks" | OK |
| #35 metrics-grafana | #30, #34 | shared-store-migration, plugin-system | OK |
| #36 perf-quickwins | #35 | — | OK |
**Finding F-1 (Minor):** Cross-Phase-Block-Referenz in `#29.Blocks`: `09-authentik-admin-sso/authentik-setup.md` — beide existieren laut PROGRESS-Logik. Kein Issue, sondern Plus.
**Finding F-2 (Cosmetic):** `metrics-grafana.md` deklariert `#36` nicht explizit als blocked-by. Das passt zur Realität (perf-quickwins braucht metrics zum Messen, ist explizit als Depends-on in #36 deklariert).
---
## 3. Implementation-Steps-Qualität (Atomic-Commits, Tech-Korrektheit)
### 3.1 Atomic-Granularität
| Task | Anzahl Steps | Atomic? | Hinweis |
|---|---|---|---|
| #29 | 11 | OK | Step 7 (Backup-Container) ist groß aber legitim atomar; Step 11 ist Deployment (kein Commit) — sauber gekennzeichnet |
| #30 | 11 | OK | jede Datei einzeln, Sanitize-Refactor vor Wiring (Test-First-Ordnung) |
| #31 | 11 | OK | Step 1 (deps) vor Steps 2-11 — korrekt; Test-Route-Registration nur dev/staging — gut |
| #32 | 6 | OK | Shell-Helper vor Workflow-YAML — korrekt; Step 6 ist reine Operations |
| #33 | 13 | OK | Bootstrap-Module + Entry-Point-Anpassung pro Runtime jeweils 2 Commits — clean |
| #34 | 17 | OK | High-PII einzeln (5 Commits), Bulks in 3-Commit-Buckets — sicherer Ansatz |
| #35 | 18 | OK | Foundation (Packages) zuerst, dann pro Runtime, dann Business-Hooks, dann Infra |
| #36 | 9 (8 Items + Wrap-up) | OK | jeder Item-Fix ein Commit, sauber separierbar |
### 3.2 Tech-Korrektheit Cross-Check
**React-Router-v7-Korrektheit (#31, #33):**
- `apps/web/app/entry.client.tsx`, `entry.server.tsx`, `root.tsx` — Pfade verifiziert (existieren). OK.
- `ErrorBoundary` in `root.tsx` mit `useEffect(() => Sentry.captureException(error), [error])` — korrekt; aber **Hinweis**: bei SSR-Errors ist React-Router-v7 die Convention, dass die Server-Integration via `requestDataIntegration` automatisch captures. Manueller `useEffect`-Capture ist sinnvoll als Client-Side-Backup. OK.
- Step 7 (#31): "Falls `@sentry/react-router` v8 verfügbar" — pragmatischer Decision-Punkt, gut dokumentiert. OK.
- **Finding F-3 (Minor)**: `#33` Step 9 importiert `import './otel.server'` (mit `.server`-Suffix), aber die Datei wird in Step 9 als `apps/web/app/lib/otel/server.ts` angelegt — Pfad-Inkonsistenz. **Fix vor Implementation**: entweder Import-Pfad als `./lib/otel/server` korrigieren oder Datei als `entry.server.otel.ts` benennen. Kleiner Schönheitsfehler in Step-Beschreibung.
**Hono-Korrektheit (#30, #35):**
- `apps/api/src/app.ts:86` ruft `createErrorHandler({...})` — **verifiziert** (`app.onError(createErrorHandler())` an Z. 86). OK.
- Hono-Tracing-Middleware-Approach (Step 5) — korrekt, da Sentry kein offizielles Hono-Integrationspaket hat. `c.req.routePath` für Route-Pattern (#35 Lessons Learned erwähnt) — korrekt.
**BullMQ-Korrektheit (#30):**
- Step 9 erwähnt `Sentry.bunIntegration` als BullMQ-Integration — **das ist falsch**. `bunIntegration` ist die Bun-Runtime-Integration, **nicht** BullMQ. Sentry v8 hat keine offizielle BullMQ-Integration (Stand 2026-05). Der Step relativiert das mit "oder manuelle BullMQ-Hooks falls nicht offiziell" — der manuelle Wrapper-Approach in Step 9 ist der richtige Weg.
- **Finding F-4 (Minor)**: Falsche API-Namens-Referenz `Sentry.bunIntegration` muss raus oder als "Platzhalter, prüfen ob `bullMQIntegration` existiert; sonst manueller Wrapper" gekennzeichnet werden. **Empfehlung**: Step 9 vor Implementation um `Explore`-Subagent-Hinweis ergänzen: "Prüfe `@sentry/node` v8.x changelog auf BullMQ-Integration; falls nicht vorhanden, manuellen Wrapper aus diesem Step nutzen".
**Sentry-SDK-Korrektheit (#30, #31):**
- `httpIntegration`, `prismaIntegration({ client: prisma })`, `requestDataIntegration({ include: { ip: false, ... } })` — alle gültige Sentry-v8-Integrations. OK.
- `replayIntegration({ maskAllText: false, maskAllInputs: true })` — gültig. OK.
- `browserTracingIntegration()` — gültig. OK.
**Prisma-Korrektheit (#33):**
- Step 2 PII-Scrubber: `db.statement` Param-Bindings strippen — Prisma's OTel-Integration setzt `db.statement` ohne Param-Values (Default), aber defense-in-depth-Scrubbing ist korrekt.
---
## 4. Test-Requirements-Verifizierbarkeit
| Task | Unit | Integration | E2E | Coverage-Target | Verifizierbar? |
|---|---|---|---|---|---|
| #29 | N/A (Infra) | smoke.sh | manual staging | N/A | OK |
| #30 | sanitize, sentry-bootstrap, hono-mw, runtime | In-Memory-Sentry-Transport | manual staging | ≥90%/≥80% | OK |
| #31 | sentry-client/server, routeTracing | SSR-render | Playwright `/__test/client-throw` | ≥80% | OK |
| #32 | sentry-release.sh (bats) | test-workflow `workflow_dispatch` | manual CI-run | N/A | OK |
| #33 | pii-span-processor, otel-bootstrap | mock-OTLP-endpoint | manual E2E-trace 4-spans | ≥85%/≥70% | OK |
| #34 | PII-mock-tests pro Service, eslint-rule-test | pre-commit-hook | staging-log-inspection | N/A (Migration) | OK |
| #35 | registry/auth/middleware/scheduler | in-process API | prom-targets + grafana-live | ≥80%/≥70% | OK |
| #36 | pro Item-Fix | regression (existing) | oha-load-test | nicht senken | OK |
**Spezifisch zu den User-Fragen:**
- **Sentry-Tests verifizierbar?** Ja — In-Memory-Transport (#30 Test-Reqs erwähnt `@sentry/core` testkit explizit) plus Staging-Smoke. Korrekt.
- **OTel-Spans testen?** Ja — Mock-OTLP-Endpoint in Vitest-Suite (#33 Test-Reqs) + End-to-End-Trace-Verifikation auf Staging (Trace-ID-Korrelation über 4 Services). Korrekt.
- **Pino-Redaction testen?** Ja — `webLogger`-Mock-Spy mit `assertNotCalled` für PII-Keys (#34 Test-Reqs). Korrekt. **Zusatz-Hinweis**: ESLint-Rule selbst wird mit synthetischem Source-File + ESLint-API getestet — sauber.
**Finding F-5 (Minor)**: `#34` definiert keine konkrete Coverage-%-Zahl ("keine harte %-Zahl") — das ist akzeptabel für eine Migration-Task, aber die "Pflicht: Tests für die 5 High-PII-Services" ist eine implizite Coverage-Zusage und sollte als hartes Akzeptanz-Item bleiben (ist es bereits in Acceptance Criteria). OK.
---
## 5. Acceptance-Criteria-Verifizierbarkeit
Alle 8 Files haben Acceptance-Criteria mit:
- Konkreten Datei-Existenz-Checks (`infra/sentry/...`, `packages/metrics/...`)
- Endpoint-Behaviors (`/status` → 200/503, `/metrics` → 401/200)
- Staging-Verifikation (Test-Event in Sentry-UI, Trace mit N Spans, Dashboard-Live-Daten)
- Lint+Typecheck+Test-Gates
**Objektiv prüfbar:** Ja. Alle Criteria sind entweder Datei-Existenz, Endpoint-Response, Lint-Output oder UI-Sichtbarkeit. Kein "schöner Code"-Subjektiv-Kriterium.
**Finding F-6 (Minor)**: `#35` AC "Cardinality-Schutz aktiv (tenant_id-Bucketing dokumentiert)" ist etwas weich. Schärfer wäre: "Prom-Query `count by (__name__)({__name__=~\"ai_.*\"})` liefert < 1000 Series auf Staging". Aber als Doku-Anforderung mit Verweis auf Approach-Section-Decision-Punkt (top-50 + other ab 1000 tenants) noch akzeptabel.
---
## 6. Staging-Deployment-Steps (Sentry-Selfhost auf Hetzner+Coolify)
### 6.1 Sizing-Empfehlungen — vorhanden?
**Ja, in #29 Approach-Section:**
- "16 GB RAM empfohlen (12 GB hartes Minimum, Clickhouse + Kafka sind RAM-hungrig)"
- "4 vCPU empfohlen"
- "~50 GB Disk für 90 Tage Event-Retention"
- "~10 Container" mit Liste
**Plus GlitchTip-Alternative** für 8-GB-Server in `sentry-glitchtip-alternative.md` dokumentiert (Step 10).
OK.
### 6.2 Backup-Restore-Drills — vorhanden?
**Backup-Steps** vorhanden (#29 Step 7: täglicher Cron-Container, postgres-dump + clickhouse-snapshot → S3, `SENTRY_SECRET_KEY` notieren).
**Restore-Drill**: AC enthält "`docs/observability/sentry-selfhost.md` enthält: Deploy-Schritte, Update-Strategie, **Backup/Restore-Drill**, Hardening-Checkliste, Ressourcen-Anforderungen". Step 8 dokumentiert Updates aber nicht explizit Restore.
**Finding F-7 (Minor — actionable)**: Im Implementation-Step 8 oder neuem Step 8a sollte ein expliziter Punkt "Restore-Drill dokumentieren + auf Staging einmalig durchspielen" stehen. Aktuell ist es nur in AC erwähnt, ohne Implementation-Step. **Fix**: Step 8 erweitern um Bullet "Restore-Drill: Postgres-Dump auf parallelem Compose-Stack importieren, Clickhouse-Snapshot wiederherstellen, Login + 1 Test-Issue prüfen — Verfahren in Doku-File ergänzen".
### 6.3 Vollständigkeit Coolify-Deployment
Step-by-Step für Coolify-UI-Anlegen (#29 Staging-Deployment Step 2) ist konkret: Repo, Compose-Path, Domain, Env-Vars aus Vault. OK.
`install.sh`-Wrapper läuft beim ersten Deployment automatisch → Admin-User-Setup. OK.
Smoke-Tests sind konkret mit `sentry shell -c "..."`, `sentry-cli send-event`, `curl OTLP-Endpoint` — verifizierbar.
OK.
---
## 7. Konsistenz mit User-Wünschen
### 7.1 "Sentry Selfhost und alles einbinden" — alle 3 Runtimes abgedeckt?
| Runtime | Init-Modul | Reporter | Tracing | E2E-Verifikation |
|---|---|---|---|---|
| `apps/api` | `apps/api/src/sentry.ts` (#30 Step 3) | `apiErrorReporter` (Step 4) | Hono-MW (Step 5-6) + Prisma-Auto | Smoke #30 Step 5 |
| `apps/worker` | `apps/worker/src/sentry.ts` (#30 Step 7) | `workerErrorReporter` (Step 8) | BullMQ-Wrapper (Step 9) + Prisma-Auto | Smoke #30 Step 5 |
| `apps/web` (client) | `apps/web/app/sentry.client.ts` (#31 Step 2) | n/a (Sentry-API direkt) | Browser-Tracing + Route-Tracing (Step 7) | Playwright + manual |
| `apps/web` (SSR) | `apps/web/app/sentry.server.ts` (#31 Step 4) | `webErrorReporter` (Step 4) | requestDataIntegration | Manual SSR-throw |
**Verdict**: Alle 3 Runtimes (4 Wiring-Punkte) abgedeckt. OK.
### 7.2 "Full Auto-Instrumentation mit Sentry-OTLP-Backend" — korrekt?
- `@opentelemetry/auto-instrumentations-node` als Meta-Package (HTTP/fetch/Prisma/BullMQ/Redis/fs/dns/net) — **korrekt** für Full-Auto-Instrumentation (#33 Step 1).
- `fs`-Instrumentation explizit disabled (Step 3: "`'@opentelemetry/instrumentation-fs': { enabled: false }` /* zu noisy */") — sinnvoller Default.
- **Sentry-Relay als OTLP-Ingestor**: konfiguriert in `infra/sentry/relay/config.yml` mit `relay.ingest.otlp.enabled: true, max_payload_size: 5MiB` (#33 Step 10).
- Cross-Verifikation mit `#29 Step 6`: "OTLP-Ingest aktivieren — `relay/config.yml` so anpassen, dass Relay einen OTLP-HTTP-Endpoint (`/api/{project}/integration/otlp/v1/traces`) annimmt" — konsistent. OK. Update 2026-05-18: Sentry-Relay 25.x bietet OTLP-Ingest bereits out-of-the-box auf dem `/integration/otlp/`-Pfad an (keine config-Anpassung nötig); B-005 war ein Pfad-Mismatch, kein Relay-Bug.
**Finding F-8 (Minor — Verifikations-Hinweis)**: Sentry-Relay OTLP-Ingest ist **Beta-Feature** (Stand Anfang 2026). Der Explore-Subagent-Hinweis im Subagent Recommendations (#33: "Sentry-Relay OTLP-Ingest-Config Stand 2026-05, gibt es Rate-Limits") greift das auf — gut. Empfehlung: Vor Implementation **Decision-Punkt** dokumentieren: "Falls OTLP-Ingest in v25.x noch nicht stabil → Fallback auf Jaeger/Tempo (Doku in `docs/observability/otel.md` als Alt-Exporter bereits vorgesehen)".
### 7.3 "no-console: error für alle server-side Code" — Erstphase Migration + Zweitphase ESLint klar getrennt?
`#34` ist explizit in **zwei Phasen** geteilt:
- Phase 1 — Aufräumen (manuelle Migration) — Steps 1-13
- Phase 2 — Enforce (ESLint-Rule) — Steps 14-17
Reihenfolge ist korrekt: erst migrieren, dann Rule aktivieren (sonst würde Rule den eigenen Migration-Commit blocken). OK.
Override-Strategie:
- Server-Code (`**/*.server.ts(x)`, `apps/api/src/**`, `apps/worker/src/**`, `packages/**/src/**`) → `error`
- Test-Files → `off`
- Client-Bundle (`apps/web/app/components/**`, `apps/web/app/routes/**` ohne `.server.`) → `off` (Browser-Konsole legitim)
OK.
**Finding F-9 (Pfad-Korrektur)**: Die im #34 erwähnten **High-PII-Files-Pfade sind teilweise falsch:**
| In Detail-MD | Tatsächlicher Pfad im Repo |
|---|---|
| `apps/web/app/utils/services/.server/authService.ts:99` | `apps/web/app/utils/services/authService.ts` (kein `.server`-Subdir, **0 console-Calls** beim Stichproben-grep) |
| `apps/web/app/utils/services/.server/EmailService.ts:29` | `apps/web/app/modules/emails/services/EmailService.ts` (Pfad in `modules/emails/`, console-Call bestätigt: `EmailService.ts:29` + `:44`) |
| `apps/web/app/utils/services/.server/CrmService.ts:433/445` | `apps/web/app/modules/crm/services/CrmService.ts` (Pfad in `modules/crm/`, console-Calls bestätigt: `:393`, `:421`, `:437`, `:449`, `:571-604`) |
| `apps/web/app/utils/services/.server/PromptBuilderService.ts:185/256` | `apps/web/app/modules/promptBuilder/services/.server/PromptBuilderService.ts` (Pfad in `modules/promptBuilder/services/.server/`, console-Calls bestätigt) |
| `apps/web/app/utils/services/.server/SeedService.ts:199-217` | `apps/web/app/utils/db/seed/SeedService.ts` (Pfad in `utils/db/seed/`, nicht in `services/.server/`) |
**Fix vor Implementation**: Detail-MD Step 2-6 mit korrekten Pfaden updaten. Sonst greift ein Subagent ins Leere. Konkret: Inventar-Script aus Step 1 zuerst laufen lassen, das wird die Pfade automatisch korrigieren — Empfehlung an ausführenden Agent: **Step 1 strikt vor Step 2 fertigstellen** und Inventar als Source-of-Truth für die Reihenfolge nehmen (nicht die hier vorgeschlagenen statischen Pfade).
### 7.4 "/status + Prometheus + Grafana mit Business-Metriken" — Business-Metrics abgedeckt?
Final-Liste in #35 Approach-Section "Business":
- `active_tenants` ✓
- `active_users_24h` ✓
- `ai_tokens_consumed_total{provider, model, tenant_id}` ✓ (mit Cardinality-Bucketing-Decision)
- `stripe_webhook_events_total{event_type, status}` ✓
- `mail_sent_total{provider, status}` ✓
- `webhook_delivery_attempts_total{status}` ✓
- `csrf_token_rotation_total` ✓
- `subscription_active{plan_id}` ✓
User-Wunsch explizit gefordert: `active_tenants`, `ai_tokens`, `stripe_webhook_events` — **alle drei abgedeckt** + zusätzlich Mail, Webhook-Delivery, CSRF-Rotation, Subscriptions. OK.
Instrumentierung als Implementation-Steps separiert (Step 10 AI-Cost, Step 11 Stripe, Step 12 Mail, Step 13 Webhook-Delivery) mit je einem atomic Commit. OK.
5 Grafana-Dashboards (Overview, API-Latency, Queue-Health, Business-KPI, AI-Cost-per-Tenant) — sinnvolle Aufteilung, deckt User-Wunsch ab.
OK.
---
## 8. Performance-Quickwins — Alle 8 Items mit file:line?
| # | Item | Datei | Line | Datei existiert? |
|---|---|---|---|---|
| 1 | N+1 Users-Admin | `apps/web/app/routes/admin/accounts/users.tsx` | 119-129 | ✓ existiert |
| 2 | currentTenant Doppel-Fetch | `apps/web/app/utils/data/.server/rootData.ts` | 30 | ✓ existiert |
| 3 | Storage Signed-URL-Cache | `apps/api/src/storage.ts` | 56-80, 82-131 | ✓ existiert |
| 4 | clearSubscriptionsCache Thrash | `apps/web/app/utils/services/.server/subscriptionService.ts` | 543-548 | ✓ existiert |
| 5 | CSRF Body-Clone | `apps/web/app/utils/session.server.ts` | 230-268 | ✓ existiert |
| 6 | Recovery-Code Rejection-Sampling | `apps/web/app/utils/auth/mfa/recovery-codes.server.ts` | 80-83 | ✓ existiert |
| 7 | UUID-Collision-Check | `apps/api/src/auth/session/session-store.ts` | 47-61 | ✓ existiert |
| 8 | CSRF-Token 100 → 32 Bytes | `apps/web/app/utils/session.server.ts` | 212-219 | ✓ existiert |
Alle 8 Items mit konkretem file:line + Problem-Description + Fix-Approach + Tests. Item 8 hat zusätzlich Rollback-Warnung (bestehende Sessions mit 100-Byte-Token) und Migration-Hinweis (beide Längen akzeptieren bis TTL ablauf). **Sehr gründlich.** OK.
**Finding F-10 (Optional)**: Item 8 könnte vor Implementation in eigenen Folge-Task ausgegliedert werden (siehe Item-8-Subagent-Hinweis), falls Session-TTL > 24h ist. Die Detail-MD adressiert das bereits in Rollback-Strategy + Subagent-Plan-Hinweis. OK.
---
## 9. Phase-Reihenfolge (intern)
- **#29 Sentry-Setup zuerst** ✓ (Voraussetzung für DSNs)
- **#30 / #31 parallel danach** ✓ (unterschiedliche Apps, kein File-Konflikt)
- **#32 nach #30+#31** ✓ (CI-Release-Tagging braucht Wiring)
- **#33 OTel "später"** ✓ (kann theoretisch parallel zu #34/#35, README ordnet sie parallel ein — OK weil #33 keinen #32-Block deklariert; OTel-Backend = Sentry-Relay aus #29, also nur #29 ist harte Voraussetzung)
- **#34 logging-migration** parallel zu #33/#35 ✓
- **#35 metrics-grafana** parallel zu #33/#34 ✓
- **#36 perf-quickwins** zuletzt ✓ (braucht #35 zum Messen)
**OTel nutzt Sentry-Relay als Backend** — bestätigt:
- #29 Step 6 aktiviert OTLP-Ingest in Relay
- #33 Step 10 verifiziert Relay-Config (idempotent zu #29 Step 6 — saubere Defense-in-Depth)
- #33 Default-Exporter zielt auf `https://sentry.<domain>/api/<project_id>/otlp`
Reihenfolge ist korrekt. OK.
---
## 10. Fehlende Aspekte (Critical Gaps)
### 10.1 Geprüft — vorhanden ✓
- DSGVO/PII-Schutz (in #30 beforeSend, #31 Replay-Masking, #33 PII-Span-Processor, #34 Pino-Redaction)
- Rollback-Strategie pro Task
- Security Notes pro Task
- Cardinality-Schutz (#35)
- Self-Host vs SaaS-Entscheidung dokumentiert + Alternative (GlitchTip)
- TLS via Coolify-Traefik
- 2FA für Sentry-Admin
- Secret-Rotation (CI-Token, METRICS_TOKEN)
- Token-minimal-Scope (`project:releases`)
- Source-Maps werden gelöscht nach Upload (`filesToDeleteAfterUpload`)
- Sample-Rates env-konfigurierbar
- No-Op-Verhalten ohne DSN/Endpoint/Token
### 10.2 Fehlend / Verbesserungswürdig
**Finding F-11 (Minor — Suggestion)**: **Alerting** ist nicht thematisiert. Phase 02 baut Metriken + Sentry, aber **wer wird benachrichtigt** wenn `queue_jobs_failed_total` spike oder `stripe_webhook_events_total{status="failed"}` > threshold? Empfehlung:
- In `#35 metrics-grafana.md` Acceptance-Criteria ergänzen: "Mindestens 1 Grafana-Alert-Rule definiert (z.B. `up == 0 for 2m` für API/Worker/Web) mit Notification-Channel (Email oder Webhook)".
- Optional: Sentry-Alerts (Issue-Frequency-Threshold) in `#29` oder `#30` AC ergänzen.
**Finding F-12 (Minor — Suggestion)**: **Log-Aggregation** (Loki o.ä.) ist nicht eingeplant. Pino schreibt JSON in stdout, Coolify aggregiert in Container-Logs — aber kein zentraler queryable Store. Phase-02 erwähnt es nur am Rand ("Loki/Grafana"). Für 10/10-Boilerplate wäre `infra/loki/` mit Promtail-Sidecar sinnvoll. **Empfehlung**: Entweder in Phase 02 hinzufügen (eigener `loki-stack.md`-Task) oder in `11-backlog/` aufnehmen mit Begründung "Pino-JSON in Coolify-Container-Logs reicht für MVP".
**Finding F-13 (Minor — Suggestion)**: **Sentry-Sentry-Heartbeat / Dead-Man-Switch** für den Sentry-Self-Host selbst nicht behandelt. Wenn Sentry down ist, weiß niemand davon. Empfehlung in `#29` AC ergänzen: "Externer Uptime-Check (z.B. UptimeKuma in separatem Stack) prüft `https://sentry.<domain>/_health/` alle 5 Min, alarmiert via Email bei Down".
**Finding F-14 (Cosmetic)**: Web-Side `OTel`-Bootstrap in `#33` Steps 7-9 erwähnt Browser+SSR getrennt, was korrekt ist, aber das Service-Naming-Schema sollte sauberer differenziert sein: `boilerplate-web-client` vs `boilerplate-web-ssr`. Aktuell beide unter `boilerplate-web` → in Traces nicht unterscheidbar. **Empfehlung**: Resource-Attribute-Liste in Approach-Section anpassen oder in Step 7/9 explizit dokumentieren.
**Finding F-15 (Nice-to-have)**: **Synthetic-Monitoring** (z.B. ein Worker-Job alle 5 Min, der einen End-to-End-Flow simuliert und Metriken/Traces emittiert) wird nicht explizit besprochen. Wäre die natürliche Erweiterung von `#36` aber out-of-scope für Phase 02. OK to defer.
---
## 11. Findings — Zusammenfassung & Priorität
### Must-fix vor Implementation (3 Items)
1. **F-3** — `#33` Step 9: Import-Pfad `./otel.server` vs Dateipfad `lib/otel/server.ts` konsistent machen.
2. **F-4** — `#30` Step 9: Falsche Sentry-API-Referenz `Sentry.bunIntegration` für BullMQ entfernen; durch "manueller Wrapper (Default-Approach in diesem Step)" ersetzen.
3. **F-9** — `#34` Steps 2-6: Pfade der 5 High-PII-Services korrigieren ODER explizit "Pfade sind Beispiele; Inventar aus Step 1 als Source-of-Truth nehmen" hinzufügen.
### Should-fix (3 Items)
4. **F-7** — `#29` Step 8: Restore-Drill als expliziten Implementation-Step ergänzen (nicht nur AC).
5. **F-8** — `#33`: OTLP-Ingest-Beta-Status erwähnen + Fallback-Pfad zu Tempo/Jaeger dokumentieren (ist in Doku-Plan, aber als Decision-Punkt im Approach gut sichtbar machen).
6. **F-14** — `#33`: Web-Service-Naming `boilerplate-web-client` vs `boilerplate-web-ssr` differenzieren.
### Nice-to-have (4 Items)
7. **F-11** — `#35` AC: Mindestens 1 Grafana-Alert-Rule + Notification-Channel.
8. **F-12** — Loki/Promtail-Sidecar als Phase-02-Erweiterung oder in `11-backlog/`.
9. **F-13** — `#29` AC: Externer Uptime-Check für Sentry-Self-Host.
10. **F-15** — Synthetic-Monitoring als zukünftige Erweiterung dokumentieren.
### Reine Beobachtungen (keine Fixes nötig)
- **F-1**: Cross-Phase-Block `#29 → 09-authentik-setup` korrekt deklariert.
- **F-2**: `#35` bzgl. `#36` Symmetrie OK (Depends-on auf #36-Seite reicht).
- **F-5**: `#34` Coverage-Strategie OK (Migration-Task).
- **F-6**: `#35` Cardinality-AC etwas weich aber akzeptabel.
- **F-10**: `#36` Item 8 Risiko bereits in Rollback adressiert.
---
## 12. Quantitative Gesamt-Bewertung
| Dimension | Score (0-10) | Begründung |
|---|---|---|
| Template-Konformität | 10 | Alle 8 Files folgen Pflicht-Reihenfolge |
| Dependency-Korrektheit | 9.5 | Sauber deklariert, Cross-Phase-Refs gültig |
| Atomic-Commits-Eignung | 9 | Steps sind sauber separierbar, einige Bulk-Migrationen pragmatisch zusammengefasst |
| Tech-Korrektheit | 8.5 | -1.5 für F-3, F-4 (kleine API-Referenzen) |
| Test-Verifizierbarkeit | 9.5 | In-Memory-Transports + Staging-Smoke konkret |
| AC-Objektivität | 9 | Wenige weiche Items (Cardinality-Schutz) |
| Staging-Coverage | 9 | -1 für fehlenden Restore-Drill-Step (F-7) |
| User-Wunsch-Abdeckung | 9.5 | Alles drin; -0.5 für Pfad-Inkonsistenz F-9 |
| Performance-Quickwins-Konkretheit | 10 | Alle 8 file:line verifiziert, alle Dateien existieren |
| Phase-Reihenfolge-Sauberkeit | 10 | Saubere Topologie, parallelisierbar gut markiert |
| Security-Notes-Tiefe | 10 | PII, Token-Scopes, Cardinality, Allowlists durchgängig |
| **Gesamtscore** | **9.3 / 10** | **Ausführungsreif nach 3 Must-fixes** |
---
## 13. Empfehlung
**APPROVED FOR EXECUTION** nach Adressierung der 3 Must-fixes (F-3, F-4, F-9). Die 3 Should-fixes können während Implementation adressiert werden (im jeweiligen Step-Subagent-Brief erwähnt). Nice-to-haves sind echte Verbesserungen — wenn Zeit, in Phase 02 mitnehmen, sonst in `11-backlog/` aufnehmen.
**Konkrete nächste Schritte für ausführenden Agent:**
1. Vor Phase-02-Start die 3 Must-fixes als kleinen Doku-Commit auf `agent/phase-02-prep` einarbeiten (5-Minuten-Edit auf den Detail-MDs).
2. `#29` als Solo-Agent-Run starten (3-4d), parallel kann ein anderer Agent bereits `#34`-Inventar-Script (Step 1) bauen — das ist Pre-Migration-Vorbereitung ohne Wiring-Dependency.
3. Nach `#29` grün: `#30` + `#31` parallel zwei Agents.
4. Nach `#30`+`#31` grün: `#32` (Solo) + `#33`/`#34`/`#35` parallel drei Agents starten.
5. Zuletzt `#36` mit `#35`-Baseline.