Data Model: CLI Acceptance Scenarios & Cucumber-JVM Nightly
This mission has no persistent data store. The "entities" below are the conceptual objects the runner and the scenario corpus are built from — their fields, invariants, and (where relevant) lifecycle — so the implementation and review share one vocabulary.
Entity: Scenario
A Gherkin description of a promised CLI behaviour.
- Location:
tests/scenarios/<area>/<name>.feature(seed area:init). - Fields:
area(directory, e.g.init);name; GherkinFeature/Scenario/steps; optionaltags. - Invariants:
- A scenario drives the CLI only through step bindings that call the
SpecKittyDriverport — never Python imports, never internal Java (C-001). - A bound, untagged scenario runs in the gating nightly; the two seed scenarios are of this kind.
- Relationships: executed by the Runner; may carry one or more Tags.
Entity: Tag
An annotation on a Scenario.
- Allowed:
@promise:<id>,@wip(+@wip-owner:<handle>,@wip-since:<YYYY-MM-DD>),@defect:<issue>,@qa:SC-A-NNN,@manual. - Meaning in core: only
@wipand@manualhave effect — each excludes the scenario from the gating nightly run.@promise/@defect/@qaare inert in core (C-003, FR-014); spec-kitty-qa attaches meaning to them. - Invariant: no core check (per-PR or nightly) reads a promise index or validates
@promiseids — that machinery is out of core scope.
Entity: SpecKittyDriver (port)
The only seam a step is allowed to use.
- Operations: run a CLI command and return
{ exit_code, stdout, stderr, parsed_json (for --json), working_tree }. - Invariant: steps depend on this interface, not on any concrete process mechanics.
Entity: CliProcessDriver (implementation of SpecKittyDriver)
- Fields/behaviour:
ProcessBuilderinvocation; drained stdout/stderr; a timeout; descendant processes killed on exit/timeout; line endings normalised (CRLF↔LF); paths compared via real/canonical path. - Invariant: the CLI it invokes is the wheel under test installed in the temp venv — never PyPI, never an editable install (C-002).
Entity: Isolated Scenario Environment
Created fresh per scenario; destroyed after.
- Fields: a temporary directory containing a freshly
git init-ed repo (the harness's repo, so a scenario can observe git state); env varsSPEC_KITTY_HOME,HOME,USERPROFILE, the XDG dirs andLOCALAPPDATAall redirected inside the temp dir;LANG/LC_ALL= UTF-8;NO_COLOR=1;COLUMNSfixed. - Invariant (NFR-002): a scenario performs 0 reads/writes to the real
HOME/SPEC_KITTY_HOME/global state. Positive control: the run observably writes inside the temp dir. - Note: the harness
git initis the test fixture; the behaviour under test (FR-004) is thatspec-kitty inititself never creates a repo or a commit — asserted against the unchanged harness-repo HEAD/commit-count.
Entity: CLI-under-test (wheel)
- Provenance: built by
uv buildfrom the commit, installed into a throwaway venv (C-002). - Lifecycle: built once per nightly job leg, reused across scenarios on that leg.
Entity: Runner (acceptance-jvm Gradle subproject)
- Fields: pinned Gradle (
tools/pins.json, version + sha256); Gradle-generatedgradle/verification-metadata.xml;build.gradle(Cucumber-JVM 7, JUnit Platform, allure-cucumber7-jvm); JDK 25; reads../tests/scenariosin place; no committed wrapper jar. - Invariant (NFR-003): 100% of JVM deps pinned/verified; distribution checksum-pinned; 0 wrapper jars committed; drift guarded by the generalized
gradle_pin_check.py.
Entity: Nightly acceptance-bdd job
- Fields: 3-OS matrix (Linux/macOS/Windows); a gating run (
not @wip and not @manual, 0 retries) and a separate non-gating@wiprun; uploads Cucumber JSON, JUnit XML, Allure results as named artifacts. - Invariant: gates no per-PR job (NFR-004); a red on any OS leg fails that leg (NFR-001).
Entity: Router group (ci-router)
scenarios→ globstests/scenarios/;acceptance_jvm→ globsacceptance-jvm/.- Invariant (C-005/FR-012): neither is referenced by any job
if:→ both gate no per-PR job; both are registered in_DELIBERATELY_UNGATED_FILTER_GROUPS; both globs match ≥1 git-tracked path.