This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
MUNify DELEGATOR is a SvelteKit-based application for managing Model United Nations conference registration, delegation assignment, and organizational matters. Built with Svelte 5, TypeScript, Prisma ORM, and GraphQL (via Pothos & Yoga), it's designed for DMUN e.V. but can be adapted for other MUN conferences.
- Frontend: SvelteKit with Svelte 5 (runes mode), TailwindCSS 4, DaisyUI
- Backend: Node.js with SvelteKit server routes
- Database: PostgreSQL via Prisma ORM
- GraphQL: Pothos schema builder with graphql-yoga server, Houdini client
- Auth: OpenID Connect (OIDC) - recommended provider: Logto
- i18n: Paraglide-JS for internationalization (default locale: German)
- Runtime: Bun (package manager and development runtime)
- Observability: OpenTelemetry tracing support
# Install dependencies
bun install
# Start development (Docker + dev server)
bun run dev
# Separate commands
bun run dev:docker # Start Docker containers (PostgreSQL, etc.)
bun run dev:server # Start Vite dev server only
# Install git hooks (lefthook)
bunx lefthook install# Generate Prisma client
bunx prisma generate
# Create and apply migrations
bunx prisma migrate dev
# Reset database (WARNING: deletes all data)
bunx prisma migrate reset
# Seed database with development data
bun prisma/seed/dev/seed.ts
# Open Prisma Studio (database GUI)
bun run studio# Format code
bun run format
# Type checking (PREFERRED for development - fast feedback)
bun run check # Single run - use this to verify changes
bun run check:watch # Watch mode
bun run typecheck # TypeScript only
# Lint (slow - runs automatically on git push via lefthook)
bun run lint # Only run manually when specifically needed
# Testing
bun test # Run tests once
bun run test:watch # Watch mode
bun run coverage # With coverage reportNote: Prefer bun run check over bun run lint during development. Linting is slow (~2 min) and runs automatically on push via lefthook pre-push hooks. Use bun run check for quick type-checking feedback.
# Check for missing/unused translations
bun run i18n:check
# Validate translation project
bun run i18n:validate
# Auto-translate missing keys
bun run i18n:translate
# Add new translation key interactively
bun run add-translation# Production build
bun run build
# Preview production build
bun run preview-
src/routes/- SvelteKit routes (filesystem-based routing)(authenticated)/- Protected routes requiring authenticationdashboard/- Conference dashboardsmanagement/- Admin interfaces for conference/delegation managementregistration/- User registration flowsassignment-assistant/- Delegation assignment tools
api/graphql/- GraphQL API endpointauth/- OIDC authentication callbacksseats/,vc/,validateCertificate/- Public-facing pages
-
src/api/- Backend GraphQL API layerresolvers/- GraphQL resolvers organized by modulemodules/- Domain-specific resolvers (conference, delegation, committee, etc.)api.ts- Main GraphQL Yoga server configurationbuilder.ts- Pothos schema builder setup with plugins
abilities/- CASL-based authorization rules per entityservices/- Backend business logic services
-
src/lib/- Shared frontend codecomponents/- Reusable Svelte componentsqueries/- Houdini GraphQL queries/mutationsservices/- Frontend utility functionsschemata/- Zod validation schemasparaglide/- Generated i18n code
-
src/config/- Environment configurationpublic.ts- Client-side config (PUBLIC_* env vars)private.ts- Server-side config (secrets, DB URLs)
-
src/tasks/- Background scheduled tasks (node-schedule)conferenceStatus.ts- Auto-update conference statesmailSync.ts- Email synchronization with external systems
-
prisma/- Database layerschema.prisma- Prisma schema definitionmigrations/- Database migration historyseed/- Database seeding scriptspothos/- Generated Pothos types from Prisma
- Schema-first approach using Pothos code-first schema builder
- All resolvers in
src/api/resolvers/modules/are auto-imported inapi.ts - Plugin stack: Prisma integration, complexity limiting, OpenTelemetry tracing, utilities
- Authorization: CASL ability checks per entity type in
src/api/abilities/entities/ - Context includes: Prisma client, OIDC user info, request metadata
-
Houdini manages GraphQL client-side operations
-
Type-safe generated queries in
.houdini/directory -
Queries/mutations in
src/lib/queries/trigger codegen -
Config in
houdini.config.js- runes mode enabled -
Cache Invalidation: After mutations that modify data, you must invalidate Houdini's cache to update the UI:
import { cache } from '$houdini'; import { invalidateAll } from '$app/navigation'; // After a mutation: cache.markStale(); await invalidateAll();
- OIDC flow via
openid-clientlibrary - Login callbacks in
src/routes/auth/ - User context injected into GraphQL context via
src/api/context/ - CASL abilities define fine-grained permissions per entity:
- Actions:
list,read,update,delete,impersonate - Each entity type has dedicated ability definitions
- Actions:
- Team member roles:
Admin,PROJECT_MANAGEMENT,PARTICIPANT_CARE, etc.
- Paraglide-JS with
urlandbaseLocalestrategy - Source translations in
messages/directory - Use
$t()function in components for translated strings - Generate translations via Inlang CLI
- German gender-inclusive language: Use gender-neutral forms when possible (e.g., "Teilnehmende" instead of "Teilnehmer"). When neutral forms aren't available, use the gender-asterisk format (e.g., "der/die Teilnehmer*in", "Delegationsleiter*in").
- Prisma ORM with PostgreSQL
- Models:
Conference,Delegation,Committee,DelegationMember,SingleParticipant, etc. - Conference state machine:
PRE→PARTICIPANT_REGISTRATION→PREPARATION→ACTIVE→POST - Payment tracking via
PaymentTransactionmodel - Paper submission system with versioning and reviews
- node-schedule for cron jobs
- Tasks registered in
src/tasks/index.ts - Conference state auto-transitions, email list synchronization
- Task output written to
tasksOut/directory (configured in build)
$api → src/api
$assets → src/assets
$db → prisma
$config → src/config
$houdini → .houdini
- PDF Generation:
pdf-libwith custom fonts for certificates - Barcode/QR:
@bwip-js/browser,@svelte-put/qr,barcode-detector - Rich Text Editor: TipTap for committee agenda items
- Maps: Leaflet via
sveafletfor delegation locations - External APIs: Listmonk email service integration in
src/tasks/apis/
See CLAUDE-UI.md for comprehensive UI design documentation including:
- Form components and
FormFieldsetgrouping patterns - Modal and Drawer usage
- Dashboard section layouts
- DataTable configuration
- Navigation components (Tabs, NavMenu)
- Status indicators and badges
- Color theming and icons
- Code examples for common patterns
Quick reference:
- Forms: Always wrap related inputs with
FormFieldsetfor visual grouping - Modals: Use
Modalcomponent withactionsnippet for footer buttons - Layout: Use DaisyUI classes; prefer
bg-base-*and semantic colors - Icons: Use FontAwesome Duotone (
fa-duotone fa-icon-name) - URL State: Use
sveltekit-search-paramsfor URL-persisted state
Maintenance: When creating new UI components, significantly modifying existing ones (props, usage patterns), or deprecating components, update CLAUDE-UI.md accordingly. Keep documentation in sync with the actual component implementations.
Follow Conventional Commits:
feat:- New featurefix:- Bug fixstyle:- UI/styling changesrefactor:- Code restructuring without behavior changeperf:- Performance improvementsdocs:- Documentation updatestest:- Test updateschore:- Maintenance tasksbuild:- Build system changesci:- CI/CD changes
Format: <type> (<scope>): <description> (e.g., feat (Frontend): implement login modal)
- Vitest with jsdom environment
- Test files:
src/tests/directory - Coverage provider: v8
- Run tests before pushing (enforced by lefthook pre-push hook)
Required variables (see .env.example):
DATABASE_URL- PostgreSQL connection stringSECRET- Session encryption secretNODE_ENV- Environment (development/production/test)PUBLIC_OIDC_AUTHORITY- OIDC provider URLPUBLIC_OIDC_CLIENT_ID- OAuth client IDOIDC_SCOPES- OAuth scopes (must includeopenid)OIDC_ROLE_CLAIM- JWT claim for rolesCERTIFICATE_SECRET- Secret for signing participation certificates- OpenTelemetry vars (optional):
OTEL_ENDPOINT_URL,OTEL_SERVICE_NAME
- Create resolver module in
src/api/resolvers/modules/ - Import in
src/api/resolvers/api.ts - Define CASL abilities in
src/api/abilities/entities/if new entity - Restart dev server to regenerate schema
- Create/modify components in
src/lib/components/ - Add GraphQL queries in
src/lib/queries/ - Houdini auto-generates types on save
- Use generated stores in Svelte components
- Add translations to
messages/directory
- Edit
prisma/schema.prisma - Run
bunx prisma migrate dev --name <descriptive-name> - Prisma auto-generates client and Pothos types
- Update seed scripts if needed
- Test migrations against seed data
- Create task file in
src/tasks/ - Use
node-scheduleto define cron schedule - Import task in
src/tasks/index.ts - Ensure task output directory exists
Pre-commit: Auto-format staged files with Prettier Pre-push: Run tests and lint staged files
- Svelte 5 runes mode enabled - use
$state,$derived,$effectinstead of legacy stores - Houdini query deduplication and caching
- GraphQL query complexity limiting via Pothos plugin
- OpenTelemetry tracing for performance monitoring
- Image optimization: Use WebP format, lazy load images
- Install dependencies always into the devDependencies section as is best practice for sveltekit projects if not explicitly required at runtime.
- OIDC authentication required for all routes in
(authenticated)/ - CASL authorization checks on all GraphQL mutations
- Certificate signatures use HMAC-SHA256
- Environment secrets must never be committed
- Prisma parameterized queries prevent SQL injection
- GraphQL complexity limits prevent DoS attacks
This codebase supports 100% end-to-end type safety from database to frontend. The type system is your primary defense against bugs—use it properly.
-
NEVER use
any- Theanytype defeats the purpose of TypeScript. Only use it when absolutely unavoidable (e.g., third-party library limitations), and always add a comment explaining why. -
NEVER use type casting (
as Type) - Type assertions bypass the compiler's checks. If you feel the need to cast, it indicates a type definition problem that should be fixed at the source. -
Trust the generated types - Prisma, Pothos, and Houdini generate accurate types. If types don't match your expectations, investigate why rather than casting.
-
Fix type errors at the source - When encountering type mismatches:
- Check if the GraphQL query/mutation needs updating
- Verify the Prisma schema is correct
- Ensure Houdini codegen has run (
bun run devtriggers this) - Never silence errors with
as anyor@ts-ignore
-
Use type narrowing - Prefer type guards, discriminated unions, and proper null checks over assertions.
- Prisma generates types from
schema.prisma - Pothos generates GraphQL schema types from Prisma
- Houdini generates frontend types from GraphQL operations
- This chain provides compile-time guarantees that data flows correctly through the entire stack
If any or casting is truly unavoidable, you MUST:
- Add a
// TYPE-SAFETY-EXCEPTION:comment explaining why - Keep the scope as narrow as possible
- Consider opening an issue to fix it properly later
This project uses Model Context Protocol (MCP) servers to enhance AI-assisted development. Configuration is in .mcp.json.
| Server | Package/URL | Purpose |
|---|---|---|
| svelte | @sveltejs/mcp |
Official Svelte 5 documentation, code validation and fixes |
| daisyui-github | gitmcp.io/saadeghi/daisyui |
DaisyUI component library documentation |
| tailwind-github | gitmcp.io/tailwindlabs/tailwindcss |
TailwindCSS documentation |
| github | @anthropic-ai/github-mcp-server |
GitHub PRs, issues, code search, workflow management |
| prisma | prisma mcp |
Database migrations, schema introspection, Prisma Studio |
| vitest | @djankies/vitest-mcp |
Test running with structured output, coverage analysis |
| context7 | @upstash/context7-mcp |
Up-to-date documentation for any library |
| memory | @modelcontextprotocol/server-memory |
Persistent knowledge graph across sessions |
| sequential-thinking | @modelcontextprotocol/server-sequential-thinking |
Complex problem-solving through structured thinking |
| houdini-github | gitmcp.io/HoudiniGraphql/houdini |
Houdini GraphQL client documentation |
| pothos-github | gitmcp.io/hayes/pothos |
Pothos GraphQL schema builder documentation |
GitHub MCP Server: Requires a GitHub Personal Access Token. Replace <YOUR_TOKEN> in .mcp.json with your token, or set the GITHUB_PERSONAL_ACCESS_TOKEN environment variable.
Context7: Optionally get a free API key at context7.com/dashboard for higher rate limits.
- Use
use context7in prompts to fetch current documentation for any library - The Svelte MCP server validates Svelte 5 code and suggests fixes
- Prisma MCP can run migrations and open Prisma Studio directly
- Vitest MCP provides structured test output optimized for AI analysis
- Memory MCP remembers context across conversation sessions
- Never use the git commit command after a task is finished.
When creating pull requests, always apply the appropriate PR: * label based on the conventional commit type in the PR title:
| Commit prefix | Label |
|---|---|
feat (new functionality) |
PR: Feature |
feat (improve existing) |
PR: Enhancement |
fix |
PR: Bug |
perf |
PR: Performance |
refactor, style |
PR: Refactor |
docs |
documentation |
ci, build, chore |
PR: Infrastructure |
deps, dependencies |
dependencies |
test |
PR: Tests |
Every PR must have at least one of these labels before merging. This powers the auto-generated release notes.