# CannaManage Sprint 5 Plan — Multi-Persona Review Panel

**Date:** 2026-06-12
**Reviewed Document:** `docs/sprint-5/cannamanage-sprint5-plan.md` (v2, ~840 lines)
**Review Method:** 6-persona stakeholder simulation, scoring on 4 dimensions (0–100%)
**Iteration:** 1

---

## v2 Decisions Incorporated

The Sprint 5 plan v2 incorporates all Q&A decisions from the planning session:

1. ✅ **@tanstack/react-query** — caching, refetch, optimistic updates (Q1)
2. ✅ **Per-component loading** — independent card/table loading, no full-page blocking (Q2)
3. ✅ **Stale-while-revalidate + "Offline" banner** — graceful degradation (Q3)
4. ✅ **Full CRUD staff management** — list, invite, edit perms, revoke (Q4)
5. ✅ **Dual seed data strategy** — SQL for dev/test + API-driven for system E2E (Q5)
6. ✅ **Next.js 15.2.8 → 15.5.18 upgrade** — addresses 8+ Snyk CVEs in Phase 1 (Bonus)

---

## 1. 👤 Club Member (End User)

*"I'm a regular member of a cannabis social club. I want to see my quota, pick up my cannabis, and check my history — now with real data instead of fake numbers."*

### Findings

| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Real-time quota data** — my portal dashboard now shows actual usage from the database, not mock numbers. I can trust the "28g remaining" display because it's coming from the real backend with server-authoritative calculations. |
| 2 | ✅ Positive | **Stale-while-revalidate** — if my phone briefly loses signal in the club, I still see my last-known quota instead of a blank page. The "Offline" banner is unobtrusive. |
| 3 | ✅ Positive | **Per-component loading** — when I open the portal, I see my quota radial load first (fast), then history loads separately. No frozen full-page spinner. |
| 4 | ✅ Positive | **Distribution history is live** — I can see a distribution appear in my history immediately after the staff records it. No manual refresh needed. |
| 5 | ⚠️ Minor | **No push notification for distributions** — I still won't get notified when a distribution is recorded against my account. I have to check manually. (Acknowledged as Sprint 6+ scope.) |
| 6 | ✅ Positive | **Portal auth is explicitly planned** — Phase 5 specifically calls out wiring the portal login flow with session-based auth. My separate login experience is preserved. |

### Scores

| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 92% | Portal integration is explicitly scoped in Phase 5 with specific endpoint mappings (`/portal/dashboard`, `/portal/history`). Loading patterns and offline handling are precisely defined. |
| Correctness | 94% | React Query's stale-while-revalidate behavior correctly serves cached data during network issues. Quota calculations remain server-authoritative. Session-based portal auth is appropriate for member-facing UI. |
| Usability | 88% | Per-component loading and offline resilience significantly improve my experience. Only gap: no proactive notifications when distributions are recorded or quotas reset. |
| Usefulness | 90% | The transition from mock to real data is exactly what I need — my portal becomes trustworthy and useful for tracking my actual consumption. |

**Composite Score: 91%**

### Remaining Gaps (minor, Sprint 6+)
- Push/email notifications for distributions and quota resets
- PWA manifest for mobile home-screen shortcut

---

## 2. 🏢 Club Owner / Vorstand (Business Owner)

*"I run the Anbauvereinigung. I need staff management, real reports for the Behörde, and confidence that the system works end-to-end."*

### Findings

| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Full CRUD staff management** — Phase 6 covers list, invite, edit permissions, revoke. All 8 granular permissions are specified. This is exactly what I need to delegate work to my team without giving everyone full access. |
| 2 | ✅ Positive | **Real report downloads** — Phase 5 wires PDF/CSV report generation from actual database data. Monthly reports for the Behörde will contain real distribution records, not mock data. |
| 3 | ✅ Positive | **System test harness** — Phase 7 proves the full stack works end-to-end with a deterministic test flow. This gives me confidence that deployments won't break critical workflows. |
| 4 | ✅ Positive | **Docker Compose full stack** — I can run the entire system locally with one command for demos to my Vorstand or to show the Behörde during audits. |
| 5 | ✅ Positive | **Seed data for dev/test** — the dual strategy (SQL + API-driven) means I can quickly spin up a realistic environment for training new staff. |
| 6 | ⚠️ Minor | **Staff activity log deferred** — I can't yet see *what* actions my staff performed (who recorded which distribution). Deferred to Sprint 6. |
| 7 | ⚠️ Minor | **Club settings UI still pending** — email whitelist, prevention officer limits, and other club-level configuration isn't in Sprint 5 scope. |
| 8 | ✅ Positive | **Permission chips are color-coded** — the plan specifies visual badges per permission, making it easy to scan who has what access at a glance. |

