From 2130f5199574b07f1fc9e07c2fac17fec8b8762c Mon Sep 17 00:00:00 2001 From: Marie Harris Date: Tue, 10 Mar 2026 21:12:01 -0400 Subject: [PATCH 1/4] Add CONTRIBUTING.md for new developer onboarding Covers environment setup, project structure, development workflow, testing, code style, PR process, DMN file conventions, and deployment. Co-Authored-By: Claude Sonnet 4.6 --- CONTRIBUTING.md | 372 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..1c9bd3fe --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,372 @@ +# Contributing to Benefit Decision Toolkit + +Thank you for your interest in contributing! BDT is a volunteer-driven open source project run by [Code for Philly](https://codeforphilly.org/projects/dmn_benefit_toolbox-including_the_philly_property_tax_relief_screener). We welcome contributions of all kinds — code, documentation, bug reports, design feedback, and DMN eligibility rules. + +## Table of Contents + +- [Ways to Contribute](#ways-to-contribute) +- [Project Structure](#project-structure) +- [Development Environment Setup](#development-environment-setup) +- [Running the Project](#running-the-project) +- [Making Changes](#making-changes) +- [Testing](#testing) +- [Code Style](#code-style) +- [Submitting a Pull Request](#submitting-a-pull-request) +- [Working with DMN Files](#working-with-dmn-files) +- [Deployment (Maintainers)](#deployment-maintainers) +- [Getting Help](#getting-help) + +--- + +## Ways to Contribute + +- **Bug reports**: Open a GitHub issue with steps to reproduce +- **Feature requests**: Open a GitHub issue describing the use case +- **Code**: Fix bugs, implement features, improve tests +- **Documentation**: Improve guides, fix typos, add examples +- **DMN rules**: Add or improve eligibility rules in `library-api` +- **Testing PRs**: Review and test open pull requests — see the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md) (no local setup needed!) + +--- + +## Project Structure + +This is a monorepo with four distinct applications: + +``` +benefit-decision-toolkit/ +├── library-api/ # Quarkus + Kogito: DMN files → REST APIs (Java 17) +├── builder-api/ # Quarkus: Admin backend, Firebase integration (Java 21) +├── builder-frontend/ # Solid.js: Admin UI + public screener interface +├── website/ # Public project website +├── docs/ # User and developer documentation +├── e2e/ # Playwright end-to-end tests +├── bin/ # Developer helper scripts +├── .github/ # GitHub Actions workflows +├── devbox.json # Development environment declaration (Nix) +└── process-compose.yml # Multi-service orchestration for local dev +``` + +**Important**: `library-api` uses **Java 17 + Quarkus 2.16.10** (required for Kogito), while `builder-api` uses **Java 21 + Quarkus 3.23.0**. Do not mix them up. + +--- + +## Development Environment Setup + +There are three ways to set up your environment. **Devbox is recommended** for local development. + +### Option 1: GitHub Codespaces (Easiest) + +No local installation required. Click the badge in the [README](README.md) to open a cloud-based development environment. Wait a few minutes for the first-time build to complete. + +This is also a great way to test changes from a pull request — see the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md). + +### Option 2: Devbox (Recommended for Local Development) + +[Devbox](https://www.jetify.com/docs/devbox/) manages all project dependencies (Java 17, Java 21, Maven, Node.js 22, Firebase Tools, Bruno, etc.) using Nix, ensuring a consistent environment across machines. + +```bash +# Install Devbox + Nix and run one-time setup +bin/install-devbox && devbox run setup +``` + +**Tips**: +- Install the [Devbox direnv integration](https://www.jetify.com/docs/devbox/ide-configuration/direnv) to auto-activate the Devbox shell when you `cd` into the project +- VS Code users: install the [Devbox + Direnv extensions](https://www.jetify.com/docs/devbox/ide-configuration/vscode) +- Customize your shell by editing `.devboxrc` (not committed to git) + +### Option 3: Devcontainer (VS Code) + +Open the project in VS Code, install the `Dev Containers` extension, and run **"Dev Containers: Open Folder in Container..."** from the command palette. The container includes the full Devbox environment. Expect a longer first-time build. + +### Option 4: DIY (Not Recommended) + +Manually install the dependencies listed in `devbox.json` (JDK 17, JDK 21, Maven, Node.js 22, Firebase Tools 14.27.0, Bruno, process-compose), then run: + +```bash +bin/setup +``` + +--- + +## Running the Project + +### Start All Services + +```bash +# Starts all 5 services in the correct dependency order +devbox services up +``` + +This orchestrates (in order): +1. **Firebase Emulators** — Auth (9099), Firestore (8080), Storage (9199), UI (4000) +2. **library-api** — REST API from DMN files at http://localhost:8083 +3. **sync-library-metadata** — Syncs library check metadata to Firebase emulators +4. **builder-api** — Admin API (port configured via `QUARKUS_HTTP_PORT`) +5. **builder-frontend** — Vite dev server at http://localhost:5173 + +> **Do not start services out of order.** `builder-api` depends on Firebase emulators being fully ready, and the metadata sync step must complete before `builder-api` starts. + +### Running Individual Services + +If you're only working on one part of the stack: + +```bash +# library-api only (no Firebase dependency) +cd library-api && quarkus dev +# Swagger UI: http://localhost:8083/q/swagger-ui + +# Firebase emulators (required before builder-api) +firebase emulators:start --project demo-bdt-dev --only auth,firestore,storage + +# builder-api (after emulators are running) +cd builder-api && quarkus dev + +# builder-frontend (after builder-api is running) +cd builder-frontend && npm run dev +``` + +### Service URLs + +| Service | URL | +|---|---| +| builder-frontend | http://localhost:5173 | +| library-api | http://localhost:8083 | +| library-api Swagger UI | http://localhost:8083/q/swagger-ui | +| Firebase Emulator UI | http://localhost:4000 | + +--- + +## Making Changes + +### Branching + +Create a feature branch from `main`: + +```bash +git checkout main +git pull +git checkout -b your-feature-name +``` + +Use descriptive branch names, e.g. `fix-dmn-evaluation-edge-case` or `add-snap-eligibility-check`. + +### Commits + +Write clear, descriptive commit messages. Focus on *what* changed and *why*: + +``` +# Good +fix: handle null income value in eligibility check + +# Good +feat: add SNAP benefit eligibility check for Philadelphia + +# Avoid +fix stuff +update +``` + +### Syncing Library Metadata After library-api Changes + +If you modify DMN files in `library-api`, re-sync metadata so `builder-api` picks up the changes: + +```bash +# Re-sync metadata +./bin/library/sync-metadata + +# Then restart builder-api (in the process-compose UI, restart the process) +``` + +--- + +## Testing + +### Java Unit Tests + +```bash +# builder-api +cd builder-api && mvn test + +# library-api +cd library-api && mvn test +``` + +These run automatically in CI on every push and pull request that touches `builder-api/` or `library-api/`. + +### Bruno API Tests (library-api) + +[Bruno](https://www.usebruno.com/) is used to test the DMN-generated REST endpoints in `library-api`. The test suite lives in `library-api/test/bdt/` and mirrors the DMN file structure. + +```bash +# Run all Bruno tests +cd library-api/test/bdt && bru run +``` + +**When adding a new benefit or check to `library-api`, add Bruno tests first** (test-driven development). Bruno tests validate DMN logic, while Java tests validate internal application behavior. + +### End-to-End Tests (Playwright) + +```bash +# Run e2e tests (requires all services to be running) +cd e2e && npx playwright test +``` + +E2E tests run automatically in CI. They start all services via `devbox services up` and run against them. + +### Before Submitting a PR + +- [ ] Run Java unit tests for any service you modified +- [ ] Run Bruno tests if you modified `library-api` DMN files +- [ ] Verify your changes work end-to-end with all services running +- [ ] Run Prettier on changed frontend files (see [Code Style](#code-style)) + +--- + +## Code Style + +### JavaScript / TypeScript (builder-frontend) + +The project uses [Prettier](https://prettier.io/) for formatting. Configuration is in `.prettierrc`: + +```json +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "tabWidth": 2 +} +``` + +Format changed files before committing: + +```bash +# Format a specific file +npx prettier --write path/to/file.tsx + +# Format all frontend files +cd builder-frontend && npx prettier --write "src/**/*.{ts,tsx,js,jsx}" +``` + +### Java (builder-api, library-api) + +Follow standard Java conventions. There is no automated style enforcement beyond what Maven provides, so match the style of the surrounding code. + +### DMN Files + +- File names: `kebab-case.dmn` +- Decision Service names: `{ModelName}Service` (e.g., a file named `snap-benefit.dmn` → Decision Service named `SnapBenefitService`) +- Model names must be **globally unique** across the entire `library-api` — check existing names before adding a new DMN file +- Imported decision services cannot share names even if in different models + +--- + +## Submitting a Pull Request + +1. Push your branch and open a PR against `main` +2. Write a clear PR description: what changed, why, and how to test it +3. All PRs require review from at least one maintainer (@Michael-Dratch or @prestoncabe — see [CODEOWNERS](.github/CODEOWNERS)) +4. CI runs API unit tests and E2E tests automatically — check that they pass +5. For changes to `library-api` DMN files, include Bruno test results or a description of manual testing + +**For non-technical contributors** testing a PR: follow the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md) — no local setup needed. + +--- + +## Working with DMN Files + +DMN (Decision Model and Notation) is the core of this project. If you're new to DMN, start here: +- [Learn DMN in 15 minutes](https://learn-dmn-in-15-minutes.com/) +- [DMN Editor for VS Code](https://marketplace.visualstudio.com/items?itemName=kie-group.dmn-vscode-extension) (strongly recommended) + +### Adding a Benefit to library-api + +Benefits represent specific programs (e.g., SNAP, property tax relief). They define eligibility logic using DMN and automatically become REST endpoints. + +1. **Write Bruno tests first** in `library-api/test/bdt/benefits/{jurisdiction}/{benefit-name}/` +2. Create a DMN file: `library-api/src/main/resources/benefits/{jurisdiction}/{benefit-name}.dmn` +3. Define a Decision Service named `{BenefitName}Service` +4. Include a `checks` context and an `isEligible` boolean decision +5. Save — Quarkus dev mode hot-reloads and the endpoint appears at `POST /api/v1/benefits/{jurisdiction}/{benefit-name}` +6. Verify in Swagger UI: http://localhost:8083/q/swagger-ui +7. Run your Bruno tests: `cd library-api/test/bdt && bru run` + +### Adding a Reusable Check + +Checks are reusable decision logic that can be shared across multiple benefits. + +1. Create a DMN file: `library-api/src/main/resources/checks/{category}/{check-name}.dmn` +2. Define a Decision Service named `{CheckName}Service` +3. Standard inputs: `situation` (tSituation type), `parameters` (check-specific context) +4. Standard output: `result` (boolean) +5. Endpoint: `POST /api/v1/checks/{category}/{check-name}` + +### Key DMN Constraints + +- **Decision Service name must be `{ModelName}Service`** — this is required for automatic endpoint generation +- **Model names are globally unique** — if two DMN files have the same model name, things break +- Imported decision services cannot share names even across different files +- Import `BDT.dmn` for shared types like `tSituation` + +### Editing DMN Files + +- Use the VS Code DMN Editor extension for visual editing +- To see raw XML: right-click the file → "Reopen with Text Editor" +- Changes are hot-reloaded in `quarkus dev` mode — no restart needed + +--- + +## Deployment (Maintainers) + +### library-api + +Deployments are triggered by pushing a git tag matching `library-api-v*`. Use the provided script to create a release atomically: + +```bash +cd library-api + +# Update pom.xml version and create git tag in one step +./bin/tag-release 0.3.0 + +# Review the changes +git show + +# Push the commit and the tag (triggers GitHub Actions deployment) +git push origin +git push origin library-api-v0.3.0 +``` + +The GitHub Actions workflow validates that the tag version matches `pom.xml`, builds the Docker image, pushes to Google Artifact Registry, deploys to Cloud Run, and syncs library metadata automatically. + +**Version source of truth**: `pom.xml`. The git tag, Docker image tag, and Cloud Run revision name all derive from it. + +### builder-api + +Deploys automatically to Cloud Run on every push to `main` that touches `builder-api/` files. No manual versioning needed. + +### builder-frontend and docs + +Deploy automatically to Firebase Hosting on push to `main`. + +### Production Library Metadata Sync (Manual) + +If you need to sync library metadata to production outside of a `library-api` deployment: + +```bash +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 +gcloud auth application-default login +./bin/library/sync-metadata +``` + +--- + +## Getting Help + +- **GitHub Issues**: For bugs and feature requests +- **Code for Philly**: Join [codeforphilly.org](https://codeforphilly.org) to connect with the team +- **Project maintainers**: @Michael-Dratch and @prestoncabe + +If you're stuck on setup, open an issue — improving the onboarding experience is itself a valuable contribution. From ed6efafc8ee90d2054168006bb4555d5c3f5cb5c Mon Sep 17 00:00:00 2001 From: Marie Harris Date: Tue, 10 Mar 2026 21:38:41 -0400 Subject: [PATCH 2/4] fix: correct Java version and builder-api port details in CONTRIBUTING.md - devbox.json only provides JDK 21; library-api targets Java 17 via Maven --release 17, not a separate JDK install - Add builder-api (port 8081) to Service URLs table - Clarify DIY setup requires JDK 21 only, not both 17 and 21 --- CONTRIBUTING.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c9bd3fe..24dc055d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,7 +47,7 @@ benefit-decision-toolkit/ └── process-compose.yml # Multi-service orchestration for local dev ``` -**Important**: `library-api` uses **Java 17 + Quarkus 2.16.10** (required for Kogito), while `builder-api` uses **Java 21 + Quarkus 3.23.0**. Do not mix them up. +**Important**: `library-api` targets **Java 17 bytecode + Quarkus 2.16.10** (required for Kogito), while `builder-api` uses **Java 21 + Quarkus 3.23.0**. Devbox provides JDK 21 for both — Maven compiles `library-api` with `--release 17` automatically. --- @@ -63,7 +63,7 @@ This is also a great way to test changes from a pull request — see the [Codesp ### Option 2: Devbox (Recommended for Local Development) -[Devbox](https://www.jetify.com/docs/devbox/) manages all project dependencies (Java 17, Java 21, Maven, Node.js 22, Firebase Tools, Bruno, etc.) using Nix, ensuring a consistent environment across machines. +[Devbox](https://www.jetify.com/docs/devbox/) manages all project dependencies (JDK 21, Maven, Node.js 22, Firebase Tools, Bruno, etc.) using Nix, ensuring a consistent environment across machines. A single JDK 21 covers both services — Maven handles the Java 17 target for `library-api` automatically via `--release 17`. ```bash # Install Devbox + Nix and run one-time setup @@ -81,7 +81,7 @@ Open the project in VS Code, install the `Dev Containers` extension, and run **" ### Option 4: DIY (Not Recommended) -Manually install the dependencies listed in `devbox.json` (JDK 17, JDK 21, Maven, Node.js 22, Firebase Tools 14.27.0, Bruno, process-compose), then run: +Manually install the dependencies listed in `devbox.json` (JDK 21, Maven, Node.js 22, Firebase Tools 14.27.0, Bruno, process-compose), then run: ```bash bin/setup @@ -131,6 +131,7 @@ cd builder-frontend && npm run dev | Service | URL | |---|---| | builder-frontend | http://localhost:5173 | +| builder-api | http://localhost:8081 | | library-api | http://localhost:8083 | | library-api Swagger UI | http://localhost:8083/q/swagger-ui | | Firebase Emulator UI | http://localhost:4000 | From 43b00f91095553846493796131d1009dbbe2f28f Mon Sep 17 00:00:00 2001 From: Marie Harris Date: Tue, 10 Mar 2026 21:43:56 -0400 Subject: [PATCH 3/4] refactor: replace duplicate setup section with pointer to README.md Development environment setup is already covered in detail in README.md. CONTRIBUTING.md now links there instead of repeating the same content. --- CONTRIBUTING.md | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24dc055d..20ce876f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,39 +53,7 @@ benefit-decision-toolkit/ ## Development Environment Setup -There are three ways to set up your environment. **Devbox is recommended** for local development. - -### Option 1: GitHub Codespaces (Easiest) - -No local installation required. Click the badge in the [README](README.md) to open a cloud-based development environment. Wait a few minutes for the first-time build to complete. - -This is also a great way to test changes from a pull request — see the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md). - -### Option 2: Devbox (Recommended for Local Development) - -[Devbox](https://www.jetify.com/docs/devbox/) manages all project dependencies (JDK 21, Maven, Node.js 22, Firebase Tools, Bruno, etc.) using Nix, ensuring a consistent environment across machines. A single JDK 21 covers both services — Maven handles the Java 17 target for `library-api` automatically via `--release 17`. - -```bash -# Install Devbox + Nix and run one-time setup -bin/install-devbox && devbox run setup -``` - -**Tips**: -- Install the [Devbox direnv integration](https://www.jetify.com/docs/devbox/ide-configuration/direnv) to auto-activate the Devbox shell when you `cd` into the project -- VS Code users: install the [Devbox + Direnv extensions](https://www.jetify.com/docs/devbox/ide-configuration/vscode) -- Customize your shell by editing `.devboxrc` (not committed to git) - -### Option 3: Devcontainer (VS Code) - -Open the project in VS Code, install the `Dev Containers` extension, and run **"Dev Containers: Open Folder in Container..."** from the command palette. The container includes the full Devbox environment. Expect a longer first-time build. - -### Option 4: DIY (Not Recommended) - -Manually install the dependencies listed in `devbox.json` (JDK 21, Maven, Node.js 22, Firebase Tools 14.27.0, Bruno, process-compose), then run: - -```bash -bin/setup -``` +See the [README](README.md#development-setup) for full setup instructions covering Codespaces, Devbox, Devcontainer, and DIY options. --- From a2ebbadded5d5584f5fb10fb18adcc088e250d69 Mon Sep 17 00:00:00 2001 From: Marie Harris Date: Thu, 12 Mar 2026 08:24:34 -0400 Subject: [PATCH 4/4] docs: address PR #352 review feedback - Mention .vscode/java.format.xml in the Java style section - Update DMN section intro wording per reviewer suggestion - Add tip about /new-dmn-check CC command for new check boilerplate - Replace manual gcloud sync steps with GH Action trigger recommendation Co-Authored-By: Claude Sonnet 4.6 --- CONTRIBUTING.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 20ce876f..aa14705a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -219,7 +219,7 @@ cd builder-frontend && npx prettier --write "src/**/*.{ts,tsx,js,jsx}" ### Java (builder-api, library-api) -Follow standard Java conventions. There is no automated style enforcement beyond what Maven provides, so match the style of the surrounding code. +Follow standard Java conventions. The project includes a VS Code Java formatter config at `.vscode/java.format.xml` — if you use VS Code with the Java extension, this will be picked up automatically. Otherwise, match the style of the surrounding code. ### DMN Files @@ -244,7 +244,7 @@ Follow standard Java conventions. There is no automated style enforcement beyond ## Working with DMN Files -DMN (Decision Model and Notation) is the core of this project. If you're new to DMN, start here: +DMN (Decision Model and Notation) is the core technology used for eligibility logic in this project. If you're new to DMN, start here: - [Learn DMN in 15 minutes](https://learn-dmn-in-15-minutes.com/) - [DMN Editor for VS Code](https://marketplace.visualstudio.com/items?itemName=kie-group.dmn-vscode-extension) (strongly recommended) @@ -264,6 +264,8 @@ Benefits represent specific programs (e.g., SNAP, property tax relief). They def Checks are reusable decision logic that can be shared across multiple benefits. +> **Tip**: Use the `/new-dmn-check` Claude Code (CC) command to generate the boilerplate DMN file and Bruno test stubs automatically. + 1. Create a DMN file: `library-api/src/main/resources/checks/{category}/{check-name}.dmn` 2. Define a Decision Service named `{CheckName}Service` 3. Standard inputs: `situation` (tSituation type), `parameters` (check-specific context) @@ -319,16 +321,12 @@ Deploy automatically to Firebase Hosting on push to `main`. ### Production Library Metadata Sync (Manual) -If you need to sync library metadata to production outside of a `library-api` deployment: +If you need to sync library metadata to production outside of a `library-api` deployment, trigger the **"Load Library API Metadata"** GitHub Actions workflow manually: -```bash -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 -gcloud auth application-default login -./bin/library/sync-metadata -``` +1. Go to [Actions → Load Library API Metadata](../../actions/workflows/load-library-metadata.yml) +2. Click **Run workflow** → **Run workflow** + +This is the recommended approach — it handles GCP authentication, runs the sync script against the production library-api, and automatically restarts `builder-api` to pick up the new metadata. ---