Skip to content

Commit 2130f51

Browse files
morissetteclaude
andcommitted
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 <noreply@anthropic.com>
1 parent abcc789 commit 2130f51

1 file changed

Lines changed: 372 additions & 0 deletions

File tree

CONTRIBUTING.md

Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
# Contributing to Benefit Decision Toolkit
2+
3+
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.
4+
5+
## Table of Contents
6+
7+
- [Ways to Contribute](#ways-to-contribute)
8+
- [Project Structure](#project-structure)
9+
- [Development Environment Setup](#development-environment-setup)
10+
- [Running the Project](#running-the-project)
11+
- [Making Changes](#making-changes)
12+
- [Testing](#testing)
13+
- [Code Style](#code-style)
14+
- [Submitting a Pull Request](#submitting-a-pull-request)
15+
- [Working with DMN Files](#working-with-dmn-files)
16+
- [Deployment (Maintainers)](#deployment-maintainers)
17+
- [Getting Help](#getting-help)
18+
19+
---
20+
21+
## Ways to Contribute
22+
23+
- **Bug reports**: Open a GitHub issue with steps to reproduce
24+
- **Feature requests**: Open a GitHub issue describing the use case
25+
- **Code**: Fix bugs, implement features, improve tests
26+
- **Documentation**: Improve guides, fix typos, add examples
27+
- **DMN rules**: Add or improve eligibility rules in `library-api`
28+
- **Testing PRs**: Review and test open pull requests — see the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md) (no local setup needed!)
29+
30+
---
31+
32+
## Project Structure
33+
34+
This is a monorepo with four distinct applications:
35+
36+
```
37+
benefit-decision-toolkit/
38+
├── library-api/ # Quarkus + Kogito: DMN files → REST APIs (Java 17)
39+
├── builder-api/ # Quarkus: Admin backend, Firebase integration (Java 21)
40+
├── builder-frontend/ # Solid.js: Admin UI + public screener interface
41+
├── website/ # Public project website
42+
├── docs/ # User and developer documentation
43+
├── e2e/ # Playwright end-to-end tests
44+
├── bin/ # Developer helper scripts
45+
├── .github/ # GitHub Actions workflows
46+
├── devbox.json # Development environment declaration (Nix)
47+
└── process-compose.yml # Multi-service orchestration for local dev
48+
```
49+
50+
**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.
51+
52+
---
53+
54+
## Development Environment Setup
55+
56+
There are three ways to set up your environment. **Devbox is recommended** for local development.
57+
58+
### Option 1: GitHub Codespaces (Easiest)
59+
60+
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.
61+
62+
This is also a great way to test changes from a pull request — see the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md).
63+
64+
### Option 2: Devbox (Recommended for Local Development)
65+
66+
[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.
67+
68+
```bash
69+
# Install Devbox + Nix and run one-time setup
70+
bin/install-devbox && devbox run setup
71+
```
72+
73+
**Tips**:
74+
- 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
75+
- VS Code users: install the [Devbox + Direnv extensions](https://www.jetify.com/docs/devbox/ide-configuration/vscode)
76+
- Customize your shell by editing `.devboxrc` (not committed to git)
77+
78+
### Option 3: Devcontainer (VS Code)
79+
80+
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.
81+
82+
### Option 4: DIY (Not Recommended)
83+
84+
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:
85+
86+
```bash
87+
bin/setup
88+
```
89+
90+
---
91+
92+
## Running the Project
93+
94+
### Start All Services
95+
96+
```bash
97+
# Starts all 5 services in the correct dependency order
98+
devbox services up
99+
```
100+
101+
This orchestrates (in order):
102+
1. **Firebase Emulators** — Auth (9099), Firestore (8080), Storage (9199), UI (4000)
103+
2. **library-api** — REST API from DMN files at http://localhost:8083
104+
3. **sync-library-metadata** — Syncs library check metadata to Firebase emulators
105+
4. **builder-api** — Admin API (port configured via `QUARKUS_HTTP_PORT`)
106+
5. **builder-frontend** — Vite dev server at http://localhost:5173
107+
108+
> **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.
109+
110+
### Running Individual Services
111+
112+
If you're only working on one part of the stack:
113+
114+
```bash
115+
# library-api only (no Firebase dependency)
116+
cd library-api && quarkus dev
117+
# Swagger UI: http://localhost:8083/q/swagger-ui
118+
119+
# Firebase emulators (required before builder-api)
120+
firebase emulators:start --project demo-bdt-dev --only auth,firestore,storage
121+
122+
# builder-api (after emulators are running)
123+
cd builder-api && quarkus dev
124+
125+
# builder-frontend (after builder-api is running)
126+
cd builder-frontend && npm run dev
127+
```
128+
129+
### Service URLs
130+
131+
| Service | URL |
132+
|---|---|
133+
| builder-frontend | http://localhost:5173 |
134+
| library-api | http://localhost:8083 |
135+
| library-api Swagger UI | http://localhost:8083/q/swagger-ui |
136+
| Firebase Emulator UI | http://localhost:4000 |
137+
138+
---
139+
140+
## Making Changes
141+
142+
### Branching
143+
144+
Create a feature branch from `main`:
145+
146+
```bash
147+
git checkout main
148+
git pull
149+
git checkout -b your-feature-name
150+
```
151+
152+
Use descriptive branch names, e.g. `fix-dmn-evaluation-edge-case` or `add-snap-eligibility-check`.
153+
154+
### Commits
155+
156+
Write clear, descriptive commit messages. Focus on *what* changed and *why*:
157+
158+
```
159+
# Good
160+
fix: handle null income value in eligibility check
161+
162+
# Good
163+
feat: add SNAP benefit eligibility check for Philadelphia
164+
165+
# Avoid
166+
fix stuff
167+
update
168+
```
169+
170+
### Syncing Library Metadata After library-api Changes
171+
172+
If you modify DMN files in `library-api`, re-sync metadata so `builder-api` picks up the changes:
173+
174+
```bash
175+
# Re-sync metadata
176+
./bin/library/sync-metadata
177+
178+
# Then restart builder-api (in the process-compose UI, restart the process)
179+
```
180+
181+
---
182+
183+
## Testing
184+
185+
### Java Unit Tests
186+
187+
```bash
188+
# builder-api
189+
cd builder-api && mvn test
190+
191+
# library-api
192+
cd library-api && mvn test
193+
```
194+
195+
These run automatically in CI on every push and pull request that touches `builder-api/` or `library-api/`.
196+
197+
### Bruno API Tests (library-api)
198+
199+
[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.
200+
201+
```bash
202+
# Run all Bruno tests
203+
cd library-api/test/bdt && bru run
204+
```
205+
206+
**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.
207+
208+
### End-to-End Tests (Playwright)
209+
210+
```bash
211+
# Run e2e tests (requires all services to be running)
212+
cd e2e && npx playwright test
213+
```
214+
215+
E2E tests run automatically in CI. They start all services via `devbox services up` and run against them.
216+
217+
### Before Submitting a PR
218+
219+
- [ ] Run Java unit tests for any service you modified
220+
- [ ] Run Bruno tests if you modified `library-api` DMN files
221+
- [ ] Verify your changes work end-to-end with all services running
222+
- [ ] Run Prettier on changed frontend files (see [Code Style](#code-style))
223+
224+
---
225+
226+
## Code Style
227+
228+
### JavaScript / TypeScript (builder-frontend)
229+
230+
The project uses [Prettier](https://prettier.io/) for formatting. Configuration is in `.prettierrc`:
231+
232+
```json
233+
{
234+
"semi": true,
235+
"singleQuote": false,
236+
"trailingComma": "all",
237+
"tabWidth": 2
238+
}
239+
```
240+
241+
Format changed files before committing:
242+
243+
```bash
244+
# Format a specific file
245+
npx prettier --write path/to/file.tsx
246+
247+
# Format all frontend files
248+
cd builder-frontend && npx prettier --write "src/**/*.{ts,tsx,js,jsx}"
249+
```
250+
251+
### Java (builder-api, library-api)
252+
253+
Follow standard Java conventions. There is no automated style enforcement beyond what Maven provides, so match the style of the surrounding code.
254+
255+
### DMN Files
256+
257+
- File names: `kebab-case.dmn`
258+
- Decision Service names: `{ModelName}Service` (e.g., a file named `snap-benefit.dmn` → Decision Service named `SnapBenefitService`)
259+
- Model names must be **globally unique** across the entire `library-api` — check existing names before adding a new DMN file
260+
- Imported decision services cannot share names even if in different models
261+
262+
---
263+
264+
## Submitting a Pull Request
265+
266+
1. Push your branch and open a PR against `main`
267+
2. Write a clear PR description: what changed, why, and how to test it
268+
3. All PRs require review from at least one maintainer (@Michael-Dratch or @prestoncabe — see [CODEOWNERS](.github/CODEOWNERS))
269+
4. CI runs API unit tests and E2E tests automatically — check that they pass
270+
5. For changes to `library-api` DMN files, include Bruno test results or a description of manual testing
271+
272+
**For non-technical contributors** testing a PR: follow the [Codespaces Testing Guide](docs/testing-prs-with-codespaces.md) — no local setup needed.
273+
274+
---
275+
276+
## Working with DMN Files
277+
278+
DMN (Decision Model and Notation) is the core of this project. If you're new to DMN, start here:
279+
- [Learn DMN in 15 minutes](https://learn-dmn-in-15-minutes.com/)
280+
- [DMN Editor for VS Code](https://marketplace.visualstudio.com/items?itemName=kie-group.dmn-vscode-extension) (strongly recommended)
281+
282+
### Adding a Benefit to library-api
283+
284+
Benefits represent specific programs (e.g., SNAP, property tax relief). They define eligibility logic using DMN and automatically become REST endpoints.
285+
286+
1. **Write Bruno tests first** in `library-api/test/bdt/benefits/{jurisdiction}/{benefit-name}/`
287+
2. Create a DMN file: `library-api/src/main/resources/benefits/{jurisdiction}/{benefit-name}.dmn`
288+
3. Define a Decision Service named `{BenefitName}Service`
289+
4. Include a `checks` context and an `isEligible` boolean decision
290+
5. Save — Quarkus dev mode hot-reloads and the endpoint appears at `POST /api/v1/benefits/{jurisdiction}/{benefit-name}`
291+
6. Verify in Swagger UI: http://localhost:8083/q/swagger-ui
292+
7. Run your Bruno tests: `cd library-api/test/bdt && bru run`
293+
294+
### Adding a Reusable Check
295+
296+
Checks are reusable decision logic that can be shared across multiple benefits.
297+
298+
1. Create a DMN file: `library-api/src/main/resources/checks/{category}/{check-name}.dmn`
299+
2. Define a Decision Service named `{CheckName}Service`
300+
3. Standard inputs: `situation` (tSituation type), `parameters` (check-specific context)
301+
4. Standard output: `result` (boolean)
302+
5. Endpoint: `POST /api/v1/checks/{category}/{check-name}`
303+
304+
### Key DMN Constraints
305+
306+
- **Decision Service name must be `{ModelName}Service`** — this is required for automatic endpoint generation
307+
- **Model names are globally unique** — if two DMN files have the same model name, things break
308+
- Imported decision services cannot share names even across different files
309+
- Import `BDT.dmn` for shared types like `tSituation`
310+
311+
### Editing DMN Files
312+
313+
- Use the VS Code DMN Editor extension for visual editing
314+
- To see raw XML: right-click the file → "Reopen with Text Editor"
315+
- Changes are hot-reloaded in `quarkus dev` mode — no restart needed
316+
317+
---
318+
319+
## Deployment (Maintainers)
320+
321+
### library-api
322+
323+
Deployments are triggered by pushing a git tag matching `library-api-v*`. Use the provided script to create a release atomically:
324+
325+
```bash
326+
cd library-api
327+
328+
# Update pom.xml version and create git tag in one step
329+
./bin/tag-release 0.3.0
330+
331+
# Review the changes
332+
git show
333+
334+
# Push the commit and the tag (triggers GitHub Actions deployment)
335+
git push origin <your-branch>
336+
git push origin library-api-v0.3.0
337+
```
338+
339+
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.
340+
341+
**Version source of truth**: `pom.xml`. The git tag, Docker image tag, and Cloud Run revision name all derive from it.
342+
343+
### builder-api
344+
345+
Deploys automatically to Cloud Run on every push to `main` that touches `builder-api/` files. No manual versioning needed.
346+
347+
### builder-frontend and docs
348+
349+
Deploy automatically to Firebase Hosting on push to `main`.
350+
351+
### Production Library Metadata Sync (Manual)
352+
353+
If you need to sync library metadata to production outside of a `library-api` deployment:
354+
355+
```bash
356+
export LIBRARY_API_BASE_URL=https://library-api-1034049717668.us-central1.run.app
357+
unset FIRESTORE_EMULATOR_HOST
358+
unset GCS_BUCKET_NAME
359+
unset QUARKUS_GOOGLE_CLOUD_STORAGE_HOST_OVERRIDE
360+
gcloud auth application-default login
361+
./bin/library/sync-metadata
362+
```
363+
364+
---
365+
366+
## Getting Help
367+
368+
- **GitHub Issues**: For bugs and feature requests
369+
- **Code for Philly**: Join [codeforphilly.org](https://codeforphilly.org) to connect with the team
370+
- **Project maintainers**: @Michael-Dratch and @prestoncabe
371+
372+
If you're stuck on setup, open an issue — improving the onboarding experience is itself a valuable contribution.

0 commit comments

Comments
 (0)