Contributing
This project follows the Contributor Covenant. By taking part you agree to uphold it.
Where to start
Section titled “Where to start”- GitHub Discussions — questions, setup help, and general conversation. Start with the pinned welcome post if you are new.
- Issues labeled
good first issueare well-scoped and explained in detail. - Issues labeled
help wantedare larger but ready to be picked up. - For bugs and feature requests, use the issue templates. For larger changes, open an issue first so we can discuss the approach before you invest time.
Development setup
Section titled “Development setup”Prerequisites: Java 25+, Node.js 22+ (dashboard only), Docker (native builds only). Maven is bundled via the wrapper.
Follow the README Quick Start for cloning, credentials, dev mode, and webhook forwarding — it is the single source of truth for setup commands. Contributor-specific extras:
cd frontend && npm install && npm run dev # dashboard with hot reloadBefore you open a PR
Section titled “Before you open a PR”Run the Java verification locally before opening a PR:
./mvnw spotless:apply # google-java-format; CI runs spotless:check./mvnw verify # tests + JaCoCo coverage gate + SpotBugsIf you changed the dashboard, also run cd frontend && npm ci && npm run test && npm run build.
To exercise the UI without a backend, run cd frontend && npm run dev:mock.
CI additionally runs Dependency Review (required), SonarCloud, Trivy, and a Docker
build check on pull requests, plus the GraalVM native build when the change touches
pom.xml, src/main/** or the CI workflow — see Dual-gate merge policy below.
To build a disposable test image for a branch or PR (JVM or native, without
touching latest), use the Docker test image workflow in Actions
(.github/workflows/docker-test-image.yml).
The bar, enforced by CI:
- All tests pass — and new code comes with tests. Test behavior, not implementation: prefer asserting observable outcomes over
assertDoesNotThrow, and avoid reflection on private members (relax to package-private instead if a test genuinely needs the seam). - Coverage doesn't drop — JaCoCo, Codecov patch coverage, and the SonarCloud quality gate all run on every PR.
- SpotBugs clean at
effort=Max/threshold=Low. If you hit a false positive, prefer restructuring; document any exclusion inconfig/spotbugs-exclude.xmlwith a justification comment. - No new Sonar issues — the quality gate requires an A reliability/security rating on new code.
Dual-gate merge policy
Section titled “Dual-gate merge policy”ThrillhouseBot's LLM review and the repo's static CI gates are complementary, not substitutes. Merge only when both are green (or explicitly waived by a maintainer with a written reason on the PR).
| Gate | What it catches | Required on this repo |
|---|---|---|
| Static | Dependency CVEs/license regressions (Dependency Review), filesystem CVEs (Trivy), format/tests/frontend, SpotBugs/Sonar/CodeQL | Yes — format, test, frontend, trivy, and dependency-review are required status checks on main/develop |
| ThrillhouseBot | Diff narrative, incomplete fixes, framework-context issues static tools miss | Soft gate — wait for the ThrillhouseBot Review check / posted review when the App is installed; do not merge solely because CI is green if the bot flagged open threads |
Optional third signal: Cursor Bugbot (or similar) may run on PRs. It is not a merge requirement here — useful when present, never a replacement for the two gates above.
This policy does not fold linters into the LLM prompt (#34); that work stays separate. The static gate is the CI safety net for supply-chain and compile-time classes of bug the bot can miss.
When the gates disagree
Section titled “When the gates disagree”Maintainers triage; do not "average" the signals away.
| Static | ThrillhouseBot | What to do |
|---|---|---|
| Red | Green / quiet | Fix or formally suppress the static finding. LLM silence is not evidence the CVE is safe. Prefer a real version bump; if the advisory does not apply to this codebase, document the hold with the file the failing tool actually reads — Dependency Review: allow-ghsas or .github/dependency-review-config.yml; Trivy: .trivyignore; OpenSSF Scorecard / osv-scanner: osv-scanner.toml (Jackson GHSA pattern in #308). |
| Green | Red / open threads | Address or refute the bot findings in the PR discussion before merge. Static green does not clear logic/copy/incomplete-fix issues. |
| Red | Red | Clear the static gate first (often blocks merge already), then resolve bot threads. |
| Same area, conflicting severity | — | Prefer the static tool for dependency/CVE/license facts; prefer the bot for intent, call-site context, and "did the PR actually finish the fix?" Prefer neither blindly — leave a short maintainer note on the PR. |
Waivers (rare): only a maintainer may merge with a red soft gate or a documented static suppression, and the PR must record who waived what and why.
Commit messages
Section titled “Commit messages”The project uses Conventional Commits: feat:, fix:, perf:, test:, docs:, ci:, deps:. One logical change per commit; explain the why in the body when it isn't obvious.
Architecture
Section titled “Architecture”See the architecture overview
(source: docs/ARCHITECTURE.md). Package flow:
webhook/ → review/ (review/ai/) → github/ → dashboard/ (frontend/).
Adding an AI provider
Section titled “Adding an AI provider”There is no provider-specific code to write. The model is reached through
LangChain4j's OpenAI-compatible client, so a new provider is configuration: point
AI_BASE_URL and AI_MODEL at the endpoint, and add a matching
thrillhousebot.ai.pricing.<model>.input-per-1k / .output-per-1k pair in
application.properties if you want the dashboard to track its cost. The
README provider table lists the ones that are known
to work.
Prompt eval corpus
Section titled “Prompt eval corpus”Changes to the review or verifier prompts (PrReviewPrompts, FindingVerifierPrompts)
should be checked against the labeled regression corpus in
src/test/resources/evalcorpus/ before they ship. Each case directory pins a real
dogfood outcome — a case.json spec plus diff.txt in the exact format the review
pipeline sends to the model:
- verifier cases feed a candidate finding through the second-pass audit and assert
the expected verdict (
confirmed/downgraded/rejected); - generator cases run the first-pass review prompt over a diff and assert that a
finding matching the case's keywords is (
must-find) or is not (must-not-find) raised on the target file.
The corpus schema is validated by EvalCorpusTest in every build. The live suite is
opt-in — it calls the configured AI provider:
QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=<key> ./mvnw test -Peval -Dtest=PromptEvalTestPoint AI_BASE_URL / AI_MODEL at the provider you want to evaluate (defaults apply
otherwise). Each case is sampled -Deval.samples times (default 3) and judged by
majority; -Deval.tolerated (default 0) allows a known-unfixed label — one tracked by
an open prompt-hardening issue — to fail without blocking unrelated prompt work.
To add a case, create a new directory under evalcorpus/ with case.json and
diff.txt (see an existing case for the shape). Source new cases from resolved PR
review threads: a refuted false positive becomes expectedVerdicts: ["rejected"], a
confirmed true positive ["confirmed"], and note the provenance in why.
Reporting security issues
Section titled “Reporting security issues”Please do not open a public issue — see SECURITY.md.