### Scores

| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 93% | Staff management is thoroughly specified: 8 named permissions, invite flow, pending status handling, revoke confirmation. API endpoints are concrete. |
| Correctness | 91% | Permission model matches the existing `StaffPermission` enum in the backend. RBAC enforcement (403 for unauthorized) is explicitly tested. Report generation uses established PDF/CSV generation code. |
| Usability | 85% | Full CRUD staff management covers my daily needs. Missing activity log means I can't audit staff behavior yet. Club settings require direct backend config. |
| Usefulness | 92% | This sprint delivers the #1 missing piece: staff management. Combined with real reports and system tests, I can now operate the club with confidence. |

**Composite Score: 90%**

### Remaining Gaps (Sprint 6)
- Staff activity log (who did what, when)
- Club settings UI (email whitelist, prevention officer limits)

---

## 3. 💻 Developer (Technical Implementer)

*"I'm the one building this. Is the integration architecture sound? Are dependencies clear? Is the testing strategy robust?"*

### Findings

| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **React Query architecture is well-designed** — two-layer client (server-side `apiServer()` + client-side `apiFetch()`) with Next.js rewrite proxy keeps auth tokens server-side. Clean separation of concerns. |
| 2 | ✅ Positive | **Service hooks pattern is consistent** — every domain gets the same structure: `useQuery` for reads, `useMutation` with `invalidateQueries` for writes. Easy to replicate across 7 service files. |
| 3 | ✅ Positive | **Next.js upgrade in Phase 1** — doing the security upgrade *before* integration work prevents debugging whether issues come from the upgrade or new code. Smart sequencing. |
| 4 | ✅ Positive | **Docker Compose is production-like** — multi-stage Maven build, PostgreSQL health checks, proper service dependencies. Build cache optimization mentioned for developer experience. |
| 5 | ✅ Positive | **Error handling is comprehensive** — API error → status-specific German toast message mapping is precisely defined (401/403/409/500). Offline detection returns "Verbindungsfehler" toast. |
| 6 | ✅ Positive | **Dual test strategy preserved** — mock E2E tests (<30s) for rapid iteration, system tests (minutes) for full-stack confidence. Both have clear value and separate configs. |
| 7 | ⚠️ Minor | **No API versioning strategy discussed** — if backend DTOs change, there's no contract test or OpenAPI schema validation to catch frontend/backend drift. Currently relies on manual verification during Phase 3. |
| 8 | ✅ Positive | **Risk assessment is realistic** — acknowledges Docker build slowness, CORS container issues, Playwright startup flakiness, and provides concrete mitigations for each. |
| 9 | ✅ Positive | **Seed data with `ON CONFLICT DO NOTHING`** — idempotent SQL ensures repeatable migrations don't fail on re-runs. Correct Flyway pattern. |
| 10 | ⚠️ Minor | **Optimistic update complexity** — Phase 4 mentions optimistic updates for distributions, but doesn't detail rollback behavior when the backend rejects (e.g., quota exceeded after optimistic decrement). React Query's `onMutate`/`onError` rollback pattern should be specified. |

### Scores

| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 91% | Code examples for every pattern (React Query, CORS, Docker, error handling, seeds). File paths are explicit. Only missing: optimistic update rollback detail. |
| Correctness | 93% | Architecture decisions are technically sound. React Query + Next.js rewrite proxy is the established pattern. Docker multi-stage build is correct. Flyway repeatable migration is appropriate for seed data. |
| Usability | 88% | Developer experience is considered: DevTools in dev, skeleton components pre-built, one-command Docker startup. Phase sequencing is logical. Slightly high effort estimate (10.5 days single worker) may compress. |
| Usefulness | 92% | This plan is immediately actionable. Every phase has acceptance criteria checkboxes. Directory structure shows exact file placement. Minimal ambiguity in implementation order. |

