This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Benefit Decision Toolkit (BDT) is a platform for creating benefit eligibility screeners using Decision Model and Notation (DMN) and Form-JS. The project consists of four main applications:
- library-api: Standalone Quarkus API that generates REST endpoints from DMN files (Kogito-based)
- builder-api + builder-frontend: Web application for creating and managing screeners (admin tool). Also deploys a public-facing screener evaluation interface (end-user tool).
The core concept: Subject matter experts can define eligibility rules using visual DMN decision tables, which automatically become REST APIs and interactive screeners without traditional software development.
# One-time setup with Devbox (recommended)
bin/install-devbox && devbox run setup
# Or without Devbox (requires manual dependency installation)
bin/setup# Start all services in development mode (uses process-compose)
devbox services up
# Or manually with process-compose (if not using devbox)
process-compose
# Run library-api standalone
cd library-api && quarkus dev
# Serves at http://localhost:8083, Swagger UI at /q/swagger-ui
# Run builder services (requires Firebase emulators)
# Terminal 1: Start Firebase emulators
firebase emulators:start --project demo-bdt-dev --only auth,firestore,storage
# Terminal 2: Start builder-api
cd builder-api && quarkus dev
# Debug port: 5005
# Terminal 3: Start builder-frontend
cd builder-frontend && npm run dev# Build specific API
cd builder-api && mvn clean package
cd library-api && mvn clean package
# Build frontend
cd builder-frontend && npm run build
# Clean rebuild (useful when DMN files change)
mvn clean compile# Run Java tests for an API
cd builder-api && mvn test
# Run library-api tests with Bruno (API testing tool)
cd library-api/test/bdt && bru run
# Frontend doesn't have test suites currentlyThis is a monorepo containing four distinct applications that work together:
-
library-api (Kogito-based DMN → REST API generator)
- Standalone Quarkus app using Kogito for automatic API generation
- DMN files in
src/main/resources/become REST endpoints - See
library-api/CLAUDE.mdfor detailed documentation
-
builder-api (Quarkus REST API, ~3,300 LOC)
- Admin backend for screener creation and management
- Integrates Firebase (Auth, Firestore, Cloud Storage)
- Uses KIE DMN (not Kogito) for manual DMN compilation and evaluation
- Main packages:
controller,service,persistence,model
-
builder-frontend (Solid.js, Form-JS Editor, DMN-JS)
- Visual editor for creating benefit screeners
- Features: Form editor, DMN decision editor, benefit configuration, preview, publish
- Routes:
/(home),/project/:id(editor),/check/:id(DMN editor)
Builder Flow:
Admin → builder-frontend → builder-api → Firebase (Firestore + Storage)
↓
Compile DMN → Store compiled JAR
↓
Publish screener
Separation of Concerns:
- Eligibility Checks: Reusable DMN models (independent decision logic)
- Benefits: Configurations that reference one or more eligibility checks
- Forms: Separate schemas defining user input fields
- Screeners: Containers that combine forms + benefits + checks
DMN Processing Differences:
- library-api: Uses Kogito (automatic code generation at build time)
- builder-api: Uses KIE DMN directly (runtime compilation from XML)
Storage Strategy:
- Metadata (relationships, configs): Firestore NoSQL collections
- Large artifacts (DMN files, form schemas, compiled JARs): Google Cloud Storage
- Reference data (location lookups): Embedded SQLite databases
Authentication:
- builder-api/builder-frontend: Firebase Auth required (user ownership model). Specific endpoints starting with /published are publicly accessible.
- library-api: No authentication (standalone utility)
Backend (All APIs):
- builder-api: Quarkus 3.23.0, Java 21, KIE DMN 10.0.0
- library-api: Quarkus 2.16.10, Java 17, Kogito 1.44.1
Frontend:
- Framework: Solid.js (reactive JavaScript framework)
- Form Builder/Renderer: Form-JS (BPMN.io)
- DMN Editor: DMN-JS + Kogito Tooling
- Styling: Tailwind CSS
Infrastructure:
- Dev Environment: Devbox (Nix-based) or Devcontainer
- Process Management: process-compose
- Cloud Services: Firebase (Auth, Firestore, Cloud Storage)
- Database: SQLite (embedded for reference data)
In library-api (Kogito):
- Add/edit DMN file in
library-api/src/main/resources/ - Run
quarkus dev- Kogito auto-generates REST endpoints - Check
/q/swagger-uifor new endpoints - Test with Bruno:
cd library-api/test/bdt && bru run
In builder-api (Manual):
- Create eligibility check via builder-frontend UI
- Upload DMN file through UI (stores in Cloud Storage)
- builder-api compiles DMN on-demand during evaluation
- Publish screener to create pre-compiled artifact
DMN Editing:
- Use VS Code extension: DMN Editor
- Learn DMN basics: https://learn-dmn-in-15-minutes.com/
- Access raw XML: Right-click → "Reopen with Text Editor"
The builder-api needs metadata about available library checks (from library-api). This metadata is stored in Firebase Storage and referenced from Firestore.
Automatic Sync (Development):
- Runs automatically when you start services via
devbox services up - Syncs from local library-api (http://localhost:8083) to Firebase emulators
- Happens after library-api starts, before builder-api starts
- Library checks will then be visible in the builder UI!
Manual Sync (Development):
# Re-sync after making library-api changes
./scripts/sync-library-metadata.sh
# Then restart builder-api to pick up new metadata
# (In process-compose UI, restart the builder-api process)Production Sync (maintainers only):
# Set production library-api URL and unset emulator variables
export LIBRARY_API_BASE_URL=https://library-api-1034049717668.us-central1.run.app
unset FIRESTORE_EMULATOR_HOST
unset GCS_BUCKET_NAME
unset QUARKUS_GOOGLE_CLOUD_STORAGE_HOST_OVERRIDE
# Authenticate with GCP
gcloud auth application-default login
# Run sync
./scripts/sync-library-metadata.shHow It Works:
- Fetches OpenAPI spec from library-api
- Extracts check metadata (inputs, outputs, versions)
- Uploads JSON to Firebase Storage
- Updates Firestore
system/configwith storage path - builder-api reads this metadata on startup
Environment Configuration:
- Default:
http://localhost:8083(development mode - no config needed) - Production: Set
LIBRARY_API_BASE_URLto production Cloud Run URL - Environment is inferred from URL pattern (localhost = dev, else = prod)
Troubleshooting:
- "Firebase Storage emulator not responding": Start emulators first (
firebase emulators:start) - "library-api not responding": Start library-api (
cd library-api && quarkus dev) - Stale metadata in builder-api: Restart builder-api (reads metadata on startup)
The project uses Firebase emulators for local development:
# Start emulators (automatically imports data from ./emulator-data)
firebase emulators:start --project demo-bdt-dev --only auth,firestore,storage
# Export current state
firebase emulators:export ./emulator-data
# Access UIs:
# - Auth UI: http://localhost:4000/auth
# - Firestore UI: http://localhost:4000/firestore
# - Storage UI: http://localhost:4000/storageThe project uses .env files for configuration:
# Root .env (loaded by devbox)
# See .env.example for template
# Service-specific .env files
builder-api/.env
builder-frontend/.env
# Setup script copies .env.example → .env
bin/setupbuilder-api:
- Port: Configured via
QUARKUS_HTTP_PORTenv var - Debug port: 5005
- Main classes:
ScreenerResource,DecisionResource,KieDmnService - Tests:
mvn test(some tests may be skipped in CI)
builder-frontend:
- Dev server:
npm run dev - Key components:
ProjectEditor,KogitoDmnEditorView,FormEditor - API client:
src/api/(usesauthFetchwrapper)
Add a new benefit to library-api:
- Create DMN file in
library-api/src/main/resources/benefits/ - Import BDT.dmn for shared types and utilities
- Define Decision Service in DMN
- Run
quarkus dev- endpoint auto-generated - Test with Bruno or Swagger UI
Create a custom screener:
- Start all services:
devbox services up - Open builder-frontend (typically http://localhost:5173)
- Create project → Edit form → Add/configure benefits → Preview → Publish
- Access published screener via Public URL
Debug DMN evaluation issues:
- Check DMN syntax in VS Code with DMN extension
- Review Swagger UI for expected input/output schemas
- Use Quarkus dev mode logs (shows DMN evaluation details)
- Test individual decisions via Swagger UI before integrating
Modify an existing DMN in library-api:
- Edit DMN file (XML or via VS Code extension)
- Save file - Quarkus dev mode auto-reloads
- Re-run Bruno tests to verify changes
- No manual compilation needed (Kogito handles it)
library-api uses a semantic versioning workflow that keeps git tags, pom.xml version, Docker image tags, and Cloud Run revision names synchronized.
Deployment Workflow:
# 1. Create a release (updates pom.xml + creates git tag atomically)
cd library-api
./bin/tag-release 0.2.0
# 2. Review the changes
git show
# 3. Push the commit and tag
git push origin <branch-name>
git push origin library-api-v0.2.0When you push a tag matching library-api-v*, GitHub Actions automatically:
- Extracts version from pom.xml using Maven
- Validates the git tag matches the pom.xml version
- Builds the Quarkus application
- Builds and pushes Docker images with both
:v0.2.0and:latesttags - Deploys to Cloud Run with revision name
library-api-v0-2-0
Version Management:
- Source of truth:
pom.xmlversion field - Git tags:
library-api-v{version}(e.g.,library-api-v0.2.0) - Docker tags:
:v{version}and:latest(e.g.,:v0.2.0,:latest) - Cloud Run revisions:
library-api-v{version-with-dashes}(e.g.,library-api-v0-2-0)
Helper Scripts:
library-api/bin/tag-release <version>: Creates release atomically (updates pom.xml, commits, and tags)bin/validate-library-api-version: Validates that git tag exists for current pom.xml version
Validation: The deployment has three layers of validation to ensure version sync:
- Primary:
tag-releasescript updates pom.xml and creates matching git tag atomically - Secondary: Git pre-push hook validates version before allowing tag push (optional, see setup below)
- Tertiary: GitHub Actions workflow validates and fails if versions don't match
To prevent accidental version mismatches locally, you can install a pre-push hook:
# Create the pre-push hook
cat > .git/hooks/pre-push << 'EOF'
#!/usr/bin/env bash
# Validate library-api version before pushing tags
bin/validate-library-api-version --pre-push-hook
exit $?
EOF
# Make it executable
chmod +x .git/hooks/pre-pushThis hook will:
- Intercept pushes of
library-api-v*tags - Validate that the tag version matches pom.xml version
- Block the push if versions don't match
- Provide helpful error messages with fix instructions
Note: Pre-push hooks are not committed to the repository. Each developer must set up their own hook.
Google Cloud Project: benefit-decision-toolkit-play
- Region:
us-central1 - Service Account:
library-api-service-account@benefit-decision-toolkit-play.iam.gserviceaccount.com - Container Registry:
us-central1-docker.pkg.dev/benefit-decision-toolkit-play/benefit-decision-toolkit-play/library-api - Max Instances: 2
- Authentication: Unauthenticated (public API)
Dockerfile: library-api/src/main/docker/Dockerfile.jvm
- Uses Kogito builder and runtime images (version 1.44.1)
- Multi-stage build with optimized layers
- Java 17 runtime
DMN Import Rules:
- Imported decision services cannot have the same name, even if in different models
- Always namespace-qualify decision references when importing
- Check
library-api/CLAUDE.mdfor detailed DMN import hierarchy
Firebase Dependency:
- builder-api requires Firebase configuration
- Use emulators for local dev (no real Firebase project needed)
- Production requires actual Firebase project setup
Process Compose Dependencies:
- Services start in order: firebase → APIs → frontends
- Check
process-compose.ymlfor dependency chain - APIs wait for "All emulators ready!" log message
Devbox vs. Native:
- Devbox ensures consistent dependency versions
- Native setup requires manual installation (see
devbox.jsonfor versions) - direnv integration recommended for automatic environment loading