Status: Accepted, accepted by the owner on 2026-10-13. Amended 2026-10-15, see Amendment.
Date: 2026-10-13
Deciders: Stijn Dejongh (owner), with the spec-kitty-qa maintainers for the evidence contract.
Technical Story: follows ADR 2026-10-11-3 and its 2026-10-11 amendment. The QA half is spec-kitty-qa ADR-0001 and ADR-0002. Design: the acceptance scenario contract.
Reader: a maintainer deciding what this repository owns about acceptance scenarios, and what it hands to spec-kitty-qa.
Context and Problem Statement
ADR 2026-10-11-3 put Gherkin scenarios in tests/scenarios/ and a Cucumber-JVM nightly in
acceptance-jvm/. Its amendment moved the promise index, the per-PR promise-tag check and the
index sync to spec-kitty-qa, which left @promise inert here and the scenarios router group
gating nothing.
Four problems remain:
- No stated boundary. Nothing says what a scenario may carry about a promise. Scenarios are about to be drafted from promise pages, with AI help, so wording, rationale or status could leak into this public repository.
- No per-PR check at all. A malformed feature file, a bad tag or a step with no binding is found only by the next nightly.
- Evidence cannot be traced to a build. The
@wippass runs after the gating pass with--rerun-tasksinto the same folders, so it overwrites the gating Cucumber JSON and JUnit XML, and mixes both passes' Allure results, before the upload. No result records the CLI version, commit, OS or pass, the Allure folder is not set, and the artifact has no explicit retention. - Fixtures stop at
init. Arrange steps cannot reach a project with a mission in it, so the next promises cannot be checked.
Decision Drivers
- Promise content stays out of the open-core repository.
- Each repository keeps the scenario tooling that suits its stack; no shared format is forced across the promise and scenario layers.
- A contributor gets scenario feedback on the pull request, without a JDK.
- Every result names the exact build, OS and pass it came from.
- Scenarios stay black-box: steps drive the CLI as a process.
Considered Options
- Identifier-only link, syntax checked here, evidence bundle per leg (chosen).
- Sync a public promise index into core and check identifiers against it. This was ADR 2026-10-11-3's original plan. Rejected by its amendment, and still rejected: it couples core to the promise store's lifecycle, and needs a cross-repository token.
- Carry the promise slug or title in the tag. Rejected: slugs are renamed, and titles leak promise wording.
- Run the JVM suite per pull request. Rejected: every contributor would pay for a JDK and a wheel build on every change, while the per-PR question is only "does this parse, and is every step bound?".
- Leave evidence as it is, and let spec-kitty-qa infer context from the run. Rejected: the gating results are overwritten before the upload, and the run alone does not say which CLI version was built.
Decision Outcome
Chosen option: option 1.
- The link is the persistent promise identifier. A scenario cites a promise only as
@promise:<id>.<id>is opaque here and matches[A-Za-z0-9][A-Za-z0-9._-]*. No promise wording, slug, status or rationale goes into this repository. Resolving identifiers, computing coverage and reporting gaps stay in spec-kitty-qa. - Tag vocabulary.
@promise:<id>;@wipwith@wip-owner:<handle>and@wip-since:<YYYY-MM-DD>;@manual;@defect:<issue>;@qa:<ref>; free-form bare tags. A drafted scenario lands as@wip. Only a person adds a@promisetag. - Per-PR scenario check.
tests/architectural/test_scenario_contract.py, standard library plus first-partykernel.clock(no third-party dependency), checks that every feature file parses, every tag follows the vocabulary, and every step of a scenario without@wipmatches exactly one step definition read from theacceptance-jvmstep annotations. It has one broken fixture per rule. Thescenariosandacceptance_jvmrouter groups gate the job that runs it. This narrows the 2026-10-11-3 amendment's "thescenariosgroup gates no per-PR job"; it does not bring back the promise index. - Evidence bundle. Each pass (gating,
@wip) writes its own folder underacceptance-jvm/build/acceptance/<pass>/, with the Allure results folder set explicitly. The runner writes Allureenvironment.properties(CLI version, commit, OS, JDK, pass) andexecutor.json. The job writesevidence.json(schema_version1) per leg. The upload keeps the nameacceptance-bdd-results-<os>, fails when the gating results are missing, and keeps them for 30 days. The job's deadpull_requestcondition is removed. - Black-box fixture builders. Builders that reach a mission state do so only by running the CLI through the driver, never by writing project files directly.
Consequences
Positive
- Scenarios can be drafted from promises and published without exposing promise content.
- Broken scenarios and unbound steps are caught on the pull request.
- spec-kitty-qa can attribute every result to a build, an OS and a pass, and the gating evidence is no longer overwritten.
Negative
- A second, partial Gherkin reader and Cucumber expression matcher exist beside the JVM runner. They must follow the runner: an unsupported parameter type fails the check rather than passing.
- The
scenariosrouter group now runs a job on scenario-only pull requests.
Neutral
@promiseidentifiers are still not checked for existence here; a typo'd identifier is found by spec-kitty-qa's trace lint, not on the pull request.- The Allure report, its history and the trace lint stay in spec-kitty-qa.
Confirmation
tests/architectural/test_scenario_contract.pypasses on the real tree, and each broken fixture fails its rule.- A nightly leg's artifact holds
gating/andwip/folders, anevidence.jsonwith the commit and CLI version, and Allure results withenvironment.properties. grep -r "promise" tests/scenariosfinds only@promise:<id>tags and guide text, no promise wording.
More Information
- The acceptance scenario contract: the design, including the fixture builders.
- CLI acceptance scenarios: how to write and run scenarios today.
Amendment (2026-10-15): authored files in fixture builders
Decision 6 of ADR 2026-10-15-1
(operator ruling) narrows decision 5 above. A fixture builder may write the files an agent or a user
authors: the planning files of specify, plan and tasks (spec.md, plan.md, tasks.md,
tasks/WP*.md), a work package's code in its checkout, review feedback outside the repository, and
plain files a user edits in git. It still reaches every state through the CLI and never writes
CLI-managed state (meta.json, the status log, lanes.json, review-cycle records, .kittify/).