**Composite Score: 91%**

### Remaining Gaps (nice-to-have)
- OpenAPI contract tests or schema validation between frontend types and backend DTOs
- Explicit optimistic update rollback pattern documentation

---

## 4. 🛡️ Compliance Officer (CanKG / BfArM Regulatory)

*"I ensure the system meets CanKG regulatory requirements. Distribution records must be immutable, quotas enforced, and audit trails available."*

### Findings

| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Quota enforcement is server-authoritative** — the plan explicitly routes quota checks through the backend (`GET /compliance/quota/{memberId}`), not client-side calculations. The 409 response for exceeded limits prevents frontend manipulation. |
| 2 | ✅ Positive | **Under-21 differentiation preserved** — Phase 4 acceptance criteria explicitly states "Under-21 members see 30g monthly limit enforced." The reduced quota is backend-enforced. |
| 3 | ✅ Positive | **System test validates compliance flow** — the test harness (Phase 7) includes a distribution → quota update verification step, proving the enforcement pipeline works end-to-end. |
| 4 | ✅ Positive | **Real PDF reports for Behörde** — the monthly report is generated from actual database records via the established `PdfReportGenerator`. No more mock data in regulatory documents. |
| 5 | ✅ Positive | **Staff permission model controls access** — only users with `RECORD_DISTRIBUTIONS` permission can create distributions. `MANAGE_COMPLIANCE` controls compliance dashboard access. Least-privilege principle applied. |
| 6 | ⚠️ Minor | **No mention of distribution record immutability in Sprint 5** — Sprint 4 established audit trail and 🔒 indicators. Sprint 5 should confirm these survive the integration (i.e., the real backend enforces immutability, not just the frontend). |
| 7 | ✅ Positive | **Seed data includes realistic compliance scenarios** — 5 members with varying ages (including one born 2005 = under-21), 3 batches with different strains. Good for testing age-based quota differentiation. |

### Scores

| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 88% | Quota enforcement endpoints and error codes are precisely specified. Under-21 handling is explicit. Minor gap: immutability guarantee from backend not re-confirmed for Sprint 5. |
| Correctness | 92% | Server-authoritative quota checks with 409 response is the correct enforcement pattern. Staff permissions align with CanKG operational requirements. Age calculation from `date_of_birth` in seed data is testable. |
| Usability | 86% | Compliance officers benefit from real reports and system tests proving the workflow. Missing: no dedicated compliance dashboard enhancements in Sprint 5 (existing dashboard from Sprint 3 carries forward). |
| Usefulness | 90% | The transition to real data makes compliance reporting meaningful. System tests provide regulatory confidence. Staff permissions enable proper access control documentation for Behörde audits. |

**Composite Score: 89%**

### Remaining Gaps (Sprint 6)
- Explicit immutability confirmation at the API level (backend `@PreAuthorize` + DB trigger or soft-delete pattern)
- Monthly report auto-sealing (cryptographic timestamp)
- Compliance dashboard enhancements (violation alerts, trend charts)

---

## 5. 🔒 Security Auditor

*"I review the system for security vulnerabilities. Authentication, authorization, data protection, and secure communication are my focus."*

### Findings

| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Next.js upgrade 15.2.8 → 15.5.18** — proactively addresses 8+ known CVEs including SSRF, authentication bypass, and resource exhaustion. Doing this in Phase 1 before integration work is the correct priority. |
| 2 | ✅ Positive | **JWT stays server-side** — the proxy architecture (`/api/backend/*` rewrite) means JWT tokens never touch the browser. Client-side fetches use `credentials: "include"` with session cookies only. Reduced token theft surface. |
| 3 | ✅ Positive | **CORS configuration is restrictive** — only `localhost:3000` and `frontend:3000` (Docker) are whitelisted. `allowCredentials: true` with explicit origins (not `*`). `maxAge: 3600L` limits preflight cache. |
| 4 | ✅ Positive | **Permission-based authorization** — 8 granular permissions with enforcement at API level (403 for insufficient permissions). Only ADMIN + MANAGE_STAFF holders can modify staff. Defense in depth. |
| 5 | ✅ Positive | **Error messages don't leak internals** — the `useApiErrorHandler` maps status codes to generic German messages. No stack traces, no internal paths, no SQL errors exposed to the client. |
| 6 | ⚠️ Minor | **Seed data contains plaintext password placeholders** — `$2a$10$...bcrypt...` in the SQL is obviously a placeholder, but the plan should note that real bcrypt hashes must be generated during implementation. Password `admin123` is acceptable for test-seed profile only. |
| 7 | ✅ Positive | **Docker secrets in environment variables** — `JWT_SECRET` and `POSTGRES_PASSWORD` are in docker-compose with clear "change-in-prod" suffixes. The plan acknowledges these are dev-only values. |
| 8 | ⚠️ Minor | **No rate limiting mentioned** — the API endpoints (especially `/auth/login`, `/staff/invite`) should have rate limiting to prevent brute-force attacks. Not Sprint 5 scope but worth noting. |
| 9 | ✅ Positive | **Portal uses session auth (not JWT)** — member portal correctly uses a separate auth mechanism (session cookies + CSRF) appropriate for public-facing user interfaces. |
| 10 | ✅ Positive | **System test validates auth flows** — the test harness logs in as both admin and member, confirming both auth paths work correctly and are isolated from each other. |

### Scores

| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 90% | CORS config is exact (origins, methods, headers, maxAge). Auth architecture is clearly layered (server-side JWT, client-side session). Permission model is enumerated. |
| Correctness | 92% | Next.js upgrade addresses real CVEs. Server-side token handling is the security best practice. CORS with explicit origins + credentials is correctly restrictive. bcrypt for passwords is appropriate. |
| Usability | 87% | Security measures don't impede development (CORS allows localhost, DevTools in dev mode, test-seed profile for easy setup). Good security/DX balance. |
| Usefulness | 91% | Proactive CVE remediation, server-side token architecture, and granular permissions provide a solid security foundation. Rate limiting and 2FA are reasonable Sprint 6+ deferrals. |

**Composite Score: 90%**

### Remaining Gaps (Sprint 6+)
- Rate limiting on auth endpoints
- 2FA (TOTP) for admin accounts
- CSRF token handling for portal session auth (not explicitly mentioned)
- Content Security Policy headers (carried forward from Sprint 4)

---

## 6. 🎨 UX Designer

*"I focus on user experience, interaction design, accessibility, and visual consistency across the application."*

### Findings

| # | Type | Observation |
|---|------|-------------|
| 1 | ✅ Positive | **Per-component skeleton loaders** — the plan includes specific skeleton components (`skeleton-card.tsx`, `skeleton-table.tsx`) that show shimmer during loading. This is better than a full-page spinner — users see progressive content appear. |
| 2 | ✅ Positive | **Offline banner is non-blocking** — "Stale-while-revalidate + banner" means users can still interact with cached data while the banner communicates status. No modal or blocking overlay. |
| 3 | ✅ Positive | **Error states have retry actions** — `error-state.tsx` component includes a retry button. Users aren't stuck — they can attempt recovery without navigating away. |
| 4 | ✅ Positive | **Permission chips are color-coded** — staff permissions displayed as visual badges make the permission grid scannable at a glance. Pattern is consistent with existing UI components (shadcn Badge). |
| 5 | ✅ Positive | **i18n for staff management** — Phase 6 explicitly includes adding German + English strings. Language consistency maintained across the new feature. |
| 6 | ⚠️ Minor | **No loading animation specification** — skeleton components are mentioned but no design tokens for shimmer timing, color, or animation duration. Should match existing shadcn skeleton defaults. |
| 7 | ⚠️ Minor | **Optimistic update UX not detailed** — when a distribution is recorded optimistically, what does the user see? Immediate list update with a subtle "saving..." indicator? Or just instant appearance? The undo pattern for failed optimistic updates needs visual design. |
| 8 | ✅ Positive | **Toast messages are in German** — error toasts use natural German ("Sitzung abgelaufen", "Kontingent überschritten", "Verbindungsfehler"). Consistent with the i18n-first approach from Sprint 4. |
| 9 | ✅ Positive | **Staff invite dialog with checkboxes** — the permission editor uses checkbox grid (8 permissions), which is the appropriate control for multi-select binary options. Familiar pattern. |
| 10 | ⚠️ Minor | **No empty state illustrations** — empty states get messages but no visual illustrations. Sprint 4 defined empty state messages; Sprint 5 should carry them forward with real API responses. |

