From 0c582730dc7ecada408ff754bd7cd9914ba6a18d Mon Sep 17 00:00:00 2001 From: georgemclaughlin Date: Sun, 24 May 2026 10:04:15 -0500 Subject: [PATCH] docs: standardize given scenario guidance --- .changeset/given-scenario-guidance.md | 7 ++++ .../.openspec.yaml | 2 ++ .../design.md | 26 ++++++++++++++ .../proposal.md | 30 ++++++++++++++++ .../specs/openspec-conventions/spec.md | 34 +++++++++++++++++++ .../tasks.md | 11 ++++++ schemas/spec-driven/schema.yaml | 5 +-- schemas/spec-driven/templates/spec.md | 3 +- schemas/workspace-planning/schema.yaml | 2 +- src/commands/schema.ts | 1 + src/core/templates/workflows/onboard.ts | 3 +- src/core/templates/workflows/sync-specs.ts | 4 +++ src/core/validation/constants.ts | 4 +-- .../templates/skill-templates-parity.test.ts | 12 +++---- 14 files changed, 131 insertions(+), 13 deletions(-) create mode 100644 .changeset/given-scenario-guidance.md create mode 100644 openspec/changes/standardize-given-scenario-guidance/.openspec.yaml create mode 100644 openspec/changes/standardize-given-scenario-guidance/design.md create mode 100644 openspec/changes/standardize-given-scenario-guidance/proposal.md create mode 100644 openspec/changes/standardize-given-scenario-guidance/specs/openspec-conventions/spec.md create mode 100644 openspec/changes/standardize-given-scenario-guidance/tasks.md diff --git a/.changeset/given-scenario-guidance.md b/.changeset/given-scenario-guidance.md new file mode 100644 index 000000000..e237cc8d0 --- /dev/null +++ b/.changeset/given-scenario-guidance.md @@ -0,0 +1,7 @@ +--- +"@fission-ai/openspec": patch +--- + +### Fixed + +- Standardize generated scenario guidance to include GIVEN before WHEN and THEN. diff --git a/openspec/changes/standardize-given-scenario-guidance/.openspec.yaml b/openspec/changes/standardize-given-scenario-guidance/.openspec.yaml new file mode 100644 index 000000000..68948146d --- /dev/null +++ b/openspec/changes/standardize-given-scenario-guidance/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-24 diff --git a/openspec/changes/standardize-given-scenario-guidance/design.md b/openspec/changes/standardize-given-scenario-guidance/design.md new file mode 100644 index 000000000..5253f00cf --- /dev/null +++ b/openspec/changes/standardize-given-scenario-guidance/design.md @@ -0,0 +1,26 @@ +## Context + +OpenSpec's conventions already name `GIVEN` as a scenario keyword, but the default spec template and schema instructions use `WHEN`/`THEN` examples. That means newly generated specs can drift away from the richer Given/When/Then shape even when docs describe it. + +## Goals / Non-Goals + +**Goals:** +- Make `GIVEN` visible in the default spec-writing path. +- Keep this change limited to templates, generated instructions, and guidance examples. +- Preserve compatibility with existing specs that omit `GIVEN`. + +**Non-Goals:** +- Do not parse scenario steps into structured fields. +- Do not add validation warnings or errors for missing `GIVEN`. +- Do not rewrite the existing spec corpus. + +## Decisions + +- Update guidance before enforcement. This aligns generated content with the convention without causing noise in existing projects. +- Keep `GIVEN` non-breaking. The convention should encourage precondition clarity, but not require artificial preconditions where none exist. +- Update built-in schema templates and workflow examples that generate spec guidance so repo-local, workspace-planning, and sync flows teach the same structure. + +## Risks / Trade-offs + +- Existing examples remain mixed until separate cleanup work updates the full corpus. Mitigation: update the source templates and validation guidance first so new artifacts improve immediately. +- Agents may add vague `GIVEN` steps for simple scenarios. Mitigation: phrase guidance as initial state or preconditions rather than forcing a redundant placeholder. diff --git a/openspec/changes/standardize-given-scenario-guidance/proposal.md b/openspec/changes/standardize-given-scenario-guidance/proposal.md new file mode 100644 index 000000000..f80c626ac --- /dev/null +++ b/openspec/changes/standardize-given-scenario-guidance/proposal.md @@ -0,0 +1,30 @@ +## Why + +OpenSpec already documents `GIVEN` as part of scenario steps, but the default spec template and generated instructions teach agents to write only `WHEN`/`THEN`. This creates inconsistent specs and weakens precondition clarity for generated requirements. + +## What Changes + +- Make `GIVEN` part of the default scenario guidance for newly generated specs and help examples. +- Keep existing specs valid; this change does not add parser enforcement or validation warnings. +- Clarify that `GIVEN` captures initial state or preconditions, while `WHEN` captures the trigger and `THEN` captures observable outcomes. + +## Capabilities + +### New Capabilities + +None. + +### Modified Capabilities + +- `openspec-conventions`: scenario guidance should make `GIVEN` the normal first step for generated scenario examples while remaining non-breaking for existing specs. + +## Impact + +- `schemas/spec-driven/templates/spec.md` +- `schemas/spec-driven/schema.yaml` +- `schemas/workspace-planning/schema.yaml` +- `src/core/validation/constants.ts` +- `src/core/templates/workflows/onboard.ts` +- `src/core/templates/workflows/sync-specs.ts` +- `src/commands/schema.ts` +- `test/core/templates/skill-templates-parity.test.ts` diff --git a/openspec/changes/standardize-given-scenario-guidance/specs/openspec-conventions/spec.md b/openspec/changes/standardize-given-scenario-guidance/specs/openspec-conventions/spec.md new file mode 100644 index 000000000..6244c7b9d --- /dev/null +++ b/openspec/changes/standardize-given-scenario-guidance/specs/openspec-conventions/spec.md @@ -0,0 +1,34 @@ +## MODIFIED Requirements + +### Requirement: Behavioral Spec Format + +Behavioral specifications SHALL use a structured format with consistent section headers and keywords to ensure visual consistency, precondition clarity, and parseability. + +#### Scenario: Writing requirement sections + +- **GIVEN** an author is documenting a behavioral specification +- **WHEN** documenting a requirement in a behavioral specification +- **THEN** use a level-3 heading with format `### Requirement: [Name]` +- **AND** immediately follow with a SHALL statement describing core behavior +- **AND** keep requirement names descriptive and under 50 characters + +#### Scenario: Documenting scenarios + +- **GIVEN** an author is documenting a behavior or use case +- **WHEN** documenting specific behaviors or use cases +- **THEN** use level-4 headings with format `#### Scenario: [Description]` +- **AND** use bullet points with bold keywords for steps: + - **GIVEN** for initial state or preconditions + - **WHEN** for conditions or triggers + - **THEN** for expected outcomes + - **AND** for additional outcomes or conditions +- **AND** generated scenario examples SHOULD include a `GIVEN` step before `WHEN` and `THEN` + +#### Scenario: Adding implementation details + +- **GIVEN** a scenario step needs supporting examples or specifics +- **WHEN** a step requires additional detail +- **THEN** use sub-bullets under the main step +- **AND** maintain consistent indentation + - Sub-bullets provide examples or specifics + - Keep sub-bullets concise diff --git a/openspec/changes/standardize-given-scenario-guidance/tasks.md b/openspec/changes/standardize-given-scenario-guidance/tasks.md new file mode 100644 index 000000000..26f058283 --- /dev/null +++ b/openspec/changes/standardize-given-scenario-guidance/tasks.md @@ -0,0 +1,11 @@ +## 1. Guidance Updates + +- [x] 1.1 Update built-in spec templates to include `GIVEN` in scenario examples. +- [x] 1.2 Update spec-driven schema instructions and examples to describe Given/When/Then format. +- [x] 1.3 Update validation remediation examples to show `GIVEN` before `WHEN` and `THEN`. +- [x] 1.4 Update generated workflow guidance that describes scenario formatting where it still implies WHEN/THEN-only. + +## 2. Verification + +- [x] 2.1 Validate the OpenSpec change. +- [x] 2.2 Run build, lint, and relevant tests. diff --git a/schemas/spec-driven/schema.yaml b/schemas/spec-driven/schema.yaml index 45f61e222..21e89c666 100644 --- a/schemas/spec-driven/schema.yaml +++ b/schemas/spec-driven/schema.yaml @@ -47,7 +47,7 @@ artifacts: Format requirements: - Each requirement: `### Requirement: ` followed by description - Use SHALL/MUST for normative requirements (avoid should/may) - - Each scenario: `#### Scenario: ` with WHEN/THEN format + - Each scenario: `#### Scenario: ` with GIVEN/WHEN/THEN steps where preconditions apply - **CRITICAL**: Scenarios MUST use exactly 4 hashtags (`####`). Using 3 hashtags or bullets will fail silently. - Every requirement MUST have at least one scenario. @@ -68,7 +68,8 @@ artifacts: The system SHALL allow users to export their data in CSV format. #### Scenario: Successful export - - **WHEN** user clicks "Export" button + - **GIVEN** the user has data available to export + - **WHEN** the user clicks "Export" button - **THEN** system downloads a CSV file with all user data ## REMOVED Requirements diff --git a/schemas/spec-driven/templates/spec.md b/schemas/spec-driven/templates/spec.md index 095d711c8..154bf05d0 100644 --- a/schemas/spec-driven/templates/spec.md +++ b/schemas/spec-driven/templates/spec.md @@ -4,5 +4,6 @@ #### Scenario: -- **WHEN** +- **GIVEN** +- **WHEN** - **THEN** diff --git a/schemas/workspace-planning/schema.yaml b/schemas/workspace-planning/schema.yaml index f8bf64252..2bb8dd357 100644 --- a/schemas/workspace-planning/schema.yaml +++ b/schemas/workspace-planning/schema.yaml @@ -36,7 +36,7 @@ artifacts: - **REMOVED Requirements**: Deprecated behavior with Reason and Migration. - **RENAMED Requirements**: Name changes only; use FROM:/TO: format. - Each requirement must use SHALL/MUST language and include at least one `#### Scenario:` block. + Each requirement must use SHALL/MUST language and include at least one `#### Scenario:` block. Prefer GIVEN/WHEN/THEN steps where preconditions apply. requires: - proposal diff --git a/src/commands/schema.ts b/src/commands/schema.ts index 7f8d0b788..d091c1a00 100644 --- a/src/commands/schema.ts +++ b/src/commands/schema.ts @@ -957,6 +957,7 @@ function createDefaultTemplate(artifactId: string): string { Description of the requirement. #### Scenario: Example scenario +- **GIVEN** some initial state - **WHEN** some condition - **THEN** some outcome `; diff --git a/src/core/templates/workflows/onboard.ts b/src/core/templates/workflows/onboard.ts index 4690d9d2c..d3693a3a4 100644 --- a/src/core/templates/workflows/onboard.ts +++ b/src/core/templates/workflows/onboard.ts @@ -296,13 +296,14 @@ Here's the spec: #### Scenario: +- **GIVEN** - **WHEN** - **THEN** - **AND** --- -This format—WHEN/THEN/AND—makes requirements testable. You can literally read them as test cases. +This format—GIVEN/WHEN/THEN/AND—makes requirements testable. You can literally read them as test cases. \`\`\` Save to the concrete file path chosen from \`resolvedOutputPath\`. diff --git a/src/core/templates/workflows/sync-specs.ts b/src/core/templates/workflows/sync-specs.ts index bbdb2c5e6..af56638a4 100644 --- a/src/core/templates/workflows/sync-specs.ts +++ b/src/core/templates/workflows/sync-specs.ts @@ -95,6 +95,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e The system SHALL do something new. #### Scenario: Basic case +- **GIVEN** relevant preconditions are met - **WHEN** user does X - **THEN** system does Y @@ -102,6 +103,7 @@ The system SHALL do something new. ### Requirement: Existing Feature #### Scenario: New scenario to add +- **GIVEN** relevant preconditions are met - **WHEN** user does A - **THEN** system does B @@ -243,6 +245,7 @@ This is an **agent-driven** operation - you will read delta specs and directly e The system SHALL do something new. #### Scenario: Basic case +- **GIVEN** relevant preconditions are met - **WHEN** user does X - **THEN** system does Y @@ -250,6 +253,7 @@ The system SHALL do something new. ### Requirement: Existing Feature #### Scenario: New scenario to add +- **GIVEN** relevant preconditions are met - **WHEN** user does A - **THEN** system does B diff --git a/src/core/validation/constants.ts b/src/core/validation/constants.ts index a6cf0de60..6ded665fb 100644 --- a/src/core/validation/constants.ts +++ b/src/core/validation/constants.ts @@ -40,9 +40,9 @@ export const VALIDATION_MESSAGES = { GUIDE_NO_DELTAS: 'No deltas found. Ensure your change has a specs/ directory with capability folders (e.g. specs/http-server/spec.md) containing .md files that use delta headers (## ADDED/MODIFIED/REMOVED/RENAMED Requirements) and that each requirement includes at least one "#### Scenario:" block. Tip: run "openspec change show --json --deltas-only" to inspect parsed deltas.', GUIDE_MISSING_SPEC_SECTIONS: - 'Missing required sections. Expected headers: "## Purpose" and "## Requirements". Example:\n## Purpose\n[brief purpose]\n\n## Requirements\n### Requirement: Clear requirement statement\nUsers SHALL ...\n\n#### Scenario: Descriptive name\n- **WHEN** ...\n- **THEN** ...', + 'Missing required sections. Expected headers: "## Purpose" and "## Requirements". Example:\n## Purpose\n[brief purpose]\n\n## Requirements\n### Requirement: Clear requirement statement\nUsers SHALL ...\n\n#### Scenario: Descriptive name\n- **GIVEN** ...\n- **WHEN** ...\n- **THEN** ...', GUIDE_MISSING_CHANGE_SECTIONS: 'Missing required sections. Expected headers: "## Why" and "## What Changes". Ensure deltas are documented in specs/ using delta headers.', GUIDE_SCENARIO_FORMAT: - 'Scenarios must use level-4 headers. Convert bullet lists into:\n#### Scenario: Short name\n- **WHEN** ...\n- **THEN** ...\n- **AND** ...', + 'Scenarios must use level-4 headers. Convert bullet lists into:\n#### Scenario: Short name\n- **GIVEN** ...\n- **WHEN** ...\n- **THEN** ...\n- **AND** ...', } as const; diff --git a/test/core/templates/skill-templates-parity.test.ts b/test/core/templates/skill-templates-parity.test.ts index f851082e5..078b11157 100644 --- a/test/core/templates/skill-templates-parity.test.ts +++ b/test/core/templates/skill-templates-parity.test.ts @@ -35,8 +35,8 @@ const EXPECTED_FUNCTION_HASHES: Record = { getContinueChangeSkillTemplate: 'fbc6c379ed3dd39f59f52b10584b8df5b1dc08b5422bcf1c6d6255a944d22a11', getApplyChangeSkillTemplate: 'e746f230c2513a5fd40842bde494bb3cdb3c5f7c1bcece101f92090983d4ff55', getFfChangeSkillTemplate: '50e68fbb49b76d2690b614bffa9e6210e45539fb74419fc2e4311158b6d38485', - getSyncSpecsSkillTemplate: '9f02b41227db70875b89eefeb275c769142607dc5b2593f4e606794aed2fdbad', - getOnboardSkillTemplate: '4f4b60fea6e3fc7d2185815b2808fad51535fdd00cd4401b32d1536f32fa2b6d', + getSyncSpecsSkillTemplate: '1d3189ee04c49398c1df651b6114637577d9e7fae5d74f0c6c91b153e365c2d0', + getOnboardSkillTemplate: '0bb1930d20f8f799763dcab7bc7799f558d1d8b198efc45f9c916d1456a3bf01', getOpsxExploreCommandTemplate: '4d5e64e3ede6703113cf2fd23b797371ef2407b702478b4f7240fc81cbf2d3a5', getOpsxNewCommandTemplate: '757f72e2d9a1a6794b2188704fd39dd2ab65428899b4b361c76cc15a5e4f2ccc', getOpsxContinueCommandTemplate: '62f8863edda2bfe4e210f8bc3095fd4369aaaaf7772a5cba9602d0f0bca1d0c9', @@ -44,10 +44,10 @@ const EXPECTED_FUNCTION_HASHES: Record = { getOpsxFfCommandTemplate: 'f775b242bcfd56594c431c7f31a0129208a1bacfdb2427074d412543072ef7ca', getArchiveChangeSkillTemplate: 'bdf022ae2cdef1feef4d641a068bef3a7fc5d98a323f7ce9f77ac578fe8d20c6', getBulkArchiveChangeSkillTemplate: 'fdb1715804e86de85be96222b8efeb9d5b350c6d5c19e343e244655deff8e62b', - getOpsxSyncCommandTemplate: '4c8118afaea79ff4fed3d946c88e6a7abbba904a5fbf643e4372da1e3735a467', + getOpsxSyncCommandTemplate: '27d5730661b825c6c641074576be80e6997ee8c3d6677448fde0ea1678732683', getVerifyChangeSkillTemplate: '3c5dda8b49ba00f50b5bae7f04763dd00cc00a05e5f1d8a2068ad7fb701d8165', getOpsxArchiveCommandTemplate: '5181ec2f59c9f0f3376e61d952ed4be976cbd01595b6b0d5e67466c8bd6bac6d', - getOpsxOnboardCommandTemplate: '57c1f3e2590bda8f47818bab1d528456c1b8a9a7501f63ab9e2115e0cfaf6f35', + getOpsxOnboardCommandTemplate: 'd257a0691a2dfe056b8797122a3322b7b804a59eee7b561d3a06ab7db08396d9', getOpsxBulkArchiveCommandTemplate: 'b76c421023ccb5a12867c349f27cdb186234b692c1811980fb94127567bdabda', getOpsxVerifyCommandTemplate: '9a7a3f9e5bc3d0c0878b1a4493efbbb38729597d9b9be78f63284cc2da7c20c3', getOpsxProposeSkillTemplate: 'bae22279f8c7f711a8d5c5289551551d48197ddf5a99b695d96fff5339e08a49', @@ -61,11 +61,11 @@ const EXPECTED_GENERATED_SKILL_CONTENT_HASHES: Record = { 'openspec-continue-change': 'c00e2a60f79cd60197094cc59762babe5ee6a2dc1e859a0ede3f436a775ccecf', 'openspec-apply-change': 'd849442efd925b9247651e254a5cd696945321610cca5a9432ad420430554548', 'openspec-ff-change': '9d9b1995b6f4adb3da570676f7d11fee4cd1cf6c5df8ec83c033e02783a544df', - 'openspec-sync-specs': '2e0f67ec6fadffc6107b4b1a28eef23a99a6649e5fae706897ea1dd9deb852a8', + 'openspec-sync-specs': '8b55ffd00412ca6d3b0ab930103e584d3d49b6fa811c5da8aedead5943e7dffc', 'openspec-archive-change': '8d14af2c8b2e4358308ac9fc14f75db42a4b41a07e175825035852a82479793e', 'openspec-bulk-archive-change': '16207683996b1952559cd4e33463f28fb097761f2c5d912107733d01a90d3f2f', 'openspec-verify-change': 'a2acecd0c2b4e57080a314e5e7a093e0688293c37e446eb45d378f5050058550', - 'openspec-onboard': 'b924ea3c97543ebb7ee82c5f194afe7ce87a521c32b85616f445240ab33a02ab', + 'openspec-onboard': 'ab74cb7eeb5428169171e475ca7bcbe5203225193ed080bacb7d9f1c6c775d2b', 'openspec-propose': '56aa526fe1e9fac956ad3ad570a3a259d27f54b05086940d85af136a62069292', };