This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
These instructions are for AI assistants working in this project.
Always open @/openspec/AGENTS.md when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use @/openspec/AGENTS.md to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
Stable Manager (SM) is an equine care management platform for stables. It tracks horses, health events, vaccinations, worm tests, treatments, and sends automated notifications. Multi-tenant support isolates data per stable.
- Backend: Jmix 2.7.1, Spring Boot, Java
- Frontend: Vaadin 24.9.2 Flow UI (Java-based views)
- Database: PostgreSQL with EclipseLink ORM
- Build: Gradle 8.x
- Scheduling: Quartz for automated jobs
- File Storage: AWS S3 via jmix-awsfs
- Notifications: Email (SMTP), SMS (GatewayAPI), In-app
# Development
./gradlew bootRun # Run dev server (port 7070)
./gradlew build # Full build with tests
# Testing
./gradlew test # Run all tests
./gradlew test --tests "ClassName" # Run single test class
./gradlew test --tests "ClassName.methodName" # Run single test method
# Frontend
npm install # Install frontend dependencies
./gradlew vaadinBuildFrontend # Build frontend bundle
# Docker (user builds manually - do NOT run bootBuildImage automatically)
./gradlew bootBuildImage --imageName=torbenmerrald/stablemanager:<version>
# Deployment (after user builds and pushes image)
docker push torbenmerrald/stablemanager:<version>
kamal deploy --skip-push --version=<version>entity/- JPA entities with Jmix annotations (42 entities)view/- Vaadin Flow UI views (76 views)service/- Business logic layerapp/- Scheduled Quartz jobs and application listenerslistener/- Entity event listeners for cascading updatessecurity/- Roles and user repository
Horse is the central entity, linked to:
VaccinePlan/EventVaccination- Vaccination trackingWormTestPlan/EventWormTest- Worm test schedulingTreatmentSchedule/PlannedTreatmentTask- Recurring treatmentsEvent(polymorphic) - EventVet, EventFood, EventHealth, etc.Documents- File attachments in S3
- Multi-Tenant Isolation: All entities use
@TenantId. Queries automatically filter by tenant. - Soft Deletes: Use Jmix soft delete patterns. Never hard delete.
- Event Listeners:
listener/*.javahandles cascading updates and denormalization. - Scheduled Jobs:
app/*Job.javafor reminders, task generation, cache refresh. - Notification System:
NotificationHistoryprevents duplicate notifications. - Denormalization: Horse entity caches computed fields for performance.
| Job | Purpose |
|---|---|
| VaccinationReminderJob | Sends vaccination due/overdue alerts |
| WormTestReminderJob | Sends worm test reminders |
| TaskGenerationJob | Creates PlannedTreatmentTask from schedules |
| PassportReminderJob | Passport expiry notifications |
Detailed guidance for common patterns is available in .claude/skills/:
jmix-soft-deletion- Working with soft-deleted entitiesjmix-data-loaders- Custom data loading with LoadContext hintsjmix-help-dialogs- Info icon buttons with help dialogsjmix-dashboard-cards- Dashboard cards with success/warning statesjmix-orchestrator-jobs- Global jobs that orchestrate multiple related jobsvaadin-grid-styling- Dynamic row styling withsetPartNameGenerator()and::part()CSSkamal-deployment- Kamal 2 deployment with pre-deploy backups (user builds images manually)
NEVER auto-generate Liquibase changesets. Use Jmix Studio exclusively for schema changes. See src/main/resources/dk/merrald/sm/liquibase/CLAUDE.md for details.
If database changes are needed:
- Stop and inform the user
- Ask for guidance on proper workflow
- Wait for explicit permission
All queries must respect tenant isolation. Never bypass with native queries that skip tenant filtering.
Always check NotificationHistory before sending notifications to prevent duplicates.
- JUnit 5 with
@SpringBootTestfor integration tests AuthenticatedAsAdminextension intest_support/for system authentication- Test classes mirror source structure with
Testsuffix - Run
./gradlew testbefore pushing
- Java: 4-space indentation, Lombok for DTOs
- Naming:
*Service,*Repository,*View,*Job,*Listener - Package-by-domain organization
- Translations in
messages_en.propertiesandmessages_da_DK.properties - Commits: conventional style (
feat:,fix:,chore:)
application.properties- Base configurationapplication-dev.properties- Local development (port 7070)application-prod.properties- Production (uses env vars)menu.xml- Application menu structure
SQL_URL, SQL_USERNAME, SQL_PASSWORD # Database
S3_BUCKET, S3_ACCESSKEY, S3_SECRET_ACCESSKEY, S3_ENDPOINT # File storage
SMS_GATEWAYAPI_KEY, SMS_GATEWAYAPI_SECRET # SMS
OPENAI_API_KEY (optional) # Document summarization