### Scores

| Dimension | Score | Rationale |
|-----------|-------|-----------|
| Precision | 87% | Loading patterns (skeletons, error state, retry) are specified as components. Toast messages have exact copy. Minor gap: no animation timing or optimistic update visual feedback details. |
| Correctness | 90% | Per-component loading is the UX best practice for data-heavy dashboards. Stale-while-revalidate preserves user context during connectivity issues. Checkbox grid for permissions is appropriate. |
| Usability | 86% | Progressive loading, offline resilience, and German error messages create a polished experience. Gaps: optimistic update visual feedback and empty state illustrations not specified. |
| Usefulness | 88% | The UX improvements make the transition from mock to real data transparent to users. Loading and error states prevent confusion. Staff management UI follows established patterns. |

**Composite Score: 88%**

### Remaining Gaps (nice-to-have)
- Optimistic update visual feedback pattern (saving indicator, rollback animation)
- Empty state illustrations (consistent with shadcn/ui style)
- Loading animation design tokens (shimmer timing, colors)
- Accessibility audit for new staff management components (focus management in dialogs)

---

## Summary & Composite Scores

| # | Persona | Composite Score | Top Concern |
|---|---------|-----------------|-------------|
| 1 | 👤 Club Member | **91%** | No push notifications for distributions |
| 2 | 🏢 Club Owner | **90%** | Staff activity log deferred |
| 3 | 💻 Developer | **91%** | Optimistic update rollback pattern unspecified |
| 4 | 🛡️ Compliance Officer | **89%** | Immutability re-confirmation at API level |
| 5 | 🔒 Security Auditor | **90%** | Rate limiting on auth endpoints |
| 6 | 🎨 UX Designer | **88%** | Optimistic update visual feedback unspecified |

### Overall Score: **90%** ✅

**Verdict: APPROVED — exceeds 85% threshold on first pass.**

---

## Dimension Averages

| Dimension | Average | Min | Max |
|-----------|---------|-----|-----|
| **Precision** | 90% | 87% (UX) | 93% (Owner) |
| **Correctness** | 92% | 90% (UX) | 94% (Member) |
| **Usability** | 87% | 85% (Owner) | 88% (Dev/Member) |
| **Usefulness** | 91% | 88% (UX) | 92% (Dev/Owner) |

---

## Consolidated Improvement Suggestions (Non-Blocking)

These are recommendations for implementation-time refinement, not plan revisions:

| # | Category | Suggestion | Priority |
|---|----------|------------|----------|
| 1 | Technical | Add explicit optimistic update rollback pattern (React Query `onMutate`/`onError`) | Medium |
| 2 | Security | Add rate limiting to `/auth/login` and `/staff/invite` endpoints | Medium |
| 3 | Compliance | Re-confirm distribution immutability at API level (not just UI lock icons) | Medium |
| 4 | UX | Define visual feedback for optimistic updates (saving indicator, error rollback) | Low |
| 5 | Technical | Consider OpenAPI schema validation for frontend/backend DTO contract | Low |
| 6 | UX | Carry forward empty state illustrations from Sprint 4 design system | Low |
| 7 | Security | Document CSRF token handling for portal session auth | Low |

---

## Comparison with Sprint 4 Review

| Metric | Sprint 4 (Iteration 1) | Sprint 4 (Iteration 2) | Sprint 5 (This Review) |
|--------|------------------------|------------------------|------------------------|
| Overall Score | 78% | 88% | **90%** |
| Iterations Needed | 2 | — | **1** ✅ |
| Blocking Gaps | 8 | 0 | **0** |
| Non-Blocking Gaps | 5 | 3 | **7** (minor) |

Sprint 5 plan achieves a higher first-pass score than Sprint 4's final iteration, demonstrating that learnings from the Sprint 4 review process were incorporated into the planning methodology. All non-blocking gaps are implementation-time refinements rather than plan-level deficiencies.
