# 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.