opsx-gherkin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/CONTRIBUTING.md +53 -0
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/SECURITY.md +14 -0
- package/dist/cli.mjs +50 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/converter-CFtzcfoM.mjs +977 -0
- package/dist/converter-CFtzcfoM.mjs.map +1 -0
- package/dist/mdg-to-opsx.mjs +8 -0
- package/dist/mdg-to-opsx.mjs.map +1 -0
- package/dist/official-99qBUZpr.mjs +91 -0
- package/dist/official-99qBUZpr.mjs.map +1 -0
- package/dist/opsx-to-feature.mjs +8 -0
- package/dist/opsx-to-feature.mjs.map +1 -0
- package/dist/opsx-to-mdg.mjs +8 -0
- package/dist/opsx-to-mdg.mjs.map +1 -0
- package/dist/validate-spec.mjs +56 -0
- package/dist/validate-spec.mjs.map +1 -0
- package/package.json +87 -0
- package/skills/opsx-gherkin/SKILL.md +144 -0
- package/skills/opsx-gherkin/references/OPENSPEC_MDG_PROFILE.feature.md +372 -0
- package/skills/opsx-gherkin/references/examples/account.feature +85 -0
- package/skills/opsx-gherkin/references/examples/account.feature.md +96 -0
- package/skills/opsx-gherkin/references/examples/account.openspec.md +109 -0
- package/skills/opsx-gherkin/references/examples/authentication-delta.feature +41 -0
- package/skills/opsx-gherkin/references/examples/authentication-delta.feature.md +45 -0
- package/skills/opsx-gherkin/references/examples/authentication-delta.openspec.md +52 -0
- package/skills/opsx-gherkin/references/examples/minimal.feature +17 -0
- package/skills/opsx-gherkin/references/examples/minimal.feature.md +19 -0
- package/skills/opsx-gherkin/references/examples/minimal.openspec.md +24 -0
- package/skills/opsx-gherkin/references/profile-access.feature.md +34 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: opsx-gherkin
|
|
3
|
+
description: Write, review, or convert specification syntax that must remain compatible between OpenSpec, Markdown with Gherkin (.feature.md), and plain Gherkin (.feature) using opsx-gherkin. Use for format mappings, compatibility constraints, and conversion diagnostics; ordinary prose editing or choosing application behavior does not need this skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OpenSpec and Gherkin compatibility
|
|
7
|
+
|
|
8
|
+
Preserve the intended behavior while expressing it in the requested format.
|
|
9
|
+
Read the [canonical compatibility contract](references/OPENSPEC_MDG_PROFILE.feature.md)
|
|
10
|
+
in full before authoring or changing a compatible specification. Its Rules and
|
|
11
|
+
Scenarios specify the converter and demonstrate the format. Apply that structure
|
|
12
|
+
and its compatibility constraints to the requested domain's behavior. The contract
|
|
13
|
+
defines the supported mappings; this entry point explains how to use them.
|
|
14
|
+
|
|
15
|
+
## Select the representation
|
|
16
|
+
|
|
17
|
+
| Input | Output | Command |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| Markdown with Gherkin | OpenSpec | `opsx-gherkin mdg-to-opsx` |
|
|
20
|
+
| OpenSpec | Markdown with Gherkin | `opsx-gherkin opsx-to-mdg` |
|
|
21
|
+
| OpenSpec | Plain Gherkin | `opsx-gherkin opsx-to-feature` |
|
|
22
|
+
|
|
23
|
+
These are the supported conversion directions. Plain `.feature` is an output
|
|
24
|
+
and a directly validatable format; it is not a converter input.
|
|
25
|
+
|
|
26
|
+
After reading the contract, choose a complete example for the constructs you need:
|
|
27
|
+
|
|
28
|
+
- Basic main spec: [minimal.feature.md](references/examples/minimal.feature.md),
|
|
29
|
+
with a Feature description, a software target, one normative Rule, and a single-row Outline;
|
|
30
|
+
equivalent [OpenSpec](references/examples/minimal.openspec.md) and
|
|
31
|
+
[plain Gherkin](references/examples/minimal.feature).
|
|
32
|
+
- Shared setup, outlines, multiple Examples groups, Data Tables, and Doc Strings:
|
|
33
|
+
[account.feature.md](references/examples/account.feature.md), with
|
|
34
|
+
[OpenSpec](references/examples/account.openspec.md) and
|
|
35
|
+
[plain Gherkin](references/examples/account.feature) counterparts.
|
|
36
|
+
- Delta operations: [authentication-delta.feature.md](references/examples/authentication-delta.feature.md)
|
|
37
|
+
with [OpenSpec](references/examples/authentication-delta.openspec.md) and
|
|
38
|
+
[plain Gherkin](references/examples/authentication-delta.feature) counterparts.
|
|
39
|
+
This is a structural change document; each added/modified behavior still needs
|
|
40
|
+
a complete example, while removal/rename entries retain their restricted shape.
|
|
41
|
+
- Finding and retrieving the contract: [profile-access.feature.md](references/profile-access.feature.md).
|
|
42
|
+
|
|
43
|
+
These examples apply the canonical contract to their own domains.
|
|
44
|
+
|
|
45
|
+
## Author within the contract
|
|
46
|
+
|
|
47
|
+
- Model each completed Feature as one whole public use case. Give each scenario
|
|
48
|
+
its own necessary prior facts, one action or query, and observable consequences.
|
|
49
|
+
Keep acceptance and rejection in separate scenarios of the same use case.
|
|
50
|
+
- Expose meaningful example values as named Outline arguments, even for one row.
|
|
51
|
+
Keep independent roles distinct, such as stored/submitted credentials and
|
|
52
|
+
requested/resulting quantities. Keep fixed criteria in Rules, cohesive records
|
|
53
|
+
in Data Tables, and literal source text intact when the use case examines it.
|
|
54
|
+
- Match the requested representation: Gherkin headings and bullet steps in MDG,
|
|
55
|
+
OpenSpec requirement headings and bold step keywords in OpenSpec, native
|
|
56
|
+
keyword lines in `.feature`. A fenced native document is not MDG structure.
|
|
57
|
+
- Use the contract's Feature/Purpose, Rule/Requirement, Background, and
|
|
58
|
+
Scenario mappings. Supply a meaningful main-spec Purpose and the required
|
|
59
|
+
`SHALL` or `MUST` wording for ordinary Rules and ADDED/MODIFIED requirements.
|
|
60
|
+
- Place descriptions before their child structures. Keep each Background in
|
|
61
|
+
its intended scope; preserve the original step roles and all example values.
|
|
62
|
+
- Use English structural keywords supported by this profile. Do not translate
|
|
63
|
+
keywords or rewrite domain content merely to make conversion succeed.
|
|
64
|
+
- Bind outline placeholders with concrete Examples rows. Keep Examples groups,
|
|
65
|
+
their tags, and step Data Tables distinct. Follow the contract's indented
|
|
66
|
+
Markdown table syntax and escaping. Check parsed row counts and literal
|
|
67
|
+
values, especially pipes, backslashes, newlines, and Doc String contents.
|
|
68
|
+
- Identify a completed Feature's verification boundary: `@code` for software,
|
|
69
|
+
`@skill` for an agent applying guidance, `@prompt` for a model executing a prompt,
|
|
70
|
+
or `@plugin` for its host integration. The software teaching examples use `@code`.
|
|
71
|
+
These tags identify the exercised boundary, not its internal dependencies.
|
|
72
|
+
Descriptive topic tags are optional and need not be copied from the contract.
|
|
73
|
+
- Conversion accepts compatible untagged documents and plain Scenarios too.
|
|
74
|
+
Preserve their existing model; do not insert targets or invent parameters
|
|
75
|
+
while converting. Authoring conventions do not narrow syntax acceptance.
|
|
76
|
+
- In MDG, put each tag in its own backtick span immediately above the tagged
|
|
77
|
+
heading. In native Gherkin, use bare tags. Reserve `@openspec-*` and `@mdg-*`
|
|
78
|
+
names for the structural roles defined by the contract.
|
|
79
|
+
- For deltas, mark the Feature and each operation as the contract specifies.
|
|
80
|
+
REMOVED supports the requirement name only. RENAMED supports its canonical
|
|
81
|
+
FROM and TO names only. Additional removal/rename content has no mapping;
|
|
82
|
+
report that boundary instead of silently deleting information.
|
|
83
|
+
|
|
84
|
+
### Keep table and argument values intact
|
|
85
|
+
|
|
86
|
+
Indent Markdown table rows by two spaces. In MDG, put data rows directly after
|
|
87
|
+
the header, without a separator row; the converter emits this canonical form.
|
|
88
|
+
OpenSpec tables retain their Markdown separator row. The pinned MDG parser
|
|
89
|
+
accepts either input form and preserves the interpreted rows; table formatting
|
|
90
|
+
must not change Examples cases or Data Table values. Plain Gherkin also omits
|
|
91
|
+
separators. Use `\|` for a literal pipe, `\\` for a literal backslash,
|
|
92
|
+
and `\n` for a newline inside a cell. Do not apply cell escaping to Doc Strings.
|
|
93
|
+
|
|
94
|
+
Keep a Markdown Doc String in a triple-backtick block indented by two spaces
|
|
95
|
+
under its step; put its optional media type immediately after the opening fence.
|
|
96
|
+
Preserve the content's indentation relative to the fence. In an outline, each
|
|
97
|
+
`<placeholder>` names an Examples column; retain every intended data row and
|
|
98
|
+
verify the expanded cases. The account example demonstrates two separate
|
|
99
|
+
Examples groups and a JSON Doc String containing a placeholder.
|
|
100
|
+
|
|
101
|
+
## Convert and verify
|
|
102
|
+
|
|
103
|
+
Each converter accepts one path, `--text`, or stdin. Use stdout to inspect a
|
|
104
|
+
result; use `-o <destination>` for an explicit output file. Existing outputs
|
|
105
|
+
require `--force`. Respect the user's destination and overwrite intent.
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
opsx-gherkin mdg-to-opsx input.feature.md -o spec.md
|
|
109
|
+
opsx-gherkin opsx-to-mdg spec.md -o output.feature.md
|
|
110
|
+
opsx-gherkin opsx-to-feature spec.md -o output.feature
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Use `--name` when OpenSpec text has no title or usable source-path context.
|
|
114
|
+
`--project-root` supplies existing main-spec context for delta validation when
|
|
115
|
+
needed. Consult a converter's `--help` for its exact options.
|
|
116
|
+
|
|
117
|
+
The converters validate both endpoints and reject changes to the normalized
|
|
118
|
+
model before emitting output. Formatting may become canonical; names, values,
|
|
119
|
+
structure, and supported metadata must retain their meaning.
|
|
120
|
+
|
|
121
|
+
Direct endpoint validation checks syntax rather than conversion eligibility.
|
|
122
|
+
The standalone OpenSpec validator below accepts main specifications. For deltas,
|
|
123
|
+
use a supported conversion: it invokes the official strict delta validator and
|
|
124
|
+
checks model preservation.
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
validate-spec --format openspec spec.md
|
|
128
|
+
validate-spec --format mdg input.feature.md
|
|
129
|
+
validate-spec --format feature output.feature
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Passing these checks establishes example syntax and conversion preservation.
|
|
133
|
+
Review the authored scenarios against the requested domain behavior as well;
|
|
134
|
+
validator success alone does not establish reliable agent authoring.
|
|
135
|
+
|
|
136
|
+
With npm/npx, use `npx opsx-gherkin <command>` for the main CLI and
|
|
137
|
+
`npx --package opsx-gherkin validate-spec ...` for the standalone validator.
|
|
138
|
+
`opsx-gherkin profile` prints the same contract linked above.
|
|
139
|
+
|
|
140
|
+
Exit status `1` means invalid input, unsupported profile content, or failed
|
|
141
|
+
conversion validation; `2` means usage or I/O failure. Read stderr and correct
|
|
142
|
+
the indicated issue without inventing a mapping or changing the intended
|
|
143
|
+
behavior. If the CLI is unavailable, explain the syntax from the contract and
|
|
144
|
+
state that execution has not been verified.
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
`@code` `@contract`
|
|
2
|
+
# Feature: Convert a compatible specification
|
|
3
|
+
|
|
4
|
+
This document is the normative contract for the conversion profile. The converters SHALL preserve the domain structure represented here and SHALL reject mappings not defined here.
|
|
5
|
+
|
|
6
|
+
Its Rules and Scenarios specify the converter, and their structure demonstrates valid Markdown with Gherkin. To author a specification for another domain, express that domain's requested behavior using the demonstrated structure and the compatibility rules in this complete contract.
|
|
7
|
+
|
|
8
|
+
The **@code** target identifies software as this Feature’s verification boundary. Descriptive tags such as **@contract** are optional example metadata. A new main specification may omit them; conversion of an existing document must preserve its tags. Markers in the reserved **@openspec-** and **@mdg-** namespaces have only the structural roles defined below.
|
|
9
|
+
|
|
10
|
+
The separate [profile-access use case](profile-access.feature.md) specifies how to find and retrieve this contract.
|
|
11
|
+
|
|
12
|
+
## Background: Official validation
|
|
13
|
+
|
|
14
|
+
* Given the OpenSpec 1.11.0 strict validator is available
|
|
15
|
+
* And the Cucumber Gherkin 42.0.1 Markdown and plain-text parsers are available
|
|
16
|
+
|
|
17
|
+
### Scenario: Preserve a scenario outside a Rule
|
|
18
|
+
|
|
19
|
+
* Given a valid feature-level scenario
|
|
20
|
+
* When it is converted to OpenSpec
|
|
21
|
+
* Then it is stored in the reserved **@mdg-feature-scenarios** Requirement
|
|
22
|
+
|
|
23
|
+
### Scenario: Restore a scenario outside a Rule
|
|
24
|
+
|
|
25
|
+
* Given a compatible OpenSpec main spec containing the reserved **@mdg-feature-scenarios** Requirement
|
|
26
|
+
* When it is converted to MDG
|
|
27
|
+
* Then its scenarios become Feature-level scenarios
|
|
28
|
+
* And the reserved Requirement wrapper is removed
|
|
29
|
+
|
|
30
|
+
`@mapping`
|
|
31
|
+
## Rule: Map document and requirement structure
|
|
32
|
+
|
|
33
|
+
The converter SHALL map a Gherkin Feature to an OpenSpec title and Purpose, and SHALL map every ordinary Rule to a Requirement with the same name and normative description.
|
|
34
|
+
|
|
35
|
+
### Background: A valid normative Rule
|
|
36
|
+
|
|
37
|
+
* Given every ordinary Rule description contains `SHALL` or `MUST`
|
|
38
|
+
|
|
39
|
+
`@purpose`
|
|
40
|
+
### Scenario: Map Feature metadata
|
|
41
|
+
|
|
42
|
+
* Given an MDG Feature with a name, description, and optional descriptive tags
|
|
43
|
+
* When it is converted to OpenSpec
|
|
44
|
+
* Then the name is written as the level-one title
|
|
45
|
+
* And the description is written under `## Purpose`
|
|
46
|
+
* And any supplied tags remain backticked tags adjacent to the title
|
|
47
|
+
* And an untagged Feature does not acquire illustrative tags from this contract
|
|
48
|
+
|
|
49
|
+
### Scenario: Map Rules to Requirements
|
|
50
|
+
|
|
51
|
+
* Given the following structural correspondence:
|
|
52
|
+
|
|
53
|
+
| MDG | OpenSpec |
|
|
54
|
+
| Feature | title and Purpose |
|
|
55
|
+
| Rule | Requirement |
|
|
56
|
+
| Rule description | normative requirement text |
|
|
57
|
+
| Scenario | Scenario |
|
|
58
|
+
* When either representation is converted
|
|
59
|
+
* Then names, descriptions, tags, and child order are preserved
|
|
60
|
+
* And optional tags in a newly authored document do not permit dropping metadata from an existing source
|
|
61
|
+
|
|
62
|
+
### Scenario: Map shared setup
|
|
63
|
+
|
|
64
|
+
* Given a compatible MDG main spec with both a Feature Background and a Rule Background
|
|
65
|
+
* When the specification is converted to OpenSpec
|
|
66
|
+
* Then it is written as a readable `**Background:**` block in the Requirements preamble
|
|
67
|
+
* And the Rule Background is written as `#### Background:` inside its Requirement
|
|
68
|
+
|
|
69
|
+
### Scenario: Convert a complete sign-in specification to MDG
|
|
70
|
+
|
|
71
|
+
* Given this compatible OpenSpec document:
|
|
72
|
+
|
|
73
|
+
```markdown
|
|
74
|
+
`@code`
|
|
75
|
+
# Sign in with account credentials
|
|
76
|
+
|
|
77
|
+
## Purpose
|
|
78
|
+
|
|
79
|
+
Give an active account access when the submitted credentials match its registered credentials.
|
|
80
|
+
|
|
81
|
+
## Requirements
|
|
82
|
+
|
|
83
|
+
**Background:**
|
|
84
|
+
|
|
85
|
+
- **GIVEN** the authentication service is available
|
|
86
|
+
|
|
87
|
+
`@credentials`
|
|
88
|
+
### Requirement: Matching credentials grant access
|
|
89
|
+
|
|
90
|
+
The system SHALL grant access to an active account when submitted credentials match and SHALL reject a nonmatching password.
|
|
91
|
+
|
|
92
|
+
`@accepted`
|
|
93
|
+
#### Scenario Outline: Grant access for matching credentials
|
|
94
|
+
|
|
95
|
+
- **GIVEN** an active account has registered email `<registered_email>` and password `<registered_password>`
|
|
96
|
+
- **WHEN** a user signs in with email `<submitted_email>` and password `<submitted_password>`
|
|
97
|
+
- **THEN** access to the account registered as `<expected_email>` is granted
|
|
98
|
+
|
|
99
|
+
##### Examples: Matching credentials
|
|
100
|
+
|
|
101
|
+
| registered_email | registered_password | submitted_email | submitted_password | expected_email |
|
|
102
|
+
| --- | --- | --- | --- | --- |
|
|
103
|
+
| user@example.com | correct-password | user@example.com | correct-password | user@example.com |
|
|
104
|
+
|
|
105
|
+
`@rejected`
|
|
106
|
+
#### Scenario Outline: Reject access for a nonmatching password
|
|
107
|
+
|
|
108
|
+
- **GIVEN** an active account has registered email `<registered_email>` and password `<registered_password>`
|
|
109
|
+
- **WHEN** a user signs in with email `<submitted_email>` and password `<submitted_password>`
|
|
110
|
+
- **THEN** access is rejected
|
|
111
|
+
|
|
112
|
+
##### Examples: Nonmatching password
|
|
113
|
+
|
|
114
|
+
| registered_email | registered_password | submitted_email | submitted_password |
|
|
115
|
+
| --- | --- | --- | --- |
|
|
116
|
+
| user@example.com | correct-password | user@example.com | wrong-password |
|
|
117
|
+
```
|
|
118
|
+
* When the document is converted to MDG
|
|
119
|
+
* Then the destination is:
|
|
120
|
+
|
|
121
|
+
```markdown
|
|
122
|
+
`@code`
|
|
123
|
+
# Feature: Sign in with account credentials
|
|
124
|
+
|
|
125
|
+
Give an active account access when the submitted credentials match its registered credentials.
|
|
126
|
+
|
|
127
|
+
## Background:
|
|
128
|
+
|
|
129
|
+
* Given the authentication service is available
|
|
130
|
+
|
|
131
|
+
`@credentials`
|
|
132
|
+
## Rule: Matching credentials grant access
|
|
133
|
+
|
|
134
|
+
The system SHALL grant access to an active account when submitted credentials match and SHALL reject a nonmatching password.
|
|
135
|
+
|
|
136
|
+
`@accepted`
|
|
137
|
+
### Scenario Outline: Grant access for matching credentials
|
|
138
|
+
|
|
139
|
+
* Given an active account has registered email `<registered_email>` and password `<registered_password>`
|
|
140
|
+
* When a user signs in with email `<submitted_email>` and password `<submitted_password>`
|
|
141
|
+
* Then access to the account registered as `<expected_email>` is granted
|
|
142
|
+
|
|
143
|
+
#### Examples: Matching credentials
|
|
144
|
+
|
|
145
|
+
| registered_email | registered_password | submitted_email | submitted_password | expected_email |
|
|
146
|
+
| user@example.com | correct-password | user@example.com | correct-password | user@example.com |
|
|
147
|
+
|
|
148
|
+
`@rejected`
|
|
149
|
+
### Scenario Outline: Reject access for a nonmatching password
|
|
150
|
+
|
|
151
|
+
* Given an active account has registered email `<registered_email>` and password `<registered_password>`
|
|
152
|
+
* When a user signs in with email `<submitted_email>` and password `<submitted_password>`
|
|
153
|
+
* Then access is rejected
|
|
154
|
+
|
|
155
|
+
#### Examples: Nonmatching password
|
|
156
|
+
|
|
157
|
+
| registered_email | registered_password | submitted_email | submitted_password |
|
|
158
|
+
| user@example.com | correct-password | user@example.com | wrong-password |
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Rule: Preserve behavioral Gherkin constructs
|
|
162
|
+
|
|
163
|
+
The converter SHALL preserve Scenario and Scenario Outline structure, typed steps, tags, Examples, Data Tables, and Doc Strings without treating them as opaque prose.
|
|
164
|
+
|
|
165
|
+
Canonical MDG tables SHALL contain an indented header followed directly by data rows, without Markdown separator rows. Canonical OpenSpec tables SHALL retain their Markdown separator rows. Compatible MDG inputs with or without separators SHALL preserve the same interpreted rows and values.
|
|
166
|
+
|
|
167
|
+
### Scenario: Map typed steps
|
|
168
|
+
|
|
169
|
+
* Given a Scenario containing Given, When, Then, And, and But steps
|
|
170
|
+
* When it is converted to OpenSpec
|
|
171
|
+
* Then each step uses a Markdown list item with its uppercase keyword in bold
|
|
172
|
+
|
|
173
|
+
### Scenario: Restore English typed step roles
|
|
174
|
+
|
|
175
|
+
* Given a compatible OpenSpec Scenario containing bold GIVEN, WHEN, THEN, AND, and BUT list items
|
|
176
|
+
* When it is converted to MDG
|
|
177
|
+
* Then each step retains its corresponding English Gherkin keyword
|
|
178
|
+
|
|
179
|
+
`@outline`
|
|
180
|
+
### Scenario Outline: Preserve `<expected_groups>` Examples groups in `<target_format>`
|
|
181
|
+
|
|
182
|
+
* Given a compatible `<source_format>` main spec with an Outline containing `<source_groups>` Examples groups
|
|
183
|
+
* When it is converted to `<target_format>`
|
|
184
|
+
* Then the Outline has `<expected_groups>` Examples groups
|
|
185
|
+
* And each group retains its name, tags, description, headers, and rows
|
|
186
|
+
|
|
187
|
+
`@single-group`
|
|
188
|
+
#### Examples: One group
|
|
189
|
+
|
|
190
|
+
| source_format | target_format | source_groups | expected_groups |
|
|
191
|
+
| MDG | OpenSpec | 1 | 1 |
|
|
192
|
+
| OpenSpec | MDG | 1 | 1 |
|
|
193
|
+
|
|
194
|
+
`@multiple-groups`
|
|
195
|
+
#### Examples: Several groups
|
|
196
|
+
|
|
197
|
+
| source_format | target_format | source_groups | expected_groups |
|
|
198
|
+
| OpenSpec | MDG | 3 | 3 |
|
|
199
|
+
| MDG | OpenSpec | 3 | 3 |
|
|
200
|
+
|
|
201
|
+
### Scenario Outline: Preserve literal step arguments in `<target_format>`
|
|
202
|
+
|
|
203
|
+
* Given a compatible `<source_format>` main spec with a scenario step containing this Data Table:
|
|
204
|
+
|
|
205
|
+
| field | value |
|
|
206
|
+
| status | active |
|
|
207
|
+
| attempts | 4 |
|
|
208
|
+
* And another step in the same scenario containing this Doc String:
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"error": "invalid_credentials"
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
* When the document is converted to `<target_format>`
|
|
216
|
+
* Then table cells, Doc String media type, and Doc String content are unchanged
|
|
217
|
+
|
|
218
|
+
#### Examples: Argument conversion directions
|
|
219
|
+
|
|
220
|
+
| source_format | target_format |
|
|
221
|
+
| MDG | OpenSpec |
|
|
222
|
+
| OpenSpec | MDG |
|
|
223
|
+
|
|
224
|
+
## Rule: Project OpenSpec to a plain Gherkin feature
|
|
225
|
+
|
|
226
|
+
The `opsx-to-feature` command SHALL preserve the same normalized behavioral model as `opsx-to-mdg` and MUST emit plain-text Gherkin accepted by the official parser.
|
|
227
|
+
|
|
228
|
+
### Scenario: Render plain Gherkin syntax
|
|
229
|
+
|
|
230
|
+
* Given a valid OpenSpec main specification
|
|
231
|
+
* When `opsx-to-feature` converts it
|
|
232
|
+
* Then Feature, Rule, Background, Scenario, Scenario Outline, Examples, steps, Data Tables, Doc Strings, descriptions, and tags use plain Gherkin syntax
|
|
233
|
+
* And structural Markdown headings, list markers, backticked tags, and table separator rows are absent
|
|
234
|
+
* And the output is suitable for a `.feature` file
|
|
235
|
+
|
|
236
|
+
### Scenario: Render an OpenSpec delta as Gherkin
|
|
237
|
+
|
|
238
|
+
* Given a valid OpenSpec delta
|
|
239
|
+
* When `opsx-to-feature` converts it
|
|
240
|
+
* Then the Feature and Rules retain the reserved delta tags defined by this contract
|
|
241
|
+
* And RENAMED retains its canonical FROM and TO requirement names
|
|
242
|
+
|
|
243
|
+
## Rule: Represent OpenSpec deltas idiomatically in MDG
|
|
244
|
+
|
|
245
|
+
The converter SHALL mark a delta Feature with **@openspec-delta** and SHALL represent each delta operation as a Rule carrying exactly one reserved operation tag.
|
|
246
|
+
|
|
247
|
+
### Scenario Outline: Map every delta operation
|
|
248
|
+
|
|
249
|
+
* Given an OpenSpec `<operation>` requirement
|
|
250
|
+
* When it is converted to MDG
|
|
251
|
+
* Then its Rule carries `<tag>`
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
#### Examples: Supported operations
|
|
255
|
+
|
|
256
|
+
| operation | tag |
|
|
257
|
+
| ADDED | @openspec-added |
|
|
258
|
+
| MODIFIED | @openspec-modified |
|
|
259
|
+
| REMOVED | @openspec-removed |
|
|
260
|
+
| RENAMED | @openspec-renamed |
|
|
261
|
+
|
|
262
|
+
### Scenario Outline: Restore the `<operation>` section from its reserved marker
|
|
263
|
+
|
|
264
|
+
* Given a compatible MDG delta with a Rule carrying `<tag>` and the content required for that operation
|
|
265
|
+
* When the delta is converted to OpenSpec
|
|
266
|
+
* Then the entry belongs to the `<operation>` section
|
|
267
|
+
* And its requirement identity and supported content are preserved
|
|
268
|
+
|
|
269
|
+
#### Examples: Operation identities
|
|
270
|
+
|
|
271
|
+
| operation | tag |
|
|
272
|
+
| ADDED | @openspec-added |
|
|
273
|
+
| MODIFIED | @openspec-modified |
|
|
274
|
+
| REMOVED | @openspec-removed |
|
|
275
|
+
| RENAMED | @openspec-renamed |
|
|
276
|
+
|
|
277
|
+
### Scenario Outline: Preserve the rename from `<old_name>` to `<new_name>`
|
|
278
|
+
|
|
279
|
+
* Given a compatible OpenSpec delta renaming the requirement `<old_name>` to `<new_name>`
|
|
280
|
+
* When the delta is converted to MDG
|
|
281
|
+
* Then the Rule description contains canonical FROM and TO lines:
|
|
282
|
+
|
|
283
|
+
```text
|
|
284
|
+
FROM: `### Requirement: <old_name>`
|
|
285
|
+
TO: `### Requirement: <new_name>`
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
#### Examples: Renamed requirements
|
|
289
|
+
|
|
290
|
+
| old_name | new_name |
|
|
291
|
+
| Login audit | Authentication audit |
|
|
292
|
+
|
|
293
|
+
## Rule: Validate and normalize every conversion
|
|
294
|
+
|
|
295
|
+
The converter SHALL validate both endpoints with their official deterministic validators and MUST emit only a canonical result whose normalized model equals the input model.
|
|
296
|
+
|
|
297
|
+
### Scenario: Complete a successful conversion
|
|
298
|
+
|
|
299
|
+
* Given a source document accepted by its official validator and this profile
|
|
300
|
+
* When either converter runs
|
|
301
|
+
* Then the generated document is accepted by the destination validator
|
|
302
|
+
* And parsing the generated document produces the same normalized model
|
|
303
|
+
|
|
304
|
+
### Scenario Outline: Reject a Rule without normative wording
|
|
305
|
+
|
|
306
|
+
* Given a syntactically valid MDG main spec whose ordinary Rule description contains neither SHALL nor MUST
|
|
307
|
+
* When conversion to OpenSpec is requested
|
|
308
|
+
* Then the command exits with status `<exit_status>`
|
|
309
|
+
* And no destination document is emitted
|
|
310
|
+
* And diagnostics identify the Rule that lacks normative wording
|
|
311
|
+
|
|
312
|
+
#### Examples: Invalid profile outcome
|
|
313
|
+
|
|
314
|
+
| exit_status |
|
|
315
|
+
| 1 |
|
|
316
|
+
|
|
317
|
+
### Scenario: Reject removal metadata without a defined mapping
|
|
318
|
+
|
|
319
|
+
* Given a REMOVED Requirement or Rule containing prose, a Background, or scenarios
|
|
320
|
+
* When conversion is attempted
|
|
321
|
+
* Then the command reports that REMOVED supports only the requirement name
|
|
322
|
+
* And no content is silently discarded
|
|
323
|
+
|
|
324
|
+
### Scenario: Reject extra rename content
|
|
325
|
+
|
|
326
|
+
* Given a RENAMED Rule containing content beyond its canonical FROM and TO names
|
|
327
|
+
* When conversion is attempted
|
|
328
|
+
* Then the command reports the unsupported content
|
|
329
|
+
* And no destination document is emitted
|
|
330
|
+
|
|
331
|
+
### Scenario Outline: Preserve delta group order in `<target_format>`
|
|
332
|
+
|
|
333
|
+
* Given a compatible `<source_format>` delta with its MODIFIED group before its ADDED group
|
|
334
|
+
* When the delta is converted to `<target_format>`
|
|
335
|
+
* Then MODIFIED still precedes ADDED
|
|
336
|
+
* And requirement order within each group is unchanged
|
|
337
|
+
|
|
338
|
+
#### Examples: Ordered delta conversion directions
|
|
339
|
+
|
|
340
|
+
| source_format | target_format |
|
|
341
|
+
| OpenSpec | MDG |
|
|
342
|
+
| MDG | OpenSpec |
|
|
343
|
+
|
|
344
|
+
## Rule: Support symmetric command input and output
|
|
345
|
+
|
|
346
|
+
All converter commands SHALL accept one path, inline text, or stdin and SHALL return text on stdout or write one destination path atomically.
|
|
347
|
+
|
|
348
|
+
### Scenario Outline: Select an input source
|
|
349
|
+
|
|
350
|
+
* Given input is supplied through `<source>`
|
|
351
|
+
* When the converter starts
|
|
352
|
+
* Then that source is read without guessing whether literal text is a path
|
|
353
|
+
|
|
354
|
+
#### Examples: Input modes
|
|
355
|
+
|
|
356
|
+
| source |
|
|
357
|
+
| positional path |
|
|
358
|
+
| --text |
|
|
359
|
+
| stdin |
|
|
360
|
+
|
|
361
|
+
### Scenario Outline: Preserve an existing destination without overwrite permission
|
|
362
|
+
|
|
363
|
+
* Given a compatible MDG source is available
|
|
364
|
+
* And the destination `<destination>` already contains a document
|
|
365
|
+
* When conversion to OpenSpec is requested at `<destination>` without `--force`
|
|
366
|
+
* Then the command exits with status `<exit_status>`
|
|
367
|
+
* And the bytes in `<destination>` remain unchanged
|
|
368
|
+
|
|
369
|
+
#### Examples: Protected destination
|
|
370
|
+
|
|
371
|
+
| destination | exit_status |
|
|
372
|
+
| account.spec.md | 2 |
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
@code @authentication @security
|
|
2
|
+
Feature: User sign-in
|
|
3
|
+
|
|
4
|
+
Allow registered users to access their accounts securely while rejecting invalid credentials and limiting repeated attempts.
|
|
5
|
+
|
|
6
|
+
Background:
|
|
7
|
+
Given the authentication service is available
|
|
8
|
+
|
|
9
|
+
@credentials
|
|
10
|
+
Rule: Credentials are verified securely
|
|
11
|
+
|
|
12
|
+
The system SHALL grant access only when an active registered user provides the correct email address and password.
|
|
13
|
+
|
|
14
|
+
@smoke @positive
|
|
15
|
+
Scenario Outline: Sign-in succeeds with correct credentials
|
|
16
|
+
Given an active registered account with these credentials:
|
|
17
|
+
| email | password |
|
|
18
|
+
| <storedEmail> | <storedPassword> |
|
|
19
|
+
And the account has <priorFailureCount> consecutive failed sign-in attempts
|
|
20
|
+
When the user signs in with email "<submittedEmail>" and password "<submittedPassword>"
|
|
21
|
+
Then the system grants access to the account with email "<expectedAccountEmail>"
|
|
22
|
+
And the system starts an authenticated session
|
|
23
|
+
|
|
24
|
+
Examples: Matching credentials
|
|
25
|
+
| storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | expectedAccountEmail |
|
|
26
|
+
| user@example.com | correct-password | 0 | user@example.com | correct-password | user@example.com |
|
|
27
|
+
|
|
28
|
+
@negative @api
|
|
29
|
+
Scenario Outline: Sign-in rejects invalid credentials
|
|
30
|
+
Given only the following active account is registered:
|
|
31
|
+
| email | password |
|
|
32
|
+
| <storedEmail> | <storedPassword> |
|
|
33
|
+
And the account has <priorFailureCount> consecutive failed sign-in attempts
|
|
34
|
+
When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
|
|
35
|
+
Then the system rejects access with HTTP status <httpStatus>
|
|
36
|
+
And the response body is:
|
|
37
|
+
"""json
|
|
38
|
+
{
|
|
39
|
+
"error": "<errorCode>"
|
|
40
|
+
}
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
@unknown-account
|
|
44
|
+
Examples: Unknown account
|
|
45
|
+
| storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | httpStatus | errorCode |
|
|
46
|
+
| user@example.com | correct-password | 0 | missing@example.com | any-password | 401 | invalid_credentials |
|
|
47
|
+
|
|
48
|
+
@incorrect-password
|
|
49
|
+
Examples: Incorrect password
|
|
50
|
+
| storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | httpStatus | errorCode |
|
|
51
|
+
| user@example.com | correct-password | 0 | user@example.com | wrong-password | 401 | invalid_credentials |
|
|
52
|
+
|
|
53
|
+
@account-protection
|
|
54
|
+
Rule: Repeated failures lock the account
|
|
55
|
+
|
|
56
|
+
The system SHALL lock an account after five consecutive failed sign-in attempts. The system SHALL reject sign-in while the account is locked, even when the submitted credentials are correct.
|
|
57
|
+
|
|
58
|
+
@negative @lockout
|
|
59
|
+
Scenario Outline: Sign-in locks the account at the failure limit
|
|
60
|
+
Given an active registered account with these credentials:
|
|
61
|
+
| email | password |
|
|
62
|
+
| <storedEmail> | <storedPassword> |
|
|
63
|
+
And the account has <priorFailureCount> consecutive failed sign-in attempts
|
|
64
|
+
When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
|
|
65
|
+
Then the system rejects access
|
|
66
|
+
And the account has <expectedFailureCount> consecutive failed sign-in attempts
|
|
67
|
+
And the system locks the account
|
|
68
|
+
|
|
69
|
+
Examples: Failure limit reached
|
|
70
|
+
| storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword | expectedFailureCount |
|
|
71
|
+
| user@example.com | correct-password | 4 | user@example.com | wrong-password | 5 |
|
|
72
|
+
|
|
73
|
+
@negative @lockout
|
|
74
|
+
Scenario Outline: A locked account rejects correct credentials
|
|
75
|
+
Given a registered account with these credentials:
|
|
76
|
+
| email | password |
|
|
77
|
+
| <storedEmail> | <storedPassword> |
|
|
78
|
+
And the account has <priorFailureCount> consecutive failed sign-in attempts
|
|
79
|
+
But the account is locked
|
|
80
|
+
When a user signs in with email "<submittedEmail>" and password "<submittedPassword>"
|
|
81
|
+
Then the system rejects access
|
|
82
|
+
|
|
83
|
+
Examples: Correct credentials while locked
|
|
84
|
+
| storedEmail | storedPassword | priorFailureCount | submittedEmail | submittedPassword |
|
|
85
|
+
| user@example.com | correct-password | 5 | user@example.com | correct-password |
|