Development Setup
Before anything else: GitHub auth
Section titled “Before anything else: GitHub auth”gh auth loginThe engine artifact resolves from GitHub Packages, which requires authentication even for
public packages; build.sbt reads the token via gh auth token. If you skip this, the
failure looks like a broken build rather than a missing credential:
unresolved dependency: lv.id.jc#dicechess-engine-scala...That signature always means auth, never a broken build.
Toolchain
Section titled “Toolchain”Tools are pinned in mise.toml: Java temurin-25, native scalafmt, lefthook, betterleaks, actionlint, gh,
jq. Install everything and register the Git hooks with:
mise run setupDocker is needed only by the four Postgres suites. On Rancher Desktop, export
DOCKER_HOST=unix://$HOME/.rd/docker.sock and TESTCONTAINERS_RYUK_DISABLED=true first —
without them the test run hangs at container startup rather than failing.
Commands
Section titled “Commands”| Command | What it does |
|---|---|
mise run compile |
sbt compile Test/compile |
mise run test |
sbt "testOnly *" (full suite, needs Docker for four suites) |
mise run check |
The CI mirror: shutdown, disposable cache, scalafmtCheckAll, clean, coverage, testOnly *, coverageReport |
mise run format |
sbt scalafmtAll |
mise run run |
Start the server on :8080 |
mise run contrib-docs:dev |
This site, locally |
mise run contrib-docs:schema |
Regenerate the schema reference from the migrations |
mise run docs:dev |
The Bot API site, locally |
For a targeted, Docker-free run: sbt "testOnly dicechess.play.server.*".
Code conventions
Section titled “Code conventions”- Scala 3 “fewer braces” throughout: colon syntax for template bodies and lambdas, no end
markers. Formatting is law — scalafmt decides,
maxColumnis 120. -Werror -Wunused:all -deprecation -feature -explain. One unused import fails the build.- Pure Typelevel FP in cats-effect
IO. No nulls, no exceptions for control flow. - Comments explain why, not what. There are zero
TODO/FIXMEcomments insrc/— keep it that way; encode the decision as a rationale comment instead. - Two-space indent, per
.editorconfig.
Quality gates
Section titled “Quality gates”mise run checkpasses locally. It mirrors CI exactly.- Backend CI is path-filtered to
src/**,build.sbt,project/**,.scalafmt.conf, and its own workflow file. A documentation-only pull request gets zero checks — that is normal, not a stuck pipeline. A pull request touching only other workflow files likewise gets no run; validate those withgh workflow run. - SonarCloud imports the scoverage report. No coverage minimum is enforced — which is not a licence to skip tests.
- Branch naming and
Closes #nlinking are validated automatically; external contributors must sign the CLA.
Traps that produce a false green
Section titled “Traps that produce a false green”Warm-cache formatting. On a warm target/, sbt-scalafmt’s incremental cache can skip a
genuinely misformatted file, so a local check passes while CI — a fresh checkout — fails. Check
after a clean, or confirm with the native scalafmt --test <files>.
Untracked Scala files. sbt scalafmtAll skips them. git add a new .scala file before
running the formatter, or the native pre-commit hook rejects the commit.
Three scalafmt toolchains. .scalafmt.conf, the native CLI pinned in mise.toml (used by
the hooks; it does not auto-dispatch by version), and sbt-scalafmt must be bumped together.
Never pipe test output through grep or head — it masks the exit code.
Things build.sbt does on purpose
Section titled “Things build.sbt does on purpose”- Force-bumps testcontainers-java / docker-java and sets
-Dapi.version=1.43: the wrapper’s pinned docker-java speaks an API version modern daemons reject. ThisBuild/versionis frozen at0.1.0-SNAPSHOT. Real versions come only from git tags via the CD workflow. Do not bump it.- The Dockerfile pins
eclipse-temurin:25-*-noble; the unsuffixed tag drifted to a base image whose coreutils break the launcher. TheJAVA_OPTSflags in Dockerfile and compose are required by cats-effect on Java 25. - Docker builds pass the GitHub token as a BuildKit secret so it never lands in a layer — never convert it to a build argument.
Pull requests
Section titled “Pull requests”Branch as <type>/<short-desc> or <type>/<id>-<short-desc>, where type is one of
task, feat, bug, refactor, chore, docs, ci, test, perf. Run git status
before editing so unrelated work does not bleed into your commit, and stage files by name —
git add -A is forbidden. Commits, pull request descriptions, and review replies are English
only. Split large work into small, reviewable pull requests.
Releases, production promotion, schema migrations against shared databases, data repair, and secret rotation are operator actions: prepare and propose them, never execute them.