Quickstart: running the acceptance-jvm scenarios locally
> The runner is nightly-only in CI (no per-PR JDK cost). This is how a maintainer runs it by > hand. It is a black box: it builds the wheel, installs it into a throwaway venv, and drives the > spec-kitty CLI as an external process — it never imports Python.
Prerequisites
- JDK 25 on
PATH(the version the nightly uses; mirrors thecontracts/toolchain bumped to 25). - Python +
uv(to build the wheel under test). - The pinned Gradle — installed the same way CI does, via the shared installer:
``bash python contracts/tools/install_tools.py \ --pins acceptance-jvm/tools/pins.json \ --dest "$PWD/.tooltmp" --only gradle export PATH="$PWD/.tooltmp/gradle/bin:$PATH" # install_tools appends this in CI via $GITHUB_PATH ``
The installer refuses to unpack Gradle unless the downloaded bytes match the pinned sha256.
Run the scenarios
cd acceptance-jvm
# Gating set (what the nightly gates on): everything that is not @wip and not @manual
gradle test --no-daemon -Dcucumber.filter.tags="not @wip and not @manual"
# The @wip set (the nightly's separate, non-gating run)
gradle test --no-daemon -Dcucumber.filter.tags="@wip"
The build builds the wheel (uv build) and installs it into a temp venv itself; you do not install spec-kitty globally. Each scenario gets its own temp git repo and a fully redirected environment, so a run never touches your real ~/.spec-kitty or $HOME.
Results
After a run, results are under acceptance-jvm/build/:
- Cucumber JSON + JUnit XML (consumed by the nightly upload),
- Allure results (
allure-cucumber7-jvm), uploaded for the spec-kitty-qa acceptance nightly.
Regenerating the dependency verification metadata
When JVM dependencies change, regenerate (never hand-edit) the pinned metadata:
cd acceptance-jvm
gradle --write-verification-metadata sha256 test --no-daemon
Commit the regenerated acceptance-jvm/gradle/verification-metadata.xml. The shared contracts/tools/gradle_pin_check.py (generalized by this mission) cross-checks that the Gradle version used by the build, the metadata, and the nightly workflow all match acceptance-jvm/tools/pins.json — a mismatch fails GRADLE_PIN_MISMATCH.
Adding a scenario
1. Add a .feature under tests/scenarios/<area>/. 2. Bind any new steps through the SpecKittyDriver port (never import Python; never call internal Java). 3. Leave it bound + untagged to run in the gating nightly, or tag @manual for a heavy/on-demand scenario (excluded from the gating run), or @wip while its steps are incomplete. 4. The new tests/scenarios/** path must be git-tracked (the ci-router scenarios glob asserts live paths).