Requirement / Scenario grammar for normative .kmd
kmd specs/kmd/requirement-scenario.kmd
Gramática opcional, mas normativa quando usada, para expressar requisitos testáveis dentro de uma spec `.kmd`. Um bloco `### Requirement:` declara uma obrigação em RFC 2119 (SHALL / SHALL NOT / SHOULD / MAY); cada `#### Scenario:` filho descreve o critério observável em Given / When / Then. Cada Scenario é a unidade testável rastreável: o `/k-test-gen` emite exatamente um TDD por Scenario, ligado de volta à cláusula da spec pelo seu id estável. Internaliza o que a OpenSpec faz de melhor (spec-driven, requisito antes do código, cenário = teste) dentro da infra de spec já existente da Koder Stack, sem adotar a ferramenta externa (ver stack-RFC-013).
When this spec applies
All triggers
- Escrever uma spec `.kmd` com requisitos que devem virar testes
- Implementar lint/parse de blocos Requirement/Scenario (kmd lint, koder-spec-audit)
- Wire de /k-test-gen para consumir Scenarios como casos de teste
- Auditar cobertura requisito→teste de uma spec
Specification body
Requirement / Scenario grammar for normative .kmd
This grammar is an opt-in extension of
specs/kmd/format.kmd. A spec is free to keep using prose +triggers:/gates. But once a document uses a### Requirement:heading, every rule below is normative for that document:kmd lintandkoder-spec-auditreject malformed blocks, and/k-test-genis entitled to treat each#### Scenario:as a test case.
KMD already gives us frontmatter, validated cross-refs and triggers:. What it
lacks — and what OpenSpec's RFC-2119 + Given/When/Then format gives — is a
structured, machine-consumable unit of obligation that maps 1:1 to a test.
This spec adds exactly that unit, reusing KMD's heading/list syntax verbatim (no
new tokens), so the existing parser, linter and renderer keep working.
1. Grammar
1.1 Requirement block
### Requirement: User authentication {#req-auth}
The Identity service SHALL issue a Koder ID session token on a successful
password+passkey login, and SHALL NOT issue one on a failed factor.
#### Scenario: Valid credentials
- GIVEN a registered user with a valid password and an enrolled passkey
- WHEN the user completes both factors
- THEN a session token is returned
- AND the token's `aud` claim equals the requesting client_id
#### Scenario: Wrong password
- GIVEN a registered user
- WHEN the user submits an incorrect password
- THEN no token is issued
- AND the failure is logged with reason `bad_factor`
1.2 Shape
| Element | Form | Level |
|---|---|---|
| Requirement | ### Requirement: <Title> (optional {#anchor}) | H3 |
| Obligation prose | one or more paragraphs containing ≥1 RFC-2119 keyword | — |
| Scenario | #### Scenario: <Name> | H4 |
| Steps | a single bullet list, each item opening with a step keyword | — |
Step keywords (uppercase, verbatim): GIVEN, WHEN, THEN, AND, BUT.
AND/BUT continue the kind of the previous step (Given/When/Then).
RFC-2119 keywords (uppercase, verbatim): SHALL, SHALL NOT, MUST,
MUST NOT, SHOULD, SHOULD NOT, MAY. MUST≡SHALL and MUST NOT≡SHALL NOT (alias; prefer SHALL in new specs for parity with the imported grammar).
2. Rules (normative)
Requirement: A Requirement block is well-formed {#req-wellformed}
The grammar SHALL be enforced by kmd lint and koder-spec-audit so that a
malformed requirement fails the audit rather than silently degrading.
Scenario: Requirement carries an obligation
- GIVEN a
### Requirement:heading - WHEN its prose body contains zero RFC-2119 keywords
- THEN lint emits error
req-no-obligation
Scenario: Requirement title is unique
- GIVEN two
### Requirement:headings in the same document with the same title and no distinguishing{#anchor} - WHEN the document is linted
- THEN lint emits error
req-duplicate-id
Scenario: Scenario has an action and an outcome
- GIVEN a
#### Scenario:block - WHEN its step list lacks a
WHENstep or lacks aTHENstep - THEN lint emits error
scenario-incomplete
Scenario: Steps use known keywords
- GIVEN a
#### Scenario:step list - WHEN a bullet does not begin with one of GIVEN/WHEN/THEN/AND/BUT
- THEN lint emits error
scenario-bad-step
Requirement: Every Scenario has a stable id {#req-scenario-id}
Each Scenario SHALL have a deterministic, content-stable id of the form
<spec-slug>#<requirement-slug>/<scenario-slug>, where spec-slug is the
spec's path slug, requirement-slug is the requirement {#anchor} (or the
title slugified), and scenario-slug is the scenario name slugified. This id is
the traceability key shared by koder.manifest.json and the regression-test
registry.
Scenario: Id is stable across prose edits
- GIVEN a Scenario whose id was recorded in
koder.manifest.json - WHEN the Requirement's obligation prose is reworded but the headings are unchanged
- THEN the computed Scenario id is unchanged
- AND the previously generated test is still matched to it
Scenario: Renaming a Scenario is a tracked change
- GIVEN a Scenario with an existing generated test
- WHEN the Scenario heading text changes
- THEN its id changes
- AND
/k-testreports the old id as orphaned rather than silently dropping coverage
Requirement: A Requirement without Scenarios is advisory {#req-advisory}
A Requirement MAY exist with zero Scenarios, but it then carries no test obligation and SHALL be reported so the gap is visible (it cannot masquerade as covered).
Scenario: Scenario-less requirement is flagged
- GIVEN a
### Requirement:with a SHALL clause and no#### Scenario: - WHEN the document is audited
- THEN audit emits warning
req-no-scenario - AND the requirement is excluded from the test-gen coverage denominator until a Scenario is added
3. Test-generation contract
Requirement: One Scenario yields one traceable test {#req-testgen}
/k-test-gen (and its category subagents) SHALL emit exactly one TDD per
#### Scenario:, named and cross-referenced by the Scenario id (req-scenario-id).
GIVEN steps map to test setup/arrange, WHEN to the act, THEN/AND-after-THEN to
the assertions.
Scenario: Scenario becomes a generated test
- GIVEN a spec with a SHALL Requirement and one Scenario
- WHEN
/k-test-genruns for the implementing module - THEN a TDD is emitted whose id references the Scenario id
- AND its assertions cover every THEN/BUT step
Scenario: Missing coverage fails the gate
- GIVEN a SHALL Requirement with ≥1 Scenario and no corresponding generated test
- WHEN
/k-testruns - THEN the run fails with the uncovered Scenario id named
- AND (per
regression-tests.kmd§7) a generator-coverage backfill ticket is suggested
Requirement: The verify triad maps to concrete gates {#req-verify}
The OpenSpec verify triad SHALL be realised as explicit, already-owned gates
rather than a separate tool: Completeness = every Requirement has ≥1
Scenario (req-advisory warning count is zero); Correctness = every
Scenario's THEN is asserted by a passing generated test (req-testgen gate);
Coherence = kmd lint cross-refs resolve and no two Scenarios in a document
assert contradictory outcomes for the same WHEN.
Scenario: Coherence catches contradictory scenarios
- GIVEN two Scenarios in one document with identical GIVEN+WHEN and contradictory THEN
- WHEN the document is audited
- THEN audit emits error
scenario-contradiction
4. Relationship to existing mechanisms
triggers:stay as-is — they answer "when must a human read this spec?". Requirements answer "what must the system do, and how is it tested?". Both coexist in one document.gatesremain the coarse pass/fail switches; a Requirement is the fine- grained, individually-traceable obligation underneath.regression-tests.kmd— a fixed bug whose class was expressible as a Scenario SHOULD gain that Scenario in the owning spec, closing the generator-coverage loop (§7) at the spec layer, not just the test layer.- Spec-delta lifecycle (stack-RFC-013 phase 4) — Requirements are the unit a spec-delta adds/modifies/removes, which is why this grammar lands first.
5. Conversion recipe (prose → Requirement/Scenario)
Receita validada no backfill pilot (fase 6 — landing-pages/products.kmd e
errors/user-facing-messages.kmd):
- Não reescreva a prosa — adicione uma seção
## Rules (normative)(ou converta a seção de regras existente) com os blocos; a prosa original permanece como contexto. A conversão NÃO pode mudar significado normativo: cada SHALL do bloco precisa rastrear para uma frase já existente. - 1 Requirement = 1 obrigação coesa com
{#anchor}estável (req-…); 2–4 Scenarios por Requirement: o happy path observável + a violação que o gate deve pegar (o "Kodix-class" — pense no post-mortem que essa cláusula evitaria). - THEN observável ou não é Scenario — cada THEN/AND tem que ser verificável por um teste estático/headless (grep, exit code, render). Se só um humano avalia, fica na prosa.
- Frontmatter:
kanon: "<versão corrente>"+applies_when:(liga a spec aos módulos pro/k-test-genFASE 1.5 descobrir os ids por dados) e, quando existir oráculo executável,kanon_bindings:(gateblockexigefp_budget, RFC-014 R3). - Validar:
kmd lint <spec>(forma) +koder-spec-audit requirement-scenario --doc=<spec>(ids/semântica) — ambos limpos antes do commit.
Disposição do corpus restante (decisão registrada, fase 6): a conversão é
oportunista — uma spec ganha a gramática quando receber edição
substancial (mesma regra de migração do document-format.kmd), não por
sweep rastreado. Racional: a conversão exige julgamento sobre o que é
testável; um sweep mecânico produziria Scenarios de baixa qualidade em massa
e o denominador de cobertura cresceria mais rápido que a capacidade do
/k-test-gen de honrá-lo.
References
engines/lang/kmdmeta/docs/stack/specs/kmd/format.kmdmeta/docs/stack/rfcs/stack-RFC-013-spec-driven-requirement-scenario-and-spec-delta.kmdmeta/docs/stack/policies/regression-tests.kmdmeta/docs/stack/policies/document-format.kmdmeta/context/commands/k-test-gen.md