Pular para o conteúdo

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).

Quando esta spec se aplica

Todos os triggers

Corpo da especificação

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 lint and koder-spec-audit reject malformed blocks, and /k-test-gen is 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

ElementFormLevel
Requirement### Requirement: <Title> (optional {#anchor})H3
Obligation proseone or more paragraphs containing ≥1 RFC-2119 keyword
Scenario#### Scenario: <Name>H4
Stepsa 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. MUSTSHALL and MUST NOTSHALL 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 WHEN step or lacks a THEN step
  • 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-test reports 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-gen runs 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-test runs
  • 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.
  • gates remain 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):

  1. 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.
  2. 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).
  3. 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.
  4. Frontmatter: kanon: "<versão corrente>" + applies_when: (liga a spec aos módulos pro /k-test-gen FASE 1.5 descobrir os ids por dados) e, quando existir oráculo executável, kanon_bindings: (gate block exige fp_budget, RFC-014 R3).
  5. 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.

Referências