Skip to content

Contributing

This project follows the Contributor Covenant. By taking part you agree to uphold it.

  • GitHub Discussions — questions, setup help, and general conversation. Start with the pinned welcome post if you are new.
  • Issues labeled good first issue are well-scoped and explained in detail.
  • Issues labeled help wanted are 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.

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:

Terminal window
cd frontend && npm install && npm run dev # dashboard with hot reload

Run the Java verification locally before opening a PR:

Terminal window
./mvnw spotless:apply # google-java-format; CI runs spotless:check
./mvnw verify # tests + JaCoCo coverage gate + SpotBugs

If 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 in config/spotbugs-exclude.xml with a justification comment.
  • No new Sonar issues — the quality gate requires an A reliability/security rating on new code.

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).

GateWhat it catchesRequired on this repo
StaticDependency CVEs/license regressions (Dependency Review), filesystem CVEs (Trivy), format/tests/frontend, SpotBugs/Sonar/CodeQLYes — format, test, frontend, trivy, and dependency-review are required status checks on main/develop
ThrillhouseBotDiff narrative, incomplete fixes, framework-context issues static tools missSoft 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.

Maintainers triage; do not "average" the signals away.

StaticThrillhouseBotWhat to do
RedGreen / quietFix 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).
GreenRed / open threadsAddress or refute the bot findings in the PR discussion before merge. Static green does not clear logic/copy/incomplete-fix issues.
RedRedClear the static gate first (often blocks merge already), then resolve bot threads.
Same area, conflicting severityPrefer 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.

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.

See the architecture overview (source: docs/ARCHITECTURE.md). Package flow: webhook/review/ (review/ai/) → github/dashboard/ (frontend/).

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.

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:

Terminal window
QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=<key> ./mvnw test -Peval -Dtest=PromptEvalTest

Point 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.

Please do not open a public issue — see SECURITY.md.