# Crewli — Claude Code Master Prompt # Plak dit BOVEN elke task. Vervang [TASK] onderaan. ## MANDATORY PREAMBLE — READ BEFORE DOING ANYTHING Read `/CLAUDE.md` and `/dev-docs/SCHEMA.md` in full before starting. These are your single source of truth. Do not deviate from them. Do not make assumptions about the database schema — always verify against SCHEMA.md. Also read `/dev-docs/API.md` for the existing API contract. If the task involves a module that already has existing code, read that code first. Understand the current state before making changes. ## PROJECT CONTEXT Crewli is a multi-tenant SaaS platform for event and festival management. - **Backend:** Laravel 12 REST API (no Blade views, no Inertia), Sanctum auth, Spatie Permission, MySQL 8, Redis - **Frontend:** Two standalone Vue 3 + TypeScript SPAs on Vuexy 9.5 / Vuetify 3.10 — `apps/app/` (port 5174), `apps/portal/` (port 5175) - **State:** Pinia + TanStack Vue Query - **Forms:** VeeValidate + Zod - **API base path:** `/api/v1/` - **Testing:** PHPUnit (backend), Vitest (frontend) ### Repository structure ``` crewli/ ├── api/ # Laravel 12 backend │ ├── app/ │ │ ├── Http/ │ │ │ ├── Controllers/Api/V1/ │ │ │ ├── Middleware/ │ │ │ └── Requests/ # Form Requests per endpoint │ │ ├── Models/ │ │ ├── Policies/ │ │ ├── Services/ # Business logic (NOT in controllers) │ │ ├── Enums/ # PHP Enums for all status/type fields │ │ ├── Events/ + Listeners/ │ │ └── Jobs/ # Queued jobs (briefings, notifications) │ ├── database/ │ │ ├── migrations/ │ │ ├── factories/ │ │ └── seeders/ │ └── tests/Feature/Api/V1/ ├── apps/ │ ├── app/ # Organizer + Platform Admin SPA (main app) │ │ └── src/ │ │ ├── lib/axios.ts # THE ONLY axios instance │ │ ├── composables/api/ # TanStack Query composables │ │ ├── stores/ # Pinia stores │ │ ├── types/ # TypeScript interfaces │ │ └── pages/ │ └── portal/ # Dual-mode portal ├── dev-docs/ # Developer documentation (source of truth) │ ├── SCHEMA.md │ ├── API.md │ ├── BACKLOG.md │ ├── design-document.md │ ├── dev-guide.md │ └── start-guide.md ├── docs/ # VitePress end-user documentation (Dutch) ├── CLAUDE.md # This file's companion — workspace rules └── .cursorrules ``` ### Key architectural decisions (final, non-negotiable) - **ULID** primary keys on all business tables via `HasUlids` — NEVER UUID v4 - **Integer auto-increment** on pure pivot tables only - **JSON columns** exclusively for opaque config (settings, blocks) — never for queryable data - **Multi-tenancy** via `OrganisationScope` Eloquent Global Scope on all event-related models - **Soft delete** per table type as documented in SCHEMA.md — immutable audit records (check_ins, form_submissions, briefing_sends) do NOT get soft delete - **Portal dual-mode:** Sanctum session for volunteers/crew (persistent identity), `portal.token` middleware for artists/suppliers/press (event-specific, no account) --- ## ZERO-COMPROMISE RULES — VIOLATIONS ARE BLOCKERS These rules are absolute. No workarounds. No "we'll fix it later". No partial implementations. If something cannot be done properly, STOP and report it. ### Architecture & design 1. **ARCHITECTURE FIRST.** If the task requires a new pattern, data structure, or integration that isn't documented in CLAUDE.md or SCHEMA.md — stop and ask. Do not invent architecture. Write an Architecture Decision Record (ADR) in `/dev-docs/decisions/` if a significant design choice is needed. 2. **DELETE OVER ADAPT.** If you find duplicate logic, conflicting patterns, or legacy code that does the same thing differently: delete the worse version. Do not build alongside it. Do not "wrap" it. If two implementations exist, one must die. Consolidate to one source of truth. 3. **STRICT LAYERING.** Business logic belongs in `app/Services/`, NEVER in controllers. Controllers handle HTTP concerns only: receive request, call service, return resource. Data access patterns go through Eloquent models with proper scopes. No "quick fixes" in the wrong layer. - Controller → receives FormRequest, calls Service, returns API Resource - Service → contains business logic, validation beyond FormRequest, orchestration - Model → relationships, scopes, accessors, mutators - Job → async work dispatched by Service 4. **CONTRACT-FIRST.** Before implementing any module: - Define the PHP Enum(s) in `app/Enums/` for all status/type fields - Define the API Resource (response shape) first - Define the Form Request (input validation) first - Then implement the Service and Controller Types and contracts define behaviour. Implementation follows. 5. **CONSISTENCY OVER CLEVERNESS.** Use the same pattern for every similar problem. If existing modules use a specific approach for pagination, error handling, or resource loading — use that exact same approach. Never introduce a "better" alternative pattern without refactoring ALL existing code to match. One pattern per problem type, across the entire codebase. 6. **SINGLE SOURCE OF TRUTH.** Every piece of information exists in exactly one place: - Enum values → PHP Enum class (not string literals) - Validation rules → Form Request (not duplicated in frontend) - Response shape → API Resource (not ad-hoc arrays) - Config values → .env / config files (not hardcoded) - Schema definition → SCHEMA.md (not guessed) ### Code quality 7. **NO TODO / FIXME / HACK.** Zero tolerance. If you cannot complete something, stop and report it. Do not leave stubs, placeholders, or "implement later" comments. Every file you touch must be production-ready. 8. **NO UNTYPED CODE.** PHP: `declare(strict_types=1)` on every file. Return types on all methods. Typed properties. Use PHP Enums (not string literals) for all status, type, and role fields. No `mixed` where a concrete type or union type would work. 9. **NO GENERIC NAMES.** Names must be specific and self-documenting: - ❌ `DataService`, `Helper`, `Manager`, `handleData()`, `processItem()` - ✅ `ShiftAssignmentService`, `VolunteerAvailabilityChecker`, `resolveShiftConflict()`, `calculateFillRate()` 10. **NO SILENT ERROR HANDLING.** No empty catch blocks. No `catch { return null; }`. Every error must be: logged (via `Log::error()` with context), or rethrown, or handled with a proper API error response. Use `report($e)` for unexpected errors. ### Testing 11. **TESTS ARE DESIGN, NOT AFTERTHOUGHT.** Tests define expected behaviour. Every controller needs feature tests covering: - 200/201 (happy path for each action) - 401 (unauthenticated access) - 403 (wrong organisation — cross-tenant access attempt) - 403 (insufficient role/permission) - 422 (validation errors with specific field assertions) - Edge cases specific to the module (e.g., shift conflict, capacity full) Run `php artisan test` after EVERY module. Fix failures before proceeding. Never skip, comment out, or mark tests as incomplete. 12. **FACTORIES ARE REALISTIC.** Use realistic Dutch test data (names, addresses, company names) that reflects actual usage. Factories must create valid, complete records — no missing required fields or placeholder values. ### Data & persistence 13. **DATA MODEL IS SACRED.** Never deviate from SCHEMA.md. Every column, every constraint, every index documented there must be in the migration. If SCHEMA.md is unclear, ask — do not guess. 14. **EVERY MIGRATION HAS down().** The `down()` method must cleanly reverse the `up()`. Drop tables, remove columns, restore previous state. No `down()` methods that throw or do nothing. 15. **INDEXES ARE MANDATORY.** Every foreign key column, every column used in WHERE/ORDER BY clauses, every unique constraint from SCHEMA.md must have an explicit index. Verify composite indexes match the documented patterns. ### Security & multi-tenancy 16. **NO UNSCOPED QUERIES.** Every query on event-related models must be scoped to `organisation_id` via `OrganisationScope`. Mental test: can User A from Org 1 ever see, modify, or infer the existence of data from Org 2? If yes → security bug → fix immediately. 17. **POLICIES ARE COMPLETE.** Every policy method checks: - Does the user belong to the correct organisation? - Does the user have the required Spatie role/permission for this action? - No `return true` placeholders. No missing methods. 18. **FORM REQUESTS ARE COMPLETE.** Every store/update has a Form Request with full validation matching SCHEMA.md constraints: required, nullable, max length, enum values (referencing the PHP Enum), exists rules with proper scoping (e.g., `exists:events,id` scoped to organisation). ### API responses 19. **NO BARE MODEL RETURNS.** Every API response goes through an API Resource. Never `return $model`, `$model->toArray()`, or raw arrays. Resources define the public contract. Computed fields (fill_rate, status_label, slot counts) belong in the Resource. 20. **CONSISTENT RESPONSE STRUCTURE.** Follow the existing pattern: `{ data: {...}, meta: {...} }` for paginated lists. Consistent error format with `{ message: "...", errors: {...} }` for validation failures. ### Resilience & operations 21. **IDEMPOTENT OPERATIONS.** Every queued job must be safe to retry. Check-before-act: verify state hasn't changed. Use database transactions for multi-step mutations. Queued notifications and external API calls (Zender SMS/WhatsApp) must handle duplicates gracefully. 22. **OBSERVABILITY.** Log significant business events using `spatie/laravel-activitylog` (already installed). Every create, update, delete, and status change on business entities must be logged with: - `causedBy($user)` — who did it - `performedOn($model)` — what was affected - `withProperties([...])` — relevant context (old values, new values) 23. **API VERSIONING.** All routes under `/api/v1/`. Controllers in `App\Http\Controllers\Api\V1\`. Never introduce breaking changes to existing endpoints — add new fields, don't rename or remove. ### Process 24. **MODULE GENERATION ORDER.** Always follow this sequence. No skipping. 1. PHP Enum(s) for status/type fields (`app/Enums/`) 2. Migration(s) — verify against SCHEMA.md 3. Eloquent Model with: HasUlids, HasFactory, SoftDeletes (if documented), OrganisationScope (if event-related), relationships, scopes, accessors 4. Factory with realistic Dutch test data 5. Service class for business logic (`app/Services/`) 6. Policy for authorisation 7. Form Request(s) for validation 8. API Resource for response transformation 9. Resource Controller (thin — delegates to Service) 10. Routes in `api/routes/api.php` 11. Feature tests — run them, fix failures 12. Activity log integration in Service methods 13. Update `/dev-docs/API.md` with new routes 25. **GIT.** Auto-commit after each completed module: `feat(module-name): add backend scaffold with tests` --- ## VERIFICATION CHECKLIST (run before reporting "done") ```bash # All tests pass php artisan test # Database rebuilds cleanly php artisan migrate:fresh --seed # New routes are visible php artisan route:list --path=api/v1 # No forbidden patterns grep -rn "TODO\|FIXME\|HACK\|dd(\|dump(\|var_dump\|Model::all()" \ api/app/ api/tests/ --include="*.php" # No UUID v4 in migrations grep -rn "uuid(" api/database/migrations/ --include="*.php" # Static analysis (if configured) ./vendor/bin/phpstan analyse ``` Manual verification: - [ ] Every new model has: HasUlids, HasFactory, correct SoftDeletes, complete $fillable, all relationships from SCHEMA.md - [ ] Every new model with event data has OrganisationScope - [ ] Every controller action is covered by a Policy method - [ ] Every Service method logs activity via spatie/laravel-activitylog - [ ] Every Form Request references PHP Enums (not string literals) for enum validation - [ ] Business logic is in Service classes, not in Controllers - [ ] API Resource includes computed fields where applicable - [ ] No N+1: index actions use `with()` for all accessed relationships - [ ] `/dev-docs/API.md` updated with new routes --- ## TASK [INSERT SPECIFIC TASK HERE]